diff --git a/external/ft8_lib/ft8/ldpc.c b/external/ft8_lib/ft8/ldpc.c index 539a1df..0fd0244 100644 --- a/external/ft8_lib/ft8/ldpc.c +++ b/external/ft8_lib/ft8/ldpc.c @@ -11,15 +11,21 @@ #include "ldpc.h" #include "constants.h" +#include "crc.h" #include #include #include #include +#include static int ldpc_check(uint8_t codeword[]); static float fast_tanh(float x); static float fast_atanh(float x); +static void pack_bits91(const uint8_t bit_array[], int num_bits, uint8_t packed[]); +static bool check_crc91(const uint8_t plain91[]); +static void encode174_91_nocrc_bits(const uint8_t message91[], uint8_t codeword[]); +static int cmp_reliability_desc(const void* lhs, const void* rhs); // codeword is 174 log-likelihoods. // plain is a return value, 174 ints, to be 0 or 1. @@ -249,3 +255,733 @@ static float fast_atanh(float x) float b = (945.0f + x2 * (-1050.0f + x2 * 225.0f)); return a / b; } + +typedef struct +{ + int index; + float abs_llr; +} reliability_entry_t; + +typedef struct +{ + int* head; + int* next; + int (*pairs)[2]; + int capacity; + int count; + int size; + int last_pattern; + int next_index; +} osd_box_t; + +static void pack_bits91(const uint8_t bit_array[], int num_bits, uint8_t packed[]) +{ + int num_bytes = (num_bits + 7) / 8; + memset(packed, 0, (size_t)num_bytes); + uint8_t mask = 0x80u; + int byte_idx = 0; + for (int i = 0; i < num_bits; ++i) + { + if (bit_array[i]) + packed[byte_idx] |= mask; + mask >>= 1; + if (mask == 0) + { + mask = 0x80u; + ++byte_idx; + } + } +} + +static bool check_crc91(const uint8_t plain91[]) +{ + uint8_t a91[FTX_LDPC_K_BYTES]; + pack_bits91(plain91, FTX_LDPC_K, a91); + uint16_t crc_extracted = ftx_extract_crc(a91); + a91[9] &= 0xF8; + a91[10] &= 0x00; + uint16_t crc_calculated = ftx_compute_crc(a91, 96 - 14); + return crc_extracted == crc_calculated; +} + +static uint8_t parity8(uint8_t x) +{ + x ^= x >> 4; + x ^= x >> 2; + x ^= x >> 1; + return x & 1u; +} + +static void encode174_91_nocrc_bits(const uint8_t message91[], uint8_t codeword[]) +{ + uint8_t packed[FTX_LDPC_K_BYTES]; + pack_bits91(message91, FTX_LDPC_K, packed); + for (int i = 0; i < FTX_LDPC_K; ++i) + { + codeword[i] = message91[i] & 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 ^= parity8(packed[j] & kFTX_LDPC_generator[i][j]); + } + codeword[FTX_LDPC_K + i] = nsum & 0x1u; + } +} + +static int cmp_reliability_desc(const void* lhs, const void* rhs) +{ + const reliability_entry_t* a = (const reliability_entry_t*)lhs; + const reliability_entry_t* b = (const reliability_entry_t*)rhs; + if (a->abs_llr < b->abs_llr) + return 1; + if (a->abs_llr > b->abs_llr) + return -1; + return 0; +} + +static void xor_rows(uint8_t* dst, const uint8_t* src, int len) +{ + for (int i = 0; i < len; ++i) + dst[i] ^= src[i]; +} + +static void mrbencode91(const uint8_t* me, uint8_t* codeword, uint8_t* g2, int n, int k) +{ + memset(codeword, 0, (size_t)n); + for (int i = 0; i < k; ++i) + { + if (me[i] == 0) + continue; + for (int j = 0; j < n; ++j) + codeword[j] ^= g2[j * k + i]; + } +} + +static void nextpat91(uint8_t* mi, int k, int iorder, int* iflag) +{ + int ind = -1; + for (int i = 0; i < k - 1; ++i) + { + if (mi[i] == 0 && mi[i + 1] == 1) + ind = i; + } + if (ind < 0) + { + *iflag = -1; + return; + } + + uint8_t* ms = (uint8_t*)calloc((size_t)k, sizeof(uint8_t)); + if (!ms) + { + *iflag = -1; + return; + } + for (int i = 0; i < ind; ++i) + ms[i] = mi[i]; + ms[ind] = 1; + ms[ind + 1] = 0; + if (ind + 1 < k) + { + int nz = iorder; + for (int i = 0; i < k; ++i) + nz -= ms[i]; + for (int i = k - nz; i < k; ++i) + ms[i] = 1; + } + memcpy(mi, ms, (size_t)k); + free(ms); + + *iflag = -1; + for (int i = 0; i < k; ++i) + { + if (mi[i] == 1) + { + *iflag = i; + break; + } + } +} + +static bool osd_box_init(osd_box_t* box, int ntau) +{ + box->size = 1 << ntau; + box->capacity = 5000; + box->count = 0; + box->last_pattern = -1; + box->next_index = -1; + box->head = (int*)malloc(sizeof(int) * box->size); + box->next = (int*)malloc(sizeof(int) * box->capacity); + box->pairs = malloc(sizeof(int[2]) * box->capacity); + if (!box->head || !box->next || !box->pairs) + { + free(box->head); + free(box->next); + free(box->pairs); + return false; + } + for (int i = 0; i < box->size; ++i) + box->head[i] = -1; + for (int i = 0; i < box->capacity; ++i) + { + box->next[i] = -1; + box->pairs[i][0] = -1; + box->pairs[i][1] = -1; + } + return true; +} + +static void osd_box_free(osd_box_t* box) +{ + free(box->head); + free(box->next); + free(box->pairs); +} + +static int pattern_hash(const uint8_t* e2, int ntau) +{ + int ipat = 0; + for (int i = 0; i < ntau; ++i) + { + if (e2[i]) + ipat |= (1 << (ntau - i - 1)); + } + return ipat; +} + +static void boxit91(osd_box_t* box, const uint8_t* e2, int ntau, int i1, int i2) +{ + if (box->count >= box->capacity) + return; + int idx = box->count++; + box->pairs[idx][0] = i1; + box->pairs[idx][1] = i2; + int ipat = pattern_hash(e2, ntau); + int ip = box->head[ipat]; + if (ip == -1) + { + box->head[ipat] = idx; + } + else + { + while (box->next[ip] != -1) + ip = box->next[ip]; + box->next[ip] = idx; + } +} + +static void fetchit91(osd_box_t* box, const uint8_t* e2, int ntau, int* i1, int* i2) +{ + int ipat = pattern_hash(e2, ntau); + int index = box->head[ipat]; + if (box->last_pattern != ipat && index >= 0) + { + *i1 = box->pairs[index][0]; + *i2 = box->pairs[index][1]; + box->next_index = box->next[index]; + } + else if (box->last_pattern == ipat && box->next_index >= 0) + { + *i1 = box->pairs[box->next_index][0]; + *i2 = box->pairs[box->next_index][1]; + box->next_index = box->next[box->next_index]; + } + else + { + *i1 = -1; + *i2 = -1; + box->next_index = -1; + } + box->last_pattern = ipat; +} + +static void osd174_91(float llr[], int k, uint8_t apmask[], int ndeep, uint8_t message91[], uint8_t cw[], int* nhardmin, float* dmin) +{ + const int n = FTX_LDPC_N; + static bool gen_ready = false; + static uint8_t gen[FTX_LDPC_K][FTX_LDPC_N]; + if (!gen_ready) + { + for (int i = 0; i < FTX_LDPC_K; ++i) + { + uint8_t msg[FTX_LDPC_K] = { 0 }; + msg[i] = 1; + encode174_91_nocrc_bits(msg, gen[i]); + } + gen_ready = true; + } + + uint8_t* genmrb = (uint8_t*)malloc((size_t)k * n); + uint8_t* g2 = (uint8_t*)malloc((size_t)n * k); + uint8_t* temp = (uint8_t*)malloc((size_t)k); + uint8_t* m0 = (uint8_t*)malloc((size_t)k); + uint8_t* me = (uint8_t*)malloc((size_t)k); + uint8_t* mi = (uint8_t*)malloc((size_t)k); + uint8_t* misub = (uint8_t*)malloc((size_t)k); + uint8_t* e2sub = (uint8_t*)malloc((size_t)(n - k)); + uint8_t* e2 = (uint8_t*)malloc((size_t)(n - k)); + uint8_t* ui = (uint8_t*)malloc((size_t)(n - k)); + uint8_t* r2pat = (uint8_t*)malloc((size_t)(n - k)); + uint8_t* hdec = (uint8_t*)malloc((size_t)n); + uint8_t* c0 = (uint8_t*)malloc((size_t)n); + uint8_t* ce = (uint8_t*)malloc((size_t)n); + uint8_t* nxor = (uint8_t*)malloc((size_t)n); + uint8_t* apmaskr = (uint8_t*)malloc((size_t)n); + float* rx = (float*)malloc(sizeof(float) * n); + float* absrx = (float*)malloc(sizeof(float) * n); + reliability_entry_t* rel = (reliability_entry_t*)malloc(sizeof(reliability_entry_t) * n); + int* indices = (int*)malloc(sizeof(int) * n); + if (!genmrb || !g2 || !temp || !m0 || !me || !mi || !misub || !e2sub || !e2 || !ui || !r2pat || !hdec || !c0 || !ce || !nxor || !apmaskr || !rx || !absrx || !rel || !indices) + { + goto cleanup; + } + + for (int i = 0; i < n; ++i) + { + rx[i] = llr[i]; + apmaskr[i] = apmask[i]; + hdec[i] = (rx[i] >= 0.0f) ? 1u : 0u; + absrx[i] = fabsf(rx[i]); + rel[i].index = i; + rel[i].abs_llr = absrx[i]; + } + qsort(rel, n, sizeof(rel[0]), cmp_reliability_desc); + for (int i = 0; i < n; ++i) + { + indices[i] = rel[i].index; + for (int row = 0; row < k; ++row) + genmrb[row * n + i] = gen[row][indices[i]]; + } + + for (int id = 0; id < k; ++id) + { + int max_col = k + 20; + if (max_col > n) + max_col = n; + for (int col = id; col < max_col; ++col) + { + if (genmrb[id * n + col] == 0) + continue; + if (col != id) + { + for (int row = 0; row < k; ++row) + { + uint8_t swap = genmrb[row * n + id]; + genmrb[row * n + id] = genmrb[row * n + col]; + genmrb[row * n + col] = swap; + } + int itmp = indices[id]; + indices[id] = indices[col]; + indices[col] = itmp; + } + for (int row = 0; row < k; ++row) + { + if (row != id && genmrb[row * n + id] == 1) + xor_rows(&genmrb[row * n], &genmrb[id * n], n); + } + break; + } + } + + for (int row = 0; row < k; ++row) + { + for (int col = 0; col < n; ++col) + g2[col * k + row] = genmrb[row * n + col]; + } + + for (int i = 0; i < n; ++i) + { + hdec[i] = (rx[indices[i]] >= 0.0f) ? 1u : 0u; + absrx[i] = fabsf(rx[indices[i]]); + rx[i] = llr[indices[i]]; + apmaskr[i] = apmask[indices[i]]; + } + for (int i = 0; i < k; ++i) + m0[i] = hdec[i]; + + mrbencode91(m0, c0, g2, n, k); + for (int i = 0; i < n; ++i) + nxor[i] = c0[i] ^ hdec[i]; + *nhardmin = 0; + *dmin = 0.0f; + for (int i = 0; i < n; ++i) + { + *nhardmin += nxor[i]; + *dmin += nxor[i] ? absrx[i] : 0.0f; + } + memcpy(cw, c0, (size_t)n); + + if (ndeep > 6) + ndeep = 6; + int nord = 0, npre1 = 0, npre2 = 0, nt = 0, ntheta = 0, ntau = 0; + if (ndeep == 0) + { + goto reorder; + } + else if (ndeep == 1) + { + nord = 1; nt = 40; ntheta = 12; + } + else if (ndeep == 2) + { + nord = 1; npre1 = 1; nt = 40; ntheta = 10; + } + else if (ndeep == 3) + { + nord = 1; npre1 = 1; npre2 = 1; nt = 40; ntheta = 12; ntau = 14; + } + else if (ndeep == 4) + { + nord = 2; npre1 = 1; npre2 = 1; nt = 40; ntheta = 12; ntau = 17; + } + else if (ndeep == 5) + { + nord = 3; npre1 = 1; npre2 = 1; nt = 40; ntheta = 12; ntau = 15; + } + else + { + nord = 4; npre1 = 1; npre2 = 1; nt = 95; ntheta = 12; ntau = 15; + } + + for (int iorder = 1; iorder <= nord; ++iorder) + { + memset(misub, 0, (size_t)k); + for (int i = k - iorder; i < k; ++i) + misub[i] = 1; + int iflag = k - iorder; + while (iflag >= 0) + { + int iend = (iorder == nord && npre1 == 0) ? iflag : 0; + float d1 = 0.0f; + for (int n1 = iflag; n1 >= iend; --n1) + { + memcpy(mi, misub, (size_t)k); + mi[n1] = 1; + bool masked = false; + for (int i = 0; i < k; ++i) + { + if (apmaskr[i] && mi[i]) + { + masked = true; + break; + } + } + if (masked) + continue; + for (int i = 0; i < k; ++i) + me[i] = m0[i] ^ mi[i]; + if (n1 == iflag) + { + mrbencode91(me, ce, g2, n, k); + for (int i = 0; i < n - k; ++i) + { + e2sub[i] = ce[k + i] ^ hdec[k + i]; + e2[i] = e2sub[i]; + } + int nd1kpt = 1; + for (int i = 0; i < nt; ++i) + nd1kpt += e2sub[i]; + d1 = 0.0f; + for (int i = 0; i < k; ++i) + d1 += ((me[i] ^ hdec[i]) ? absrx[i] : 0.0f); + if (nd1kpt <= ntheta) + { + float dd = d1; + for (int i = 0; i < n - k; ++i) + dd += e2sub[i] ? absrx[k + i] : 0.0f; + if (dd < *dmin) + { + *dmin = dd; + memcpy(cw, ce, (size_t)n); + *nhardmin = 0; + for (int i = 0; i < n; ++i) + *nhardmin += (ce[i] ^ hdec[i]); + } + } + } + else + { + for (int i = 0; i < n - k; ++i) + e2[i] = e2sub[i] ^ g2[(k + i) * k + n1]; + int nd1kpt = 2; + for (int i = 0; i < nt; ++i) + nd1kpt += e2[i]; + if (nd1kpt <= ntheta) + { + mrbencode91(me, ce, g2, n, k); + float dd = d1 + ((ce[n1] ^ hdec[n1]) ? absrx[n1] : 0.0f); + for (int i = 0; i < n - k; ++i) + dd += e2[i] ? absrx[k + i] : 0.0f; + if (dd < *dmin) + { + *dmin = dd; + memcpy(cw, ce, (size_t)n); + *nhardmin = 0; + for (int i = 0; i < n; ++i) + *nhardmin += (ce[i] ^ hdec[i]); + } + } + } + } + nextpat91(misub, k, iorder, &iflag); + } + } + + if (npre2 == 1) + { + osd_box_t box; + if (osd_box_init(&box, ntau)) + { + for (int i1 = k - 1; i1 >= 0; --i1) + { + for (int i2 = i1 - 1; i2 >= 0; --i2) + { + for (int i = 0; i < ntau; ++i) + mi[i] = g2[(k + i) * k + i1] ^ g2[(k + i) * k + i2]; + boxit91(&box, mi, ntau, i1, i2); + } + } + + memset(misub, 0, (size_t)k); + for (int i = k - nord; i < k; ++i) + misub[i] = 1; + int iflag = k - nord; + while (iflag >= 0) + { + for (int i = 0; i < k; ++i) + me[i] = m0[i] ^ misub[i]; + mrbencode91(me, ce, g2, n, k); + for (int i = 0; i < n - k; ++i) + e2sub[i] = ce[k + i] ^ hdec[k + i]; + for (int i2 = 0; i2 <= ntau; ++i2) + { + memset(ui, 0, (size_t)(n - k)); + if (i2 > 0) + ui[i2 - 1] = 1; + for (int i = 0; i < ntau; ++i) + r2pat[i] = e2sub[i] ^ ui[i]; + box.last_pattern = -1; + box.next_index = -1; + while (true) + { + int in1, in2; + fetchit91(&box, r2pat, ntau, &in1, &in2); + if (in1 < 0 || in2 < 0) + break; + memcpy(mi, misub, (size_t)k); + mi[in1] = 1; + mi[in2] = 1; + int w = 0; + bool masked = false; + for (int i = 0; i < k; ++i) + { + w += mi[i]; + if (apmaskr[i] && mi[i]) + masked = true; + } + if (w < nord + npre1 + npre2 || masked) + continue; + for (int i = 0; i < k; ++i) + me[i] = m0[i] ^ mi[i]; + mrbencode91(me, ce, g2, n, k); + float dd = 0.0f; + int nh = 0; + for (int i = 0; i < n; ++i) + { + uint8_t diff = ce[i] ^ hdec[i]; + nh += diff; + if (diff) + dd += absrx[i]; + } + if (dd < *dmin) + { + *dmin = dd; + memcpy(cw, ce, (size_t)n); + *nhardmin = nh; + } + } + } + nextpat91(misub, k, nord, &iflag); + } + osd_box_free(&box); + } + } + +reorder: + { + uint8_t reordered_cw[FTX_LDPC_N]; + for (int i = 0; i < n; ++i) + reordered_cw[indices[i]] = cw[i]; + memcpy(cw, reordered_cw, (size_t)n); + memcpy(message91, cw, FTX_LDPC_K); + if (!check_crc91(message91)) + *nhardmin = -*nhardmin; + } + +cleanup: + free(genmrb); + free(g2); + free(temp); + free(m0); + free(me); + free(mi); + free(misub); + free(e2sub); + free(e2); + free(ui); + free(r2pat); + free(hdec); + free(c0); + free(ce); + free(nxor); + free(apmaskr); + free(rx); + free(absrx); + free(rel); + free(indices); +} + +void decode174_91_osd(float llr[], int keff, int maxosd, int norder, uint8_t apmask[], uint8_t message91[], uint8_t cw[], int* ntype, int* nharderror, float* dmin) +{ + if (keff != FTX_LDPC_K) + { + *ntype = 0; + *nharderror = -1; + *dmin = 0.0f; + return; + } + + const int maxiterations = 30; + int nosd = 0; + if (maxosd > 3) + maxosd = 3; + float zsave[3][FTX_LDPC_N] = { { 0 } }; + if (maxosd == 0) + { + nosd = 1; + memcpy(zsave[0], llr, sizeof(float) * FTX_LDPC_N); + } + else if (maxosd > 0) + { + nosd = maxosd; + } + + float tov[FTX_LDPC_N][3] = { { 0 } }; + float toc[FTX_LDPC_M][7] = { { 0 } }; + float zsum[FTX_LDPC_N] = { 0 }; + uint8_t hdec[FTX_LDPC_N]; + uint8_t best_cw[FTX_LDPC_N] = { 0 }; + int ncnt = 0; + int nclast = 0; + + for (int iter = 0; iter <= maxiterations; ++iter) + { + float zn[FTX_LDPC_N]; + for (int i = 0; i < FTX_LDPC_N; ++i) + { + zn[i] = llr[i]; + if (apmask[i] != 1) + zn[i] += tov[i][0] + tov[i][1] + tov[i][2]; + zsum[i] += zn[i]; + } + if (iter > 0 && iter <= maxosd) + memcpy(zsave[iter - 1], zsum, sizeof(zsum)); + + for (int i = 0; i < FTX_LDPC_N; ++i) + best_cw[i] = (zn[i] > 0.0f) ? 1u : 0u; + int ncheck = ldpc_check(best_cw); + if (ncheck == 0 && check_crc91(best_cw)) + { + memcpy(message91, best_cw, FTX_LDPC_K); + memcpy(cw, best_cw, FTX_LDPC_N); + for (int i = 0; i < FTX_LDPC_N; ++i) + hdec[i] = (llr[i] >= 0.0f) ? 1u : 0u; + *nharderror = 0; + *dmin = 0.0f; + for (int i = 0; i < FTX_LDPC_N; ++i) + { + uint8_t diff = hdec[i] ^ best_cw[i]; + *nharderror += diff; + if (diff) + *dmin += fabsf(llr[i]); + } + *ntype = 1; + return; + } + + if (iter > 0) + { + int nd = ncheck - nclast; + ncnt = (nd < 0) ? 0 : (ncnt + 1); + if (ncnt >= 5 && iter >= 10 && ncheck > 15) + { + *nharderror = -1; + break; + } + } + nclast = ncheck; + + for (int m = 0; m < FTX_LDPC_M; ++m) + { + for (int n_idx = 0; n_idx < kFTX_LDPC_Num_rows[m]; ++n_idx) + { + int n = kFTX_LDPC_Nm[m][n_idx] - 1; + toc[m][n_idx] = zn[n]; + for (int kk = 0; kk < 3; ++kk) + { + if ((kFTX_LDPC_Mn[n][kk] - 1) == m) + toc[m][n_idx] -= tov[n][kk]; + } + } + } + + for (int m = 0; m < FTX_LDPC_M; ++m) + { + float tanhtoc[7]; + for (int i = 0; i < 7; ++i) + tanhtoc[i] = tanhf(-toc[m][i] / 2.0f); + for (int j = 0; j < kFTX_LDPC_Num_rows[m]; ++j) + { + int n = kFTX_LDPC_Nm[m][j] - 1; + float Tmn = 1.0f; + for (int n_idx = 0; n_idx < kFTX_LDPC_Num_rows[m]; ++n_idx) + { + if ((kFTX_LDPC_Nm[m][n_idx] - 1) != n) + Tmn *= tanhtoc[n_idx]; + } + for (int kk = 0; kk < 3; ++kk) + { + if ((kFTX_LDPC_Mn[n][kk] - 1) == m) + tov[n][kk] = 2.0f * fast_atanh(-Tmn); + } + } + } + } + + for (int i = 0; i < nosd; ++i) + { + int osd_harderror = -1; + float osd_dmin = 0.0f; + osd174_91(zsave[i], keff, apmask, norder, message91, cw, &osd_harderror, &osd_dmin); + if (osd_harderror > 0) + { + *nharderror = osd_harderror; + *dmin = 0.0f; + for (int j = 0; j < FTX_LDPC_N; ++j) + { + hdec[j] = (llr[j] >= 0.0f) ? 1u : 0u; + if ((hdec[j] ^ cw[j]) != 0) + *dmin += fabsf(llr[j]); + } + *ntype = 2; + return; + } + } + + *ntype = 0; + *nharderror = -1; + *dmin = 0.0f; +} diff --git a/external/ft8_lib/ft8/ldpc.h b/external/ft8_lib/ft8/ldpc.h index 02f4a99..e7d98ba 100644 --- a/external/ft8_lib/ft8/ldpc.h +++ b/external/ft8_lib/ft8/ldpc.h @@ -16,6 +16,8 @@ void ldpc_decode(float codeword[], int max_iters, uint8_t plain[], int* ok); void bp_decode(float codeword[], int max_iters, uint8_t plain[], int* ok); +void decode174_91_osd(float llr[], int keff, int maxosd, int norder, uint8_t apmask[], uint8_t message91[], uint8_t cw[], int* ntype, int* nharderror, float* dmin); + #ifdef __cplusplus } #endif diff --git a/src/decoders/trx-ft8/src/ft8_wrapper.c b/src/decoders/trx-ft8/src/ft8_wrapper.c index be50932..a871c45 100644 --- a/src/decoders/trx-ft8/src/ft8_wrapper.c +++ b/src/decoders/trx-ft8/src/ft8_wrapper.c @@ -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) {