feat(round-11): |IQ| in the preview time plot + full-scale final normalization + per-tone LFM comb scaling #52

Merged
ykp merged 1 commit from round-11-features into master 2026-10-07 23:38:02 +02:00
Owner

Summary

Three operator-confirmed features (round 11, decisions locked via explicit Q&A):

  1. |IQ| in the preview time series — the result-graph time subplot now draws three traces: I (orange), Q (blue) and the new |IQ| magnitude (teal), title "Time (I/Q/|IQ|)". The hover annotation's nearest-trace logic picks up the third trace unchanged (readout reports magnitude samples too). Display decimation applies to all traces; no new settings.

  2. Final normalization: full-scale target (gain allowed) — pipeline.apply_post_chain ends with _normalize_full_scale: any peak differing from full scale is scaled by 1/peak — up OR down — under the operator's exact condition (peak > 1e-6 and abs(1 − peak) > 1e-6). Guards: near-zero signals (peak ≤ 1e-6) stay untouched (a 1/peak gain would amplify numerical noise); 1-ulp float-noise overshoots (|1−peak| ≤ 1e-6) are not renormalized. The retired scale-down-only contract (Level = % of full scale preserved in the OUTPUT) is gone by confirmed decision: per-band Level ratios still shape the INTERNAL signal, but everything written/displayed peaks at exactly 1.0. Spec §5.3.5 rewritten, §3.2 carries the caveat; the rule lives in one place (pipeline._normalize_full_scale) that the tests pin against.

  3. create_lfm_tones (k=0 periodic chirp): exact create_multi_tones tone/scale handling — the builder no longer generates the full-union uniform comb. It now synthesizes only the requested per-band tones (Fmin:spacing:Fmax per band), each nearest-snapped onto the exact fe/P period grid — no in-gap lines (empty by construction; BandIsolation no longer needed to carve k=0 gaps) — with per-band Level scales per line (Q20 ratios preserved, peak normalized to max(|scales|)), duplicates keeping the last tone's scale (fancy-assignment semantics, per the operator's overwrite choice), and the −π/4 quadratic chirp phase law in the grid position across the union span (the sweep rate is unchanged and requested-tone gaps do not distort it). Still strictly P-periodic (exact fe/P grid, length-P inverse DFT — no 2^N quantization anywhere). Spec §5.2.2 k=0 bullet rewritten.

Tests

  • test_multi_tones.py: k=0 section rewritten — requested-tones-only (inter-band gap EMPTY), per-band Level line ratios (scales 1.0/0.25 → 4× magnitude ratio), duplicate-overwrite (last scale wins), chirp-phase assertion (unit-circle-wrapped comparison against π(m̃²/N − 0.25)); period/guards/dispatch tests unchanged.
  • test_pipeline.py: pass-through pinned at peak == 1.0 (same-object contract), new boost test (0.9 → 1.0) and near-zero untouched test; all bitwise stage pins wrapped with pipeline._normalize_full_scale; CFR RMS bound widened for the boost factor.
  • test_generate.py + chirp/comb-IQ integration: "all post off" compares the file against the normalized raw output, with the no-clip bound relaxed to one int16 LSB (the peak legitimately lands at full scale).
  • test_protected_notch.py: passthrough vs the normalization rule.

Gate: 1296 passed / 64 skipped (headless UI tier), ruff check clean, mypy clean.

## Summary Three operator-confirmed features (round 11, decisions locked via explicit Q&A): 1. **|IQ| in the preview time series** — the result-graph time subplot now draws three traces: I (orange), Q (blue) and the new **|IQ| magnitude** (teal), title "Time (I/Q/|IQ|)". The hover annotation's nearest-trace logic picks up the third trace unchanged (readout reports magnitude samples too). Display decimation applies to all traces; no new settings. 2. **Final normalization: full-scale target (gain allowed)** — `pipeline.apply_post_chain` ends with `_normalize_full_scale`: **any peak differing from full scale is scaled by `1/peak` — up OR down** — under the operator's exact condition (`peak > 1e-6 and abs(1 − peak) > 1e-6`). Guards: near-zero signals (`peak ≤ 1e-6`) stay untouched (a `1/peak` gain would amplify numerical noise); 1-ulp float-noise overshoots (`|1−peak| ≤ 1e-6`) are not renormalized. The retired **scale-down-only** contract (Level = % of full scale preserved in the OUTPUT) is gone by confirmed decision: per-band Level ratios still shape the INTERNAL signal, but everything written/displayed peaks at exactly 1.0. Spec §5.3.5 rewritten, §3.2 carries the caveat; the rule lives in one place (`pipeline._normalize_full_scale`) that the tests pin against. 3. **`create_lfm_tones` (k=0 periodic chirp): exact `create_multi_tones` tone/scale handling** — the builder no longer generates the full-union uniform comb. It now synthesizes **only the requested per-band tones** (`Fmin:spacing:Fmax` per band), each nearest-snapped onto the exact `fe/P` period grid — **no in-gap lines** (empty by construction; BandIsolation no longer needed to carve k=0 gaps) — with **per-band Level scales per line** (Q20 ratios preserved, peak normalized to `max(|scales|)`), **duplicates keeping the last tone's scale** (fancy-assignment semantics, per the operator's overwrite choice), and the **−π/4 quadratic chirp phase law in the grid position across the union span** (the sweep rate is unchanged and requested-tone gaps do not distort it). Still strictly P-periodic (exact `fe/P` grid, length-P inverse DFT — no 2^N quantization anywhere). Spec §5.2.2 k=0 bullet rewritten. ## Tests - `test_multi_tones.py`: k=0 section rewritten — requested-tones-only (inter-band gap EMPTY), per-band Level line ratios (scales 1.0/0.25 → 4× magnitude ratio), duplicate-overwrite (last scale wins), chirp-phase assertion (unit-circle-wrapped comparison against `π(m̃²/N − 0.25)`); period/guards/dispatch tests unchanged. - `test_pipeline.py`: pass-through pinned at peak == 1.0 (same-object contract), new boost test (0.9 → 1.0) and near-zero untouched test; all bitwise stage pins wrapped with `pipeline._normalize_full_scale`; CFR RMS bound widened for the boost factor. - `test_generate.py` + chirp/comb-IQ integration: "all post off" compares the file against the **normalized** raw output, with the no-clip bound relaxed to one int16 LSB (the peak legitimately lands at full scale). - `test_protected_notch.py`: passthrough vs the normalization rule. **Gate**: 1296 passed / 64 skipped (headless UI tier), `ruff check` clean, `mypy` clean.
feat(round-11): |IQ| in the preview time plot + full-scale final normalization + per-tone LFM comb scaling
Some checks failed
test / test (ubuntu-latest) (pull_request) Failing after 2m18s
4964fcb8ec
Operator-confirmed round (three features):

- Preview: the time subplot gains the |IQ| magnitude trace alongside I and
  Q (title 'Time (I/Q/|IQ|)'; the hover annotation's nearest-trace logic
  covers it unchanged).
- pipeline final normalization (5.3.5): full-scale target — ANY peak that
  differs from full scale is scaled by 1/peak, up OR down (guards: peak
  <= 1e-6 untouched; |1-peak| <= 1e-6 float noise not renormalized). The
  retired scale-down-only contract (Level = % of full scale preserved in
  the OUTPUT) is gone by decision: per-band Level ratios still shape the
  INTERNAL signal. Spec 5.3.5 + 3.2 updated; pipeline._normalize_full_scale
  is the single rule the tests pin against.
- create_lfm_tones (k=0 periodic chirp) upgraded to the exact same
  tone/scale handling as create_multi_tones: ONLY the requested per-band
  tones (Fmin:spacing:Fmax, nearest-snapped onto the exact fe/P grid) —
  no in-gap lines (BandIsolation no longer needed to carve k=0 gaps) —
  with per-band Level scales per line (Q20 ratios preserved, peak ==
  max(|scales|)), duplicates keeping the LAST tone's scale (fancy-
  assignment semantics), and the -pi/4 quadratic chirp phase law in the
  grid POSITION across the union span (gaps do not distort the sweep).
  Spec 5.2.2 k=0 bullet rewritten.
- Tests: k=0 comb tests rewritten (requested-tones-only, gap empty,
  per-band level line ratios, duplicate overwrite, chirp phase via
  unit-circle wrap); pipeline normalization tests (boost, pass-through at
  peak==1.0, near-zero untouched, bitwise pins wrapped with
  _normalize_full_scale); generate/integration file comparisons vs the
  normalized raw with a one-int16-LSB clip bound; protected-notch
  passthrough vs the normalization rule.
- Gate: 1296 passed / 64 skipped (headless), ruff clean, mypy clean.
ykp merged commit 3133cd95e5 into master 2026-10-07 23:38:02 +02:00
ykp deleted branch round-11-features 2026-10-07 23:38:02 +02:00
Sign in to join this conversation.
No description provided.