diff --git a/lib/qra/qra64/qra64.c b/lib/qra/qra64/qra64.c index 3951c031b..30bd466de 100644 --- a/lib/qra/qra64/qra64.c +++ b/lib/qra/qra64/qra64.c @@ -40,7 +40,7 @@ resulting code is a (12,63) code #include "../qracodes/qracodes.h" #include "../qracodes/qra13_64_64_irr_e.h" #include "../qracodes/pdmath.h" -#include "../qracodes/normrnd.h" +//#include "../qracodes/normrnd.h" // Code parameters of the QRA64 mode #define QRA64_CODE qra_13_64_64_irr_e @@ -526,7 +526,7 @@ int qra64_decode_fastfading( } - +/* int qra64_fastfading_channel(float **rxen, const int *xmsg, const int submode, const float EbN0dB, const float B90, const int fadingModel) { // Simulate transmission over a fading channel and non coherent detection @@ -647,7 +647,7 @@ int qra64_fastfading_channel(float **rxen, const int *xmsg, const int submode, c return 0; } - +*/ // Static functions definitions ---------------------------------------------- diff --git a/lib/qra/qra64/qra64_all.c b/lib/qra/qra64/qra64_all.c new file mode 100644 index 000000000..28f8ab928 --- /dev/null +++ b/lib/qra/qra64/qra64_all.c @@ -0,0 +1,1050 @@ +/* +qra64.c +Encoding/decoding functions for the QRA64 mode + +(c) 2016 - Nico Palermo, IV3NWV + +------------------------------------------------------------------------------- + + 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 . + +----------------------------------------------------------------------------- + +QRA 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 + +#include "qra64.h" +#include "../qracodes/qracodes.h" +#include "../qracodes/qra13_64_64_irr_e.h" +#include "../qracodes/pdmath.h" +#include "../qracodes/normrnd.h" + +// Code parameters of the QRA64 mode +#define QRA64_CODE qra_13_64_64_irr_e +#define QRA64_NMSG 218 // Must much value indicated in QRA64_CODE.NMSG + +#define QRA64_KC (QRA64_K+1) // Information symbols (crc included) +#define QRA64_NC (QRA64_N+1) // Codeword length (as defined in the code) +#define QRA64_NITER 100 // max number of iterations per decode + +// 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 qra64_decode_attempts(qra64codec *pcodec, int *xdec, const float *ix); +static int qra64_do_decode(int *x, const float *pix, const int *ap_mask, + const int *ap_x); +static float qra64_fastfading_estim_noise_std( + float *rxen, + const float esnometric, + const int submode); +static void qra64_fastfading_intrinsics( + float *pix, + const float *rxamp, + const float *hptr, + const int hlen, + const float cmetric, + const int submode); +static float qra64_fastfading_msg_esno( + const int *ydec, + const float *rxamp, + const float sigma, + const float EsNoMetric, + const int hlen, + const int submode); + + +// 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_GRIDFULL12 0x3FFC // less aggressive mask (to be used with full AP decoding) +#define MASK_GRIDBIT 0x8000 // b[15] is 1 for free text, 0 otherwise +// ---------------------------------------------------------------------------- + +qra64codec *qra64_init(int flags) +{ + + // 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*(QRA64_KC)/(QRA64_NC); + + qra64codec *pcodec = (qra64codec*)malloc(sizeof(qra64codec)); + + if (!pcodec) + return 0; // can't allocate memory + + pcodec->decEsNoMetric = 1.0f*QRA64_m*R*EbNoMetric; + pcodec->apflags = flags; + + memset(pcodec->apmsg_set,0,APTYPE_SIZE*sizeof(int)); + + if (flags==QRA_NOAP) + return pcodec; + + // 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); + + // 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_GRIDFULL12); + encodemsg_jt65(pcodec->apmask_cq_call2, MASK_CQQRZ, MASK_CALL2, MASK_GRIDBIT); + encodemsg_jt65(pcodec->apmask_cq_call2_ooo, MASK_CQQRZ, MASK_CALL2, MASK_GRIDFULL12); + + 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] +// APTYPE_CQHISCALL set [cq/qrz hiscall ?/blank] and [cq/qrz 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; + case APTYPE_CQHISCALL: + encodemsg_jt65(pcodec->apmsg_cq_call2, CALL_CQ, hiscall, GRID_BLANK); + encodemsg_jt65(pcodec->apmsg_cq_call2_grid, CALL_CQ, 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 (aptype=APTYPE_SIZE) + 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 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 + qra_encode(&QRA64_CODE, ency, encx); // encode msg+crc using given QRA code + + // copy codeword to output puncturing the crc symbol + memcpy(y,ency,QRA64_K*sizeof(int)); // copy information symbols + memcpy(y+QRA64_K,ency+QRA64_KC,QRA64_C*sizeof(int)); // copy parity symbols + + 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, nothing to do + + // It's a [hiscall mycall grid] message + + // 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 hiscall + // Set APTYPE_BOTHCALLS so decoder will try for [mycall hiscall ?] msgs + qra64_apset(pcodec,mycall, hiscall, GRID_BLANK, APTYPE_BOTHCALLS); + } + +} + +#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 + noisestd = qra_mfskbesselmetric(ix, rxen, QRA64_m, QRA64_N,pcodec->decEsNoMetric); + + // de-puncture observations adding a uniform distribution for the crc symbol + + // move check symbols distributions one symbol towards the end + dsttmp = PD_ROWADDR(ix,QRA64_M, QRA64_NC-1); //Point to last symbol prob dist + srctmp = dsttmp-QRA64_M; // source is the previous pd + 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 (ebnoval4) + return -17; // invalid submode + + if (B90<1.0f || B90>238.0f) + return -18; // B90 out of range + + // compute index to most appropriate amplitude weighting function coefficients + hidx = (int)(log((float)B90)/log(1.09f) - 0.499f); + + if (hidx<0 || hidx > 64) + return -19; // index of weighting function out of range + + if (fadingModel==0) { // gaussian fading model + // point to gaussian weighting taps + hlen = hlen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = hptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else if (fadingModel==1) { + // point to lorentzian weighting taps + hlen = hlen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = hptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else + return -20; // invalid fading model index + + + // compute (euristically) the optimal decoder metric accordingly the given spread amount + // We assume that the decoder threshold is: + // Es/No(dB) = Es/No(AWGN)(dB) + 8*log(B90)/log(240)(dB) + // that's to say, at the maximum Doppler spread bandwidth (240 Hz) there's a ~8 dB Es/No degradation + // over the AWGN case + tempf = 8.0f*(float)log((float)B90)/(float)log(240.0f); + EsNoMetric = pcodec->decEsNoMetric*(float)pow(10.0f,tempf/10.0f); + + // Step 1 ----------------------------------------------------------------------------------- + // Evaluate the noise stdev from the received energies at nominal tone frequencies + // and transform energies to amplitudes + tempf = hptr[hlen-1]; // amplitude weigth at nominal freq; + tempf = tempf*tempf; // fractional energy at nominal freq. bin + + noisestd = qra64_fastfading_estim_noise_std(rxen, EsNoMetric, submode); + cmetric = (float)sqrt(M_PI_2*EsNoMetric)/noisestd; + + // Step 2 ----------------------------------------------------------------------------------- + // Compute message symbols probability distributions + qra64_fastfading_intrinsics(ix, rxen, hptr, hlen, cmetric, submode); + + // Step 3 --------------------------------------------------------------------------- + // De-puncture observations adding a uniform distribution for the crc symbol + // Move check symbols distributions one symbol towards the end + dsttmp = PD_ROWADDR(ix,QRA64_M, QRA64_NC-1); //Point to last symbol prob dist + srctmp = dsttmp-QRA64_M; // source is the previous pd + for (k=0;k238.0f) + return -18; // B90 out of range + + // compute index to most appropriate amplitude weighting function coefficients + hidx = (int)(log((float)B90)/log(1.09f) - 0.499f); + + if (hidx<0 || hidx > 64) + return -19; // index of weighting function out of range + + if (fadingModel==0) { // gaussian fading model + // point to gaussian weighting taps + hlen = hlen_tab_gauss[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = hptr_tab_gauss[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else if (fadingModel==1) { + // point to lorentzian weighting taps + hlen = hlen_tab_lorentz[hidx]; // hlen = (L+1)/2 (where L=(odd) number of taps of w fun) + hptr = hptr_tab_lorentz[hidx]; // pointer to the first (L+1)/2 coefficients of w fun + } + else + return -20; // invalid fading model index + + + // Compute the unfaded tone amplitudes from the Eb/No value passed to the call + N0 = 1.0f; // assume unitary noise PSD + sigmanoise = (float)sqrt(N0/2); + EsN0 = (float)pow(10.0f,EbN0dB/10.0f)*QRA64_m*QRA64_K/QRA64_N; // Es/No = m*R*Eb/No + Es = EsN0*N0; + A = (float)sqrt(Es/2.0f); // unfaded tone amplitude (i^2+q^2 = Es/2+Es/2 = Es) + + + // Generate gaussian noise iq components + normrnd_s(channel_out, bpm*2, 0 , sigmanoise); + + // Add message symbols energies + for (n=0;n=0;j--) { + sigmasig = A*hptr[j]; + normrnd_s(iq, 2, 0 , sigmasig); +// iq[0]=sigmasig*sqrt(2); iq[1]=0; debug: used to verify Eb/No + *curi++ += iq[0]; + *curq++ += iq[1]; +// tote +=iq[0]*iq[0]+iq[1]*iq[1]; // debug + } + + } + +// tote = tote/QRA64_N; // debug + + // compute total bin energies (S+N) and store in first half of buffer + curi = channel_out; + curq = channel_out+bpm; + for (n=0;n=0;j--) { + u = *curbin++ * hptr[j]*cmetric; + u = u*u/(u+(float)M_E); // log(I0(u)) approx. + loglh = loglh + u; + } + if (loglh>maxloglh) // keep track of the max loglikelihood + maxloglh = loglh; + curix[k]=loglh; + } + // scale to likelihoods + sumix = 0.f; + for (k=0;k=0;j--) { + u = *curbin++; + msgsn += u*u; + } + + } + + msgsn = msgsn/(QRA64_N*tothlen); // avg msg energy per bin (noise included) + + // as sigma is overestimated (sigmatrue = sigma*sqrt((1+EsNoMetric/bps)/(1+EsNo/bps)) + // we have: msgsn = (1+x/hlen)/(1+x/bps)*2*sigma^2*(1+EsnoMetric/bps), where x = Es/N0(true) + // + // we can then write: + // u = msgsn/2.0f/(sigma*sigma)/(1.0f+EsNoMetric/bps); + // (1+x/hlen)/(1+x/bps) = u + + u = msgsn/(2.0f*sigma*sigma)/(1.0f+EsNoMetric/bps); + + // check u>1 + if (u<1) + return 0.f; + + // check u(bps/tothlen)) + return 10000.f; + + // solve for Es/No + esno = (u-1.0f)/(1.0f/tothlen-u/bps); + + return esno; + + +} + + +// Attempt to decode given intrisic information +static int qra64_decode_attempts(qra64codec *pcodec, int *xdec, const float *ix) +{ + int rc; + + // Attempt to decode without a-priori info -------------------------------- + rc = qra64_do_decode(xdec, ix, NULL, NULL); + if (rc>=0) + return 0; // successfull decode with AP0 + else + if (pcodec->apflags==QRA_NOAP) + // nothing more to do + return rc; // rc<0 = unsuccessful decode + + // Here we handle decoding with AP knowledge + + // Attempt to decode CQ calls + rc = qra64_do_decode(xdec,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); + if (rc>=0) return 1; // decoded [cq/qrz ? ?] + + rc = qra64_do_decode(xdec, ix, pcodec->apmask_cqqrz_ooo, + pcodec->apmsg_cqqrz); + if (rc>=0) return 2; // decoded [cq ? ooo] + + // 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) return 3; // decoded [mycall ? ?] + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call1_ooo, + pcodec->apmsg_call1); + 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) return 5; // decoded [mycall srccall ?] + } + + // attempt to decode [? hiscall ?/b] msgs + if (pcodec->apmsg_set[APTYPE_HISCALL]) { + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2, + pcodec->apmsg_call2); + if (rc>=0) return 6; // decoded [? hiscall ?] + rc = qra64_do_decode(xdec, ix, pcodec->apmask_call2_ooo, + pcodec->apmsg_call2); + if (rc>=0) return 7; // decoded [? hiscall ooo] + } + + // attempt to decode [cq/qrz hiscall ?/b/grid] msgs + if (pcodec->apmsg_set[APTYPE_CQHISCALL]) { + + rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2, + pcodec->apmsg_cq_call2); + if (rc>=0) return 9; // decoded [cq/qrz hiscall ?] + + rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2_ooo, + pcodec->apmsg_cq_call2_grid); + if (rc>=0) { + // Full AP mask need special handling + // To minimize false decodes we check the decoded message + // with what passed in the ap_set call + if (memcmp(pcodec->apmsg_cq_call2_grid,xdec, QRA64_K*sizeof(int))!=0) + return -1; + else + return 11; // decoded [cq/qrz hiscall grid] + }; + + rc = qra64_do_decode(xdec, ix, pcodec->apmask_cq_call2_ooo, + pcodec->apmsg_cq_call2); + if (rc>=0) { + // Full AP mask need special handling + // To minimize false decodes we check the decoded message + // with what passed in the ap_set call + if (memcmp(pcodec->apmsg_cq_call2,xdec, QRA64_K*sizeof(int))!=0) + return -1; + else + return 10; // decoded [cq/qrz hiscall ] + } + } + + // attempt to decode [mycall hiscall grid] + 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) { + // Full AP mask need special handling + // All the three msg fields were given. + // To minimize false decodes we check the decoded message + // with what passed in the ap_set call + if (memcmp(pcodec->apmsg_call1_call2_grid,xdec, QRA64_K*sizeof(int))!=0) + return -1; + else + return 8; // decoded [mycall hiscall grid] + } + } + + // all decoding attempts failed + return rc; +} + + + +// Decode with given a-priori information +static int qra64_do_decode(int *xdec, const float *pix, const int *ap_mask, + const int *ap_x) +{ + int rc; + const float *ixsrc; + float ix_masked[QRA64_NC*QRA64_M]; // Masked intrinsic information + float ex[QRA64_NC*QRA64_M]; // Extrinsic information from the decoder + + float v2cmsg[QRA64_NMSG*QRA64_M]; // buffers for the decoder messages + float c2vmsg[QRA64_NMSG*QRA64_M]; + + 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(&QRA64_CODE,ex,ixsrc,QRA64_NITER,v2cmsg,c2vmsg); + if (rc<0) + return -1; // no convergence in given iterations + + // decode + qra_mapdecode(&QRA64_CODE,xdec,ex,ixsrc); + + // verify crc + if (calc_crc6(xdec,QRA64_K)!=xdec[QRA64_K]) // crc doesn't match (detected error) + return -2; // decoding was succesfull but crc doesn't match + + return 0; +} + + +// 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 (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; +} + +static void ix_mask(float *dst, const float *src, const int *mask, + const int *x) +{ + // mask intrinsic information (channel observations) with a priori knowledge + + int k,kk, smask; + float *row; + + memcpy(dst,src,(QRA64_NC*QRA64_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[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; + +} +void decodemsg_jt65(int *call1, int *call2, int *grid, const int *x) +{ + int nc1, nc2, ng; + + 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; + + ng = x[11]; + ng |= x[10]<<6; + ng |= (x[9]&0x0F)<<12; + + *call1 = nc1; + *call2 = nc2; + *grid = ng; +}