Some cleanup and reformatting of QRA65 code (with apologies to Nico).

I've tried to make code "pretty print" and easier to follow, at least for me.


git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6808 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Joe Taylor 2016-06-24 19:54:34 +00:00
parent db820683ff
commit dd2e5fbdda
5 changed files with 615 additions and 582 deletions

View File

@ -1,53 +1,57 @@
// main.c /*
// QRA65 mode encode/decode test main.c
// QRA65 mode encode/decode test
// (c) 2016 - Nico Palermo, IV3NWV
//
// Thanks to Andrea Montefusco IW0HDV for his help on adapting the sources
// to OSs other than MS Windows
//
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
// Files in this package:
// main.c - this file
// qra65.c/.h - qra65 mode encode/decoding functions
//
// ../qracodes/normrnd.{c,h} - random gaussian number generator
// ../qracodes/npfwht.{c,h} - Fast Walsh-Hadamard Transforms
// ../qracodes/pdmath.{c,h} - Elementary math on probability distributions
// ../qracodes/qra12_63_64_irr_b.{c,h} - Tables for a QRA(12,63) irregular RA code over GF(64)
// ../qracodes/qra13_64_64_irr_e.{c,h} - Tables for a QRA(13,64) irregular RA code " "
// ../qracodes/qracodes.{c,h} - QRA codes encoding/decoding functions
//
// -------------------------------------------------------------------------------
//
// qracodes is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// qracodes is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License (c) 2016 - Nico Palermo, IV3NWV
// along with qracodes source distribution.
// If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------- Thanks to Andrea Montefusco IW0HDV for his help on adapting the sources
to OSs other than MS Windows
// The code used by the QRA65 mode is the code: ------------------------------------------------------------------------------
// QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.{h,c}) This file is part of the qracodes project, a Forward Error Control
// This code has been designed to include a CRC as the 13th information symbol encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
// 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.
// ------------------------------------------------------------------------------ Files in this package:
main.c - this file
qra65.c/.h - qra65 mode encode/decoding functions
// OS dependent defines and includes -------------------------------------------- ../qracodes/normrnd.{c,h} - random gaussian number generator
../qracodes/npfwht.{c,h} - Fast Walsh-Hadamard Transforms
../qracodes/pdmath.{c,h} - Elementary math on probability distributions
../qracodes/qra12_63_64_irr_b.{c,h} - Tables for a QRA(12,63) irregular RA
code over GF(64)
../qracodes/qra13_64_64_irr_e.{c,h} - Tables for a QRA(13,64) irregular RA
code over GF(64)
../qracodes/qracodes.{c,h} - QRA codes encoding/decoding functions
-------------------------------------------------------------------------------
qracodes is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qracodes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with qracodes source distribution.
If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------------
The code used by the QRA65 mode is the code: QRA13_64_64_IRR_E: K=13
N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.{h,c}).
This code has been designed to include a CRC as the 13th information
symbol and improve the code UER (Undetected Error Rate). The CRC
symbol is not sent along the channel (the codes are punctured) and the
resulting code is still a (12,63) code with an effective code rate of
R = 12/63.
*/
// OS dependent defines and includes ------------------------------------------
#if _WIN32 // note the underscore: without it, it's not msdn official! #if _WIN32 // note the underscore: without it, it's not msdn official!
// Windows (x64 and x86) // Windows (x64 and x86)
@ -79,7 +83,7 @@ unsigned GetTickCount(void) {
#include "qra65.h" #include "qra65.h"
#include "../qracodes/normrnd.h" // gaussian numbers generator #include "../qracodes/normrnd.h" // gaussian numbers generator
// ----------------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// channel types // channel types
#define CHANNEL_AWGN 0 #define CHANNEL_AWGN 0
@ -112,15 +116,18 @@ static float r[NSAMPLES];
float *mfskchannel(int *x, int channel_type, float EbNodB) float *mfskchannel(int *x, int channel_type, float EbNodB)
{ {
// simulate a MFSK channel (AWGN or Rayleigh) /*
// x is a pointer to the transmitted codeword (an array of QRA65_N integers in the range 0..63) Simulate an MFSK channel, either AWGN or Rayleigh.
//
// the function returns the received symbol energies (squared amplitudes)
// as an array of (QRA65_M*QRA65_N) float numbers
// the first QRA65_M entries of this array are the energies of the first symbol in the codeword
// the second QRA65_M entries are those of the second symbol
// and so on up to the last codeword symbol
x is a pointer to the transmitted codeword, an array of QRA65_N
integers in the range 0..63.
Returns the received symbol energies (squared amplitudes) as an array of
(QRA65_M*QRA65_N) floats. The first QRA65_M entries of this array are
the energies of the first symbol in the codeword. The second QRA65_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 No = 1.0f; // noise spectral density
const float sigma = (float)sqrt(No/2.0f); // std dev of noise I/Q components 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 sigmach = (float)sqrt(1/2.0f); // std dev of channel I/Q gains
@ -156,7 +163,6 @@ float *mfskchannel(int *x, int channel_type, float EbNodB)
r[k] = rp[k]*rp[k] + rq[k]*rq[k]; r[k] = rp[k]*rp[k] + rq[k]*rq[k];
return r; return r;
} }
// These defines are some packed fields as computed by JT65 // These defines are some packed fields as computed by JT65
@ -168,94 +174,106 @@ float *mfskchannel(int *x, int channel_type, float EbNodB)
char decode_type[6][32] = { char decode_type[6][32] = {
"[? ? ?] AP0", "[? ? ?] AP0",
"[CQ ? ?] AP27", "[CQ ? ?] AP27",
"[CQ ? ] AP44", "[CQ ? ] AP42",
"[CALL ? ?] AP29", "[CALL ? ?] AP29",
"[CALL ? ] AP45", "[CALL ? ] AP44",
"[CALL CALL ?] AP57" "[CALL CALL ?] AP57"
}; };
int test_proc_1(int channel_type, float EbNodB, int mode) int test_proc_1(int channel_type, float EbNodB, int mode)
{ {
// Here we simulate the following (dummy) QSO: /*
// 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.
// I.e. at 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
// At 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 ?].
// At 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)
// At 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. 1) CQ IV3NWV
// In this case it also reports the type of decode (see table decode_type above) 2) IV3NWV K1JT
3) K1JT IV3NWV 73
4) IV3NWV K1JT 73
// When mode=QRA_NOAP, only [? ? ?] decodes are attempted and no a-priori information No message repetition is attempted
// is used by the decoder
// The function returns 0 if all of the four messages have been decoded by their recipients The QSO is counted as successfull if IV3NWV received the last message
// (with no retries) and -1 if any of them could not be decoded 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[QRA65_K], xdec[QRA65_K]; int x[QRA65_K], xdec[QRA65_K];
int y[QRA65_N]; int y[QRA65_N];
float *rx; float *rx;
int rc; int rc;
// each simulated station must use its own codec // Each simulated station must use its own codec, since it might work with
// as it might work with different a-priori information // different a-priori information.
qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT
// Step 1a: IV3NWV makes a CQ call (with no grid)
// IV3NWV makes a CQ call (with no grid)
printf("IV3NWV tx: CQ IV3NWV\n"); printf("IV3NWV tx: CQ IV3NWV\n");
encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_BLANK); encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_BLANK);
qra65_encode(codec_iv3nwv, y, x); qra65_encode(codec_iv3nwv, y, x);
rx = mfskchannel(y,channel_type,EbNodB); rx = mfskchannel(y,channel_type,EbNodB);
// K1JT attempts to decode // Step 1b: K1JT attempts to decode [? ? ?], [CQ/QRZ ? ?] or [CQ/QRZ ?]
rc = qra65_decode(codec_k1jt, xdec,rx); rc = qra65_decode(codec_k1jt, xdec,rx);
if (rc>=0) { // decoded if (rc>=0) { // decoded
printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// K1JT replies to IV3NWV (with no grid)
// Step 2a: K1JT replies to IV3NWV (with no grid)
printf("K1JT tx: IV3NWV K1JT\n"); printf("K1JT tx: IV3NWV K1JT\n");
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_BLANK); encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_BLANK);
qra65_encode(codec_k1jt, y, x); qra65_encode(codec_k1jt, y, x);
rx = mfskchannel(y,channel_type,EbNodB); rx = mfskchannel(y,channel_type,EbNodB);
// IV3NWV attempts to decode // Step 2b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?] or [IV3NWV ?]
rc = qra65_decode(codec_iv3nwv, xdec,rx); rc = qra65_decode(codec_iv3nwv, xdec,rx);
if (rc>=0) { // decoded if (rc>=0) { // decoded
printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// IV3NWV replies to K1JT with a 73
// Step 3a: IV3NWV replies to K1JT with a 73
printf("IV3NWV tx: K1JT IV3NWV 73\n"); printf("IV3NWV tx: K1JT IV3NWV 73\n");
encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV, GRID_73); encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV, GRID_73);
qra65_encode(codec_iv3nwv, y, x); qra65_encode(codec_iv3nwv, y, x);
rx = mfskchannel(y,channel_type,EbNodB); rx = mfskchannel(y,channel_type,EbNodB);
// K1JT attempts to decode // Step 3b: K1JT attempts to decode [? ? ?] or [K1JT IV3NWV ?]
rc = qra65_decode(codec_k1jt, xdec,rx); rc = qra65_decode(codec_k1jt, xdec,rx);
if (rc>=0) { // decoded if (rc>=0) { // decoded
printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// K1JT replies to IV3NWV with a 73
// Step 4a: K1JT replies to IV3NWV with a 73
printf("K1JT tx: IV3NWV K1JT 73\n"); printf("K1JT tx: IV3NWV K1JT 73\n");
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_73); encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_73);
qra65_encode(codec_k1jt, y, x); qra65_encode(codec_k1jt, y, x);
rx = mfskchannel(y,channel_type,EbNodB); rx = mfskchannel(y,channel_type,EbNodB);
// IV3NWV attempts to decode // Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?]
rc = qra65_decode(codec_iv3nwv, xdec,rx); rc = qra65_decode(codec_iv3nwv, xdec,rx);
if (rc>=0) { // decoded if (rc>=0) { // decoded
printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]); printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
@ -270,26 +288,31 @@ int test_proc_1(int channel_type, float EbNodB, int mode)
int test_proc_2(int channel_type, float EbNodB, int mode) int test_proc_2(int channel_type, float EbNodB, int mode)
{ {
/*
Here we simulate the decoder of K1JT after K1JT has sent a msg [IV3NWV K1JT]
and IV3NWV sends him the msg [K1JT IV3NWV JN66].
// Here we simulate the decoder of K1JT after K1JT has sent a msg [IV3NWV K1JT] If mode=QRA_NOAP, K1JT decoder attempts to decode only msgs of type [? ? ?].
// 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 qra65_decode function If mode=QRA_AUTOP, K1JT decoder will attempt to decode also the msgs
// indicates the amount of a-priori information required to decode the received message [K1JT IV3NWV] and [K1JT IV3NWV ?].
// accordingly to this table:
// rc=0 [? ? ?] AP0
// rc=1 [CQ ? ?] AP27
// rc=2 [CQ ? ] AP44
// rc=3 [CALL ? ?] AP29
// rc=4 [CALL ? ] AP45
// rc=5 [CALL CALL ?] AP57
// The return code is <0 when decoding is unsuccessful
// This test simulates the situation ntx times and reports how many times In the case a decode is successful the return code of the qra65_decode function
// a particular type decode among the above 6 cases succeded. 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
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[QRA65_K], xdec[QRA65_K]; int x[QRA65_K], xdec[QRA65_K];
int y[QRA65_N]; int y[QRA65_N];
@ -302,12 +325,12 @@ int test_proc_2(int channel_type, float EbNodB, int mode)
qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT
// this will enable k1jt's decoder to look for iv3nwv calls // This will enable K1JT's decoder to look for IV3NWV calls
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK); encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK);
qra65_encode(codec_k1jt, y, x); qra65_encode(codec_k1jt, y, x);
printf("K1JT tx: IV3NWV K1JT\n"); printf("K1JT tx: IV3NWV K1JT\n");
// iv3nwv reply to k1jt // IV3NWV reply to K1JT
printf("IV3NWV tx: K1JT IV3NWV JN66\n"); printf("IV3NWV tx: K1JT IV3NWV JN66\n");
encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66); encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66);
qra65_encode(codec_iv3nwv, y, x); qra65_encode(codec_iv3nwv, y, x);
@ -352,23 +375,20 @@ void syntax(void)
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
int k, rc, nok=0; int k, rc, nok=0;
float SNRdB = -27.5f; float SNRdB = -27.5f;
unsigned int channel = CHANNEL_AWGN; unsigned int channel = CHANNEL_AWGN;
unsigned int mode = QRA_AUTOAP; unsigned int mode = QRA_AUTOAP;
unsigned int testtype=0; unsigned int testtype=0;
int nqso = 100; int nqso = 100;
float EbNodB; float EbNodB;
// parse command line // Parse the command line
while(--argc) { while(--argc) {
argv++; argv++;
if (strncmp(*argv,"-h",2)==0) { if (strncmp(*argv,"-h",2)==0) {
syntax(); syntax();
return 0; return 0;
} } else {
else
if (strncmp(*argv,"-a",2)==0) { if (strncmp(*argv,"-a",2)==0) {
mode = ( int)atoi((*argv)+2); mode = ( int)atoi((*argv)+2);
if (mode>1) { if (mode>1) {
@ -376,8 +396,7 @@ int main(int argc, char* argv[])
syntax(); syntax();
return -1; return -1;
} }
} } else {
else
if (strncmp(*argv,"-s",2)==0) { if (strncmp(*argv,"-s",2)==0) {
SNRdB = (float)atof((*argv)+2); SNRdB = (float)atof((*argv)+2);
if (SNRdB>0 || SNRdB<-40) { if (SNRdB>0 || SNRdB<-40) {
@ -385,8 +404,7 @@ int main(int argc, char* argv[])
syntax(); syntax();
return -1; return -1;
} }
} } else {
else
if (strncmp(*argv,"-t",2)==0) { if (strncmp(*argv,"-t",2)==0) {
testtype = ( int)atoi((*argv)+2); testtype = ( int)atoi((*argv)+2);
if (testtype>1) { if (testtype>1) {
@ -394,8 +412,7 @@ int main(int argc, char* argv[])
syntax(); syntax();
return -1; return -1;
} }
} } else {
else
if (strncmp(*argv,"-c",2)==0) { if (strncmp(*argv,"-c",2)==0) {
channel = ( int)atoi((*argv)+2); channel = ( int)atoi((*argv)+2);
if (channel>CHANNEL_RAYLEIGH) { if (channel>CHANNEL_RAYLEIGH) {
@ -403,13 +420,16 @@ int main(int argc, char* argv[])
syntax(); syntax();
return -1; return -1;
} }
} } else {
else {
printf("Invalid option\n"); printf("Invalid option\n");
syntax(); syntax();
return -1; return -1;
} }
} }
}
}
}
}
EbNodB = SNRdB+29.1f; EbNodB = SNRdB+29.1f;
@ -425,17 +445,14 @@ int main(int argc, char* argv[])
nok++; nok++;
} }
printf("\n\n%d/%d QSOs to end without repetitions\n",nok,nqso); printf("\n\n%d/%d QSOs to end without repetitions\n",nok,nqso);
} } else {
else
test_proc_2(channel, EbNodB, mode); test_proc_2(channel, EbNodB, mode);
}
printf("SNR = %.1fdB channel=%s ap-mode=%s\n\n", printf("SNR = %.1fdB channel=%s ap-mode=%s\n\n",
SNRdB, SNRdB,
channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH", channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH",
mode==QRA_NOAP?"NO_AP":"AUTO_AP" mode==QRA_NOAP?"NO_AP":"AUTO_AP"
); );
return 0; return 0;
} }

View File

@ -1,35 +1,37 @@
// qra65.c /*
// Encoding/decoding functions for the QRA65 mode qra65.c
// Encoding/decoding functions for the QRA65 mode
// (c) 2016 - Nico Palermo, IV3NWV
//
// -------------------------------------------------------------------------------
//
// qracodes is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// qracodes is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License (c) 2016 - Nico Palermo, IV3NWV
// along with qracodes source distribution.
// If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------- -------------------------------------------------------------------------------
// Code used in this sowftware release: qracodes is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
qracodes is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
// QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_e.h /.c) You should have received a copy of the GNU General Public License
along with qracodes source distribution.
If not, see <http://www.gnu.org/licenses/>.
// Codes with K=13 are designed to include a CRC as the 13th information symbol -----------------------------------------------------------------------------
// and improve the code UER (Undetected Error Rate).
// The CRC symbol is not sent along the channel (the codes are punctured) and the
// resulting code is a (12,63) code
// ------------------------------------------------------------------------------ Code used in this sowftware release:
QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in
qra13_64_64_irr_e.h /.c)
Codes with K=13 are designed to include a CRC as the 13th information symbol
and improve the code UER (Undetected Error Rate).
The CRC symbol is not sent along the channel (the codes are punctured) and the
resulting code is a (12,63) code
*/
//----------------------------------------------------------------------------
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
@ -41,28 +43,26 @@
// Code parameters of the QRA65 mode // Code parameters of the QRA65 mode
#define QRA65_CODE qra_13_64_64_irr_e #define QRA65_CODE qra_13_64_64_irr_e
#define QRA65_NMSG 218 // this must much the value indicated in QRA65_CODE.NMSG #define QRA65_NMSG 218 // Must much value indicated in QRA65_CODE.NMSG
#define QRA65_KC (QRA65_K+1) // information symbols crc included (as defined in the code) #define QRA65_KC (QRA65_K+1) // Information symbols (crc included)
#define QRA65_NC (QRA65_N+1) // codeword length (as defined in the code) #define QRA65_NC (QRA65_N+1) // Codeword length (as defined in the code)
#define QRA65_NITER 100 // max number of iterations per decode #define QRA65_NITER 100 // max number of iterations per decode
// static functions declarations ----------------------------------------------
// static functions declarations -------------------------------------------------
static int calc_crc6(const int *x, int sz); static int calc_crc6(const int *x, int sz);
static void ix_mask(float *dst, const float *src, const int *mask, const int *x); static void ix_mask(float *dst, const float *src, const int *mask,
static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const int *ap_x); const int *x);
static int qra65_do_decode(int *x, const float *pix, const int *ap_mask,
// a-priori information masks for fields in jt65-like msgs ----------------------- const int *ap_x);
// a-priori information masks for fields in JT65-like msgs --------------------
#define MASK_CQQRZ 0xFFFFFFC // CQ/QRZ calls common bits #define MASK_CQQRZ 0xFFFFFFC // CQ/QRZ calls common bits
#define MASK_CALL1 0xFFFFFFF #define MASK_CALL1 0xFFFFFFF
#define MASK_CALL2 0xFFFFFFF #define MASK_CALL2 0xFFFFFFF
#define MASK_GRIDFULL 0xFFFF #define MASK_GRIDFULL 0xFFFF
#define MASK_GRIDBIT 0x8000 // b[15] is 0 for all the messages which are not text #define MASK_GRIDBIT 0x8000 // b[15] is 1 for free text, 0 otherwise
// ----------------------------------------------------------------------------
// -------------------------------------------------------------------------------
qra65codec *qra65_init(int flags, const int mycall) qra65codec *qra65_init(int flags, const int mycall)
{ {
@ -93,17 +93,17 @@ qra65codec *qra65_init(int flags, const int mycall)
// 'CQ nnn', 'CQ DX' and 'DE' msgs // 'CQ nnn', 'CQ DX' and 'DE' msgs
// will be handled by the decoder as messages with no a-priori knowledge // 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->apmsg_cqqrz, CALL_CQ, 0, GRID_BLANK);
encodemsg_jt65(pcodec->apmask_cqqrz, MASK_CQQRZ,0, MASK_GRIDBIT); // AP27 (26+1) encodemsg_jt65(pcodec->apmask_cqqrz, MASK_CQQRZ,0, MASK_GRIDBIT); // AP27
encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL); // AP42 (26+16) encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL);// AP42
// encode [mycall ? x] messages and set masks // encode [mycall ? x] messages and set masks
encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK); encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK);
encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); // AP29 (28+1) encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); // AP29
encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1, 0, MASK_GRIDFULL); // AP44 (28+16) encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1,0, MASK_GRIDFULL);// AP44
// set mask for [mycall srccall ?] messages // set mask for [mycall srccall ?] messages
encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1, MASK_CALL2, MASK_GRIDBIT); // AP56 (28+28) encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1,MASK_CALL2,
MASK_GRIDBIT); // AP56
return pcodec; return pcodec;
} }
@ -114,14 +114,13 @@ void qra65_encode(qra65codec *pcodec, int *y, const int *x)
int call1,call2,grid; int call1,call2,grid;
memcpy(encx,x,QRA65_K*sizeof(int)); // copy input to the encoder buffer memcpy(encx,x,QRA65_K*sizeof(int)); // Copy input to encoder buffer
encx[QRA65_K]=calc_crc6(encx,QRA65_K); // compute and add the crc symbol encx[QRA65_K]=calc_crc6(encx,QRA65_K); // Compute and add crc symbol
qra_encode(&QRA65_CODE, ency, encx); // encode msg+crc using given QRA code
qra_encode(&QRA65_CODE, ency, encx); // encode msg+crc using the given QRA code
// copy codeword to output puncturing the crc symbol // copy codeword to output puncturing the crc symbol
memcpy(y,ency,QRA65_K*sizeof(int)); // copy the information symbols memcpy(y,ency,QRA65_K*sizeof(int)); // copy information symbols
memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy the parity check symbols memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy parity symbols
if (pcodec->apflags!=QRA_AUTOAP) if (pcodec->apflags!=QRA_AUTOAP)
return; return;
@ -130,89 +129,95 @@ void qra65_encode(qra65codec *pcodec, int *y, const int *x)
if ((x[9]&0x80)==1) if ((x[9]&0x80)==1)
return; // no, it's a text message return; // no, it's a text message
// it's a [call1 call2 grid] message // It's a [call1 call2 grid] message
// we assume that call2 is our call (but we don't check it) // 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..) // call1 the station callsign we are calling or indicates a general call (CQ/QRZ/etc..)
decodemsg_jt65(&call1,&call2,&grid,x); decodemsg_jt65(&call1,&call2,&grid,x);
if ((call1>=CALL_CQ && call1<=CALL_CQ999) || call1==CALL_CQDX || call1==CALL_DE) { if ((call1>=CALL_CQ && call1<=CALL_CQ999) || call1==CALL_CQDX ||
// we are making a general call, so we still don't know who can reply us (srccall) call1==CALL_DE) {
// reset apsrccall to 0 so that the decoder won't look for [mycall srccall ?] msgs // 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; pcodec->apsrccall = 0;
} } else {
else { // We are replying to someone named call1
// we are replying someone named call1 // Set apmsg_call1_call2 so decoder will try for [mycall call1 ?] msgs
// set apmsg_call1_call2 so that the decoder will attempt to decode [mycall call1 ?] msgs
pcodec->apsrccall = call1; pcodec->apsrccall = call1;
encodemsg_jt65(pcodec->apmsg_call1_call2, pcodec->apmycall, pcodec->apsrccall, 0); encodemsg_jt65(pcodec->apmsg_call1_call2, pcodec->apmycall,
pcodec->apsrccall, 0);
} }
} }
int qra65_decode(qra65codec *pcodec, int *x, const float *rxen) int qra65_decode(qra65codec *pcodec, int *x, const float *rxen)
{ {
int k; int k;
float *srctmp, *dsttmp; float *srctmp, *dsttmp;
float ix[QRA65_NC*QRA65_M]; // (depunctured) intrisic information to the decoder float ix[QRA65_NC*QRA65_M]; // (depunctured) intrisic information
int rc; int rc;
// sanity check if (QRA65_NMSG!=QRA65_CODE.NMSG) // sanity check
if (QRA65_NMSG!=QRA65_CODE.NMSG)
return -16; // QRA65_NMSG define is wrong return -16; // QRA65_NMSG define is wrong
// compute symbols intrinsic probabilities from received energy observations // compute symbols intrinsic probabilities from received energy observations
qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric); qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric);
// de-puncture observations adding a uniform distribution for the crc symbol ---------- // de-puncture observations adding a uniform distribution for the crc symbol
// move check symbols distributions one symbol towards the end // move check symbols distributions one symbol towards the end
dsttmp = PD_ROWADDR(ix,QRA65_M, QRA65_NC-1); // point to the last symbol prob dist dsttmp = PD_ROWADDR(ix,QRA65_M, QRA65_NC-1); //Point to last symbol prob dist
srctmp = dsttmp-QRA65_M; // source is the previous pd srctmp = dsttmp-QRA65_M; // source is the previous pd
for (k=0;k<QRA65_C;k++) { for (k=0;k<QRA65_C;k++) {
pd_init(dsttmp,srctmp,QRA65_M); pd_init(dsttmp,srctmp,QRA65_M);
dsttmp -=QRA65_M; dsttmp -=QRA65_M;
srctmp -=QRA65_M; srctmp -=QRA65_M;
} }
// init crc prob to a uniform distribution // Initialize crc prob to a uniform distribution
pd_init(dsttmp,pd_uniform(QRA65_m),QRA65_M); pd_init(dsttmp,pd_uniform(QRA65_m),QRA65_M);
// attempt to decode without a-priori -------------------------------------------------- // Attempt to decode without a-priori info --------------------------------
rc = qra65_do_decode(x, ix, NULL, NULL); rc = qra65_do_decode(x, ix, NULL, NULL);
if (rc>=0) return 0; // successfull decode with 0 ap if (rc>=0) return 0; // successfull decode with AP0
if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode
// attempt to decode CQ calls // Attempt to decode CQ calls
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // 27 bit AP rc = qra65_do_decode(x,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // AP27
if (rc>=0) return 1; // decoded [cq/qrz ? ?] if (rc>=0) return 1; // decoded [cq/qrz ? ?]
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo, pcodec->apmsg_cqqrz); // 44 bit AP
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo,
pcodec->apmsg_cqqrz); // AP42
if (rc>=0) return 2; // decoded [cq ? ooo] if (rc>=0) return 2; // decoded [cq ? ooo]
// attempt to decode calls directed to us (mycall) // attempt to decode calls directed to us (mycall)
rc = qra65_do_decode(x, ix, pcodec->apmask_call1, pcodec->apmsg_call1); // 29 bit AP rc = qra65_do_decode(x, ix, pcodec->apmask_call1,
pcodec->apmsg_call1); // AP29
if (rc>=0) return 3; // decoded [mycall ? ?] if (rc>=0) return 3; // decoded [mycall ? ?]
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo, pcodec->apmsg_call1); // 45 bit AP
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo,
pcodec->apmsg_call1); // AP44
if (rc>=0) return 4; // decoded [mycall ? ooo] if (rc>=0) return 4; // decoded [mycall ? ooo]
// if apsrccall is set attempt to decode [mycall srccall ?] msgs // if apsrccall is set attempt to decode [mycall srccall ?] msgs
if (pcodec->apsrccall==0) return rc; // nothing more to do if (pcodec->apsrccall==0) return rc; // nothing more to do
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_call2, pcodec->apmsg_call1_call2); // 57 bit AP rc = qra65_do_decode(x, ix, pcodec->apmask_call1_call2,
pcodec->apmsg_call1_call2); // AP57
if (rc>=0) return 5; // decoded [mycall srccall ?] if (rc>=0) return 5; // decoded [mycall srccall ?]
return rc; return rc;
} }
// static functions definitions ---------------------------------------------------------------- // Static functions definitions ----------------------------------------------
// decode with given a-priori information // Decode with given a-priori information
static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const int *ap_x) static int qra65_do_decode(int *x, const float *pix, const int *ap_mask,
const int *ap_x)
{ {
int rc; int rc;
const float *ixsrc; const float *ixsrc;
float ix_masked[QRA65_NC*QRA65_M]; // (masked) intrinsic information to the decoder float ix_masked[QRA65_NC*QRA65_M]; // Masked intrinsic information
float ex[QRA65_NC*QRA65_M]; // extrinsic information from the decoder float ex[QRA65_NC*QRA65_M]; // Extrinsic information from the decoder
float v2cmsg[QRA65_NMSG*QRA65_M]; // buffers for the decoder messages float v2cmsg[QRA65_NMSG*QRA65_M]; // buffers for the decoder messages
float c2vmsg[QRA65_NMSG*QRA65_M]; float c2vmsg[QRA65_NMSG*QRA65_M];
@ -220,8 +225,8 @@ static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const i
if (ap_mask==NULL) { // no a-priori information if (ap_mask==NULL) { // no a-priori information
ixsrc = pix; // intrinsic source is what passed as argument ixsrc = pix; // intrinsic source is what passed as argument
} } else {
else { // a-priori information provided // a-priori information provided
// mask channel observations with a-priori // mask channel observations with a-priori
ix_mask(ix_masked,pix,ap_mask,ap_x); ix_mask(ix_masked,pix,ap_mask,ap_x);
ixsrc = ix_masked; // intrinsic source is the masked version ixsrc = ix_masked; // intrinsic source is the masked version
@ -243,16 +248,15 @@ static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const i
memcpy(x,xdec,QRA65_K*sizeof(int)); memcpy(x,xdec,QRA65_K*sizeof(int));
return 0; return 0;
} }
// crc functions ------------------------------------------------------------------------------- // crc functions --------------------------------------------------------------
// crc-6 generator polynomial // crc-6 generator polynomial
// g(x) = x^6 + a5*x^5 + ... + a1*x + a0 // g(x) = x^6 + a5*x^5 + ... + a1*x + a0
// g(x) = x^6 + x + 1 // g(x) = x^6 + x + 1
#define CRC6_GEN_POL 0x30 // MSB=a0 LSB=a5 #define CRC6_GEN_POL 0x30 // MSB=a0 LSB=a5
// g(x) = x^6 + x^2 + x + 1 (as suggested by Joe. See: https://users.ece.cmu.edu/~koopman/crc/) // g(x) = x^6 + x^2 + x + 1 (See: https://users.ece.cmu.edu/~koopman/crc/)
// #define CRC6_GEN_POL 0x38 // MSB=a0 LSB=a5. Simulation results are similar // #define CRC6_GEN_POL 0x38 // MSB=a0 LSB=a5. Simulation results are similar
static int calc_crc6(const int *x, int sz) static int calc_crc6(const int *x, int sz)
@ -272,7 +276,8 @@ static int calc_crc6(const int *x, int sz)
return sr; return sr;
} }
static void ix_mask(float *dst, const float *src, const int *mask, const int *x) static void ix_mask(float *dst, const float *src, const int *mask,
const int *x)
{ {
// mask intrinsic information (channel observations) with a priori knowledge // mask intrinsic information (channel observations) with a priori knowledge
@ -340,5 +345,3 @@ void decodemsg_jt65(int *call1, int *call2, int *grid, const int *x)
*call2 = nc2; *call2 = nc2;
*grid = ng; *grid = ng;
} }

View File

@ -11,20 +11,32 @@ void qra65_enc_(int x[], int y[])
qra65_encode(codec, y, x); qra65_encode(codec, y, x);
} }
void qra65_dec_(float r[], int xdec[], int* rc) void qra65_dec_(float r[], int* nmycall, int xdec[], int* rc)
{ {
// Return codes: // Return codes:
// rc<0 no decode // rc=-16 failed sanity check
// rc=-2 decoded, but crc check failed
// rc=-1 no decode
// rc=0 [? ? ?] AP0 (decoding with no a-priori information) // rc=0 [? ? ?] AP0 (decoding with no a-priori information)
// rc=1 [CQ ? ?] AP27 // rc=1 [CQ ? ?] AP27
// rc=2 [CQ ? ] AP44 // rc=2 [CQ ? ] AP42
// rc=3 [CALL ? ?] AP29 // rc=3 [CALL ? ?] AP29
// rc=4 [CALL ? ] AP45 // rc=4 [CALL ? ] AP44
// rc=5 [CALL CALL ?] AP57 // rc=5 [CALL CALL ?] AP57
int ncall=0xf70c238; //K1ABC static int ncall0=0;
// int ncall=0x890c60c; //KA1ABC int ncall;
int i; int x[63],y[12];
ncall = *nmycall;
qra65codec *codec = qra65_init(1,ncall); //codec for ncall qra65codec *codec = qra65_init(1,ncall); //codec for ncall
/*
if(ncall != ncall0) {
memset(y,0,sizeof(y));
qra65_encode(codec, y, x);
printf("Updated codec %d\n",ncall);
}
ncall0=ncall;
*/
*rc = qra65_decode(codec,xdec,r); *rc = qra65_decode(codec,xdec,r);
} }

View File

@ -121,6 +121,8 @@ program qra65sim
h=default_header(12000,npts) h=default_header(12000,npts)
dfsig=2000.0/nsigs !Freq spacing between sigs in file (Hz) dfsig=2000.0/nsigs !Freq spacing between sigs in file (Hz)
print*,'A',nsigs,nfiles
do ifile=1,nfiles !Loop over requested number of files do ifile=1,nfiles !Loop over requested number of files
write(fname,1002) ifile !Output filename write(fname,1002) ifile !Output filename
1002 format('000000_',i4.4) 1002 format('000000_',i4.4)
@ -146,9 +148,8 @@ program qra65sim
call packmsg(msg,dgen,itype) !Pack message into 12 six-bit bytes call packmsg(msg,dgen,itype) !Pack message into 12 six-bit bytes
call qra65_enc(dgen,sent) !Encode using QRA65 call qra65_enc(dgen,sent) !Encode using QRA65
! call qra65_dec(sent,dgen,ierr) !Decode (### for test only ###) ! call qra65_dec(sent,dgen,ierr) !Decode (### for test only ###)
! write(*,3001) sent
write(*,3001) sent !3001 format(21i3)
3001 format(21i3)
k=0 k=0
do j=1,nsym !Insert sync and data into itone() do j=1,nsym !Insert sync and data into itone()