mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-29 12:00:24 -04:00 
			
		
		
		
	
		
			
	
	
		
			475 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			475 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | // qracodes.c
 | ||
|  | // Q-ary RA codes encoding/decoding functions
 | ||
|  | // 
 | ||
|  | // (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
 | ||
|  | // ------------------------------------------------------------------------------
 | ||
|  | // This file is part of the qracodes project, a Forward Error Control
 | ||
|  | // encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
 | ||
|  | //
 | ||
|  | //    qracodes is free software: you can redistribute it and/or modify
 | ||
|  | //    it under the terms of the GNU General Public License as published by
 | ||
|  | //    the Free Software Foundation, either version 3 of the License, or
 | ||
|  | //    (at your option) any later version.
 | ||
|  | //    qracodes 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 General Public License for more details.
 | ||
|  | 
 | ||
|  | //    You should have received a copy of the GNU General Public License
 | ||
|  | //    along with qracodes source distribution.  
 | ||
|  | //    If not, see <http://www.gnu.org/licenses/>.
 | ||
|  | 
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <math.h>
 | ||
|  | 
 | ||
|  | #include "npfwht.h"	
 | ||
|  | #include "pdmath.h"
 | ||
|  | 
 | ||
|  | #include "qracodes.h"
 | ||
|  | 
 | ||
|  | int qra_encode(const qracode *pcode, int *y, const int *x) | ||
|  | { | ||
|  | 	int k,j,kk,jj; | ||
|  | 	int t, chk = 0; | ||
|  | 
 | ||
|  | 	const int K = pcode->K; | ||
|  | 	const int M = pcode->M; | ||
|  | 	const int NC= pcode->NC; | ||
|  | 	const int a = pcode->a; | ||
|  | 	const int  *acc_input_idx  = pcode->acc_input_idx; | ||
|  | 	const int *acc_input_wlog = pcode->acc_input_wlog; | ||
|  | 	const int  *gflog		   = pcode->gflog; | ||
|  | 	const int *gfexp          = pcode->gfexp; | ||
|  | 
 | ||
|  | 	// copy the systematic symbols to destination
 | ||
|  | 	memcpy(y,x,K*sizeof(int)); | ||
|  | 
 | ||
|  | 	y = y+K;	// point to check symbols
 | ||
|  | 
 | ||
|  | 	// compute the code check symbols as a weighted accumulation of a permutated
 | ||
|  | 	// sequence of the (repeated) systematic input symbols:
 | ||
|  | 	// chk(k+1) = x(idx(k))*alfa^(logw(k)) + chk(k)
 | ||
|  | 	// (all operations performed over GF(M))
 | ||
|  | 
 | ||
|  | 	if (a==1) { // grouping factor = 1
 | ||
|  | 		for (k=0;k<NC;k++) { | ||
|  | 			t = x[acc_input_idx[k]]; | ||
|  | 			if (t) { | ||
|  | 				// multiply input by weight[k] and xor it with previous check
 | ||
|  | 				t = (gflog[t] + acc_input_wlog[k])%(M-1); | ||
|  | 				t = gfexp[t]; | ||
|  | 				chk ^=t; | ||
|  | 				} | ||
|  | 			y[k] = chk; | ||
|  | 			} | ||
|  | 
 | ||
|  | 		#ifdef QRA_DEBUG
 | ||
|  | 			// verify that the encoder accumulator is terminated to 0
 | ||
|  | 			// (we designed the code this way so that Iex = 1 when Ia = 1)
 | ||
|  | 			t = x[acc_input_idx[k]]; | ||
|  | 			if (t) { | ||
|  | 				t = (gflog[t] + acc_input_wlog[k])%(M-1); | ||
|  | 				t = gfexp[t]; | ||
|  | 				// accumulation
 | ||
|  | 				chk ^=t; | ||
|  | 				} | ||
|  | 			return (chk==0); | ||
|  | 		#else
 | ||
|  | 			return 1; | ||
|  | 		#endif // QRA_DEBUG
 | ||
|  | 		} | ||
|  | 	else { // grouping factor > 1
 | ||
|  | 		for (k=0;k<NC;k++) { | ||
|  | 			kk = a*k; | ||
|  | 			for (j=0;j<a;j++) { | ||
|  | 				jj = kk+j; | ||
|  | 				// irregular grouping support
 | ||
|  | 				if (acc_input_idx[jj]<0) | ||
|  | 					continue; | ||
|  | 				t = x[acc_input_idx[jj]]; | ||
|  | 				if (t) { | ||
|  | 					// multiply input by weight[k] and xor it with previous check
 | ||
|  | 					t = (gflog[t] + acc_input_wlog[jj])%(M-1); | ||
|  | 					t = gfexp[t]; | ||
|  | 					chk ^=t; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			y[k] = chk; | ||
|  | 			} | ||
|  | 		#ifdef QRA_DEBUG
 | ||
|  | 			// verify that the encoder accumulator is terminated to 0
 | ||
|  | 			// (we designed the code this way so that Iex = 1 when Ia = 1)
 | ||
|  | 			kk = a*k; | ||
|  | 			for (j=0;j<a;j++) { | ||
|  | 				jj = kk+j; | ||
|  | 				if (acc_input_idx[jj]<0) | ||
|  | 					continue; | ||
|  | 				t = x[acc_input_idx[jj]]; | ||
|  | 				if (t) { | ||
|  | 					// multiply input by weight[k] and xor it with previous check
 | ||
|  | 					t = (gflog[t] + acc_input_wlog[jj])%(M-1); | ||
|  | 					t = gfexp[t]; | ||
|  | 					chk ^=t; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			return (chk==0); | ||
|  | 		#else
 | ||
|  | 			return 1; | ||
|  | 		#endif // QRA_DEBUG
 | ||
|  | 		}  | ||
|  | } | ||
|  | 
 | ||
|  | static void qra_ioapprox(float *src, float C, int nitems) | ||
|  | { | ||
|  | 	// In place approximation of the modified bessel function I0(x*C)
 | ||
|  | 	// Computes src[k] = Io(src[k]*C) where Io() is the modified Bessel function of first kind and order 0
 | ||
|  | 
 | ||
|  | 	float v; | ||
|  | 	float vsq; | ||
|  | 
 | ||
|  | 	while (nitems--) { | ||
|  | 		v = src[nitems]*C; | ||
|  | 
 | ||
|  | 		// rational approximation of log(Io(v))
 | ||
|  | 		vsq = v*v; | ||
|  | 		v = vsq*(v+0.039f)/(vsq*.9931f+v*2.6936f+0.5185f); | ||
|  | 
 | ||
|  | 		if (v>80.f) // avoid floating point exp() overflows 
 | ||
|  | 			v=80.f; | ||
|  | 
 | ||
|  | 		src[nitems] = (float)exp(v); | ||
|  | 		} | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | float qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric) | ||
|  | { | ||
|  | 	// Computes the codeword symbols intrinsic probabilities
 | ||
|  | 	// given the square of the received input amplitudes.
 | ||
|  | 
 | ||
|  | 	// The input vector rqs must be a linear array of size M*N, where M=2^m,
 | ||
|  | 	// containing the squared amplitudes (rp*rp+rq*rq) of the input samples
 | ||
|  | 
 | ||
|  | 	// First symbol amplitudes should be stored in the first M positions,
 | ||
|  | 	// second symbol amplitudes stored at positions [M ... 2*M-1], and so on.
 | ||
|  | 
 | ||
|  | 	// Output vector is the intrinsic symbol metric (the probability distribution)
 | ||
|  | 	// assuming that symbols are transmitted using a M-FSK modulation 
 | ||
|  | 	// and incoherent demodulation.
 | ||
|  | 
 | ||
|  | 	// As the input Es/No is generally unknown (as it cannot be exstimated accurately
 | ||
|  | 	// when the codeword length is few tens symbols) but an exact metric requires it
 | ||
|  | 	// we simply fix it to a predefined EsNoMetric value so that the metric is what
 | ||
|  | 	// expected at that specific value.
 | ||
|  | 	// The metric computed in this way is optimal only at this predefined Es/No value,
 | ||
|  | 	// nevertheless it is usually better than a generic parameter-free metric which
 | ||
|  | 	// makes no assumptions on the input Es/No.
 | ||
|  | 
 | ||
|  | 	// returns the estimated noise standard deviation
 | ||
|  | 
 | ||
|  | 	int k; | ||
|  | 	float rsum = 0.f; | ||
|  | 	float sigmaest, cmetric; | ||
|  | 
 | ||
|  | 	const int M = 1<<m; | ||
|  | 	const int nsamples = M*N; | ||
|  | 
 | ||
|  | 	// compute total power and modulus of input signal
 | ||
|  | 	for (k=0;k<nsamples;k++) { | ||
|  | 		rsum = rsum+rsq[k]; | ||
|  | 		pix[k] = (float)sqrt(rsq[k]); | ||
|  | 		} | ||
|  | 
 | ||
|  | 	rsum = rsum/nsamples;	// average S+N	
 | ||
|  | 
 | ||
|  | 	// IMPORTANT NOTE: in computing the noise stdev it is assumed that 
 | ||
|  | 	// in the input amplitudes there's no strong interference!
 | ||
|  | 	// A more robust estimation can be done evaluating the histogram of the input amplitudes
 | ||
|  | 
 | ||
|  | 	sigmaest = (float)sqrt(rsum/(1.0f+EsNoMetric/M)/2); // estimated noise stdev
 | ||
|  | 	cmetric = (float)sqrt(2*EsNoMetric)/sigmaest; | ||
|  | 
 | ||
|  | 	for (k=0;k<N;k++) { | ||
|  | 		// compute bessel metric for each symbol in the codeword
 | ||
|  | 		qra_ioapprox(PD_ROWADDR(pix,M,k),cmetric,M); | ||
|  | 		// normalize to a probability distribution
 | ||
|  | 		pd_norm(PD_ROWADDR(pix,M,k),m); | ||
|  | 		} | ||
|  | 
 | ||
|  | 	return sigmaest; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | #ifdef QRA_DEBUG
 | ||
|  | void pd_print(int imsg,float *ppd,int size) | ||
|  | { | ||
|  | 	int k; | ||
|  | 	printf("imsg=%d\n",imsg); | ||
|  | 	for (k=0;k<size;k++) | ||
|  | 		printf("%7.1e ",ppd[k]); | ||
|  | 	printf("\n"); | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | 
 | ||
|  | #define ADDRMSG(fp, msgidx)    PD_ROWADDR(fp,qra_M,msgidx)
 | ||
|  | #define C2VMSG(msgidx)         PD_ROWADDR(qra_c2vmsg,qra_M,msgidx)
 | ||
|  | #define V2CMSG(msgidx)         PD_ROWADDR(qra_v2cmsg,qra_M,msgidx)
 | ||
|  | #define MSGPERM(logw)          PD_ROWADDR(qra_pmat,qra_M,logw)
 | ||
|  | 
 | ||
|  | #define QRACODE_MAX_M	256	// Maximum alphabet size handled by qra_extrinsic
 | ||
|  | 
 | ||
|  | int qra_extrinsic(const qracode *pcode,  | ||
|  | 				  float *pex,  | ||
|  | 				  const float *pix,  | ||
|  | 				  int maxiter, | ||
|  | 				  float *qra_v2cmsg, | ||
|  | 				  float *qra_c2vmsg) | ||
|  | { | ||
|  | 	const int qra_M		= pcode->M; | ||
|  | 	const int qra_m		= pcode->m; | ||
|  | 	const int qra_V		= pcode->V; | ||
|  | 	const int qra_MAXVDEG  = pcode->MAXVDEG; | ||
|  | 	const int  *qra_vdeg    = pcode->vdeg; | ||
|  | 	const int qra_C		= pcode->C; | ||
|  | 	const int qra_MAXCDEG  = pcode->MAXCDEG; | ||
|  | 	const int *qra_cdeg    = pcode->cdeg; | ||
|  | 	const int  *qra_v2cmidx = pcode->v2cmidx; | ||
|  | 	const int  *qra_c2vmidx = pcode->c2vmidx; | ||
|  | 	const int  *qra_pmat    = pcode->gfpmat; | ||
|  | 	const int *qra_msgw    = pcode->msgw; | ||
|  | 
 | ||
|  | //	float msgout[qra_M];		 // buffer to store temporary results
 | ||
|  | 	float msgout[QRACODE_MAX_M]; // we use a fixed size in order to avoid mallocs
 | ||
|  | 
 | ||
|  | 	float totex;	// total extrinsic information
 | ||
|  | 	int nit;		// current iteration
 | ||
|  | 	int nv;		// current variable
 | ||
|  | 	int nc;		// current check
 | ||
|  | 	int k,kk;		// loop indexes
 | ||
|  | 
 | ||
|  | 	int ndeg;		// current node degree
 | ||
|  | 	int msgbase;	// current offset in the table of msg indexes
 | ||
|  | 	int imsg;		// current message index
 | ||
|  | 	int wmsg;		// current message weight
 | ||
|  | 
 | ||
|  | 	int rc     = -1; // rc>=0  extrinsic converged to 1 at iteration rc (rc=0..maxiter-1)
 | ||
|  | 					 // rc=-1  no convergence in the given number of iterations
 | ||
|  | 					 // rc=-2  error in the code tables (code checks degrees must be >1)
 | ||
|  | 					 // rc=-3  M is larger than QRACODE_MAX_M
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 	if (qra_M>QRACODE_MAX_M) | ||
|  | 		return -3; | ||
|  | 
 | ||
|  | 	// message initialization -------------------------------------------------------
 | ||
|  | 
 | ||
|  | 	// init c->v variable intrinsic msgs
 | ||
|  | 	pd_init(C2VMSG(0),pix,qra_M*qra_V); | ||
|  | 
 | ||
|  | 	// init the v->c messages directed to code factors (k=1..ndeg) with the intrinsic info
 | ||
|  | 	for (nv=0;nv<qra_V;nv++) { | ||
|  | 
 | ||
|  | 		ndeg = qra_vdeg[nv];		// degree of current node
 | ||
|  | 		msgbase = nv*qra_MAXVDEG;	// base to msg index row for the current node
 | ||
|  | 
 | ||
|  | 		// copy intrinsics on v->c
 | ||
|  | 		for (k=1;k<ndeg;k++) {		 | ||
|  | 			imsg = qra_v2cmidx[msgbase+k]; | ||
|  | 			pd_init(V2CMSG(imsg),ADDRMSG(pix,nv),qra_M); | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 	// message passing algorithm iterations ------------------------------
 | ||
|  | 
 | ||
|  | 	for (nit=0;nit<maxiter;nit++) { | ||
|  | 
 | ||
|  | 		// c->v step -----------------------------------------------------
 | ||
|  | 		// Computes messages from code checks to code variables.
 | ||
|  | 		// As the first qra_V checks are associated with intrinsic information
 | ||
|  | 		// (the code tables have been constructed in this way)
 | ||
|  | 		// we need to do this step only for code checks in the range [qra_V..qra_C)
 | ||
|  | 
 | ||
|  | 		// The convolutions of probability distributions over the alphabet of a finite field GF(qra_M)
 | ||
|  | 		// are performed with a fast convolution algorithm over the given field.
 | ||
|  | 		// 
 | ||
|  | 		// I.e. given the code check x1+x2+x3 = 0 (with x1,x2,x3 in GF(2^m))
 | ||
|  | 		// and given Prob(x2) and Prob(x3), we have that:
 | ||
|  | 		// Prob(x1=X1) = Prob((x2+x3)=X1) = sum((Prob(x2=X2)*Prob(x3=(X1+X2))) for all the X2s in the field
 | ||
|  | 		// This translates to Prob(x1) = IWHT(WHT(Prob(x2))*WHT(Prob(x3)))
 | ||
|  | 		// where WHT and IWHT are the direct and inverse Walsh-Hadamard transforms of the argument.
 | ||
|  | 		// Note that the WHT and the IWHF differs only by a multiplicative coefficent and since in this step 
 | ||
|  | 		// we don't need that the output distribution is normalized we use the relationship
 | ||
|  | 		// Prob(x1) =(proportional to) WH(WH(Prob(x2))*WH(Prob(x3)))
 | ||
|  | 
 | ||
|  | 		// In general given the check code x1+x2+x3+..+xm = 0
 | ||
|  | 		// the output distribution of a variable given the distributions of the other m-1 variables
 | ||
|  | 		// is the inverse WHT of the product of the WHTs of the distribution of the other m-1 variables
 | ||
|  | 		// The complexity of this algorithm scales with M*log2(M) instead of the M^2 complexity of
 | ||
|  | 		// the brute force approach (M=size of the alphabet)
 | ||
|  | 
 | ||
|  | 		for (nc=qra_V;nc<qra_C;nc++) { | ||
|  | 
 | ||
|  | 			ndeg = qra_cdeg[nc];		// degree of current node
 | ||
|  | 
 | ||
|  | 			if (ndeg==1) 				// this should never happen (code factors must have deg>1)
 | ||
|  | 				return -2;				// bad code tables
 | ||
|  | 
 | ||
|  | 			msgbase = nc*qra_MAXCDEG;	// base to msg index row for the current node
 | ||
|  | 
 | ||
|  | 			// transforms inputs in the Walsh-Hadamard "frequency" domain
 | ||
|  | 			// v->c  -> fwht(v->c)
 | ||
|  | 			for (k=0;k<ndeg;k++) {		 | ||
|  | 				imsg = qra_c2vmidx[msgbase+k];		// msg index
 | ||
|  | 				np_fwht(qra_m,V2CMSG(imsg),V2CMSG(imsg)); // compute fwht 
 | ||
|  | 				} | ||
|  | 
 | ||
|  | 			// compute products and transform them back in the WH "time" domain
 | ||
|  | 			for (k=0;k<ndeg;k++) { | ||
|  | 
 | ||
|  | 				// init output message to uniform distribution
 | ||
|  | 				pd_init(msgout,pd_uniform(qra_m),qra_M); | ||
|  | 
 | ||
|  | 				// c->v = prod(fwht(v->c))
 | ||
|  | 				// TODO: we assume that checks degrees are not larger than three but
 | ||
|  | 				// if they are larger the products can be computed more efficiently
 | ||
|  | 				for (kk=0;kk<ndeg;kk++)  | ||
|  | 					if (kk!=k) { | ||
|  | 						imsg = qra_c2vmidx[msgbase+kk]; | ||
|  | 						pd_imul(msgout,V2CMSG(imsg),qra_m); | ||
|  | 						} | ||
|  | 
 | ||
|  | 				// transform product back in the WH "time" domain
 | ||
|  | 				 | ||
|  | 				// Very important trick: 
 | ||
|  | 				// we bias WHT[0] so that the sum of output pd components is always strictly positive
 | ||
|  | 				// this helps avoiding the effects of underflows in the v->c steps when multipling 
 | ||
|  | 				// small fp numbers
 | ||
|  | 				msgout[0]+=1E-7f;	// TODO: define the bias accordingly to the field size
 | ||
|  | 
 | ||
|  | 				np_fwht(qra_m,msgout,msgout);  | ||
|  | 			 | ||
|  | 				// inverse weight and output 
 | ||
|  | 				imsg = qra_c2vmidx[msgbase+k]; // current output msg index
 | ||
|  | 				wmsg = qra_msgw[imsg];		   // current msg weight
 | ||
|  | 
 | ||
|  | 				if (wmsg==0) | ||
|  | 					pd_init(C2VMSG(imsg),msgout,qra_M); | ||
|  | 				else | ||
|  | 					// output p(alfa^(-w)*x)
 | ||
|  | 					pd_bwdperm(C2VMSG(imsg),msgout, MSGPERM(wmsg), qra_M); | ||
|  | 
 | ||
|  | 				} // for (k=0;k<ndeg;k++)
 | ||
|  | 
 | ||
|  | 			} // for (nc=qra_V;nc<qra_C;nc++)
 | ||
|  | 
 | ||
|  | 		// v->c step -----------------------------------------------------
 | ||
|  | 		for (nv=0;nv<qra_V;nv++) { | ||
|  | 
 | ||
|  | 			ndeg = qra_vdeg[nv];		// degree of current node
 | ||
|  | 			msgbase = nv*qra_MAXVDEG;	// base to msg index row for the current node
 | ||
|  | 
 | ||
|  | 			for (k=0;k<ndeg;k++) { | ||
|  | 				// init output message to uniform distribution
 | ||
|  | 				pd_init(msgout,pd_uniform(qra_m),qra_M); | ||
|  | 
 | ||
|  | 				// v->c msg = prod(c->v)
 | ||
|  | 				// TODO: factor factors to reduce the number of computations for high degree nodes
 | ||
|  | 				for (kk=0;kk<ndeg;kk++)  | ||
|  | 					if (kk!=k) { | ||
|  | 						imsg = qra_v2cmidx[msgbase+kk]; | ||
|  | 						pd_imul(msgout,C2VMSG(imsg),qra_m); | ||
|  | 						} | ||
|  | 
 | ||
|  | #ifdef QRA_DEBUG
 | ||
|  | // normalize and check if product of messages v->c are null
 | ||
|  | 				// normalize output to a probability distribution
 | ||
|  | 				if (pd_norm(msgout,qra_m)<=0) { | ||
|  | 					// dump msgin;
 | ||
|  | 					printf("warning: v->c pd with invalid norm. nit=%d nv=%d k=%d\n",nit,nv,k); | ||
|  | 					for (kk=0;kk<ndeg;kk++) { | ||
|  | 						imsg = qra_v2cmidx[msgbase+kk]; | ||
|  | 						pd_print(imsg,C2VMSG(imsg),qra_M); | ||
|  | 						} | ||
|  | 					printf("-----------------\n"); | ||
|  | 					} | ||
|  | #else
 | ||
|  | 				// normalize the result to a probability distribution
 | ||
|  | 				pd_norm(msgout,qra_m); | ||
|  | #endif
 | ||
|  | 				// weight and output 
 | ||
|  | 				imsg = qra_v2cmidx[msgbase+k]; // current output msg index
 | ||
|  | 				wmsg = qra_msgw[imsg];		   // current msg weight
 | ||
|  | 
 | ||
|  | 				if (wmsg==0) { | ||
|  | 					pd_init(V2CMSG(imsg),msgout,qra_M); | ||
|  | 					} | ||
|  | 				else { | ||
|  | 					// output p(alfa^w*x)
 | ||
|  | 					pd_fwdperm(V2CMSG(imsg),msgout, MSGPERM(wmsg), qra_M); | ||
|  | 					} | ||
|  | 
 | ||
|  | 				} // for (k=0;k<ndeg;k++)
 | ||
|  | 			} // for (nv=0;nv<qra_V;nv++)
 | ||
|  | 
 | ||
|  | 		// check extrinsic information ------------------------------
 | ||
|  | 		// We assume that decoding is successful if each of the extrinsic
 | ||
|  | 		// symbol probability is close to ej, where ej = [0 0 0 1(j-th position) 0 0 0 ] 
 | ||
|  | 		// Therefore, for each symbol k in the codeword we compute max(prob(Xk)) 
 | ||
|  | 		// and we stop the iterations if sum(max(prob(xk)) is close to the codeword length
 | ||
|  | 		// Note: this is a more restrictive criterium than that of computing the a 
 | ||
|  | 		// posteriori probability of each symbol, making a hard decision and then check 
 | ||
|  | 		// if the codeword syndrome is null.
 | ||
|  | 		// WARNING: this is tricky and probably works only for the particular class of RA codes 
 | ||
|  | 		// we are coping with (we designed the code weights so that for any input symbol the
 | ||
|  | 		// sum of its weigths is always 0, thus terminating the accumulator trellis to zero
 | ||
|  | 		// for every combination of the systematic symbols).
 | ||
|  | 		// More generally we should instead compute the max a posteriori probabilities
 | ||
|  | 		// (as a product of the intrinsic and extrinsic information), make a symbol by symbol hard
 | ||
|  | 		// decision and then check that the syndrome of the result is indeed null.
 | ||
|  | 
 | ||
|  | 		totex = 0; | ||
|  | 		for (nv=0;nv<qra_V;nv++)  | ||
|  | 			totex += pd_max(V2CMSG(nv),qra_M); | ||
|  | 
 | ||
|  | 		if (totex>(1.*(qra_V)-0.01)) { | ||
|  | 			// the total maximum extrinsic information of each symbol in the codeword
 | ||
|  | 			// is very close to one. This means that we have reached the (1,1) point in the
 | ||
|  | 			// code EXIT chart(s) and we have successfully decoded the input.
 | ||
|  | 			rc = nit; | ||
|  | 			break;	// remove the break to evaluate the decoder speed performance as a function of the max iterations number)
 | ||
|  | 			} | ||
|  | 
 | ||
|  | 		} // for (nit=0;nit<maxiter;nit++)
 | ||
|  | 
 | ||
|  | 	// copy extrinsic information to output to do the actual max a posteriori prob decoding
 | ||
|  | 	pd_init(pex,V2CMSG(0),(qra_M*qra_V)); | ||
|  | 	return rc; | ||
|  | } | ||
|  | 
 | ||
|  | void qra_mapdecode(const qracode *pcode, int *xdec, float *pex, const float *pix) | ||
|  | { | ||
|  | // Maximum a posteriori probability decoding.
 | ||
|  | // Given the intrinsic information (pix) and extrinsic information (pex) (computed with qra_extrinsic(...))
 | ||
|  | // compute pmap = pex*pix and decode each (information) symbol of the received codeword
 | ||
|  | // as the symbol which maximizes pmap
 | ||
|  | 
 | ||
|  | // Returns:
 | ||
|  | //	xdec[k] = decoded (information) symbols k=[0..qra_K-1]
 | ||
|  | 
 | ||
|  | //  Note: pex is destroyed and overwritten with mapp
 | ||
|  | 
 | ||
|  | 	const int qra_M		= pcode->M; | ||
|  | 	const int qra_m		= pcode->m; | ||
|  | 	const int qra_K		= pcode->K; | ||
|  | 
 | ||
|  | 	int k; | ||
|  | 
 | ||
|  | 	for (k=0;k<qra_K;k++) { | ||
|  | 		// compute a posteriori prob
 | ||
|  | 		pd_imul(PD_ROWADDR(pex,qra_M,k),PD_ROWADDR(pix,qra_M,k),qra_m); | ||
|  | 		xdec[k]=pd_argmax(NULL, PD_ROWADDR(pex,qra_M,k), qra_M); | ||
|  | 		} | ||
|  | } |