From 1fe63257a11fa666645ef2852a617d53c1968770 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Thu, 19 Mar 2026 19:59:52 +0100 Subject: [PATCH] [fix](trx-ftx): align FT2 decoder with Fortran reference thresholds Remove normalize_llr which was undoing the scalefac=2.83 scaling, causing LLRs to be 2.83x too small for the BP+OSD decoder. Align sync thresholds with reference: coarse 0.50->0.40, decode 0.65->0.55, sync quality 10->9, maxosd 3->4. Revert norm_sqr back to norm in bitmetrics since the metric difference is nonlinear. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Stan Grams --- src/decoders/trx-ftx/src/decode.rs | 26 ---------------------- src/decoders/trx-ftx/src/ft2/bitmetrics.rs | 2 +- src/decoders/trx-ftx/src/ft2/mod.rs | 13 +++++------ 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/src/decoders/trx-ftx/src/decode.rs b/src/decoders/trx-ftx/src/decode.rs index e1c0f49..8599087 100644 --- a/src/decoders/trx-ftx/src/decode.rs +++ b/src/decoders/trx-ftx/src/decode.rs @@ -521,32 +521,6 @@ fn ft2_extract_logl_seq( } } -/// Normalize LLR array by dividing by standard deviation, then optionally -/// scaling to a target variance. -/// -/// If `target_variance` is `Some(v)`, the output has variance ≈ v. -/// If `None`, the output has unit variance (σ = 1). -pub(crate) fn normalize_llr(log174: &mut [f32; FTX_LDPC_N], target_variance: Option) { - let mut sum = 0.0f32; - let mut sum2 = 0.0f32; - for &v in log174.iter() { - sum += v; - sum2 += v * v; - } - let inv_n = 1.0 / FTX_LDPC_N as f32; - let variance = (sum2 - sum * sum * inv_n) * inv_n; - if variance <= 1e-12 { - return; - } - let scale = match target_variance { - Some(tv) => (tv / variance).sqrt(), - None => 1.0 / variance.sqrt(), - }; - for v in log174.iter_mut() { - *v *= scale; - } -} - /// Verify CRC of a 174-bit plaintext and build an FtxMessage. /// /// `plain174`: decoded LDPC codeword (174 bits, each 0 or 1). diff --git a/src/decoders/trx-ftx/src/ft2/bitmetrics.rs b/src/decoders/trx-ftx/src/ft2/bitmetrics.rs index e9d47f1..f363e5f 100644 --- a/src/decoders/trx-ftx/src/ft2/bitmetrics.rs +++ b/src/decoders/trx-ftx/src/ft2/bitmetrics.rs @@ -134,7 +134,7 @@ impl BitMetricsWorkspace { } _ => Complex32::new(0.0, 0.0), }; - let coherent = sum.norm_sqr(); + let coherent = sum.norm(); for ib in 0..=ibmax { if ((i >> (ibmax - ib)) & 1) != 0 { diff --git a/src/decoders/trx-ftx/src/ft2/mod.rs b/src/decoders/trx-ftx/src/ft2/mod.rs index 57ddbe9..73890e9 100644 --- a/src/decoders/trx-ftx/src/ft2/mod.rs +++ b/src/decoders/trx-ftx/src/ft2/mod.rs @@ -16,7 +16,7 @@ pub mod sync; use num_complex::Complex32; use realfft::RealFftPlanner; -use crate::decode::{normalize_llr, verify_crc_and_build_message, FtxMessage}; +use crate::decode::{verify_crc_and_build_message, FtxMessage}; use crate::protocol::*; use bitmetrics::BitMetricsWorkspace; @@ -428,7 +428,7 @@ impl Ft2Pipeline { idf += 3; } - if best_score < 0.50 { + if best_score < 0.40 { continue; } @@ -452,7 +452,7 @@ impl Ft2Pipeline { } } - if best_score < 0.50 { + if best_score < 0.40 { continue; } @@ -516,7 +516,7 @@ impl Ft2Pipeline { } } - if best_score < 0.65 { + if best_score < 0.55 { return None; } @@ -575,7 +575,7 @@ impl Ft2Pipeline { 0 }; } - if sync_qual < 10 { + if sync_qual < 9 { return None; } @@ -628,7 +628,6 @@ impl Ft2Pipeline { break; } let mut log174 = llr_passes[pass]; - normalize_llr(&mut log174, None); let mut message91 = [0u8; FTX_LDPC_K]; let mut cw = [0u8; FTX_LDPC_N]; @@ -639,7 +638,7 @@ impl Ft2Pipeline { osd::ft2_decode174_91_osd( &mut log174, FTX_LDPC_K, - 3, + 4, 3, &mut apmask, &mut message91,