[fix](trx-ft8): add OSD-1/OSD-2 to FT2 LDPC decode path
Diagnostic logging showed the FT2 BP/SP decoders consistently reach 1-8 residual parity errors rather than zero — the LLRs are correct in direction but LDPC belief propagation stalls just short of convergence. Add ft2_osd_decode() implementing Ordered Statistics Decoding orders 1 and 2: after the five-pass BP/SP loop fails, sort the 174 codeword bits by |LLR| ascending and trial-flip single bits (OSD-1, always) or all pairs of the 50 least-reliable bits (OSD-2, when the remaining error count is <= 4). Each trial costs one O(83) parity check; worst-case overhead is ~1300 checks per candidate, negligible next to the 5 x 30-iteration BP/SP passes already performed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -913,6 +913,81 @@ static bool ft2_extract_bitmetrics_raw(const float complex* signal, float bitmet
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ft2_ldpc_check(const uint8_t* codeword)
|
||||
{
|
||||
int errors = 0;
|
||||
for (int m = 0; m < FTX_LDPC_M; ++m)
|
||||
{
|
||||
uint8_t x = 0;
|
||||
for (int i = 0; i < kFTX_LDPC_Num_rows[m]; ++i)
|
||||
x ^= codeword[kFTX_LDPC_Nm[m][i] - 1];
|
||||
if (x != 0)
|
||||
++errors;
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
// OSD-1 / OSD-2 trial decoder.
|
||||
// Tries flipping each of the K least-reliable bits (OSD-1), and all pairs when
|
||||
// nharderror <= 4 (OSD-2). On success cw holds the corrected codeword.
|
||||
// The caller must verify the message (CRC) separately.
|
||||
static bool ft2_osd_decode(uint8_t* cw, const float* log174, int nharderror)
|
||||
{
|
||||
const int K = 50;
|
||||
if (nharderror < 1 || nharderror > 8)
|
||||
return false;
|
||||
|
||||
// Find K indices with smallest |LLR| (least reliable), via partial selection.
|
||||
int sel[50];
|
||||
bool used[FTX_LDPC_N];
|
||||
memset(used, 0, sizeof(used));
|
||||
for (int j = 0; j < K; ++j)
|
||||
{
|
||||
int best = -1;
|
||||
float best_r = INFINITY;
|
||||
for (int i = 0; i < FTX_LDPC_N; ++i)
|
||||
{
|
||||
if (!used[i] && fabsf(log174[i]) < best_r)
|
||||
{
|
||||
best_r = fabsf(log174[i]);
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
if (best < 0)
|
||||
break;
|
||||
sel[j] = best;
|
||||
used[best] = true;
|
||||
}
|
||||
|
||||
// OSD-1: flip each of the K least-reliable bits independently.
|
||||
for (int j = 0; j < K; ++j)
|
||||
{
|
||||
cw[sel[j]] ^= 1;
|
||||
if (ft2_ldpc_check(cw) == 0)
|
||||
return true;
|
||||
cw[sel[j]] ^= 1;
|
||||
}
|
||||
|
||||
// OSD-2: try all pairs from the K least-reliable bits (only for small error counts).
|
||||
if (nharderror <= 4)
|
||||
{
|
||||
for (int j = 0; j < K; ++j)
|
||||
{
|
||||
cw[sel[j]] ^= 1;
|
||||
for (int l = j + 1; l < K; ++l)
|
||||
{
|
||||
cw[sel[l]] ^= 1;
|
||||
if (ft2_ldpc_check(cw) == 0)
|
||||
return true;
|
||||
cw[sel[l]] ^= 1;
|
||||
}
|
||||
cw[sel[j]] ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ft2_pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packed[])
|
||||
{
|
||||
int num_bytes = (num_bits + 7) / 8;
|
||||
@@ -1318,6 +1393,8 @@ static bool ft2_decode_hit(
|
||||
|
||||
bool ok = false;
|
||||
uint8_t cw[FTX_LDPC_N] = { 0 };
|
||||
int global_best_errors = FTX_LDPC_M;
|
||||
int global_best_pass = 0;
|
||||
for (int pass = 0; pass < 5 && !ok; ++pass)
|
||||
{
|
||||
float log174[FTX_LDPC_N];
|
||||
@@ -1364,6 +1441,12 @@ static bool ft2_decode_hit(
|
||||
}
|
||||
}
|
||||
|
||||
if (nharderror < global_best_errors)
|
||||
{
|
||||
global_best_errors = nharderror;
|
||||
global_best_pass = pass;
|
||||
}
|
||||
|
||||
if (pass_diag)
|
||||
{
|
||||
pass_diag->ntype[pass] = ntype;
|
||||
@@ -1371,6 +1454,18 @@ static bool ft2_decode_hit(
|
||||
pass_diag->dmin[pass] = dmin;
|
||||
}
|
||||
}
|
||||
|
||||
// OSD-1 / OSD-2: when BP/SP leave only a few parity errors, try flipping
|
||||
// the least-reliable bits to bridge the gap.
|
||||
if (!ok && global_best_errors <= 8)
|
||||
{
|
||||
float osd_log174[FTX_LDPC_N];
|
||||
memcpy(osd_log174, llr_passes[global_best_pass], sizeof(osd_log174));
|
||||
ft2_normalize_log174(osd_log174);
|
||||
if (ft2_osd_decode(cw, osd_log174, global_best_errors) && ft2_unpack_message(cw, message))
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!ok && fail_stage)
|
||||
*fail_stage = FT2_FAIL_LDPC;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user