diff --git a/external/ft8_lib/ft8/ldpc.c b/external/ft8_lib/ft8/ldpc.c index 5d66b23..539a1df 100644 --- a/external/ft8_lib/ft8/ldpc.c +++ b/external/ft8_lib/ft8/ldpc.c @@ -11,23 +11,15 @@ #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 float platanh(float x); -static void pack_bits91(const uint8_t bit_array[], int num_bits, uint8_t packed[]); -static void unpack_bits91(const uint8_t packed[], int num_bits, uint8_t bit_array[]); -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. @@ -257,764 +249,3 @@ static float fast_atanh(float x) float b = (945.0f + x2 * (-1050.0f + x2 * 225.0f)); return a / b; } - -static float platanh(float x) -{ - int isign = 1; - float z = x; - if (x < 0.0f) - { - isign = -1; - z = -x; - } - if (z <= 0.664f) - return x / 0.83f; - if (z <= 0.9217f) - return isign * ((z - 0.4064f) / 0.322f); - if (z <= 0.9951f) - return isign * ((z - 0.8378f) / 0.0524f); - if (z <= 0.9998f) - return isign * ((z - 0.9914f) / 0.0012f); - return isign * 7.0f; -} - -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 void unpack_bits91(const uint8_t packed[], int num_bits, uint8_t bit_array[]) -{ - for (int i = 0; i < num_bits; ++i) - bit_array[i] = (packed[i / 8] >> (7 - (i % 8))) & 0x1u; -} - -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; - if (i < 77) - { - for (int j = 77; j < FTX_LDPC_K; ++j) - msg[j] = 0; - } - 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 * platanh(-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 e7d98ba..02f4a99 100644 --- a/external/ft8_lib/ft8/ldpc.h +++ b/external/ft8_lib/ft8/ldpc.h @@ -16,8 +16,6 @@ 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/build.rs b/src/decoders/trx-ft8/build.rs index 3017158..bd3cfeb 100644 --- a/src/decoders/trx-ft8/build.rs +++ b/src/decoders/trx-ft8/build.rs @@ -12,6 +12,7 @@ fn main() { .include(format!("{base}/ft8")) .define("_GNU_SOURCE", None) .define("_POSIX_C_SOURCE", "200809L") + .file("src/ft2_ldpc.c") .file("src/ft8_wrapper.c") .file(format!("{base}/common/monitor.c")) .file(format!("{base}/fft/kiss_fft.c")) @@ -29,6 +30,8 @@ fn main() { println!("cargo:rustc-link-lib=m"); + println!("cargo:rerun-if-changed=src/ft2_ldpc.c"); + println!("cargo:rerun-if-changed=src/ft2_ldpc.h"); println!("cargo:rerun-if-changed=src/ft8_wrapper.c"); println!("cargo:rerun-if-changed={base}/common/monitor.c"); println!("cargo:rerun-if-changed={base}/common/monitor.h"); diff --git a/src/decoders/trx-ft8/src/ft2_ldpc.c b/src/decoders/trx-ft8/src/ft2_ldpc.c new file mode 100644 index 0000000..58d671d --- /dev/null +++ b/src/decoders/trx-ft8/src/ft2_ldpc.c @@ -0,0 +1,821 @@ +// SPDX-FileCopyrightText: 2026 Stanislaw Grams +// +// SPDX-License-Identifier: BSD-2-Clause + +#include "ft2_ldpc.h" + +#include +#include + +#include +#include +#include +#include + +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 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; +} + +static float ft2_fast_atanh(float x) +{ + float x2 = x * x; + float a = x * (945.0f + x2 * (-735.0f + x2 * 64.0f)); + float b = 945.0f + x2 * (-1050.0f + x2 * 225.0f); + return a / b; +} + +static float ft2_platanh(float x) +{ + int isign = 1; + float z = x; + if (x < 0.0f) + { + isign = -1; + z = -x; + } + if (z <= 0.664f) + return x / 0.83f; + if (z <= 0.9217f) + return isign * ((z - 0.4064f) / 0.322f); + if (z <= 0.9951f) + return isign * ((z - 0.8378f) / 0.0524f); + if (z <= 0.9998f) + return isign * ((z - 0.9914f) / 0.0012f); + return isign * 7.0f; +} + +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] != 0) + 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, const 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 == NULL) + { + *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 == NULL || box->next == NULL || box->pairs == NULL) + { + 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] != 0) + 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; + if (i < 77) + { + for (int j = 77; j < FTX_LDPC_K; ++j) + msg[j] = 0; + } + 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* 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 == NULL || g2 == NULL || m0 == NULL || me == NULL || mi == NULL || misub == NULL || e2sub == NULL || e2 == NULL || ui == NULL || r2pat == NULL || hdec == NULL || c0 == NULL || ce == NULL || nxor == NULL || apmaskr == NULL || rx == NULL || absrx == NULL || rel == NULL || indices == NULL) + { + 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] != 0 ? absrx[i] : 0.0f; + } + memcpy(cw, c0, (size_t)n); + + if (ndeep > 6) + ndeep = 6; + int nord = 0; + int npre1 = 0; + int npre2 = 0; + int nt = 0; + int ntheta = 0; + int ntau = 0; + if (ndeep == 0) + { + goto reorder; + } + 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] != 0 && mi[i] != 0) + { + 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]) != 0 ? absrx[i] : 0.0f; + if (nd1kpt <= ntheta) + { + float dd = d1; + for (int i = 0; i < n - k; ++i) + dd += e2sub[i] != 0 ? 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]) != 0 ? absrx[n1] : 0.0f); + for (int i = 0; i < n - k; ++i) + dd += e2[i] != 0 ? 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 = -1; + int in2 = -1; + 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] != 0 && mi[i] != 0) + 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 != 0) + 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(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 ft2_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 = ft2_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 != 0) + *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 * ft2_platanh(-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/src/decoders/trx-ft8/src/ft2_ldpc.h b/src/decoders/trx-ft8/src/ft2_ldpc.h new file mode 100644 index 0000000..a6f5759 --- /dev/null +++ b/src/decoders/trx-ft8/src/ft2_ldpc.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2026 Stanislaw Grams +// +// SPDX-License-Identifier: BSD-2-Clause + +#ifndef TRX_FT2_LDPC_H +#define TRX_FT2_LDPC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void ft2_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 + +#endif diff --git a/src/decoders/trx-ft8/src/ft8_wrapper.c b/src/decoders/trx-ft8/src/ft8_wrapper.c index e7ffb3e..2b8e86b 100644 --- a/src/decoders/trx-ft8/src/ft8_wrapper.c +++ b/src/decoders/trx-ft8/src/ft8_wrapper.c @@ -12,6 +12,7 @@ #include #include #include +#include "ft2_ldpc.h" #include #include @@ -1225,7 +1226,7 @@ static bool ft2_decode_hit( 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); + ft2_decode174_91_osd(log174, FTX_LDPC_K, 3, 3, apmask, message91, cw, &ntype, &nharderror, &dmin); if (pass_diag) { pass_diag->ntype[pass] = ntype;