Skip to content

test(bpsk): the #1363 gate arms, their controls, and the estimator that was wrong - #1430

Merged
dc0sk merged 1 commit into
mainfrom
investigate/1363-lock-x-dominance
Sep 22, 2026
Merged

dc0sk merged 1 commit into
mainfrom
investigate/1363-lock-x-dominance

Conversation

@dc0sk

@dc0sk dc0sk commented Sep 22, 2026

Copy link
Copy Markdown
Owner

Apparatus for #1363. Test-only; no production code changes. It lands the measurements behind the
last three comments on that issue so the numbers stay reproducible, and it is what produced #1428
(the follow-up task) and #1429 (a live production defect).

mod carrier_dip_tiebreak gains the (lock × dominance) 2×2, the candidate gate arms with
matched-duty controls, off-band cells (pure Doppler and AWGN down to #821's σ = 0.9), a
least-squares tap estimator, and two new default-run pins.

What it established

  • This issue's own predicate is retired. |g_cur| < |g_next| fires 3.9 % / 4.9 % where the
    penalty lives and 48 % / 84 % where it does not, and costs frames in every cell — worse than
    the shipped arm, not merely useless.
  • The mechanism in the band where frames are lost is a sign-inverted effective ISI with an intact
    DC term
    — delayed-ray-dominant is identical to "the sub-dominant echo is a pre-echo". The
    deep-null tie-break is a different regime, one bin down.
  • sign_dd recovers the fade gap (32/20/15 frames lost at 8/12/16 dB against the shipped arm's
    45/26/21) and costs 37 of 96 frames at σ = 0.9, through 7.7 % noise-driven misfire against the
    genie's 0.0 %. Visible only because the off-band cells exist; the in-band table could not see it.
  • "β estimation is refuted by the mechanism" is withdrawn. With exact taps, genie complex-β is
    the best arm in every cell including −3.3 dB AWGN (25 against 46). That 2026-09-13 note refuted a
    deep-null statement.

The instrument was wrong before any of it

The original correlation tap estimator has 1/√41 ≈ 0.16 of relative self-noise. On a clean frame
with no channel, no noise and genie symbols it put Re(g_next/g_cur) at p05 0.043 / p50 0.285 /
p95 0.532 against a truth of 0.308, and fired the sign predicate 4.3 % of the time against
least-squares' 0.0 %.

So the "genie" arms were genie symbols through a noisy estimator, and my doc comment calling one
"a CEILING, not an achievable receiver" was backwards — with that estimator it was a floor.
Least-squares is now the default. The correlation estimator is kept, renamed
estimate_taps_correlation and documented with its measured self-noise, because the comparison
between them is the finding.

Two defects found while landing it

  1. The selector defaulted to the defective estimator — least-squares was gated behind an opt-in
    env var, so correlation ran unless you asked otherwise. Flipped.
  2. Then the banner lied about which one it ran. After the flip it still printed
    estimator = correlation while executing least-squares, because the label read a different env
    var than the selector. A label that can disagree with the instrument it names is exactly the
    defect this probe exists to catch. Both now read one switch, verified in both positions.

The pins are what make the tables trustworthy

Four run in the default suite:

  • the_composed_arm_matches_the_shipped_demodulator — byte identity against bpsk_demodulate
  • the_same_seed_reproduces_the_same_fading_realisation
  • the_ray_split_agrees_with_the_envelope — sabotage-verified
  • the_variable_canceller_reproduces_the_shipped_one (new) — the per-symbol complex-β canceller
    must be cancel_crossfade_isi at β = 1/3 to 1e-5, or every arm below would be measured against a
    re-implementation rather than the product

Side finding, resolved

The clean-frame β of 0.308 against the shipped CROSSFADE_ISI_BETA = 1/3 is the exact discrete
composite at n = 32 (g_c 1.0400, g_n 0.3200). 1/3 is the continuous integral. Same shape as the
8PSK crossfade fix, where β is computed from the window rather than assumed. Residual ISI
~0.027/symbol — no action on its own, but if a β path ever ships, derive it from the window.

Stated limits

Every number is bad bytes over the 200 payload bytes of a 255-byte wire frame, no scrambler, RS
never actually run — a lower bound for all arms alike and not a decode rate. The engine-level
coded A/B is #1428's step 1, and is the cheapest thing that could kill the whole direction.

Test results

suites=339 tests_passed=2578 tests_failed=0
GATE: PASS bb9d74b53e3e63dade9f7cb4bc36fe96a2af8b66 clean 20260922T082654Z

All 13 steps ok. 2578 tests against main's 2577 — the one new pin.

Refactors: CAP-12
Verification-objective: a probe's reported instrument must be the instrument it ran, and an
alternative transform must be measured against the shipped one rather than a re-implementation

Review: none — this lands apparatus whose findings and write-ups were each reviewed before the
corresponding issue comments were posted; the design verdict it produced is recorded on #1428.

🤖 Generated with Claude Code

https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6

…at was wrong

Test-only; no production code changes. `mod carrier_dip_tiebreak` gains the
(lock x dominance) 2x2, the candidate gate arms with matched-duty controls, off-band
cells (pure Doppler and AWGN down to #821's sigma 0.9), and two new default-run pins.

FINDINGS, posted to #1363:
  * `|g_cur| < |g_next|` — that issue's own predicate — is RETIRED: fires 3.9%/4.9%
    where the penalty lives, 48%/84% where it does not, and costs frames in every cell.
  * The mechanism in the band where frames are lost is a SIGN-INVERTED effective ISI
    with an intact DC term (delayed-dominant == the sub-dominant echo is a pre-echo),
    not the deep-null tie-break, which is a different regime one bin down.
  * `sign_dd` recovers the fade gap (32/20/15 vs shipped 45/26/21) and costs 37 of 96
    frames at sigma 0.9 through 7.7% noise-driven misfire — visible ONLY because the
    off-band cells were added.
  * "beta estimation is refuted by the mechanism" is WITHDRAWN: with exact taps, genie
    complex-beta is the best arm in every cell including -3.3 dB AWGN (25 vs 46).

THE INSTRUMENT WAS WRONG FIRST. The original correlation tap estimator has 1/sqrt(41)
~ 0.16 of self-noise: on a CLEAN frame with no channel, no noise and genie symbols it
put Re(g_next/g_cur) at p05 0.043 / p50 0.285 / p95 0.532 against a truth of 0.308, and
fired the sign predicate 4.3% of the time against least-squares' 0.0%. The "genie" arms
were genie SYMBOLS through a noisy ESTIMATOR, and a doc comment calling one of them "a
CEILING" was backwards — it was a floor. Least-squares is now the default; the
correlation estimator is kept, renamed and documented as the defective control, because
the comparison between them is itself the finding.

A MISLABELLING CAUGHT WHILE LANDING THIS: after flipping the default the banner still
printed `estimator = correlation` while running least-squares, because it read a
different env var than the selector. A label that can disagree with the instrument it
names is the defect this probe exists to catch. Both now read one switch, verified in
both positions.

The pin that makes the arms mean anything is
`the_variable_canceller_reproduces_the_shipped_one`: the per-symbol complex-beta
canceller must BE `cancel_crossfade_isi` at beta = 1/3, or every arm would be measured
against a re-implementation instead of the product.

Side finding resolved: the clean-frame beta of 0.308 against the shipped 1/3 is the
exact DISCRETE composite at n=32; 1/3 is the continuous integral. Same shape as the
8PSK crossfade fix. Residual ISI ~0.027/symbol, no action on its own.

Limits: every number is bad bytes over 200 payload bytes of a 255-byte wire frame, no
scrambler, RS never run — a lower bound for all arms alike and NOT a decode rate. The
engine-level coded A/B is #1428's step 1.

Refactors: CAP-12
Verification-objective: a probe's reported instrument must be the instrument it ran,
and an alternative transform must be measured against the shipped one rather than a
re-implementation of it

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6
@dc0sk
dc0sk merged commit 0242610 into main Sep 22, 2026
11 checks passed
@dc0sk
dc0sk deleted the investigate/1363-lock-x-dominance branch September 22, 2026 12:07
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