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 180a62efda
commit 27490897da
5 changed files with 615 additions and 582 deletions

View File

@ -1,58 +1,62 @@
// 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.
/*
main.c
QRA65 mode encode/decode test
// 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/>.
(c) 2016 - Nico Palermo, IV3NWV
// -----------------------------------------------------------------------------
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 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.
------------------------------------------------------------------------------
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
// 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!
// Windows (x64 and x86)
#include <windows.h> // required only for GetTickCount(...)
#include <process.h> // _beginthread
// Windows (x64 and x86)
#include <windows.h> // required only for GetTickCount(...)
#include <process.h> // _beginthread
#endif
#if __linux__
@ -79,7 +83,7 @@ unsigned GetTickCount(void) {
#include "qra65.h"
#include "../qracodes/normrnd.h" // gaussian numbers generator
// -----------------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// channel types
#define CHANNEL_AWGN 0
@ -87,19 +91,19 @@ unsigned GetTickCount(void) {
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");
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");
int k;
printf("\n%s ",msg);
for (k=0;k<size;k++)
printf("%02hx ",x[k]);
printf("\n");
}
#define NSAMPLES (QRA65_N*QRA65_M)
@ -112,51 +116,53 @@ static float r[NSAMPLES];
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)
//
// 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
/*
Simulate an MFSK channel, either AWGN or Rayleigh.
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;
x is a pointer to the transmitted codeword, an array of QRA65_N
integers in the range 0..63.
float EbNo = (float)pow(10,EbNodB/10);
float EsNo = 1.0f*QRA65_m*R*EbNo;
float Es = EsNo*No;
float A = (float)sqrt(Es);
int k;
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 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);
normrnd_s(rq,NSAMPLES,0,sigma);
float EbNo = (float)pow(10,EbNodB/10);
float EsNo = 1.0f*QRA65_m*R*EbNo;
float Es = EsNo*No;
float A = (float)sqrt(Es);
int k;
if (channel_type == CHANNEL_AWGN)
for (k=0;k<QRA65_N;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
}
normrnd_s(rp,NSAMPLES,0,sigma);
normrnd_s(rq,NSAMPLES,0,sigma);
// 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];
if (channel_type == CHANNEL_AWGN)
for (k=0;k<QRA65_N;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
@ -166,276 +172,287 @@ float *mfskchannel(int *x, int channel_type, float EbNodB)
#define GRID_73 0x7ED0 // 73
char decode_type[6][32] = {
"[? ? ?] AP0",
"[CQ ? ?] AP27",
"[CQ ? ] AP44",
"[CALL ? ?] AP29",
"[CALL ? ] AP45",
"[CALL CALL ?] AP57"
"[? ? ?] AP0",
"[CQ ? ?] AP27",
"[CQ ? ] AP42",
"[CALL ? ?] AP29",
"[CALL ? ] AP44",
"[CALL CALL ?] AP57"
};
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.
// 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)
/*
Here we simulate the following (dummy) QSO:
// 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)
1) CQ IV3NWV
2) IV3NWV K1JT
3) K1JT IV3NWV 73
4) IV3NWV K1JT 73
// When mode=QRA_NOAP, only [? ? ?] decodes are attempted and no a-priori information
// is used by the decoder
No message repetition is attempted
// 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
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
int x[QRA65_K], xdec[QRA65_K];
int y[QRA65_N];
float *rx;
int rc;
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 ?].
// each simulated station must use its own codec
// as 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 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)
// 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);
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 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);
// K1JT attempts to decode
rc = qra65_decode(codec_k1jt, xdec,rx);
// Step 4b: IV3NWV attempts to decode [? ? ?], [IV3NWV ? ?], or [IV3NWV ?]
rc = qra65_decode(codec_iv3nwv, xdec,rx);
if (rc>=0) { // decoded
printf("K1JT rx: received with apcode=%d %s\n",rc, decode_type[rc]);
// 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);
// 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]);
// 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;
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)
{
/*
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]
// 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 ?].
If mode=QRA_NOAP, K1JT decoder attempts to decode only msgs of type [? ? ?].
// In the case a decode is successful the return code of the qra65_decode function
// indicates the amount of a-priori information required to decode the received message
// 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
If mode=QRA_AUTOP, K1JT decoder will attempt to decode also the msgs
[K1JT IV3NWV] and [K1JT IV3NWV ?].
// This test simulates the situation ntx times and reports how many times
// a particular type decode among the above 6 cases succeded.
In the case a decode is successful the return code of the qra65_decode function
indicates the amount of a-priori information required to decode the received
message according to this table:
int x[QRA65_K], xdec[QRA65_K];
int y[QRA65_N];
float *rx;
int rc,k;
rc=0 [? ? ?] AP0
rc=1 [CQ ? ?] AP27
rc=2 [CQ ? ] AP42
rc=3 [CALL ? ?] AP29
rc=4 [CALL ? ] AP44
rc=5 [CALL CALL ?] AP57
int ndecok[6] = { 0, 0, 0, 0, 0, 0};
int ntx = 100,ndec=0;
The return code is <0 when decoding is unsuccessful
qra65codec *codec_iv3nwv = qra65_init(mode,CALL_IV3NWV); // codec for IV3NWV
qra65codec *codec_k1jt = qra65_init(mode,CALL_K1JT); // codec for K1JT
This test simulates the situation ntx times and reports how many times
a particular type decode among the above 6 cases succeded.
*/
// this will enable k1jt's decoder to look for iv3nwv calls
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK);
qra65_encode(codec_k1jt, y, x);
printf("K1JT tx: IV3NWV K1JT\n");
int x[QRA65_K], xdec[QRA65_K];
int y[QRA65_N];
float *rx;
int rc,k;
// iv3nwv reply to k1jt
printf("IV3NWV tx: K1JT IV3NWV JN66\n");
encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66);
qra65_encode(codec_iv3nwv, y, x);
int ndecok[6] = { 0, 0, 0, 0, 0, 0};
int ntx = 100,ndec=0;
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++) {
printf(".");
rx = mfskchannel(y,channel_type,EbNodB);
rc = qra65_decode(codec_k1jt, xdec,rx);
if (rc>=0)
ndecok[rc]++;
}
printf("\n");
// This will enable K1JT's decoder to look for IV3NWV calls
encodemsg_jt65(x,CALL_IV3NWV,CALL_K1JT,GRID_BLANK);
qra65_encode(codec_k1jt, y, x);
printf("K1JT tx: IV3NWV K1JT\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");
// IV3NWV reply to K1JT
printf("IV3NWV tx: K1JT IV3NWV JN66\n");
encodemsg_jt65(x,CALL_K1JT,CALL_IV3NWV,GRID_JN66);
qra65_encode(codec_iv3nwv, y, x);
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)
{
printf("\nQRA65 Mode Tests\n");
printf("2016, Nico Palermo - IV3NWV\n\n");
printf("---------------------------\n\n");
printf("Syntax: qra65 [-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\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(" 1=simulate K1JT receiving K1JT IV3NWV JN66\n");
printf(" -h: this help\n");
printf("\nQRA65 Mode Tests\n");
printf("2016, Nico Palermo - IV3NWV\n\n");
printf("---------------------------\n\n");
printf("Syntax: qra65 [-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\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(" 1=simulate K1JT receiving K1JT IV3NWV JN66\n");
printf(" -h: this help\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;
// 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;
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;
// 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__)
srand48(GetTickCount());
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);
}
else
test_proc_2(channel, EbNodB, mode);
printf("SNR = %.1fdB channel=%s ap-mode=%s\n\n",
SNRdB,
channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH",
mode==QRA_NOAP?"NO_AP":"AUTO_AP"
);
return 0;
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);
} else {
test_proc_2(channel, EbNodB, mode);
}
printf("SNR = %.1fdB channel=%s ap-mode=%s\n\n",
SNRdB,
channel==CHANNEL_AWGN?"AWGN":"RAYLEIGH",
mode==QRA_NOAP?"NO_AP":"AUTO_AP"
);
return 0;
}

View File

@ -1,35 +1,37 @@
// 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.
/*
qra65.c
Encoding/decoding functions for the QRA65 mode
// 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/>.
(c) 2016 - Nico Palermo, IV3NWV
// -----------------------------------------------------------------------------
-------------------------------------------------------------------------------
// 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 <math.h>
@ -41,304 +43,305 @@
// Code parameters of the QRA65 mode
#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_NC (QRA65_N+1) // codeword length (as defined in the code)
#define QRA65_NITER 100 // max number of iterations per decode
#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_NITER 100 // max number of iterations per decode
// static functions declarations -------------------------------------------------
// static functions declarations ----------------------------------------------
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 int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const int *ap_x);
// a-priori information masks for fields in jt65-like msgs -----------------------
static void ix_mask(float *dst, const float *src, const int *mask,
const int *x);
static int qra65_do_decode(int *x, const float *pix, const int *ap_mask,
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_CALL1 0xFFFFFFF
#define MASK_CALL2 0xFFFFFFF
#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)
{
// Eb/No value for which we optimize the decoder metric
const float EbNodBMetric = 2.8f;
const float EbNoMetric = (float)pow(10,EbNodBMetric/10);
const float R = 1.0f*(QRA65_KC)/(QRA65_NC);
// Eb/No value for which we optimize the decoder metric
const float EbNodBMetric = 2.8f;
const float EbNoMetric = (float)pow(10,EbNodBMetric/10);
const float R = 1.0f*(QRA65_KC)/(QRA65_NC);
qra65codec *pcodec = (qra65codec*)malloc(sizeof(qra65codec));
qra65codec *pcodec = (qra65codec*)malloc(sizeof(qra65codec));
if (!pcodec)
return 0; // can't allocate memory
pcodec->decEsNoMetric = 1.0f*QRA65_m*R*EbNoMetric;
pcodec->apflags = flags;
if (!pcodec)
return 0; // can't allocate memory
if (flags!=QRA_AUTOAP)
return pcodec;
pcodec->decEsNoMetric = 1.0f*QRA65_m*R*EbNoMetric;
pcodec->apflags = flags;
// initialize messages and mask for decoding with a-priori information
if (flags!=QRA_AUTOAP)
return pcodec;
pcodec->apmycall = mycall;
pcodec->apsrccall = 0;
// initialize messages and mask for decoding with a-priori information
// encode CQ/QRZ messages and masks
// NOTE: Here we handle only CQ and QRZ msgs
// 'CQ nnn', 'CQ DX' and 'DE' msgs
// will be handled by the decoder as messages with no a-priori knowledge
encodemsg_jt65(pcodec->apmsg_cqqrz, CALL_CQ, 0, GRID_BLANK);
encodemsg_jt65(pcodec->apmask_cqqrz, MASK_CQQRZ,0, MASK_GRIDBIT); // AP27 (26+1)
encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL); // AP42 (26+16)
pcodec->apmycall = mycall;
pcodec->apsrccall = 0;
// encode [mycall ? x] messages and set masks
encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK);
encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); // AP29 (28+1)
encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1, 0, MASK_GRIDFULL); // AP44 (28+16)
// encode CQ/QRZ messages and masks
// NOTE: Here we handle only CQ and QRZ msgs
// 'CQ nnn', 'CQ DX' and 'DE' msgs
// will be handled by the decoder as messages with no a-priori knowledge
encodemsg_jt65(pcodec->apmsg_cqqrz, CALL_CQ, 0, GRID_BLANK);
encodemsg_jt65(pcodec->apmask_cqqrz, MASK_CQQRZ,0, MASK_GRIDBIT); // AP27
encodemsg_jt65(pcodec->apmask_cqqrz_ooo, MASK_CQQRZ,0, MASK_GRIDFULL);// AP42
// set mask for [mycall srccall ?] messages
encodemsg_jt65(pcodec->apmask_call1_call2,MASK_CALL1, MASK_CALL2, MASK_GRIDBIT); // AP56 (28+28)
// encode [mycall ? x] messages and set masks
encodemsg_jt65(pcodec->apmsg_call1, mycall, 0, GRID_BLANK);
encodemsg_jt65(pcodec->apmask_call1, MASK_CALL1, 0, MASK_GRIDBIT); // AP29
encodemsg_jt65(pcodec->apmask_call1_ooo, MASK_CALL1,0, MASK_GRIDFULL);// AP44
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)
{
int encx[QRA65_KC]; // encoder input buffer
int ency[QRA65_NC]; // encoder output buffer
int encx[QRA65_KC]; // encoder input 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
encx[QRA65_K]=calc_crc6(encx,QRA65_K); // compute and add the crc symbol
memcpy(encx,x,QRA65_K*sizeof(int)); // Copy input to encoder buffer
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
memcpy(y,ency,QRA65_K*sizeof(int)); // copy the information symbols
memcpy(y+QRA65_K,ency+QRA65_KC,QRA65_C*sizeof(int)); // copy the parity check symbols
if (pcodec->apflags!=QRA_AUTOAP)
return;
if (pcodec->apflags!=QRA_AUTOAP)
return;
// look if the msg sent is a std type message (bit15 of grid field = 0)
if ((x[9]&0x80)==1)
return; // no, it's a text message
// look if the msg sent is a std type message (bit15 of grid field = 0)
if ((x[9]&0x80)==1)
return; // no, it's a text message
// 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)
// call1 the station callsign we are calling or indicates a general call (CQ/QRZ/etc..)
decodemsg_jt65(&call1,&call2,&grid,x);
// We assume that call2 is our call (but we don't check it)
// call1 the station callsign we are calling or indicates a general call (CQ/QRZ/etc..)
decodemsg_jt65(&call1,&call2,&grid,x);
if ((call1>=CALL_CQ && call1<=CALL_CQ999) || call1==CALL_CQDX || call1==CALL_DE) {
// we are making a general call, so we still don't know who can reply us (srccall)
// reset apsrccall to 0 so that the decoder won't look for [mycall srccall ?] msgs
pcodec->apsrccall = 0;
}
else {
// we are replying someone named call1
// set apmsg_call1_call2 so that the decoder will attempt to decode [mycall call1 ?] msgs
pcodec->apsrccall = call1;
encodemsg_jt65(pcodec->apmsg_call1_call2, pcodec->apmycall, pcodec->apsrccall, 0);
}
if ((call1>=CALL_CQ && call1<=CALL_CQ999) || call1==CALL_CQDX ||
call1==CALL_DE) {
// We are making a general call; don't know who might reply (srccall)
// Reset apsrccall to 0 so decoder won't look for [mycall srccall ?] msgs
pcodec->apsrccall = 0;
} else {
// We are replying to someone named call1
// Set apmsg_call1_call2 so decoder will try for [mycall call1 ?] msgs
pcodec->apsrccall = call1;
encodemsg_jt65(pcodec->apmsg_call1_call2, pcodec->apmycall,
pcodec->apsrccall, 0);
}
}
int qra65_decode(qra65codec *pcodec, int *x, const float *rxen)
{
int k;
float *srctmp, *dsttmp;
float ix[QRA65_NC*QRA65_M]; // (depunctured) intrisic information to the decoder
int rc;
int k;
float *srctmp, *dsttmp;
float ix[QRA65_NC*QRA65_M]; // (depunctured) intrisic information
int rc;
if (QRA65_NMSG!=QRA65_CODE.NMSG) // sanity check
return -16; // QRA65_NMSG define is wrong
// sanity check
if (QRA65_NMSG!=QRA65_CODE.NMSG)
return -16; // QRA65_NMSG define is wrong
// compute symbols intrinsic probabilities from received energy observations
qra_mfskbesselmetric(ix, rxen, QRA65_m, QRA65_N,pcodec->decEsNoMetric);
// compute symbols intrinsic probabilities from received energy observations
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
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
dsttmp = PD_ROWADDR(ix,QRA65_M, QRA65_NC-1); // point to the 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;
}
// init crc prob to a uniform distribution
pd_init(dsttmp,pd_uniform(QRA65_m),QRA65_M);
// Attempt to decode without a-priori info --------------------------------
rc = qra65_do_decode(x, ix, NULL, NULL);
if (rc>=0) return 0; // successfull decode with AP0
// attempt to decode without a-priori --------------------------------------------------
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
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, pcodec->apmsg_cqqrz); // 27 bit AP
if (rc>=0) return 1; // decoded [cq/qrz ? ?]
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo, pcodec->apmsg_cqqrz); // 44 bit AP
if (rc>=0) return 2; // decoded [cq ? ooo]
rc = qra65_do_decode(x, ix, pcodec->apmask_cqqrz_ooo,
pcodec->apmsg_cqqrz); // AP42
if (rc>=0) return 2; // decoded [cq ? ooo]
// attempt to decode calls directed to us (mycall)
rc = qra65_do_decode(x, ix, pcodec->apmask_call1, pcodec->apmsg_call1); // 29 bit AP
if (rc>=0) return 3; // decoded [mycall ? ?]
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo, pcodec->apmsg_call1); // 45 bit AP
if (rc>=0) return 4; // decoded [mycall ? ooo]
// attempt to decode calls directed to us (mycall)
rc = qra65_do_decode(x, ix, pcodec->apmask_call1,
pcodec->apmsg_call1); // AP29
if (rc>=0) return 3; // decoded [mycall ? ?]
// if apsrccall is set attempt to decode [mycall srccall ?] msgs
if (pcodec->apsrccall==0) return rc; // nothing more to do
rc = qra65_do_decode(x, ix, pcodec->apmask_call1_ooo,
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 (rc>=0) return 5; // decoded [mycall srccall ?]
// if apsrccall is set attempt to decode [mycall srccall ?] msgs
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
static int qra65_do_decode(int *x, const float *pix, const int *ap_mask, const int *ap_x)
// Decode with given a-priori information
static int qra65_do_decode(int *x, const float *pix, const int *ap_mask,
const int *ap_x)
{
int rc;
const float *ixsrc;
float ix_masked[QRA65_NC*QRA65_M]; // (masked) intrinsic information to the decoder
float ex[QRA65_NC*QRA65_M]; // extrinsic information from the decoder
int rc;
const float *ixsrc;
float ix_masked[QRA65_NC*QRA65_M]; // Masked intrinsic information
float ex[QRA65_NC*QRA65_M]; // Extrinsic information from the decoder
float v2cmsg[QRA65_NMSG*QRA65_M]; // buffers for the decoder messages
float c2vmsg[QRA65_NMSG*QRA65_M];
int xdec[QRA65_KC];
float v2cmsg[QRA65_NMSG*QRA65_M]; // buffers for the decoder messages
float c2vmsg[QRA65_NMSG*QRA65_M];
int xdec[QRA65_KC];
if (ap_mask==NULL) { // no a-priori information
ixsrc = pix; // intrinsic source is what passed as argument
}
else { // a-priori information provided
// mask channel observations with a-priori
ix_mask(ix_masked,pix,ap_mask,ap_x);
ixsrc = ix_masked; // intrinsic source is the masked version
}
if (ap_mask==NULL) { // no a-priori information
ixsrc = pix; // intrinsic source is what passed as argument
} else {
// a-priori information provided
// mask channel observations with a-priori
ix_mask(ix_masked,pix,ap_mask,ap_x);
ixsrc = ix_masked; // intrinsic source is the masked version
}
// run the decoding algorithm
rc = qra_extrinsic(&QRA65_CODE,ex,ixsrc,QRA65_NITER,v2cmsg,c2vmsg);
if (rc<0)
return -1; // no convergence in given iterations
// run the decoding algorithm
rc = qra_extrinsic(&QRA65_CODE,ex,ixsrc,QRA65_NITER,v2cmsg,c2vmsg);
if (rc<0)
return -1; // no convergence in given iterations
// decode
qra_mapdecode(&QRA65_CODE,xdec,ex,ixsrc);
// decode
qra_mapdecode(&QRA65_CODE,xdec,ex,ixsrc);
// verify crc
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
// verify crc
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
// success. copy decoded message to output buffer
memcpy(x,xdec,QRA65_K*sizeof(int));
return 0;
// success. copy decoded message to output buffer
memcpy(x,xdec,QRA65_K*sizeof(int));
return 0;
}
// crc functions -------------------------------------------------------------------------------
// crc functions --------------------------------------------------------------
// crc-6 generator polynomial
// g(x) = x^6 + a5*x^5 + ... + a1*x + a0
// g(x) = x^6 + x + 1
#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
static int calc_crc6(const int *x, int sz)
{
// todo: compute it faster using a look up table
int k,j,t,sr = 0;
for (k=0;k<sz;k++) {
t = x[k];
for (j=0;j<6;j++) {
if ((t^sr)&0x01)
sr = (sr>>1) ^ CRC6_GEN_POL;
else
sr = (sr>>1);
t>>=1;
}
}
return sr;
// todo: compute it faster using a look up table
int k,j,t,sr = 0;
for (k=0;k<sz;k++) {
t = x[k];
for (j=0;j<6;j++) {
if ((t^sr)&0x01)
sr = (sr>>1) ^ CRC6_GEN_POL;
else
sr = (sr>>1);
t>>=1;
}
}
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;
float *row;
int k,kk, smask;
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
smask = mask[k];
row = PD_ROWADDR(dst,QRA65_M,k);
if (smask) {
for (kk=0;kk<QRA65_M;kk++)
if (((kk^x[k])&smask)!=0)
*(row+kk) = 0.f;
for (k=0;k<QRA65_K;k++) { // we can mask only information symbols distrib
smask = mask[k];
row = PD_ROWADDR(dst,QRA65_M,k);
if (smask) {
for (kk=0;kk<QRA65_M;kk++)
if (((kk^x[k])&smask)!=0)
*(row+kk) = 0.f;
pd_norm(row,QRA65_m);
}
}
pd_norm(row,QRA65_m);
}
}
}
// encode/decode msgs as done in JT65
void encodemsg_jt65(int *y, const int call1, const int call2, const int grid)
{
y[0]= (call1>>22)&0x3F;
y[1]= (call1>>16)&0x3F;
y[2]= (call1>>10)&0x3F;
y[3]= (call1>>4)&0x3F;
y[4]= (call1<<2)&0x3F;
y[0]= (call1>>22)&0x3F;
y[1]= (call1>>16)&0x3F;
y[2]= (call1>>10)&0x3F;
y[3]= (call1>>4)&0x3F;
y[4]= (call1<<2)&0x3F;
y[4] |= (call2>>26)&0x3F;
y[5]= (call2>>20)&0x3F;
y[6]= (call2>>14)&0x3F;
y[7]= (call2>>8)&0x3F;
y[8]= (call2>>2)&0x3F;
y[9]= (call2<<4)&0x3F;
y[4] |= (call2>>26)&0x3F;
y[5]= (call2>>20)&0x3F;
y[6]= (call2>>14)&0x3F;
y[7]= (call2>>8)&0x3F;
y[8]= (call2>>2)&0x3F;
y[9]= (call2<<4)&0x3F;
y[9] |= (grid>>12)&0x3F;
y[10]= (grid>>6)&0x3F;
y[11]= (grid)&0x3F;
y[9] |= (grid>>12)&0x3F;
y[10]= (grid>>6)&0x3F;
y[11]= (grid)&0x3F;
}
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[3]<<4;
nc1 |= x[2]<<10;
nc1 |= x[1]<<16;
nc1 |= x[0]<<22;
nc1 = x[4]>>2;
nc1 |= x[3]<<4;
nc1 |= x[2]<<10;
nc1 |= x[1]<<16;
nc1 |= x[0]<<22;
nc2 = x[9]>>4;
nc2 |= x[8]<<2;
nc2 |= x[7]<<8;
nc2 |= x[6]<<14;
nc2 |= x[5]<<20;
nc2 |= (x[4]&0x03)<<26;
nc2 = x[9]>>4;
nc2 |= x[8]<<2;
nc2 |= x[7]<<8;
nc2 |= x[6]<<14;
nc2 |= x[5]<<20;
nc2 |= (x[4]&0x03)<<26;
ng = x[11];
ng |= x[10]<<6;
ng |= (x[9]&0x0F)<<12;
ng = x[11];
ng |= x[10]<<6;
ng |= (x[9]&0x0F)<<12;
*call1 = nc1;
*call2 = nc2;
*grid = ng;
*call1 = nc1;
*call2 = nc2;
*grid = ng;
}

View File

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

View File

@ -11,20 +11,32 @@ void qra65_enc_(int x[], int y[])
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:
// 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=1 [CQ ? ?] AP27
// rc=2 [CQ ? ] AP44
// rc=2 [CQ ? ] AP42
// rc=3 [CALL ? ?] AP29
// rc=4 [CALL ? ] AP45
// rc=4 [CALL ? ] AP44
// rc=5 [CALL CALL ?] AP57
int ncall=0xf70c238; //K1ABC
// int ncall=0x890c60c; //KA1ABC
int i;
static int ncall0=0;
int ncall;
int x[63],y[12];
ncall = *nmycall;
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);
}

View File

@ -121,6 +121,8 @@ program qra65sim
h=default_header(12000,npts)
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
write(fname,1002) ifile !Output filename
1002 format('000000_',i4.4)
@ -146,9 +148,8 @@ program qra65sim
call packmsg(msg,dgen,itype) !Pack message into 12 six-bit bytes
call qra65_enc(dgen,sent) !Encode using QRA65
! call qra65_dec(sent,dgen,ierr) !Decode (### for test only ###)
write(*,3001) sent
3001 format(21i3)
! write(*,3001) sent
!3001 format(21i3)
k=0
do j=1,nsym !Insert sync and data into itone()