kopia lustrzana https://github.com/jamescoxon/dl-fldigi
1665 wiersze
40 KiB
C++
1665 wiersze
40 KiB
C++
// ----------------------------------------------------------------------------
|
|
// psk.cxx -- psk modem
|
|
//
|
|
// Copyright (C) 2006-2010
|
|
// Dave Freese, W1HKJ
|
|
// Copyright (C) 2009-2010
|
|
// John Douyere, VK2ETA
|
|
//
|
|
// PSK-FEC and PSK-R modes contributed by VK2ETA
|
|
//
|
|
// This file is part of fldigi. Adapted from code contained in gmfsk
|
|
// source code distribution.
|
|
// gmfsk Copyright (C) 2001, 2002, 2003
|
|
// Tomi Manninen (oh2bns@sral.fi)
|
|
//
|
|
// Fldigi 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.
|
|
//
|
|
// Fldigi 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 fldigi. If not, see <http://www.gnu.org/licenses/>.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
|
|
#include "psk.h"
|
|
#include "main.h"
|
|
#include "fl_digi.h"
|
|
#include "trx.h"
|
|
#include "misc.h"
|
|
#include "waterfall.h"
|
|
#include "configuration.h"
|
|
#include "status.h"
|
|
#include "viewpsk.h"
|
|
#include "pskeval.h"
|
|
#include "modem.h"
|
|
#include "Viewer.h"
|
|
#include "macros.h"
|
|
|
|
extern waterfall *wf;
|
|
|
|
// Change the following for DCD low pass filter adjustment
|
|
#define SQLCOEFF 0.01
|
|
#define SQLDECAY 50
|
|
|
|
//=====================================================================
|
|
|
|
#define K 5
|
|
#define POLY1 0x17
|
|
#define POLY2 0x19
|
|
|
|
// PSK + FEC + INTERLEAVE
|
|
#define PSKR_K 7
|
|
#define PSKR_POLY1 0x6d
|
|
#define PSKR_POLY2 0x4f
|
|
|
|
#define SEPARATION 1.4 //separation between carriers expressed as a ratio to sc_bw
|
|
|
|
#define GALILEO_K 15
|
|
#define GALILEO_POLY1 046321
|
|
#define GALILEO_POLY2 051271
|
|
|
|
char pskmsg[80];
|
|
viewpsk *pskviewer = (viewpsk *)0;
|
|
|
|
void psk::tx_init(SoundBase *sc)
|
|
{
|
|
scard = sc;
|
|
for (int car = 0; car < numcarriers; car++) {
|
|
phaseacc[car] = 0;
|
|
prevsymbol[car] = cmplx (1.0, 0.0);
|
|
}
|
|
preamble = dcdbits;
|
|
if (_pskr) {
|
|
// MFSK based varicode instead of psk
|
|
shreg = 1;
|
|
shreg2 = 1;
|
|
} else {
|
|
shreg = 0;
|
|
shreg2 = 0;
|
|
}
|
|
videoText();
|
|
|
|
// interleaver
|
|
bitshreg = 0;
|
|
startpreamble = true;
|
|
|
|
symbols = 0;
|
|
acc_symbols = 0;
|
|
ovhd_symbols = 0;
|
|
|
|
}
|
|
|
|
void psk::rx_init()
|
|
{
|
|
for (int car = 0; car < numcarriers; car++) {
|
|
phaseacc[car] = 0;
|
|
prevsymbol[car] = cmplx (1.0, 0.0);
|
|
}
|
|
quality = cmplx (0.0, 0.0);
|
|
if (_pskr) {
|
|
// MFSK varicode instead of psk
|
|
shreg = 1;
|
|
shreg2 = 1;
|
|
} else {
|
|
shreg = 0;
|
|
shreg2 = 0;
|
|
}
|
|
dcdshreg = 0;
|
|
dcdshreg2 = 0;
|
|
dcd = 0;
|
|
bitclk = 0;
|
|
freqerr = 0.0;
|
|
if (mailserver && progdefaults.PSKmailSweetSpot) sigsearch = SIGSEARCH;
|
|
else sigsearch = 0;
|
|
put_MODEstatus(mode);
|
|
resetSN_IMD();
|
|
imdValid = false;
|
|
afcmetric = 0.0;
|
|
// interleaver, split incoming bit stream into two, one late by one bit
|
|
rxbitstate = 0;
|
|
fecmet = fecmet2 = 0;
|
|
if (Rxinlv) Rxinlv->flush();
|
|
if (Rxinlv2) Rxinlv2->flush();
|
|
|
|
}
|
|
|
|
|
|
void psk::restart()
|
|
{
|
|
if (numcarriers == 1)
|
|
pskviewer->restart(mode);
|
|
evalpsk->setbw(sc_bw);
|
|
}
|
|
|
|
void psk::init()
|
|
{
|
|
restart();
|
|
modem::init();
|
|
set_scope_mode(Digiscope::PHASE);
|
|
initSN_IMD();
|
|
snratio = 1.0;
|
|
imdratio = 0.001;
|
|
rx_init();
|
|
}
|
|
|
|
psk::~psk()
|
|
{
|
|
if (tx_shape) delete [] tx_shape;
|
|
if (enc) delete enc;
|
|
if (dec) delete dec;
|
|
// FEC 2nd Viterbi decoder
|
|
if (dec2) delete dec2;
|
|
|
|
for (int i = 0; i < MAX_CARRIERS; i++) {
|
|
if (fir1[i]) delete fir1[i];
|
|
if (fir2[i]) delete fir2[i];
|
|
}
|
|
if (snfilt) delete snfilt;
|
|
if (imdfilt) delete imdfilt;
|
|
if (::pskviewer == pskviewer)
|
|
::pskviewer = 0;
|
|
delete pskviewer;
|
|
delete evalpsk;
|
|
|
|
// Interleaver
|
|
if (Rxinlv) delete Rxinlv;
|
|
if (Rxinlv2) delete Rxinlv2;
|
|
if (Txinlv) delete Txinlv;
|
|
|
|
}
|
|
|
|
psk::psk(trx_mode pskmode) : modem()
|
|
{
|
|
cap |= CAP_AFC | CAP_AFC_SR;
|
|
|
|
mode = pskmode;
|
|
int isize = 5;
|
|
int idepth = 5; // 5x5x5 interleaver
|
|
|
|
numcarriers = 1;
|
|
|
|
switch (mode) {
|
|
case MODE_PSK31:
|
|
symbollen = 256;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 32;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK63:
|
|
symbollen = 128;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 64;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK125:
|
|
symbollen = 64;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 128;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK250:
|
|
symbollen = 32;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 256;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK500:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK1000:
|
|
symbollen = 8;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 128;
|
|
numcarriers = 1;
|
|
break;
|
|
|
|
case MODE_QPSK31:
|
|
symbollen = 256;
|
|
_qpsk = true;
|
|
_pskr = false;
|
|
dcdbits = 32;
|
|
cap |= CAP_REV;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_QPSK63:
|
|
symbollen = 128;
|
|
_qpsk = true;
|
|
_pskr = false;
|
|
dcdbits = 64;
|
|
cap |= CAP_REV;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_QPSK125:
|
|
symbollen = 64;
|
|
_qpsk = true;
|
|
_pskr = false;
|
|
dcdbits = 128;
|
|
cap |= CAP_REV;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_QPSK250:
|
|
symbollen = 32;
|
|
_qpsk = true;
|
|
_pskr = false;
|
|
dcdbits = 256;
|
|
cap |= CAP_REV;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_QPSK500:
|
|
symbollen = 16;
|
|
_qpsk = true;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
cap |= CAP_REV;
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK63F: // As per Multipsk (BPSK63 + FEC + MFSK Varicode)
|
|
symbollen = 128;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 64;
|
|
numcarriers = 1;
|
|
break;
|
|
|
|
case MODE_PSK125R:
|
|
symbollen = 64;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 128;
|
|
isize = 2;
|
|
idepth = 40; // 2x2x40 interleaver
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK250R:
|
|
symbollen = 32;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 256;
|
|
isize = 2;
|
|
idepth = 80; // 2x2x80 interleaver
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK500R:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 512;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 1;
|
|
break;
|
|
case MODE_PSK1000R:
|
|
symbollen = 8;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 512;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 1;
|
|
break;
|
|
|
|
// multi-carrier modems
|
|
case MODE_4X_PSK63R:
|
|
symbollen = 128;//PSK63
|
|
dcdbits = 128;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 4;
|
|
isize = 2;
|
|
idepth = 80; // 2x2x80 interleaver
|
|
break;
|
|
case MODE_5X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 5;
|
|
isize = 2;
|
|
idepth = 260; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_10X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 10;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_20X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 20;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_32X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 32;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_4X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 4;
|
|
isize = 2;
|
|
idepth = 80; // 2x2x80 interleaver
|
|
break;
|
|
case MODE_5X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 5;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_10X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 10;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_12X_PSK125:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 128;//512;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
numcarriers = 12;
|
|
break;
|
|
case MODE_12X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 12;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_16X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 16;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_2X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 2;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_3X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
dcdbits = 512;
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 3;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_5X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 5;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_6X_PSK250:
|
|
symbollen = 32;//PSK250
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
numcarriers = 6;
|
|
break;
|
|
case MODE_6X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 6;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_7X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 7;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_2X_PSK500:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
numcarriers = 2;
|
|
break;
|
|
case MODE_4X_PSK500:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
numcarriers = 4;
|
|
break;
|
|
|
|
case MODE_2X_PSK500R:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 2;
|
|
break;
|
|
case MODE_3X_PSK500R:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 3;
|
|
break;
|
|
case MODE_4X_PSK500R:
|
|
symbollen = 16;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 4;
|
|
break;
|
|
|
|
case MODE_2X_PSK800:
|
|
symbollen = 10;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
numcarriers = 2;
|
|
break;
|
|
case MODE_2X_PSK800R:
|
|
symbollen = 10;
|
|
_qpsk = false;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 2;
|
|
break;
|
|
|
|
case MODE_2X_PSK1000:
|
|
symbollen = 8;//PSK1000
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 1024;
|
|
numcarriers = 2;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_2X_PSK1000R:
|
|
symbollen = 8;//PSK1000
|
|
_qpsk = false;
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 2;
|
|
isize = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
default:
|
|
mode = MODE_PSK31;
|
|
symbollen = 256;
|
|
_qpsk = false;
|
|
_pskr = false;
|
|
dcdbits = 32;
|
|
numcarriers = 1;
|
|
}
|
|
|
|
//printf("%s: symlen %d, dcdbits %d, _qpsk %d, _pskr %d, numc %d\n",
|
|
//mode_info[mode].sname,
|
|
//symbollen, dcdbits, _qpsk, _pskr, numcarriers);
|
|
|
|
enc = (encoder *)0;
|
|
dec = (viterbi *)0;
|
|
// BPSK+FEC - 2nd Viterbi decoder and de-interleaver
|
|
dec2 = (viterbi *)0;
|
|
Txinlv = (interleave *)0;
|
|
Rxinlv = (interleave *)0;
|
|
Rxinlv2 = (interleave *)0;
|
|
|
|
|
|
// create impulse response for experimental FIR filters
|
|
double fir1c[64];
|
|
double fir2c[64];
|
|
|
|
for (int i = 0; i < MAX_CARRIERS; i++) {
|
|
if (i < numcarriers) {
|
|
fir1[i] = new C_FIR_filter();
|
|
fir2[i] = new C_FIR_filter();
|
|
} else {
|
|
fir1[i] = (C_FIR_filter *)0;
|
|
fir2[i] = (C_FIR_filter *)0;
|
|
}
|
|
}
|
|
switch (progdefaults.PSK_filter) {
|
|
case 1:
|
|
// use the original gmfsk matched filters
|
|
for (int i = 0; i < 64; i++) {
|
|
fir1c[i] = gmfir1c[i];
|
|
fir2c[i] = gmfir2c[i];
|
|
}
|
|
for (int i = 0; i < numcarriers; i++) {
|
|
fir1[i]->init(FIRLEN, symbollen > 15 ? symbollen / 16 : 1, fir1c, fir1c);
|
|
fir2[i]->init(FIRLEN, 1, fir2c, fir2c);
|
|
}
|
|
break;
|
|
case 2:
|
|
// creates fir1c matched sin(x)/x filter w hamming
|
|
wsincfilt(fir1c, 1.0 / symbollen, false);
|
|
// creates fir2c matched sin(x)/x filter w hamming
|
|
wsincfilt(fir2c, 1.0 / 16.0, false);
|
|
for (int i = 0; i < numcarriers; i++) {
|
|
fir1[i]->init(FIRLEN, symbollen > 15 ? symbollen / 16 : 1, fir1c, fir1c);
|
|
fir2[i]->init(FIRLEN, 1, fir2c, fir2c);
|
|
}
|
|
break;
|
|
case 3:
|
|
// creates fir1c matched sin(x)/x filter w hamming
|
|
wsincfilt(fir1c, 1.0 / symbollen, false);
|
|
// 1/22 with Hamming window nearly identical to gmfir2c
|
|
wsincfilt(fir2c, 1.0 / 22.0, false);
|
|
for (int i = 0; i < numcarriers; i++) {
|
|
fir1[i]->init(FIRLEN, symbollen > 15 ? symbollen / 16 : 1, fir1c, fir1c);
|
|
fir2[i]->init(FIRLEN, 1, fir2c, fir2c);
|
|
}
|
|
break;
|
|
case 4:
|
|
wsincfilt(fir2c, 1.5 / 16.0, true);
|
|
for (int i = 0; i < numcarriers; i++) {
|
|
fir1[i]->init_lowpass (FIRLEN, 16, 1.5 / symbollen);
|
|
fir2[i]->init(FIRLEN, 1, fir2c, fir2c);
|
|
}
|
|
case 0:
|
|
default :
|
|
// creates fir1c matched sin(x)/x filter w blackman
|
|
wsincfilt(fir1c, 1.0 / symbollen, true);
|
|
// creates fir2c matched sin(x)/x filter w blackman
|
|
wsincfilt(fir2c, 1.0 / 16.0, true);
|
|
for (int i = 0; i < numcarriers; i++) {
|
|
fir1[i]->init(FIRLEN, symbollen > 15 ? symbollen / 16 : 1, fir1c, fir1c);
|
|
fir2[i]->init(FIRLEN, 1, fir2c, fir2c);
|
|
}
|
|
}
|
|
|
|
snfilt = new Cmovavg(16);
|
|
imdfilt = new Cmovavg(16);
|
|
|
|
if (_qpsk) {
|
|
enc = new encoder(K, POLY1, POLY2);
|
|
dec = new viterbi(K, POLY1, POLY2);
|
|
}
|
|
|
|
if (_pskr) {
|
|
// FEC for BPSK. Use a 2nd Viterbi decoder for comparison.
|
|
// Set decode size to 4 since some characters can be as small
|
|
// as 3 bits long. This minimises intercharacters decoding
|
|
// interactions.
|
|
enc = new encoder(PSKR_K, PSKR_POLY1, PSKR_POLY2);
|
|
dec = new viterbi(PSKR_K, PSKR_POLY1, PSKR_POLY2);
|
|
dec->setchunksize(4);
|
|
dec2 = new viterbi(PSKR_K, PSKR_POLY1, PSKR_POLY2);
|
|
dec2->setchunksize(4);
|
|
|
|
// Interleaver. To maintain constant time delay between bits,
|
|
// we double the number of concatenated square iterleavers for
|
|
// each doubling of speed: 2x2x20 for BSK63+FEC, 2x2x40 for
|
|
// BPSK125+FEC, etc..
|
|
if (_pskr && (mode != MODE_PSK63F)) {
|
|
// 2x2x(20,40,80,160)
|
|
Txinlv = new interleave (isize, idepth, INTERLEAVE_FWD);//numinterleavers, INTERLEAVE_FWD);
|
|
// 2x2x(20,40,80,160)
|
|
Rxinlv = new interleave (isize, idepth, INTERLEAVE_REV);//numinterleavers, INTERLEAVE_REV);
|
|
// 2x2x(20,40,80,160)
|
|
Rxinlv2 = new interleave (isize, idepth, INTERLEAVE_REV);//numinterleavers, INTERLEAVE_REV);
|
|
}
|
|
bitshreg = 0;
|
|
rxbitstate = 0;
|
|
startpreamble = true;
|
|
}
|
|
|
|
tx_shape = new double[symbollen];
|
|
|
|
// raised cosine shape for the transmitter
|
|
for ( int i = 0; i < symbollen; i++)
|
|
tx_shape[i] = 0.5 * cos(i * M_PI / symbollen) + 0.5;
|
|
|
|
samplerate = PskSampleRate;
|
|
fragmentsize = symbollen;
|
|
sc_bw = samplerate / symbollen;
|
|
//JD added for multiple carriers
|
|
inter_carrier = SEPARATION * sc_bw;
|
|
bandwidth = sc_bw * ( 1 + SEPARATION * (numcarriers - 1));
|
|
|
|
snratio = s2n = imdratio = imd = 0;
|
|
|
|
if (mailserver && progdefaults.PSKmailSweetSpot)
|
|
sigsearch = SIGSEARCH;
|
|
else
|
|
sigsearch = 0;
|
|
for (int i = 0; i < 16; i++)
|
|
syncbuf[i] = 0.0;
|
|
E1 = E2 = E3 = 0.0;
|
|
acquire = 0;
|
|
|
|
evalpsk = new pskeval;
|
|
if (numcarriers == 1) {
|
|
::pskviewer = pskviewer = new viewpsk(evalpsk, mode);
|
|
} else
|
|
::pskviewer = pskviewer = 0;
|
|
|
|
}
|
|
|
|
//=============================================================================
|
|
//=========================== psk31 receive routines ==========================
|
|
//=============================================================================
|
|
|
|
|
|
void psk::s2nreport(void)
|
|
{
|
|
modem::s2nreport();
|
|
s2n_sum = s2n_sum2 = s2n_ncount = 0.0;
|
|
}
|
|
|
|
void psk::rx_bit(int bit)
|
|
{
|
|
int c;
|
|
|
|
shreg = (shreg << 1) | !!bit;
|
|
if (_pskr) {
|
|
// MFSK varicode instead of PSK Varicode
|
|
if ((shreg & 7) == 1) {
|
|
c = varidec(shreg >> 1);
|
|
// Voting at the character level
|
|
if (fecmet >= fecmet2) {
|
|
if ((c != -1) && (c != 0) && (dcd == true)) {
|
|
put_rx_char(c);
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
|
|
s2n_sum += s2n_metric;
|
|
s2n_sum2 += (s2n_metric * s2n_metric);
|
|
s2n_ncount ++;
|
|
if (c == EOT)
|
|
s2nreport();
|
|
}
|
|
}
|
|
}
|
|
shreg = 1;
|
|
}
|
|
} else {
|
|
if ((shreg & 3) == 0) {
|
|
c = psk_varicode_decode(shreg >> 2);
|
|
if ((c != -1) && (dcd == true)) {
|
|
put_rx_char(c);
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
|
|
s2n_sum += s2n_metric;
|
|
s2n_sum2 += (s2n_metric * s2n_metric);
|
|
s2n_ncount++;
|
|
if (c == EOT)
|
|
s2nreport();
|
|
}
|
|
}
|
|
shreg = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void psk::rx_bit2(int bit)
|
|
{
|
|
int c;
|
|
|
|
shreg2 = (shreg2 << 1) | !!bit;
|
|
// MFSK varicode instead of PSK Varicode
|
|
if ((shreg2 & 7) == 1) {
|
|
c = varidec(shreg2 >> 1);
|
|
// Voting at the character level
|
|
if (fecmet < fecmet2) {
|
|
if ((c != -1) && (c != 0) && (dcd == true)) {
|
|
put_rx_char(c);
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
|
|
s2n_sum += s2n_metric;
|
|
s2n_sum2 += (s2n_metric * s2n_metric);
|
|
s2n_ncount++;
|
|
if (c == EOT)
|
|
s2nreport();
|
|
}
|
|
}
|
|
}
|
|
shreg2 = 1;
|
|
}
|
|
}
|
|
|
|
void psk::rx_qpsk(int bits)
|
|
{
|
|
unsigned char sym[2];
|
|
int c;
|
|
|
|
if (_qpsk && !reverse)
|
|
bits = (4 - bits) & 3;
|
|
|
|
sym[0] = (bits & 1) ? 255 : 0;
|
|
sym[1] = (bits & 2) ? 0 : 255; // top bit is flipped
|
|
|
|
//JD added de-interleaver
|
|
// Rxinlv->symbols(sym);
|
|
|
|
c = dec->decode(sym, NULL);
|
|
|
|
if (c != -1) {
|
|
rx_bit(c & 0x80);
|
|
rx_bit(c & 0x40);
|
|
rx_bit(c & 0x20);
|
|
rx_bit(c & 0x10);
|
|
rx_bit(c & 0x08);
|
|
rx_bit(c & 0x04);
|
|
rx_bit(c & 0x02);
|
|
rx_bit(c & 0x01);
|
|
}
|
|
}
|
|
|
|
void psk::rx_pskr(unsigned char symbol)
|
|
{
|
|
int met;
|
|
unsigned char twosym[2];
|
|
unsigned char tempc;
|
|
int c;
|
|
|
|
//In the case of multiple carriers, if even number of carriers then we
|
|
// know the bit-order and don't need voting otherwise
|
|
// we accumulate the soft bits for the interleaver THEN submit to Viterbi
|
|
// decoder in alternance so that each one is processed one bit later.
|
|
// Only two possibilities for sync: current bit or previous one since
|
|
// we encode with R = 1/2 and send encoded bits one after the other
|
|
// through the interleaver.
|
|
|
|
symbolpair[1] = symbolpair[0];
|
|
symbolpair[0] = symbol;
|
|
|
|
|
|
if (rxbitstate == 0) {
|
|
rxbitstate++;
|
|
//Only use one decoder is using even carriers (we know the bits order)
|
|
// if (((int)numcarriers) % 2 == 0) {
|
|
// fecmet2 = -9999.0;
|
|
// return;
|
|
// }
|
|
// copy to avoid scrambling symbolpair for the next bit
|
|
twosym[0] = symbolpair[0];
|
|
twosym[1] = symbolpair[1];
|
|
// De-interleave for Robust modes only
|
|
if (mode != MODE_PSK63F) Rxinlv2->symbols(twosym);
|
|
// pass de-interleaved bits pair to the decoder, reversed
|
|
tempc = twosym[1];
|
|
twosym[1] = twosym[0];
|
|
twosym[0] = tempc;
|
|
// Then viterbi decoder
|
|
c = dec2->decode(twosym, &met);
|
|
if (c != -1) {
|
|
// FEC only take metric measurement after backtrace
|
|
// Will be used for voting between the two decoded streams
|
|
fecmet2 = decayavg(fecmet2, met, 20);
|
|
rx_bit2(c & 0x08);
|
|
rx_bit2(c & 0x04);
|
|
rx_bit2(c & 0x02);
|
|
rx_bit2(c & 0x01);
|
|
}
|
|
} else {
|
|
// Again for the same stream shifted by one bit
|
|
rxbitstate = 0;
|
|
twosym[0] = symbolpair[0];
|
|
twosym[1] = symbolpair[1];
|
|
// De-interleave
|
|
if (mode != MODE_PSK63F) Rxinlv->symbols(twosym);
|
|
tempc = twosym[1];
|
|
twosym[1] = twosym[0];
|
|
twosym[0] = tempc;
|
|
// Then viterbi decoder
|
|
c = dec->decode(twosym, &met);
|
|
if (c != -1) {
|
|
fecmet = decayavg(fecmet, met, 20);
|
|
rx_bit(c & 0x08);
|
|
rx_bit(c & 0x04);
|
|
rx_bit(c & 0x02);
|
|
rx_bit(c & 0x01);
|
|
}
|
|
}
|
|
}
|
|
|
|
void psk::searchDown()
|
|
{
|
|
double srchfreq = frequency - sc_bw * 2;
|
|
double minfreq = sc_bw * 2;
|
|
double spwr, npwr;
|
|
while (srchfreq > minfreq) {
|
|
spwr = wf->powerDensity(srchfreq, sc_bw);
|
|
npwr = wf->powerDensity(srchfreq + sc_bw, sc_bw/2) + 1e-10;
|
|
if (spwr / npwr > pow(10, progdefaults.ServerACQsn / 10)) {
|
|
frequency = srchfreq;
|
|
set_freq(frequency);
|
|
sigsearch = SIGSEARCH;
|
|
break;
|
|
}
|
|
srchfreq -= sc_bw;
|
|
}
|
|
}
|
|
|
|
void psk::searchUp()
|
|
{
|
|
double srchfreq = frequency + sc_bw * 2;
|
|
double maxfreq = IMAGE_WIDTH - sc_bw * 2;
|
|
double spwr, npwr;
|
|
while (srchfreq < maxfreq) {
|
|
spwr = wf->powerDensity(srchfreq, sc_bw/2);
|
|
npwr = wf->powerDensity(srchfreq - sc_bw, sc_bw/2) + 1e-10;
|
|
if (spwr / npwr > pow(10, progdefaults.ServerACQsn / 10)) {
|
|
frequency = srchfreq;
|
|
set_freq(frequency);
|
|
sigsearch = SIGSEARCH;
|
|
break;
|
|
}
|
|
srchfreq += sc_bw;
|
|
}
|
|
}
|
|
|
|
int waitcount = 0;
|
|
|
|
void psk::findsignal()
|
|
{
|
|
int ftest, f1, f2;
|
|
|
|
if (sigsearch > 0) {
|
|
sigsearch--;
|
|
if (mailserver) { // mail server search algorithm
|
|
if (progdefaults.PSKmailSweetSpot) {
|
|
f1 = (int)(progdefaults.ServerCarrier - progdefaults.ServerOffset);
|
|
f2 = (int)(progdefaults.ServerCarrier + progdefaults.ServerOffset);
|
|
} else {
|
|
f1 = (int)(frequency - progdefaults.ServerOffset);
|
|
f2 = (int)(frequency + progdefaults.ServerOffset);
|
|
}
|
|
if (evalpsk->sigpeak(ftest, f1, f2) > pow(10, progdefaults.ServerACQsn / 10) ) {
|
|
if (progdefaults.PSKmailSweetSpot) {
|
|
if (fabs(ftest - progdefaults.ServerCarrier) < progdefaults.ServerOffset) {
|
|
frequency = ftest;
|
|
set_freq(frequency);
|
|
freqerr = 0.0;
|
|
} else {
|
|
frequency = progdefaults.ServerCarrier;
|
|
set_freq(frequency);
|
|
freqerr = 0.0;
|
|
}
|
|
} else {
|
|
frequency = ftest;
|
|
set_freq(frequency);
|
|
freqerr = 0.0;
|
|
}
|
|
} else { // less than the detection threshold
|
|
if (progdefaults.PSKmailSweetSpot) {
|
|
frequency = progdefaults.ServerCarrier;
|
|
set_freq(frequency);
|
|
sigsearch = SIGSEARCH;
|
|
}
|
|
}
|
|
} else { // normal signal search algorithm
|
|
f1 = (int)(frequency - progdefaults.SearchRange/2);
|
|
f2 = (int)(frequency + progdefaults.SearchRange/2);
|
|
if (evalpsk->sigpeak(ftest, f1, f2) > pow(10, progdefaults.ACQsn / 10.0) ) {
|
|
frequency = ftest;
|
|
set_freq(frequency);
|
|
freqerr = 0.0;
|
|
sigsearch = 0;
|
|
acquire = dcdbits;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//JD: disable for multiple carriers as we are running as modem and
|
|
// therefore use other strategies for frequency alignment like RSID
|
|
void psk::phaseafc()
|
|
{
|
|
double error;
|
|
if (afcmetric < 0.05 ||
|
|
mode == MODE_PSK500 ||
|
|
mode == MODE_QPSK500 ||
|
|
numcarriers > 1 ) return;
|
|
|
|
error = (phase - bits * M_PI / 2.0);
|
|
if (error < -M_PI / 2.0 || error > M_PI / 2.0) return;
|
|
error *= samplerate / (TWOPI * symbollen);
|
|
if (fabs(error) < sc_bw ) {
|
|
freqerr = error / dcdbits;
|
|
frequency -= freqerr;
|
|
if (mailserver) {
|
|
if (frequency < progdefaults.ServerCarrier - progdefaults.ServerAFCrange)
|
|
frequency = progdefaults.ServerCarrier - progdefaults.ServerAFCrange;
|
|
if (frequency > progdefaults.ServerCarrier + progdefaults.ServerAFCrange)
|
|
frequency = progdefaults.ServerCarrier + progdefaults.ServerAFCrange;
|
|
}
|
|
set_freq (frequency);
|
|
}
|
|
if (acquire) acquire--;
|
|
}
|
|
|
|
void psk::afc()
|
|
{
|
|
if (!progStatus.afconoff)
|
|
return;
|
|
if (dcd == true || acquire)
|
|
phaseafc();
|
|
}
|
|
|
|
|
|
void psk::rx_symbol(cmplx symbol, int car)
|
|
{
|
|
int n;
|
|
unsigned char softbit = 0;
|
|
double softangle;
|
|
double softamp;
|
|
double sigamp = norm(symbol);
|
|
|
|
static double averageamp;
|
|
|
|
phase = arg ( conj(prevsymbol[car]) * symbol );
|
|
prevsymbol[car] = symbol;
|
|
|
|
if (phase < 0) phase += TWOPI;
|
|
|
|
if (_qpsk) {
|
|
bits = ((int) (phase / M_PI_2 + 0.5)) & 3;
|
|
n = 4;
|
|
} else { // bpsk and pskr
|
|
bits = (((int) (phase / M_PI + 0.5)) & 1) << 1;
|
|
// hard decode if needed
|
|
// softbit = (bits & 2) ? 0 : 255;
|
|
// reversed as we normally pass "!bits" when hard decoding
|
|
|
|
// Soft decode section below
|
|
averageamp = decayavg(averageamp, sigamp, SQLDECAY);
|
|
if (sigamp > 0 && averageamp > 0) {
|
|
if (sigamp > averageamp) {
|
|
softamp = clamp( sqrt(sigamp / averageamp), 1.0, 1e6);
|
|
} else {
|
|
softamp = clamp( sqrt(averageamp / sigamp), 1.0, 1e6);
|
|
}
|
|
} else {
|
|
softamp = 2; // arbritary number (50% impact)
|
|
}
|
|
// Compute values between -128 and +127 for phase value only
|
|
double alpha = phase / M_PI;
|
|
if (alpha > 1.0) alpha = 2.0 - alpha;
|
|
softangle = 127.0 - 255.0 * alpha;
|
|
softbit = (unsigned char) ((softangle / ( 1.0 + softamp / 2.0)) + 128);
|
|
|
|
n = 2;
|
|
}
|
|
|
|
// simple low pass filter for quality of signal
|
|
quality = cmplx(
|
|
decayavg(quality.real(), cos(n*phase), _pskr ? SQLDECAY * 10 : SQLDECAY),
|
|
decayavg(quality.imag(), sin(n*phase), _pskr ? SQLDECAY * 10 : SQLDECAY));
|
|
metric = 100.0 * norm(quality);
|
|
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
|
|
//s2n reporting: rescale depending on mode, clip after scaling
|
|
if (_pskr)
|
|
s2n_metric = metric * 1.5 + 8;
|
|
else
|
|
s2n_metric = metric;
|
|
s2n_metric = CLAMP(s2n_metric, 0.0, 100.0);
|
|
}
|
|
|
|
// FEC: adjust squelch for extra sensitivity.
|
|
// Otherwise we miss good characters
|
|
if (_pskr) {
|
|
metric = metric * 4;
|
|
}
|
|
|
|
if (metric > 100)
|
|
metric = 100;
|
|
|
|
afcmetric = decayavg(afcmetric, norm(quality), 50);
|
|
|
|
dcdshreg = (dcdshreg << 2) | bits;
|
|
|
|
imdValid = false;
|
|
switch (dcdshreg) {
|
|
|
|
case 0xAAAAAAAA: // DCD on by preamble for psk modes
|
|
if (!_pskr) {
|
|
dcd = true;
|
|
acquire = 0;
|
|
quality = cmplx (1.0, 0.0);
|
|
imdValid = true;
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient))
|
|
s2n_sum = s2n_sum2 = s2n_ncount = 0.0;
|
|
}
|
|
break;
|
|
|
|
case 0xA0A0A0A0: // DCD on by preamble for PSKR modes ("11001100" sequence sent as preamble)
|
|
if (_pskr) {
|
|
dcd = true;
|
|
acquire = 0;
|
|
quality = cmplx (1.0, 0.0);
|
|
imdValid = true;
|
|
//VK2ETA added logic to prevent resetting
|
|
// noSOHyet = true;
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient))
|
|
s2n_sum = s2n_sum2 = s2n_ncount = 0.0;
|
|
}
|
|
break;
|
|
|
|
case 0: // DCD off by postamble. Not for PSKR modes as this is not unique to postamble.
|
|
if (!_pskr) {
|
|
dcd = false;
|
|
acquire = 0;
|
|
quality = cmplx (0.0, 0.0);
|
|
}
|
|
break;
|
|
default:
|
|
if (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == false) {
|
|
dcd = true;
|
|
} else {
|
|
dcd = false;
|
|
}
|
|
}
|
|
|
|
if (!_pskr) {
|
|
set_phase(phase, norm(quality), dcd);
|
|
|
|
if (dcd == true) {
|
|
if (_qpsk )
|
|
rx_qpsk(bits);
|
|
else
|
|
rx_bit(!bits);
|
|
}
|
|
} else { // pskr processing
|
|
// FEC: moved below the rx_bit to use proper value for dcd
|
|
rx_pskr(softbit);
|
|
set_phase(phase, norm(quality), dcd);
|
|
}
|
|
|
|
}
|
|
|
|
void psk::signalquality()
|
|
{
|
|
|
|
if (m_Energy[1])
|
|
snratio = snfilt->run(m_Energy[0]/m_Energy[1]);
|
|
else
|
|
snratio = snfilt->run(1.0);
|
|
|
|
if (m_Energy[0] && imdValid)
|
|
imdratio = imdfilt->run(m_Energy[2]/m_Energy[0]);
|
|
else
|
|
imdratio = imdfilt->run(0.001);
|
|
|
|
}
|
|
|
|
void psk::update_syncscope()
|
|
{
|
|
static char msg1[15];
|
|
static char msg2[15];
|
|
|
|
display_metric(metric);
|
|
|
|
s2n = 10.0*log10( snratio );
|
|
snprintf(msg1, sizeof(msg1), "s/n %2d dB", (int)(floor(s2n)));
|
|
|
|
imd = 10.0*log10( imdratio );
|
|
snprintf(msg2, sizeof(msg2), "imd %3d dB", (int)(floor(imd)));
|
|
|
|
if (imdValid) {
|
|
put_Status1(msg1, progdefaults.StatusTimeout, progdefaults.StatusDim ? STATUS_DIM : STATUS_CLEAR);
|
|
put_Status2(msg2, progdefaults.StatusTimeout, progdefaults.StatusDim ? STATUS_DIM : STATUS_CLEAR);
|
|
}
|
|
}
|
|
|
|
char bitstatus[100];
|
|
|
|
int psk::rx_process(const double *buf, int len)
|
|
{
|
|
double delta[MAX_CARRIERS], frequencies[MAX_CARRIERS];
|
|
cmplx z, z2[MAX_CARRIERS];
|
|
bool can_rx_symbol = false;
|
|
|
|
if (numcarriers == 1) {
|
|
if (!progdefaults.report_when_visible ||
|
|
dlgViewer->visible() || progStatus.show_channels )
|
|
if (pskviewer && !bHistory) pskviewer->rx_process(buf, len);
|
|
if (evalpsk)
|
|
evalpsk->sigdensity();
|
|
}
|
|
|
|
frequencies[0] = frequency + ((-1 * numcarriers) + 1) * inter_carrier / 2;
|
|
delta[0] = TWOPI * frequencies[0] / samplerate;
|
|
for (int car = 1; car < numcarriers; car++) {
|
|
frequencies[car] = frequencies[car - 1] + inter_carrier;
|
|
delta[car] = TWOPI * frequencies[car] / samplerate;
|
|
}
|
|
|
|
while (len-- > 0) {
|
|
|
|
for (int car = 0; car < numcarriers; car++) {
|
|
|
|
// Mix with the internal NCO
|
|
z = cmplx ( *buf * cos(phaseacc[car]), *buf * sin(phaseacc[car]) );
|
|
|
|
phaseacc[car] += delta[car];
|
|
if (phaseacc[car] > TWOPI) phaseacc[car] -= TWOPI;
|
|
|
|
// Filter and downsample
|
|
// by 16 (psk31, qpsk31)
|
|
// by 8 (psk63, qpsk63)
|
|
// by 4 (psk125, qpsk125)
|
|
// by 2 (psk250, qpsk250)
|
|
// by 1 (psk500, qpsk500) = no down sampling
|
|
// first filter
|
|
if (fir1[car]->run( z, z )) { // fir1 returns true every Nth sample
|
|
// final filter
|
|
fir2[car]->run( z, z2[car] ); // fir2 returns value on every sample
|
|
|
|
//On last carrier processing
|
|
if (car == numcarriers - 1) {
|
|
|
|
calcSN_IMD(z); //JD OR all carriers together check logic???
|
|
|
|
/**
|
|
* This is the symbol timing recovery mechanism. After the demodulated
|
|
* signal is processed by the matched filters, the signal lobes are
|
|
* expected to have been modified to a fairly symmetric shape. The
|
|
* magnitude of the samples are taken, thus rectifying the signal to
|
|
* positive values. "bitclk" is a counter that is very close in rate to
|
|
* (samples / symbol). Its purpose is to repeatedly "draw" one symbol
|
|
* waveform in the syncbuf array, according to its amplitude (not phase).
|
|
*/
|
|
|
|
int idx = (int) bitclk;
|
|
double sum = 0.0;
|
|
double ampsum = 0.0;
|
|
for (int ii = 0; ii < numcarriers; ii++) {
|
|
sum += abs(z2[ii])/numcarriers;
|
|
}
|
|
// syncbuf[idx] = 0.8 * syncbuf[idx] + 0.2 * z2[car].mag();
|
|
syncbuf[idx] = 0.8 * syncbuf[idx] + 0.2 * sum;
|
|
sum = 0.0;
|
|
|
|
//Handling of modes faster than PSK500/PSK500R
|
|
|
|
/* for (int i = 0; i < 8; i++) {
|
|
sum += (syncbuf[i] - syncbuf[i+8]);
|
|
ampsum += (syncbuf[i] + syncbuf[i+8]);
|
|
}
|
|
*/
|
|
// double bitsteps = (symbollen >= 16 ? 16 : 8);
|
|
double bitsteps = (symbollen >= 16 ? 16 : symbollen);
|
|
int symsteps = (int) (bitsteps / 2);
|
|
|
|
/**
|
|
* Here we sum up the difference between each sample's magnitude in the
|
|
* lower half of the array with its counterpart on the upper half of the
|
|
* array, or the other side of the waveform. Each pair's difference is
|
|
* divided by their sum, scaling it so that the signal amplitude does not
|
|
* affect the result. When the differences are summed, it gives an
|
|
* indication of which side is larger than the other.
|
|
*/
|
|
|
|
for (int i = 0; i < symsteps; i++) {
|
|
sum += (syncbuf[i] - syncbuf[i+symsteps]);
|
|
ampsum += (syncbuf[i] + syncbuf[i+symsteps]);
|
|
}
|
|
// added correction as per PocketDigi
|
|
sum = (ampsum == 0 ? 0 : sum / ampsum);
|
|
|
|
/**
|
|
* If the lower side is larger (meaning that the waveform is shifted in that
|
|
* direction), then the sum is negative, and bitclk needs to be adjusted to
|
|
* be a little faster, so that the next drawing of the waveform in syncbuf
|
|
* will be shifted right. Conversely, if the sum is positive, then it needs
|
|
* to slow down bitclk so that the waveform is shifted left. Thus the
|
|
* error is subtracted from bitclk, rather than added. The goal is to
|
|
* get the error as close to zero as possible, so that the receiver is
|
|
* exactly synced with the transmitter and the waveform is exactly in
|
|
* the middle of syncbuf.
|
|
*/
|
|
|
|
// bitclk -= sum / 5.0;
|
|
bitclk -= sum / (5.0 * 16 / bitsteps);
|
|
bitclk += 1;
|
|
|
|
/**
|
|
* When bitclock reaches the end of the buffer, then a complete waveform
|
|
* has been received. It is time to output the current sample and wrap
|
|
* around to the next cycle.
|
|
*
|
|
* There is a complete symbol waveform in syncbuf, so that each
|
|
* sample[0..N/2-1] is very close in amplitude with the corresponding
|
|
* sample in [N/2..N-1].
|
|
*
|
|
* | ******** ******** |
|
|
* | **** **** **** **** |
|
|
* | *** *** *** *** |
|
|
* | ** ** ** ** |
|
|
* | * * * * |
|
|
* | * * * * |
|
|
* |* * *|
|
|
* |_______________________________________________________________|
|
|
* 0 N/2 N-1
|
|
*
|
|
* === or some variation of it .... ===
|
|
*
|
|
* |**** ******** *****|
|
|
* | **** **** **** **** |
|
|
* | *** *** *** *** |
|
|
* | ** ** ** ** |
|
|
* | * * * * |
|
|
* | * * * * |
|
|
* | * * |
|
|
* |_______________________________________________________________|
|
|
* 0 N/2 N-1
|
|
*
|
|
* At the end of this cycle, bitclk is pointing at a sample which will
|
|
* have the maximum phase difference, if any, from the previous symbol's
|
|
* phase.
|
|
*
|
|
*/
|
|
|
|
// if (bitclk < 0) bitclk += 16.0;
|
|
// if (bitclk >= 16.0) {
|
|
// bitclk -= 16.0;
|
|
|
|
if (bitclk < 0) bitclk += bitsteps;
|
|
if (bitclk >= bitsteps) {
|
|
bitclk -= bitsteps;
|
|
can_rx_symbol = true;
|
|
update_syncscope();
|
|
afc();
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if (can_rx_symbol) {
|
|
for (int car = 0; car < numcarriers; car++) {
|
|
rx_symbol(z2[car], car);
|
|
}
|
|
can_rx_symbol = false;
|
|
}
|
|
buf++;
|
|
}
|
|
|
|
if (sigsearch)
|
|
findsignal();
|
|
else if (mailserver) {
|
|
if (waitcount > 0) {
|
|
--waitcount;
|
|
if (waitcount == 0) {
|
|
if (progdefaults.PSKmailSweetSpot) {
|
|
frequency = progdefaults.PSKsweetspot;
|
|
set_freq(frequency);
|
|
}
|
|
sigsearch = SIGSEARCH;
|
|
}
|
|
}
|
|
else if ( E1/ E2 <= 1.0) {
|
|
waitcount = 8;
|
|
sigsearch = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//=====================================================================
|
|
// transmit processes
|
|
//=====================================================================
|
|
|
|
void psk::tx_carriers()
|
|
{
|
|
double delta[MAX_CARRIERS];
|
|
double ival, qval, shapeA, shapeB;
|
|
cmplx symbol;
|
|
double frequencies[MAX_CARRIERS];
|
|
|
|
//Process all carrier's symbols, then submit to sound card
|
|
frequencies[0] = get_txfreq_woffset() + ((-1 * numcarriers) + 1) * inter_carrier / 2;
|
|
delta[0] = TWOPI * frequencies[0] / samplerate;
|
|
for (int car = 1; car < symbols; car++) {
|
|
frequencies[car] = frequencies[car - 1] + inter_carrier;
|
|
delta[car] = TWOPI * frequencies[car] / samplerate;
|
|
}
|
|
|
|
double maxamp = 0;
|
|
int sym;
|
|
for (int car = 0; car < symbols; car++) {
|
|
sym = txsymbols[car];
|
|
|
|
if (_qpsk && !reverse)
|
|
sym = (4 - sym) & 3;
|
|
|
|
// differential QPSK modulation - top bit flipped
|
|
switch (sym) {
|
|
case 0:
|
|
symbol = cmplx (-1.0, 0.0); // 180 degrees
|
|
break;
|
|
case 1:
|
|
symbol = cmplx (0.0, -1.0); // 270 degrees
|
|
break;
|
|
case 2:
|
|
symbol = cmplx (1.0, 0.0); // 0 degrees
|
|
break;
|
|
case 3:
|
|
symbol = cmplx (0.0, 1.0); // 90 degrees
|
|
break;
|
|
}
|
|
symbol = prevsymbol[car] * symbol; // cmplx multiplication
|
|
|
|
for (int i = 0; i < symbollen; i++) {
|
|
|
|
shapeA = tx_shape[i];
|
|
shapeB = (1.0 - shapeA);
|
|
|
|
ival = shapeA * prevsymbol[car].real() + shapeB * symbol.real();
|
|
qval = shapeA * prevsymbol[car].imag() + shapeB * symbol.imag();
|
|
|
|
if (car != 0) {
|
|
outbuf[i] += (ival * cos(phaseacc[car]) + qval * sin(phaseacc[car]));// / numcarriers;
|
|
} else {
|
|
outbuf[i] = (ival * cos(phaseacc[car]) + qval * sin(phaseacc[car]));// / numcarriers;
|
|
}
|
|
if (maxamp < fabs(outbuf[i])) {
|
|
maxamp = fabs(outbuf[i]);
|
|
}
|
|
|
|
phaseacc[car] += delta[car];
|
|
if (phaseacc[car] > TWOPI) phaseacc[car] -= TWOPI;
|
|
}
|
|
|
|
prevsymbol[car] = symbol;
|
|
}
|
|
|
|
if (maxamp)
|
|
for (int i = 0; i < symbollen; i++) outbuf[i] /= maxamp;
|
|
ModulateXmtr(outbuf, symbollen);
|
|
}
|
|
|
|
void psk::tx_symbol(int sym)
|
|
{
|
|
acc_symbols++;
|
|
txsymbols[symbols] = sym;
|
|
if (++symbols < numcarriers) {
|
|
return;
|
|
}
|
|
tx_carriers();
|
|
symbols = 0; //reset
|
|
}
|
|
|
|
void psk::tx_bit(int bit)
|
|
{
|
|
unsigned int sym;
|
|
|
|
// qpsk transmission
|
|
if (_qpsk) {
|
|
sym = enc->encode(bit);
|
|
sym = sym & 3;//JD just to make sure
|
|
tx_symbol(sym);
|
|
} else if (_pskr) {
|
|
// Encode into two bits
|
|
bitshreg = enc->encode(bit);
|
|
// pass through interleaver
|
|
if (mode != MODE_PSK63F) Txinlv->bits(&bitshreg);
|
|
// Send low bit first. tx_symbol expects 0 or 2 for BPSK
|
|
sym = (bitshreg & 1) << 1;
|
|
tx_symbol(sym);
|
|
sym = bitshreg & 2;
|
|
tx_symbol(sym);
|
|
// else normal bpsk tranmission
|
|
} else {
|
|
sym = bit << 1;
|
|
tx_symbol(sym);
|
|
}
|
|
}
|
|
|
|
unsigned char ch;
|
|
void psk::tx_char(unsigned char c)
|
|
{
|
|
ch = c;
|
|
const char *code;
|
|
char_symbols = acc_symbols;
|
|
if (_pskr) {
|
|
// acc_symbols = 0;
|
|
// ARQ varicode instead of MFSK for PSK63FEC
|
|
code = varienc(c);
|
|
} else {
|
|
code = psk_varicode_encode(c);
|
|
}
|
|
while (*code) {
|
|
tx_bit((*code - '0'));
|
|
code++;
|
|
}
|
|
|
|
if (! _pskr) {
|
|
// MSFK varicode instead of psk varicode
|
|
tx_bit(0);
|
|
tx_bit(0);
|
|
}
|
|
char_symbols = acc_symbols - char_symbols;
|
|
}
|
|
|
|
void psk::tx_flush()
|
|
{
|
|
if (_pskr) {
|
|
ovhd_symbols = ((numcarriers - symbols) % numcarriers);
|
|
//VK2ETA replace with a more effective flushing sequence (avoids cutting the last characters in low s/n)
|
|
for (int i = 0; i < ovhd_symbols/2; i++) tx_bit(0);
|
|
|
|
for (int i = 0; i < dcdbits; i++) tx_bit(0);
|
|
return;
|
|
}
|
|
// QPSK - flush the encoder
|
|
if (_qpsk) {
|
|
for (int i = 0; i < dcdbits; i++)
|
|
tx_bit(0);
|
|
// FEC : replace unmodulated carrier by an encoded sequence of zeros
|
|
}
|
|
// Standard BPSK postamble
|
|
// DCD off sequence (unmodulated carrier)
|
|
//VK2ETA remove for pskr since it is not used for DCD and only adds delay and creates TX overlaps
|
|
if (!_pskr) {
|
|
for (int i = 0; i < dcdbits; i++)
|
|
tx_symbol(2);
|
|
}
|
|
}
|
|
|
|
// Necessary to clear the interleaver before we start sending
|
|
void psk::clearbits()
|
|
{
|
|
bitshreg = enc->encode(0);
|
|
for (int k = 0; k < 160; k++) {
|
|
Txinlv->bits(&bitshreg);
|
|
}
|
|
}
|
|
|
|
int psk::tx_process()
|
|
{
|
|
int c;
|
|
|
|
if (preamble > 0) {
|
|
if (_pskr) {
|
|
if (startpreamble == true) {
|
|
if (mode != MODE_PSK63F) clearbits();
|
|
startpreamble = false;
|
|
}
|
|
// FEC prep the encoder with one/zero sequences of bits
|
|
preamble--;
|
|
preamble--;
|
|
tx_bit(1);
|
|
tx_bit(0);
|
|
// FEC: Mark start of first character with a double zero
|
|
// to ensure sync at end of preamble
|
|
if (preamble == 0)
|
|
while (acc_symbols % numcarriers) tx_bit(0);
|
|
return 0;
|
|
} else {
|
|
//JD for QPSK500R
|
|
// if (mode == MODE_QPSK500) clearbits();
|
|
// Standard BPSK/QPSK preamble
|
|
preamble--;
|
|
tx_symbol(0); // send phase reversals
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
c = get_tx_char();
|
|
|
|
if (c == GET_TX_CHAR_ETX || stopflag) {
|
|
tx_flush();
|
|
stopflag = false;
|
|
cwid();
|
|
|
|
char_samples = char_symbols * symbollen / numcarriers;
|
|
xmt_samples = acc_symbols * symbollen / numcarriers;
|
|
ovhd_samples = (acc_symbols - char_symbols - ovhd_symbols) * symbollen / numcarriers;
|
|
|
|
return -1; // we're done
|
|
}
|
|
|
|
if (c == GET_TX_CHAR_NODATA) {
|
|
if (_pskr) {
|
|
// MFSK varicode instead of psk
|
|
tx_char(0); // <NUL>
|
|
tx_bit(1);
|
|
// extended zero bit stream
|
|
for (int i = 0; i < 32; i++)
|
|
tx_bit(0);
|
|
} else {
|
|
tx_bit(0);
|
|
}
|
|
} else {
|
|
tx_char(c);
|
|
put_echo_char(c);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//============================================================================
|
|
// psk signal evaluation
|
|
// using Goertzel IIR filter
|
|
// derived from pskcore by Moe Wheatley, AE4JY
|
|
//============================================================================
|
|
|
|
void psk::initSN_IMD()
|
|
{
|
|
for(int i = 0; i < NUM_FILTERS; i++)
|
|
{
|
|
I1[i] = I2[i] = Q1[i] = Q2[i] = 0.0;
|
|
m_Energy[i] = 0.0;
|
|
}
|
|
m_NCount = 0;
|
|
|
|
COEF[0] = 2.0 * cos(TWOPI * 9 / GOERTZEL);
|
|
COEF[1] = 2.0 * cos(TWOPI * 18 / GOERTZEL);
|
|
COEF[2] = 2.0 * cos(TWOPI * 27 / GOERTZEL);
|
|
}
|
|
|
|
void psk::resetSN_IMD()
|
|
{
|
|
for(int i = 0; i < NUM_FILTERS; i++) {
|
|
I1[i] = I2[i] = Q1[i] = Q2[i] = 0.0;
|
|
}
|
|
m_NCount = 0;
|
|
}
|
|
|
|
//============================================================================
|
|
// This routine calculates the energy in the frequency bands of
|
|
// carrier=F0(15.625), noise=F1(31.25), and
|
|
// 3rd order product=F2(46.875)
|
|
// It is called with cmplx data samples at 500 Hz.
|
|
//============================================================================
|
|
|
|
void psk::calcSN_IMD(cmplx z)
|
|
{
|
|
int i;
|
|
double tempI = 0, tempQ = 0;
|
|
|
|
for(i = 0; i < NUM_FILTERS; i++) {
|
|
tempI = I1[i];
|
|
tempQ = Q1[i];
|
|
I1[i] = I1[i] * COEF[i]- I2[i] + z.real();
|
|
Q1[i] = Q1[i] * COEF[i]- Q2[i] + z.imag();
|
|
I2[i] = tempI;
|
|
Q2[i] = tempQ;
|
|
}
|
|
|
|
if( ++m_NCount >= GOERTZEL ) {
|
|
m_NCount = 0;
|
|
for(i = 0; i < NUM_FILTERS; i++) {
|
|
m_Energy[i] = I1[i]*I1[i] + Q1[i]*Q1[i]
|
|
+ I2[i]*I2[i] + Q2[i]*Q2[i]
|
|
- I1[i]*I2[i]*COEF[i]
|
|
- Q1[i]*Q2[i]*COEF[i];
|
|
I1[i] = I2[i] = Q1[i] = Q2[i] = 0.0;
|
|
}
|
|
signalquality();
|
|
}
|
|
}
|