Skip to content

V2 Tx: apply SSB BPF to data and EOO uniformly, verification procedure clarification - #80

Open
drowe67 wants to merge 7 commits into
mainfrom
dr-tx-bpf
Open

drowe67 wants to merge 7 commits into
mainfrom
dr-tx-bpf

Conversation

@drowe67

@drowe67 drowe67 commented Sep 4, 2026

Copy link
Copy Markdown
Owner

tx2.py now applies SSB BPF consistently across Tx signal and EOO. Addresses EOO testing #79 and making it consistent with C Tx. Update V2 test report.

Companion PR on rade_c (TX-side filter implementation) to follow.

Edits to verification procedure to emphasise use of rade_c rather than using Python for reference loss/features.

radae_v2.py's RADEv2Transmitter (used by tx2.py, the streaming reference
rade_c ports) previously used a different, narrower carrier-bandwidth-derived
filter than inference.py's actual SSB radio emulation (300-2700Hz, matching
training), and never filtered the EOO frame at all. Both now use the same
300-2700Hz filter, applied continuously across data and EOO through one
persistent filter state -- a real SSB radio's front end doesn't distinguish
data samples from EOO ones.

tx2.py's --ssb_bpf flipped to default-on (--no_bpf added as opt-out,
matching the existing --no_auxdata/--no_eoo pattern), since this is the
filter the model was actually trained under. inference.py's own default is
left untouched; --ssb_bpf added explicitly to the v2_tx2 ctest to keep it
matched now that tx2.sh defaults to filtered.

Also replaces rade_c_v2_rx_eoo (which used inference.py's --end_of_over_v2
splice -- AWGN-only on the EOO tail regardless of channel flags, and never
SSB-filtered) with a tx2.py -> ch -> C rx stack that genuinely fades/filters
the whole continuous data+EOO stream, plus MPP variants for both tx2.py and
the C Tx.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzNQqmYXwUStAMrN4a5GFy
drowe67 and others added 4 commits September 5, 2026 08:47
tx2.py is the streaming reference (what rade_c ports, what the new
default-on SSB BPF applies to for both data and EOO); inference.py's V2
path never filtered EOO and used a different, non-canonical filter
definition. V1 stays on inference.sh -- untouched.

tx2.py is Tx-only, so runs rx2.sh separately on the clean, untransmitted
output to get the genie decode reference, same pattern already used for
the --v2_c (C) branch.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzNQqmYXwUStAMrN4a5GFy
Adds a usage example demonstrating the C tools (--v2_c) path and the
requirement to match --v2_c on both Tx and Rx to get a valid loss
comparison. Also formats the V1/V2 loss output into an aligned
Target/Measured table instead of raw loss.py passthrough lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzNQqmYXwUStAMrN4a5GFy
In practice every real integration uses rade_c, not pure Python, so the
verification procedure now assumes and demonstrates the rade_c library
throughout rather than treating it as a secondary case:

- Scope: states the rade_c integration assumption up front.
- Requirements: RX-only reference transmitter is rade_tx_wav, not tx2.py.
- Step 1 (baseline): uses rade_tx_wav/rade_rx_wav instead of tx2.py/rx2.py.
- RX-only worked example: simplified, no longer needs the f32toint16/sox
  conversion dance since rade_tx_wav already outputs a WAV directly.
- Cross-referenced Step 1 to the existing Worked Example section (already
  rade_c-based) for clip-value guidance and the --compare pass/fail
  pattern, rather than duplicating that content.
- Refreshed all baseline/example loss numbers and the two plot PNGs
  against the current build (0.113/0.082 -> 0.111/0.079) -- the old
  values had gone stale relative to the doc's own "don't use a cached
  value" guidance.

README.md's verification section trimmed to a short pointer at
verification_procedure.md, removing a duplicated baseline command block
that would otherwise need updating in two places every time the model
or baseline shifts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzNQqmYXwUStAMrN4a5GFy
@drowe67 drowe67 changed the title V2 Tx: apply SSB BPF to data and EOO uniformly, default on V2 Tx: apply SSB BPF to data and EOO uniformly, verification procedure clarification Sep 6, 2026
@drowe67

drowe67 commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

@tmiw @peterbmarks - some tweaks to the Verification procedure. Key point is we need to obtain reference features and loss from `-f' option of C Tx tools when testing using C Rx, so use of Python tools for reference loss and feature files has been removed. You may be doing this already.

@tmiw

tmiw commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

@tmiw @peterbmarks - some tweaks to the Verification procedure. Key point is we need to obtain reference features and loss from `-f' option of C Tx tools when testing using C Rx, so use of Python tools for reference loss and feature files has been removed. You may be doing this already.

Quick question: would running the Linux rade_rx_wav / rade_tx_wav, grabbing the loss figure there and using that for testing the Windows version of the app be valid? Or do we need to update rade_c to build those applications on Windows and use the Windows .exe files to generate the reference loss? If the latter, I can create a rade_c issue and/or PR to discuss further.

@drowe67

drowe67 commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

Quick question: would running the Linux rade_rx_wav / rade_tx_wav, grabbing the loss figure there and using that for testing the Windows version of the app be valid? Or do we need to update rade_c to build those applications on Windows and use the Windows .exe files to generate the reference loss? If the latter, I can create a rade_c issue and/or PR to discuss further.

This is a good question. It requires some careful thought and a little investigation:

  1. rade_c cross compiled to Windows should perform almost exactly the same as a Linux build, to within numerical precision errors. If this is indeed the case, then generating the reference loss figure from the Linux tools should be equivalent of doing the same thing on Windows.
  2. However I think it's worth testing this assumption. If there is a difference when running the same tools on Linux & Windows, it will mess up the verification procedure under Windows.
  3. I'll build rade_rx_wav / rade_tx_wav for Win32 and test locally using wine. Stand by.

@drowe67

drowe67 commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

@tmiw - Claude manged to get a Win32 build running and do some x-checks. Better than I expected.

Metric Result
Loss (Linux) 0.079
Loss (Windows/Wine) 0.079 — identical
TX/encoder features bit-exact identical (max diff 0.0)
RX/decoder features tiny difference (max 0.0029, RMS 0.00017)
Raw tx.wav bytes differ slightly (ULP-level libm differences — sinf/cosf in MinGW vs glibc — in OFDM waveform synthesis, downstream of the bit-exact features)

So, assuming your Windows build is the same as mine, the answer to your original question is yes, you can use the Linux loss target for your Windows tests.

You should extract the tx/rx feature files from your Windows applications as they run.

Claude wanted me to mention: this build needs libssp-0.dll

Question: would you like me to push the Windows build code to this or another PR, with some build instructions? That way we have a guaranteed loss-equivalent Windows DLL build for rade_c (e.g. for use with freedv-gui).

Wires up rade_c's new rade_get_stats()-based diagnostics (see matching
rade_c commit) into the --v2_c path: writes state/delta_hat/delta_hat_g/
freq_offset/gain/snr_est per OFDM symbol and calls the same plot_v2_logs()
Octave plot the Python path already produced, closing the "no plot for
the C path yet" gap. Also adds -v 2 to the rade_rx_wav call, giving V2
the same one-line-per-symbol log in *_report.txt that V1 already has by
default (V1/V2 receivers differ in their default verbosity: rade_rx.c
defaults verbose=2, rade_rx_v2 defaults to 0).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzNQqmYXwUStAMrN4a5GFy
@tmiw

tmiw commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

I'd say another PR.

BTW Windows freedv-gui is built via LLVM MinGW and the CMake toolchain files here. Not sure if that matters for this purpose.

test/eoo_detect_prob.sh, test/eoo_false_prob.sh, and the v2_eoo_mpp_high/
low ctests (CMakeLists.txt) converted from inference.py's --end_of_over_v2
--g_file splice to tx2.py -> ch -> rx2.py. The old splice applied a fixed
AWGN-only tail onto the already-channel-processed signal, so multipath
fading and the TX SSB BPF never actually reached the EOO frame regardless
of --channel/filter flags -- only AWGN-on-EOO was ever genuinely tested.
tx2.py's continuous data+EOO stream through ch fades/filters both for
real. CLI changed --EbNodB -> --No (ch's native parameter; no fixed
conversion between the two exists). eoo_false_prob.sh needs --no_eoo
explicitly now that tx2.py defaults EOO on, and a fading-offset bound fix
(never accounted for needing wav_dur of remaining fading-file data after
the offset). Both scripts now surface SNR3k per-trial and as a run mean.

test/eoo_report_data.sh: runs the corrected detection-probability sweep
(AWGN + MPP, N=20, --No values from quick single-trial calibration) plus
the false-trigger sweep, writing doc/v2_test_report_eoo.tex. Fixed a row-
ordering bug found while reviewing: rows were written in sweep order, not
sorted, so SNR ran descending only for the leading clean-reference point
then ascending for the three real test points -- now sorted (channel,
-SNR) for a consistent descending order matching report convention.

doc/v2_test_report.tex: EOO section now \input{}s the real N=20 table
instead of the old hand-written --EbNodB-based one. Updated prose to
describe the corrected methodology and replace a stale "sharp knee at
~1dB" claim (no longer accurate -- AWGN now declines gradually from 65%
at 4.3dB to 5% at 0.3dB) with the actual curve. False-trigger paragraph
now cites the new N=20 zero-false-trigger results alongside the existing
librispeech.wav finding. Sample command line fixed (--EbNodB -> --No).

Also: removed a stale "SSB BPF bandwidth: ..." debug print from
radae.py's constructor (radae/radae.py) -- useful during the BPF
investigation, just noise now that it's settled.

Two follow-up questions identified reviewing the new numbers, to become
GitHub Issues: (1) AWGN EOO detection is noticeably worse than the old
table even though AWGN was supposedly always genuinely tested -- possible
cause is the TX BPF added for Issue #2, which postdates the old
characterization and could be affecting the EOO frame's time-domain
sparsity signature that detect_eoo() relies on. (2) The false-trigger
sweep only tests two fixed, moderate-SNR points and can't distinguish a
genuinely near-zero false-alarm rate from a threshold that's simply not
being stressed -- proper characterization needs a P(detect) vs.
P(false-alarm) tradeoff curve, and should test whether false triggers are
data-content-driven (as recalled from development) rather than purely
SNR-driven before assuming SNR is even the right variable to sweep.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PzNQqmYXwUStAMrN4a5GFy
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.

2 participants