Skip to content

EHT capture with the A9000

Setup on 2026-10-13

Part Details
AP UniFi U7 Pro Max, SSID linkside on 6 GHz only, WPA3-Personal, MLO off, channel 37 (6135 MHz) at 160 MHz, center1 6185 MHz
Client Stick B on the Fedora PC, mt7925u firmware build 20260813113118. iw dev link: rx EHT-MCS 9 NSS 2, tx EHT-MCS 13 NSS 2, 160 MHz, GI 0.8, signal −46 to −49 dBm
Monitor Stick A in the Pi as capA, kernel 6.18.50, firmware build 20260414153105, tune capA 6135 160
Traffic TCP download on the client (Hetzner 10 GB file) over Wi-Fi, the PC's Ethernet disconnected
Contrast The Mac (Wi-Fi 6E, HE) on 5 GHz channel 48 at 80 MHz, tune capA 5240 80, with a download on the Mac

Each capture: 30 s of linkside-cap with wlan addr1 <client> or wlan addr2 <client>; drop=0, utrunc=0, kernel_filter=yes in all of them.

Radiotap from the mt7925 driver

  • EHT data frames carry the U-SIG and EHT blocks. MCS, NSS, GI and bandwidth are all known; none sits under a not-known field.
  • NSS in the EHT user info is zero-based: mt76 writes status->nss - 1. The raw value 1 is two streams.
  • HE data frames carry the HE block with MCS, NSS (actual count), bandwidth and GI known.
  • Control frames from the AP (Block Ack, RTS) and from the client (Block Ack) come at legacy 24 and 6 Mbps. RTS with bandwidth signaling carries the BSSID with the Individual/Group bit set (9f:… for 9e:…).
  • Per-chain signal: antennas 0 and 1, plus the combined value.
  • No FCS: a frame comes without it, and the radiotap Flags field says so (its FCS bit clear on all 3,350 frames checked). In the recordings of the test data and ~/linkside-captures up to 2026-10-29, 743 EAPOL frames in the clear and 2,603 deauthentications and disassociations measure to the length their own fields give (the four longer by 18 bytes, broadcast deauthentications, carry BIP's MME element); an encrypted frame is its header, CCMP's 8, the body and the MIC. In Mist's reauthentication of the iPhone over its keys (2026-10-29 10:37) the AP's encrypted EAP-Request/Identity has 17 bytes of payload, its EAP-TLS fragments 1032, its EAP-Success 16, M1 129 and M3 363, and the iPhone's M4 107: each LLC/SNAP (8), the EAPOL header (4) and the body the same frames have in the clear (5, 1020, 4, 117, 351, 95).
  • Every MPDU carries its own A-MPDU reference, the subframes of one A-MPDU too, and a TSF (radiotap.mactime) of its own, later than the subframe before it. The reference does not group the MPDUs of a PPDU: 2,702 references for 2,706 EHT QoS data frames in eht-6g-160-full-downlink, 63,760 for 65,298 HT ones in testlab-intermittent-loss (2026-10-29). Wireshark therefore puts its preamble on every HT MPDU: 144 µs for 1,566 bytes at 117 Mbit/s, 107 µs of them at the rate. It is a bug of mt76, not reported upstream: the engine groups the MPDUs by the TSF instead, and item 070 of the plan (kernel 7.x) checks a new driver or firmware for that. VHT data from mt76 is in no recording. A run of data frames at one rate with no other frame between them does group them (HE from the AP45, 2026-10-21, power-save.md), and the TSF tells where the run's PPDUs start: it stamps each MPDU at its end, so within a PPDU it steps by the next MPDU's length at the rate. Of 271 steps between consecutive sequence numbers whose lengths at the rate differ by more than 20 µs in eht-6g-160-full-downlink it is 0.6 µs above the next MPDU's length (median) and 27 µs below the previous one's, of 5,166 in testlab-intermittent-loss (HT on 2.4 GHz) 4.9 µs below the next and 42 µs below the previous. The lengths here are the 802.11 frame's: frame.len includes the radiotap header, 128 bytes on EHT frames, 76 on HE, 64 on HT and 56 on legacy. Between two MPDUs of one transmitter to the other with no frame between them, 90 % of the 2,296 steps in eht-6g-160-full-downlink lie within 1.4 µs below and 1.6 µs above the next MPDU's length, none from 10 to 120 µs above; in testlab-intermittent-loss the HT stamps scatter more, up to 60 µs above, 6 of 19,776 from 60 to 90 µs. An MPDU the sensor missed inside an A-MPDU lengthens the step by its own length and skips a sequence number: in eht-6g-160-low-capture 17 steps one MPDU long with one skipped, 4 two with two and 2 three with three, in u7-phone-search-copies 47 one with one. Two MPDUs of one pair with a frame captured between them are two PPDUs: over 30,704 such pairs in eht-6g-160-full-downlink, eht-6g-160-low-capture, testlab-intermittent-loss, testlab-power-save-second-client and u7-phone-search-copies the rule of the engine, 60 µs beyond the next MPDU's length and the missed ones' (definitions), joins none, where 80 µs joined one. With a preamble per PPDU, the downlink airtime of eht-6g-160-full-downlink (linkside-cli replay) went from 234.8 to 139.0 ms, of testlab-long-quiet from 2,993 to 2,302 ms (2026-10-29).

Data frames against acknowledged MPDUs

Captured data frames against the MPDUs the Block Acks of the other side report as delivered plus failed. The second EHT capture had the sensor 1 m from the client, farther from the AP. The engine reports the same measure against the delivered MPDUs alone as the capture share (linkside-cli replay, capture_share).

Capture Direction Signal at the sensor Captured / acknowledged Rates captured
eht-6g-160-A AP to client −47 dBm 17,138 / 112,689 (15 %) EHT MCS 5–9, 2 streams, 160 and 80 MHz
eht-6g-160-A client to AP −48 dBm 401 / 82,422 (0.5 %) EHT MCS 7–12, 2 streams
eht-6g-160-A-near AP to client −57 dBm 3,445 / 107,360 (3 %) EHT MCS 6–7, 2 streams
eht-6g-160-A-near client to AP −40 dBm 27,047 / 85,590 (32 %) EHT MCS 9–12, 2 streams; no MCS 13
he-5g-80-mac AP to client −36 dBm 42,201 / 45,407 (93 %) HE MCS 6–11, 2 streams
he-5g-80-mac client to AP −41 dBm 3,109 / 3,175 (98 %) HE MCS 11, 2 streams

The A9000 decodes EHT data PPDUs at 160 MHz in both directions, with two streams up to MCS 12. Which data frames it gets follows its own SNR: closer to the client, the uplink went from 0.5 % to 32 %; farther from the AP, the downlink went from 15 % to 3 %, and the captured MCS shifted down with the signal. The bulk of the traffic ran at MCS 9 (AP) and MCS 13 (client); at the sensor's position those were mostly not decodable. The AP beamforms its two streams toward the client, which leaves a receiver elsewhere worse off than its RSSI suggests.

The Block Acks were complete in every capture (about 1,000 per second per direction), so BA success covers the traffic the data frames miss.

5 GHz at 80 MHz

The SSID moved to 5 GHz only, on the same AP's 5 GHz radio: channel 48 (5240 MHz) at 80 MHz, center1 5210. The client: rx and tx EHT-MCS 13 NSS 2, signal −36 dBm. The Mac on 2.4 GHz with awdl0 down. tune capA 5240 80, the sensor 1 m from the client (eht-5g-80-A).

Direction Signal at the sensor Captured / acknowledged Rates captured
AP to client −40 dBm 114,846 / 126,107 (91 %) EHT MCS 9–13, 2 streams, mostly MCS 12; 80, 40 and a few 20 MHz
client to AP −37 dBm 37,116 / 83,015 (45 %) EHT MCS 13 (87 %) and 11–12, 2 streams

The sensor decodes MCS 13 (4096-QAM) at this signal. The uplink was SU: 45 trigger frames in 30 s. linkside-cap reported utrunc=3, all in the first second; the count did not grow after that.

Stick B as the monitor

The sticks swapped: stick B in the Pi as capB (phy1, USB 2-1, SuperSpeed), stick A as the client on the Fedora PC. NetworkManager kept the same random client address. The client: rx and tx EHT-MCS 13 NSS 2 at 80 MHz, signal −38 dBm.

Capture Direction Signal at the sensor Captured / acknowledged Rates captured
eht-5g-80-B (1 m) AP to client −38 dBm 129,922 / 136,855 (95 %) EHT MCS 8–12, 2 streams, 80 MHz
eht-5g-80-B (1 m) client to AP −33 dBm 159 / 85,933 (0.2 %) EHT MCS 12–13 at 40 and 20 MHz only; none at 80 MHz
eht-6g-160-B (1 m) AP to client −45 dBm 131,774 / 130,813 (all; the count includes retries) EHT MCS 4–6, 2 streams, 160 MHz; some 1 stream at MCS 8–11
eht-6g-160-B (1 m) client to AP −36 dBm 1,271 / 86,172 (1.5 %) EHT MCS 7–11, 2 streams; no MCS 13

The uplink was SU (46 and 13 trigger frames in 30 s). On 6 GHz the AP ran at MCS 4–6 this time, within the sensor's SNR, and the sensor got every downlink MPDU at 160 MHz with two streams.

Outcome

Both A9000s decode EHT data at 5 GHz/80 MHz and 6 GHz/160 MHz in both directions, with radiotap MCS, NSS, GI and bandwidth known. The share of data frames follows the sensor's SNR against the MCS in use. Uplink at MCS 13 (4096-QAM) from a client 1 m away was mostly missed with both sticks on both bands; on 2026-10-23 stick A caught 63 % of it at 1 m and 54 % at 3 m (below).

Counter-test at 20 MHz

tune capA 6135 20 with the same download, the sensor 1 m from the client (eht-6g-20-A): 66,536 control frames and 1 data frame (from the AP, legacy 6 Mbps); no EHT data. BA success still came out: 99.7 % downlink over 30,127 BAs, 98.0 % uplink over 24,461. The EHT data frames above therefore come from decoding at the full width, and BA success does not depend on it.

Stick A in the Pi, stick B the client on AP A's 5 GHz ch 48/80 (iw dev link: rx and tx EHT-MCS 13 NSS 2, signal −39 dBm), the client in place and the Pi moved. 30 s of TCP uplink per distance, 94 Mbit/s: a 100 Mbit/s link between the AP and the iperf3 server kept the A-MPDUs short. The MPDUs the AP's Block Acks acknowledged, each once, against the client's data frames the sensor decoded:

Sensor from the client Client at the sensor Rate of the frames decoded Uplink captured
1 m −42 dBm EHT MCS 13 86 %, MCS 12 12 %, 2 streams 63 % (150,380 MPDUs)
3 m −42 to −46 dBm MCS 13 83 %, MCS 12 14 % 54 % (150,010)

Per second the share went from 48 to 77 % at 1 m and from 10 to 91 % at 3 m. At 1 m the same stick on the same channel caught 45 % on 2026-10-13 (above). 5 m needs the sensor on a battery. The recording is ~/linkside-captures/2026-10-23-uplink-distance, outside the repo.

6 GHz at 160 MHz at 2 Mbit/s, 2026-10-27

Item 077's recordings: stick A in the Pi as capA on 6135/160, stick B the client on the Fedora PC at about 1 m, on AP A's linkside (6 GHz ch 37 at 160 MHz; U7 Pro Max firmware 8.7.11), UDP at 2 Mbit/s each way for two minutes. Once on one link, once with MLO (5 GHz ch 48 and 6 GHz ch 37), where the Block Acks show the traffic ran on the 6 GHz link: the same 20,700 Block Acks each way as on one link. The client's iw dev link: tx EHT-MCS 12, 2 streams, 160 MHz.

Recording Direction Signal at the sensor Block Acks Captured Rates captured
one link AP to client −34 dBm (AP) 20,740 7 %
one link client to AP 20,278 0 %
MLO AP to client −45 dBm 20,732 0 % (35 data frames) EHT MCS 6, 2 streams, 160 MHz
MLO client to AP −31 dBm 20,273 0 % (3 data frames) EHT MCS 9, 2 streams, 160 MHz

At 6 GHz and 160 MHz the sensor 1 m from the client decoded next to none of the data in either direction, as stick B did as the monitor at 1 m (1.5 %, above); the Block Acks stayed complete and BA success read 100 % both ways. A capture share this low is the expected state at 160 MHz on 6 GHz, not a fault of the setup.

The retry bit and the MCS against the capture share, 2026-10-25

The sensor misses the fast frames, so the retry bit and the median MCS of what it decodes describe those frames, not the direction. How far they move with the share captured: CaptureShareMeasureTests drops a direction's data frames before the analyzer, the fastest first (MCS, then the number of streams, ties in a fixed random order), in steps of 5 % of them, as a sensor with a worse SNR would miss them. The Block Acks stay, so BA success holds and the capture share falls. The first step, nothing dropped, is the truth where the recording captured the whole direction.

LINKSIDE_CAPTURE_SHARE=$PWD/../../testdata/sessions/testlab-long-quiet swift test --filter CaptureShareMeasureTests

From which capture share the retry bit left the whole direction's by more than 2 points, and the median MCS fell by one:

Recording Direction Captured Retry bit Median MCS Retry bit 2 points off from Median one lower from
testlab-long-quiet (the phone, AP45) AP to client 99.6 % 14.6 % 8 93 % (low) 75 %
testlab-power-save-second-client AP to client 99.9 % 21.2 % 9 72 % (high) 50 %
testlab-power-save-video AP to client 100 % 22.6 % 8 65 % (low) 48 %
testlab-intermittent-loss (the Mac, 2.4 GHz) AP to client 92.9 % 20.3 % 4 not above 5 % 62 %
testlab-intermittent-loss client to AP 99.9 % 27.6 % – 76 % (high) –
eht-6g-160-full-downlink AP to client 91.4 % 2.4 % 4 not above 5 % not above 5 %

The phone's uplink in the AP45 recordings, with 1 to 2 % retries, stayed within 2 points down to 10 to 23 % captured. The retry bit errs both ways. Where retries go at a lower rate than first tries, what is left reads high (the Mac's uplink, the phone beside a second client); where the frames sent again went at the fast rates, it reads low (the phone in normal use). The median only reads low.

The real low shares sit with the drops. ap45-phone-band-roam read 27.2 % at 63 % captured against Mist's 12.5 %, but the same phone on the same AP read 1.7 to 1.8 times Mist's share with 99.4 to 99.6 % captured (testlab-long-quiet, testlab-client-leaves-eduroam, mist-events.md): of the factor 2.2 at 63 %, the capture share accounts for about a fifth, as the drops give. eht-6g-160-low-capture decoded 3.9 % of the downlink at a median of MCS 6, three below the AP's bulk at MCS 9 (above); the drops give two at 5 %. The drops leave out a fade at the sensor that also hits the client beside it and makes the AP send frames again; the two real points are all that measures it.

From this, on 2026-10-25: the retry bit and the MCS of a direction count from 90 % captured (minimumCaptureShare, 50 % before), 14 points above where the retry bit first read high and 15 above where the median moved; between 76 and 93 % they left the truth one way or the other. The AP's frames sent again by the retry bit (retransmitCaptureShare) stay at 90 %: within 2.1 points from 93 % up, and Mist's count, which takes over below it, read 6.4 points under the retry bit at 99.6 % for the phone. low-mcs (rateCaptureShare) stays at 90 %. No golden verdict changed: the finding that reads the retry bit at the lowest share, congestion in testlab-co-channel, had 96 % of the uplink in its window.