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,58 +1,62 @@
// 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)
#include <windows.h> // required only for GetTickCount(...) #include <windows.h> // required only for GetTickCount(...)
#include <process.h> // _beginthread #include <process.h> // _beginthread
#endif #endif
#if __linux__ #if __linux__
@ -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
@ -87,19 +91,19 @@ unsigned GetTickCount(void) {
void printwordd(char *msg, int *x, int size) void printwordd(char *msg, int *x, int size)
{ {
int k; int k;
printf("\n%s ",msg); printf("\n%s ",msg);
for (k=0;k<size;k++) for (k=0;k<size;k++)
printf("%2d ",x[k]); printf("%2d ",x[k]);
printf("\n"); printf("\n");
} }
void printwordh(char *msg, int *x, int size) void printwordh(char *msg, int *x, int size)
{ {
int k; int k;
printf("\n%s ",msg); printf("\n%s ",msg);
for (k=0;k<size;k++) for (k=0;k<size;k++)
printf("%02hx ",x[k]); printf("%02hx ",x[k]);
printf("\n"); printf("\n");
} }
#define NSAMPLES (QRA65_N*QRA65_M) #define NSAMPLES (QRA65_N*QRA65_M)
@ -112,51 +116,53 @@ 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
const float No = 1.0f; // noise spectral density x is a pointer to the transmitted codeword, an array of QRA65_N
const float sigma = (float)sqrt(No/2.0f); // std dev of noise I/Q components integers in the range 0..63.
const float sigmach = (float)sqrt(1/2.0f); // std dev of channel I/Q gains
const float R = 1.0f*QRA65_K/QRA65_N;
float EbNo = (float)pow(10,EbNodB/10); Returns the received symbol energies (squared amplitudes) as an array of
float EsNo = 1.0f*QRA65_m*R*EbNo; (QRA65_M*QRA65_N) floats. The first QRA65_M entries of this array are
float Es = EsNo*No; the energies of the first symbol in the codeword. The second QRA65_M
float A = (float)sqrt(Es); entries are those of the second symbol, and so on up to the last codeword
int k; 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*QRA65_K/QRA65_N;
normrnd_s(rp,NSAMPLES,0,sigma); float EbNo = (float)pow(10,EbNodB/10);
normrnd_s(rq,NSAMPLES,0,sigma); float EsNo = 1.0f*QRA65_m*R*EbNo;
float Es = EsNo*No;
float A = (float)sqrt(Es);
int k;
if (channel_type == CHANNEL_AWGN) normrnd_s(rp,NSAMPLES,0,sigma);
for (k=0;k<QRA65_N;k++) normrnd_s(rq,NSAMPLES,0,sigma);
rp[k*QRA65_M+x[k]]+=A;
else
if (channel_type == CHANNEL_RAYLEIGH) {
normrnd_s(chp,QRA65_N,0,sigmach);
normrnd_s(chq,QRA65_N,0,sigmach);
for (k=0;k<QRA65_N;k++) {
rp[k*QRA65_M+x[k]]+=A*chp[k];
rq[k*QRA65_M+x[k]]+=A*chq[k];
}
}
else {
return 0; // unknown channel type
}
// compute the squares of the amplitudes of the received samples if (channel_type == CHANNEL_AWGN)
for (k=0;k<NSAMPLES;k++) for (k=0;k<QRA65_N;k++)
r[k] = rp[k]*rp[k] + rq[k]*rq[k]; rp[k*QRA65_M+x[k]]+=A;
else
if (channel_type == CHANNEL_RAYLEIGH) {
normrnd_s(chp,QRA65_N,0,sigmach);
normrnd_s(chq,QRA65_N,0,sigmach);
for (k=0;k<QRA65_N;k++) {
rp[k*QRA65_M+x[k]]+=A*chp[k];
rq[k*QRA65_M+x[k]]+=A*chq[k];
}
}
else {
return 0; // unknown channel type
}
return r; // 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 // These defines are some packed fields as computed by JT65
@ -166,276 +172,287 @@ float *mfskchannel(int *x, int channel_type, float EbNodB)
#define GRID_73 0x7ED0 // 73 #define GRID_73 0x7ED0 // 73
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
int x[QRA65_K], xdec[QRA65_K]; Step 2) if IV3NWV's decoder is unable to decode K1JT's without AP it
int y[QRA65_N]; attempts to decode messages of the type [IV3NWV ? ?] and [IV3NWV ?].
float *rx;
int rc;
// each simulated station must use its own codec Step 3) K1JT's decoder attempts to decode [? ? ?] and [K1JT IV3NWV ?]
// as it might work with different a-priori information (this last decode type has been enabled by K1JT's encoder at step 2)
qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT
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)
// IV3NWV makes a CQ call (with no grid) At each step the simulation reports if a decode was successful. In
printf("IV3NWV tx: CQ IV3NWV\n"); this case it also reports the type of decode (see table decode_type
encodemsg_jt65(x,CALL_CQ,CALL_IV3NWV,GRID_BLANK); above)
qra65_encode(codec_iv3nwv, y, x);
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 y[QRA65_N];
float *rx;
int rc;
// Each simulated station must use its own codec, since it might work with
// different a-priori information.
qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // 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);
qra65_encode(codec_iv3nwv, y, x);
rx = mfskchannel(y,channel_type,EbNodB);
// Step 1b: K1JT attempts to decode [? ? ?], [CQ/QRZ ? ?] or [CQ/QRZ ?]
rc = qra65_decode(codec_k1jt, 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);
qra65_encode(codec_k1jt, y, x);
rx = mfskchannel(y,channel_type,EbNodB);
// Step 2b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?] or [IV3NWV ?]
rc = qra65_decode(codec_iv3nwv, 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);
qra65_encode(codec_iv3nwv, y, x);
rx = mfskchannel(y,channel_type,EbNodB);
// Step 3b: K1JT attempts to decode [? ? ?] or [K1JT IV3NWV ?]
rc = qra65_decode(codec_k1jt, 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);
qra65_encode(codec_k1jt, y, x);
rx = mfskchannel(y,channel_type,EbNodB); rx = mfskchannel(y,channel_type,EbNodB);
// K1JT attempts to decode // Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?]
rc = qra65_decode(codec_k1jt, xdec,rx); rc = qra65_decode(codec_iv3nwv, xdec,rx);
if (rc>=0) { // decoded if (rc>=0) { // decoded
printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]); printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// K1JT replies to IV3NWV (with no grid) return 0;
printf("K1JT tx: IV3NWV K1JT\n"); }
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_BLANK); }
qra65_encode(codec_k1jt, y, x); }
rx = mfskchannel(y,channel_type,EbNodB); }
printf("the other party did not decode\n");
// IV3NWV attempts to decode return -1;
rc = qra65_decode(codec_iv3nwv, xdec,rx);
if (rc>=0) { // decoded
printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// IV3NWV replies to K1JT with a 73
printf("IV3NWV tx: K1JT IV3NWV 73\n");
encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV, GRID_73);
qra65_encode(codec_iv3nwv, y, x);
rx = mfskchannel(y,channel_type,EbNodB);
// K1JT attempts to decode
rc = qra65_decode(codec_k1jt, xdec,rx);
if (rc>=0) { // decoded
printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// K1JT replies to IV3NWV with a 73
printf("K1JT tx: IV3NWV K1JT 73\n");
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT, GRID_73);
qra65_encode(codec_k1jt, y, x);
rx = mfskchannel(y,channel_type,EbNodB);
// IV3NWV attempts to decode
rc = qra65_decode(codec_iv3nwv, xdec,rx);
if (rc>=0) { // decoded
printf("IV3NWV rx: received with apcode=%d %s\n",rc, decode_type[rc]);
return 0;
}
}
}
}
printf("the other party did not decode\n");
return -1;
} }
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:
int x[QRA65_K], xdec[QRA65_K]; rc=0 [? ? ?] AP0
int y[QRA65_N]; rc=1 [CQ ? ?] AP27
float *rx; rc=2 [CQ ? ] AP42
int rc,k; rc=3 [CALL ? ?] AP29
rc=4 [CALL ? ] AP44
rc=5 [CALL CALL ?] AP57
int ndecok[6] = { 0, 0, 0, 0, 0, 0}; The return code is <0 when decoding is unsuccessful
int ntx = 100,ndec=0;
qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV This test simulates the situation ntx times and reports how many times
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT a particular type decode among the above 6 cases succeded.
*/
// this will enable k1jt's decoder to look for iv3nwv calls int x[QRA65_K], xdec[QRA65_K];
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK); int y[QRA65_N];
qra65_encode(codec_k1jt, y, x); float *rx;
printf("K1JT tx: IV3NWV K1JT\n"); int rc,k;
// iv3nwv reply to k1jt int ndecok[6] = { 0, 0, 0, 0, 0, 0};
printf("IV3NWV tx: K1JT IV3NWV JN66\n"); int ntx = 100,ndec=0;
encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66);
qra65_encode(codec_iv3nwv, y, x);
printf("Simulating decodes by K1JT up to AP56 ..."); qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT
for (k=0;k<ntx;k++) { // This will enable K1JT's decoder to look for IV3NWV calls
printf("."); encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK);
rx = mfskchannel(y,channel_type,EbNodB); qra65_encode(codec_k1jt, y, x);
rc = qra65_decode(codec_k1jt, xdec,rx); printf("K1JT tx: IV3NWV K1JT\n");
if (rc>=0)
ndecok[rc]++;
}
printf("\n");
printf("Transimtted:%d - Decoded:\n",ntx); // IV3NWV reply to K1JT
for (k=0;k<6;k++) { printf("IV3NWV tx: K1JT IV3NWV JN66\n");
printf("%3d with %s\n",ndecok[k],decode_type[k]); encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66);
ndec += ndecok[k]; qra65_encode(codec_iv3nwv, y, x);
}
printf("Total: %d/%d\n",ndec,ntx);
printf("\n");
return 0; printf("Simulating decodes by K1JT up to AP56 ...");
for (k=0;k<ntx;k++) {
printf(".");
rx = mfskchannel(y,channel_type,EbNodB);
rc = qra65_decode(codec_k1jt, xdec,rx);
if (rc>=0)
ndecok[rc]++;
}
printf("\n");
printf("Transimtted:%d - Decoded:\n",ntx);
for (k=0;k<6;k++) {
printf("%3d with %s\n",ndecok[k],decode_type[k]);
ndec += ndecok[k];
}
printf("Total: %d/%d\n",ndec,ntx);
printf("\n");
return 0;
} }
void syntax(void) void syntax(void)
{ {
printf("\nQRA65 Mode Tests\n"); printf("\nQRA65 Mode Tests\n");
printf("2016, Nico Palermo - IV3NWV\n\n"); printf("2016, Nico Palermo - IV3NWV\n\n");
printf("---------------------------\n\n"); printf("---------------------------\n\n");
printf("Syntax: qra65 [-s<snrdb>] [-c<channel>] [-a<ap-type>] [-t<testtype>] [-h]\n"); printf("Syntax: qra65 [-s<snrdb>] [-c<channel>] [-a<ap-type>] [-t<testtype>] [-h]\n");
printf("Options: \n"); printf("Options: \n");
printf(" -s<snrdb> : set simulation SNR in 2500 Hz BW (default:-27.5 dB)\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(" -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=NO_AP 1=AUTO_AP (default)\n");
printf(" -t<testtype>: 0=simulate seq of msgs between IV3NWV and K1JT (default)\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(" 1=simulate K1JT receiving K1JT IV3NWV JN66\n");
printf(" -h: this help\n"); printf(" -h: this help\n");
} }
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
while(--argc) {
argv++;
if (strncmp(*argv,"-h",2)==0) {
syntax();
return 0;
}
else
if (strncmp(*argv,"-a",2)==0) {
mode = ( int)atoi((*argv)+2);
if (mode>1) {
printf("Invalid decoding mode\n");
syntax();
return -1;
}
}
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");
syntax();
return -1;
}
}
else
if (strncmp(*argv,"-t",2)==0) {
testtype = ( int)atoi((*argv)+2);
if (testtype>1) {
printf("Invalid test type\n");
syntax();
return -1;
}
}
else
if (strncmp(*argv,"-c",2)==0) {
channel = ( int)atoi((*argv)+2);
if (channel>CHANNEL_RAYLEIGH) {
printf("Invalid channel type\n");
syntax();
return -1;
}
}
else {
printf("Invalid option\n");
syntax();
return -1;
}
}
EbNodB = SNRdB+29.1f;
// Parse the command line
while(--argc) {
argv++;
if (strncmp(*argv,"-h",2)==0) {
syntax();
return 0;
} else {
if (strncmp(*argv,"-a",2)==0) {
mode = ( int)atoi((*argv)+2);
if (mode>1) {
printf("Invalid decoding mode\n");
syntax();
return -1;
}
} 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");
syntax();
return -1;
}
} else {
if (strncmp(*argv,"-t",2)==0) {
testtype = ( int)atoi((*argv)+2);
if (testtype>1) {
printf("Invalid test type\n");
syntax();
return -1;
}
} else {
if (strncmp(*argv,"-c",2)==0) {
channel = ( int)atoi((*argv)+2);
if (channel>CHANNEL_RAYLEIGH) {
printf("Invalid channel type\n");
syntax();
return -1;
}
} else {
printf("Invalid option\n");
syntax();
return -1;
}
}
}
}
}
}
EbNodB = SNRdB+29.1f;
#if defined(__linux__) || defined(__unix__) #if defined(__linux__) || defined(__unix__)
srand48(GetTickCount()); srand48(GetTickCount());
#endif #endif
if (testtype==0) { if (testtype==0) {
for (k=0;k<nqso;k++) { for (k=0;k<nqso;k++) {
printf("\n\n------------------------\n"); printf("\n\n------------------------\n");
rc = test_proc_1(channel, EbNodB, mode); rc = test_proc_1(channel, EbNodB, mode);
if (rc==0) if (rc==0)
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,304 +43,305 @@
// 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)
{ {
// Eb/No value for which we optimize the decoder metric // Eb/No value for which we optimize the decoder metric
const float EbNodBMetric = 2.8f; const float EbNodBMetric = 2.8f;
const float EbNoMetric = (float)pow(10,EbNodBMetric/10); const float EbNoMetric = (float)pow(10,EbNodBMetric/10);
const float R = 1.0f*(QRA65_KC)/(QRA65_NC); const float R = 1.0f*(QRA65_KC)/(QRA65_NC);
qra65codec *pcodec = (qra65codec*)malloc(sizeof(qra65codec)); qra65codec *pcodec = (qra65codec*)malloc(sizeof(qra65codec));
if (!pcodec) if (!pcodec)
return 0; // can't allocate memory return 0; // can't allocate memory
pcodec->decEsNoMetric = 1.0f*QRA65_m*R*EbNoMetric;
pcodec->apflags = flags;
if (flags!=QRA_AUTOAP) pcodec->decEsNoMetric = 1.0f*QRA65_m*R*EbNoMetric;
return pcodec; pcodec->apflags = flags;
// initialize messages and mask for decoding with a-priori information if (flags!=QRA_AUTOAP)
return pcodec;
pcodec->apmycall = mycall; // initialize messages and mask for decoding with a-priori information
pcodec->apsrccall = 0;
// encode CQ/QRZ messages and masks pcodec->apmycall = mycall;
// NOTE: Here we handle only CQ and QRZ msgs pcodec->apsrccall = 0;
// '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 (26+1)
encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL); // AP42 (26+16)
// encode [mycall ? x] messages and set masks // encode CQ/QRZ messages and masks
encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK); // NOTE: Here we handle only CQ and QRZ msgs
encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); // AP29 (28+1) // 'CQ nnn', 'CQ DX' and 'DE' msgs
encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1, 0, MASK_GRIDFULL); // AP44 (28+16) // 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
// set mask for [mycall srccall ?] messages // encode [mycall ? x] messages and set masks
encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1, MASK_CALL2, MASK_GRIDBIT); // AP56 (28+28) 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
return pcodec; // set mask for [mycall srccall ?] messages
encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1,MASK_CALL2,
MASK_GRIDBIT); // AP56
return pcodec;
} }
void qra65_encode(qra65codec *pcodec, int *y, const int *x) void qra65_encode(qra65codec *pcodec, int *y, const int *x)
{ {
int encx[QRA65_KC]; // encoder input buffer int encx[QRA65_KC]; // encoder input buffer
int ency[QRA65_NC]; // encoder output buffer int ency[QRA65_NC]; // encoder output buffer
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
memcpy(y,ency,QRA65_K*sizeof(int)); // copy information symbols
memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy parity symbols
// copy codeword to output puncturing the crc symbol if (pcodec->apflags!=QRA_AUTOAP)
memcpy(y,ency,QRA65_K*sizeof(int)); // copy the information symbols return;
memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy the parity check symbols
if (pcodec->apflags!=QRA_AUTOAP) // look if the msg sent is a std type message (bit15 of grid field = 0)
return; if ((x[9]&0x80)==1)
return; // no, it's a text message
// look if the msg sent is a std type message (bit15 of grid field = 0) // It's a [call1 call2 grid] message
if ((x[9]&0x80)==1)
return; // no, it's a text message
// it's a [call1 call2 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..)
// we assume that call2 is our call (but we don't check it) decodemsg_jt65(&call1,&call2,&grid,x);
// 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) { 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)
pcodec->apsrccall = 0; // Reset apsrccall to 0 so decoder won't look for [mycall srccall ?] msgs
} pcodec->apsrccall = 0;
else { } else {
// we are replying someone named call1 // We are replying to someone named call1
// set apmsg_call1_call2 so that the decoder will attempt to decode [mycall call1 ?] msgs // Set apmsg_call1_call2 so decoder will try for [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;
if (QRA65_NMSG!=QRA65_CODE.NMSG) // sanity check
return -16; // QRA65_NMSG define is wrong
// sanity check // compute symbols intrinsic probabilities from received energy observations
if (QRA65_NMSG!=QRA65_CODE.NMSG) qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric);
return -16; // QRA65_NMSG define is wrong
// compute symbols intrinsic probabilities from received energy observations // de-puncture observations adding a uniform distribution for the crc symbol
qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric);
// de-puncture observations adding a uniform distribution for the crc symbol ---------- // move check symbols distributions one symbol towards the end
dsttmp = PD_ROWADDR(ix,QRA65_M, QRA65_NC-1); //Point to last symbol prob dist
srctmp = dsttmp-QRA65_M; // source is the previous pd
for (k=0;k<QRA65_C;k++) {
pd_init(dsttmp,srctmp,QRA65_M);
dsttmp -=QRA65_M;
srctmp -=QRA65_M;
}
// Initialize crc prob to a uniform distribution
pd_init(dsttmp,pd_uniform(QRA65_m),QRA65_M);
// move check symbols distributions one symbol towards the end // Attempt to decode without a-priori info --------------------------------
dsttmp = PD_ROWADDR(ix,QRA65_M, QRA65_NC-1); // point to the last symbol prob dist rc = qra65_do_decode(x, ix, NULL, NULL);
srctmp = dsttmp-QRA65_M; // source is the previous pd if (rc>=0) return 0; // successfull decode with AP0
for (k=0;k<QRA65_C;k++) {
pd_init(dsttmp,srctmp,QRA65_M);
dsttmp -=QRA65_M;
srctmp -=QRA65_M;
}
// init crc prob to a uniform distribution
pd_init(dsttmp,pd_uniform(QRA65_m),QRA65_M);
// attempt to decode without a-priori -------------------------------------------------- if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode
rc = qra65_do_decode(x, ix, NULL, NULL);
if (rc>=0) return 0; // successfull decode with 0 ap
if (pcodec->apflags!=QRA_AUTOAP) return rc; // rc<0 = unsuccessful decode // Attempt to decode CQ calls
rc = qra65_do_decode(x,ix,pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // AP27
if (rc>=0) return 1; // decoded [cq/qrz ? ?]
// attempt to decode CQ calls rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo,
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz, pcodec->apmsg_cqqrz); // 27 bit AP pcodec->apmsg_cqqrz); // AP42
if (rc>=0) return 1; // decoded [cq/qrz ? ?] if (rc>=0) return 2; // decoded [cq ? ooo]
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo, pcodec->apmsg_cqqrz); // 44 bit AP
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,
if (rc>=0) return 3; // decoded [mycall ? ?] pcodec->apmsg_call1); // AP29
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo, pcodec->apmsg_call1); // 45 bit AP if (rc>=0) return 3; // decoded [mycall ? ?]
if (rc>=0) return 4; // decoded [mycall ? ooo]
// if apsrccall is set attempt to decode [mycall srccall ?] msgs rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo,
if (pcodec->apsrccall==0) return rc; // nothing more to do pcodec->apmsg_call1); // AP44
if (rc>=0) return 4; // decoded [mycall ? ooo]
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_call2, pcodec->apmsg_call1_call2); // 57 bit AP // if apsrccall is set attempt to decode [mycall srccall ?] msgs
if (rc>=0) return 5; // decoded [mycall srccall ?] if (pcodec->apsrccall==0) return rc; // nothing more to do
return rc; rc = qra65_do_decode(x, ix, pcodec->apmask_call1_call2,
pcodec->apmsg_call1_call2); // AP57
if (rc>=0) return 5; // decoded [mycall srccall ?]
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];
int xdec[QRA65_KC]; int xdec[QRA65_KC];
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
} }
// run the decoding algorithm // run the decoding algorithm
rc = qra_extrinsic(&QRA65_CODE,ex,ixsrc,QRA65_NITER,v2cmsg,c2vmsg); rc = qra_extrinsic(&QRA65_CODE,ex,ixsrc,QRA65_NITER,v2cmsg,c2vmsg);
if (rc<0) if (rc<0)
return -1; // no convergence in given iterations return -1; // no convergence in given iterations
// decode // decode
qra_mapdecode(&QRA65_CODE,xdec,ex,ixsrc); qra_mapdecode(&QRA65_CODE,xdec,ex,ixsrc);
// verify crc // verify crc
if (calc_crc6(xdec,QRA65_K)!=xdec[QRA65_K]) // crc doesn't match (detected error) if (calc_crc6(xdec,QRA65_K)!=xdec[QRA65_K]) // crc doesn't match (detected error)
return -2; // decoding was succesfull but crc doesn't match return -2; // decoding was succesfull but crc doesn't match
// success. copy decoded message to output buffer // success. copy decoded message to output buffer
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)
{ {
// todo: compute it faster using a look up table // todo: compute it faster using a look up table
int k,j,t,sr = 0; int k,j,t,sr = 0;
for (k=0;k<sz;k++) { for (k=0;k<sz;k++) {
t = x[k]; t = x[k];
for (j=0;j<6;j++) { for (j=0;j<6;j++) {
if ((t^sr)&0x01) if ((t^sr)&0x01)
sr = (sr>>1) ^ CRC6_GEN_POL; sr = (sr>>1) ^ CRC6_GEN_POL;
else else
sr = (sr>>1); sr = (sr>>1);
t>>=1; t>>=1;
} }
} }
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
int k,kk, smask; int k,kk, smask;
float *row; float *row;
memcpy(dst,src,(QRA65_NC*QRA65_M)*sizeof(float)); memcpy(dst,src,(QRA65_NC*QRA65_M)*sizeof(float));
for (k=0;k<QRA65_K;k++) { // we can mask only information symbols distrib for (k=0;k<QRA65_K;k++) { // we can mask only information symbols distrib
smask = mask[k]; smask = mask[k];
row = PD_ROWADDR(dst,QRA65_M,k); row = PD_ROWADDR(dst,QRA65_M,k);
if (smask) { if (smask) {
for (kk=0;kk<QRA65_M;kk++) for (kk=0;kk<QRA65_M;kk++)
if (((kk^x[k])&smask)!=0) if (((kk^x[k])&smask)!=0)
*(row+kk) = 0.f; *(row+kk) = 0.f;
pd_norm(row,QRA65_m); pd_norm(row,QRA65_m);
} }
} }
} }
// encode/decode msgs as done in JT65 // encode/decode msgs as done in JT65
void encodemsg_jt65(int *y, const int call1, const int call2, const int grid) void encodemsg_jt65(int *y, const int call1, const int call2, const int grid)
{ {
y[0]= (call1>>22)&0x3F; y[0]= (call1>>22)&0x3F;
y[1]= (call1>>16)&0x3F; y[1]= (call1>>16)&0x3F;
y[2]= (call1>>10)&0x3F; y[2]= (call1>>10)&0x3F;
y[3]= (call1>>4)&0x3F; y[3]= (call1>>4)&0x3F;
y[4]= (call1<<2)&0x3F; y[4]= (call1<<2)&0x3F;
y[4] |= (call2>>26)&0x3F; y[4] |= (call2>>26)&0x3F;
y[5]= (call2>>20)&0x3F; y[5]= (call2>>20)&0x3F;
y[6]= (call2>>14)&0x3F; y[6]= (call2>>14)&0x3F;
y[7]= (call2>>8)&0x3F; y[7]= (call2>>8)&0x3F;
y[8]= (call2>>2)&0x3F; y[8]= (call2>>2)&0x3F;
y[9]= (call2<<4)&0x3F; y[9]= (call2<<4)&0x3F;
y[9] |= (grid>>12)&0x3F; y[9] |= (grid>>12)&0x3F;
y[10]= (grid>>6)&0x3F; y[10]= (grid>>6)&0x3F;
y[11]= (grid)&0x3F; y[11]= (grid)&0x3F;
} }
void decodemsg_jt65(int *call1, int *call2, int *grid, const int *x) void decodemsg_jt65(int *call1, int *call2, int *grid, const int *x)
{ {
int nc1, nc2, ng; int nc1, nc2, ng;
nc1 = x[4]>>2; nc1 = x[4]>>2;
nc1 |= x[3]<<4; nc1 |= x[3]<<4;
nc1 |= x[2]<<10; nc1 |= x[2]<<10;
nc1 |= x[1]<<16; nc1 |= x[1]<<16;
nc1 |= x[0]<<22; nc1 |= x[0]<<22;
nc2 = x[9]>>4; nc2 = x[9]>>4;
nc2 |= x[8]<<2; nc2 |= x[8]<<2;
nc2 |= x[7]<<8; nc2 |= x[7]<<8;
nc2 |= x[6]<<14; nc2 |= x[6]<<14;
nc2 |= x[5]<<20; nc2 |= x[5]<<20;
nc2 |= (x[4]&0x03)<<26; nc2 |= (x[4]&0x03)<<26;
ng = x[11]; ng = x[11];
ng |= x[10]<<6; ng |= x[10]<<6;
ng |= (x[9]&0x0F)<<12; ng |= (x[9]&0x0F)<<12;
*call1 = nc1; *call1 = nc1;
*call2 = nc2; *call2 = nc2;
*grid = ng; *grid = ng;
} }

View File

@ -43,18 +43,18 @@
#define GRID_BLANK 0x7E91 #define GRID_BLANK 0x7E91
typedef struct { typedef struct {
float decEsNoMetric; float decEsNoMetric;
int apflags; int apflags;
int apmycall; int apmycall;
int apsrccall; int apsrccall;
int apmsg_cqqrz[12]; // [cq/qrz ? blank] int apmsg_cqqrz[12]; // [cq/qrz ? blank]
int apmsg_call1[12]; // [mycall ? blank] int apmsg_call1[12]; // [mycall ? blank]
int apmsg_call1_call2[12]; // [mycall srccall ?] int apmsg_call1_call2[12]; // [mycall srccall ?]
int apmask_cqqrz[12]; int apmask_cqqrz[12];
int apmask_cqqrz_ooo[12]; int apmask_cqqrz_ooo[12];
int apmask_call1[12]; int apmask_call1[12];
int apmask_call1_ooo[12]; int apmask_call1_ooo[12];
int apmask_call1_call2[12]; int apmask_call1_call2[12];
} qra65codec; } qra65codec;
#ifdef __cplusplus #ifdef __cplusplus

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()