Left open by the RTL8733B CCX CLM port (#440, merged as fc66aa9). Full description in src/sensing/CLAUDE.md ("Known gap").
1. SenseWindow picks its source by pointer type, not by capability
SenseWindow::read takes the phydm branch whenever the IRtlRadio* is non-null. The RTL8733B derives from IRtlRadio, implements no GetRxEnergy, and answers GetChannelBusy() only through an armed CLM window — so on that die the window gets the all-invalid base energy read and never calls GetChannelBusy(). examples/chanscout constructs exactly that shape (dynamic_cast<IRtlRadio *> succeeds), so a scout on an RTL8733B reports neither CLM nor NHM. Every other backend is unaffected.
A capability gate on rx_energy_ok is necessary but not sufficient: an unarmed GetChannelBusy() there yields nothing, and nothing in src/sensing/ calls ArmChannelBusy. The arm needs a window sized to the dwell, which no layer between ScanPlanConfig::dwell_ms and SenseWindow carries, and arming interacts with the NHM read on the Jaguar families (which spoils the same engine — what dwell_executor pins). So: gate + dwell-sized arm + a selftest arm for the no-energy backend, then tests/chanscout_stress.sh on an RTL8733BU.
2. The other families' Stop() do not forget an armed window
#440 resets the busy window in the RTL8733B Stop(). Jaguar1/2/3 Stop() do not, and none of them clears the flag their with_ccx gates on, so a GetChannelBusy() straight after Stop() reads CCX registers on a deinitialised chip. Out of contract, but the asymmetry is worth closing the same way.
3. Bursty-load claim is untestable on a backend without a sampled path
tests/busy_window_probe.sh skips the armed-vs-sampled comparison where rx_energy_ok is false. On the RTL8733B the 240 ms window against a 50/450 ms burst reads 0–19% per window (mean 6–10%, spread 19). If a bursty claim is wanted there, it needs a window longer than the burst period or a many-window mean, not the current five reads.
Left open by the RTL8733B CCX CLM port (#440, merged as fc66aa9). Full description in
src/sensing/CLAUDE.md("Known gap").1.
SenseWindowpicks its source by pointer type, not by capabilitySenseWindow::readtakes the phydm branch whenever theIRtlRadio*is non-null. The RTL8733B derives fromIRtlRadio, implements noGetRxEnergy, and answersGetChannelBusy()only through an armed CLM window — so on that die the window gets the all-invalid base energy read and never callsGetChannelBusy().examples/chanscoutconstructs exactly that shape (dynamic_cast<IRtlRadio *>succeeds), so a scout on an RTL8733B reports neither CLM nor NHM. Every other backend is unaffected.A capability gate on
rx_energy_okis necessary but not sufficient: an unarmedGetChannelBusy()there yields nothing, and nothing insrc/sensing/callsArmChannelBusy. The arm needs a window sized to the dwell, which no layer betweenScanPlanConfig::dwell_msandSenseWindowcarries, and arming interacts with the NHM read on the Jaguar families (which spoils the same engine — whatdwell_executorpins). So: gate + dwell-sized arm + a selftest arm for the no-energy backend, thentests/chanscout_stress.shon an RTL8733BU.2. The other families'
Stop()do not forget an armed window#440 resets the busy window in the RTL8733B
Stop(). Jaguar1/2/3Stop()do not, and none of them clears the flag theirwith_ccxgates on, so aGetChannelBusy()straight afterStop()reads CCX registers on a deinitialised chip. Out of contract, but the asymmetry is worth closing the same way.3. Bursty-load claim is untestable on a backend without a sampled path
tests/busy_window_probe.shskips the armed-vs-sampled comparison whererx_energy_okis false. On the RTL8733B the 240 ms window against a 50/450 ms burst reads 0–19% per window (mean 6–10%, spread 19). If a bursty claim is wanted there, it needs a window longer than the burst period or a many-window mean, not the current five reads.