Nico's additions for new AP decoding modes and improved control of AP decoding.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6926 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Joe Taylor 2016-07-18 12:42:10 +00:00
parent df8d0ab665
commit f3703e0241
7 changed files with 455 additions and 136 deletions

View File

@ -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<ntx;k++) {
printf(".");
rx = mfskchannel(y,channel_type,EbNodB);
rc = qra64_decode(codec_k1jt, xdec,rx);
if (rc>=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<snrdb> : set simulation SNR in 2500 Hz BW (default:-27.5 dB)\n");
printf(" -c<channel> : set channel type 0=AWGN (default) 1=Rayleigh\n");
printf(" -a<ap-type> : set decode type 0=NO_AP 1=AUTO_AP (default)\n");
printf(" -a<ap-type> : set decode type 0=NOAP 1=AUTOAP (default) 2=USERAP\n");
printf(" -t<testtype>: 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;
}

View File

@ -35,6 +35,7 @@ resulting code is a (12,63) code
#include <stdlib.h>
#include <math.h>
#include <string.h>
#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 (aptype<APTYPE_CQQRZ || aptype>APTYPE_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;k<QRA64_N;k++) {
msge +=rxen[ydec[k]]; // add energy of current symbol
rxen+=QRA64_M; // ptr to next symbol
}
// NOTE:
// To make a more accurate Eb/No estimation we should compute the noise variance
// on all the rxen values but the transmitted symbols.
// Noisestd is compute by qra_mfskbesselmetric assuming that
// the signal power is much less than the total noise power in the QRA64_M tones
// but this is true only if the Eb/No is low.
// Here, in order to improve accuracy, we linearize the estimated Eb/No value empirically
// (it gets compressed when it is very high as in this case the noise variance
// is overestimated)
// this would be the exact value if the noisestd were not overestimated at high Eb/No
ebnoval = (0.5f/(QRA64_K*QRA64_m))*msge/(noisestd*noisestd)-1.0f;
// Empirical linearization (to remove the noise variance overestimation)
// the resulting SNR is accurate up to +20 dB (51 dB Eb/No)
if (ebnoval>57.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<EBNO_MIN)
ebnoval = EBNO_MIN;
*ebno = ebnoval;
return rc;
}
@ -211,7 +362,7 @@ int qra64_decode(qra64codec *pcodec, int *x, const float *rxen)
// Static functions definitions ----------------------------------------------
// Decode with given a-priori information
static int qra64_do_decode(int *x, const float *pix, const int *ap_mask,
static int qra64_do_decode(int *xdec, const float *pix, const int *ap_mask,
const int *ap_x)
{
int rc;
@ -221,7 +372,6 @@ static int qra64_do_decode(int *x, const float *pix, const int *ap_mask,
float v2cmsg[QRA64_NMSG*QRA64_M]; // buffers for the decoder messages
float c2vmsg[QRA64_NMSG*QRA64_M];
int xdec[QRA64_KC];
if (ap_mask==NULL) { // no a-priori information
ixsrc = pix; // intrinsic source is what passed as argument
@ -244,9 +394,6 @@ static int qra64_do_decode(int *x, const float *pix, const int *ap_mask,
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
// success. copy decoded message to output buffer
memcpy(x,xdec,QRA64_K*sizeof(int));
return 0;
}
// crc functions --------------------------------------------------------------

View File

@ -23,8 +23,9 @@
#define _qra64_h_
// qra64_init(...) initialization flags
#define QRA_NOAP 0 // don't use a-priori knowledge
#define QRA_AUTOAP 1 // use auto a-priori knowledge
#define QRA_NOAP 0 // don't use a-priori knowledge
#define QRA_AUTOAP 1 // use auto a-priori knowledge
#define QRA_USERAP 2 // a-priori knowledge messages provided by the user
// QRA code parameters
#define QRA64_K 12 // information symbols
@ -42,33 +43,48 @@
#define CALL_DE 0xFF641D1
#define GRID_BLANK 0x7E91
// Types of a-priori knowledge messages
#define APTYPE_CQQRZ 0 // [cq/qrz ? ?/blank]
#define APTYPE_MYCALL 1 // [mycall ? ?/blank]
#define APTYPE_HISCALL 2 // [? hiscall ?/blank]
#define APTYPE_BOTHCALLS 3 // [mycall hiscall ?]
#define APTYPE_FULL 4 // [mycall hiscall grid]
#define APTYPE_SIZE (APTYPE_FULL+1)
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 apmsg_set[APTYPE_SIZE]; // indicate which ap type knowledge has been set by the user
// ap messages buffers
int apmsg_cqqrz[12]; // [cq/qrz ? ?/blank]
int apmsg_call1[12]; // [mycall ? ?/blank]
int apmsg_call2[12]; // [? hiscall ?/blank]
int apmsg_call1_call2[12]; // [mycall hiscall ?]
int apmsg_call1_call2_grid[12]; // [mycall hiscall grid]
// ap messages masks
int apmask_cqqrz[12];
int apmask_cqqrz_ooo[12];
int apmask_call1[12];
int apmask_call1_ooo[12];
int apmask_call2[12];
int apmask_call2_ooo[12];
int apmask_call1_call2[12];
int apmask_call1_call2_grid[12];
} qra64codec;
#ifdef __cplusplus
extern "C" {
#endif
qra64codec *qra64_init(int flags, const int mycall);
qra64codec *qra64_init(int flags);
// QRA64 mode initialization function
// arguments:
// flags: set the decoder mode
// When flags = QRA_NOAP no a-priori information will be used by the decoder
// When flags = QRA_AUTOAP the decoder will attempt to decode with the amount
// of available a-priori information
// mycall: 28-bit packed callsign of the user (as computed by JT65)
// When flags = QRA_USERAP the decoder will attempt to decode with the amount
// of a-priori information as provided by the caller with the function qra64_apset(...)
// returns:
// Pointer to the qra64codec data structure allocated and inizialized by the function
// this handle should be passed to the encoding/decoding functions
@ -86,10 +102,12 @@ void qra64_encode(qra64codec *pcodec, int *y, const int *x);
// y must point to an array of integers of lenght 63 (i.e. defined as int y[63])
// -------------------------------------------------------------------------------------------
int qra64_decode(qra64codec *pcodec, int *x, const float *r);
int qra64_decode(qra64codec *pcodec, float *ebno, int *x, const float *r);
// QRA64 mode decoder
// arguments:
// pcodec = pointer to a qra64codec data structure as returned by qra64_init
// ebno = pointer to a float number where the avg Eb/No (in dB) will be stored
// in case of successfull decoding (pass a null pointer if not interested)
// x = pointer to the array of integers where the decoded message will be stored
// x must point to an array of integers (i.e. defined as int x[12])
// r = pointer to the received symbols energies (squared amplitudes)
@ -102,14 +120,54 @@ int qra64_decode(qra64codec *pcodec, int *x, const float *r);
// The return code is <0 when decoding is unsuccessful
// -16 indicates that the definition of QRA64_NMSG does not match what required by the code
// If the decoding process is successfull the return code is accordingly to the following table
// rc=0 [? ? ?] AP0 (decoding with no a-priori)
// rc=1 [CQ ? ?] AP27
// rc=2 [CQ ? ] AP44
// rc=3 [CALL ? ?] AP29
// rc=4 [CALL ? ] AP45
// rc=5 [CALL CALL ?] AP57
// return codes in the range 1-5 indicate the amount of a-priori information which was required
// to decode the received message and are possible only when the QRA_AUTOAP mode has been enabled.
// rc=0 [? ? ?] AP0 (decoding with no a-priori)
// rc=1 [CQ ? ?] AP27
// rc=2 [CQ ? ] AP44
// rc=3 [CALL ? ?] AP29
// rc=4 [CALL ? ] AP45
// rc=5 [CALL CALL ?] AP57
// rc=6 [? CALL ?] AP29
// rc=7 [? CALL ] AP45
// rc=8 [CALL CALL GRID] AP72
// return codes in the range 1-8 indicate the amount and the type of a-priori information
// which was required to decode the received message
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 the type of AP to be set accordingly to the following table:
// 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 (aptype must be in the range [APTYPE_MYCALL..APTYPE_FULL]
// (APTYPE_CQQRZ [cq/qrz ? ?] is set during the initialization function and
// doesn't need to be set by the user
void qra64_apdisable(qra64codec *pcodec, const int aptype);
// disable specific AP type
// arguments:
// pcodec = pointer to a qra64codec data structure as returned by qra64_init
// aptype = define the type of AP to be disabled
// APTYPE_CQQRZ disable [cq/qrz ? ?/blank]
// APTYPE_MYCALL disable [mycall ? ?/blank]
// APTYPE_HISCALL disable [? hiscall ?/blank]
// APTYPE_BOTHCALLS disable [mycall hiscall ?]
// APTYPE_FULL disable [mycall hiscall grid]
void qra64_close(qra64codec *pcodec);
// Free memory allocated by qra64_init
// arguments:
// pcodec = pointer to a qra64codec data structure as returned by qra64_init
// -------------------------------------------------------------------------------------------
// encode/decode std msgs in 12 symbols as done in jt65

View File

@ -4,11 +4,16 @@
#include "qra64.h"
#include <stdio.h>
#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
}

View File

@ -56,18 +56,38 @@
#include <process.h> // _beginthread
#endif
#if __linux__
#include <unistd.h>
#if defined(__linux__)
// remove unwanted macros
#define __cdecl
// implements Windows API
#include <time.h>
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 <unistd.h>
#define Sleep(x) usleep(x*1000)
#endif
#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) )
#include <pthread.h>
#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;j<nthreads;j++)
#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) )
{
void *rc;
if (pthread_join (wt[j].thread, &rc)) {
perror ("Waiting working threads to exit");
exit (255);
}
}
#else
while(wt[j].done==0)
Sleep(1);
#endif
printf("\n");
fflush (stdout);
@ -601,6 +650,7 @@ int main(int argc, char* argv[])
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();

View File

@ -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;
}

View File

@ -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
}