Skip to content

Active frame-free absolute noise floor (RX-idle, wedge-free) (#202)#304

Merged
josephnef merged 1 commit into
masterfrom
rx-active-noise-floor-202
Jul 18, 2026
Merged

Active frame-free absolute noise floor (RX-idle, wedge-free) (#202)#304
josephnef merged 1 commit into
masterfrom
rx-active-noise-floor-202

Conversation

@josephnef

Copy link
Copy Markdown
Collaborator

Summary

Resolves the wedge in #202. Adds an opt-in active / frame-free absolute noise floor (DEVOURER_RX_NOISE_FLOORRxQuality.abs_noise_floor_dbm) — the 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 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:

Gen Path Result
Jaguar1 (8812A/8821A) debug-port sampling (0x0FA0) run once at bring-up, before StartRxLoop → wedge-free by construction; cached 0/6 wedged runs (vs the issue's live-poll 2/6); floor −89…−97 dBm. Validated.
Jaguar2 (8822B/8821C) HW idle-noise report 0x0FF0 (freeze 0x9E4[30], no clock-stop) read live best-effort — see below
Jaguar3 (8822C/8822E) vendor 8822C dispatch returns 0 (no path) always null; passive floor (#201) is J3's only floor

Jaguar2 caveat (honest)

On the tested 8812BU (1×1 8822B cut, my only J2 sample) the 0x0FF0 report is only rarely populated in monitor-mode bring-up — most reads return the 0x80/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, and null otherwise (never a fake dBm). A 2T2R 8822BU or a fuller init may populate it reliably.

What's here

Full build + ctest (28/28) green.

🤖 Generated with Claude Code

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
josephnef merged commit 7626ad1 into master Jul 18, 2026
18 checks passed
@josephnef
josephnef deleted the rx-active-noise-floor-202 branch July 18, 2026 20:28
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant