Beamforming self-sounding: MU report → per-subcarrier SNR#155
Beamforming self-sounding: MU report → per-subcarrier SNR#155josephnef wants to merge 3 commits into
Conversation
An SU report gives only per-tone steering direction + a per-stream scalar SNR. An MU report additionally appends the MU Exclusive Beamforming Report — real per-subcarrier SNR. Adds arm_beamformee_mu() (layers the MU group-table registers 0x14C0/C4/C8/CC + entry 0x1684 on the SU responder base, programming the group/user-position directly so self-sounding needs no over-the-air Group ID Management handshake; recipe from hal_txbf_8822b_enter), wired on Jaguar-3 via DEVOURER_BF_ARM_BFEE_MU=1, plus the NDPA MU feedback bit via DEVOURER_TX_NDPA_MU=1. bf_report_decode.py parses the MU report: Realtek packs the SNR as 8-bit pairs (series A = the per-tone SNR that swings with the channel), which the decoder extracts, maps to dB and to a modulation per tone, and trims of devourer's trailing chip-FCS/RX bytes. --operating-snr re-centres the measured per-tone shape to a stated link budget to show the textbook per-tone QAM ladder. Validated: 8822CU MU-beamformee reports run 153 B (vs 99 SU); series A is a smooth 45-53 dB per-tone curve with ~8 dB frequency-selective swing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a live scrolling truecolor per-subcarrier SNR waterfall — pipe WiFiDriverDemo's raw report stream into tools/bf_waterfall.py (or run the whole rig via tests/bf_waterfall.sh) to watch the per-tone channel across frequency (X) and time (Y), coloured by the modulation a rate-adaptive link would pick per subcarrier. tools/bf_waterfall_svg.py renders the same as an SVG for docs/img/bf_waterfall.svg (embedded in the doc). Also corrects the MU per-tone SNR mapping: the values cross 128 (a stronger tone reads e.g. 131 > 122), so the field is UNSIGNED (dB = -10 + 0.25*v); the prior signed int8 reading wrapped the strongest tones to negative (spurious navy cells). QAM thresholds recalibrated to the ~1e-3 uncoded-BER ballpark. A measured bench capture now reads a realistic ~13-21 dB per-tone SNR (was a too-high ~45-53) — 16-QAM on the stronger tones, QPSK on the weaker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Added: live per-subcarrier SNR waterfall + a decode correctionWaterfall. Correction (important). While building the waterfall I found the MU per-tone SNR field is unsigned, not signed int8. The per-tone values cross 128 (a stronger tone reads e.g. 131 > 122), so the old signed reading wrapped the strongest tones to negative dB — which is what produced spurious dark cells. Fixed to |
tools/bf_waterfall_gif.py renders the per-subcarrier SNR waterfall from a real capture as an animated GIF styled as a live UI — DEVOURER-branded header, a blinking LIVE indicator, a scrolling frequency×time heatmap that fades into the past, a per-frame readout (peak/min SNR, best/worst modulation, a current-tone profile bar), and the SNR→QAM legend. Embedded at the top of the doc in place of the static SVG (bf_waterfall_svg.py stays for a diff-able static export). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Follow-up to #154 (SU self-sounding). An SU beamforming report carries only the per-tone steering direction plus a per-stream scalar SNR. An MU report additionally appends the MU Exclusive Beamforming Report — genuine per-subcarrier SNR — which is the per-tone channel quality that maps to a per-tone modulation order (the textbook "256-QAM on clean tones, QPSK on faded tones" picture).
How
arm_beamformee_mu()(src/BeamformingSounder.h) layers the MU group-table registers (0x14C0MU-TX-CTL,0x14C4/0x14C8/0x14CCgroup/user-position, entry0x1684) on top of the SU responder base. For self-sounding we program the group/user-position directly, so no over-the-air VHT Group ID Management handshake is needed. Recipe from the vendorhal_txbf_8822b_enter()MU branch (identical across 8822B/C/E).DEVOURER_BF_ARM_BFEE_MU=1; the NDPA MU feedback bit (STA-info bit 12) viaDEVOURER_TX_NDPA_MU=1inWiFiDriverTxDemo.tools/bf_report_decode.pyparses the MU report. Realtek packs the SNR not as the spec's 4-bit deltas but as 8-bit values in pairs — series A (even bytes) is the per-tone SNR that swings with the channel. The decoder extracts it, maps to dB (22 + 0.25·int8) and to a modulation per tone, and trims devourer's trailing chip-FCS/RX bytes where the smooth series collapses.--operating-snr Nre-centres the measured per-tone shape to a stated link budget, turning the real frequency-selectivity into the textbook QAM ladder even on a strong bench link.Result (8822CU MU-beamformee, ch100, 20 MHz)
Reports grow 99 → 153 bytes; the per-tone SNR series A is a smooth 45–53 dB curve with ~8 dB of real frequency-selective swing:
Note:
DEVOURER_TX_PWRdoes not attenuate the link — the NDP is hardware-generated by the sounding engine, not sent throughsend_packet— hence the operating-point model rather than a physical power sweep.ctestgreen; all new code is env-gated and no-op by default.🤖 Generated with Claude Code