From 27490897dacec550a494329691d600344467a3ee Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Fri, 24 Jun 2016 19:54:34 +0000 Subject: [PATCH] Some cleanup and reformatting of QRA65 code (with apologies to Nico). I've tried to make code "pretty print" and easier to follow, at least for me. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6808 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- lib/qra/qra65/main.c | 669 +++++++++++++++++++------------------ lib/qra/qra65/qra65.c | 471 +++++++++++++------------- lib/qra/qra65/qra65.h | 24 +- lib/qra/qra65/qra65_subs.c | 26 +- lib/qra/qra65/qra65sim.f90 | 7 +- 5 files changed, 615 insertions(+), 582 deletions(-) diff --git a/lib/qra/qra65/main.c b/lib/qra/qra65/main.c index af68c791b..901749f03 100644 --- a/lib/qra/qra65/main.c +++ b/lib/qra/qra65/main.c @@ -1,58 +1,62 @@ -// main.c -// QRA65 mode encode/decode test -// -// (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 -// qra65.c/.h - qra65 mode encode/decoding functions -// -// ../qracodes/normrnd.{c,h} - random gaussian number generator -// ../qracodes/npfwht.{c,h} - Fast Walsh-Hadamard Transforms -// ../qracodes/pdmath.{c,h} - Elementary math on probability distributions -// ../qracodes/qra12_63_64_irr_b.{c,h} - Tables for a QRA(12,63) irregular RA code over GF(64) -// ../qracodes/qra13_64_64_irr_e.{c,h} - Tables for a QRA(13,64) irregular RA code " " -// ../qracodes/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. +/* +main.c +QRA65 mode encode/decode test -// You should have received a copy of the GNU General Public License -// along with qracodes source distribution. -// If not, see . +(c) 2016 - Nico Palermo, IV3NWV -// ----------------------------------------------------------------------------- +Thanks to Andrea Montefusco IW0HDV for his help on adapting the sources +to OSs other than MS Windows -// The code used by the QRA65 mode is the code: -// QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.{h,c}) -// This code has been 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. +------------------------------------------------------------------------------ +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 + qra65.c/.h - qra65 mode encode/decoding functions -// OS dependent defines and includes -------------------------------------------- + ../qracodes/normrnd.{c,h} - random gaussian number generator + ../qracodes/npfwht.{c,h} - Fast Walsh-Hadamard Transforms + ../qracodes/pdmath.{c,h} - Elementary math on probability distributions + ../qracodes/qra12_63_64_irr_b.{c,h} - Tables for a QRA(12,63) irregular RA + code over GF(64) + ../qracodes/qra13_64_64_irr_e.{c,h} - Tables for a QRA(13,64) irregular RA + code over GF(64) + ../qracodes/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 . + +----------------------------------------------------------------------------- + +The code used by the QRA65 mode is the code: QRA13_64_64_IRR_E: K=13 +N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.{h,c}). + +This code has been 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 +// Windows (x64 and x86) +#include // required only for GetTickCount(...) +#include // _beginthread #endif #if __linux__ @@ -79,7 +83,7 @@ unsigned GetTickCount(void) { #include "qra65.h" #include "../qracodes/normrnd.h" // gaussian numbers generator -// ----------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // channel types #define CHANNEL_AWGN 0 @@ -87,19 +91,19 @@ unsigned GetTickCount(void) { void printwordd(char *msg, int *x, int size) { - int k; - printf("\n%s ",msg); - for (k=0;k=0) { // decoded + printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); + +// Step 2a: K1JT replies to IV3NWV (with no grid) + printf("K1JT tx: IV3NWV K1JT\n"); + encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_BLANK); + qra65_encode(codec_k1jt, y, x); + rx = mfskchannel(y,channel_type,EbNodB); + +// Step 2b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?] or [IV3NWV ?] + rc = qra65_decode(codec_iv3nwv, xdec,rx); + if (rc>=0) { // decoded + printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); + +// Step 3a: IV3NWV replies to K1JT with a 73 + printf("IV3NWV tx: K1JT IV3NWV 73\n"); + encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV, GRID_73); + qra65_encode(codec_iv3nwv, y, x); + rx = mfskchannel(y,channel_type,EbNodB); + +// Step 3b: K1JT attempts to decode [? ? ?] or [K1JT IV3NWV ?] + rc = qra65_decode(codec_k1jt, xdec,rx); + if (rc>=0) { // decoded + printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); + +// Step 4a: K1JT replies to IV3NWV with a 73 + printf("K1JT tx: IV3NWV K1JT 73\n"); + encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_73); + qra65_encode(codec_k1jt, y, x); rx = mfskchannel(y,channel_type,EbNodB); - // K1JT attempts to decode - rc = qra65_decode(codec_k1jt, xdec,rx); +// Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?] + rc = qra65_decode(codec_iv3nwv, xdec,rx); if (rc>=0) { // decoded - printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); - // K1JT replies to IV3NWV (with no grid) - printf("K1JT tx: IV3NWV K1JT\n"); - encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_BLANK); - qra65_encode(codec_k1jt, y, x); - rx = mfskchannel(y,channel_type,EbNodB); - - // IV3NWV attempts to decode - rc = qra65_decode(codec_iv3nwv, xdec,rx); - if (rc>=0) { // decoded - printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); - // IV3NWV replies to K1JT with a 73 - printf("IV3NWV tx: K1JT IV3NWV 73\n"); - encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV, GRID_73); - qra65_encode(codec_iv3nwv, y, x); - rx = mfskchannel(y,channel_type,EbNodB); - - // K1JT attempts to decode - rc = qra65_decode(codec_k1jt, xdec,rx); - if (rc>=0) { // decoded - printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); - // K1JT replies to IV3NWV with a 73 - printf("K1JT tx: IV3NWV K1JT 73\n"); - encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_73); - qra65_encode(codec_k1jt, y, x); - rx = mfskchannel(y,channel_type,EbNodB); - - // IV3NWV attempts to decode - rc = qra65_decode(codec_iv3nwv, xdec,rx); - if (rc>=0) { // decoded - printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); - return 0; - } - } - } - } - printf("the other party did not decode\n"); - return -1; + printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); + return 0; + } + } + } + } + printf("the other party did not decode\n"); + return -1; } int test_proc_2(int channel_type, float EbNodB, int mode) { +/* +Here we simulate the decoder of K1JT after K1JT has sent a msg [IV3NWV K1JT] +and IV3NWV sends him the msg [K1JT IV3NWV JN66]. - // Here we simulate the decoder of K1JT after K1JT has sent a msg [IV3NWV K1JT] - // and IV3NWV sends him the msg [K1JT IV3NWV JN66] - // If mode=QRA_NOAP, K1JT decoder attempts to decode only msgs of type [? ? ?]. - // If mode=QRA_AUTOP, K1JT decoder will attempt to decode also the msgs [K1JT IV3NWV] and - // [K1JT IV3NWV ?]. +If mode=QRA_NOAP, K1JT decoder attempts to decode only msgs of type [? ? ?]. - // In the case a decode is successful the return code of the qra65_decode function - // indicates the amount of a-priori information required to decode the received message - // accordingly to this table: - // rc=0 [? ? ?] AP0 - // rc=1 [CQ ? ?] AP27 - // rc=2 [CQ ? ] AP44 - // rc=3 [CALL ? ?] AP29 - // rc=4 [CALL ? ] AP45 - // rc=5 [CALL CALL ?] AP57 - // The return code is <0 when decoding is unsuccessful +If mode=QRA_AUTOP, K1JT decoder will attempt to decode also the msgs +[K1JT IV3NWV] and [K1JT IV3NWV ?]. - // This test simulates the situation ntx times and reports how many times - // a particular type decode among the above 6 cases succeded. +In the case a decode is successful the return code of the qra65_decode function +indicates the amount of a-priori information required to decode the received +message according to this table: - int x[QRA65_K], xdec[QRA65_K]; - int y[QRA65_N]; - float *rx; - int rc,k; + rc=0 [? ? ?] AP0 + rc=1 [CQ ? ?] AP27 + rc=2 [CQ ? ] AP42 + rc=3 [CALL ? ?] AP29 + rc=4 [CALL ? ] AP44 + rc=5 [CALL CALL ?] AP57 - int ndecok[6] = { 0, 0, 0, 0, 0, 0}; - int ntx = 100,ndec=0; +The return code is <0 when decoding is unsuccessful - qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV - qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT +This test simulates the situation ntx times and reports how many times +a particular type decode among the above 6 cases succeded. +*/ - // this will enable k1jt's decoder to look for iv3nwv calls - encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK); - qra65_encode(codec_k1jt, y, x); - printf("K1JT tx: IV3NWV K1JT\n"); + int x[QRA65_K], xdec[QRA65_K]; + int y[QRA65_N]; + float *rx; + int rc,k; - // iv3nwv reply to k1jt - printf("IV3NWV tx: K1JT IV3NWV JN66\n"); - encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66); - qra65_encode(codec_iv3nwv, y, x); + int ndecok[6] = { 0, 0, 0, 0, 0, 0}; + int ntx = 100,ndec=0; - printf("Simulating decodes by K1JT up to AP56 ..."); + qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV + qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT - for (k=0;k=0) - ndecok[rc]++; - } - printf("\n"); +// This will enable K1JT's decoder to look for IV3NWV calls + encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK); + qra65_encode(codec_k1jt, y, x); + printf("K1JT tx: IV3NWV K1JT\n"); - printf("Transimtted:%d - Decoded:\n",ntx); - for (k=0;k<6;k++) { - printf("%3d with %s\n",ndecok[k],decode_type[k]); - ndec += ndecok[k]; - } - printf("Total: %d/%d\n",ndec,ntx); - printf("\n"); + // IV3NWV reply to K1JT + printf("IV3NWV tx: K1JT IV3NWV JN66\n"); + encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66); + qra65_encode(codec_iv3nwv, y, x); - return 0; + printf("Simulating decodes by K1JT up to AP56 ..."); + + for (k=0;k=0) + ndecok[rc]++; + } + printf("\n"); + + printf("Transimtted:%d - Decoded:\n",ntx); + for (k=0;k<6;k++) { + printf("%3d with %s\n",ndecok[k],decode_type[k]); + ndec += ndecok[k]; + } + printf("Total: %d/%d\n",ndec,ntx); + printf("\n"); + + return 0; } void syntax(void) { - printf("\nQRA65 Mode Tests\n"); - printf("2016, Nico Palermo - IV3NWV\n\n"); - printf("---------------------------\n\n"); - printf("Syntax: qra65 [-s] [-c] [-a] [-t] [-h]\n"); - 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(" -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"); + printf("\nQRA65 Mode Tests\n"); + printf("2016, Nico Palermo - IV3NWV\n\n"); + printf("---------------------------\n\n"); + printf("Syntax: qra65 [-s] [-c] [-a] [-t] [-h]\n"); + 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(" -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"); } int main(int argc, char* argv[]) { - int k, rc, nok=0; - - float SNRdB = -27.5f; - unsigned int channel = CHANNEL_AWGN; - unsigned int mode = QRA_AUTOAP; - unsigned int testtype=0; - int nqso = 100; - - float EbNodB; - - // parse command line - while(--argc) { - argv++; - if (strncmp(*argv,"-h",2)==0) { - syntax(); - return 0; - } - else - if (strncmp(*argv,"-a",2)==0) { - mode = ( int)atoi((*argv)+2); - if (mode>1) { - printf("Invalid decoding mode\n"); - syntax(); - return -1; - } - } - 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"); - syntax(); - return -1; - } - } - else - if (strncmp(*argv,"-t",2)==0) { - testtype = ( int)atoi((*argv)+2); - if (testtype>1) { - printf("Invalid test type\n"); - syntax(); - return -1; - } - } - else - if (strncmp(*argv,"-c",2)==0) { - channel = ( int)atoi((*argv)+2); - if (channel>CHANNEL_RAYLEIGH) { - printf("Invalid channel type\n"); - syntax(); - return -1; - } - } - else { - printf("Invalid option\n"); - syntax(); - return -1; - } - } - - EbNodB = SNRdB+29.1f; + int k, rc, nok=0; + float SNRdB = -27.5f; + unsigned int channel = CHANNEL_AWGN; + unsigned int mode = QRA_AUTOAP; + unsigned int testtype=0; + int nqso = 100; + float EbNodB; +// Parse the command line + while(--argc) { + argv++; + if (strncmp(*argv,"-h",2)==0) { + syntax(); + return 0; + } else { + if (strncmp(*argv,"-a",2)==0) { + mode = ( int)atoi((*argv)+2); + if (mode>1) { + printf("Invalid decoding mode\n"); + syntax(); + return -1; + } + } 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"); + syntax(); + return -1; + } + } else { + if (strncmp(*argv,"-t",2)==0) { + testtype = ( int)atoi((*argv)+2); + if (testtype>1) { + printf("Invalid test type\n"); + syntax(); + return -1; + } + } else { + if (strncmp(*argv,"-c",2)==0) { + channel = ( int)atoi((*argv)+2); + if (channel>CHANNEL_RAYLEIGH) { + printf("Invalid channel type\n"); + syntax(); + return -1; + } + } else { + printf("Invalid option\n"); + syntax(); + return -1; + } + } + } + } + } + } + + EbNodB = SNRdB+29.1f; + #if defined(__linux__) || defined(__unix__) - srand48(GetTickCount()); + srand48(GetTickCount()); #endif - if (testtype==0) { - for (k=0;k. +(c) 2016 - Nico Palermo, IV3NWV -// ----------------------------------------------------------------------------- +------------------------------------------------------------------------------- -// Code used in this sowftware release: + 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. -// QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.h /.c) + You should have received a copy of the GNU General Public License + along with qracodes source distribution. + If not, see . -// 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 a (12,63) code +----------------------------------------------------------------------------- -// ------------------------------------------------------------------------------ +Code used in this sowftware release: + +QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in +qra13_64_64_irr_e.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 a (12,63) code +*/ +//---------------------------------------------------------------------------- #include #include @@ -41,304 +43,305 @@ // Code parameters of the QRA65 mode #define QRA65_CODE qra_13_64_64_irr_e -#define QRA65_NMSG 218 // this must much the value indicated in QRA65_CODE.NMSG +#define QRA65_NMSG 218 // Must much value indicated in QRA65_CODE.NMSG -#define QRA65_KC (QRA65_K+1) // information symbols crc included (as defined in the code) -#define QRA65_NC (QRA65_N+1) // codeword length (as defined in the code) -#define QRA65_NITER 100 // max number of iterations per decode +#define QRA65_KC (QRA65_K+1) // Information symbols (crc included) +#define QRA65_NC (QRA65_N+1) // Codeword length (as defined in the code) +#define QRA65_NITER 100 // max number of iterations per decode - - -// static functions declarations ------------------------------------------------- +// static functions declarations ---------------------------------------------- static int calc_crc6(const int *x, int sz); -static void ix_mask(float *dst, const float *src, const int *mask, const int *x); -static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const int *ap_x); - -// a-priori information masks for fields in jt65-like msgs ----------------------- +static void ix_mask(float *dst, const float *src, const int *mask, + const int *x); +static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, + const int *ap_x); +// a-priori information masks for fields in JT65-like msgs -------------------- #define MASK_CQQRZ 0xFFFFFFC // CQ/QRZ calls common bits #define MASK_CALL1 0xFFFFFFF #define MASK_CALL2 0xFFFFFFF #define MASK_GRIDFULL 0xFFFF -#define MASK_GRIDBIT 0x8000 // b[15] is 0 for all the messages which are not text - -// ------------------------------------------------------------------------------- +#define MASK_GRIDBIT 0x8000 // b[15] is 1 for free text, 0 otherwise +// ---------------------------------------------------------------------------- qra65codec *qra65_init(int flags, const int mycall) { - // Eb/No value for which we optimize the decoder metric - const float EbNodBMetric = 2.8f; - const float EbNoMetric = (float)pow(10,EbNodBMetric/10); - const float R = 1.0f*(QRA65_KC)/(QRA65_NC); + // Eb/No value for which we optimize the decoder metric + const float EbNodBMetric = 2.8f; + const float EbNoMetric = (float)pow(10,EbNodBMetric/10); + const float R = 1.0f*(QRA65_KC)/(QRA65_NC); - qra65codec *pcodec = (qra65codec*)malloc(sizeof(qra65codec)); + qra65codec *pcodec = (qra65codec*)malloc(sizeof(qra65codec)); - if (!pcodec) - return 0; // can't allocate memory - - pcodec->decEsNoMetric = 1.0f*QRA65_m*R*EbNoMetric; - pcodec->apflags = flags; + if (!pcodec) + return 0; // can't allocate memory - if (flags!=QRA_AUTOAP) - return pcodec; + pcodec->decEsNoMetric = 1.0f*QRA65_m*R*EbNoMetric; + pcodec->apflags = flags; - // initialize messages and mask for decoding with a-priori information + if (flags!=QRA_AUTOAP) + return pcodec; - pcodec->apmycall = mycall; - pcodec->apsrccall = 0; + // initialize messages and mask for decoding with a-priori information - // 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 (26+1) - encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL); // AP42 (26+16) + pcodec->apmycall = mycall; + pcodec->apsrccall = 0; - // 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 (28+1) - encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1, 0, MASK_GRIDFULL); // AP44 (28+16) + // 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 - // set mask for [mycall srccall ?] messages - encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1, MASK_CALL2, MASK_GRIDBIT); // AP56 (28+28) + // 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 - return pcodec; + // set mask for [mycall srccall ?] messages + encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1,MASK_CALL2, + MASK_GRIDBIT); // AP56 + return pcodec; } void qra65_encode(qra65codec *pcodec, int *y, const int *x) { - int encx[QRA65_KC]; // encoder input buffer - int ency[QRA65_NC]; // encoder output buffer + int encx[QRA65_KC]; // encoder input buffer + int ency[QRA65_NC]; // encoder output buffer - int call1,call2,grid; + int call1,call2,grid; - memcpy(encx,x,QRA65_K*sizeof(int)); // copy input to the encoder buffer - encx[QRA65_K]=calc_crc6(encx,QRA65_K); // compute and add the crc symbol + memcpy(encx,x,QRA65_K*sizeof(int)); // Copy input to encoder buffer + encx[QRA65_K]=calc_crc6(encx,QRA65_K); // Compute and add crc symbol + qra_encode(&QRA65_CODE, ency, encx); // encode msg+crc using given QRA code - qra_encode(&QRA65_CODE, ency, encx); // encode msg+crc using the given QRA code + // copy codeword to output puncturing the crc symbol + memcpy(y,ency,QRA65_K*sizeof(int)); // copy information symbols + memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy parity symbols - // copy codeword to output puncturing the crc symbol - memcpy(y,ency,QRA65_K*sizeof(int)); // copy the information symbols - memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy the parity check symbols + if (pcodec->apflags!=QRA_AUTOAP) + return; - if (pcodec->apflags!=QRA_AUTOAP) - return; + // 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 - // 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 + // It's a [call1 call2 grid] message - // it's a [call1 call2 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); + // 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, so we still don't know who can reply us (srccall) - // reset apsrccall to 0 so that the decoder won't look for [mycall srccall ?] msgs - pcodec->apsrccall = 0; - } - else { - // we are replying someone named call1 - // set apmsg_call1_call2 so that the decoder will attempt to decode [mycall call1 ?] msgs - pcodec->apsrccall = call1; - encodemsg_jt65(pcodec->apmsg_call1_call2, pcodec->apmycall, pcodec->apsrccall, 0); - } - + 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; + } 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); + } } int qra65_decode(qra65codec *pcodec, int *x, const float *rxen) { - int k; - float *srctmp, *dsttmp; - float ix[QRA65_NC*QRA65_M]; // (depunctured) intrisic information to the decoder - int rc; + int k; + float *srctmp, *dsttmp; + float ix[QRA65_NC*QRA65_M]; // (depunctured) intrisic information + int rc; + + if (QRA65_NMSG!=QRA65_CODE.NMSG) // sanity check + return -16; // QRA65_NMSG define is wrong - // sanity check - if (QRA65_NMSG!=QRA65_CODE.NMSG) - return -16; // QRA65_NMSG define is wrong + // compute symbols intrinsic probabilities from received energy observations + qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric); - // compute symbols intrinsic probabilities from received energy observations - qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric); + // de-puncture observations adding a uniform distribution for the crc symbol - // de-puncture observations adding a uniform distribution for the crc symbol ---------- + // move check symbols distributions one symbol towards the end + dsttmp = PD_ROWADDR(ix,QRA65_M, QRA65_NC-1); //Point to last symbol prob dist + srctmp = dsttmp-QRA65_M; // source is the previous pd + for (k=0;k=0) return 0; // successfull decode with AP0 - // attempt to decode without a-priori -------------------------------------------------- - rc = qra65_do_decode(x, ix, NULL, NULL); - if (rc>=0) return 0; // successfull decode with 0 ap + if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode - if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode + // Attempt to decode CQ calls + rc = qra65_do_decode(x,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // AP27 + if (rc>=0) return 1; // decoded [cq/qrz ? ?] - // attempt to decode CQ calls - rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // 27 bit AP - if (rc>=0) return 1; // decoded [cq/qrz ? ?] - rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo, pcodec->apmsg_cqqrz); // 44 bit AP - if (rc>=0) return 2; // decoded [cq ? ooo] + rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo, + pcodec->apmsg_cqqrz); // AP42 + if (rc>=0) return 2; // decoded [cq ? ooo] - // attempt to decode calls directed to us (mycall) - rc = qra65_do_decode(x, ix, pcodec->apmask_call1, pcodec->apmsg_call1); // 29 bit AP - if (rc>=0) return 3; // decoded [mycall ? ?] - rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo, pcodec->apmsg_call1); // 45 bit AP - if (rc>=0) return 4; // decoded [mycall ? ooo] + // attempt to decode calls directed to us (mycall) + rc = qra65_do_decode(x, ix, pcodec->apmask_call1, + pcodec->apmsg_call1); // AP29 + if (rc>=0) return 3; // decoded [mycall ? ?] - // if apsrccall is set attempt to decode [mycall srccall ?] msgs - if (pcodec->apsrccall==0) return rc; // nothing more to do + rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo, + pcodec->apmsg_call1); // AP44 + if (rc>=0) return 4; // decoded [mycall ? ooo] - rc = qra65_do_decode(x, ix, pcodec->apmask_call1_call2, pcodec->apmsg_call1_call2); // 57 bit AP - if (rc>=0) return 5; // decoded [mycall srccall ?] + // if apsrccall is set attempt to decode [mycall srccall ?] msgs + if (pcodec->apsrccall==0) return rc; // nothing more to do - return rc; + rc = qra65_do_decode(x, ix, pcodec->apmask_call1_call2, + pcodec->apmsg_call1_call2); // AP57 + if (rc>=0) return 5; // decoded [mycall srccall ?] + + return rc; } -// static functions definitions ---------------------------------------------------------------- +// Static functions definitions ---------------------------------------------- -// decode with given a-priori information -static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const int *ap_x) +// Decode with given a-priori information +static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, + const int *ap_x) { - int rc; - const float *ixsrc; - float ix_masked[QRA65_NC*QRA65_M]; // (masked) intrinsic information to the decoder - float ex[QRA65_NC*QRA65_M]; // extrinsic information from the decoder + int rc; + const float *ixsrc; + float ix_masked[QRA65_NC*QRA65_M]; // Masked intrinsic information + float ex[QRA65_NC*QRA65_M]; // Extrinsic information from the decoder - float v2cmsg[QRA65_NMSG*QRA65_M]; // buffers for the decoder messages - float c2vmsg[QRA65_NMSG*QRA65_M]; - int xdec[QRA65_KC]; + float v2cmsg[QRA65_NMSG*QRA65_M]; // buffers for the decoder messages + float c2vmsg[QRA65_NMSG*QRA65_M]; + int xdec[QRA65_KC]; - if (ap_mask==NULL) { // no a-priori information - ixsrc = pix; // intrinsic source is what passed as argument - } - else { // a-priori information provided - // mask channel observations with a-priori - ix_mask(ix_masked,pix,ap_mask,ap_x); - ixsrc = ix_masked; // intrinsic source is the masked version - } + if (ap_mask==NULL) { // no a-priori information + ixsrc = pix; // intrinsic source is what passed as argument + } else { + // a-priori information provided + // mask channel observations with a-priori + ix_mask(ix_masked,pix,ap_mask,ap_x); + ixsrc = ix_masked; // intrinsic source is the masked version + } - // run the decoding algorithm - rc = qra_extrinsic(&QRA65_CODE,ex,ixsrc,QRA65_NITER,v2cmsg,c2vmsg); - if (rc<0) - return -1; // no convergence in given iterations + // run the decoding algorithm + rc = qra_extrinsic(&QRA65_CODE,ex,ixsrc,QRA65_NITER,v2cmsg,c2vmsg); + if (rc<0) + return -1; // no convergence in given iterations - // decode - qra_mapdecode(&QRA65_CODE,xdec,ex,ixsrc); + // decode + qra_mapdecode(&QRA65_CODE,xdec,ex,ixsrc); - // verify crc - if (calc_crc6(xdec,QRA65_K)!=xdec[QRA65_K]) // crc doesn't match (detected error) - return -2; // decoding was succesfull but crc doesn't match + // verify crc + if (calc_crc6(xdec,QRA65_K)!=xdec[QRA65_K]) // crc doesn't match (detected error) + return -2; // decoding was succesfull but crc doesn't match - // success. copy decoded message to output buffer - memcpy(x,xdec,QRA65_K*sizeof(int)); - - return 0; + // success. copy decoded message to output buffer + memcpy(x,xdec,QRA65_K*sizeof(int)); + return 0; } -// crc functions ------------------------------------------------------------------------------- +// crc functions -------------------------------------------------------------- // 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/) +// g(x) = x^6 + x^2 + x + 1 (See: https://users.ece.cmu.edu/~koopman/crc/) // #define CRC6_GEN_POL 0x38 // MSB=a0 LSB=a5. Simulation results are similar static int calc_crc6(const int *x, int sz) { - // todo: compute it faster using a look up table - int k,j,t,sr = 0; - for (k=0;k>1) ^ CRC6_GEN_POL; - else - sr = (sr>>1); - t>>=1; - } - } - return sr; + // todo: compute it faster using a look up table + int k,j,t,sr = 0; + for (k=0;k>1) ^ CRC6_GEN_POL; + else + sr = (sr>>1); + t>>=1; + } + } + return sr; } -static void ix_mask(float *dst, const float *src, const int *mask, const int *x) +static void ix_mask(float *dst, const float *src, const int *mask, + const int *x) { - // mask intrinsic information (channel observations) with a priori knowledge + // mask intrinsic information (channel observations) with a priori knowledge - int k,kk, smask; - float *row; + int k,kk, smask; + float *row; - memcpy(dst,src,(QRA65_NC*QRA65_M)*sizeof(float)); + memcpy(dst,src,(QRA65_NC*QRA65_M)*sizeof(float)); - for (k=0;k>22)&0x3F; - y[1]= (call1>>16)&0x3F; - y[2]= (call1>>10)&0x3F; - y[3]= (call1>>4)&0x3F; - y[4]= (call1<<2)&0x3F; + y[0]= (call1>>22)&0x3F; + y[1]= (call1>>16)&0x3F; + y[2]= (call1>>10)&0x3F; + y[3]= (call1>>4)&0x3F; + y[4]= (call1<<2)&0x3F; - y[4] |= (call2>>26)&0x3F; - y[5]= (call2>>20)&0x3F; - y[6]= (call2>>14)&0x3F; - y[7]= (call2>>8)&0x3F; - y[8]= (call2>>2)&0x3F; - y[9]= (call2<<4)&0x3F; + y[4] |= (call2>>26)&0x3F; + y[5]= (call2>>20)&0x3F; + y[6]= (call2>>14)&0x3F; + y[7]= (call2>>8)&0x3F; + y[8]= (call2>>2)&0x3F; + y[9]= (call2<<4)&0x3F; - y[9] |= (grid>>12)&0x3F; - y[10]= (grid>>6)&0x3F; - y[11]= (grid)&0x3F; + y[9] |= (grid>>12)&0x3F; + y[10]= (grid>>6)&0x3F; + y[11]= (grid)&0x3F; } void decodemsg_jt65(int *call1, int *call2, int *grid, const int *x) { - int nc1, nc2, ng; + int nc1, nc2, ng; - nc1 = x[4]>>2; - nc1 |= x[3]<<4; - nc1 |= x[2]<<10; - nc1 |= x[1]<<16; - nc1 |= x[0]<<22; + nc1 = x[4]>>2; + nc1 |= x[3]<<4; + nc1 |= x[2]<<10; + nc1 |= x[1]<<16; + nc1 |= x[0]<<22; - nc2 = x[9]>>4; - nc2 |= x[8]<<2; - nc2 |= x[7]<<8; - nc2 |= x[6]<<14; - nc2 |= x[5]<<20; - nc2 |= (x[4]&0x03)<<26; + nc2 = x[9]>>4; + nc2 |= x[8]<<2; + nc2 |= x[7]<<8; + nc2 |= x[6]<<14; + nc2 |= x[5]<<20; + nc2 |= (x[4]&0x03)<<26; - ng = x[11]; - ng |= x[10]<<6; - ng |= (x[9]&0x0F)<<12; + ng = x[11]; + ng |= x[10]<<6; + ng |= (x[9]&0x0F)<<12; - *call1 = nc1; - *call2 = nc2; - *grid = ng; + *call1 = nc1; + *call2 = nc2; + *grid = ng; } - - diff --git a/lib/qra/qra65/qra65.h b/lib/qra/qra65/qra65.h index 25b4b4402..8521ffe2b 100644 --- a/lib/qra/qra65/qra65.h +++ b/lib/qra/qra65/qra65.h @@ -43,18 +43,18 @@ #define GRID_BLANK 0x7E91 typedef struct { - float decEsNoMetric; - int apflags; - int apmycall; - int apsrccall; - int apmsg_cqqrz[12]; // [cq/qrz ? blank] - int apmsg_call1[12]; // [mycall ? blank] - int apmsg_call1_call2[12]; // [mycall srccall ?] - int apmask_cqqrz[12]; - int apmask_cqqrz_ooo[12]; - int apmask_call1[12]; - int apmask_call1_ooo[12]; - int apmask_call1_call2[12]; + float decEsNoMetric; + int apflags; + int apmycall; + int apsrccall; + int apmsg_cqqrz[12]; // [cq/qrz ? blank] + int apmsg_call1[12]; // [mycall ? blank] + int apmsg_call1_call2[12]; // [mycall srccall ?] + int apmask_cqqrz[12]; + int apmask_cqqrz_ooo[12]; + int apmask_call1[12]; + int apmask_call1_ooo[12]; + int apmask_call1_call2[12]; } qra65codec; #ifdef __cplusplus diff --git a/lib/qra/qra65/qra65_subs.c b/lib/qra/qra65/qra65_subs.c index e5c7f2a06..46bddc125 100644 --- a/lib/qra/qra65/qra65_subs.c +++ b/lib/qra/qra65/qra65_subs.c @@ -11,20 +11,32 @@ void qra65_enc_(int x[], int y[]) qra65_encode(codec, y, x); } -void qra65_dec_(float r[], int xdec[], int* rc) +void qra65_dec_(float r[], int* nmycall, int xdec[], int* rc) { // Return codes: -// rc<0 no decode +// rc=-16 failed sanity check +// rc=-2 decoded, but crc check failed +// rc=-1 no decode // rc=0 [? ? ?] AP0 (decoding with no a-priori information) // rc=1 [CQ ? ?] AP27 -// rc=2 [CQ ? ] AP44 +// rc=2 [CQ ? ] AP42 // rc=3 [CALL ? ?] AP29 -// rc=4 [CALL ? ] AP45 +// rc=4 [CALL ? ] AP44 // rc=5 [CALL CALL ?] AP57 - int ncall=0xf70c238; //K1ABC - // int ncall=0x890c60c; //KA1ABC - int i; + static int ncall0=0; + int ncall; + int x[63],y[12]; + + ncall = *nmycall; qra65codec *codec = qra65_init(1,ncall); //codec for ncall +/* + if(ncall != ncall0) { + memset(y,0,sizeof(y)); + qra65_encode(codec, y, x); + printf("Updated codec %d\n",ncall); + } + ncall0=ncall; +*/ *rc = qra65_decode(codec,xdec,r); } diff --git a/lib/qra/qra65/qra65sim.f90 b/lib/qra/qra65/qra65sim.f90 index fdb8ea70e..476c938a3 100644 --- a/lib/qra/qra65/qra65sim.f90 +++ b/lib/qra/qra65/qra65sim.f90 @@ -121,6 +121,8 @@ program qra65sim h=default_header(12000,npts) dfsig=2000.0/nsigs !Freq spacing between sigs in file (Hz) + print*,'A',nsigs,nfiles + do ifile=1,nfiles !Loop over requested number of files write(fname,1002) ifile !Output filename 1002 format('000000_',i4.4) @@ -146,9 +148,8 @@ program qra65sim call packmsg(msg,dgen,itype) !Pack message into 12 six-bit bytes call qra65_enc(dgen,sent) !Encode using QRA65 ! call qra65_dec(sent,dgen,ierr) !Decode (### for test only ###) - - write(*,3001) sent -3001 format(21i3) +! write(*,3001) sent +!3001 format(21i3) k=0 do j=1,nsym !Insert sync and data into itone()