[feat](trx-rs): port FT2 hybrid LDPC decoder
This commit is contained in:
@@ -107,6 +107,8 @@ static ftx_callsign_hash_interface_t hash_if = {
|
||||
.save_hash = hashtable_add,
|
||||
};
|
||||
|
||||
static bool ft2_unpack_message(const uint8_t plain174[], ftx_message_t* message);
|
||||
|
||||
// Decoder wrapper
|
||||
|
||||
typedef struct
|
||||
@@ -172,6 +174,12 @@ typedef struct
|
||||
float score;
|
||||
} ft2_raw_candidate_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int index;
|
||||
float reliability;
|
||||
} ft2_reliability_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int peaks_found;
|
||||
@@ -226,6 +234,17 @@ static int ft2_cmp_candidates_desc(const void* lhs, const void* rhs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ft2_cmp_reliability_asc(const void* lhs, const void* rhs)
|
||||
{
|
||||
const ft2_reliability_t* a = (const ft2_reliability_t*)lhs;
|
||||
const ft2_reliability_t* b = (const ft2_reliability_t*)rhs;
|
||||
if (a->reliability < b->reliability)
|
||||
return -1;
|
||||
if (a->reliability > b->reliability)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ft2_cmp_scan_hits_desc(const void* lhs, const void* rhs)
|
||||
{
|
||||
const ft2_scan_hit_t* a = (const ft2_scan_hit_t*)lhs;
|
||||
@@ -808,6 +827,135 @@ static void ft2_pack_bits(const uint8_t bit_array[], int num_bits, uint8_t packe
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t ft2_parity8(uint8_t x)
|
||||
{
|
||||
x ^= x >> 4;
|
||||
x ^= x >> 2;
|
||||
x ^= x >> 1;
|
||||
return x & 1u;
|
||||
}
|
||||
|
||||
static void ft2_encode_codeword_from_a91(const uint8_t a91[FTX_LDPC_K_BYTES], uint8_t codeword[FTX_LDPC_N])
|
||||
{
|
||||
for (int i = 0; i < FTX_LDPC_K; ++i)
|
||||
{
|
||||
codeword[i] = (a91[i / 8] >> (7 - (i % 8))) & 0x1u;
|
||||
}
|
||||
for (int i = 0; i < FTX_LDPC_M; ++i)
|
||||
{
|
||||
uint8_t nsum = 0;
|
||||
for (int j = 0; j < FTX_LDPC_K_BYTES; ++j)
|
||||
{
|
||||
nsum ^= ft2_parity8(a91[j] & kFTX_LDPC_generator[i][j]);
|
||||
}
|
||||
codeword[FTX_LDPC_K + i] = nsum & 0x1u;
|
||||
}
|
||||
}
|
||||
|
||||
static float ft2_codeword_distance(const uint8_t codeword[FTX_LDPC_N], const float log174[FTX_LDPC_N])
|
||||
{
|
||||
float distance = 0.0f;
|
||||
for (int i = 0; i < FTX_LDPC_N; ++i)
|
||||
{
|
||||
uint8_t hard = (log174[i] >= 0.0f) ? 1u : 0u;
|
||||
if (codeword[i] != hard)
|
||||
distance += fabsf(log174[i]);
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
static bool ft2_try_crc_candidate(const uint8_t a91[FTX_LDPC_K_BYTES], ftx_message_t* message)
|
||||
{
|
||||
uint8_t codeword[FTX_LDPC_N];
|
||||
ft2_encode_codeword_from_a91(a91, codeword);
|
||||
return ft2_unpack_message(codeword, message);
|
||||
}
|
||||
|
||||
static bool ft2_osd_lite_decode(const float log174[FTX_LDPC_N], ftx_message_t* message)
|
||||
{
|
||||
uint8_t base_a91[FTX_LDPC_K_BYTES];
|
||||
memset(base_a91, 0, sizeof(base_a91));
|
||||
for (int i = 0; i < FTX_LDPC_K; ++i)
|
||||
{
|
||||
if (log174[i] >= 0.0f)
|
||||
base_a91[i / 8] |= (uint8_t)(0x80u >> (i % 8));
|
||||
}
|
||||
|
||||
if (ft2_try_crc_candidate(base_a91, message))
|
||||
return true;
|
||||
|
||||
ft2_reliability_t reliabilities[FTX_LDPC_K];
|
||||
for (int i = 0; i < FTX_LDPC_K; ++i)
|
||||
{
|
||||
reliabilities[i].index = i;
|
||||
reliabilities[i].reliability = fabsf(log174[i]);
|
||||
}
|
||||
qsort(reliabilities, FTX_LDPC_K, sizeof(reliabilities[0]), ft2_cmp_reliability_asc);
|
||||
|
||||
const int max_candidates = 12;
|
||||
const int n = (FTX_LDPC_K < max_candidates) ? FTX_LDPC_K : max_candidates;
|
||||
uint8_t trial_a91[FTX_LDPC_K_BYTES];
|
||||
uint8_t best_codeword[FTX_LDPC_N];
|
||||
float best_distance = INFINITY;
|
||||
bool have_best = false;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
memcpy(trial_a91, base_a91, sizeof(trial_a91));
|
||||
int b0 = reliabilities[i].index;
|
||||
trial_a91[b0 / 8] ^= (uint8_t)(0x80u >> (b0 % 8));
|
||||
if (ft2_try_crc_candidate(trial_a91, message))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
for (int j = i + 1; j < n; ++j)
|
||||
{
|
||||
memcpy(trial_a91, base_a91, sizeof(trial_a91));
|
||||
int b0 = reliabilities[i].index;
|
||||
int b1 = reliabilities[j].index;
|
||||
trial_a91[b0 / 8] ^= (uint8_t)(0x80u >> (b0 % 8));
|
||||
trial_a91[b1 / 8] ^= (uint8_t)(0x80u >> (b1 % 8));
|
||||
if (ft2_try_crc_candidate(trial_a91, message))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
for (int j = i + 1; j < n; ++j)
|
||||
{
|
||||
for (int k = j + 1; k < n; ++k)
|
||||
{
|
||||
memcpy(trial_a91, base_a91, sizeof(trial_a91));
|
||||
int b0 = reliabilities[i].index;
|
||||
int b1 = reliabilities[j].index;
|
||||
int b2 = reliabilities[k].index;
|
||||
trial_a91[b0 / 8] ^= (uint8_t)(0x80u >> (b0 % 8));
|
||||
trial_a91[b1 / 8] ^= (uint8_t)(0x80u >> (b1 % 8));
|
||||
trial_a91[b2 / 8] ^= (uint8_t)(0x80u >> (b2 % 8));
|
||||
if (ft2_try_crc_candidate(trial_a91, message))
|
||||
return true;
|
||||
|
||||
uint8_t codeword[FTX_LDPC_N];
|
||||
ft2_encode_codeword_from_a91(trial_a91, codeword);
|
||||
float distance = ft2_codeword_distance(codeword, log174);
|
||||
if (distance < best_distance)
|
||||
{
|
||||
memcpy(best_codeword, codeword, sizeof(best_codeword));
|
||||
best_distance = distance;
|
||||
have_best = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_best)
|
||||
return ft2_unpack_message(best_codeword, message);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool ft2_unpack_message(const uint8_t plain174[], ftx_message_t* message)
|
||||
{
|
||||
uint8_t a91[FTX_LDPC_K_BYTES];
|
||||
@@ -971,22 +1119,23 @@ static bool ft2_decode_hit(
|
||||
llr_passes[4][i] = (a + b + c) / 3.0f;
|
||||
}
|
||||
|
||||
uint8_t plain174[FTX_LDPC_N];
|
||||
bool ok = false;
|
||||
bool ldpc_passed = false;
|
||||
uint8_t apmask[FTX_LDPC_N] = { 0 };
|
||||
uint8_t message91[FTX_LDPC_K] = { 0 };
|
||||
uint8_t cw[FTX_LDPC_N] = { 0 };
|
||||
for (int pass = 0; pass < 5 && !ok; ++pass)
|
||||
{
|
||||
float log174[FTX_LDPC_N];
|
||||
memcpy(log174, llr_passes[pass], sizeof(log174));
|
||||
int ldpc_errors = 0;
|
||||
bp_decode(log174, 50, plain174, &ldpc_errors);
|
||||
if (ldpc_errors > 0)
|
||||
continue;
|
||||
ldpc_passed = true;
|
||||
ok = ft2_unpack_message(plain174, message);
|
||||
int ntype = 0;
|
||||
int nharderror = -1;
|
||||
float dmin = 0.0f;
|
||||
decode174_91_osd(log174, FTX_LDPC_K, 3, 3, apmask, message91, cw, &ntype, &nharderror, &dmin);
|
||||
if (ntype != 0 && nharderror > 0)
|
||||
ok = ft2_unpack_message(cw, message);
|
||||
}
|
||||
if (!ok && fail_stage)
|
||||
*fail_stage = ldpc_passed ? FT2_FAIL_CRC : FT2_FAIL_LDPC;
|
||||
*fail_stage = FT2_FAIL_LDPC;
|
||||
|
||||
if (ok)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user