From f3703e024171b7cc134110538321f23e1f4d3ce2 Mon Sep 17 00:00:00 2001
From: Joe Taylor <k1jt@arrl.org>
Date: Mon, 18 Jul 2016 12:42:10 +0000
Subject: [PATCH] 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
---
 lib/qra/qra64/main.c        | 107 +++++++++-----
 lib/qra/qra64/qra64.c       | 277 +++++++++++++++++++++++++++---------
 lib/qra/qra64/qra64.h       |  94 +++++++++---
 lib/qra/qra64/qra64_subs.c  |  37 ++++-
 lib/qra/qracodes/main.c     |  60 +++++++-
 lib/qra/qracodes/qracodes.c |   8 +-
 lib/qra/qracodes/qracodes.h |   8 +-
 7 files changed, 455 insertions(+), 136 deletions(-)

diff --git a/lib/qra/qra64/main.c b/lib/qra/qra64/main.c
index 85b57ff28..c4ea87f4f 100644
--- a/lib/qra/qra64/main.c
+++ b/lib/qra/qra64/main.c
@@ -89,6 +89,9 @@ unsigned GetTickCount(void) {
 #define CHANNEL_AWGN     0
 #define CHANNEL_RAYLEIGH 1
 
+#define JT65_SNR_EBNO_OFFSET 29.1f		// with the synch used in JT65
+#define QRA64_SNR_EBNO_OFFSET 31.0f		// with the costas array synch 
+
 void printwordd(char *msg, int *x, int size)
 {
   int k;
@@ -171,13 +174,21 @@ symbol.
 #define GRID_JN66		0x3AE4		// JN66
 #define GRID_73 		0x7ED0		// 73
 
-char decode_type[6][32] = {
+char decode_type[9][32] = {
   "[?    ?    ?] AP0",
   "[CQ   ?    ?] AP27",
   "[CQ   ?     ] AP42",
   "[CALL ?    ?] AP29",
   "[CALL ?     ] AP44",
-  "[CALL CALL ?] AP57"
+  "[CALL CALL ?] AP57",
+  "[?    CALL ?] AP29",
+  "[?    CALL  ] AP44",
+  "[CALL CALL G] AP72"
+};
+char apmode_type[3][32] = {
+  "NO AP",
+  "AUTO AP",
+  "USER AP"
 };
 
 int test_proc_1(int channel_type, float EbNodB, int mode)
@@ -229,10 +240,10 @@ be decoded
   float *rx;
   int rc;
 
-// Each simulated station must use its own codec, since it might work with
+// Each simulated station must use its own codec since it might work with
 // different a-priori information.
-  qra64codec *codec_iv3nwv = qra64_init(mode,CALL_IV3NWV);  // codec for IV3NWV
-  qra64codec *codec_k1jt   = qra64_init(mode,CALL_K1JT);    // codec for K1JT
+  qra64codec *codec_iv3nwv = qra64_init(mode);  // codec for IV3NWV
+  qra64codec *codec_k1jt   = qra64_init(mode);    // codec for K1JT
 
 // Step 1a: IV3NWV makes a CQ call (with no grid)
   printf("IV3NWV tx: CQ IV3NWV\n");
@@ -241,7 +252,7 @@ be decoded
   rx = mfskchannel(y,channel_type,EbNodB);
 
 // Step 1b: K1JT attempts to decode [? ? ?], [CQ/QRZ ? ?] or [CQ/QRZ ?]
-  rc = qra64_decode(codec_k1jt, xdec,rx);
+  rc = qra64_decode(codec_k1jt, 0, xdec,rx);
   if (rc>=0) { // decoded
     printf("K1JT   rx: received with apcode=%d %s\n",rc, decode_type[rc]);
 
@@ -252,7 +263,7 @@ be decoded
     rx = mfskchannel(y,channel_type,EbNodB);
 
 // Step 2b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?] or [IV3NWV ?]
-    rc = qra64_decode(codec_iv3nwv, xdec,rx);
+    rc = qra64_decode(codec_iv3nwv, 0, xdec,rx);
     if (rc>=0) { // decoded
       printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
 
@@ -263,7 +274,7 @@ be decoded
       rx = mfskchannel(y,channel_type,EbNodB);
 
 // Step 3b: K1JT attempts to decode [? ? ?] or [K1JT IV3NWV ?]
-      rc = qra64_decode(codec_k1jt, xdec,rx);
+      rc = qra64_decode(codec_k1jt, 0, xdec,rx);
       if (rc>=0) { // decoded
 	printf("K1JT   rx: received with apcode=%d %s\n",rc, decode_type[rc]);
 
@@ -274,7 +285,7 @@ be decoded
 	rx = mfskchannel(y,channel_type,EbNodB);
 
 // Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?]
-	rc = qra64_decode(codec_iv3nwv, xdec,rx);
+	rc = qra64_decode(codec_iv3nwv, 0, xdec,rx);
 	if (rc>=0) { // decoded
 	  printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
 	  return 0;
@@ -282,7 +293,7 @@ be decoded
       }
     }
   }
-  printf("the other party did not decode\n");
+  printf("no decode\n");
   return -1;
 }
 
@@ -307,6 +318,9 @@ message according to this table:
  rc=3    [CALL ?    ?] AP29
  rc=4    [CALL ?     ] AP44
  rc=5    [CALL CALL ?] AP57
+ rc=6    [?    CALL ?]     AP29
+ rc=7    [?    CALL  ]     AP44
+ rc=8    [CALL CALL GRID ] AP72
 
 The return code is <0 when decoding is unsuccessful
 
@@ -317,42 +331,67 @@ a particular type decode among the above 6 cases succeded.
   int x[QRA64_K], xdec[QRA64_K];
   int y[QRA64_N];
   float *rx;
+  float ebnodbest, ebnodbavg=0;
   int rc,k;
 
-  int ndecok[6] = { 0, 0, 0, 0, 0, 0};
+  int ndecok[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0};
+  int nundet = 0;
   int ntx = 100,ndec=0;
 
-  qra64codec *codec_iv3nwv = qra64_init(mode,CALL_IV3NWV);   // codec for IV3NWV
-  qra64codec *codec_k1jt   = qra64_init(mode,CALL_K1JT);     // codec for K1JT
+  qra64codec *codec_iv3nwv = qra64_init(mode);   // codec for IV3NWV
+  qra64codec *codec_k1jt   = qra64_init(mode);     // codec for K1JT
 
-// This will enable K1JT's decoder to look for IV3NWV calls
-  encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK);
-  qra64_encode(codec_k1jt, y, x);
-  printf("K1JT   tx: IV3NWV K1JT\n");
+  printf("\nQRA64 Test #2 - Decoding with AP knowledge (SNR-Eb/No offset = %.1f dB)\n\n",
+		   QRA64_SNR_EBNO_OFFSET);
+
+// This will enable K1JT's decoder to look for calls directed to him [K1JT ? ?/b]
+  printf("K1JT decoder enabled for [K1JT  ?     ?/blank]\n");
+  qra64_apset(codec_k1jt, CALL_K1JT,0,0,APTYPE_MYCALL);
+
+// This will enable K1JT's decoder to look for IV3NWV calls directed to him [K1JT IV3NWV ?/b]
+  printf("K1JT decoder enabled for [K1JT IV3NWV ?]\n");
+  qra64_apset(codec_k1jt, CALL_K1JT,CALL_IV3NWV,0,APTYPE_BOTHCALLS);
+
+// This will enable K1JT's decoder to look for msges sent by IV3NWV [? IV3NWV ?]
+  printf("K1JT decoder enabled for [?    IV3NWV ?/blank]\n");
+  qra64_apset(codec_k1jt, 0,CALL_IV3NWV,GRID_BLANK,APTYPE_HISCALL);
+
+// This will enable K1JT's decoder to look for full-knowledge [K1JT IV3NWV JN66] msgs
+  printf("K1JT decoder enabled for [K1JT IV3NWV JN66]\n");
+  qra64_apset(codec_k1jt, CALL_K1JT,CALL_IV3NWV,GRID_JN66,APTYPE_FULL);
 
   // IV3NWV reply to K1JT
-  printf("IV3NWV tx: K1JT IV3NWV JN66\n");
+  printf("\nIV3NWV encoder sends msg: [K1JT IV3NWV JN66]\n\n");
   encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66);
   qra64_encode(codec_iv3nwv, y, x);
 
-  printf("Simulating decodes by K1JT up to AP56 ...");
+  printf("Simulating K1JT decoder up to AP72\n");
 
   for (k=0;k<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;
 }
diff --git a/lib/qra/qra64/qra64.c b/lib/qra/qra64/qra64.c
index 89ed5a15f..2f35a651a 100644
--- a/lib/qra/qra64/qra64.c
+++ b/lib/qra/qra64/qra64.c
@@ -35,6 +35,7 @@ resulting code is a (12,63) code
 
 #include <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 --------------------------------------------------------------
diff --git a/lib/qra/qra64/qra64.h b/lib/qra/qra64/qra64.h
index 8fd7cc51e..456a2c6f7 100644
--- a/lib/qra/qra64/qra64.h
+++ b/lib/qra/qra64/qra64.h
@@ -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
diff --git a/lib/qra/qra64/qra64_subs.c b/lib/qra/qra64/qra64_subs.c
index 933035b3f..d857dc8af 100644
--- a/lib/qra/qra64/qra64_subs.c
+++ b/lib/qra/qra64/qra64_subs.c
@@ -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  
 }
diff --git a/lib/qra/qracodes/main.c b/lib/qra/qracodes/main.c
index 3fe7d44f3..5f9f067d4 100644
--- a/lib/qra/qracodes/main.c
+++ b/lib/qra/qracodes/main.c
@@ -56,18 +56,38 @@
 	#include <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();
diff --git a/lib/qra/qracodes/qracodes.c b/lib/qra/qracodes/qracodes.c
index 7af0945bf..748a9c9cd 100644
--- a/lib/qra/qracodes/qracodes.c
+++ b/lib/qra/qracodes/qracodes.c
@@ -27,8 +27,6 @@
 
 #include "qracodes.h"
 
-#define QRA_DEBUG 
-
 int qra_encode(const qracode *pcode, int *y, const int *x)
 {
 	int k,j,kk,jj;
@@ -144,7 +142,7 @@ static void qra_ioapprox(float *src, float C, int nitems)
 }
 
 
-void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric)
+float qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric)
 {
 	// Computes the codeword symbols intrinsic probabilities
 	// given the square of the received input amplitudes.
@@ -167,6 +165,8 @@ void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N
 	// nevertheless it is usually better than a generic parameter-free metric which
 	// makes no assumptions on the input Es/No.
 
+	// returns the estimated noise standard deviation
+
 	int k;
 	float rsum = 0.f;
 	float sigmaest, cmetric;
@@ -196,7 +196,7 @@ void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N
 		pd_norm(PD_ROWADDR(pix,M,k),m);
 		}
 
-	return;
+	return sigmaest;
 }
 
 
diff --git a/lib/qra/qracodes/qracodes.h b/lib/qra/qracodes/qracodes.h
index e4bac50aa..97542c118 100644
--- a/lib/qra/qracodes/qracodes.h
+++ b/lib/qra/qracodes/qracodes.h
@@ -67,10 +67,10 @@ typedef struct {
 extern "C" {
 #endif
 
-int  qra_encode(const qracode *pcode, int *y, const int *x);
-void qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric);
-int  qra_extrinsic(const qracode *pcode, float *pex, const float *pix, int maxiter,float *qra_v2cmsg,float *qra_c2vmsg);
-void qra_mapdecode(const qracode *pcode, int *xdec, float *pex, const float *pix);
+int   qra_encode(const qracode *pcode, int *y, const int *x);
+float qra_mfskbesselmetric(float *pix, const float *rsq, const int m, const int N, float EsNoMetric);
+int   qra_extrinsic(const qracode *pcode, float *pex, const float *pix, int maxiter,float *qra_v2cmsg,float *qra_c2vmsg);
+void  qra_mapdecode(const qracode *pcode, int *xdec, float *pex, const float *pix);
 
 #ifdef __cplusplus
 }