This is one offered load, seen from a single radio head's receiver. 50 client stations transmit to that one head; the head is the receiver and is not drawn. Everything below is what reaches it: a bundle received, a bundle missed, or nothing. Both strands run the same slot clock (120 ms), the same station count, and literally the same arrivals: one draw per station per slot feeds both, so the same station receives the same burst at the same instant on each and the only difference downstream is how the two systems handle it. Channel capacity is 1000 / SLOT_MS = 8.3 A-MPDU/s per strand, which at a 2.000 ms TXOP limit is about 91 MPDUs an aggregate. The unit of work is the A-MPDU; the packets fill it in, and how many fit is set by the TXOP limit the beacon carries. Each system is drawn as two rows: the packets queued in the clients' stacks, and what reaches the head on the air. Only the air carries aggregates - in a queue a packet is just a packet, and the A-MPDU does not exist until the MAC builds one, so only the air row is bundled and outlined. All three rows count packets on one shared scale — the queue rows per station, the airtime row the aggregate that went — so a packet is the same length of light wherever it is drawn, and blue accumulating into an aggregate is the same quantity that leaves as green. Each system is drawn in the three stages a packet passes through inside the network: blue waiting in the concentrator's 802.11 forwarding plane above the L2 drivers, yellow posted to a radio head's ring by L2 DMA and waiting on EDCA arbitration, green arriving as receiver airtime. The two systems differ in where they keep their packets: contention posts everything down immediately and queues in the MAC, while a grant holds the packets in the forwarding plane until the air is available.
What switching on L4S does and does not do here. Marking shortens the queue and slows the sender together, and W = L/λ, so for a single saturating flow the wait it reports is largely unchanged — which is what the whole-path total shows, and it is arithmetic rather than a defect. L4S earns its keep for other traffic sharing the path: a short latency-sensitive flow sitting behind a shallow queue instead of a deep one. This page runs one traffic class, so it can show the queue getting shorter and cannot show who benefits from that. What it can show is where the shortening stops. Marking reaches everything above the L2 drivers and nothing below them: the frozen population is untouched at any mark rate, and the contention strand's wait stays in the tens of seconds because its binding constraint is not queue depth at all, it is EDCA arbitration collapse. Granted access moves the same wait by more than an order of magnitude, which no marking scheme in the IP queue can do.
Every layer re‑quantizes the copy. The end‑to‑end goal is to copy a message from the memory of one machine to the memory of another, and each layer between implements that copy in its own natural unit: a write on the sender, a packet in the forwarding plane, a descriptor in the driver's ring, a TXOP on the air. Those units are not interchangeable, and the boundaries between them are where the interesting things happen — a packet can be marked and a descriptor cannot, a descriptor is posted one at a time and a TXOP succeeds or fails whole. Each row below draws its unit of work broken out from the queue behind it, because the unit is also what separates work from waiting: the time that unit takes is processing, and everything queued behind it is delay.
The rows are a pipeline, and it only runs one way. No writes, no packets. No packets, no DMAs. No DMAs, no EDCA arbitration. No arbitration, no airtime transmissions. Every row can only be occupied because the row above it was occupied first, and an aggregate on the air is never anything but packets that were written, queued, posted and then arbitrated for — it is consumed out of a DMA, never created by one. At a low enough offered load you can watch the stages light in that order, one packet at a time.
None of those three network stages is on the sender. The sender is upstream, with a socket buffer of its own, and it is the only thing in the whole path that a congestion signal can actually slow. That is why the pipeline below carries it as a separate stage: marks are written in the concentrator's forwarding plane, and the only party able to answer them is somewhere else entirely. TCP's CCA runs as fast as possible, so with a classic sender that socket buffer stays empty and every byte it has is already inside the network. Only ECN slows the sender, and so only ECN moves the waiting back out of the network and into the host, where nothing else queues behind it.
That boundary is where an ECN mark gets bound. A host can schedule a
DMA but cannot touch the packet afterwards, so CE has to be written before
the post — everything in the blue row can still be marked, and nothing
in the yellow row can. Marking is therefore early-bound, and how early
depends on how long the packets sit below the decision. Contention posts
everything immediately, so its marks are bound far ahead of the airtime they
are meant to describe and are stale by the time the aggregate actually goes,
after backoff and retries. A grant holds packets in the IP stack until the
air is available, so the mark is bound close to the transmission it refers
to. The depth of the yellow row is the size of that stale population. Green is in the 802.11 MAC: arbitrating or transmitting.
Red is a miss: two bundles went at once and the head received neither,
so both are drawn. One light is one packet and a bundle is one A-MPDU, which
is the countable thing - exactly one bundle can be received in a slot, and
red means two or more tried and none arrived.
Yellow is a grant that opened onto nothing, one light because it
carries no packets at all. Dark is nothing to send. The contention strand holds nothing back, so it is only
ever green or red; blue and yellow are what a scheduler looks like. Those four colours are the whole
palette. The contention window is reported as a number below rather than as
a shade, so nothing competes with them.
Build guide — bill of materials, wiring and the Arduino sketch for the physical two-strand version.
| stage | atom | L, packets | queueing | processing | share of the wait |
|---|---|---|---|---|---|
| send buffer, on the sender upstream | one write | 0 | - | - | |
| IP stack, in the concentrator | one packet | 0 | - | - | |
| EDCA arbitration + L2 DMA, frozen | one descriptor | 0 | - | - | |
| airtime, in service | one TXOP | 0 | - | - | |
| in the network | 0 | - | - | what every other flow queues behind | |
| whole path | 0 | - | - | plus what the sender is holding |
| stage | atom | L, packets | queueing | processing | share of the wait |
|---|---|---|---|---|---|
| send buffer, on the sender upstream | one write | 0 | - | - | |
| IP stack, in the concentrator | one packet | 0 | - | - | |
| grant wait + L2 DMA, frozen | one descriptor | 0 | - | - | |
| airtime, in service | one TXOP | 0 | - | - | |
| in the network | 0 | - | - | what every other flow queues behind | |
| whole path | 0 | - | - | plus what the sender is holding |