dl-fldigi/src/psk/viewpsk.cxx

395 wiersze
11 KiB
C++

// ----------------------------------------------------------------------------
// viewpsk.cxx
//
// Copyright (C) 2008
// Dave Freese, W1HKJ
//
// This file is part of fldigi.
//
// 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/>.
// ----------------------------------------------------------------------------
// viewpsk is a multi channel psk decoder which allows the parallel processing
// of the complete audio spectrum from 200 to 3500 Hz in equal 100 Hz
// channels. Each channel is separately decoded and the decoded characters
// passed to the user interface routines for presentation. The number of
// channels can be up to and including 30.
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include "viewpsk.h"
#include "pskeval.h"
#include "pskcoeff.h"
#include "pskvaricode.h"
#include "misc.h"
#include "configuration.h"
#include "Viewer.h"
#include "qrunner.h"
#include "status.h"
extern waterfall *wf;
//=====================================================================
// Change the following for DCD low pass filter adjustment
#define SQLCOEFF 0.01
//#define SQLDECAY 50
#define SQLDECAY 20
//=====================================================================
viewpsk::viewpsk(pskeval* eval, trx_mode pskmode)
{
for (int i = 0; i < MAXCHANNELS; i++) {
channel[i].fir1 = (C_FIR_filter *)0;
channel[i].fir2 = (C_FIR_filter *)0;
}
evalpsk = eval;
viewmode = MODE_PREV;
restart(pskmode);
}
viewpsk::~viewpsk()
{
for (int i = 0; i < MAXCHANNELS; i++) {
if (channel[i].fir1) delete channel[i].fir1;
if (channel[i].fir2) delete channel[i].fir2;
}
}
void viewpsk::init()
{
nchannels = progdefaults.VIEWERchannels;
lowfreq = progdefaults.LowFreqCutoff;
for (int i = 0; i < MAXCHANNELS; i++) {
channel[i].phaseacc = 0;
channel[i].prevsymbol = cmplx (1.0, 0.0);
channel[i].quality = cmplx (0.0, 0.0);
channel[i].shreg = 0;
channel[i].dcdshreg = 0;
channel[i].dcd = false;
channel[i].bitclk = 0;
channel[i].freqerr = 0.0;
channel[i].timeout = 0;
channel[i].frequency = NULLFREQ;
channel[i].reset = false;
channel[i].acquire = 0;
for (int j = 0; j < 16; j++)
channel[i].syncbuf[j] = 0.0;
}
for (int i = 0; i < nchannels; i++)
REQ(&viewclearchannel, i);
evalpsk->clear();
reset_all = false;
}
void viewpsk::restart(trx_mode pskmode)
{
if (viewmode == pskmode) return;
viewmode = pskmode;
double fir1c[64];
double fir2c[64];
switch (viewmode) {
case MODE_PSK31:
symbollen = 256;
dcdbits = 32;
break;
case MODE_PSK63:
case MODE_PSK63F:
symbollen = 128;
dcdbits = 64;
break;
case MODE_PSK125:
case MODE_PSK125R:
symbollen = 64;
dcdbits = 128;
break;
case MODE_PSK250:
case MODE_PSK250R:
symbollen = 32;
dcdbits = 256;
break;
case MODE_PSK500:
case MODE_PSK500R:
symbollen = 16;
dcdbits = 512;
break;
default: // punt! mode not one of the above.
symbollen = 512;
dcdbits = 32;
break;
}
wsincfilt(fir1c, 1.0 / symbollen, true); // creates fir1c matched sin(x)/x filter w blackman
wsincfilt(fir2c, 1.0 / 16.0, true); // creates fir2c matched sin(x)/x filter w blackman
for (int i = 0; i < MAXCHANNELS; i++) {
if (channel[i].fir1) delete channel[i].fir1;
channel[i].fir1 = new C_FIR_filter();
channel[i].fir1->init(FIRLEN, symbollen / 16, fir1c, fir1c);
if (channel[i].fir2) delete channel[i].fir2;
channel[i].fir2 = new C_FIR_filter();
channel[i].fir2->init(FIRLEN, 1, fir2c, fir2c);
}
bandwidth = VPSKSAMPLERATE / symbollen;
init();
}
//=============================================================================
//========================= viewpsk receive routines ==========================
//=============================================================================
void viewpsk::rx_bit(int ch, int bit)
{
int c;
channel[ch].shreg = (channel[ch].shreg << 1) | !!bit;
if ((channel[ch].shreg & 3) == 0) {
c = psk_varicode_decode(channel[ch].shreg >> 2);
channel[ch].shreg = 0;
if (c == -1) return;
if (c == '\n' || c == '\r') c = ' ';
if (iscntrl(c & 0xFF)) return;
REQ(&viewaddchr, ch, (int)channel[ch].frequency, c, viewmode);
}
}
void viewpsk::afc(int ch)
{
if (channel[ch].dcd == true || channel[ch].acquire) {
double error;
double lower_bound = (lowfreq + ch * 100) - bandwidth;
if (lower_bound < bandwidth) lower_bound = bandwidth;
double upper_bound = lowfreq + (ch+1)*100 + bandwidth;
error = (channel[ch].phase - channel[ch].bits * M_PI / 2);
if (error < M_PI / 2.0) error += 2 * M_PI;
if (error > M_PI / 2.0) error -= 2 * M_PI;
error *= (VPSKSAMPLERATE / (symbollen * 2 * M_PI))/16.0;
channel[ch].frequency -= error;
channel[ch].frequency = CLAMP(channel[ch].frequency, lower_bound, upper_bound);
}
if (channel[ch].acquire) channel[ch].acquire--;
}
void viewpsk::clearch(int n)
{
channel[n].reset = true;
evalpsk->clear();
}
void viewpsk::clear()
{
for (int i = 0; i < nchannels; i++)
channel[i].reset = true;
evalpsk->clear();
}
inline void viewpsk::timeout_check()
{
for (int ch = 0; ch < nchannels; ch++) {
if (channel[ch].timeout) channel[ch].timeout--;
if (channel[ch].frequency == NULLFREQ) continue;
if (channel[ch].reset || (!channel[ch].timeout && !channel[ch].acquire) ||
(ch && (fabs(channel[ch-1].frequency - channel[ch].frequency) < bandwidth))) {
channel[ch].reset = false;
channel[ch].dcd = 0;
channel[ch].frequency = NULLFREQ;
channel[ch].acquire = 0;
REQ(&viewclearchannel, ch);
REQ(&viewaddchr, ch, NULLFREQ, 0, viewmode);
}
}
}
void viewpsk::findsignals()
{
if (!evalpsk) return;
double level = progStatus.VIEWER_psksquelch;
int nomfreq = 0;
int lfreq = 0;
int hfreq = 0;
int ftest;
int f1, f2;
timeout_check();
for (int i = 0; i < nchannels; i++) {
nomfreq = lowfreq + 100 * i;
lfreq = nomfreq - 20;
hfreq = nomfreq + 120; // suppress detection outside of this range
if (!channel[i].dcd && !channel[i].timeout) {
if (!channel[i].acquire) {
channel[i].frequency = NULLFREQ;
f1 = nomfreq - 0.5 * bandwidth;
if (f1 < 2 * bandwidth) f1 = 2 * bandwidth;
f2 = f1 + 100;
ftest = (f1 + f2) / 2;
} else {
if (channel[i].frequency < lfreq || channel[i].frequency >= hfreq)
channel[i].frequency = nomfreq + 50;
ftest = channel[i].frequency;
f1 = ftest - bandwidth;
f2 = ftest + bandwidth;
if (f1 < 2 * bandwidth) {
f1 = 2 * bandwidth;
f2 = f1 + bandwidth;
}
}
if (evalpsk->peak(ftest, f1, f2, level)) {
if (ftest < lfreq || ftest >= hfreq) goto nexti;
f1 = ftest - bandwidth;
f2 = ftest + bandwidth;
if (evalpsk->peak(ftest, f1, f2, level)) {
if (ftest < lfreq || ftest >= hfreq) goto nexti;
if (i &&
(channel[i-1].dcd || channel[i-1].acquire) &&
fabs(channel[i-1].frequency - ftest) < bandwidth) goto nexti;
if ((i < nchannels - 1) &&
(channel[i+1].dcd || channel[i+1].acquire) &&
fabs(channel[i+1].frequency - ftest) < bandwidth) goto nexti;
channel[i].frequency = ftest;
channel[i].freqerr = 0.0;
channel[i].metric = 0.0;
if (!channel[i].acquire)
channel[i].acquire = 2 * 8000 / 512;
}
}
}
nexti: ;
}
}
void viewpsk::rx_symbol(int ch, cmplx symbol)
{
int n;
channel[ch].phase = arg ( conj(channel[ch].prevsymbol) * symbol );
channel[ch].prevsymbol = symbol;
if (channel[ch].phase < 0)
channel[ch].phase += 2 * M_PI;
channel[ch].bits = (((int) (channel[ch].phase / M_PI + 0.5)) & 1) << 1;
n = 2; // psk
channel[ch].dcdshreg = (channel[ch].dcdshreg << 2) | channel[ch].bits;
switch (channel[ch].dcdshreg) {
case 0xAAAAAAAA: /* DCD on by preamble */
if (!channel[ch].dcd)
REQ(&viewaddchr, ch, (int)channel[ch].frequency, 0, viewmode);
channel[ch].dcd = true;
channel[ch].quality = cmplx (1.0, 0.0);
channel[ch].metric = 100;
channel[ch].timeout = progdefaults.VIEWERtimeout * VPSKSAMPLERATE / WFBLOCKSIZE;
channel[ch].acquire = 0;
break;
case 0: /* DCD off by postamble */
channel[ch].dcd = false;
channel[ch].quality = cmplx (0.0, 0.0);
channel[ch].metric = 0;
channel[ch].acquire = 0;
// channel[ch].frequency = NULLFREQ;
break;
default:
channel[ch].quality = cmplx (
decayavg(channel[ch].quality.real(), cos(n*channel[ch].phase), SQLDECAY),
decayavg(channel[ch].quality.imag(), sin(n*channel[ch].phase), SQLDECAY));
// channel[ch].quality.re =
// decayavg(channel[ch].quality.re, cos(n*channel[ch].phase), SQLDECAY);
// channel[ch].quality.im =
// decayavg(channel[ch].quality.im, sin(n*channel[ch].phase), SQLDECAY);
channel[ch].metric = norm(channel[ch].quality);
if (channel[ch].metric > (progStatus.VIEWER_psksquelch + 6.0)/26.0) {
channel[ch].dcd = true;
} else {
channel[ch].dcd = false;
}
}
if (channel[ch].dcd == true) {
channel[ch].timeout = progdefaults.VIEWERtimeout * VPSKSAMPLERATE / WFBLOCKSIZE;
rx_bit(ch, !channel[ch].bits);
channel[ch].acquire = 0;
}
}
int viewpsk::rx_process(const double *buf, int len)
{
double sum;
double ampsum;
int idx;
cmplx z, z2;
if (nchannels != progdefaults.VIEWERchannels || lowfreq != progdefaults.LowFreqCutoff)
init();
// process all channels
for (int ch = 0; ch < nchannels; ch++) {
if (channel[ch].frequency == NULLFREQ) continue;
for (int ptr = 0; ptr < len; ptr++) {
// Mix with the internal NCO for each channel
z = cmplx ( buf[ptr] * cos(channel[ch].phaseacc), buf[ptr] * sin(channel[ch].phaseacc) );
channel[ch].phaseacc += 2.0 * M_PI * channel[ch].frequency / VPSKSAMPLERATE;
// filter & decimate
if (channel[ch].fir1->run( z, z )) {
channel[ch].fir2->run( z, z2 );
idx = (int) channel[ch].bitclk;
sum = 0.0;
ampsum = 0.0;
channel[ch].syncbuf[idx] = 0.8 * channel[ch].syncbuf[idx] + 0.2 * abs(z2);
for (int i = 0; i < 8; i++) {
sum += (channel[ch].syncbuf[i] - channel[ch].syncbuf[i+8]);
ampsum += (channel[ch].syncbuf[i] + channel[ch].syncbuf[i+8]);
}
sum = (ampsum == 0 ? 0 : sum / ampsum);
channel[ch].bitclk -= sum / 5.0;
channel[ch].bitclk += 1;
if (channel[ch].bitclk < 0) channel[ch].bitclk += 16.0;
if (channel[ch].bitclk >= 16.0) {
channel[ch].bitclk -= 16.0;
rx_symbol(ch, z2);
afc(ch);
}
}
}
}
findsignals();
return 0;
}
int viewpsk::get_freq(int n)
{
if (channel[n].dcd)
return (int)channel[n].frequency;
return NULLFREQ;
}