aboutsummaryrefslogtreecommitdiff
path: root/src/mpdecode_core.c
diff options
context:
space:
mode:
authorAuthor Name <[email protected]>2023-07-07 12:20:59 +0930
committerDavid Rowe <[email protected]>2023-07-07 12:29:06 +0930
commitac7c48b4dee99d4c772f133d70d8d1b38262fcd2 (patch)
treea2d0ace57a9c0e2e5b611c4987f6fed1b38b81e7 /src/mpdecode_core.c
shallow zip-file copy from codec2 e9d726bf20
Diffstat (limited to 'src/mpdecode_core.c')
-rw-r--r--src/mpdecode_core.c789
1 files changed, 789 insertions, 0 deletions
diff --git a/src/mpdecode_core.c b/src/mpdecode_core.c
new file mode 100644
index 0000000..1392b2f
--- /dev/null
+++ b/src/mpdecode_core.c
@@ -0,0 +1,789 @@
+/*
+ FILE...: mpdecode_core.c
+ AUTHOR.: Matthew C. Valenti, Rohit Iyer Seshadri, David Rowe
+ CREATED: Sep 2016
+
+ C-callable core functions moved from MpDecode.c, so they can be used for
+ Octave and C programs.
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+#include "mpdecode_core.h"
+#ifndef USE_ORIGINAL_PHI0
+#include "phi0.h"
+#endif
+
+#include "debug_alloc.h"
+
+#ifdef __EMBEDDED__
+#include "machdep.h"
+#endif
+
+#define QPSK_CONSTELLATION_SIZE 4
+#define QPSK_BITS_PER_SYMBOL 2
+
+
+/* QPSK constellation for symbol likelihood calculations */
+
+static COMP S_matrix[] = {
+ { 1.0f, 0.0f},
+ { 0.0f, 1.0f},
+ { 0.0f, -1.0f},
+ {-1.0f, 0.0f}
+};
+
+// c_nodes will be an array of NumberParityBits of struct c_node
+// Each c_node contains an array of <degree> c_sub_node elements
+// This structure reduces the indexing caluclations in SumProduct()
+
+struct c_sub_node { // Order is important here to keep total size small.
+ uint16_t index; // Values from H_rows (except last 2 entries)
+ uint16_t socket; // The socket number at the v_node
+ float message; // modified during operation!
+};
+
+struct c_node {
+ int degree; // A count of elements in the following arrays
+ struct c_sub_node *subs;
+};
+
+// v_nodes will be an array of CodeLength of struct v_node
+
+struct v_sub_node {
+ uint16_t index; // the index of a c_node it is connected to
+ // Filled with values from H_cols (except last 2 entries)
+ uint16_t socket; // socket number at the c_node
+ float message; // Loaded with input data
+ // modified during operation!
+ uint8_t sign; // 1 if input is negative
+ // modified during operation!
+};
+
+struct v_node {
+ int degree; // A count of ???
+ float initial_value;
+ struct v_sub_node *subs;
+};
+
+void encode(struct LDPC *ldpc, unsigned char ibits[], unsigned char pbits[]) {
+ unsigned int p, i, tmp, par, prev=0;
+ int ind;
+ uint16_t *H_rows = ldpc->H_rows;
+
+ for (p=0; p<ldpc->NumberParityBits; p++) {
+ par = 0;
+
+ for (i=0; i<ldpc->max_row_weight; i++) {
+ ind = H_rows[p + i*ldpc->NumberParityBits];
+ if (ind) par = par + ibits[ind-1];
+ }
+
+ tmp = par + prev;
+
+ tmp &= 1; // only retain the lsb
+ prev = tmp;
+ pbits[p] = tmp;
+ }
+}
+
+#ifdef USE_ORIGINAL_PHI0
+/* Phi function */
+static float phi0(
+ float x )
+{
+ float z;
+
+ if (x>10)
+ return( 0 );
+ else if (x< 9.08e-5 )
+ return( 10 );
+ else if (x > 9)
+ return( 1.6881e-4 );
+ /* return( 1.4970e-004 ); */
+ else if (x > 8)
+ return( 4.5887e-4 );
+ /* return( 4.0694e-004 ); */
+ else if (x > 7)
+ return( 1.2473e-3 );
+ /* return( 1.1062e-003 ); */
+ else if (x > 6)
+ return( 3.3906e-3 );
+ /* return( 3.0069e-003 ); */
+ else if (x > 5)
+ return( 9.2168e-3 );
+ /* return( 8.1736e-003 ); */
+ else {
+ z = (float) exp(x);
+ return( (float) log( (z+1)/(z-1) ) );
+ }
+}
+#endif
+
+
+/* Values for linear approximation (DecoderType=5) */
+
+#define AJIAN -0.24904163195436
+#define TJIAN 2.50681740420944
+
+/* The linear-log-MAP algorithm */
+
+static float max_star0(
+ float delta1,
+ float delta2 )
+{
+ register float diff;
+
+ diff = delta2 - delta1;
+
+ if ( diff > TJIAN )
+ return( delta2 );
+ else if ( diff < -TJIAN )
+ return( delta1 );
+ else if ( diff > 0 )
+ return( delta2 + AJIAN*(diff-TJIAN) );
+ else
+ return( delta1 - AJIAN*(diff+TJIAN) );
+}
+
+void init_c_v_nodes(struct c_node *c_nodes,
+ int shift,
+ int NumberParityBits,
+ int max_row_weight,
+ uint16_t *H_rows,
+ int H1,
+ int CodeLength,
+ struct v_node *v_nodes,
+ int NumberRowsHcols,
+ uint16_t *H_cols,
+ int max_col_weight,
+ int dec_type,
+ float *input)
+{
+ int i, j, k, count, cnt, c_index, v_index;
+
+ /* first determine the degree of each c-node */
+
+ if (shift ==0){
+ for (i=0;i<NumberParityBits;i++) {
+ count = 0;
+ for (j=0;j<max_row_weight;j++) {
+ if ( H_rows[i+j*NumberParityBits] > 0 ) {
+ count++;
+ }
+ }
+ c_nodes[i].degree = count;
+ if (H1){
+ if (i==0){
+ c_nodes[i].degree=count+1;
+ }
+ else{
+ c_nodes[i].degree=count+2;
+ }
+ }
+ }
+ }
+ else{
+ cnt=0;
+ for (i=0;i<(NumberParityBits/shift);i++) {
+ for (k=0;k<shift;k++){
+ count = 0;
+ for (j=0;j<max_row_weight;j++) {
+ if ( H_rows[cnt+j*NumberParityBits] > 0 ) {
+ count++;
+ }
+ }
+ c_nodes[cnt].degree = count;
+ if ((i==0)||(i==(NumberParityBits/shift)-1)){
+ c_nodes[cnt].degree=count+1;
+ }
+ else{
+ c_nodes[cnt].degree=count+2;
+ }
+ cnt++;
+ }
+ }
+ }
+
+ if (H1){
+
+ if (shift ==0){
+ for (i=0;i<NumberParityBits;i++) {
+
+ // Allocate sub nodes
+ c_nodes[i].subs = CALLOC(c_nodes[i].degree, sizeof(struct c_sub_node));
+ assert(c_nodes[i].subs);
+
+ // Populate sub nodes
+ for (j=0;j<c_nodes[i].degree-2;j++) {
+ c_nodes[i].subs[j].index = (H_rows[i+j*NumberParityBits] - 1);
+ }
+ j=c_nodes[i].degree-2;
+
+ if (i==0){
+ c_nodes[i].subs[j].index = (H_rows[i+j*NumberParityBits] - 1);
+ }
+ else {
+ c_nodes[i].subs[j].index = (CodeLength-NumberParityBits)+i-1;
+ }
+
+ j=c_nodes[i].degree-1;
+ c_nodes[i].subs[j].index = (CodeLength-NumberParityBits)+i;
+
+ }
+ }
+ if (shift >0){
+ cnt=0;
+ for (i=0;i<(NumberParityBits/shift);i++){
+
+ for (k =0;k<shift;k++){
+
+ // Allocate sub nodes
+ c_nodes[cnt].subs = CALLOC(c_nodes[cnt].degree, sizeof(struct c_sub_node));
+ assert(c_nodes[cnt].subs);
+
+ // Populate sub nodes
+ for (j=0;j<c_nodes[cnt].degree-2;j++) {
+ c_nodes[cnt].subs[j].index = (H_rows[cnt+j*NumberParityBits] - 1);
+ }
+ j=c_nodes[cnt].degree-2;
+ if ((i ==0)||(i==(NumberParityBits/shift-1))){
+ c_nodes[cnt].subs[j].index = (H_rows[cnt+j*NumberParityBits] - 1);
+ }
+ else{
+ c_nodes[cnt].subs[j].index = (CodeLength-NumberParityBits)+k+shift*(i);
+ }
+ j=c_nodes[cnt].degree-1;
+ c_nodes[cnt].subs[j].index = (CodeLength-NumberParityBits)+k+shift*(i+1);
+ if (i== (NumberParityBits/shift-1))
+ {
+ c_nodes[cnt].subs[j].index = (CodeLength-NumberParityBits)+k+shift*(i);
+ }
+ cnt++;
+ }
+ }
+ }
+
+ } else {
+ for (i=0;i<NumberParityBits;i++) {
+ // Allocate sub nodes
+ c_nodes[i].subs = CALLOC(c_nodes[i].degree, sizeof(struct c_sub_node));
+ assert(c_nodes[i].subs);
+
+ // Populate sub nodes
+ for (j=0;j<c_nodes[i].degree;j++){
+ c_nodes[i].subs[j].index = (H_rows[i+j*NumberParityBits] - 1);
+ }
+ }
+ }
+
+
+ /* determine degree of each v-node */
+
+ for(i=0;i<(CodeLength-NumberParityBits+shift);i++){
+ count=0;
+ for (j=0;j<max_col_weight;j++) {
+ if ( H_cols[i+j*NumberRowsHcols] > 0 ) {
+ count++;
+ }
+ }
+ v_nodes[i].degree = count;
+ }
+
+ for(i=CodeLength-NumberParityBits+shift;i<CodeLength;i++){
+ count=0;
+ if (H1){
+ if(i!=CodeLength-1){
+ v_nodes[i].degree=2;
+ } else{
+ v_nodes[i].degree=1;
+ }
+
+ } else{
+ for (j=0;j<max_col_weight;j++) {
+ if ( H_cols[i+j*NumberRowsHcols] > 0 ) {
+ count++;
+ }
+ }
+ v_nodes[i].degree = count;
+ }
+ }
+
+ if (shift>0){
+ v_nodes[CodeLength-1].degree =v_nodes[CodeLength-1].degree+1;
+ }
+
+
+ /* set up v_nodes */
+
+ for (i=0;i<CodeLength;i++) {
+ // Allocate sub nodes
+ v_nodes[i].subs = CALLOC(v_nodes[i].degree, sizeof(struct v_sub_node));
+ assert(v_nodes[i].subs);
+
+ // Populate sub nodes
+
+ /* index tells which c-nodes this v-node is connected to */
+ v_nodes[i].initial_value = input[i];
+ count=0;
+
+ for (j=0;j<v_nodes[i].degree;j++) {
+ if ((H1)&& (i>=CodeLength-NumberParityBits+shift)){
+ v_nodes[i].subs[j].index=i-(CodeLength-NumberParityBits+shift)+count;
+ if (shift ==0){
+ count=count+1;
+ }
+ else{
+ count=count+shift;
+ }
+ } else {
+ v_nodes[i].subs[j].index = (H_cols[i+j*NumberRowsHcols] - 1);
+ }
+
+ /* search the connected c-node for the proper message value */
+ for (c_index=0;c_index<c_nodes[ v_nodes[i].subs[j].index ].degree;c_index++)
+ if ( c_nodes[ v_nodes[i].subs[j].index ].subs[c_index].index == i ) {
+ v_nodes[i].subs[j].socket = c_index;
+ break;
+ }
+ /* initialize v-node with received LLR */
+ if ( dec_type == 1)
+ v_nodes[i].subs[j].message = fabs(input[i]);
+ else
+ v_nodes[i].subs[j].message = phi0( fabs(input[i]) );
+
+ if (input[i] < 0)
+ v_nodes[i].subs[j].sign = 1;
+ }
+
+ }
+
+
+
+ /* now finish setting up the c_nodes */
+ for (i=0;i<NumberParityBits;i++) {
+ /* index tells which v-nodes this c-node is connected to */
+ for (j=0;j<c_nodes[i].degree;j++) {
+ /* search the connected v-node for the proper message value */
+ for (v_index=0;v_index<v_nodes[ c_nodes[i].subs[j].index ].degree;v_index++)
+ if (v_nodes[ c_nodes[i].subs[j].index ].subs[v_index].index == i ) {
+ c_nodes[i].subs[j].socket = v_index;
+ break;
+ }
+ }
+ }
+
+}
+
+
+///////////////////////////////////////
+/* function for doing the MP decoding */
+// Returns the iteration count
+int SumProduct( int *parityCheckCount,
+ char DecodedBits[],
+ struct c_node c_nodes[],
+ struct v_node v_nodes[],
+ int CodeLength,
+ int NumberParityBits,
+ int max_iter,
+ float r_scale_factor,
+ float q_scale_factor,
+ int data[] )
+{
+ int result;
+ int bitErrors;
+ int i,j, iter;
+ float phi_sum;
+ int sign;
+ float temp_sum;
+ float Qi;
+ int ssum;
+
+
+ result = max_iter;
+ for (iter=0;iter<max_iter;iter++) {
+
+ for(i=0; i<CodeLength; i++) DecodedBits[i] = 0; // Clear each pass!
+ bitErrors = 0;
+
+ /* update r */
+ ssum = 0;
+ for (j=0;j<NumberParityBits;j++) {
+ sign = v_nodes[ c_nodes[j].subs[0].index ].subs[ c_nodes[j].subs[0].socket ].sign;
+ phi_sum = v_nodes[ c_nodes[j].subs[0].index ].subs[ c_nodes[j].subs[0].socket ].message;
+
+ for (i=1;i<c_nodes[j].degree;i++) {
+ // Compiler should optomize this but write the best we can to start from.
+ struct c_sub_node *cp = &c_nodes[j].subs[i];
+ struct v_sub_node *vp = &v_nodes[ cp->index ].subs[ cp->socket ];
+ phi_sum += vp->message;
+ sign ^= vp->sign;
+ }
+
+ if (sign==0) ssum++;
+
+ for (i=0;i<c_nodes[j].degree;i++) {
+ struct c_sub_node *cp = &c_nodes[j].subs[i];
+ struct v_sub_node *vp = &v_nodes[ cp->index ].subs[ cp->socket ];
+ if ( sign ^ vp->sign ) {
+ cp->message = -phi0( phi_sum - vp->message ); // *r_scale_factor;
+ } else
+ cp->message = phi0( phi_sum - vp->message ); // *r_scale_factor;
+ }
+ }
+
+ /* update q */
+ for (i=0;i<CodeLength;i++) {
+
+ /* first compute the LLR */
+ Qi = v_nodes[i].initial_value;
+ for (j=0;j<v_nodes[i].degree;j++) {
+ struct v_sub_node *vp = &v_nodes[i].subs[j];
+ Qi += c_nodes[ vp->index ].subs[ vp->socket ].message;
+ }
+
+ /* make hard decision */
+ if (Qi < 0) {
+ DecodedBits[i] = 1;
+ }
+
+ /* now subtract to get the extrinsic information */
+ for (j=0;j<v_nodes[i].degree;j++) {
+ struct v_sub_node *vp = &v_nodes[i].subs[j];
+ temp_sum = Qi - c_nodes[ vp->index ].subs[ vp->socket ].message;
+
+ vp->message = phi0( fabs( temp_sum ) ); // *q_scale_factor;
+ if (temp_sum > 0)
+ vp->sign = 0;
+ else
+ vp->sign = 1;
+ }
+ }
+
+ /* count data bit errors, assuming that it is systematic */
+ for (i=0;i<CodeLength-NumberParityBits;i++)
+ if ( DecodedBits[i] != data[i] )
+ bitErrors++;
+
+
+ /* Halt if zero errors */
+ if (bitErrors == 0) {
+ result = iter + 1;
+ break;
+ }
+
+ // count the number of PC satisfied and exit if all OK
+ *parityCheckCount = ssum;
+ if (ssum==NumberParityBits) {
+ result = iter + 1;
+ break;
+ }
+
+
+ }
+
+return(result);
+}
+
+
+/* Convenience function to call LDPC decoder from C programs */
+
+int run_ldpc_decoder(struct LDPC *ldpc, uint8_t out_char[], float input[], int *parityCheckCount) {
+ int max_iter, dec_type;
+ float q_scale_factor, r_scale_factor;
+ int max_row_weight, max_col_weight;
+ int CodeLength, NumberParityBits, NumberRowsHcols, shift, H1;
+ int i;
+ struct c_node *c_nodes;
+ struct v_node *v_nodes;
+
+ /* default values */
+
+ max_iter = ldpc->max_iter;
+ dec_type = ldpc->dec_type;
+ q_scale_factor = ldpc->q_scale_factor;
+ r_scale_factor = ldpc->r_scale_factor;
+
+ CodeLength = ldpc->CodeLength; /* length of entire codeword */
+ NumberParityBits = ldpc->NumberParityBits;
+ NumberRowsHcols = ldpc->NumberRowsHcols;
+
+ char *DecodedBits = CALLOC( CodeLength, sizeof( char ) );
+ assert(DecodedBits);
+
+ /* derive some parameters */
+
+ shift = (NumberParityBits + NumberRowsHcols) - CodeLength;
+ if (NumberRowsHcols == CodeLength) {
+ H1=0;
+ shift=0;
+ } else {
+ H1=1;
+ }
+
+ max_row_weight = ldpc->max_row_weight;
+ max_col_weight = ldpc->max_col_weight;
+
+ /* initialize c-node and v-node structures */
+
+ c_nodes = CALLOC( NumberParityBits, sizeof( struct c_node ) );
+ assert(c_nodes);
+ v_nodes = CALLOC( CodeLength, sizeof( struct v_node));
+ assert(v_nodes);
+
+ init_c_v_nodes(c_nodes, shift, NumberParityBits, max_row_weight, ldpc->H_rows, H1, CodeLength,
+ v_nodes, NumberRowsHcols, ldpc->H_cols, max_col_weight, dec_type, input);
+
+ int DataLength = CodeLength - NumberParityBits;
+ int *data_int = CALLOC( DataLength, sizeof(int) );
+
+ /* need to clear these on each call */
+
+ for(i=0; i<CodeLength; i++) DecodedBits[i] = 0;
+
+ /* Call function to do the actual decoding */
+ int iter = SumProduct( parityCheckCount, DecodedBits, c_nodes, v_nodes,
+ CodeLength, NumberParityBits, max_iter,
+ r_scale_factor, q_scale_factor, data_int );
+
+ for (i=0; i<CodeLength; i++) out_char[i] = DecodedBits[i];
+
+ /* Clean up memory */
+
+ FREE(DecodedBits);
+ FREE( data_int );
+
+ for (i=0;i<NumberParityBits;i++) FREE( c_nodes[i].subs );
+ FREE( c_nodes );
+
+ for (i=0;i<CodeLength;i++) FREE( v_nodes[i].subs);
+ FREE( v_nodes );
+
+ return iter;
+}
+
+
+void sd_to_llr(float llr[], float sd[], int n) {
+ double sum, mean, sign, sumsq, estvar, estEsN0, x;
+ int i;
+
+ /* convert SD samples to LLRs -------------------------------*/
+
+ sum = 0.0;
+ for(i=0; i<n; i++)
+ sum += fabs(sd[i]);
+ mean = sum/n;
+
+ /* find variance from +/-1 symbol position */
+
+ sum = sumsq = 0.0;
+ for(i=0; i<n; i++) {
+ sign = (sd[i] > 0.0L) - (sd[i] < 0.0L);
+ x = ((double)sd[i]/mean - sign);
+ sum += x;
+ sumsq += x*x;
+ }
+ estvar = (n * sumsq - sum * sum) / (n * (n - 1));
+ //fprintf(stderr, "mean: %f var: %f\n", mean, estvar);
+
+ estEsN0 = 1.0/(2.0L * estvar + 1E-3);
+ for(i=0; i<n; i++)
+ llr[i] = 4.0L * estEsN0 * sd[i];
+}
+
+
+/*
+ Determine symbol likelihood from received QPSK symbols.
+
+ Notes:
+
+ 1) We assume fading[] is real, it is also possible to compute
+ with complex fading, see CML library Demod2D.c source code.
+ 2) Using floats instead of doubles, for stm32.
+ Testing shows good BERs with floats.
+*/
+
+void Demod2D(float symbol_likelihood[], /* output, M*number_symbols */
+ COMP r[], /* received QPSK symbols, number_symbols */
+ COMP S_matrix[], /* constellation of size M */
+ float EsNo,
+ float fading[], /* real fading values, number_symbols */
+ float mean_amp,
+ int number_symbols)
+{
+ int M=QPSK_CONSTELLATION_SIZE;
+ int i,j;
+ float tempsr, tempsi, Er, Ei;
+
+ /* determine output */
+
+ for (i=0;i<number_symbols;i++) { /* go through each received symbol */
+ for (j=0;j<M;j++) { /* each postulated symbol */
+ tempsr = fading[i]*S_matrix[j].real/mean_amp;
+ tempsi = fading[i]*S_matrix[j].imag/mean_amp;
+ Er = r[i].real/mean_amp - tempsr;
+ Ei = r[i].imag/mean_amp - tempsi;
+ symbol_likelihood[i*M+j] = -EsNo*(Er*Er+Ei*Ei);
+ //printf("symbol_likelihood[%d][%d] = %f\n", i,j,symbol_likelihood[i*M+j]);
+ }
+ //exit(0);
+ }
+
+}
+
+
+void Somap(float bit_likelihood[], /* number_bits, bps*number_symbols */
+ float symbol_likelihood[], /* M*number_symbols */
+ int M, /* constellation size */
+ int bps, /* bits per symbol */
+ int number_symbols)
+{
+ int n,i,j,k,mask;
+ float num[bps], den[bps];
+ float metric;
+
+ for (n=0; n<number_symbols; n++) { /* loop over symbols */
+ for (k=0;k<bps;k++) {
+ /* initialize */
+ num[k] = -1000000;
+ den[k] = -1000000;
+ }
+
+ for (i=0;i<M;i++) {
+ metric = symbol_likelihood[n*M+i]; /* channel metric for this symbol */
+
+ mask = 1 << (bps - 1);
+ for (j=0;j<bps;j++) {
+ mask = mask >> 1;
+ }
+ mask = 1 << (bps - 1);
+
+ for (k=0;k<bps;k++) { /* loop over bits */
+ if (mask&i) {
+ /* this bit is a one */
+ num[k] = max_star0( num[k], metric );
+ } else {
+ /* this bit is a zero */
+ den[k] = max_star0( den[k], metric );
+ }
+ mask = mask >> 1;
+ }
+ }
+ for (k=0;k<bps;k++) {
+ bit_likelihood[bps*n+k] = num[k] - den[k];
+ }
+ }
+}
+
+
+void symbols_to_llrs(float llr[], COMP rx_qpsk_symbols[], float rx_amps[], float EsNo, float mean_amp, int nsyms) {
+ int i;
+
+ float symbol_likelihood[nsyms*QPSK_CONSTELLATION_SIZE];
+ float bit_likelihood[nsyms*QPSK_BITS_PER_SYMBOL];
+
+ Demod2D(symbol_likelihood, rx_qpsk_symbols, S_matrix, EsNo, rx_amps, mean_amp, nsyms);
+ Somap(bit_likelihood, symbol_likelihood, QPSK_CONSTELLATION_SIZE, QPSK_BITS_PER_SYMBOL, nsyms);
+ for(i=0; i<nsyms*QPSK_BITS_PER_SYMBOL; i++) {
+ llr[i] = -bit_likelihood[i];
+ }
+}
+
+/*
+ Description: Transforms M-dimensional FSK symbols into ML symbol log-likelihoods
+
+ The calling syntax is:
+ [output] = FskDemod( input, EsNo, [csi_flag], [fade_coef] )
+
+ Where:
+ output = M by N matrix of symbol log-likelihoods
+
+ input = M by N matrix of (complex) matched filter outputs
+ EsNo = the symbol SNR (in linear, not dB, units)
+ csi_flag = 0 for coherent reception (default)
+ 1 for noncoherent reception w/ perfect amplitude estimates
+ 2 for noncoherent reception without amplitude estimates
+ fade_coef = 1 by N matrix of (complex) fading coefficients (defaults to all-ones, i.e. AWGN)
+
+ Copyright (C) 2006, Matthew C. Valenti
+
+ Last updated on May 6, 2006
+
+ Function DemodFSK is part of the Iterative Solutions
+ Coded Modulation Library. The Iterative Solutions Coded Modulation
+ Library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2.1 of the License,
+ or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+*/
+
+/* the logI_0 function */
+static float logbesseli0(float x)
+{
+ if (x < 1)
+ return( 0.226*x*x+0.0125*x-0.0012 );
+ else if (x < 2)
+ return( 0.1245*x*x+0.2177*x-0.108 );
+ else if (x < 5)
+ return( 0.0288*x*x+0.6314*x-0.5645 );
+ else if (x < 20)
+ return( 0.002*x*x+0.9048*x-1.2997 );
+ else
+ return(0.9867*x-2.2053);
+}
+
+/* Function that does the demodulation (can be used in stand-alone C) */
+
+static void FskDemod(float out[], float yr[], float v_est, float SNR, int M, int number_symbols)
+{
+ int i, j;
+ float y_envelope, scale_factor;
+
+ scale_factor = 2*SNR;
+ for (i=0;i<number_symbols;i++) {
+ for (j=0;j<M;j++) {
+ y_envelope = sqrt( yr[j*number_symbols+i]*yr[j*number_symbols+i]/(v_est*v_est));
+ out[i*M+j] = logbesseli0( scale_factor*y_envelope );
+ }
+ }
+}
+
+void fsk_rx_filt_to_llrs(float llr[], float rx_filt[], float v_est, float SNRest, int M, int nsyms) {
+ int i;
+ int bps = log2(M);
+ float symbol_likelihood[M*nsyms];
+ float bit_likelihood[bps*nsyms];
+
+ FskDemod(symbol_likelihood, rx_filt, v_est, SNRest, M, nsyms);
+ Somap(bit_likelihood, symbol_likelihood, M, bps, nsyms);
+ for(i=0; i<bps*nsyms; i++) {
+ llr[i] = -bit_likelihood[i];
+ }
+}
+
+void ldpc_print_info(struct LDPC *ldpc) {
+ fprintf(stderr, "ldpc->max_iter = %d\n", ldpc->max_iter);
+ fprintf(stderr, "ldpc->dec_type = %d\n", ldpc->dec_type);
+ fprintf(stderr, "ldpc->q_scale_factor = %d\n", ldpc->q_scale_factor);
+ fprintf(stderr, "ldpc->r_scale_factor = %d\n", ldpc->r_scale_factor);
+ fprintf(stderr, "ldpc->CodeLength = %d\n", ldpc->CodeLength);
+ fprintf(stderr, "ldpc->NumberParityBits = %d\n", ldpc->NumberParityBits);
+ fprintf(stderr, "ldpc->NumberRowsHcols = %d\n", ldpc->NumberRowsHcols);
+ fprintf(stderr, "ldpc->max_row_weight = %d\n", ldpc->max_row_weight);
+ fprintf(stderr, "ldpc->max_col_weight = %d\n", ldpc->max_col_weight);
+ fprintf(stderr, "ldpc->data_bits_per_frame = %d\n", ldpc->data_bits_per_frame);
+ fprintf(stderr, "ldpc->coded_bits_per_frame = %d\n", ldpc->coded_bits_per_frame);
+}