Signal processing for multi-channel RTTY decoding
pull/2/head
David Freese 2010-12-27 17:26:04 -06:00
rodzic 4344d53692
commit c7c3a43c78
8 zmienionych plików z 752 dodań i 19 usunięć

Wyświetl plik

@ -239,6 +239,7 @@ fldigi_SOURCES += \
cw_rtty/cw.cxx \
cw_rtty/morse.cxx \
cw_rtty/rtty.cxx \
cw_rtty/view_rtty.cxx \
contestia/contestia.cxx \
dialogs/colorsfonts.cxx \
dialogs/confdialog.cxx \
@ -360,6 +361,7 @@ fldigi_SOURCES += \
include/ringbuffer.h \
include/rsid.h \
include/rtty.h \
include/view_rtty.h \
include/rx_extract.h \
include/speak.h \
include/serial.h \

Wyświetl plik

@ -27,7 +27,7 @@
#include <iostream>
using namespace std;
#include "rtty.h"
#include "view_rtty.h"
#include "fl_digi.h"
#include "digiscope.h"
#include "misc.h"
@ -36,6 +36,9 @@ using namespace std;
#include "configuration.h"
#include "status.h"
#include "digiscope.h"
#include "trx.h"
view_rtty *rttyviewer = (view_rtty *)0;
//=====================================================================
// Baudot support
@ -220,6 +223,9 @@ void rtty::restart()
dspcnt = 2*(nbits + 2);
clear_zdata = true;
rttyviewer->restart();
}
rtty::rtty(trx_mode tty_mode)
@ -242,6 +248,8 @@ rtty::rtty(trx_mode tty_mode)
samples = new complex[8];
::rttyviewer = new view_rtty(mode);
restart();
}
@ -495,6 +503,8 @@ int rtty::rx_process(const double *buf, int len)
wf->redraw_marker();
}
if (rttyviewer && !bHistory) rttyviewer->rx_process(buf, len);
Metric();
while (len-- > 0) {

Wyświetl plik

@ -0,0 +1,544 @@
// ----------------------------------------------------------------------------
// rtty.cxx -- RTTY modem
//
// Copyright (C) 2006-2010
// Dave Freese, W1HKJ
//
// This file is part of fldigi. Adapted from code contained in gmfsk source code
// distribution.
//
// 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 <iostream>
using namespace std;
#include "rtty.h"
#include "view_rtty.h"
#include "fl_digi.h"
#include "digiscope.h"
#include "misc.h"
#include "waterfall.h"
#include "confdialog.h"
#include "configuration.h"
#include "status.h"
#include "digiscope.h"
#include "Viewer.h"
#include "qrunner.h"
//=====================================================================
// Baudot support
//=====================================================================
static unsigned char letters[32] = {
'\0', 'E', '\n', 'A', ' ', 'S', 'I', 'U',
'\r', 'D', 'R', 'J', 'N', 'F', 'C', 'K',
'T', 'Z', 'L', 'W', 'H', 'Y', 'P', 'Q',
'O', 'B', 'G', '·', 'M', 'X', 'V', '·'
};
// U.S. version of the figures case.
static unsigned char figures[32] = {
'\0', '3', '\n', '-', ' ', '\a', '8', '7',
'\r', '$', '4', '\'', ',', '!', ':', '(',
'5', '"', ')', '2', '#', '6', '0', '1',
'9', '?', '&', '·', '.', '/', ';', '·'
};
const double view_rtty::SHIFT[] = {23, 85, 160, 170, 182, 200, 240, 350, 425, 850};
const double view_rtty::BAUD[] = {45, 45.45, 50, 56, 75, 100, 110, 150, 200, 300};
const int view_rtty::BITS[] = {5, 7, 8};
void view_rtty::rx_init()
{
for (int ch = 0; ch < progdefaults.VIEWERchannels; ch++) {
channel[ch].state = IDLE;
channel[ch].rxstate = RTTY_RX_STATE_IDLE;
channel[ch].rxmode = LETTERS;
channel[ch].phaseacc = 0;
channel[ch].frequency = NULLFREQ;
for (int i = 0; i < RTTYMaxSymLen; i++ ) {
channel[ch].bbfilter[i] = 0.0;
}
channel[ch].bitfilt->reset();
channel[ch].poserr = channel[ch].negerr = 0.0;
}
}
void view_rtty::init()
{
bool wfrev = wf->Reverse();
bool wfsb = wf->USB();
reverse = wfrev ^ !wfsb;
rx_init();
}
view_rtty::~view_rtty()
{
if (hilbert) delete hilbert;
for (int ch = 0; ch < MAX_CHANNELS; ch ++) {
if (channel[ch].bitfilt) delete channel[ch].bitfilt;
if (channel[ch].bpfilt) delete channel[ch].bpfilt;
}
}
void view_rtty::restart()
{
double stl;
rtty_shift = shift = (progdefaults.rtty_shift >= 0 ?
SHIFT[progdefaults.rtty_shift] : progdefaults.rtty_custom_shift);
rtty_baud = BAUD[progdefaults.rtty_baud];
nbits = rtty_bits = BITS[progdefaults.rtty_bits];
if (rtty_bits == 5)
rtty_parity = RTTY_PARITY_NONE;
else
switch (progdefaults.rtty_parity) {
case 0 : rtty_parity = RTTY_PARITY_NONE; break;
case 1 : rtty_parity = RTTY_PARITY_EVEN; break;
case 2 : rtty_parity = RTTY_PARITY_ODD; break;
case 3 : rtty_parity = RTTY_PARITY_ZERO; break;
case 4 : rtty_parity = RTTY_PARITY_ONE; break;
default : rtty_parity = RTTY_PARITY_NONE; break;
}
rtty_stop = progdefaults.rtty_stop;
symbollen = (int) (samplerate / rtty_baud + 0.5);
bflen = symbollen/3;
set_bandwidth(shift);
rtty_BW = progdefaults.RTTY_BW;
bp_filt_lo = (shift/2.0 - rtty_BW/2.0) / samplerate;
if (bp_filt_lo < 0) bp_filt_lo = 0;
bp_filt_hi = (shift/2.0 + rtty_BW/2.0) / samplerate;
for (int ch = 0; ch < MAX_CHANNELS; ch ++) {
if (channel[ch].bpfilt) {
channel[ch].bpfilt->create_filter(bp_filt_lo, bp_filt_hi);
channel[ch].bitfilt->setLength(bflen);
} else {
channel[ch].bpfilt = new fftfilt(bp_filt_lo, bp_filt_hi, 1024);
channel[ch].bitfilt = new Cmovavg(bflen);
}
channel[ch].state = IDLE;
channel[ch].freqerr = 0.0;
channel[ch].filterptr = 0;
channel[ch].poscnt = 0;
channel[ch].negcnt = 0;
channel[ch].posfreq = 0;
channel[ch].negfreq = 0.0;
channel[ch].metric = 0.0;
channel[ch].sigpwr = 0.0;
channel[ch].noisepwr = 0.0;
channel[ch].freqerrlo = 0.0;
channel[ch].freqerrhi = 0.0;
channel[ch].sigsearch = 0;
channel[ch].frequency = NULLFREQ;
channel[ch].counter = symbollen / 2;
}
// stop length = 1, 1.5 or 2 bits
rtty_stop = progdefaults.rtty_stop;
if (rtty_stop == 0) stl = 1.0;
else if (rtty_stop == 1) stl = 1.5;
else stl = 2.0;
stoplen = (int) (stl * samplerate / rtty_baud + 0.5);
rx_init();
}
view_rtty::view_rtty(trx_mode tty_mode)
{
cap |= CAP_AFC | CAP_REV;
mode = tty_mode;
samplerate = RTTY_SampleRate;
for (int ch = 0; ch < MAX_CHANNELS; ch ++) {
channel[ch].bpfilt = (fftfilt *)0;
channel[ch].bitfilt = (Cmovavg *)0;
}
hilbert = new C_FIR_filter();
hilbert->init_hilbert(37, 1);
restart();
}
complex view_rtty::mixer(int ch, complex in)
{
complex z;
z.re = cos(channel[ch].phaseacc);
z.im = sin(channel[ch].phaseacc);
z = z * in;
channel[ch].phaseacc -= TWOPI * channel[ch].frequency / samplerate;
if (channel[ch].phaseacc > M_PI)
channel[ch].phaseacc -= TWOPI;
else if (channel[ch].phaseacc < M_PI)
channel[ch].phaseacc += TWOPI;
return z;
}
unsigned char view_rtty::bitreverse(unsigned char in, int n)
{
unsigned char out = 0;
for (int i = 0; i < n; i++)
out = (out << 1) | ((in >> i) & 1);
return out;
}
static int rparity(int c)
{
int w = c;
int p = 0;
while (w) {
p += (w & 1);
w >>= 1;
}
return p & 1;
}
int view_rtty::rttyparity(unsigned int c)
{
c &= (1 << nbits) - 1;
switch (rtty_parity) {
default:
case RTTY_PARITY_NONE:
return 0;
case RTTY_PARITY_ODD:
return rparity(c);
case RTTY_PARITY_EVEN:
return !rparity(c);
case RTTY_PARITY_ZERO:
return 0;
case RTTY_PARITY_ONE:
return 1;
}
}
int view_rtty::decode_char(int ch)
{
unsigned int parbit, par, data;
parbit = (channel[ch].rxdata >> nbits) & 1;
par = rttyparity(channel[ch].rxdata);
if (rtty_parity != RTTY_PARITY_NONE && parbit != par)
return 0;
data = channel[ch].rxdata & ((1 << nbits) - 1);
if (nbits == 5)
return baudot_dec(ch, data);
return data;
}
bool view_rtty::rx(int ch, bool bit)
{
bool flag = false;
unsigned char c;
switch (channel[ch].rxstate) {
case RTTY_RX_STATE_IDLE:
if (!bit) {
channel[ch].rxstate = RTTY_RX_STATE_START;
channel[ch].counter = symbollen / 2;
}
break;
case RTTY_RX_STATE_START:
if (--channel[ch].counter == 0) {
if (!bit) {
channel[ch].rxstate = RTTY_RX_STATE_DATA;
channel[ch].counter = symbollen;
channel[ch].bitcntr = 0;
channel[ch].rxdata = 0;
} else {
channel[ch].rxstate = RTTY_RX_STATE_IDLE;
}
} else
if (bit) channel[ch].rxstate = RTTY_RX_STATE_IDLE;
break;
case RTTY_RX_STATE_DATA:
if (--channel[ch].counter == 0) {
channel[ch].rxdata |= bit << channel[ch].bitcntr++;
channel[ch].counter = symbollen;
}
if (channel[ch].bitcntr == nbits) {
if (rtty_parity == RTTY_PARITY_NONE) {
channel[ch].rxstate = RTTY_RX_STATE_STOP;
}
else {
channel[ch].rxstate = RTTY_RX_STATE_PARITY;
}
}
break;
case RTTY_RX_STATE_PARITY:
if (--channel[ch].counter == 0) {
channel[ch].rxstate = RTTY_RX_STATE_STOP;
channel[ch].rxdata |= bit << channel[ch].bitcntr++;
channel[ch].counter = symbollen;
}
break;
case RTTY_RX_STATE_STOP:
if (--channel[ch].counter == 0) {
if (bit) {
if (channel[ch].metric >= progStatus.sldrSquelchValue) {
c = decode_char(ch);
// print this RTTY_CHANNEL
if ( c != 0 )
REQ(&viewaddchr, ch, (int)channel[ch].frequency, c, mode);
// put_rx_char(progdefaults.rx_lowercase ? tolower(c) : c);
}
flag = true;
}
channel[ch].rxstate = RTTY_RX_STATE_STOP2;
channel[ch].counter = symbollen / 2;
}
break;
case RTTY_RX_STATE_STOP2:
if (--channel[ch].counter == 0) {
channel[ch].rxstate = RTTY_RX_STATE_IDLE;
}
break;
}
return flag;
}
void view_rtty::Metric(int ch)
{
double level = pow(10, progdefaults.VIEWERsquelch / 10.0);
double delta = rtty_baud/2.0;
double np =
wf->powerDensity(channel[ch].frequency - shift * 1.5, delta) +
wf->powerDensity(channel[ch].frequency + shift * 1.5, delta) + 1e-10;
double sp =
wf->powerDensity(channel[ch].frequency - shift/2, delta) +
wf->powerDensity(channel[ch].frequency + shift/2, delta) + 1e-10;
channel[ch].sigpwr = decayavg( channel[ch].sigpwr, sp, sp - channel[ch].sigpwr > 0 ? 2 : 8);
channel[ch].noisepwr = decayavg( channel[ch].noisepwr, np, 32 );
channel[ch].metric = CLAMP(channel[ch].sigpwr/channel[ch].noisepwr, 0.0, 100.0);
if (channel[ch].state == RCVNG)
if (channel[ch].metric < level) {
channel[ch].frequency = NULLFREQ;
channel[ch].metric = 0;
channel[ch].freqerr = 0;
channel[ch].state = IDLE;
REQ(&viewclearchannel, ch);
}
}
void view_rtty::find_signals()
{
double spwrhi = 0.0, spwrlo = 0.0, npwr = 0.0;
double level = pow(10, progdefaults.VIEWERsquelch / 10.0);
for (int i = 0; i < progdefaults.VIEWERchannels; i++) {
if (channel[i].state != IDLE) continue;
int cf = progdefaults.LowFreqCutoff + 100 * i;
if (!i) cf += shift;
for (int chf = cf; chf < cf + 100; chf += 5) {
spwrlo = wf->powerDensity(chf - shift/2, rtty_baud) / 2;
spwrhi = wf->powerDensity(chf + shift/2, rtty_baud) / 2;
npwr = wf->powerDensity(chf, rtty_baud / 2) + 1e-10;
if ((spwrlo / npwr > level) && (spwrhi / npwr > level)) {
channel[i].frequency = chf;
channel[i].sigsearch = SIGSEARCH;
channel[i].state = SEARCHING;
REQ(&viewaddchr, i, (int)channel[i].frequency, 0, mode);
break;
}
}
}
}
void view_rtty::clearch(int ch)
{
channel[ch].state = IDLE;
channel[ch].rxstate = RTTY_RX_STATE_IDLE;
channel[ch].rxmode = LETTERS;
channel[ch].phaseacc = 0;
channel[ch].frequency = NULLFREQ;
for (int i = 0; i < RTTYMaxSymLen; i++ ) {
channel[ch].bbfilter[i] = 0.0;
}
channel[ch].bitfilt->reset();
channel[ch].poserr = channel[ch].negerr = 0.0;
REQ( &viewclearchannel, ch);
}
void view_rtty::clear()
{
for (int ch = 0; ch < progdefaults.VIEWERchannels; ch++) {
channel[ch].state = IDLE;
channel[ch].rxstate = RTTY_RX_STATE_IDLE;
channel[ch].rxmode = LETTERS;
channel[ch].phaseacc = 0;
channel[ch].frequency = NULLFREQ;
for (int i = 0; i < RTTYMaxSymLen; i++ ) {
channel[ch].bbfilter[i] = 0.0;
}
channel[ch].bitfilt->reset();
channel[ch].poserr = channel[ch].negerr = 0.0;
}
}
int view_rtty::rx_process(const double *buf, int buflen)
{
complex z, *zp;
double f = 0.0;
double fin;
static bool bit = true;
int n = 0;
double deadzone = shift/4;
double ferr = 0;
if (progdefaults.RTTY_BW != rtty_BW) {
rtty_BW = progdefaults.RTTY_BW;
bp_filt_lo = (shift/2.0 - rtty_BW/2.0) / samplerate;
if (bp_filt_lo < 0) bp_filt_lo = 0;
bp_filt_hi = (shift/2.0 + rtty_BW/2.0) / samplerate;
for (int ch = 0; ch < MAX_CHANNELS; ch++)
channel[ch].bpfilt->create_filter(bp_filt_lo, bp_filt_hi);
}
for (int ch = 0; ch < progdefaults.VIEWERchannels; ch++) {
if (channel[ch].state == IDLE)
continue;
if (channel[ch].sigsearch) {
channel[ch].sigsearch--;
if (!channel[ch].sigsearch)
channel[ch].state = RCVNG;
}
for (int len = 0; len < buflen; len++) {
z.re = z.im = buf[len];
hilbert->run(z, z);
z = mixer(ch, z);
n = channel[ch].bpfilt->run(z, &zp);
if (n) {
for (int i = 0; i < n; i++) {
fin = (channel[ch].prevsmpl % zp[i]).arg() * samplerate / TWOPI;
channel[ch].prevsmpl = zp[i];
if (fin > 0.0) {
channel[ch].poscnt++;
channel[ch].posfreq += fin;
}
if (fin < 0.0) {
channel[ch].negcnt++;
channel[ch].negfreq += fin;
}
fin = CLAMP(fin, - rtty_shift, rtty_shift);
// filter the result with a moving average filter
f = channel[ch].bitfilt->run(fin);
// hysterisis dead zone in frequency discriminator bit detector
if (f > deadzone )
bit = true;
if (f < -deadzone)
bit = false;
if (channel[ch].state == RCVNG) {
if ( rx( ch, reverse ? !bit : bit ) ) {
if (channel[ch].poscnt && channel[ch].negcnt) {
channel[ch].poserr = channel[ch].posfreq / channel[ch].poscnt;
channel[ch].negerr = channel[ch].negfreq / channel[ch].negcnt;
ferr = -(channel[ch].poserr + channel[ch].negerr) /
(2*(SIGSEARCH - channel[ch].sigsearch + 1));
int fs = progdefaults.rtty_afcspeed;
int avging;
if (fs == 0) avging = 8;
else if (fs == 1) avging = 4;
else avging = 1;
channel[ch].freqerr = decayavg(channel[ch].freqerr, ferr, avging);
channel[ch].poscnt = channel[ch].negcnt = 0;
channel[ch].posfreq = channel[ch].negfreq = 0.0;
}
}
}
}
}
}
Metric(ch);
if (channel[ch].metric > progStatus.sldrSquelchValue)
channel[ch].frequency -= ferr;
}
find_signals();
return 0;
}
char view_rtty::baudot_dec(int ch, unsigned char data)
{
int out = 0;
switch (data) {
case 0x1F: /* letters */
channel[ch].rxmode = LETTERS;
break;
case 0x1B: /* figures */
channel[ch].rxmode = FIGURES;
break;
case 0x04: /* unshift-on-space */
if (progdefaults.UOSrx)
channel[ch].rxmode = LETTERS;
return ' ';
break;
default:
if (channel[ch].rxmode == LETTERS)
out = letters[data];
else
out = figures[data];
break;
}
return out;
}
//=====================================================================
// RTTY transmit
//=====================================================================
int view_rtty::tx_process()
{
return 0;
}

Wyświetl plik

@ -43,6 +43,7 @@
#include "icons.h"
#include "psk_browser.h"
#include "view_rtty.h"
extern pskBrowser *mainViewer;
@ -67,7 +68,7 @@ fre_t seek_re("CQ", REG_EXTENDED | REG_ICASE | REG_NOSUB);
void initViewer()
{
if (!pskviewer) return;
// if (!pskviewer) return;
usb = wf->USB();
rfc = wf->rfcarrier();
if (mainViewer) {
@ -149,10 +150,13 @@ static void cb_btnCloseViewer(Fl_Button*, void*) {
static void cb_btnClearViewer(Fl_Button*, void*) {
brwsViewer->clear();
mainViewer->clear();
if (pskviewer) pskviewer->clear();
if (rttyviewer) rttyviewer->clear();
}
static void cb_brwsViewer(Fl_Hold_Browser*, void*) {
if (!pskviewer) return;
if (!pskviewer && !rttyviewer) return;
int sel = brwsViewer->value();
if (sel == 0 || sel > progdefaults.VIEWERchannels)
return;
@ -164,14 +168,17 @@ static void cb_brwsViewer(Fl_Hold_Browser*, void*) {
ReceiveText->addstr(brwsViewer->line(sel).c_str(), FTextBase::ALTR);
active_modem->set_freq(brwsViewer->freq(sel));
active_modem->set_sigsearch(SIGSEARCH);
mainViewer->select(sel);
}
break;
case FL_MIDDLE_MOUSE: // copy from modem
// set_freq(sel, active_modem->get_freq());
break;
case FL_RIGHT_MOUSE: // reset
pskviewer->clearch(sel-1);
if (pskviewer) pskviewer->clearch(sel-1);
if (rttyviewer) rttyviewer->clearch(sel-1);
brwsViewer->deselect();
mainViewer->deselect();
default:
break;
}
@ -199,7 +206,7 @@ Fl_Double_Window* createViewer(void)
Fl_Double_Window* w = new Fl_Double_Window(progStatus.VIEWERxpos, progStatus.VIEWERypos,
viewerwidth + 2 * BWSR_BORDER,
viewerheight + 2 * BWSR_BORDER + pad + 20,
_("PSK Browser"));
_("Signal Browser"));
brwsViewer = new pskBrowser(BWSR_BORDER, BWSR_BORDER, viewerwidth, viewerheight);
brwsViewer->callback((Fl_Callback*)cb_brwsViewer);
brwsViewer->setfont(progdefaults.ViewerFontnbr, progdefaults.ViewerFontsize);
@ -262,11 +269,18 @@ void openViewer()
void viewer_paste_freq(int freq)
{
int ch = (freq - progdefaults.LowFreqCutoff) / 100;
mainViewer->select(WCLAMP(0, progdefaults.VIEWERchannels, ch));
if (pskviewer) {
for (int i = 0; i < progdefaults.VIEWERchannels; i++) {
if (fabs(pskviewer->get_freq(i) - freq) <= 50) {
mainViewer->select(i+1);
if (dlgViewer)
brwsViewer->select(WCLAMP(0, progdefaults.VIEWERchannels, ch));
brwsViewer->select(i+1);
return;
}
}
}
if (rttyviewer) {
}
}

Wyświetl plik

@ -78,7 +78,7 @@
#include "wefax.h"
#include "wefax-pic.h"
#include "mt63.h"
#include "rtty.h"
#include "view_rtty.h"
#include "olivia.h"
#include "contestia.h"
#include "thor.h"
@ -2242,7 +2242,11 @@ void cb_mvsquelch(Fl_Widget *w, void *d)
void cb_btnClearMViewer(Fl_Widget *w, void *d)
{
pskviewer->clear();
if (brwsViewer)
brwsViewer->clear();
mainViewer->clear();
if (pskviewer) pskviewer->clear();
if (rttyviewer) rttyviewer->clear();
}
int default_handler(int event)
@ -3288,7 +3292,7 @@ void cb_btnCW_Default(Fl_Widget *w, void *v)
}
static void cb_mainViewer(Fl_Hold_Browser*, void*) {
if (!pskviewer) return;
if (!pskviewer && !rttyviewer) return;
int sel = mainViewer->value();
if (sel == 0 || sel > progdefaults.VIEWERchannels)
return;
@ -3300,11 +3304,14 @@ static void cb_mainViewer(Fl_Hold_Browser*, void*) {
ReceiveText->addstr(mainViewer->line(sel).c_str(), FTextBase::ALTR);
active_modem->set_freq(mainViewer->freq(sel));
active_modem->set_sigsearch(SIGSEARCH);
if (brwsViewer) brwsViewer->select(sel);
}
break;
case FL_RIGHT_MOUSE: // reset
pskviewer->clearch(sel-1);
if (pskviewer) pskviewer->clearch(sel-1);
if (rttyviewer) rttyviewer->clearch(sel-1);
mainViewer->deselect();
if (brwsViewer) brwsViewer->deselect();
break;
default:
break;

Wyświetl plik

@ -112,7 +112,7 @@ private:
complex quality;
int acquire;
viewpsk* pskviewer;
// viewpsk* pskviewer;
pskeval* evalpsk;
void rx_symbol(complex symbol);

Wyświetl plik

@ -0,0 +1,154 @@
// ----------------------------------------------------------------------------
// rtty.h -- RTTY modem
//
// Copyright (C) 2006
// Dave Freese, W1HKJ
//
// 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/>.
// ----------------------------------------------------------------------------
#ifndef VIEW_RTTY_H
#define VIEW_RTTY_H
#include "rtty.h"
#include "complex.h"
#include "modem.h"
#include "globals.h"
#include "filters.h"
#include "fftfilt.h"
#include "digiscope.h"
#define VIEW_RTTY_SampleRate 8000
#define VIEW_RTTYMaxSymLen (VIEW_RTTY_SampleRate / 23)
#define MAX_CHANNELS 30
enum CHANNEL_STATE {IDLE, SEARCHING, RCVNG};
struct RTTY_CHANNEL {
int state;
double phaseacc;
C_FIR_filter *lpfilt;
Cmovavg *bitfilt;
fftfilt *bpfilt;
double bbfilter[MAXPIPE];
unsigned int filterptr;
double metric;
int rxmode;
RTTY_RX_STATE rxstate;
double frequency;
double freqerr;
double phase;
double posfreq;
double negfreq;
double freqerrhi;
double freqerrlo;
double poserr;
double negerr;
int poscnt;
int negcnt;
double sigpwr;
double noisepwr;
double avgsig;
double prevsymbol;
complex prevsmpl;
int counter;
int bitcntr;
int rxdata;
int sigsearch;
};
class view_rtty : public modem {
public:
static const double SHIFT[];
static const double BAUD[];
static const int BITS[];
private:
double shift;
int symbollen;
int nbits;
int stoplen;
int msb;
bool useFSK;
RTTY_CHANNEL channel[MAX_CHANNELS];
C_FIR_filter *hilbert;
double rtty_squelch;
double rtty_shift;
double rtty_BW;
double rtty_baud;
int rtty_bits;
RTTY_PARITY rtty_parity;
int rtty_stop;
bool rtty_reverse;
bool rtty_msbfirst;
int bflen;
double bp_filt_lo;
double bp_filt_hi;
int txmode;
int preamble;
void clear_syncscope();
void update_syncscope();
inline complex mixer(int ch, complex in);
unsigned char bitreverse(unsigned char in, int n);
int decode_char(int ch);
int rttyparity(unsigned int);
bool rx(int ch, bool bit);
int rttyxprocess();
char baudot_dec(int ch, unsigned char data);
void Metric(int ch);
public:
view_rtty(trx_mode mode);
~view_rtty();
void init();
void rx_init();
void tx_init(SoundBase *sc){}
void restart();
int rx_process(const double *buf, int len);
int tx_process();
void find_signals();
void clearch(int ch);
void clear();
};
extern view_rtty *rttyviewer;
#endif

Wyświetl plik

@ -148,10 +148,12 @@ psk::~psk()
if (fir2) delete fir2;
if (snfilt) delete snfilt;
if (imdfilt) delete imdfilt;
if (::pskviewer == pskviewer)
::pskviewer = 0;
// if (::pskviewer == pskviewer)
// ::pskviewer = 0;
delete pskviewer;
pskviewer = 0;
delete evalpsk;
evalpsk = 0;
// Interleaver
if (Rxinlv) delete Rxinlv;
@ -383,8 +385,8 @@ psk::psk(trx_mode pskmode) : modem()
E1 = E2 = E3 = 0.0;
acquire = 0;
evalpsk = new pskeval;
::pskviewer = pskviewer = new viewpsk(evalpsk, mode);
if (!evalpsk) evalpsk = new pskeval;
if (!pskviewer) pskviewer = new viewpsk(evalpsk, mode);
// init();
}