[debug](trx-rs): trace FT2 decode pass outcomes

This commit is contained in:
2026-03-14 21:22:29 +01:00
parent f43f3a27fd
commit 2ad555efd1
+143 -65
View File
@@ -188,6 +188,13 @@ typedef struct
float best_sync_score;
} ft2_scan_stats_t;
typedef struct
{
int ntype[5];
int nharderror[5];
float dmin[5];
} ft2_pass_diag_t;
typedef enum
{
FT2_FAIL_NONE = 0,
@@ -660,39 +667,22 @@ static void ft2_extract_signal_region(const float complex* input, int input_len,
}
}
static void ft2_extract_logl_sequence_raw(const float complex symbols[4][FT2_FRAME_SYMBOLS], int start_sym, int n_syms, float* metrics)
static void ft2_normalize_metric(float* metric, int count)
{
const int n_bits = 2 * n_syms;
const int n_sequences = 1 << n_bits;
for (int bit = 0; bit < n_bits; ++bit)
float sum = 0.0f;
float sum2 = 0.0f;
for (int i = 0; i < count; ++i)
{
float max_zero = -INFINITY;
float max_one = -INFINITY;
for (int seq = 0; seq < n_sequences; ++seq)
{
float complex sum = 0.0f;
for (int sym = 0; sym < n_syms; ++sym)
{
int shift = 2 * (n_syms - sym - 1);
int dibit = (seq >> shift) & 0x3;
sum += symbols[kFT4_Gray_map[dibit]][start_sym + sym];
}
float strength = cabsf(sum);
int mask_bit = n_bits - bit - 1;
if (((seq >> mask_bit) & 0x1) != 0)
{
if (strength > max_one)
max_one = strength;
}
else
{
if (strength > max_zero)
max_zero = strength;
}
}
metrics[bit] = max_one - max_zero;
sum += metric[i];
sum2 += metric[i] * metric[i];
}
float mean = sum / count;
float variance = (sum2 / count) - (mean * mean);
float sigma = (variance > 0.0f) ? sqrtf(variance) : sqrtf(fmaxf(sum2 / count, 0.0f));
if (sigma <= 1.0e-6f)
return;
for (int i = 0; i < count; ++i)
metric[i] /= sigma;
}
static bool ft2_extract_bitmetrics_raw(const float complex* signal, float bitmetrics[2 * FT2_FRAME_SYMBOLS][3])
@@ -731,6 +721,18 @@ static bool ft2_extract_bitmetrics_raw(const float complex* signal, float bitmet
free(fft_mem);
static bool one_ready = false;
static uint8_t one_mask[256][8];
if (!one_ready)
{
for (int i = 0; i < 256; ++i)
{
for (int j = 0; j < 8; ++j)
one_mask[i][j] = ((i & (1 << j)) != 0) ? 1u : 0u;
}
one_ready = true;
}
int sync_ok = 0;
for (int group = 0; group < 4; ++group)
{
@@ -753,18 +755,66 @@ static bool ft2_extract_bitmetrics_raw(const float complex* signal, float bitmet
float metric1[2 * FT2_FRAME_SYMBOLS] = { 0 };
float metric2[2 * FT2_FRAME_SYMBOLS] = { 0 };
float metric4[2 * FT2_FRAME_SYMBOLS] = { 0 };
float s2[256];
for (int start = 0; start <= FT2_FRAME_SYMBOLS - 1; start += 1)
for (int nseq = 0; nseq < 3; ++nseq)
{
ft2_extract_logl_sequence_raw(symbols, start, 1, metric1 + (2 * start));
}
for (int start = 0; start <= FT2_FRAME_SYMBOLS - 2; start += 2)
{
ft2_extract_logl_sequence_raw(symbols, start, 2, metric2 + (2 * start));
}
for (int start = 0; start <= FT2_FRAME_SYMBOLS - 4; start += 4)
{
ft2_extract_logl_sequence_raw(symbols, start, 4, metric4 + (2 * start));
int nsym = (nseq == 0) ? 1 : (nseq == 1 ? 2 : 4);
int nt = 1 << (2 * nsym);
for (int ks = 0; ks <= FT2_FRAME_SYMBOLS - nsym; ks += nsym)
{
for (int i = 0; i < nt; ++i)
{
int i1 = i / 64;
int i2 = (i & 63) / 16;
int i3 = (i & 15) / 4;
int i4 = i & 3;
if (nsym == 1)
{
s2[i] = cabsf(symbols[kFT4_Gray_map[i4]][ks]);
}
else if (nsym == 2)
{
s2[i] = cabsf(symbols[kFT4_Gray_map[i3]][ks] + symbols[kFT4_Gray_map[i4]][ks + 1]);
}
else
{
s2[i] = cabsf(
symbols[kFT4_Gray_map[i1]][ks] +
symbols[kFT4_Gray_map[i2]][ks + 1] +
symbols[kFT4_Gray_map[i3]][ks + 2] +
symbols[kFT4_Gray_map[i4]][ks + 3]);
}
}
int ipt = 2 * ks;
int ibmax = (nsym == 1) ? 1 : (nsym == 2 ? 3 : 7);
for (int ib = 0; ib <= ibmax; ++ib)
{
float max_one = -INFINITY;
float max_zero = -INFINITY;
for (int i = 0; i < nt; ++i)
{
if (one_mask[i][ibmax - ib])
{
if (s2[i] > max_one)
max_one = s2[i];
}
else if (s2[i] > max_zero)
{
max_zero = s2[i];
}
}
if ((ipt + ib) >= 2 * FT2_FRAME_SYMBOLS)
continue;
if (nseq == 0)
metric1[ipt + ib] = max_one - max_zero;
else if (nseq == 1)
metric2[ipt + ib] = max_one - max_zero;
else
metric4[ipt + ib] = max_one - max_zero;
}
}
}
metric2[204] = metric1[204];
@@ -776,33 +826,16 @@ static bool ft2_extract_bitmetrics_raw(const float complex* signal, float bitmet
metric4[204] = metric1[204];
metric4[205] = metric1[205];
ft2_normalize_metric(metric1, 2 * FT2_FRAME_SYMBOLS);
ft2_normalize_metric(metric2, 2 * FT2_FRAME_SYMBOLS);
ft2_normalize_metric(metric4, 2 * FT2_FRAME_SYMBOLS);
for (int i = 0; i < 2 * FT2_FRAME_SYMBOLS; ++i)
{
bitmetrics[i][0] = metric1[i];
bitmetrics[i][1] = metric2[i];
bitmetrics[i][2] = metric4[i];
}
for (int metric = 0; metric < 3; ++metric)
{
float sum = 0.0f;
float sum2 = 0.0f;
for (int i = 0; i < 2 * FT2_FRAME_SYMBOLS; ++i)
{
float v = bitmetrics[i][metric];
sum += v;
sum2 += v * v;
}
float mean = sum / (2 * FT2_FRAME_SYMBOLS);
float variance = (sum2 / (2 * FT2_FRAME_SYMBOLS)) - (mean * mean);
float sigma = (variance > 0.0f) ? sqrtf(variance) : sqrtf(fmaxf(sum2 / (2 * FT2_FRAME_SYMBOLS), 0.0f));
if (sigma > 1.0e-6f)
{
for (int i = 0; i < 2 * FT2_FRAME_SYMBOLS; ++i)
{
bitmetrics[i][metric] /= sigma;
}
}
}
return true;
}
@@ -983,10 +1016,20 @@ static bool ft2_decode_hit(
float* dt_s,
float* freq_hz,
float* snr_db,
ft2_fail_stage_t* fail_stage)
ft2_fail_stage_t* fail_stage,
ft2_pass_diag_t* pass_diag)
{
if (fail_stage)
*fail_stage = FT2_FAIL_NONE;
if (pass_diag)
{
for (int i = 0; i < 5; ++i)
{
pass_diag->ntype[i] = 0;
pass_diag->nharderror[i] = -1;
pass_diag->dmin[i] = INFINITY;
}
}
const int nraw = dec->ft2_raw_len;
const int nfft2 = nraw / FT2_NDOWN;
float complex* cd2 = (float complex*)malloc(sizeof(float complex) * nfft2);
@@ -1131,6 +1174,12 @@ static bool ft2_decode_hit(
int nharderror = -1;
float dmin = 0.0f;
decode174_91_osd(log174, FTX_LDPC_K, 3, 3, apmask, message91, cw, &ntype, &nharderror, &dmin);
if (pass_diag)
{
pass_diag->ntype[pass] = ntype;
pass_diag->nharderror[pass] = nharderror;
pass_diag->dmin[pass] = dmin;
}
if (ntype != 0 && nharderror >= 0)
ok = ft2_unpack_message(cw, message);
}
@@ -1293,6 +1342,9 @@ int ft8_decoder_decode(ft8_decoder_t* dec, ft8_decode_result_t* out, int max_res
int fail_ldpc = 0;
int fail_crc = 0;
int fail_unpack = 0;
int pass_bp[5] = { 0 };
int pass_osd[5] = { 0 };
float pass_best_dmin[5] = { INFINITY, INFINITY, INFINITY, INFINITY, INFINITY };
for (int idx = 0; idx < num_hits && num_decoded < max_results; ++idx)
{
const ft2_scan_hit_t* hit = &hit_list[idx];
@@ -1301,8 +1353,18 @@ int ft8_decoder_decode(ft8_decoder_t* dec, ft8_decode_result_t* out, int max_res
float freq_hz = 0.0f;
float snr_db = -21.0f;
ft2_fail_stage_t fail_stage = FT2_FAIL_NONE;
if (!ft2_decode_hit(dec, hit, &message, &time_sec, &freq_hz, &snr_db, &fail_stage))
ft2_pass_diag_t pass_diag;
if (!ft2_decode_hit(dec, hit, &message, &time_sec, &freq_hz, &snr_db, &fail_stage, &pass_diag))
{
for (int pass = 0; pass < 5; ++pass)
{
if (pass_diag.ntype[pass] == 1)
++pass_bp[pass];
else if (pass_diag.ntype[pass] == 2)
++pass_osd[pass];
if (pass_diag.dmin[pass] < pass_best_dmin[pass])
pass_best_dmin[pass] = pass_diag.dmin[pass];
}
switch (fail_stage)
{
case FT2_FAIL_REFINED_SYNC:
@@ -1334,6 +1396,15 @@ int ft8_decoder_decode(ft8_decoder_t* dec, ft8_decode_result_t* out, int max_res
}
continue;
}
for (int pass = 0; pass < 5; ++pass)
{
if (pass_diag.ntype[pass] == 1)
++pass_bp[pass];
else if (pass_diag.ntype[pass] == 2)
++pass_osd[pass];
if (pass_diag.dmin[pass] < pass_best_dmin[pass])
pass_best_dmin[pass] = pass_diag.dmin[pass];
}
int idx_hash = message.hash % 200;
bool found_empty_slot = false;
@@ -1379,7 +1450,7 @@ int ft8_decoder_decode(ft8_decoder_t* dec, ft8_decode_result_t* out, int max_res
num_decoded++;
}
LOG(LOG_INFO,
"FT2 window: raw=%d peaks=%d hits=%d best_peak=%.3f best_sync=%.3f decoded=%d fail(sync=%d freq=%d down=%d bits=%d qual=%d ldpc=%d crc=%d unpack=%d)\n",
"FT2 window: raw=%d peaks=%d hits=%d best_peak=%.3f best_sync=%.3f decoded=%d fail(sync=%d freq=%d down=%d bits=%d qual=%d ldpc=%d crc=%d unpack=%d) pass(bp=%d/%d/%d/%d/%d osd=%d/%d/%d/%d/%d dmin=%.2f/%.2f/%.2f/%.2f/%.2f)\n",
dec->ft2_raw_len,
scan_stats.peaks_found,
scan_stats.hits_found,
@@ -1393,7 +1464,14 @@ int ft8_decoder_decode(ft8_decoder_t* dec, ft8_decode_result_t* out, int max_res
fail_sync_qual,
fail_ldpc,
fail_crc,
fail_unpack);
fail_unpack,
pass_bp[0], pass_bp[1], pass_bp[2], pass_bp[3], pass_bp[4],
pass_osd[0], pass_osd[1], pass_osd[2], pass_osd[3], pass_osd[4],
isfinite(pass_best_dmin[0]) ? pass_best_dmin[0] : -1.0f,
isfinite(pass_best_dmin[1]) ? pass_best_dmin[1] : -1.0f,
isfinite(pass_best_dmin[2]) ? pass_best_dmin[2] : -1.0f,
isfinite(pass_best_dmin[3]) ? pass_best_dmin[3] : -1.0f,
isfinite(pass_best_dmin[4]) ? pass_best_dmin[4] : -1.0f);
}
else
{