747 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			747 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  | main.c  | ||
|  | QRA64 mode encode/decode tests | ||
|  | 
 | ||
|  | (c) 2016 - Nico Palermo, IV3NWV | ||
|  | 
 | ||
|  | Thanks to Andrea Montefusco IW0HDV for his help on adapting the sources | ||
|  | to OSs other than MS Windows | ||
|  | 
 | ||
|  | ------------------------------------------------------------------------------ | ||
|  | This file is part of the qracodes project, a Forward Error Control | ||
|  | encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes. | ||
|  | 
 | ||
|  | Files in this package: | ||
|  |    main.c		 - this file | ||
|  |    qra64.c/.h     - qra64 mode encode/decoding functions | ||
|  | 
 | ||
|  |    ../qracodes/normrnd.{c,h}   - random gaussian number generator | ||
|  |    ../qracodes/npfwht.{c,h}    - Fast Walsh-Hadamard Transforms | ||
|  |    ../qracodes/pdmath.{c,h}    - Elementary math on probability distributions | ||
|  |    ../qracodes/qra12_63_64_irr_b.{c,h} - Tables for a QRA(12,63) irregular RA  | ||
|  |                                          code over GF(64) | ||
|  |    ../qracodes/qra13_64_64_irr_e.{c,h} - Tables for a QRA(13,64) irregular RA  | ||
|  |                                          code over GF(64) | ||
|  |    ../qracodes/qracodes.{c,h}  - QRA codes encoding/decoding functions | ||
|  | 
 | ||
|  | ------------------------------------------------------------------------------- | ||
|  | 
 | ||
|  |    qracodes is free software: you can redistribute it and/or modify | ||
|  |    it under the terms of the GNU General Public License as published by | ||
|  |    the Free Software Foundation, either version 3 of the License, or | ||
|  |    (at your option) any later version. | ||
|  |    qracodes is distributed in the hope that it will be useful, | ||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|  |    GNU General Public License for more details. | ||
|  | 
 | ||
|  |    You should have received a copy of the GNU General Public License | ||
|  |    along with qracodes source distribution.   | ||
|  |    If not, see <http://www.gnu.org/licenses/>.
 | ||
|  | 
 | ||
|  | ----------------------------------------------------------------------------- | ||
|  | 
 | ||
|  | The code used by the QRA64 mode is the code: QRA13_64_64_IRR_E: K=13 | ||
|  | N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.{h,c}). | ||
|  | 
 | ||
|  | This code has been designed to include a CRC as the 13th information | ||
|  | symbol and improve the code UER (Undetected Error Rate).  The CRC | ||
|  | symbol is not sent along the channel (the codes are punctured) and the | ||
|  | resulting code is still a (12,63) code with an effective code rate of | ||
|  | R = 12/63. | ||
|  | */ | ||
|  | 
 | ||
|  | // OS dependent defines and includes ------------------------------------------
 | ||
|  | 
 | ||
|  | #if _WIN32 // note the underscore: without it, it's not msdn official!
 | ||
|  | // Windows (x64 and x86)
 | ||
|  | #include <windows.h>   // required only for GetTickCount(...)
 | ||
|  | #include <process.h>   // _beginthread
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #if __linux__
 | ||
|  | #include <unistd.h>
 | ||
|  | #include <time.h>
 | ||
|  | 
 | ||
|  | unsigned GetTickCount(void) { | ||
|  |     struct timespec ts; | ||
|  |     unsigned theTick = 0U; | ||
|  |     clock_gettime( CLOCK_REALTIME, &ts ); | ||
|  |     theTick  = ts.tv_nsec / 1000000; | ||
|  |     theTick += ts.tv_sec * 1000; | ||
|  |     return theTick; | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #if __APPLE__
 | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #include <stdlib.h>
 | ||
|  | #include <stdio.h>
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #include "qra64.h"
 | ||
|  | #include "../qracodes/normrnd.h"		   // gaussian numbers generator
 | ||
|  | 
 | ||
|  | // ----------------------------------------------------------------------------
 | ||
|  | 
 | ||
|  | // channel types
 | ||
|  | #define CHANNEL_AWGN     0
 | ||
|  | #define CHANNEL_RAYLEIGH 1
 | ||
|  | #define CHANNEL_FASTFADE 2
 | ||
|  | 
 | ||
|  | #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; | ||
|  |   printf("\n%s ",msg); | ||
|  |   for (k=0;k<size;k++) | ||
|  |     printf("%2d ",x[k]); | ||
|  |   printf("\n"); | ||
|  | } | ||
|  | void printwordh(char *msg, int *x, int size) | ||
|  | { | ||
|  |   int k; | ||
|  |   printf("\n%s ",msg); | ||
|  |   for (k=0;k<size;k++) | ||
|  |     printf("%02hx ",x[k]); | ||
|  |   printf("\n"); | ||
|  | } | ||
|  | 
 | ||
|  | #define NSAMPLES (QRA64_N*QRA64_M)
 | ||
|  | 
 | ||
|  | static float rp[NSAMPLES]; | ||
|  | static float rq[NSAMPLES]; | ||
|  | static float chp[NSAMPLES]; | ||
|  | static float chq[NSAMPLES]; | ||
|  | static float r[NSAMPLES]; | ||
|  | 
 | ||
|  | float *mfskchannel(int *x, int channel_type, float EbNodB) | ||
|  | { | ||
|  | /*
 | ||
|  | Simulate an MFSK channel, either AWGN or Rayleigh. | ||
|  | 
 | ||
|  | x is a pointer to the transmitted codeword, an array of QRA64_N | ||
|  | integers in the range 0..63. | ||
|  | 
 | ||
|  | Returns the received symbol energies (squared amplitudes) as an array of  | ||
|  | (QRA64_M*QRA64_N) floats.  The first QRA64_M entries of this array are  | ||
|  | the energies of the first symbol in the codeword.  The second QRA64_M  | ||
|  | entries are those of the second symbol, and so on up to the last codeword  | ||
|  | symbol. | ||
|  | */ | ||
|  |   const float No = 1.0f;		        // noise spectral density
 | ||
|  |   const float sigma   = (float)sqrt(No/2.0f);	// std dev of noise I/Q components
 | ||
|  |   const float sigmach = (float)sqrt(1/2.0f);	// std dev of channel I/Q gains
 | ||
|  |   const float R = 1.0f*QRA64_K/QRA64_N;	 | ||
|  | 
 | ||
|  |   float EbNo = (float)pow(10,EbNodB/10); | ||
|  |   float EsNo = 1.0f*QRA64_m*R*EbNo; | ||
|  |   float Es = EsNo*No; | ||
|  |   float A = (float)sqrt(Es); | ||
|  |   int k; | ||
|  | 
 | ||
|  |   normrnd_s(rp,NSAMPLES,0,sigma); | ||
|  |   normrnd_s(rq,NSAMPLES,0,sigma); | ||
|  | 
 | ||
|  |   if(EbNodB>-15)  | ||
|  | 	if (channel_type == CHANNEL_AWGN)  | ||
|  | 	    for (k=0;k<QRA64_N;k++)  | ||
|  | 		  rp[k*QRA64_M+x[k]]+=A; | ||
|  | 	else  | ||
|  | 		if (channel_type == CHANNEL_RAYLEIGH) { | ||
|  | 			normrnd_s(chp,QRA64_N,0,sigmach); | ||
|  | 			normrnd_s(chq,QRA64_N,0,sigmach); | ||
|  | 			for (k=0;k<QRA64_N;k++) { | ||
|  | 				rp[k*QRA64_M+x[k]]+=A*chp[k]; | ||
|  | 				rq[k*QRA64_M+x[k]]+=A*chq[k]; | ||
|  | 				} | ||
|  | 			} | ||
|  | 		else { | ||
|  | 		return 0;	// unknown channel type
 | ||
|  | 		} | ||
|  | 
 | ||
|  |   // compute the squares of the amplitudes of the received samples
 | ||
|  |   for (k=0;k<NSAMPLES;k++)  | ||
|  |     r[k] = rp[k]*rp[k] + rq[k]*rq[k]; | ||
|  | 
 | ||
|  |   return r; | ||
|  | } | ||
|  | 
 | ||
|  | // These defines are some packed fields as computed by JT65 
 | ||
|  | #define CALL_IV3NWV		0x7F85AE7	
 | ||
|  | #define CALL_K1JT		0xF70DDD7
 | ||
|  | #define GRID_JN66		0x3AE4		// JN66
 | ||
|  | #define GRID_73 		0x7ED0		// 73
 | ||
|  | 
 | ||
|  | char decode_type[12][32] = { | ||
|  |   "[?    ?    ?] AP0", | ||
|  |   "[CQ   ?    ?] AP27", | ||
|  |   "[CQ   ?     ] AP42", | ||
|  |   "[CALL ?    ?] AP29", | ||
|  |   "[CALL ?     ] AP44", | ||
|  |   "[CALL CALL ?] AP57", | ||
|  |   "[?    CALL ?] AP29", | ||
|  |   "[?    CALL  ] AP44", | ||
|  |   "[CALL CALL G] AP72", | ||
|  |   "[CQ   CALL ?] AP55", | ||
|  |   "[CQ   CALL  ] AP70", | ||
|  |   "[CQ   CALL G] AP70" | ||
|  | }; | ||
|  | char apmode_type[3][32] = { | ||
|  |   "NO AP", | ||
|  |   "AUTO AP", | ||
|  |   "USER AP" | ||
|  | }; | ||
|  | 
 | ||
|  | int test_proc_1(int channel_type, float EbNodB, int mode) | ||
|  | { | ||
|  | /*
 | ||
|  | Here we simulate the following (dummy) QSO: | ||
|  | 
 | ||
|  | 1) CQ IV3NWV | ||
|  | 2)                 IV3NWV K1JT | ||
|  | 3) K1JT IV3NWV 73 | ||
|  | 4)                 IV3NWV K1JT 73 | ||
|  | 
 | ||
|  | No message repetition is attempted | ||
|  | 
 | ||
|  | The QSO is counted as successfull if IV3NWV received the last message | ||
|  | When mode=QRA_AUTOAP each decoder attempts to decode the message sent | ||
|  | by the other station using the a-priori information derived by what | ||
|  | has been already decoded in a previous phase of the QSO if decoding | ||
|  | with no a-priori information has not been successful. | ||
|  | 
 | ||
|  | Step 1) K1JT's decoder first attempts to decode msgs of type [? ? ?] | ||
|  | and if this attempt fails, it attempts to decode [CQ/QRZ ? ?]  or | ||
|  | [CQ/QRZ ?] msgs | ||
|  | 
 | ||
|  | Step 2) if IV3NWV's decoder is unable to decode K1JT's without AP it | ||
|  | attempts to decode messages of the type [IV3NWV ? ?] and [IV3NWV ?]. | ||
|  | 
 | ||
|  | Step 3) K1JT's decoder attempts to decode [? ? ?] and [K1JT IV3NWV ?] | ||
|  | (this last decode type has been enabled by K1JT's encoder at step 2) | ||
|  | 
 | ||
|  | Step 4) IV3NWV's decoder attempts to decode [? ? ?] and [IV3NWV K1JT | ||
|  | ?] (this last decode type has been enabled by IV3NWV's encoder at step | ||
|  | 3) | ||
|  | 
 | ||
|  | At each step the simulation reports if a decode was successful.  In | ||
|  | this case it also reports the type of decode (see table decode_type | ||
|  | above) | ||
|  | 
 | ||
|  | When mode=QRA_NOAP, only [? ? ?] decodes are attempted and no a-priori | ||
|  | information is used by the decoder | ||
|  | 
 | ||
|  | The function returns 0 if all of the four messages have been decoded | ||
|  | by their recipients (with no retries) and -1 if any of them could not | ||
|  | be decoded | ||
|  | */ | ||
|  | 
 | ||
|  |   int x[QRA64_K], xdec[QRA64_K]; | ||
|  |   int y[QRA64_N]; | ||
|  |   float *rx; | ||
|  |   int rc; | ||
|  | 
 | ||
|  | // Each simulated station must use its own codec since it might work with
 | ||
|  | // different a-priori information.
 | ||
|  |   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"); | ||
|  |   encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_BLANK); | ||
|  |   qra64_encode(codec_iv3nwv, y, x); | ||
|  |   rx = mfskchannel(y,channel_type,EbNodB); | ||
|  | 
 | ||
|  | // Step 1b: K1JT attempts to decode [? ? ?], [CQ/QRZ ? ?] or [CQ/QRZ ?]
 | ||
|  |   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]); | ||
|  | 
 | ||
|  | // Step 2a: K1JT replies to IV3NWV (with no grid)
 | ||
|  |     printf("K1JT   tx: IV3NWV K1JT\n"); | ||
|  |     encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_BLANK); | ||
|  |     qra64_encode(codec_k1jt, y, x); | ||
|  |     rx = mfskchannel(y,channel_type,EbNodB); | ||
|  | 
 | ||
|  | // Step 2b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?] or [IV3NWV ?]
 | ||
|  |     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]); | ||
|  | 
 | ||
|  | // Step 3a: IV3NWV replies to K1JT with a 73
 | ||
|  |       printf("IV3NWV tx: K1JT   IV3NWV 73\n"); | ||
|  |       encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV, GRID_73); | ||
|  |       qra64_encode(codec_iv3nwv, y, x); | ||
|  |       rx = mfskchannel(y,channel_type,EbNodB); | ||
|  | 
 | ||
|  | // Step 3b: K1JT attempts to decode [? ? ?] or [K1JT IV3NWV ?]
 | ||
|  |       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]); | ||
|  | 
 | ||
|  | // Step 4a: K1JT replies to IV3NWV with a 73
 | ||
|  | 	printf("K1JT   tx: IV3NWV K1JT   73\n"); | ||
|  | 	encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_73); | ||
|  | 	qra64_encode(codec_k1jt, y, x); | ||
|  | 	rx = mfskchannel(y,channel_type,EbNodB); | ||
|  | 
 | ||
|  | // Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?]
 | ||
|  | 	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; | ||
|  | 	} | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  |   printf("no decode\n"); | ||
|  |   return -1; | ||
|  | } | ||
|  | 
 | ||
|  | int test_proc_2(int channel_type, float EbNodB, int mode) | ||
|  | { | ||
|  | /*
 | ||
|  | Here we simulate the decoder of K1JT after K1JT has sent a msg [IV3NWV K1JT] | ||
|  | and IV3NWV sends him the msg [K1JT IV3NWV JN66]. | ||
|  | 
 | ||
|  | If mode=QRA_NOAP, K1JT decoder attempts to decode only msgs of type [? ? ?]. | ||
|  | 
 | ||
|  | If mode=QRA_AUTOP, K1JT decoder will attempt to decode also the msgs  | ||
|  | [K1JT IV3NWV] and [K1JT IV3NWV ?]. | ||
|  | 
 | ||
|  | In the case a decode is successful the return code of the qra64_decode function | ||
|  | indicates the amount of a-priori information required to decode the received  | ||
|  | message according to this table: | ||
|  | 
 | ||
|  |  rc=0    [?    ?    ?]		AP0 | ||
|  |  rc=1    [CQ   ?    ?]		AP27 | ||
|  |  rc=2    [CQ   ?     ]		AP42 | ||
|  |  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 | ||
|  |  rc=9    [CQ   CALL ?]		AP55 | ||
|  |  rc=10   [CQ   CALL  ]		AP70 | ||
|  |  rc=11   [CQ   CALL GRID]	AP70 | ||
|  | 
 | ||
|  | The return code is <0 when decoding is unsuccessful | ||
|  | 
 | ||
|  | This test simulates the situation ntx times and reports how many times | ||
|  | 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[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||
|  |   int nundet = 0; | ||
|  |   int ntx = 200,ndec=0; | ||
|  | 
 | ||
|  |   qra64codec *codec_iv3nwv = qra64_init(mode);   // codec for IV3NWV
 | ||
|  |   qra64codec *codec_k1jt   = qra64_init(mode);     // codec for K1JT
 | ||
|  | 
 | ||
|  |   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_CQ,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); | ||
|  | 
 | ||
|  | // This will enable K1JT's decoder to look for calls from IV3NWV [CQ IV3NWV ?/b] msgs
 | ||
|  |   printf("K1JT decoder enabled for [CQ   IV3NWV ?/b/JN66]\n"); | ||
|  |   qra64_apset(codec_k1jt, 0,CALL_IV3NWV,GRID_JN66,APTYPE_CQHISCALL); | ||
|  | 
 | ||
|  | 
 | ||
|  |   // Dx station IV3NWV calls
 | ||
|  |   printf("\nIV3NWV encoder sends msg: [K1JT IV3NWV JN66]\n\n"); | ||
|  |   encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_JN66); | ||
|  | 
 | ||
|  | //  printf("\nIV3NWV encoder sends msg: [CQ IV3NWV JN66]\n\n");
 | ||
|  | //  encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_JN66);
 | ||
|  | 
 | ||
|  | //  printf("\nIV3NWV encoder sends msg: [CQ IV3NWV]\n\n");
 | ||
|  | //  encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_BLANK);
 | ||
|  |   qra64_encode(codec_iv3nwv, y, x); | ||
|  | 
 | ||
|  |   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, &ebnodbest, xdec,rx); | ||
|  | 	if (rc>=0) { | ||
|  | 	  ebnodbavg +=ebnodbest; | ||
|  | 	  if (memcmp(xdec,x,12*sizeof(int))==0) | ||
|  | 		ndecok[rc]++; | ||
|  | 	  else | ||
|  | 	    nundet++; | ||
|  | 	} | ||
|  |   } | ||
|  |   printf("\n\n"); | ||
|  | 
 | ||
|  | 
 | ||
|  |   printf("Transimtted msgs:%d\nDecoded msgs:\n\n",ntx); | ||
|  |   for (k=0;k<12;k++) { | ||
|  |     printf("%3d with %s\n",ndecok[k],decode_type[k]); | ||
|  |     ndec += ndecok[k]; | ||
|  |   } | ||
|  |   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; | ||
|  | } | ||
|  | 
 | ||
|  | int test_fastfading(float EbNodB, float B90, int fadingModel, int submode, int apmode, int olddec, int channel_type, int ntx) | ||
|  | { | ||
|  |   int x[QRA64_K], xdec[QRA64_K]; | ||
|  |   int y[QRA64_N]; | ||
|  |   float *rx; | ||
|  |   float ebnodbest, ebnodbavg=0; | ||
|  |   int rc,k; | ||
|  |   float rxolddec[QRA64_N*QRA64_M];	// holds the energies at nominal tone freqs
 | ||
|  | 
 | ||
|  |   int ndecok[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; | ||
|  |   int nundet = 0; | ||
|  |   int ndec=0; | ||
|  | 
 | ||
|  |   qra64codec *codec_iv3nwv;  | ||
|  |   qra64codec *codec_k1jt;  | ||
|  | 
 | ||
|  |   codec_iv3nwv=qra64_init(QRA_NOAP);   | ||
|  |   codec_k1jt  =qra64_init(apmode);   | ||
|  | 
 | ||
|  |   if (channel_type==2) {	// fast-fading case
 | ||
|  | 	printf("Simulating the fast-fading channel\n"); | ||
|  | 	printf("B90=%.2f Hz - Fading Model=%s - Submode=QRA64%c\n",B90,fadingModel?"Lorentz":"Gauss",submode+'A'); | ||
|  | 	printf("Decoder metric = %s\n",olddec?"AWGN":"Matched to fast-fading signal"); | ||
|  | 	} | ||
|  |   else { | ||
|  |     printf("Simulating the %s channel\n",channel_type?"Rayleigh block fading":"AWGN"); | ||
|  | 	printf("Decoder metric = AWGN\n"); | ||
|  | 	} | ||
|  | 
 | ||
|  | 
 | ||
|  |   printf("\nEncoding msg [K1JT IV3NWV JN66]\n"); | ||
|  |   encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66); | ||
|  | // printf("[");
 | ||
|  | //  for (k=0;k<11;k++) printf("%02hX ",x[k]); printf("%02hX]\n",x[11]);
 | ||
|  | 
 | ||
|  |   qra64_encode(codec_iv3nwv, y, x); | ||
|  |   printf("%d transmissions will be simulated\n\n",ntx); | ||
|  | 
 | ||
|  |   if (apmode==QRA_USERAP) { | ||
|  | 	// This will enable K1JT's decoder to look for cq/qrz calls [CQ/QRZ ? ?/b]
 | ||
|  | 	printf("K1JT decoder enabled for [CQ    ?     ?/blank]\n"); | ||
|  | 	qra64_apset(codec_k1jt, CALL_K1JT,0,0,APTYPE_CQQRZ); | ||
|  | 
 | ||
|  | 	// 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 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 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 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); | ||
|  | 
 | ||
|  | 	// This will enable K1JT's decoder to look for calls from IV3NWV [CQ IV3NWV ?/b] msgs
 | ||
|  | 	printf("K1JT decoder enabled for [CQ   IV3NWV ?/b/JN66]\n"); | ||
|  | 	qra64_apset(codec_k1jt, 0,CALL_IV3NWV,GRID_JN66,APTYPE_CQHISCALL); | ||
|  | 
 | ||
|  |   } | ||
|  | 
 | ||
|  |   printf("\nNow decoding with K1JT's decoder...\n"); | ||
|  | /*
 | ||
|  |   if (channel_type==2) 	// simulate a fast-faded signal
 | ||
|  | 	  printf("Simulating a fast-fading channel with given B90 and spread type\n"); | ||
|  |   else | ||
|  | 	  printf("Simulating a %s channel\n",channel_type?"Rayleigh block fading":"AWGN"); | ||
|  | */ | ||
|  |   for (k=0;k<ntx;k++) { | ||
|  | 
 | ||
|  | 	  if ((k%10)==0) | ||
|  | 	    printf("  %5.1f %%\r",100.0*k/ntx); | ||
|  | //		printf(".");	// work in progress
 | ||
|  | 
 | ||
|  | 	if (channel_type==2) {	 | ||
|  | 		// generate a fast-faded signal
 | ||
|  | 		rc = qra64_fastfading_channel(&rx,y,submode,EbNodB,B90,fadingModel); | ||
|  | 		if (rc<0) { | ||
|  | 			printf("\nqra64_fastfading_channel error. rc=%d\n",rc); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	else // generate a awgn or Rayleigh block fading signal
 | ||
|  | 		rx = mfskchannel(y, channel_type, EbNodB); | ||
|  | 
 | ||
|  | 
 | ||
|  | 	if (channel_type==2)	// fast-fading case
 | ||
|  | 		if (olddec==1) { | ||
|  | 			int k, j; | ||
|  | 			int jj = 1<<submode; | ||
|  | 			int bps = QRA64_M*(2+jj); | ||
|  | 			float *rxbase; | ||
|  | 			float *out = rxolddec; | ||
|  | 			// calc energies at nominal freqs
 | ||
|  | 			for (k=0;k<QRA64_N;k++) { | ||
|  | 				rxbase = rx + QRA64_M + k*bps; | ||
|  | 				for (j=0;j<QRA64_M;j++) { | ||
|  | 					*out++=*rxbase; | ||
|  | 					rxbase+=jj; | ||
|  | 					} | ||
|  | 				} | ||
|  | 			// decode with awgn decoder
 | ||
|  | 			rc = qra64_decode(codec_k1jt,&ebnodbest,xdec,rxolddec); | ||
|  | 			} | ||
|  | 		else // use fast-fading decoder
 | ||
|  | 			rc = qra64_decode_fastfading(codec_k1jt,&ebnodbest,xdec,rx,submode,B90,fadingModel); | ||
|  | 	else // awgn or rayleigh channel. use the old decoder whatever the olddec option is
 | ||
|  | 		rc = qra64_decode(codec_k1jt,&ebnodbest,xdec,rx); | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | 	if (rc>=0) { | ||
|  | 	  ebnodbavg +=ebnodbest; | ||
|  | 	  if (memcmp(xdec,x,12*sizeof(int))==0) | ||
|  | 		ndecok[rc]++; | ||
|  | 	  else { | ||
|  | 		fprintf(stderr,"\nUndetected error with rc=%d\n",rc); | ||
|  | 		nundet++; | ||
|  | 		} | ||
|  | 	  } | ||
|  | 
 | ||
|  |   } | ||
|  |   printf("  %5.1f %%\r",100.0*k/ntx); | ||
|  | 
 | ||
|  |   printf("\n\n"); | ||
|  | 
 | ||
|  |   printf("Msgs transmitted:%d\nMsg decoded:\n\n",ntx); | ||
|  |   for (k=0;k<12;k++) { | ||
|  |     printf("rc=%2d   %3d with %s\n",k,ndecok[k],decode_type[k]); | ||
|  |     ndec += ndecok[k]; | ||
|  |   } | ||
|  |   printf("\nTotal: %d/%d (%d undetected errors)\n\n",ndec,ntx,nundet); | ||
|  |   printf(""); | ||
|  | 
 | ||
|  |   if (ndec>0) { | ||
|  | 	ebnodbavg/=(ndec+nundet); | ||
|  | 	printf("Estimated SNR (average in dB) = %.2f dB\n\n",ebnodbavg-QRA64_SNR_EBNO_OFFSET); | ||
|  | 	} | ||
|  | 
 | ||
|  |   return 0; | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | 
 | ||
|  | void syntax(void) | ||
|  | { | ||
|  | 
 | ||
|  |   printf("\nQRA64 Mode Tests\n"); | ||
|  |   printf("2016, Nico Palermo - IV3NWV\n\n"); | ||
|  |   printf("---------------------------\n\n"); | ||
|  |   printf("Syntax: qra64 [-s<snrdb>] [-c<channel>] [-a<ap-type>] [-t<testtype>] [-h]\n"); | ||
|  |   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 2=Fast-fading\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("                     2=simulate fast-fading/awgn/rayliegh decoders performance\n"); | ||
|  |   printf("       -n<ntx>     : simulate the transmission of ntx codewords (default=100)\n"); | ||
|  | 
 | ||
|  |   printf("Options used only for fast-fading simulations (-c2):\n"); | ||
|  |   printf("       -b          : 90%% fading bandwidth in Hz [1..230 Hz] (default = 2.5 Hz)\n"); | ||
|  |   printf("       -m          : fading model. 0=Gauss, 1=Lorentz (default = Lorentz)\n"); | ||
|  |   printf("       -q          : qra64 submode. 0=QRA64A,... 4=QRA64E (default = QRA64A)\n"); | ||
|  |   printf("       -d          : use the old awgn decoder\n"); | ||
|  |   printf("       -h: this help\n"); | ||
|  |   printf("Example:\n"); | ||
|  |   printf("        qra64 -t2 -c2 -a2 -b50 -m1 -q2 -n10000 -s-26\n"); | ||
|  |   printf("        runs the error performance test (-t2)\n"); | ||
|  |   printf("        with USER_AP (-a2)\n"); | ||
|  |   printf("        simulating a fast fading channel (-c2)\n"); | ||
|  |   printf("        with B90 = 50 Hz (-b50), Lorentz Doppler (-m1), mode QRA64C (-q2)\n"); | ||
|  |   printf("        ntx = 10000 codewords (-n10000) and SNR = -26 dB (-s-26)\n"); | ||
|  | 
 | ||
|  | } | ||
|  | 
 | ||
|  | int main(int argc, char* argv[]) | ||
|  | { | ||
|  |   int k, rc, nok=0; | ||
|  |   float SNRdB = -27.5f; | ||
|  |   unsigned int channel = CHANNEL_AWGN; | ||
|  |   unsigned int mode    = QRA_AUTOAP; | ||
|  |   unsigned int testtype=0; | ||
|  |   int   nqso = 100; | ||
|  |   float EbNodB; | ||
|  |   float B90 = 2.5; | ||
|  |   int fadingModel = 1; | ||
|  |   int submode = 0; | ||
|  |   int olddec = 0; | ||
|  |   int ntx = 100; | ||
|  | 
 | ||
|  | // Parse the command line
 | ||
|  |   while(--argc) { | ||
|  |     argv++; | ||
|  | 
 | ||
|  |     if (strncmp(*argv,"-h",2)==0) { | ||
|  |       syntax(); | ||
|  |       return 0; | ||
|  | 	  }  | ||
|  | 	else  | ||
|  | 	if (strncmp(*argv,"-n",2)==0) { | ||
|  | 		ntx = ( int)atoi((*argv)+2); | ||
|  | 		if (ntx<100 || ntx>1000000) { | ||
|  | 			printf("Invalid -n option. ntx must be in the range [100..1000000]\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		}  | ||
|  | 	else | ||
|  | 	if (strncmp(*argv,"-a",2)==0) { | ||
|  | 		mode = ( int)atoi((*argv)+2); | ||
|  | 		if (mode>2) { | ||
|  | 			printf("Invalid decoding mode\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		}  | ||
|  | 	else | ||
|  | 	if (strncmp(*argv,"-s",2)==0) { | ||
|  | 		SNRdB = (float)atof((*argv)+2); | ||
|  | 		if (SNRdB>20 || SNRdB<-50) { | ||
|  | 			printf("SNR should be in the range [-50..20]\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		}  | ||
|  | 	else  | ||
|  | 	if (strncmp(*argv,"-t",2)==0) { | ||
|  | 	    testtype = ( int)atoi((*argv)+2); | ||
|  | 	    if (testtype>2) { | ||
|  | 			printf("Invalid test type\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	else  | ||
|  | 	if (strncmp(*argv,"-c",2)==0) { | ||
|  | 		channel = ( int)atoi((*argv)+2); | ||
|  | 	    if (channel>CHANNEL_FASTFADE) { | ||
|  | 			printf("Invalid channel type\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 	    }  | ||
|  | 	else | ||
|  | 	if (strncmp(*argv,"-b",2)==0) { | ||
|  | 	    B90 = (float)atof((*argv)+2); | ||
|  | 	    if (B90<1 || B90>230) { | ||
|  | 			printf("Invalid B90\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	else | ||
|  | 	if (strncmp(*argv,"-m",2)==0) { | ||
|  | 	    fadingModel = (int)atoi((*argv)+2); | ||
|  | 	    if (fadingModel<0 || fadingModel>1) { | ||
|  | 			printf("Invalid fading model\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	else | ||
|  | 	if (strncmp(*argv,"-q",2)==0) { | ||
|  | 	    submode = (int)atoi((*argv)+2); | ||
|  | 	    if (submode<0 || submode>4) { | ||
|  | 			printf("Invalid submode\n"); | ||
|  | 			syntax(); | ||
|  | 			return -1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	else | ||
|  | 	if (strncmp(*argv,"-d",2)==0) { | ||
|  | 		olddec = 1; | ||
|  | 		} | ||
|  | 	else { | ||
|  | 	    printf("Invalid option\n"); | ||
|  | 	    syntax(); | ||
|  | 	    return -1; | ||
|  | 	    } | ||
|  | 	} | ||
|  | 
 | ||
|  |   if (testtype<2) // old tests
 | ||
|  | 	  if (channel==CHANNEL_FASTFADE) { | ||
|  | 		printf("Invalid Option. Test type 0 and 1 supports only AWGN or Rayleigh Channel model\n"); | ||
|  | 		return -1; | ||
|  | 		} | ||
|  |    | ||
|  |   EbNodB = SNRdB+QRA64_SNR_EBNO_OFFSET;	 | ||
|  |    | ||
|  | #if defined(__linux__) || defined(__unix__)
 | ||
|  |   srand48(GetTickCount()); | ||
|  | #endif
 | ||
|  | 
 | ||
|  |   if (testtype==0) { | ||
|  | 	for (k=0;k<nqso;k++) { | ||
|  | 		printf("\n\n------------------------\n"); | ||
|  | 		rc = test_proc_1(channel, EbNodB, mode); | ||
|  | 		if (rc==0) | ||
|  | 			nok++; | ||
|  | 		} | ||
|  | 	printf("\n\n%d/%d QSOs to end without repetitions\n",nok,nqso); | ||
|  | 	printf("Input SNR = %.1fdB channel=%s ap-mode=%s\n\n", | ||
|  | 		SNRdB, | ||
|  | 		channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH", | ||
|  | 		apmode_type[mode] | ||
|  | 		); | ||
|  | 	}  | ||
|  |   else if (testtype==1) { | ||
|  | 	test_proc_2(channel, EbNodB, mode); | ||
|  | 	printf("Input SNR = %.1fdB channel=%s ap-mode=%s\n\n", | ||
|  | 		SNRdB, | ||
|  | 		channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH", | ||
|  | 		apmode_type[mode] | ||
|  | 		); | ||
|  | 	} | ||
|  |   else { | ||
|  | 	printf("Input SNR = %.1fdB ap-mode=%s\n\n", | ||
|  | 	 SNRdB, | ||
|  | 	 apmode_type[mode] | ||
|  | 	 ); | ||
|  | 	test_fastfading(EbNodB,B90,fadingModel,submode,mode,olddec, channel, ntx); | ||
|  |   } | ||
|  |   return 0; | ||
|  | } |