{{ message }}
Beamforming self-sounding: MU report → per-subcarrier SNR#155
Merged
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>
Collaborator
Author
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>
Each frame was quantised with its own ADAPTIVE palette, so identical static pixels (title, labels, legend) mapped to slightly different colours every frame — the visible "trembling". Build one global palette from a montage of sampled frames and quantise every frame to it with dithering off, so unchanged pixels are bit-identical across frames (verified: title/legend inter-frame diff = 0). Smaller too (~970 KB), and the waterfall/readout still animate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

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