Active frame-free absolute noise floor (RX-idle, wedge-free) (#202)#304
Merged
Conversation
Adds an opt-in active/absolute noise floor (DEVOURER_RX_NOISE_FLOOR -> RxQuality.abs_noise_floor_dbm), the frame-free companion to the passive rssi-snr floor (#201): a true idle-channel floor readable with no wanted signal (site survey / channel selection). Folded into the existing RxEnergy/RxQuality surface and emitted on rx.quality / rx.energy; ~10 ms of USB round-trips, so off by default. The #202 blocker was that the vendor measurement WEDGES live RX. The wedge is specific to the 8812A/8821A active-sampling die (it stops CK320/CK88 and resets BB/PMAC/CCK). devourer runs each generation's path only where it is safe: - Jaguar1 (8812A/8821A): the debug-port sampling path (0x0FA0, sign/pwdb math in src/NoiseFloorMath.h) runs ONCE at bring-up, before StartRxLoop starts the bulk-IN DMA -> wedge-free by construction; cached, surfaced via GetRxEnergy. On-air: 0/6 wedged runs (vs the issue's live-poll 2/6), floor -89..-97 dBm. 8814A excluded (different vendor path). - Jaguar2 (8822B/8821C): the HW idle-noise report at 0x0FF0 (freeze 0x9E4[30], no clock-stop) is read live -> wedge-free. BEST-EFFORT: on the tested 8812BU the report is only rarely populated in monitor bring-up (reads the 0x80/0x00 "no idle sample" sentinels); investigated (no vendor enable step exists, it is channel-independent, DIG-pause/oversampling do not fix it), so it oversamples and guards to a plausible idle-floor band -- emitting a value that cross- matches the Jaguar1 floor when live, null otherwise (never a fake dBm). - Jaguar3 (8822C/8822E): the vendor 8822C dispatch returns 0 (no idle-noise path), so abs_noise_floor_dbm is always null; the passive floor is J3's only floor. src/NoiseFloorMath.h ports the two phydm sign/pwdb helpers verbatim (+ a ctest selftest). Validation: tests/rx_noise_floor_active_onair.sh (anti-wedge + sanity / cross-chip). A B210 injected-noise sweep is not used -- the bench SDR is too weakly coupled to move the RTL floor above the measurement variance (the same near-field limit as #199/#201). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
josephnef
added a commit
that referenced
this pull request
Jul 19, 2026
## Summary Brings the `SetCcaMode`/`dis_cca` lever (#199) and the noise-floor sensing (#201/#202) to the Wi-Fi 6 **Kestrel** HAL (RTL8852BU/8852CU) — the generation both were left out of — and drops the issue-number/"parity" boilerplate from the earlier #303/#304 comments (git already records provenance). ## Kestrel features - **`SetCcaMode` (#199)** — RMW `R_AX_CCA_CFG_0` all-CCA-EN (primary + sec20/40/80 + EDCCA). Injection is already CCA-off by default on Kestrel (`EnableTxScheduler` clears the gates, the ~103-stall fix), so this is the runtime toggle rather than the co-channel-deferral fix it is on Jaguar. `DEVOURER_DIS_CCA` wired at `InitWrite`. - **Active absolute noise floor (#202)** — the halbb **NHM env-monitor** via a new glue call + `GetRxEnergy` override. Frame-free, BB-driven, **no clock-stop → no wedge** (the cleanest of any generation). Two fixes were needed: `bb->cr_type = BB_CLIENT` (the env-monitor CR-init switch was falling through, leaving the NHM ready-bit address 0) and a link stub for the BE-only `halbb_get_prim_sb` (dead 11BE branch). - **Passive `rssi−snr` floor + LinkHealth (#201)** — parse physts IE_01 SNR alongside the header RSSI, feed an `RxQualityAccumulator`, `GetRxQuality` override. ## On-air validation | | 8852BU (C8852B) | 8832CU (C8852C) | |---|---|---| | SetCcaMode | ✅ lever fires | ✅ same code | | Active NHM floor | ✅ **−93 dBm**, frame-free (−93 on a 0-frame idle channel), no wedge | gated null | | Passive floor | ✅ **−94 dBm** — cross-matches NHM within ~1 dB, HEALTHY | null | **8852C** is gated to emit null rather than wrong values — its NHM reads ~25 dB high (idle ch149 −68 vs the 8852B's −93) and the physts IE layout differs (16-byte drv_info) so SNR is unparsed. Both are documented follow-ups (`docs/rx-spectrum-sensing.md`). Build all + `ctest` 28/28 green. 🤖 Generated with [Claude Code](https://claude.com/claude-code) 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.
Summary
Resolves the wedge in #202. Adds an opt-in active / frame-free absolute noise floor (
DEVOURER_RX_NOISE_FLOOR→RxQuality.abs_noise_floor_dbm) — the companion to the passiverssi−snrfloor (#201) — a true idle-channel floor readable with no wanted signal (site survey, channel selection). Folded into the existingRxEnergy/RxQualitysurface and emitted onrx.quality/rx.energy; ~10 ms of USB round-trips, so off by default.The key finding
The #202 blocker was that the vendor active measurement wedges live RX. Mapping the vendor dispatch shows the wedge is specific to the 8812A/8821A active-sampling die (it stops CK320/CK88 and resets BB/PMAC/CCK). So devourer runs each generation's path only where it's safe, and always RX-idle where the wedge applies:
0x0FA0) run once at bring-up, beforeStartRxLoop→ wedge-free by construction; cached0x0FF0(freeze0x9E4[30], no clock-stop) read liveJaguar2 caveat (honest)
On the tested 8812BU (1×1 8822B cut, my only J2 sample) the
0x0FF0report is only rarely populated in monitor-mode bring-up — most reads return the0x80/0x00"no idle sample" sentinels. Investigated: there is no vendor enable step (the report is HW-automatic; the vendor runs the measurement from a full associated init, not monitor mode), the sentinel rate is channel-independent, and oversampling / DIG-pause don't fix it. So the J2 path oversamples and guards to a plausible idle-floor band — it emits a value that cross-matches the Jaguar1 floor within a few dB when the report is live, andnullotherwise (never a fake dBm). A 2T2R 8822BU or a fuller init may populate it reliably.What's here
src/NoiseFloorMath.h— the two phydm sign/pwdb helpers ported verbatim (+noise_floor_mathctest selftest).RxEnergy/RxQualitygainabs_noise_floor_dbm(+ valid flag);DEVOURER_RX_NOISE_FLOORopt-in;rx.quality/rx.energyemit it.tests/rx_noise_floor_active_onair.sh— anti-wedge (the acceptance gate) + sanity / cross-chip. A B210 injected-noise sweep isn't used — the bench SDR is too weakly coupled to move the RTL floor above the measurement variance (same near-field limit as dis_cca / EDCCA-disable: verify on a real (non-monitor) link — measured null in monitor RX #199/GetRxQuality() — runtime RX link-quality feed + passive noise floor #201).Full build +
ctest(28/28) green.🤖 Generated with Claude Code