kopia lustrzana https://github.com/jamescoxon/dl-fldigi
2266 wiersze
57 KiB
C++
2266 wiersze
57 KiB
C++
// ----------------------------------------------------------------------------
|
|
// psk.cxx -- psk modem
|
|
//
|
|
// Copyright (C) 2006-2015
|
|
// Dave Freese, W1HKJ
|
|
// Copyright (C) 2009-2010
|
|
// John Douyere, VK2ETA
|
|
// Copyright (C) 2014
|
|
// John Phelps, KL4YFD
|
|
//
|
|
// 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"
|
|
|
|
#include "confdialog.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
|
|
// df=10 : correct up to 4 bits
|
|
#define PSKR_K 7
|
|
#define PSKR_POLY1 0x6d
|
|
#define PSKR_POLY2 0x4f
|
|
|
|
// df=14 : correct up to 6 bits
|
|
#define K11 11
|
|
#define K11_POLY1 03073 // 1595
|
|
#define K11_POLY2 02365 // 1269
|
|
|
|
// df=16 : correct up to 7 bits
|
|
// Code has good ac(df) and bc(df) parameters for puncturing
|
|
#define K13 13
|
|
#define K13_POLY1 016461 // 7473
|
|
#define K13_POLY2 012767 // 5623
|
|
|
|
// df=19 : correct up to 9 bits
|
|
#define K16 16
|
|
#define K16_POLY1 0152711 // 54729
|
|
#define K16_POLY2 0126723 // 44499
|
|
|
|
// For Gray-mapped 8PSK:
|
|
// Even when the received phase is distorted by +- 1 phase-position:
|
|
// - One of the bits is still known with 100% certianty.
|
|
// - Only up to 1 bit can be in error
|
|
static cmplx graymapped_8psk_pos[] = {
|
|
// Degrees Bits In Mapped Soft-Symbol
|
|
cmplx (1.0, 0.0), // 0 | 0b000 | 025,000,025
|
|
cmplx (0.7071, 0.7071), // 45 | 0b001 | 000,025,230
|
|
cmplx (-0.7071, 0.7071), // 135 | 0b010 | 025,255,025
|
|
cmplx (0.0, 1.0), // 90 | 0b011 | 000,230,230
|
|
cmplx (0.7071, -0.7071), // 315 | 0b100 | 230,000,025
|
|
cmplx (0.0, -1.0), // 270 | 0b101 | 255,025,230
|
|
cmplx (-1.0, 0.0), // 180 | 0b110 | 230,255,025
|
|
cmplx (-0.7071, -0.7071) // 225 | 0b111 | 255,230,230
|
|
};
|
|
|
|
// Associated soft-symbols to be used with graymapped_8psk_pos[] constellation
|
|
// These softbits have precalculated (a-priori) probabilities applied
|
|
// Use of this table automatically Gray-decodes incoming symbols.
|
|
static unsigned char graymapped_8psk_softbits[8][3] = {
|
|
{ 25, 0, 25}, // 0
|
|
{ 0, 25, 230}, // 1
|
|
{ 0, 230, 230}, // 3
|
|
{ 25, 255, 25}, // 2
|
|
{230, 255, 25}, // 6
|
|
{255, 230, 230}, // 7
|
|
{255, 25, 230}, // 5
|
|
{230, 0, 25} // 4
|
|
};
|
|
|
|
|
|
char pskmsg[80];
|
|
|
|
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 || _xpsk || _8psk || _16psk) {
|
|
// 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;
|
|
accumulated_bits = 0;
|
|
|
|
if(_8psk && _puncturing) {
|
|
enc->init();
|
|
Txinlv->flush();
|
|
}
|
|
|
|
vphase = 0;
|
|
maxamp = 0;
|
|
|
|
double bw2 = 6.0 * bandwidth;
|
|
double flo = (get_txfreq_woffset() - bw2);
|
|
if (flo <= 0) flo = 0;
|
|
double fhi = (get_txfreq_woffset() + bw2);
|
|
if (fhi >= 0.48*samplerate) fhi = 0.48*samplerate;
|
|
xmtfilt->init_bandpass (127, 1, flo/samplerate, fhi/samplerate);
|
|
}
|
|
|
|
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 || _xpsk || _8psk || _16psk) {
|
|
// 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();
|
|
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 ((mode >= MODE_PSK31 && mode <= MODE_PSK125) ||
|
|
(mode >= MODE_QPSK31 && mode <= MODE_QPSK125))
|
|
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 (e0_filt) delete e0_filt;
|
|
if (e1_filt) delete e1_filt;
|
|
if (e2_filt) delete e2_filt;
|
|
|
|
if (pskviewer) delete pskviewer;
|
|
if (evalpsk) delete evalpsk;
|
|
|
|
// Interleaver
|
|
if (Rxinlv) delete Rxinlv;
|
|
if (Rxinlv2) delete Rxinlv2;
|
|
if (Txinlv) delete Txinlv;
|
|
|
|
if (vestigial_sfft) delete vestigial_sfft;
|
|
|
|
if (xmtfilt) delete xmtfilt;
|
|
}
|
|
|
|
psk::psk(trx_mode pskmode) : modem()
|
|
{
|
|
cap |= CAP_AFC | CAP_AFC_SR;
|
|
|
|
mode = pskmode;
|
|
|
|
// Set the defaults that are common to most modes
|
|
samplerate = 8000;
|
|
numcarriers = 1;
|
|
separation = 1.4;
|
|
_16psk = _8psk = _xpsk = _pskr = _qpsk = _disablefec = _puncturing = false;
|
|
symbits = 1;
|
|
flushlength = 0;
|
|
int isize = 2;
|
|
idepth = 2;
|
|
PSKviterbi = false;
|
|
vestigial = false;
|
|
|
|
switch (mode) {
|
|
case MODE_PSK31:
|
|
symbollen = 256;
|
|
dcdbits = 32;
|
|
break;
|
|
case MODE_PSK63:
|
|
symbollen = 128;
|
|
dcdbits = 64;
|
|
break;
|
|
case MODE_PSK125:
|
|
symbollen = 64;
|
|
dcdbits = 128;
|
|
break;
|
|
case MODE_PSK250:
|
|
symbollen = 32;
|
|
dcdbits = 256;
|
|
break;
|
|
case MODE_PSK500:
|
|
symbollen = 16;
|
|
dcdbits = 512;
|
|
break;
|
|
case MODE_PSK1000:
|
|
symbollen = 8;
|
|
dcdbits = 128;
|
|
break;
|
|
|
|
case MODE_QPSK31:
|
|
symbollen = 256;
|
|
_qpsk = true;
|
|
dcdbits = 32;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_QPSK63:
|
|
symbollen = 128;
|
|
_qpsk = true;
|
|
dcdbits = 64;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_QPSK125:
|
|
symbollen = 64;
|
|
_qpsk = true;
|
|
dcdbits = 128;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_QPSK250:
|
|
symbollen = 32;
|
|
_qpsk = true;
|
|
dcdbits = 256;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_QPSK500:
|
|
symbollen = 16;
|
|
_qpsk = true;
|
|
dcdbits = 512;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_PSK63F: // As per Multipsk (BPSK63 + FEC + MFSK Varicode)
|
|
symbollen = 128;
|
|
_pskr = true;
|
|
dcdbits = 64;
|
|
break;
|
|
|
|
// 8psk modes without FEC
|
|
case MODE_8PSK125:
|
|
symbollen = 128;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
_disablefec = true;
|
|
dcdbits = 128;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK250: // 250 baud | 375 bits/sec @ 1/2 Rate FEC
|
|
symbollen = 64;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
_disablefec = true;
|
|
dcdbits = 256;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK500: // 500 baud | 1000 bits/sec @ 2/3 rate FEC
|
|
symbollen = 32;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
_disablefec = true;
|
|
dcdbits = 512;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK1000: // 1000 baud | 3000 bits/sec No FEC
|
|
symbollen = 16;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
_disablefec = true;
|
|
dcdbits = 1024;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
|
|
// 8psk modes with FEC
|
|
case MODE_8PSK125FL:
|
|
symbollen = 128;
|
|
idepth = 384; // 1024 milliseconds
|
|
flushlength = 38;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
dcdbits = 128;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK250FL: // 250 baud | 375 bits/sec @ 1/2 Rate FEC
|
|
symbollen = 64;
|
|
idepth = 512; // 682 milliseconds
|
|
flushlength = 47;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
dcdbits = 256;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK125F:
|
|
symbollen = 128;
|
|
idepth = 384; // 1024 milliseconds
|
|
flushlength = 38;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
dcdbits = 128;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK250F: // 250 baud | 375 bits/sec @ 1/2 Rate FEC
|
|
symbollen = 64;
|
|
idepth = 512; // 682 milliseconds
|
|
flushlength = 47;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
dcdbits = 256;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK500F: // 500 baud | 1000 bits/sec @ 2/3 rate FEC
|
|
symbollen = 32;
|
|
idepth = 640; // 426 milliseconds
|
|
flushlength = 62;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
_puncturing = true;
|
|
dcdbits = 512;
|
|
vestigial = true;
|
|
cap |= CAP_REV;
|
|
break;
|
|
case MODE_8PSK1000F: // 1000 baud | 3000 bits/sec
|
|
symbollen = 16;
|
|
idepth = 512;
|
|
flushlength = 56;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
dcdbits = 1024;
|
|
cap |= CAP_REV;
|
|
_puncturing = true;
|
|
vestigial = true;
|
|
PSKviterbi = true;
|
|
break;
|
|
case MODE_8PSK1200F: // 1200 baud | 1800 bits/sec
|
|
symbollen = 13;
|
|
idepth = 512;
|
|
flushlength = 56;
|
|
samplerate = 16000;
|
|
_8psk = true;
|
|
_puncturing = true;
|
|
dcdbits = 2048;
|
|
cap |= CAP_REV;
|
|
vestigial = true;
|
|
PSKviterbi = true;
|
|
break;
|
|
// end 8psk modes
|
|
|
|
case MODE_PSK125R:
|
|
symbollen = 64;
|
|
_pskr = true;
|
|
dcdbits = 128;
|
|
idepth = 40; // 2x2x40 interleaver
|
|
break;
|
|
case MODE_PSK250R:
|
|
symbollen = 32;
|
|
_pskr = true;
|
|
dcdbits = 256;
|
|
idepth = 80; // 2x2x80 interleaver
|
|
break;
|
|
case MODE_PSK500R:
|
|
symbollen = 16;
|
|
_pskr = true;
|
|
dcdbits = 512;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_PSK1000R:
|
|
symbollen = 8;
|
|
_pskr = true;
|
|
dcdbits = 512;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
// multi-carrier modems
|
|
case MODE_4X_PSK63R:
|
|
symbollen = 128;//PSK63
|
|
dcdbits = 128;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 4;
|
|
idepth = 80; // 2x2x80 interleaver
|
|
break;
|
|
case MODE_5X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 5;
|
|
idepth = 260; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_10X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 10;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_20X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 20;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_32X_PSK63R:
|
|
symbollen = 128; //PSK63
|
|
dcdbits = 512;
|
|
_pskr = true; //PSKR
|
|
numcarriers = 32;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_4X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 4;
|
|
idepth = 80; // 2x2x80 interleaver
|
|
break;
|
|
case MODE_5X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 5;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_10X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 10;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_12X_PSK125:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 128;//512;
|
|
numcarriers = 12;
|
|
break;
|
|
case MODE_12X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 12;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_16X_PSK125R:
|
|
symbollen = 64;//PSK125
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 16;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_2X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_3X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
dcdbits = 512;
|
|
_pskr = true;//PSKR
|
|
numcarriers = 3;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_5X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 5;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_6X_PSK250:
|
|
symbollen = 32;//PSK250
|
|
dcdbits = 512;
|
|
numcarriers = 6;
|
|
break;
|
|
case MODE_6X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 6;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_7X_PSK250R:
|
|
symbollen = 32;//PSK250
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 7;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
case MODE_2X_PSK500:
|
|
symbollen = 16;
|
|
dcdbits = 512;
|
|
numcarriers = 2;
|
|
break;
|
|
case MODE_4X_PSK500:
|
|
symbollen = 16;
|
|
dcdbits = 512;
|
|
numcarriers = 4;
|
|
break;
|
|
|
|
case MODE_2X_PSK500R:
|
|
symbollen = 16;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 2;
|
|
break;
|
|
case MODE_3X_PSK500R:
|
|
symbollen = 16;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 3;
|
|
break;
|
|
case MODE_4X_PSK500R:
|
|
symbollen = 16;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 4;
|
|
break;
|
|
|
|
case MODE_2X_PSK800:
|
|
symbollen = 10;
|
|
_pskr = false;
|
|
dcdbits = 512;
|
|
numcarriers = 2;
|
|
break;
|
|
case MODE_2X_PSK800R:
|
|
symbollen = 10;
|
|
_pskr = true;
|
|
dcdbits = 1024;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
numcarriers = 2;
|
|
break;
|
|
|
|
case MODE_2X_PSK1000:
|
|
symbollen = 8;//PSK1000
|
|
dcdbits = 1024;
|
|
numcarriers = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
case MODE_2X_PSK1000R:
|
|
symbollen = 8;//PSK1000
|
|
_pskr = true;//PSKR
|
|
dcdbits = 1024;
|
|
numcarriers = 2;
|
|
idepth = 160; // 2x2x160 interleaver
|
|
break;
|
|
|
|
default:
|
|
mode = MODE_PSK31;
|
|
symbollen = 256;
|
|
dcdbits = 32;
|
|
numcarriers = 1;
|
|
}
|
|
|
|
// Set the number of bits-per-symbol based on the chosen constellation
|
|
if (_qpsk || _xpsk) symbits = 2;
|
|
else if (_8psk) symbits = 3;
|
|
else if (_16psk) symbits = 4;
|
|
else symbits = 1; // else BPSK / PSKR
|
|
|
|
|
|
//printf("%s: symlen %d, dcdbits %d, _qpsk %d, _pskr %d, numc %f\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;
|
|
vestigial_sfft = (sfft *)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);
|
|
e0_filt = new Cmovavg(dcdbits / 2);
|
|
e1_filt = new Cmovavg(dcdbits / 2);
|
|
e2_filt = new Cmovavg(dcdbits / 2);
|
|
|
|
if (_disablefec) {
|
|
enc = NULL;
|
|
dec = dec2 = NULL;
|
|
|
|
} else if (_qpsk) {
|
|
enc = new encoder(K, POLY1, POLY2);
|
|
dec = new viterbi(K, POLY1, POLY2);
|
|
|
|
} else if (_pskr || PSKviterbi) {
|
|
// 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);
|
|
|
|
} else if (mode == MODE_8PSK125F || mode == MODE_8PSK250F) {
|
|
enc = new encoder(K16, K16_POLY1, K16_POLY2);
|
|
dec = new viterbi(K16, K16_POLY1, K16_POLY2);
|
|
dec->setchunksize(4);
|
|
dec2 = new viterbi(K13, K16_POLY1, K16_POLY2);
|
|
dec2->setchunksize(4);
|
|
|
|
} else if (_xpsk || _8psk || _16psk) {
|
|
enc = new encoder(K13, K13_POLY1, K13_POLY2);
|
|
dec = new viterbi(K13, K13_POLY1, K13_POLY2);
|
|
dec->setchunksize(4);
|
|
// Second viterbi decoder is only needed when modem has an odd number of bits/symbol.
|
|
if ( _8psk && !_puncturing ) { // (punctured 8psk has 3-real bits + 1-punctured bit per transmitted symbol)
|
|
dec2 = new viterbi(K13, K13_POLY1, K13_POLY2);
|
|
dec2->setchunksize(4);
|
|
}
|
|
if (_puncturing) { // punctured codes benefit from a longer traceback
|
|
dec->settraceback(K13 * 16);
|
|
if (dec2) dec2->settraceback(K13 * 16);
|
|
}
|
|
}
|
|
|
|
// Interleaver. For PSKR 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..
|
|
|
|
Txinlv = new interleave (isize, idepth, INTERLEAVE_FWD);
|
|
Rxinlv = new interleave (isize, idepth, INTERLEAVE_REV);
|
|
if (dec2) Rxinlv2 = new interleave (isize, idepth, 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;
|
|
|
|
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 ((mode >= MODE_PSK31 && mode <= MODE_PSK125) ||
|
|
(mode >= MODE_QPSK31 && mode <= MODE_QPSK125))
|
|
pskviewer = new viewpsk(evalpsk, mode);
|
|
else
|
|
pskviewer = 0;
|
|
|
|
if (vestigial) {
|
|
if (samplerate == 16000)
|
|
sfft_size = 16384;
|
|
else
|
|
sfft_size = 8192;
|
|
|
|
int bin = sc_bw * sfft_size / samplerate;
|
|
vestigial_sfft = new sfft(sfft_size, bin - 5, bin + 6); // 11 bins
|
|
for (int i = 0; i < 11; i++) sfft_bins[i] = cmplx(0,0);
|
|
}
|
|
|
|
xmtfilt = new C_FIR_filter();
|
|
|
|
}
|
|
|
|
//=============================================================================
|
|
//=========================== 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;
|
|
bool do_s2nreport = false;
|
|
shreg = (shreg << 1) | !!bit;
|
|
if (_pskr || _xpsk || _8psk || _16psk) {
|
|
// MFSK varicode instead of PSK Varicode
|
|
if ((shreg & 7) == 1) {
|
|
c = varidec(shreg >> 1);
|
|
// Voting at the character level for only PSKR modes
|
|
if (fecmet >= fecmet2 || _disablefec) {
|
|
if ((c != -1) && (c != 0) && (dcd == true)) {
|
|
put_rx_char(c);
|
|
do_s2nreport = true;
|
|
}
|
|
}
|
|
shreg = 1;
|
|
}
|
|
} else {
|
|
// PSK varicode
|
|
if ((shreg & 3) == 0) {
|
|
c = psk_varicode_decode(shreg >> 2);
|
|
if ((c != -1) && (dcd == true)) {
|
|
put_rx_char(c);
|
|
do_s2nreport = true;
|
|
}
|
|
shreg = 0;
|
|
}
|
|
}
|
|
|
|
if (do_s2nreport) {
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
|
|
s2n_sum += s2n_metric;
|
|
s2n_sum2 += (s2n_metric * s2n_metric);
|
|
s2n_ncount ++;
|
|
if (c == EOT)
|
|
s2nreport();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void psk::rx_bit2(int bit)
|
|
{
|
|
int c;
|
|
bool do_s2nreport = false;
|
|
|
|
shreg2 = (shreg2 << 1) | !!bit;
|
|
// MFSK varicode instead of PSK Varicode
|
|
if ((shreg2 & 7) == 1) {
|
|
c = varidec(shreg2 >> 1);
|
|
// Voting at the character level for only PSKR modes
|
|
if (fecmet < fecmet2 || _disablefec) {
|
|
if ((c != -1) && (c != 0) && (dcd == true)) {
|
|
put_rx_char(c);
|
|
do_s2nreport = true;
|
|
}
|
|
}
|
|
shreg2 = 1;
|
|
}
|
|
|
|
if (do_s2nreport) {
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient)) {
|
|
s2n_sum += s2n_metric;
|
|
s2n_sum2 += (s2n_metric * s2n_metric);
|
|
s2n_ncount ++;
|
|
if (c == EOT)
|
|
s2nreport();
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
// }
|
|
// XPSK and 16PSK have even number of bits/symbol
|
|
// Punctured 8PSK has even number of bits/symbol (3 real + 1 punctured)
|
|
// so bit order known: can use only one decoder to reduce CPU usage
|
|
if ( _xpsk || _16psk || (_8psk && _puncturing) ) {
|
|
fecmet2 = -9999.0;
|
|
return;
|
|
}
|
|
// copy to avoid scrambling symbolpair for the next bit
|
|
twosym[0] = symbolpair[0];
|
|
twosym[1] = symbolpair[1];
|
|
// De-interleave
|
|
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()
|
|
{
|
|
put_Status1("");
|
|
put_Status2("");
|
|
put_status("");
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//DHF: AFC based on vestigial carrier located at f0 - bandwidth
|
|
void psk::vestigial_afc() {
|
|
|
|
if (!progdefaults.pskpilot) return;
|
|
if (!vestigial_sfft->is_stable()) return;
|
|
|
|
double avg = 0;
|
|
int i = 0;
|
|
for (i = 0; i < 11; i++) avg += abs(sfft_bins[i]);
|
|
avg /= 11.0;
|
|
std::setprecision(2); std::setw(5);
|
|
for (i = 0; i < 11; i++) if (abs(sfft_bins[i]) > 2.0*avg) break;
|
|
if (i < 11) {
|
|
// std::cout << "bin: " << i
|
|
// << ", freq offset: " << (i - 5)*samplerate/16384.0
|
|
// << ", amp: " << abs(sfft_bins[i])
|
|
// << ", avg: " << avg << "\n";
|
|
if (i != 5) {
|
|
frequency -= 1.0*(i-5)*samplerate/sfft_size;
|
|
set_freq (frequency);
|
|
}
|
|
}
|
|
vestigial_sfft->reset();
|
|
}
|
|
|
|
//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;
|
|
// Skip AFC for modes it does not work with
|
|
|
|
if (vestigial) return vestigial_afc();
|
|
|
|
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;
|
|
|
|
/// align the RX constellation to the TX constellation, for Non-FEC modes
|
|
if (_disablefec && (_16psk || _8psk || _xpsk )) phase -= M_PI;
|
|
|
|
if (phase < 0) phase += TWOPI;
|
|
|
|
if (_qpsk) {
|
|
n = 4;
|
|
bits = ((int) (phase / M_PI_2 + 0.5)) & (n-1);
|
|
} else if (_xpsk) {
|
|
n = 4;
|
|
bits = ((int) (phase / M_PI_2)) & (n-1);
|
|
} else if (_8psk) {
|
|
n = 8;
|
|
bits = ((int) (phase / (M_PI/4.0) + 0.5)) & (n-1);
|
|
} else if (_16psk) {
|
|
n = 16;
|
|
bits = ((int) (phase / (M_PI/8.0) + 0.5)) & (n-1);
|
|
} else { // bpsk and pskr
|
|
n = 2;
|
|
bits = (((int) (phase / M_PI + 0.5)) & (n-1) ) << 1;
|
|
// hard decode if needed
|
|
// softbit = (bits & 2) ? 0 : 255;
|
|
// reversed as we normally pass "!bits" when hard decoding
|
|
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);
|
|
}
|
|
|
|
// simple low pass filter for quality of signal
|
|
double decay = SQLDECAY;
|
|
double attack = SQLDECAY;
|
|
double cval = cos(n*phase);
|
|
double sval = sin(n*phase);
|
|
|
|
if (_8psk) {
|
|
attack *= 2;
|
|
decay *= 4;
|
|
}
|
|
|
|
if (_pskr) {
|
|
decay *= 10;
|
|
quality = cmplx(
|
|
decayavg(quality.real(), cval, decay),
|
|
decayavg(quality.imag(), sval, decay));
|
|
} else
|
|
quality = cmplx(
|
|
decayavg(quality.real(), cval, cval > quality.real() ? attack : decay),
|
|
decayavg(quality.imag(), sval, sval > quality.real() ? attack : decay));
|
|
|
|
metric = 100.0 * norm(quality);
|
|
if (_pskr && (averageamp < 3e-5)) metric = 0;
|
|
|
|
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
|
|
// ***********************************************************
|
|
// **** DHF still needed with attack/decay filtering?
|
|
// ***********************************************************
|
|
// if (_pskr) {
|
|
// metric = metric * 4;
|
|
// }
|
|
// else if ( (_xpsk || _8psk || _16psk) && !_disablefec) {
|
|
// metric *= 2 * symbits; /// @TODO scale the metric with the psk constellation density
|
|
// }
|
|
|
|
if (metric > 100)
|
|
metric = 100;
|
|
|
|
afcmetric = decayavg(afcmetric, norm(quality), 50);
|
|
|
|
dcdshreg = ( dcdshreg << (symbits+1) ) | bits;
|
|
|
|
int set_dcdON = -1; // 1 sets DCD on ; 0 sets DCD off ; -1 does neither (no-op)
|
|
|
|
switch (dcdshreg) {
|
|
|
|
// bpsk DCD on
|
|
case 0xAAAAAAAA:
|
|
if ( _xpsk || _8psk || _16psk) break;
|
|
if (_pskr) break;
|
|
set_dcdON = 1;
|
|
break;
|
|
|
|
// pskr DCD on
|
|
case 0x0A0A0A0A:
|
|
if ( _xpsk || _8psk || _16psk) break;
|
|
if (_qpsk) break;
|
|
if (!_pskr) break;
|
|
set_dcdON = 1;
|
|
break;
|
|
|
|
// 8psk DCD on (FEC enabled, with Gray-mapped constellation)
|
|
case 0x25252525: // UN-punctured
|
|
if (_pskr || _xpsk || _16psk) break;
|
|
if (!_8psk) break;
|
|
if (_disablefec) break;
|
|
set_dcdON = 1;
|
|
break;
|
|
case 0x22222222: // punctured @ 2/3 rate
|
|
if (_pskr || _xpsk || _16psk) break;
|
|
if (!_8psk) break;
|
|
if (_disablefec) break;
|
|
set_dcdON = 1;
|
|
break;
|
|
|
|
case 0x92492492: // xpsk DCD off (with FEC disabled)
|
|
if (_pskr) break;
|
|
if (_qpsk) break;
|
|
if (!_xpsk) break;
|
|
if (!_disablefec) break;
|
|
set_dcdON = 0;
|
|
break;
|
|
|
|
case 0x10842108: // 16psk DCD off (with FEC disabled)
|
|
if (_pskr) break;
|
|
if (!_16psk) break;
|
|
if (!_disablefec) break;
|
|
set_dcdON = 0;
|
|
break;
|
|
|
|
case 0x44444444: // 8psk DCD off (with FEC disabled)
|
|
if (!_8psk) break;
|
|
if (!_disablefec) break;
|
|
set_dcdON = 0;
|
|
break;
|
|
|
|
case 0x10410410: // xpsk DCD on (with FEC enabled)
|
|
if (_pskr) break;
|
|
if (_qpsk) break;
|
|
if (_8psk) break;
|
|
if (_16psk) break;
|
|
if (!_xpsk) break;
|
|
if (_disablefec) break;
|
|
set_dcdON = 1;
|
|
break;
|
|
|
|
case 0x00000000: // bpsk DCD off. x,8,16psk DCD on (with FEC disabled).
|
|
if (_pskr) break;
|
|
if (_xpsk || _8psk || _16psk) {
|
|
if (!_disablefec) break;
|
|
set_dcdON = 1;
|
|
break;
|
|
}
|
|
set_dcdON = 0;
|
|
break;
|
|
|
|
default:
|
|
if (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == false) {
|
|
dcd = true;
|
|
} else {
|
|
dcd = false;
|
|
}
|
|
}
|
|
|
|
displaysn = false;
|
|
if ( 1 == set_dcdON ) {
|
|
displaysn = true;
|
|
dcd = true;
|
|
acquire = 0;
|
|
quality = cmplx (1.0, 0.0);
|
|
if (progdefaults.Pskmails2nreport && (mailserver || mailclient))
|
|
s2n_sum = s2n_sum2 = s2n_ncount = 0.0;
|
|
//printf("\n DCD ON!!");
|
|
} else if ( 0 == set_dcdON ){
|
|
dcd = false;
|
|
acquire = 0;
|
|
quality = cmplx (0.0, 0.0);
|
|
//printf("\n DCD OFF!!!!!!!!!");
|
|
}
|
|
|
|
if (_pskr) {
|
|
rx_pskr(softbit);
|
|
set_phase(phase, norm(quality), dcd);
|
|
|
|
} else if (dcd == true) {
|
|
|
|
set_phase(phase, norm(quality), dcd);
|
|
|
|
if (!_disablefec && (_16psk || _8psk || _xpsk) ) {
|
|
int bitmask = 1;
|
|
unsigned char xsoftsymbols[symbits];
|
|
|
|
//printf("\n");
|
|
if ( (_puncturing && _16psk) ) rx_pskr(128); // 16psk: recover punctured low bit
|
|
|
|
// Soft-decode of Gray-mapped 8psk
|
|
if (_8psk) {
|
|
bool softpuncture = false;
|
|
static double lastphasequality=0;
|
|
double phasequality = fabs(cos( n/2 * phase));
|
|
phasequality = (phasequality + lastphasequality) / 2; // Differential modem: average probabilities between current and previous symbols
|
|
lastphasequality = phasequality;
|
|
int soft_qualityerror = static_cast<int>(128 - (128 * phasequality)) ;
|
|
|
|
if (soft_qualityerror > 255-25) // Prevent soft-bit wrap-around (crossing of value 128)
|
|
softpuncture = true;
|
|
else if (soft_qualityerror < 128/3) // First 1/3 of phase delta is considered a perfect signal
|
|
soft_qualityerror = 0;
|
|
else if (soft_qualityerror > 128 - (128/8) ) // Last 1/8 of phase delta triggers a puncture
|
|
softpuncture = true;
|
|
else
|
|
soft_qualityerror /= 2; // Scale the FEC error to prevent premature cutoff
|
|
|
|
|
|
if (softpuncture) {
|
|
for(int i=0; i<symbits; i++) rx_pskr(128);
|
|
} else {
|
|
int bitindex = static_cast<int>(bits);
|
|
for(int i=symbits-1; i>=0; i--) { // Use predefined Gray-mapped softbits for soft-decoding
|
|
if (graymapped_8psk_softbits[bitindex][i] > 128) // Soft-One
|
|
rx_pskr( (graymapped_8psk_softbits[bitindex][i]) - soft_qualityerror );
|
|
else // Soft-Zero
|
|
rx_pskr( (graymapped_8psk_softbits[bitindex][i]) + soft_qualityerror );
|
|
}
|
|
}
|
|
|
|
} else {
|
|
//Hard Decode Section
|
|
for(int i=0; i<symbits; i++) { // Hard decode symbits into soft-symbols
|
|
xsoftsymbols[i] = (bits & bitmask) ? 255 : 0 ;
|
|
//printf(" %.3u ", xsoftsymbols[i]);
|
|
rx_pskr(xsoftsymbols[i]); // Feed to the PSKR FEC decoder, one bit at a time.
|
|
bitmask = bitmask << 1;
|
|
}
|
|
}
|
|
if (_puncturing) rx_pskr(128); // x/8/16psk: Recover punctured high bit
|
|
|
|
} else if (_16psk || _8psk || _xpsk) {
|
|
// Decode symbol one bit at a time
|
|
int bitmask = 1;
|
|
for (int i = 0; i < symbits; i++) {
|
|
rx_bit( ((bits) & bitmask) );
|
|
bitmask = bitmask << 1;
|
|
}
|
|
|
|
} else if (_qpsk)
|
|
rx_qpsk(bits);
|
|
|
|
else
|
|
rx_bit(!bits); //default to BPSK
|
|
}
|
|
}
|
|
|
|
static double e0, e1, e2;
|
|
|
|
void psk::signalquality()
|
|
{
|
|
// double e0, e1, e2;
|
|
e0 = e0_filt->run(m_Energy[0]);
|
|
e1 = e1_filt->run(m_Energy[1]);
|
|
e2 = e2_filt->run(m_Energy[2]);
|
|
|
|
if (((e0 - e1) > 0) && (e1 > 0))
|
|
snratio = (e0 - e1) / e1;
|
|
else
|
|
snratio = 1000.0;
|
|
|
|
if (((e0 - e1) > 0) && ((e2 - e1) > 0) )
|
|
imdratio = (e2 - e1) / (e0 - e1);
|
|
else
|
|
imdratio = 0.001;
|
|
|
|
}
|
|
|
|
void psk::update_syncscope()
|
|
{
|
|
static char msg1[16];
|
|
static char msg2[16];
|
|
|
|
display_metric(metric);
|
|
|
|
if (displaysn) {
|
|
memset(msg1, 0, sizeof(msg1));
|
|
memset(msg2, 0, sizeof(msg2));
|
|
|
|
s2n = 10.0*log10( snratio );
|
|
snprintf(msg1, sizeof(msg1), "s/n %2.0f dB", s2n);
|
|
|
|
imd = 10.0*log10( imdratio );
|
|
snprintf(msg2, sizeof(msg2), "imd %2.0f dB", imd);
|
|
|
|
put_Status1( msg1,
|
|
progdefaults.StatusTimeout,
|
|
progdefaults.StatusDim ? STATUS_DIM : STATUS_CLEAR);
|
|
put_Status2( msg2,
|
|
progdefaults.StatusTimeout,
|
|
progdefaults.StatusDim ? STATUS_DIM : STATUS_CLEAR);
|
|
}
|
|
|
|
//static char msg3[50];
|
|
//memset(msg3, 0, sizeof(msg3));
|
|
//snprintf(msg3, sizeof(msg3), "%10.3f, %10.3f, %10.3f",
|
|
//e0, e1, e2);
|
|
//put_status(msg3);
|
|
|
|
}
|
|
|
|
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 (mode >= MODE_PSK31 && mode <= MODE_PSK125) {
|
|
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]) );
|
|
|
|
// if we re-enable multi-carrier vestigial carrier
|
|
// if (vestigial && car == 0) vestigial_sfft->run(z, sfft_bins, 1);
|
|
if (vestigial && progdefaults.pskpilot) vestigial_sfft->run(z, sfft_bins, 1);
|
|
|
|
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;
|
|
|
|
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
|
|
*
|
|
* A t 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 += 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::transmit(double *buf, int len)
|
|
{
|
|
// if (btn_imd_on->value())
|
|
for (int i = 0; i < len; i++) xmtfilt->Irun(buf[i], buf[i]);
|
|
|
|
ModulateXmtr(buf, len);
|
|
}
|
|
|
|
#define SVP_MASK 0xF
|
|
#define SVP_COUNT (SVP_MASK + 1)
|
|
|
|
static cmplx sym_vec_pos[SVP_COUNT] = {
|
|
cmplx (-1.0, 0.0), // 180 degrees
|
|
cmplx (-0.9238, -0.3826), // 202.5 degrees
|
|
cmplx (-0.7071, -0.7071), // 225 degrees
|
|
cmplx (-0.3826, -0.9238), // 247.5 degrees
|
|
cmplx (0.0, -1.0), // 270 degrees
|
|
cmplx (0.3826, -0.9238), // 292.5 degrees
|
|
cmplx (0.7071, -0.7071), // 315 degrees
|
|
cmplx (0.9238, -0.3826), // 337.5 degrees
|
|
cmplx (1.0, 0.0), // 0 degrees
|
|
cmplx (0.9238, 0.3826), // 22.5 degrees
|
|
cmplx (0.7071, 0.7071), // 45 degrees
|
|
cmplx (0.3826, 0.9238), // 67.5 degrees
|
|
cmplx (0.0, 1.0), // 90 degrees
|
|
cmplx (-0.3826, 0.9238), // 112.5 degrees
|
|
cmplx (-0.7071, 0.7071), // 135 degrees
|
|
cmplx (-0.9238, 0.3826) // 157.5 degrees
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
int sym;
|
|
for (int car = 0; car < symbols; car++) {
|
|
sym = txsymbols[car];
|
|
|
|
if (_qpsk && !reverse)
|
|
sym = (4 - sym) & 3;
|
|
|
|
if (_8psk && !_disablefec) // Use Gray-mapped 8psk constellation
|
|
symbol = prevsymbol[car] * graymapped_8psk_pos[(sym & 7)]; // complex multiplication
|
|
|
|
else { // Map the incoming symbols to the underlying 16psk constellation.
|
|
if (_xpsk) sym = sym * 4 + 2; // Give it the "X" constellation shape
|
|
else if (_8psk) sym *= 2; // Map 8psk to 16psk
|
|
else sym *= 4; // For BPSK and QPSK
|
|
symbol = prevsymbol[car] * sym_vec_pos[(sym & SVP_MASK)]; // complex 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;
|
|
}
|
|
// create an imd value
|
|
double maxmag = xmtimd->value();
|
|
if (btn_imd_on->value())
|
|
if (fabs(outbuf[i]) > maxmag)
|
|
outbuf[i] = maxmag * (outbuf[i] < 0 ? -1 : 1);
|
|
|
|
phaseacc[car] += delta[car];
|
|
if (phaseacc[car] > TWOPI) phaseacc[car] -= TWOPI;
|
|
}
|
|
|
|
prevsymbol[car] = symbol;
|
|
}
|
|
|
|
if (vestigial && progdefaults.pskpilot) {
|
|
double dvp = TWOPI * (frequencies[0] - sc_bw) / samplerate;
|
|
double amp = pow(10, progdefaults.pilot_power / 20.0) * maxamp;
|
|
for (int i = 0; i < symbollen; i++) {
|
|
outbuf[i] += amp * cos(vphase);
|
|
outbuf[i] /= (1 + amp);
|
|
vphase += dvp;
|
|
if (vphase > TWOPI) vphase -= TWOPI;
|
|
}
|
|
}
|
|
|
|
maxamp = 0;
|
|
for (int i = 0; i < symbollen; i++)
|
|
if (maxamp < fabs(outbuf[i])) maxamp = fabs(outbuf[i]);
|
|
maxamp *= 1.02;
|
|
if (maxamp) {
|
|
for (int i = 0; i < symbollen; i++)
|
|
outbuf[i] /= maxamp;
|
|
}
|
|
|
|
transmit(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;
|
|
static int bitcount=0;
|
|
static int xpsk_sym=0;
|
|
|
|
// qpsk transmission
|
|
if (_qpsk) {
|
|
sym = enc->encode(bit);
|
|
sym = sym & 3;//JD just to make sure
|
|
tx_symbol(sym);
|
|
// pskr (fec + interleaver) transmission
|
|
} 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 if (_16psk || _8psk || _xpsk) {
|
|
if (_disablefec) {
|
|
//Accumulate tx bits until the correct number for symbol-size is reached
|
|
xpsk_sym |= bit << bitcount++ ;
|
|
if (bitcount == symbits) {
|
|
tx_symbol(xpsk_sym);
|
|
xpsk_sym = bitcount = 0;
|
|
}
|
|
} else
|
|
tx_xpsk(bit);
|
|
// else normal bpsk transmission
|
|
} else {
|
|
sym = bit << 1;
|
|
tx_symbol(sym);
|
|
}
|
|
}
|
|
|
|
void psk::tx_xpsk(int bit)
|
|
{
|
|
static int bitcount = 0;
|
|
static unsigned int xpsk_sym = 0;
|
|
int fecbits = 0;
|
|
|
|
// If invalid value of bitcount, reset to 0
|
|
if (_puncturing || _xpsk || _16psk)
|
|
if ( (bitcount & 0x1) )
|
|
bitcount = 0;
|
|
|
|
//printf("\n\n bit: %d", bit);
|
|
|
|
// Pass one bit and return two bits
|
|
bitshreg = enc->encode(bit);
|
|
// Interleave
|
|
Txinlv->bits(&bitshreg); // Bit-interleave
|
|
fecbits = bitshreg;
|
|
|
|
|
|
/// DEBUG
|
|
/*
|
|
* static bool flip;
|
|
if (flip) {
|
|
flip = false;
|
|
fecbits = 0;
|
|
} else {
|
|
flip = true;
|
|
fecbits = 3;
|
|
}
|
|
*/
|
|
|
|
//printf("\nfecbits: %u", fecbits);
|
|
//printf("\nbitcount: %d", bitcount);
|
|
|
|
if (_xpsk) { // 2 bits-per-symbol. Transmit every call
|
|
xpsk_sym = static_cast<unsigned int>(fecbits);
|
|
tx_symbol(xpsk_sym);
|
|
return;
|
|
}
|
|
|
|
// DEBUG
|
|
/*
|
|
if (_8psk) {
|
|
tx_symbol(0);
|
|
tx_symbol(7);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// DEBUG, send a known pattern of symbols / bits
|
|
/*
|
|
* static int counter = 0;
|
|
counter++;
|
|
if ( counter > 7 ) counter = 0;
|
|
tx_symbol(1);
|
|
return;
|
|
*/
|
|
|
|
/*
|
|
else if (mode == MODE_8PSK ???) { // Puncture @ 5/6 rate | tx 3bits/symbol (8psk)
|
|
// Collect up to 8 bits
|
|
if ( x_bitcount < 8 ) {
|
|
x_xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << x_bitcount ;
|
|
x_xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << x_bitcount ;
|
|
x_bitcount += 2;
|
|
return;
|
|
|
|
// When 10 bits are buffered,
|
|
// drop 4 bits then transmit 6 bits (2-symbols)
|
|
} else {
|
|
x_xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << x_bitcount ;
|
|
x_xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << x_bitcount ;
|
|
tx_symbol( (x_xpsk_sym & 14) >> 1);
|
|
tx_symbol( (x_xpsk_sym & 448) >> 6);
|
|
x_xpsk_sym = x_bitcount = 0;
|
|
return;
|
|
}
|
|
}
|
|
*/
|
|
|
|
else if (_8psk && _puncturing) { // @ 2/3 rate
|
|
|
|
if ( 0 == bitcount) {
|
|
xpsk_sym = static_cast<unsigned int>(fecbits);
|
|
bitcount = 2;
|
|
return;
|
|
|
|
} else if ( 2 == bitcount ) {
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << 2 ;
|
|
/// Puncture -> //xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << 2 ;
|
|
tx_symbol(xpsk_sym & 7);
|
|
xpsk_sym = bitcount = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
else if (_8psk) { // 3 bits-per-symbol. Accumulate then tx.
|
|
|
|
if ( 0 == bitcount ) { // Empty xpsk_sym buffer: add 2 bits and return
|
|
//printf("\nxpsk_sym|preadd: %u", xpsk_sym);
|
|
xpsk_sym = static_cast<unsigned int>(fecbits);
|
|
//printf("\nxpsk_sym|postadd: %u", xpsk_sym);
|
|
bitcount = 2;
|
|
return ;
|
|
|
|
} else if ( 1 == bitcount ) { // xpsk_sym buffer with one bit: add 2 bits then tx and clear
|
|
//xpsk_sym <<= 2; // shift left 2 bits
|
|
|
|
//printf("\nxpsk_sym|preadd: %u", xpsk_sym);
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << 1 ;
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << 1 ;
|
|
//printf("\nxpsk_sym|postadd: %u", xpsk_sym);
|
|
|
|
//printf("\nxpsk_sym|postinlv: %u", xpsk_sym);
|
|
tx_symbol(xpsk_sym);
|
|
xpsk_sym = bitcount = 0;
|
|
return;
|
|
|
|
} else if ( 2 == bitcount ) { // xpsk_sym buffer with 2 bits: add 1 then tx and save next bit
|
|
//printf("\nxpsk_sym|preadd: %u", xpsk_sym);
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << 2 ;
|
|
//printf("\nxpsk_sym|postadd: %u", xpsk_sym);
|
|
|
|
//printf("\nxpsk_sym|postinlv: %u", xpsk_sym);
|
|
|
|
tx_symbol(xpsk_sym);
|
|
xpsk_sym = bitcount = 0;
|
|
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) >> 1 ;
|
|
bitcount = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
else if (_puncturing && _16psk) { // @ 3/4 Rate
|
|
|
|
if ( 0 == bitcount) {
|
|
xpsk_sym = static_cast<unsigned int>(fecbits);
|
|
bitcount = 2;
|
|
return;
|
|
|
|
} else if ( 2 == bitcount ) {
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << 2 ;
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << 2 ;
|
|
bitcount = 4;
|
|
return;
|
|
} else if ( 4 == bitcount ) {
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << 4 ;
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << 4 ;
|
|
xpsk_sym >>= 1; // Shift right to drop the lowest bit
|
|
xpsk_sym &= 15; // Drop the highest bit
|
|
tx_symbol(xpsk_sym);
|
|
xpsk_sym = bitcount = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
else if (_16psk) { // 4 bits-per-symbol. Transmit every-other run.
|
|
|
|
if ( 0 == bitcount) {
|
|
xpsk_sym = static_cast<unsigned int>(fecbits);
|
|
bitcount = 2;
|
|
return;
|
|
|
|
} else if ( 2 == bitcount ) {
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 1) << 2 ;
|
|
xpsk_sym |= (static_cast<unsigned int>(fecbits) & 2) << 2 ;
|
|
//Txinlv->bits(&xpsk_sym);
|
|
tx_symbol(xpsk_sym & 7);
|
|
xpsk_sym = bitcount = 0;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
unsigned char ch;
|
|
void psk::tx_char(unsigned char c)
|
|
{
|
|
ch = c;
|
|
const char *code;
|
|
char_symbols = acc_symbols;
|
|
if (_pskr || _xpsk || _8psk || _16psk) {
|
|
// 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++;
|
|
}
|
|
|
|
// Insert PSK varicode character-delimiting bits
|
|
if (! _pskr && !_xpsk && !_8psk && !_16psk) {
|
|
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 / 2; i++) {
|
|
tx_bit(1);
|
|
tx_bit(1);
|
|
}
|
|
// QPSK - flush the encoder
|
|
} else if (_qpsk) {
|
|
for (int i = 0; i < dcdbits; i++)
|
|
tx_bit(0);
|
|
// FEC : replace unmodulated carrier by an encoded sequence of zeros
|
|
} else if (!_disablefec && (_xpsk || _8psk || _16psk) ) {
|
|
for (int i = 0; i < flushlength; i++) {
|
|
tx_char(0); // <NUL>
|
|
}
|
|
// FEC disabled: use unmodulated carrier
|
|
} else if (_disablefec && ( _xpsk || _8psk || _16psk) ) {
|
|
for (int i=0; i<symbits; i++) {
|
|
tx_char(0); // Send <NUL> to clear bit accumulators on both Tx and Rx ends.
|
|
}
|
|
|
|
int symbol;
|
|
if (_16psk) symbol = 8;
|
|
else if (_8psk) symbol = 4;
|
|
else symbol = 2;
|
|
|
|
int _dcdbits = dcdbits - 1;
|
|
if(progStatus.psk8DCDShortFlag)
|
|
_dcdbits = 32/(symbits - 1);
|
|
|
|
for (int i = 0; i <= _dcdbits; i++) // DCD window is only 32-bits wide
|
|
tx_symbol(symbol); // 0 degrees
|
|
// Standard BPSK postamble
|
|
// DCD off sequence (unmodulated carrier)
|
|
} else {
|
|
for (int i = 0; i < dcdbits; i++)
|
|
tx_symbol(2); // 0 degrees
|
|
}
|
|
}
|
|
|
|
// Necessary to clear the interleaver before we start sending
|
|
void psk::clearbits()
|
|
{
|
|
bitshreg = 0;
|
|
enc->init();
|
|
Txinlv->flush();
|
|
}
|
|
|
|
int psk::tx_process()
|
|
{
|
|
int c;
|
|
|
|
// DCD window is only 32 bits, send a maximum of 3-times.
|
|
if(progStatus.psk8DCDShortFlag) {
|
|
if ( (_8psk || _xpsk || _16psk) && preamble > 96)
|
|
preamble = 96;
|
|
}
|
|
|
|
if (preamble > 0) {
|
|
if (_pskr || ((_xpsk || _8psk || _16psk) && !_disablefec) ) {
|
|
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);
|
|
tx_char(0); // <NUL>
|
|
}
|
|
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 || _xpsk || _8psk || _16psk) {
|
|
// 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 * 36 / 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();
|
|
}
|
|
}
|