[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 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-19 19:59:52 +01:00
parent 9c9026e7ca
commit 1fe63257a1
3 changed files with 7 additions and 34 deletions
-26
View File
@@ -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<f32>) {
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. /// Verify CRC of a 174-bit plaintext and build an FtxMessage.
/// ///
/// `plain174`: decoded LDPC codeword (174 bits, each 0 or 1). /// `plain174`: decoded LDPC codeword (174 bits, each 0 or 1).
+1 -1
View File
@@ -134,7 +134,7 @@ impl BitMetricsWorkspace {
} }
_ => Complex32::new(0.0, 0.0), _ => Complex32::new(0.0, 0.0),
}; };
let coherent = sum.norm_sqr(); let coherent = sum.norm();
for ib in 0..=ibmax { for ib in 0..=ibmax {
if ((i >> (ibmax - ib)) & 1) != 0 { if ((i >> (ibmax - ib)) & 1) != 0 {
+6 -7
View File
@@ -16,7 +16,7 @@ pub mod sync;
use num_complex::Complex32; use num_complex::Complex32;
use realfft::RealFftPlanner; 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 crate::protocol::*;
use bitmetrics::BitMetricsWorkspace; use bitmetrics::BitMetricsWorkspace;
@@ -428,7 +428,7 @@ impl Ft2Pipeline {
idf += 3; idf += 3;
} }
if best_score < 0.50 { if best_score < 0.40 {
continue; continue;
} }
@@ -452,7 +452,7 @@ impl Ft2Pipeline {
} }
} }
if best_score < 0.50 { if best_score < 0.40 {
continue; continue;
} }
@@ -516,7 +516,7 @@ impl Ft2Pipeline {
} }
} }
if best_score < 0.65 { if best_score < 0.55 {
return None; return None;
} }
@@ -575,7 +575,7 @@ impl Ft2Pipeline {
0 0
}; };
} }
if sync_qual < 10 { if sync_qual < 9 {
return None; return None;
} }
@@ -628,7 +628,6 @@ impl Ft2Pipeline {
break; break;
} }
let mut log174 = llr_passes[pass]; let mut log174 = llr_passes[pass];
normalize_llr(&mut log174, None);
let mut message91 = [0u8; FTX_LDPC_K]; let mut message91 = [0u8; FTX_LDPC_K];
let mut cw = [0u8; FTX_LDPC_N]; let mut cw = [0u8; FTX_LDPC_N];
@@ -639,7 +638,7 @@ impl Ft2Pipeline {
osd::ft2_decode174_91_osd( osd::ft2_decode174_91_osd(
&mut log174, &mut log174,
FTX_LDPC_K, FTX_LDPC_K,
3, 4,
3, 3,
&mut apmask, &mut apmask,
&mut message91, &mut message91,