diff --git a/lib/qra/qra64/main.c b/lib/qra/qra64/main.c index 85b57ff28..c4ea87f4f 100644 --- a/lib/qra/qra64/main.c +++ b/lib/qra/qra64/main.c @@ -89,6 +89,9 @@ unsigned GetTickCount(void) { #define CHANNEL_AWGN 0 #define CHANNEL_RAYLEIGH 1 +#define JT65_SNR_EBNO_OFFSET 29.1f // with the synch used in JT65 +#define QRA64_SNR_EBNO_OFFSET 31.0f // with the costas array synch + void printwordd(char *msg, int *x, int size) { int k; @@ -171,13 +174,21 @@ symbol. #define GRID_JN66 0x3AE4 // JN66 #define GRID_73 0x7ED0 // 73 -char decode_type[6][32] = { +char decode_type[9][32] = { "[? ? ?] AP0", "[CQ ? ?] AP27", "[CQ ? ] AP42", "[CALL ? ?] AP29", "[CALL ? ] AP44", - "[CALL CALL ?] AP57" + "[CALL CALL ?] AP57", + "[? CALL ?] AP29", + "[? CALL ] AP44", + "[CALL CALL G] AP72" +}; +char apmode_type[3][32] = { + "NO AP", + "AUTO AP", + "USER AP" }; int test_proc_1(int channel_type, float EbNodB, int mode) @@ -229,10 +240,10 @@ be decoded float *rx; int rc; -// Each simulated station must use its own codec, since it might work with +// Each simulated station must use its own codec since it might work with // different a-priori information. - qra64codec *codec_iv3nwv = qra64_init(mode,CALL_IV3NWV); // codec for IV3NWV - qra64codec *codec_k1jt = qra64_init(mode,CALL_K1JT); // codec for K1JT + qra64codec *codec_iv3nwv = qra64_init(mode); // codec for IV3NWV + qra64codec *codec_k1jt = qra64_init(mode); // codec for K1JT // Step 1a: IV3NWV makes a CQ call (with no grid) printf("IV3NWV tx: CQ IV3NWV\n"); @@ -241,7 +252,7 @@ be decoded rx = mfskchannel(y,channel_type,EbNodB); // Step 1b: K1JT attempts to decode [? ? ?], [CQ/QRZ ? ?] or [CQ/QRZ ?] - rc = qra64_decode(codec_k1jt, xdec,rx); + rc = qra64_decode(codec_k1jt, 0, xdec,rx); if (rc>=0) { // decoded printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); @@ -252,7 +263,7 @@ be decoded rx = mfskchannel(y,channel_type,EbNodB); // Step 2b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?] or [IV3NWV ?] - rc = qra64_decode(codec_iv3nwv, xdec,rx); + rc = qra64_decode(codec_iv3nwv, 0, xdec,rx); if (rc>=0) { // decoded printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); @@ -263,7 +274,7 @@ be decoded rx = mfskchannel(y,channel_type,EbNodB); // Step 3b: K1JT attempts to decode [? ? ?] or [K1JT IV3NWV ?] - rc = qra64_decode(codec_k1jt, xdec,rx); + rc = qra64_decode(codec_k1jt, 0, xdec,rx); if (rc>=0) { // decoded printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); @@ -274,7 +285,7 @@ be decoded rx = mfskchannel(y,channel_type,EbNodB); // Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?] - rc = qra64_decode(codec_iv3nwv, xdec,rx); + rc = qra64_decode(codec_iv3nwv, 0, xdec,rx); if (rc>=0) { // decoded printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); return 0; @@ -282,7 +293,7 @@ be decoded } } } - printf("the other party did not decode\n"); + printf("no decode\n"); return -1; } @@ -307,6 +318,9 @@ message according to this table: rc=3 [CALL ? ?] AP29 rc=4 [CALL ? ] AP44 rc=5 [CALL CALL ?] AP57 + rc=6 [? CALL ?] AP29 + rc=7 [? CALL ] AP44 + rc=8 [CALL CALL GRID ] AP72 The return code is <0 when decoding is unsuccessful @@ -317,42 +331,67 @@ a particular type decode among the above 6 cases succeded. int x[QRA64_K], xdec[QRA64_K]; int y[QRA64_N]; float *rx; + float ebnodbest, ebnodbavg=0; int rc,k; - int ndecok[6] = { 0, 0, 0, 0, 0, 0}; + int ndecok[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0}; + int nundet = 0; int ntx = 100,ndec=0; - qra64codec *codec_iv3nwv = qra64_init(mode,CALL_IV3NWV); // codec for IV3NWV - qra64codec *codec_k1jt = qra64_init(mode,CALL_K1JT); // codec for K1JT + qra64codec *codec_iv3nwv = qra64_init(mode); // codec for IV3NWV + qra64codec *codec_k1jt = qra64_init(mode); // codec for K1JT -// This will enable K1JT's decoder to look for IV3NWV calls - encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK); - qra64_encode(codec_k1jt, y, x); - printf("K1JT tx: IV3NWV K1JT\n"); + printf("\nQRA64 Test #2 - Decoding with AP knowledge (SNR-Eb/No offset = %.1f dB)\n\n", + QRA64_SNR_EBNO_OFFSET); + +// This will enable K1JT's decoder to look for calls directed to him [K1JT ? ?/b] + printf("K1JT decoder enabled for [K1JT ? ?/blank]\n"); + qra64_apset(codec_k1jt, CALL_K1JT,0,0,APTYPE_MYCALL); + +// This will enable K1JT's decoder to look for IV3NWV calls directed to him [K1JT IV3NWV ?/b] + printf("K1JT decoder enabled for [K1JT IV3NWV ?]\n"); + qra64_apset(codec_k1jt, CALL_K1JT,CALL_IV3NWV,0,APTYPE_BOTHCALLS); + +// This will enable K1JT's decoder to look for msges sent by IV3NWV [? IV3NWV ?] + printf("K1JT decoder enabled for [? IV3NWV ?/blank]\n"); + qra64_apset(codec_k1jt, 0,CALL_IV3NWV,GRID_BLANK,APTYPE_HISCALL); + +// This will enable K1JT's decoder to look for full-knowledge [K1JT IV3NWV JN66] msgs + printf("K1JT decoder enabled for [K1JT IV3NWV JN66]\n"); + qra64_apset(codec_k1jt, CALL_K1JT,CALL_IV3NWV,GRID_JN66,APTYPE_FULL); // IV3NWV reply to K1JT - printf("IV3NWV tx: K1JT IV3NWV JN66\n"); + printf("\nIV3NWV encoder sends msg: [K1JT IV3NWV JN66]\n\n"); encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66); qra64_encode(codec_iv3nwv, y, x); - printf("Simulating decodes by K1JT up to AP56 ..."); + printf("Simulating K1JT decoder up to AP72\n"); for (k=0;k=0) - ndecok[rc]++; + rc = qra64_decode(codec_k1jt, &ebnodbest, xdec,rx); + if (rc>=0) { + ebnodbavg +=ebnodbest; + if (memcmp(xdec,x,12*sizeof(int))==0) + ndecok[rc]++; + else + nundet++; + } } - printf("\n"); + printf("\n\n"); - printf("Transimtted:%d - Decoded:\n",ntx); - for (k=0;k<6;k++) { + + printf("Transimtted msgs:%d\nDecoded msgs:\n\n",ntx); + for (k=0;k<9;k++) { printf("%3d with %s\n",ndecok[k],decode_type[k]); ndec += ndecok[k]; } - printf("Total: %d/%d\n",ndec,ntx); - printf("\n"); + printf("\nTotal: %d/%d (%d undetected errors)\n\n",ndec,ntx,nundet); + printf(""); + + ebnodbavg/=(ndec+nundet); + printf("Estimated SNR (average in dB) = %.2f dB\n\n",ebnodbavg-QRA64_SNR_EBNO_OFFSET); return 0; } @@ -366,7 +405,7 @@ void syntax(void) printf("Options: \n"); printf(" -s : set simulation SNR in 2500 Hz BW (default:-27.5 dB)\n"); printf(" -c : set channel type 0=AWGN (default) 1=Rayleigh\n"); - printf(" -a : set decode type 0=NO_AP 1=AUTO_AP (default)\n"); + printf(" -a : set decode type 0=NOAP 1=AUTOAP (default) 2=USERAP\n"); printf(" -t: 0=simulate seq of msgs between IV3NWV and K1JT (default)\n"); printf(" 1=simulate K1JT receiving K1JT IV3NWV JN66\n"); printf(" -h: this help\n"); @@ -391,7 +430,7 @@ int main(int argc, char* argv[]) } else { if (strncmp(*argv,"-a",2)==0) { mode = ( int)atoi((*argv)+2); - if (mode>1) { + if (mode>2) { printf("Invalid decoding mode\n"); syntax(); return -1; @@ -399,8 +438,8 @@ int main(int argc, char* argv[]) } else { if (strncmp(*argv,"-s",2)==0) { SNRdB = (float)atof((*argv)+2); - if (SNRdB>0 || SNRdB<-40) { - printf("SNR should be in the range [-40..0]\n"); + if (SNRdB>20 || SNRdB<-40) { + printf("SNR should be in the range [-40..20]\n"); syntax(); return -1; } @@ -431,7 +470,7 @@ int main(int argc, char* argv[]) } } - EbNodB = SNRdB+29.1f; + EbNodB = SNRdB+QRA64_SNR_EBNO_OFFSET; #if defined(__linux__) || defined(__unix__) srand48(GetTickCount()); @@ -449,10 +488,10 @@ int main(int argc, char* argv[]) test_proc_2(channel, EbNodB, mode); } - printf("SNR = %.1fdB channel=%s ap-mode=%s\n\n", + printf("Input SNR = %.1fdB channel=%s ap-mode=%s\n\n", SNRdB, channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH", - mode==QRA_NOAP?"NO_AP":"AUTO_AP" + apmode_type[mode] ); return 0; } diff --git a/lib/qra/qra64/qra64.c b/lib/qra/qra64/qra64.c index 89ed5a15f..2f35a651a 100644 --- a/lib/qra/qra64/qra64.c +++ b/lib/qra/qra64/qra64.c @@ -35,6 +35,7 @@ resulting code is a (12,63) code #include #include +#include #include "qra64.h" #include "../qracodes/qracodes.h" @@ -64,7 +65,7 @@ static int qra64_do_decode(int *x, const float *pix, const int *ap_mask, #define MASK_GRIDBIT 0x8000 // b[15] is 1 for free text, 0 otherwise // ---------------------------------------------------------------------------- -qra64codec *qra64_init(int flags, const int mycall) +qra64codec *qra64_init(int flags) { // Eb/No value for which we optimize the decoder metric @@ -80,39 +81,100 @@ qra64codec *qra64_init(int flags, const int mycall) pcodec->decEsNoMetric = 1.0f*QRA64_m*R*EbNoMetric; pcodec->apflags = flags; - if (flags!=QRA_AUTOAP) + memset(pcodec->apmsg_set,0,APTYPE_SIZE*sizeof(int)); + + if (flags==QRA_NOAP) return pcodec; - // initialize messages and mask for decoding with a-priori information + // for QRA_USERAP and QRA_AUTOAP modes we always enable [CQ/QRZ ? ?] mgs look-up. + // encode CQ/QRZ AP messages + // NOTE: Here we handle only CQ and QRZ msgs. + // 'CQ nnn', 'CQ DX' and 'DE' msgs will be handled by the decoder + // as messages with no a-priori knowledge + qra64_apset(pcodec, CALL_CQ, 0, GRID_BLANK, APTYPE_CQQRZ); - pcodec->apmycall = mycall; - pcodec->apsrccall = 0; + // initialize masks for decoding with a-priori information + encodemsg_jt65(pcodec->apmask_cqqrz, MASK_CQQRZ, 0, MASK_GRIDBIT); + encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ, 0, MASK_GRIDFULL); + encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); + encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1, 0, MASK_GRIDFULL); + encodemsg_jt65(pcodec->apmask_call2, 0, MASK_CALL2, MASK_GRIDBIT); + encodemsg_jt65(pcodec->apmask_call2_ooo, 0, MASK_CALL2, MASK_GRIDFULL); + encodemsg_jt65(pcodec->apmask_call1_call2, MASK_CALL1,MASK_CALL2, MASK_GRIDBIT); + encodemsg_jt65(pcodec->apmask_call1_call2_grid,MASK_CALL1,MASK_CALL2, MASK_GRIDFULL); - // encode CQ/QRZ messages and masks - // NOTE: Here we handle only CQ and QRZ msgs - // 'CQ nnn', 'CQ DX' and 'DE' msgs - // will be handled by the decoder as messages with no a-priori knowledge - encodemsg_jt65(pcodec->apmsg_cqqrz, CALL_CQ, 0, GRID_BLANK); - encodemsg_jt65(pcodec->apmask_cqqrz, MASK_CQQRZ,0, MASK_GRIDBIT); // AP27 - encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL);// AP42 - - // encode [mycall ? x] messages and set masks - encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK); - encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); // AP29 - encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1,0, MASK_GRIDFULL);// AP44 - - // set mask for [mycall srccall ?] messages - encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1,MASK_CALL2, - MASK_GRIDBIT); // AP56 return pcodec; } +void qra64_close(qra64codec *pcodec) +{ + free(pcodec); +} + +int qra64_apset(qra64codec *pcodec, const int mycall, const int hiscall, const int grid, const int aptype) +{ +// Set decoder a-priori knowledge accordingly to the type of the message to look up for +// arguments: +// pcodec = pointer to a qra64codec data structure as returned by qra64_init +// mycall = mycall to look for +// hiscall = hiscall to look for +// grid = grid to look for +// aptype = define and masks the type of AP to be set accordingly to the following: +// APTYPE_CQQRZ set [cq/qrz ? ?/blank] +// APTYPE_MYCALL set [mycall ? ?/blank] +// APTYPE_HISCALL set [? hiscall ?/blank] +// APTYPE_BOTHCALLS set [mycall hiscall ?] +// APTYPE_FULL set [mycall hiscall grid] +// returns: +// 0 on success +// -1 when qra64_init was called with the QRA_NOAP flag +// -2 invalid apytpe + + if (pcodec->apflags==QRA_NOAP) + return -1; + + switch (aptype) { + case APTYPE_CQQRZ: + encodemsg_jt65(pcodec->apmsg_cqqrz, CALL_CQ, 0, GRID_BLANK); + break; + case APTYPE_MYCALL: + encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK); + break; + case APTYPE_HISCALL: + encodemsg_jt65(pcodec->apmsg_call2, 0, hiscall, GRID_BLANK); + break; + case APTYPE_BOTHCALLS: + encodemsg_jt65(pcodec->apmsg_call1_call2, mycall, hiscall, GRID_BLANK); + break; + case APTYPE_FULL: + encodemsg_jt65(pcodec->apmsg_call1_call2_grid, mycall, hiscall, grid); + break; + default: + return -2; // invalid ap type + } + + pcodec->apmsg_set[aptype]=1; // signal the decoder to look-up for the specified type + + + return 0; +} +void qra64_apdisable(qra64codec *pcodec, const int aptype) +{ + if (pcodec->apflags==QRA_NOAP) + return; + + if (aptypeAPTYPE_FULL) + return; + + pcodec->apmsg_set[aptype] = 0; // signal the decoder not to look-up to the specified type +} + void qra64_encode(qra64codec *pcodec, int *y, const int *x) { int encx[QRA64_KC]; // encoder input buffer int ency[QRA64_NC]; // encoder output buffer - int call1,call2,grid; + int hiscall,mycall,grid; memcpy(encx,x,QRA64_K*sizeof(int)); // Copy input to encoder buffer encx[QRA64_K]=calc_crc6(encx,QRA64_K); // Compute and add crc symbol @@ -125,42 +187,56 @@ void qra64_encode(qra64codec *pcodec, int *y, const int *x) if (pcodec->apflags!=QRA_AUTOAP) return; + // Here we handle the QRA_AUTOAP mode -------------------------------------------- + + // When a [hiscall mycall ?] msg is detected we instruct the decoder + // to look for [mycall hiscall ?] msgs + // otherwise when a [cq mycall ?] msg is sent we reset the APTYPE_BOTHCALLS + // look if the msg sent is a std type message (bit15 of grid field = 0) if ((x[9]&0x80)==1) - return; // no, it's a text message + return; // no, it's a text message, nothing to do - // It's a [call1 call2 grid] message + // It's a [hiscall mycall grid] message - // We assume that call2 is our call (but we don't check it) - // call1 the station callsign we are calling or indicates a general call (CQ/QRZ/etc..) - decodemsg_jt65(&call1,&call2,&grid,x); - - if ((call1>=CALL_CQ && call1<=CALL_CQ999) || call1==CALL_CQDX || - call1==CALL_DE) { - // We are making a general call; don't know who might reply (srccall) - // Reset apsrccall to 0 so decoder won't look for [mycall srccall ?] msgs - pcodec->apsrccall = 0; + // We assume that mycall is our call (but we don't check it) + // hiscall the station we are calling or a general call (CQ/QRZ/etc..) + decodemsg_jt65(&hiscall,&mycall,&grid,x); + + + if ((hiscall>=CALL_CQ && hiscall<=CALL_CQ999) || hiscall==CALL_CQDX || + hiscall==CALL_DE) { + // tell the decoder to look for msgs directed to us + qra64_apset(pcodec,mycall,0,0,APTYPE_MYCALL); + // We are making a general call and don't know who might reply + // Reset APTYPE_BOTHCALLS so decoder won't look for [mycall hiscall ?] msgs + qra64_apdisable(pcodec,APTYPE_BOTHCALLS); } else { - // We are replying to someone named call1 - // Set apmsg_call1_call2 so decoder will try for [mycall call1 ?] msgs - pcodec->apsrccall = call1; - encodemsg_jt65(pcodec->apmsg_call1_call2, pcodec->apmycall, - pcodec->apsrccall, 0); + // We are replying to someone named hiscall + // Set APTYPE_BOTHCALLS so decoder will try for [mycall hiscall ?] msgs + qra64_apset(pcodec,mycall, hiscall, GRID_BLANK, APTYPE_BOTHCALLS); } + } -int qra64_decode(qra64codec *pcodec, int *x, const float *rxen) +#define EBNO_MIN -10.0f // minimum Eb/No value returned by the decoder (in dB) +int qra64_decode(qra64codec *pcodec, float *ebno, int *x, const float *rxen) { int k; float *srctmp, *dsttmp; float ix[QRA64_NC*QRA64_M]; // (depunctured) intrisic information + int xdec[QRA64_KC]; // decoded message (with crc) + int ydec[QRA64_NC]; // re-encoded message (for snr calculations) + float noisestd; // estimated noise variance + float msge; // estimated message energy + float ebnoval; // estimated Eb/No int rc; if (QRA64_NMSG!=QRA64_CODE.NMSG) // sanity check return -16; // QRA64_NMSG define is wrong // compute symbols intrinsic probabilities from received energy observations - qra_mfskbesselmetric(ix, rxen, QRA64_m, QRA64_N,pcodec->decEsNoMetric); + noisestd = qra_mfskbesselmetric(ix, rxen, QRA64_m, QRA64_N,pcodec->decEsNoMetric); // de-puncture observations adding a uniform distribution for the crc symbol @@ -176,34 +252,109 @@ int qra64_decode(qra64codec *pcodec, int *x, const float *rxen) pd_init(dsttmp,pd_uniform(QRA64_m),QRA64_M); // Attempt to decode without a-priori info -------------------------------- - rc = qra64_do_decode(x, ix, NULL, NULL); - if (rc>=0) return 0; // successfull decode with AP0 + rc = qra64_do_decode(xdec, ix, NULL, NULL); + if (rc>=0) { + rc = 0; // successfull decode with AP0 + goto decode_end; + } + else + if (pcodec->apflags==QRA_NOAP) + // nothing more to do + return rc; // rc<0 = unsuccessful decode - if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode + // Here we handle decoding with AP knowledge // Attempt to decode CQ calls - rc = qra64_do_decode(x,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // AP27 - if (rc>=0) return 1; // decoded [cq/qrz ? ?] + rc = qra64_do_decode(xdec,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); + if (rc>=0) { rc = 1; goto decode_end; }; // decoded [cq/qrz ? ?] - rc = qra64_do_decode(x, ix, pcodec->apmask_cqqrz_ooo, - pcodec->apmsg_cqqrz); // AP42 - if (rc>=0) return 2; // decoded [cq ? ooo] + rc = qra64_do_decode(xdec, ix, pcodec->apmask_cqqrz_ooo, + pcodec->apmsg_cqqrz); + if (rc>=0) { rc = 2; goto decode_end; }; // decoded [cq ? ooo] - // attempt to decode calls directed to us (mycall) - rc = qra64_do_decode(x, ix, pcodec->apmask_call1, - pcodec->apmsg_call1); // AP29 - if (rc>=0) return 3; // decoded [mycall ? ?] + // attempt to decode calls directed to us + if (pcodec->apmsg_set[APTYPE_MYCALL]) { + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1, + pcodec->apmsg_call1); + if (rc>=0) { rc = 3; goto decode_end; }; // decoded [mycall ? ?] + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_ooo, + pcodec->apmsg_call1); + if (rc>=0) { rc = 4; goto decode_end; }; // decoded [mycall ? ooo] + } - rc = qra64_do_decode(x, ix, pcodec->apmask_call1_ooo, - pcodec->apmsg_call1); // AP44 - if (rc>=0) return 4; // decoded [mycall ? ooo] + // attempt to decode [mycall srccall ?] msgs + if (pcodec->apmsg_set[APTYPE_BOTHCALLS]) { + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_call2, + pcodec->apmsg_call1_call2); + if (rc>=0) { rc = 5; goto decode_end; }; // decoded [mycall srccall ?] + } - // if apsrccall is set attempt to decode [mycall srccall ?] msgs - if (pcodec->apsrccall==0) return rc; // nothing more to do + // attempt to decode [? hiscall ?] msgs + if (pcodec->apmsg_set[APTYPE_HISCALL]) { + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2, + pcodec->apmsg_call2); + if (rc>=0) { rc = 6; goto decode_end; }; // decoded [? hiscall ?] + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2_ooo, + pcodec->apmsg_call2); + if (rc>=0) { rc = 7; goto decode_end; }; // decoded [? hiscall ooo] + } - rc = qra64_do_decode(x, ix, pcodec->apmask_call1_call2, - pcodec->apmsg_call1_call2); // AP57 - if (rc>=0) return 5; // decoded [mycall srccall ?] + if (pcodec->apmsg_set[APTYPE_FULL]) { + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_call2_grid, + pcodec->apmsg_call1_call2_grid); + if (rc>=0) { rc = 8; goto decode_end; }; // decoded [mycall hiscall grid] + } + + // all decoding attempts failed + return rc; + +decode_end: // successfull decode + + // copy decoded message (without crc) to output buffer + memcpy(x,xdec,QRA64_K*sizeof(int)); + + if (ebno==0) // null pointer indicates we are not interested in the Eb/No estimate + return rc; + + // reencode message and estimate Eb/No + qra_encode(&QRA64_CODE, ydec, xdec); + // puncture crc + memmove(ydec+QRA64_K,ydec+QRA64_KC,QRA64_C*sizeof(int)); + // compute total power of decoded message + msge = 0; + for (k=0;k57.004f) + ebnoval=57.004f; + ebnoval = ebnoval*57.03f/(57.03f-ebnoval); + + // compute value in dB + if (ebnoval<=0) + ebnoval = EBNO_MIN; // assume a minimum, positive value + else + ebnoval = 10.0f*(float)log10(ebnoval); + if (ebnoval +#define NICO_WANTS_SNR_DUMP + +static qra64codec *pqra64codec = NULL; + void qra64_enc_(int x[], int y[]) { - int ncall=0xf70c238; //K1ABC - qra64codec *codec = qra64_init(0,ncall); //codec for ncall - qra64_encode(codec, y, x); + if (pqra64codec==NULL) + pqra64codec = qra64_init(QRA_AUTOAP); + + qra64_encode(pqra64codec, y, x); } void qra64_dec_(float r[], int* nmycall, int xdec[], int* rc) @@ -23,14 +28,34 @@ void qra64_dec_(float r[], int* nmycall, int xdec[], int* rc) // rc=3 [CALL ? ?] AP29 // rc=4 [CALL ? ] AP44 // rc=5 [CALL CALL ?] AP57 +// rc=6 [? CALL ?] AP29 +// rc=7 [? CALL ] AP44 +// rc=8 [CALL CALL G] AP72 static ncall0=-1; int ncall=*nmycall; - static qra64codec *codec; + float EbNodBEstimated; +#ifdef NICO_WANTS_SNR_DUMP + FILE *fout; +#endif + if(ncall!=ncall0) { - codec = qra64_init(1,ncall); //codec for ncall + if (pqra64codec!=NULL) + qra64_close(pqra64codec); + pqra64codec = qra64_init(QRA_AUTOAP); + // the following apset call is not strictly necessary + // It enables AP decoding of messages directed to our call + // also in the case we have never made a CQ + qra64_apset(pqra64codec,ncall,0,0,APTYPE_MYCALL); ncall0=ncall; } - *rc = qra64_decode(codec,xdec,r); + *rc = qra64_decode(pqra64codec,&EbNodBEstimated,xdec,r); + +#ifdef NICO_WANTS_SNR_DUMP + fout = fopen("C:\\JTSDK\\snrdump.txt","a+"); + if ((*rc)>=0) + fprintf(fout,"rc=%d snr=%.2f dB\n",*rc,EbNodBEstimated-31.0f); + fclose(fout); +#endif } diff --git a/lib/qra/qracodes/main.c b/lib/qra/qracodes/main.c index 3fe7d44f3..5f9f067d4 100644 --- a/lib/qra/qracodes/main.c +++ b/lib/qra/qracodes/main.c @@ -56,18 +56,38 @@ #include // _beginthread #endif -#if __linux__ -#include +#if defined(__linux__) + +// remove unwanted macros +#define __cdecl + +// implements Windows API #include - GetTickCount(void) { + unsigned int GetTickCount(void) { struct timespec ts; - theTick = 0U; + 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__ @@ -86,7 +106,7 @@ // ----------------------------------------------------------------------------------- -#define NTHREADS_MAX 24 +#define NTHREADS_MAX 160 // channel types #define CHANNEL_AWGN 0 @@ -367,9 +387,21 @@ void wer_test_thread(wer_test_ds *pdata) 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 @@ -454,7 +486,14 @@ int wer_test_proc(const qracode *pcode, int nthreads, int chtype, int ap_index, wt[j].nerrsu=0; wt[j].done = 0; wt[j].stop = 0; + #if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) ) + if (pthread_create (&wt[j].thread, 0, wer_test_pthread, &wt[j])) { + perror ("Creating thread: "); + exit (255); + } + #else _beginthread((void*)(void*)wer_test_thread,0,&wt[j]); + #endif } nd = 0; @@ -487,9 +526,19 @@ int wer_test_proc(const qracode *pcode, int nthreads, int chtype, int ap_index, // wait for the working threads to exit for (j=0;jNTHREADS_MAX) { printf("Invalid number of threads\n"); syntax(); diff --git a/lib/qra/qracodes/qracodes.c b/lib/qra/qracodes/qracodes.c index 7af0945bf..748a9c9cd 100644 --- a/lib/qra/qracodes/qracodes.c +++ b/lib/qra/qracodes/qracodes.c @@ -27,8 +27,6 @@ #include "qracodes.h" -#define QRA_DEBUG - int qra_encode(const qracode *pcode, int *y, const int *x) { int k,j,kk,jj; @@ -144,7 +142,7 @@ static void qra_ioapprox(float *src, float C, int nitems) } -void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric) +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. @@ -167,6 +165,8 @@ void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N // 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; @@ -196,7 +196,7 @@ void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N pd_norm(PD_ROWADDR(pix,M,k),m); } - return; + return sigmaest; } diff --git a/lib/qra/qracodes/qracodes.h b/lib/qra/qracodes/qracodes.h index e4bac50aa..97542c118 100644 --- a/lib/qra/qracodes/qracodes.h +++ b/lib/qra/qracodes/qracodes.h @@ -67,10 +67,10 @@ typedef struct { extern "C" { #endif -int qra_encode(const qracode *pcode, int *y, const int *x); -void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric); -int qra_extrinsic(const qracode *pcode, float *pex, const float *pix, int maxiter,float *qra_v2cmsg,float *qra_c2vmsg); -void qra_mapdecode(const qracode *pcode, int *xdec, float *pex, const float *pix); +int qra_encode(const qracode *pcode, int *y, const int *x); +float qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric); +int qra_extrinsic(const qracode *pcode, float *pex, const float *pix, int maxiter,float *qra_v2cmsg,float *qra_c2vmsg); +void qra_mapdecode(const qracode *pcode, int *xdec, float *pex, const float *pix); #ifdef __cplusplus }