Skip to content

Commit be8d1e0

Browse files
secupclaude
andcommitted
fix(snr): SNR-SANITY exclusion requires >=3-sample physical distribution — single-snapshot disagreement cannot blind the entry pick (rig F238/F239)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019btwFxDt7D19SZSqPYvZnk
1 parent 57aec9d commit be8d1e0

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

docs/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,21 @@ This log tracks all bug fixes and behavioral changes to prevent re-doing work du
1010

1111
---
1212

13+
## 2026-07-13 — fix(snr): SNR-SANITY attribution threshold — the gate may only exclude with a ≥3-sample physical distribution (rig F238/F239: single-snapshot exclusion left entries BLIND at 15.0/none)
14+
15+
At the handshake the physical ring holds 1-2 samples in an unknown fade state.
16+
F238/F239 (MPG@20): the connect frame's reading tripped the gate against that
17+
single snapshot, was excluded from rate selection, and the entry was picked
18+
with NO measurement at all — the protocol default 15.0 dB / source NONE →
19+
blind conservative DQPSK fallback (2 of 3 campaign entries). A snapshot cannot
20+
convict a meter: the exclusion now requires the same ≥3-sample trust bar the
21+
distribution display uses; below it the gate is silent. The F236 busy-channel
22+
detection (ring well-populated) is unaffected.
23+
24+
Verified: ctest 85/85; good@20 PASS 1600 bps.
25+
26+
---
27+
1328
## 2026-07-13 — fix(snr): LTS glitch guard — a synced frame at sub-0 dB is a measurement fault, not channel state (rig F237: meter collapsed to −9.2 at a 20 dB channel)
1429

1530
The 07-10 estimation-noise debias floored at 0.01× when a glitched LTS pair

src/gui/modem/streaming_sync_acquisition.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,19 @@ void StreamingDecoder::populateDecodeMetrics(DecodeResult& result, bool is_ofdm,
303303
// −0.66 dB noise-ref residual + both estimators' per-frame variance;
304304
// tighten alongside the §2 residual fix. This exact invariant caught
305305
// the pre-recalibration OFDM optimism from a single operator log line.
306+
// ATTRIBUTION THRESHOLD (rig F238, 2026-07-13): the gate may only
307+
// EXCLUDE a reading when its physical reference is a DISTRIBUTION
308+
// (>=3 ring samples — the display's own trust bar), not a single
309+
// snapshot. At the handshake the ring holds 1-2 samples in an unknown
310+
// fade state; excluding the connect frame's ONLY reading left the
311+
// entry pick blind (protocol default 15.0/source-none -> DQPSK
312+
// fallback). Single-sample disagreement is logged by the WARN below
313+
// when it trips with n>=3; below that, silence — a snapshot cannot
314+
// convict a meter.
315+
float ph_mean = 0.0f, ph_spread = 0.0f;
316+
const size_t ph_n = physicalSnrStats(ph_mean, ph_spread);
306317
if (result.snr_source == SNRSource::MCDPSK_IN_BAND &&
318+
ph_n >= 3 &&
307319
last_physical_snr_valid_.load() &&
308320
result.snr_db > last_physical_snr_db_.load() + 2.0f) {
309321
LOG_MODEM(WARN,

0 commit comments

Comments
 (0)