// main.c // Word Error Rate test example for Q-ary RA codes over GF(64) // // (c) 2016 - Nico Palermo, IV3NWV // // Thanks to Andrea Montefusco IW0HDV for his help on adapting the sources // to OSs other than MS Windows // // ------------------------------------------------------------------------------ // 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. // // Files in this package: // main.c - this file // normrnd.c/.h - random gaussian number generator // npfwht.c/.h - Fast Walsh-Hadamard Transforms // pdmath.c/.h - Elementary math on probability distributions // qra12_63_64_irr_b.c/.h - Tables for a QRA(12,63) irregular RA code over GF(64) // qra13_64_64_irr_e.c/.h - Tables for a QRA(13,64) irregular RA code " " // qracodes.c/.h - QRA codes encoding/decoding functions // // ------------------------------------------------------------------------------- // // 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 . // ----------------------------------------------------------------------------- // Two codes are available for simulations in this sowftware release: // QRA12_63_64_IRR_B: K=12 N=63 Q=64 irregular QRA code (defined in qra12_63_64_irr_b.h /.c) // QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_b.h /.c) // Codes with K=13 are designed to include a CRC as the 13th information symbol // and improve the code UER (Undetected Error Rate). // The CRC symbol is not sent along the channel (the codes are punctured) and the // resulting code is still a (12,63) code with an effective code rate of R = 12/63. // ------------------------------------------------------------------------------ // OS dependent defines and includes -------------------------------------------- #if _WIN32 // note the underscore: without it, it's not msdn official! // Windows (x64 and x86) #include // required only for GetTickCount(...) #include // _beginthread #endif #if defined(__linux__) // remove unwanted macros #define __cdecl // implements Windows API #include unsigned int GetTickCount(void) { struct timespec ts; unsigned int theTick = 0U; clock_gettime( CLOCK_REALTIME, &ts ); theTick = ts.tv_nsec / 1000000; theTick += ts.tv_sec * 1000; return theTick; } // Convert Windows millisecond sleep // // VOID WINAPI Sleep(_In_ DWORD dwMilliseconds); // // to Posix usleep (in microseconds) // // int usleep(useconds_t usec); // #include #define Sleep(x) usleep(x*1000) #endif #if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) ) #include #endif #if __APPLE__ #endif #include #include #include "qracodes.h" #include "normrnd.h" // gaussian numbers generator #include "pdmath.h" // operations on probability distributions // defined codes #include "qra12_63_64_irr_b.h" #include "qra13_64_64_irr_e.h" // ----------------------------------------------------------------------------------- #define NTHREADS_MAX 160 // channel types #define CHANNEL_AWGN 0 #define CHANNEL_RAYLEIGH 1 // amount of a-priori information provided to the decoder #define AP_NONE 0 #define AP_28 1 #define AP_44 2 #define AP_56 3 const char ap_str[4][16] = { "None", "28 bit", "44 bit", "56 bit" }; const char fnameout_pfx[2][64] = { "wer-awgn-", "wer-rayleigh-" }; const char fnameout_sfx[4][64] = { "-ap00.txt", "-ap28.txt", "-ap44.txt", "-ap56.txt" }; const int ap_masks_jt65[4][13] = { // Each row must be 13 entries long (to handle puntc. codes 13,64) // The mask of 13th symbol (crc) is alway initializated to 0 // AP0 - no a-priori knowledge { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // AP28 - 1st field known [cq ? ?] or [dst ? ?] {0x3F,0x3F,0x3F,0x3F,0x3C, 0, 0, 0, 0, 0, 0, 0}, // AP44 - 1st and 3rd fields known [cq ? 0] or [dst ? 0] {0x3F,0x3F,0x3F,0x3F,0x3C, 0, 0, 0, 0,0x0F,0x3F,0x3F}, // AP56 - 1st and 2nd fields known [dst src ?] {0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x30, 0, 0} }; void ix_mask(const qracode *pcode, float *r, const int *mask, const int *x); void printword(char *msg, int *x, int size) { int k; printf("\n%s ",msg); for (k=0;kc msg buffer float *qra_c2vmsg; //[qra_NMSG*qra_M]; MP decoder c->v msg buffer float *rp; // [qra_N*qra_M]; received samples (real component) buffer float *rq; // [qra_N*qra_M]; received samples (imag component) buffer float *chp; //[qra_N]; channel gains (real component) buffer float *chq; //[qra_N]; channel gains (imag component) buffer float *r; //[qra_N*qra_M]; received samples (amplitude) buffer float *ix; // [qra_N*qra_M]; // intrinsic information to the MP algorithm float *ex; // [qra_N*qra_M]; // extrinsic information from the MP algorithm } wer_test_ds; typedef void( __cdecl *pwer_test_thread)(wer_test_ds*); // crc-6 generator polynomial // g(x) = x^6 + a5*x^5 + ... + a1*x + a0 // g(x) = x^6 + x + 1 #define CRC6_GEN_POL 0x30 // MSB=a0 LSB=a5 // g(x) = x^6 + x^2 + x + 1 (as suggested by Joe. See: https://users.ece.cmu.edu/~koopman/crc/) // #define CRC6_GEN_POL 0x38 // MSB=a0 LSB=a5. Simulation results are similar int calc_crc6(int *x, int sz) { int k,j,t,sr = 0; for (k=0;k>1) ^ CRC6_GEN_POL; else sr = (sr>>1); t>>=1; } } return sr; } void wer_test_thread(wer_test_ds *pdata) { const qracode *pcode=pdata->pcode; const int qra_K = pcode->K; const int qra_N = pcode->N; const int qra_M = pcode->M; const int qra_m = pcode->m; const int NSAMPLES = pcode->N*pcode->M; const float No = 1.0f; // noise spectral density const float sigma = (float)sqrt(No/2.0f); // std dev of noise I/Q components const float sigmach = (float)sqrt(1/2.0f); // std dev of channel I/Q gains // Eb/No value for which we optimize the bessel metric const float EbNodBMetric = 2.8f; const float EbNoMetric = (float)pow(10,EbNodBMetric/10); int k,t,j,diff; float R; float EsNoMetric; float EbNo, EsNo, Es, A; int channel_type, code_type; int nt=0; // transmitted codewords int nerrs = 0; // total number of errors int nerrsu = 0; // number of undetected errors int rc; // inizialize pointer to required buffers int *x=pdata->x; // message buffer int *y=pdata->y, *ydec=pdata->ydec; // encoded/decoded codeword buffers float *qra_v2cmsg=pdata->qra_v2cmsg; // table of the v->c messages float *qra_c2vmsg=pdata->qra_c2vmsg; // table of the c->v messages float *rp=pdata->rp; // received samples (real component) float *rq=pdata->rq; // received samples (imag component) float *chp=pdata->chp; // channel gains (real component) float *chq=pdata->chq; // channel gains (imag component) float *r=pdata->r; // received samples amplitudes float *ix=pdata->ix; // intrinsic information to the MP algorithm float *ex=pdata->ex; // extrinsic information from the MP algorithm channel_type = pdata->channel_type; code_type = pcode->type; // define the (true) code rate accordingly to the code type switch(code_type) { case QRATYPE_CRC: R = 1.0f*(qra_K-1)/qra_N; break; case QRATYPE_CRCPUNCTURED: R = 1.0f*(qra_K-1)/(qra_N-1); break; case QRATYPE_NORMAL: default: R = 1.0f*(qra_K)/(qra_N); } EsNoMetric = 1.0f*qra_m*R*EbNoMetric; EbNo = (float)pow(10,pdata->EbNodB/10); EsNo = 1.0f*qra_m*R*EbNo; Es = EsNo*No; A = (float)sqrt(Es); // encode the input if (code_type==QRATYPE_CRC || code_type==QRATYPE_CRCPUNCTURED) { // compute the information message symbol check as the (negated) xor of all the // information message symbols for (k=0;k<(qra_K-1);k++) x[k]=k%qra_M; x[k]=calc_crc6(x,qra_K-1); } else for (k=0;kstop==0) { // simulate the channel // NOTE: in the case that the code is punctured, for simplicity // we compute the channel outputs and the metric also for the crc symbol // then we ignore its observation. normrnd_s(rp,NSAMPLES,0,sigma); normrnd_s(rq,NSAMPLES,0,sigma); if (channel_type == CHANNEL_AWGN) { for (k=0;kdone = 1; return; // unknown channel type } // compute the squares of the amplitudes of the received samples for (k=0;km,pcode->N,EsNoMetric); if (code_type==QRATYPE_CRCPUNCTURED) { // ignore observations of the CRC symbol as it is not actually sent // over the channel pd_init(PD_ROWADDR(ix,qra_M,qra_K),pd_uniform(qra_m),qra_M); } if (pdata->ap_index!=0) // mask channel observations with a priori knowledge ix_mask(pcode,ix,ap_masks_jt65[pdata->ap_index],x); // compute the extrinsic symbols probabilities with the message-passing algorithm // stop if extrinsic information does not converges to 1 within the given number of iterations rc = qra_extrinsic(pcode,ex,ix,100,qra_v2cmsg,qra_c2vmsg); if (rc>=0) { // the MP algorithm converged to Iex~1 in rc iterations // decode the codeword qra_mapdecode(pcode,ydec,ex,ix); // look for undetected errors if (code_type==QRATYPE_CRC || code_type==QRATYPE_CRCPUNCTURED) { j = 0; diff = 0; for (k=0;k<(qra_K-1);k++) diff |= (ydec[k]!=x[k]); t = calc_crc6(ydec,qra_K-1); if (t!=ydec[k]) // error detected - crc doesn't matches nerrs += 1; else if (diff) { // decoded message is not equal to the transmitted one but // the crc test passed // add as undetected error nerrsu += 1; nerrs += 1; // uncomment to see what the undetected error pattern looks like //printword("U", ydec); } } else for (k=0;knt=nt; pdata->nerrs=nerrs; pdata->nerrsu=nerrsu; } pdata->done=1; #if _WIN32 _endthread(); #endif } #if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) ) void *wer_test_pthread(void *p) { wer_test_thread ((wer_test_ds *)p); return 0; } #endif void ix_mask(const qracode *pcode, float *r, const int *mask, const int *x) { // mask intrinsic information (channel observations) with a priori knowledge int k,kk, smask; const int qra_K=pcode->K; const int qra_M=pcode->M; const int qra_m=pcode->m; for (k=0;kNTHREADS_MAX) { printf("Error: nthreads should be <=%d\n",NTHREADS_MAX); return -1; } sprintf(fnameout,"%s%s%s", fnameout_pfx[chtype], pcode->name, fnameout_sfx[ap_index]); fout = fopen(fnameout,"w"); fprintf(fout,"# Channel (0=AWGN,1=Rayleigh), Eb/No (dB), Transmitted codewords, Errors, Undetected Errors, Avg dec. time (ms), WER\n"); printf("\nTesting the code %s over the %s channel\nSimulation data will be saved to %s\n", pcode->name, chtype==CHANNEL_AWGN?"AWGN":"Rayleigh", fnameout); fflush (stdout); // init fixed thread parameters and preallocate buffers for (j=0;jK*sizeof(int)); wt[j].y = (int*)malloc(pcode->N*sizeof(int)); wt[j].ydec = (int*)malloc(pcode->N*sizeof(int)); wt[j].qra_v2cmsg = (float*)malloc(pcode->NMSG*pcode->M*sizeof(float)); wt[j].qra_c2vmsg = (float*)malloc(pcode->NMSG*pcode->M*sizeof(float)); wt[j].rp = (float*)malloc(pcode->N*pcode->M*sizeof(float)); wt[j].rq = (float*)malloc(pcode->N*pcode->M*sizeof(float)); wt[j].chp = (float*)malloc(pcode->N*sizeof(float)); wt[j].chq = (float*)malloc(pcode->N*sizeof(float)); wt[j].r = (float*)malloc(pcode->N*pcode->M*sizeof(float)); wt[j].ix = (float*)malloc(pcode->N*pcode->M*sizeof(float)); wt[j].ex = (float*)malloc(pcode->N*pcode->M*sizeof(float)); } for (k=0;k=nerrstgt[k]) { for (j=0;j] [-t] [-c] [-a] [-f[-h]\n"); printf("Options: \n"); printf(" -q: code to simulate. 0=qra_12_63_64_irr_b\n"); printf(" 1=qra_13_64_64_irr_e (default)\n"); printf(" -t : number of threads to be used for the simulation [1..24]\n"); printf(" (default=8)\n"); printf(" -c : channel_type. 0=AWGN 1=Rayleigh \n"); printf(" (default=AWGN)\n"); printf(" -a : amount of a-priori information provided to decoder. \n"); printf(" 0= No a-priori (default)\n"); printf(" 1= 28 bit \n"); printf(" 2= 44 bit \n"); printf(" 3= 56 bit \n"); printf(" -f : name of the file containing the Eb/No values to be simulated\n"); printf(" (default=ebnovalues.txt)\n"); printf(" This file should contain lines in this format:\n"); printf(" # Eb/No(dB) Target Errors\n"); printf(" 0.1 5000\n"); printf(" 0.6 5000\n"); printf(" 1.1 1000\n"); printf(" 1.6 1000\n"); printf(" ...\n"); printf(" (lines beginning with a # are treated as comments\n\n"); } #define SIM_POINTS_MAX 20 int main(int argc, char* argv[]) { float EbNodB[SIM_POINTS_MAX]; int nerrstgt[SIM_POINTS_MAX]; FILE *fin; char fnamein[128]= "ebnovalues.txt"; char buf[128]; int nitems = 0; int code_idx = 1; int nthreads = 8; int ch_type = CHANNEL_AWGN; int ap_index = AP_NONE; // parse command line while(--argc) { argv++; if (strncmp(*argv,"-h",2)==0) { syntax(); return 0; } else if (strncmp(*argv,"-q",2)==0) { code_idx = (int)atoi((*argv)+2); if (code_idx>1) { printf("Invalid code index\n"); syntax(); return -1; } } else if (strncmp(*argv,"-t",2)==0) { nthreads = (int)atoi((*argv)+2); printf("nthreads = %d\n",nthreads); if (nthreads>NTHREADS_MAX) { printf("Invalid number of threads\n"); syntax(); return -1; } } else if (strncmp(*argv,"-c",2)==0) { ch_type = (int)atoi((*argv)+2); if (ch_type>CHANNEL_RAYLEIGH) { printf("Invalid channel type\n"); syntax(); return -1; } } else if (strncmp(*argv,"-a",2)==0) { ap_index = (int)atoi((*argv)+2); if (ap_index>AP_56) { printf("Invalid a-priori information index\n"); syntax(); return -1; } } else if (strncmp(*argv,"-f",2)==0) { strncpy(fnamein,(*argv)+2,127); } else if (strncmp(*argv,"-h",2)==0) { syntax(); return -1; } else { printf("Invalid option\n"); syntax(); return -1; } } // parse points to be simulated from the input file fin = fopen(fnamein,"r"); if (!fin) { printf("Can't open file: %s\n",fnamein); syntax(); } while (fgets(buf,128,fin)!=0) if (*buf=='#' || *buf=='\n' ) continue; else if (nitems==SIM_POINTS_MAX) break; else if (sscanf(buf,"%f %u",&EbNodB[nitems],&nerrstgt[nitems])!=2) { printf("Invalid input file format\n"); syntax(); return -1; } else nitems++; fclose(fin); if (nitems==0) { printf("No Eb/No point specified in file %s\n",fnamein); syntax(); return -1; } printf("\nQ-ary Repeat-Accumulate Code Word Error Rate Simulator\n"); printf("2016, Nico Palermo - IV3NWV\n\n"); printf("Nthreads = %d\n",nthreads); printf("Channel = %s\n",ch_type==CHANNEL_AWGN?"AWGN":"Rayleigh"); printf("Codename = %s\n",codetotest[code_idx]->name); printf("A-priori = %s\n",ap_str[ap_index]); printf("Eb/No input file = %s\n\n",fnamein); wer_test_proc(codetotest[code_idx], nthreads, ch_type, ap_index, EbNodB, nerrstgt, nitems); return 0; }