Power save: a download against a video¶
Setup on 2026-10-21¶
The AP45 at home in the work org (firmware 0.14.30137), eduroam on 5 GHz ch 52/20 at power 8, chosen by Mist. The phone (an iPhone, HE with two streams, no 6 GHz, a private address on eduroam) lay still 1 m from the sensor: the AP at −50 dBm at the sensor, the phone at −51 to −57 dBm at the AP in Mist. capA on the AP's channel; capB on the AP45's 2.4 GHz radio during the download and on its 6 GHz radio after it, as the app locked it. The Mac's Wi-Fi on 2.4 GHz ch 6 for the first two sessions and on the AP's channel for the third, where it was the second client; awdl0 down. Three sessions at the same spot, the phone's screen on:
| Session | Traffic | Time |
|---|---|---|
| Download | the phone: a 10 GB file from a speed test server in Safari, stopped after about 2 GB | 17:44:17 to 17:47:55 |
| Video | the phone: a YouTube video in the app, quality on automatic | 17:54 to 17:57 |
| Second client | the phone: a video again; this Mac (MacBook Pro M1 Pro, Wi-Fi 6, on battery) on eduroam on the same radio as reference client, at −57 dBm at the AP: 1 MB from a speed test server every 10 s | 18:37:13 to 18:40:18 |
linkside-cli mist stats every 20 s next to the app's own polls in the first two, before and after the third (mist-events.md). The Pi's clock was 16.00 s behind the Mac; the times here are the Mac's, the sensor's corrected with the session's clock offsets.
Method¶
Per data frame of the AP to a client: a transmission failed when the AP sent the same sequence number again with the retry bit within 2 s. The sensor captured the whole downlink to both clients (capture share 99.6 to 100 %), so every retransmission is in the capture. A PPDU is a run of the AP's data frames to the client at one rate with no other frame between them: the mt7925 gives every MPDU its own A-MPDU reference (eht-capture.md). A wake is the client's power management bit going from 1 to 0. The windows: 17:44:10 to 17:46:50 of the download, 17:54:00 to 17:57:00 of the video, 18:37:13 to 18:40:18 of the second client.
The phone awake and in a video¶
| Download | Video | |
|---|---|---|
| The AP's data frames to the phone | 694,520 | 9,931 |
| Failed, sent again | 5.0 % | 24.4 % |
Mist, tx_retries per tx_pkts per minute |
3.8 to 5.3 % | 12.6 to 67.7 % |
| BA success downlink, whole session | 99.2 % | 98.0 % |
| The AP's RTS answered with a CTS, then no Block Ack, whole session | 1.9 % | 13.7 % |
The download's BA success as the engine counts it since a Block Ack for an older window keeps what the Block Acks before it settled, its uncut recording replayed at dfc1efb on 2026-11-03: 99.2 %, 98.9 % with the rule before. Its other figures count frames or TXOPs and stayed as they were.
In the video 99 of the phone's wakes brought a burst of the AP's backlog, a median of 1.3 s apart (quartiles 0.6 and 2.5 s), and 88 % of the AP's frames came within 0.5 s of a wake. During the download it set power save 24 times, for a median of 0.13 s and at most 1.4 s.
By the AP's rate¶
| The AP's rate | Download: share | failed | Video: share | failed |
|---|---|---|---|---|
| MCS 6–8, two streams, GI 0.8 | 63.7 % | 3.0 % | 28.8 % | 5.6 % |
| MCS 6–8, two streams, GI 1.6 | 7.1 % | 13.3 % | 29.1 % | 9.4 % |
| MCS 9–11, two streams, GI 0.8 | 18.1 % | 3.7 % | 7.3 % | 16.7 % |
| MCS 9–11, two streams, GI 1.6 | 3.1 % | 31.1 % | 25.1 % | 67.0 % |
| One stream | 5.9 % | 6.6 % | 9.4 % | 21.5 % |
| MCS 0–5, two streams | 2.0 % | 6.3 % | 0.2 % | 5.9 % |
MCS 9–11 with two streams and GI 1.6 carry 69 % of the video's failures and 20 % of the download's. Every frame went with a 2x LTF: the two guard intervals differ in that alone. The AP used GI 1.6 for first tries as well as for retries: in the video 15.5 % of its frames were first tries at MCS 9–11 with GI 1.6, and 48 % of those failed.
The two earlier sessions with a video on the same AP and channel, the same day, show the same rates failing: MCS 9–11 with two streams and GI 1.6 were 28 and 51 % of the AP's frames and failed 28 and 55 % of the time (testlab-client-leaves-eduroam, testlab-ap-kick-eduroam), at GI 0.8 3 to 10 %.
By the place after the wake, in the video¶
| PPDU after the wake | PPDUs | Frames | Failed |
|---|---|---|---|
| First | 99 | 105 | 14.3 % |
| Second | 79 | 89 | 27.0 % |
| Third | 78 | 105 | 10.5 % |
| Fourth | 74 | 157 | 36.3 % |
| Fifth and later, within 0.5 s | 1,935 | 8,319 | 26.3 % |
| Not within 0.5 s of a wake | 1,146 | 1,156 | 11.8 % |
86 % of the video's frames went after an RTS the phone answered with a CTS, and 94 % of the frames that failed.
A second client¶
The phone with a video again, and this Mac beside it as the reference client (testlab-power-save-second-client). The Mac dozed too, between its fetches and its own traffic:
| Phone, video | Mac, 1 MB every 10 s | |
|---|---|---|
| Wakes followed by downlink | 130, a median of 1.1 s apart | 223, a median of 0.6 s apart |
| The AP's data frames | 8,849 | 14,972 |
| Failed, sent again | 22.0 % | 6.7 % |
Mist, tx_retries per tx_pkts, 18:35 to 18:41 |
27.1 % | 8.8 % |
| The AP's frames with GI 1.6 | 44.9 % | 11.3 % |
| First tries at MCS 9–11, two streams, GI 0.8, failed | 9.7 % | 3.8 % |
| The AP's TXOPs lost whole after the client's CTS | 10.1 % | 8.6 % |
| BA success downlink | 98.4 % | 98.8 % |
| First PPDU after a wake, failed | 3.4 % | 13.6 % |
| Fifth and later PPDU within 0.5 s of a wake, failed | 27.5 % | 7.0 % |
The same measures for the phone awake, in the download: 5.0 % failed, 11.8 % of the AP's frames with GI 1.6, first tries at MCS 9–11 with GI 0.8 3.4 % failed, 1.9 % of the TXOPs lost whole. For the phone alone with a video at 17:54: 24.4 %, 55.3 %, 14.6 % and 13.7 %. With GI 1.6 the AP's frames mostly failed more often than with GI 0.8 at the same MCS, for both clients, awake and in bursts. BA success read 98.4 % for the phone in the second session.
What it shows¶
- The phone is awake when the AP's frames fail: it answered the AP's RTS before nearly all of them, and its first PPDU after a wake fails less often than the rest of the burst. The phone waking late is not the cause.
- Most of the loss is the phone's, in its bursts. The Mac, dozing beside it at the same time, had a third of the phone's retransmissions, about what the phone had awake. At the same rate the phone failed 3 to 4 times as often in its bursts as awake, and 2.5 times as often as the Mac.
- A share comes with the bursts for both clients: one TXOP in ten lost whole after the client's CTS, against 1.9 % for the phone awake; for the Mac in its first PPDUs after a wake, for the phone in the later ones. Whether that is the AP after a pause or a receiver after a doze, the sensor cannot tell: it decoded every one of these frames 1 m from both clients, but the AP beamforms toward the clients, not toward the sensor.
- The AP sent the losing client far more GI 1.6: 45 to 55 % of the phone's frames in its bursts, 11 % of the Mac's and of the phone's while awake, in first tries as well as in retries. The AP's rate control reacting to the losses fits that better than GI 1.6 causing them; the capture cannot tell which came first.
- BA success misses most of it: 98 % for the phone in its bursts while a fifth to a quarter of the AP's transmissions failed. A PPDU lost whole after the client's CTS gets no Block Ack, and its frames delivered on a retry count as delivered. A comparison with the reference client on BA success shows no difference; on the frames sent again it does (
testlab-power-save-second-client). - The first BA after a pause leaves nothing out. After more than 1 s without a BA a flow counts again from the first acknowledged bit of its next BA, so a burst whose first MPDUs fail would count from the first one delivered. In
testlab-power-save-videothe first bit of each of those 267 BAs, both ways, was acknowledged. A replay without that restart (blockAckFlowTimeoutat 10⁹ s) gives the same Block Ack counts in all 23 recordings buttestlab-ap-reboot, where it counts 241 sequence numbers from before the agreement after the reboot as lost: the restart after a pause is also what keeps a new agreement's stale bits out when the sensor cannot read its setup. - "Mostly right after a wake" does not tell the causes apart when nearly all the traffic comes after wakes, as with a video. A second client dozing beside the client does, and so does the same client awake at the same rates.
The video again on 2026-10-22¶
The same phone on the same AP45 radio (eduroam, 5 GHz ch 52/20), at home: the phone joined eduroam, then Linkside started, then a YouTube video played until the end of the session, 22:44:38 to 22:49:56. No reference client; capB was on the AP45's 2.4 GHz radio and captured nothing: its filter lacked that radio's BSSID, which capA had not heard. The AP at −51 dBm at the sensor, the phone at −52 to −55 dBm with an SNR of 42 to 45 dB in Mist, Mist's noise floor −97 dBm and util_non_wifi 0. The sensor captured 99.6 % of the downlink.
| Video, 2026-10-22 | |
|---|---|
| The AP's data frames to the phone | 26,821 |
| Failed, sent again | 21.2 % |
| The AP's frames with GI 1.6 | 47.5 % |
| First tries at MCS 9–11, two streams, GI 0.8, failed | 4.8 % |
| The AP's TXOPs lost whole after the client's CTS | 9.9 % |
| BA success downlink | 99.0 % |
| Wakes followed by downlink | 178, a median of 1.1 s apart (quartiles 0.6 and 2.8 s) |
| The AP's frames within 0.5 s of a wake | 93.1 % |
| First PPDU after a wake, failed | 13.7 % |
| Fifth and later PPDU within 0.5 s of a wake, failed | 22.6 % |
The app's verdict: undecided, with the note "Sent again after wakes" (62.5 % in its worst window, 22:46:43 to 22:47:03).
First tries by MCS and guard interval¶
Every frame went with a 2x LTF, as on 2026-10-21; the radiotap of the mt7925 marks none of them beamformed.
| MCS, two streams | GI 0.8: first tries | failed | GI 1.6: first tries | failed |
|---|---|---|---|---|
| 7 | 351 | 0.3 % | 633 | 3.9 % |
| 8 | 1,993 | 1.7 % | 1,200 | 39.8 % |
| 9 | 3,428 | 2.2 % | 2,181 | 22.9 % |
| 10 | 4,938 | 5.4 % | 2,628 | 30.3 % |
| 11 | 2,198 | 7.7 % | 819 | 33.2 % |
Episodes¶
The losses came in stretches of a few seconds, five between 22:44:54 and 22:47:34, 23 to 60 s apart, and none after it. They were the video's large fetches: in the 20 seconds with 500 or more of the AP's frames, the seconds where the AP used GI 1.6 for at least three quarters of them lost 34 to 68 %, those with at most 14 % GI 1.6 lost 0.1 to 7 %. In half a second at 22:44:55, 130 first tries at MCS 10 with GI 0.8 all came through, while 203 at MCS 8 with GI 1.6 lost 90 % and 72 at MCS 9 with GI 1.6 lost 96 %; 349 at MCS 7 with GI 1.6 in the same half second all came through. The AP's beacons at the sensor stayed at −49 to −53 dBm; in the large fetches that lost and in those that did not, the beacons and the phone's frames at the sensor were in the same range (beacons −50 to −52 dBm, the phone −42 to −51 against −39 to −49 dBm).
| From | The AP's data frames | Failed | With GI 1.6 | Failed with GI 0.8 | Failed with GI 1.6 |
|---|---|---|---|---|---|
| 22:44:38 | 10,199 | 20.8 % | 47.0 % | 5.4 % | 38.1 % |
| 22:45:38 | 6,052 | 30.2 % | 62.2 % | 5.1 % | 45.5 % |
| 22:46:38 | 4,126 | 36.5 % | 68.7 % | 20.5 % | 43.7 % |
| 22:47:38 | 2,610 | 3.7 % | 30.7 % | 2.9 % | 5.4 % |
| 22:48:38 | 2,968 | 3.8 % | 17.3 % | 3.3 % | 6.1 % |
| 22:49:38 | 866 | 3.0 % | 3.9 % | 1.4 % | 41.2 % |
What it adds¶
- Another video of the phone on this AP gives the same picture: a fifth of the AP's transmissions failed while BA success read 99.0 %.
- At the same MCS the AP's first tries with GI 1.6 failed 4 to 23 times as often as with GI 0.8, and in the episodes frames at MCS 8 and 9 with GI 1.6 failed at the same moment as frames at MCS 10 with GI 0.8 came through. A channel that got worse, or a receiver that dozed, would take both.
- After 22:47:38 the AP still sent a sixth to a third of its frames with GI 1.6, and those failed 5 to 6 %, against 3 % with GI 0.8. GI 1.6 alone is not the loss: it fails in the episodes, which were large fetches.
- At MCS 6–11 with two streams the AP's PPDUs with GI 1.6 carried twice as many MPDUs as those with GI 0.8 (a mean of 6.6 against 3.3, by the method above) and were lost whole 12.3 % of the time against 4.8 %. Whether the length of the PPDU, the GI or the size of the fetch decides it, this recording cannot tell.
The roam after the download¶
At 17:48:25.9, 30 s after the download, the phone roamed with FT from the AP45's 5 GHz radio to its 2.4 GHz radio, where capB was, with no BTM request over the air. Before it the phone went off its channel again and again, setting power save each time: null frames with PM 1 and PM 0 every 50 to 150 ms. 70 ms before its last null frame it exchanged a few frames with the AP, among them 202 bytes of data, which made slow-roam count it as in use and give client side for 409 ms, 391 of them off the air. The phone was idle and the roam its own: a false alarm (ap45-phone-band-roam). In use now wants at least five data frames in the second before, and the roam reads 18 ms. On 2.4 GHz it set PM 1 and PM 0 497 times in 34 s, no scans but short dozes. At 17:50:57 it roamed back to 5 GHz by itself (Mist's events).
Short dozes¶
Measured on 2026-10-22 on the recordings, from the client's PM bit on the radio's TSF (radiotap.mactime); the capture time gives the same lengths to 0.02 ms. On 2.4 GHz the phone sent PM 1 and PM 0 in two Null frames right after each other, 0.35 to 0.41 ms apart with consecutive sequence numbers and the AP's ACK in between, in a cycle of 30 ms: every 5 and 25 ms by turns in ap45-phone-band-roam after the roam, every 8 and 22 ms in testlab-long-quiet while it was on 2.4 GHz. No radio leaves the channel and comes back in 0.35 ms; the pattern is a client keeping the AP quiet for short slots, as Bluetooth coexistence does on 2.4 GHz. On 5 GHz no doze in six recordings took under 1 ms, the shortest 1.4 ms.
Counted as wakes, the toggles put nearly every frame of the AP to the phone right after one (from the wake until the next PM 1, at most wakeBurst), so a loss on 2.4 GHz would read as the client's power save:
| Recording, band | Wakes | Doze under 1 ms | AP data frames | In a burst | In a burst, 1 ms minimum |
|---|---|---|---|---|---|
ap45-phone-band-roam, 2.4 GHz |
496 | 460 | 1 | 100 % | 0 % |
testlab-long-quiet, 2.4 GHz |
615 | 548 | 184 | 99 % | 25 % |
testlab-long-quiet, 5 GHz |
188 | 0 | 19,883 | 75 % | 75 % |
testlab-power-save-video, 5 GHz |
198 | 0 | 12,769 | 82 % | 82 % |
A doze under 1 ms (minimumDoze) is none for the engine. The golden files of all 23 recordings stay the same: neither 2.4 GHz stretch had traffic enough for a verdict.
The seconds after a join¶
Measured on 2026-10-31 on every recording with a successful join (JoinStartMeasureTests, LINKSIDE_JOIN_START=all): per second from the interval that holds the client's keys (M4, or the accepted response with FT), the AP's data frames and those with the retry bit and BA success; the client's session outside the 10 s after each join; and each 20 s window of the scan that holds the start of the connection as the loss rules take it (the keys at most 3 s before its start and a second before its end, HintFacts.Joins.holds), with its first 3, 5, 7 and 10 s after the keys left out. EAPOL is not among the data frames, as the engine counts them since that day (definitions).
A new connection starts from nothing: the AP has no rate for the client yet, the Block Ack agreements start anew, a phone dozes again between its first frames. The joins whose downlink the sensor captured nearly whole (the retry bit counts from 90 %), the AP's data frames sent again:
| Recording, join | First 3 s of the keys | BA success there | The 7 s after | The session outside the joins |
|---|---|---|---|---|
ap45-phone-reconnect, reconnect |
25 of 135, 19 % | 100 % | 24 of 304, 8 % | 32.9 % |
ap45-phone-video-reconnect, reconnect |
4 of 7 | 100 % | 25 of 83, 30 %, in the wakes 5 and 6 s after the keys | 18.4 % |
ap45-radius-timeout-reauth, reconnect |
29 of 197, 15 % | 95.8 % | 4 of 309, 1 % | 13.5 % |
testlab-ap-kick-eduroam, reconnect |
7 of 18 | 100 % | 24 of 398, 6 % | 34.4 % |
testlab-client-leaves-eduroam, reconnect |
19 of 85, 22 % | 100 % | 62 of 271, 23 % | 18.2 % |
testlab-long-quiet, roam to 2.4 GHz |
5 of 86, 6 % | 100 % | 1 of 27 | 14.8 % |
testlab-long-quiet, roam back to 5 GHz |
18 of 151, 12 % | 100 % | 286 of 2171, 13 % | 14.8 % |
In the second of the FT roam of u7-phone-ft-roam, AP A sent 48 of its 68 frames to the phone again, all of them in the 0.8 s before the phone's reassociation to AP B and none after it: the old link while the phone, carried away from AP A and the sensor, was still on it. The window that ends 3 s after the roam reads 17.6 %, a note "Sent again", and 13.1 % without the second of the roam and the two after it: the end of the old connection, not the start of the new one. The windows after it captured 31 to 54 % of the downlink, too little for the retry bit. In testlab-4way-ack each 4-way put 7 frames sent again in its first second, the AP's M3 that the A9000 as client did not hear; as EAPOL they are out of the count now, and the 4-ways' second has none.
The start is short, one to three seconds, and small: a few dozen to two hundred frames, 15 to 22 % of them sent again after three of the five reconnects (two had too few frames to say), 6 and 12 % after the two roams, where the same phone's session outside the joins has 13 to 34 %. No window that holds a join gave a loss finding; their notes are the phone's power save, as everywhere in its sessions, and once the old link in the second of a roam. Leaving the first seconds out moves a window little and either way: the window of ap45-phone-video-reconnect that ends 7 s after the keys, 29 of 89 frames sent again (32.6 %), reads 30.5 % without its first 3 s and 29.1 % without 5, its wakes staying; the one of ap45-phone-reconnect that ends 18 s after them reads 13.2 %, 12.0 % and 18.0 %; testlab-client-leaves-eduroam 18.2 %, 17.3 % and 18.6 %. The loss rules hold nothing back after a join and say when a window holds one (rules).
What is still to check is in the plan (docs/status.md).