From d84b060522c8862c9c56197ff7d43d38f7975b4f Mon Sep 17 00:00:00 2001 From: David Freese Date: Thu, 21 Jan 2010 12:27:00 -0600 Subject: [PATCH] RTTY decoder mods Changed prefilter from lowpass to bandpass. Filter adjustment visible on both Mark & Space tones Increased AFC low pass filter time constant --- src/cw_rtty/rtty.cxx | 109 ++++++++++++++++++++---------------- src/dialogs/confdialog.cxx | 15 +++-- src/dialogs/confdialog.fl | 2 +- src/include/rtty.h | 4 ++ src/waterfall/waterfall.cxx | 41 +++++++++++++- 5 files changed, 114 insertions(+), 57 deletions(-) diff --git a/src/cw_rtty/rtty.cxx b/src/cw_rtty/rtty.cxx index 474ea92d..02db29a6 100644 --- a/src/cw_rtty/rtty.cxx +++ b/src/cw_rtty/rtty.cxx @@ -4,7 +4,7 @@ // Copyright (C) 2006 // Dave Freese, W1HKJ // -// This file is part of fldigi. Adapted from code contained in gmfsk source code +// 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) @@ -120,9 +120,9 @@ void rtty::init() modem::init(); rx_init(); put_MODEstatus(mode); - snprintf(msg1, sizeof(msg1), "Shft %-4.0f", rtty_shift); + snprintf(msg1, sizeof(msg1), "Shft %-4.0f", rtty_shift); put_Status1(msg1); - snprintf(msg2, sizeof(msg2), "Baud %-4.1f", rtty_baud); + snprintf(msg2, sizeof(msg2), "Baud %-4.1f", rtty_baud); put_Status2(msg2); if (progdefaults.PreferXhairScope) set_scope_mode(Digiscope::XHAIRS); @@ -142,7 +142,7 @@ rtty::~rtty() void rtty::restart() { double stl; - + rtty_shift = shift = _SHIFT[progdefaults.rtty_shift]; rtty_baud = _BAUD[progdefaults.rtty_baud]; nbits = rtty_bits = _BITS[progdefaults.rtty_bits]; @@ -163,22 +163,27 @@ void rtty::restart() rxmode = LETTERS; symbollen = (int) (samplerate / rtty_baud + 0.5); set_bandwidth(shift); - - rtty_BW = shift + 3*rtty_baud; + + rtty_BW = 0.75 * rtty_baud; progdefaults.RTTY_BW = rtty_BW; sldrRTTYbandwidth->value(rtty_BW); - + wf->redraw_marker(); - if (bpfilt) - bpfilt->create_filter(0, rtty_BW / samplerate / 2.0); - else - bpfilt = new fftfilt(0, rtty_BW / samplerate / 2.0, 1024); + bp_filt_lo = (shift/2.0 - rtty_BW) / samplerate; + if (bp_filt_lo < 0) bp_filt_lo = 0; + bp_filt_hi = (shift/2.0 + rtty_BW) / samplerate; - if (bitfilt) - bitfilt->setLength(symbollen / 8);//4); + if (bpfilt) + bpfilt->create_filter(bp_filt_lo, bp_filt_hi); else - bitfilt = new Cmovavg(symbollen / 8);//4); + bpfilt = new fftfilt(bp_filt_lo, bp_filt_hi, 1024); + + bflen = symbollen/2; + if (bitfilt) + bitfilt->setLength(bflen); + else + bitfilt = new Cmovavg(bflen); // stop length = 1, 1.5 or 2 bits rtty_stop = progdefaults.rtty_stop; @@ -191,12 +196,12 @@ void rtty::restart() pipeptr = 0; poscnt = negcnt = 0; posfreq = negfreq = 0.0; - + metric = 0.0; - snprintf(msg1, sizeof(msg1), "Shft %-4.0f", rtty_shift); + snprintf(msg1, sizeof(msg1), "Shft %-4.0f", rtty_shift); put_Status1(msg1); - snprintf(msg2, sizeof(msg2), "Baud %-4.1f", rtty_baud); + snprintf(msg2, sizeof(msg2), "Baud %-4.1f", rtty_baud); put_Status2(msg2); put_MODEstatus(mode); for (int i = 0; i < 1024; i++) QI[i].re = QI[i].im = 0.0; @@ -205,7 +210,7 @@ void rtty::restart() freqerrlo = freqerrhi = 0.0; sigsearch = 0; dspcnt = 2*(nbits + 2);//nbits + 2; //2 * symbollen * (nbits + 2); - + clear_zdata = true; } @@ -216,18 +221,18 @@ rtty::rtty(trx_mode tty_mode) mode = tty_mode; samplerate = RTTY_SampleRate; - fragmentsize = 256; - +// fragmentsize = 256; // not needed in any modem implementation for fldigi + bpfilt = (fftfilt *)0; - + bitfilt = (Cmovavg *)0; - + hilbert = new C_FIR_filter(); hilbert->init_hilbert(37, 1); pipe = new double[MAXPIPE]; dsppipe = new double [MAXPIPE]; - + restart(); } @@ -255,9 +260,9 @@ complex rtty::mixer(complex in) z = z * in; phaseacc -= TWOPI * frequency / samplerate; - if (phaseacc > M_PI) + if (phaseacc > M_PI) phaseacc -= TWOPI; - else if (phaseacc < M_PI) + else if (phaseacc < M_PI) phaseacc += TWOPI; return z; @@ -381,7 +386,7 @@ int rtty::rx(bool bit) case RTTY_RX_STATE_STOP: if (--counter == 0) { if (bit) { - if ((metric >= progStatus.sldrSquelchValue && progStatus.sqlonoff)|| !progStatus.sqlonoff) { + if ((metric >= progStatus.sldrSquelchValue && progStatus.sqlonoff)|| !progStatus.sqlonoff) { c = decode_char(); if ( c != 0 ) put_rx_char(c); @@ -467,16 +472,19 @@ int rtty::rx_process(const double *buf, int len) if (progdefaults.RTTY_BW != rtty_BW) { rtty_BW = progdefaults.RTTY_BW; - bpfilt->create_filter(0, rtty_BW / samplerate / 2.0); + bp_filt_lo = (shift/2.0 - rtty_BW) / samplerate; + if (bp_filt_lo < 0) bp_filt_lo = 0; + bp_filt_hi = (shift/2.0 + rtty_BW) / samplerate; + bpfilt->create_filter(bp_filt_lo, bp_filt_hi); wf->redraw_marker(); } Metric(); while (len-- > 0) { - + // create analytic signal from sound card input samples - + z.re = z.im = *buf++; hilbert->run(z, z); @@ -489,18 +497,18 @@ int rtty::rx_process(const double *buf, int len) n = bpfilt->run(z, &zp); if (n) { for (int i = 0; i < n; i++) { - + // measure phase difference between successive samples to determine // the frequency of the baseband signal (+rtty_baud or -rtty_baud) // see class complex definiton for operator % - + fin = (prevsmpl % zp[i]).arg() * samplerate / TWOPI; prevsmpl = zp[i]; // filter the result with a moving average filter - + f = bitfilt->run(fin); - + // track the + and - frequency excursions separately to derive an afc signal rotate = -4.0 * M_PI * freqerr / rtty_shift; @@ -512,7 +520,7 @@ int rtty::rx_process(const double *buf, int len) posfreq += fin; QI[i].re = zp[i].re; QI[i].im = xmix * zp[i].im; - } + } if (fin < 0.0) { negcnt++; negfreq += fin; @@ -520,25 +528,28 @@ int rtty::rx_process(const double *buf, int len) QI[i].im = zp[i].re; } QI[i] = QI[i] * complex(cos(rotate), sin(rotate)); - avgsig = decayavg(avgsig, 1.25 * zp[i].mag(), 64); - - if (avgsig > 0) + avgsig = decayavg(avgsig, 1.25 * zp[i].mag(), 128);//64); + + if (avgsig > 0) QI[i] = QI[i] / avgsig; // hysterisis dead zone in frequency discriminator bit detector - if (dspcnt && (--dspcnt % (nbits + 2) == 0)) { - pipe[pipeptr] = f / shift; // display filtered signal - pipeptr = (pipeptr + 1) % symbollen; - } - if (f > deadzone ) bit = true; if (f < -deadzone) bit = false; + + if (dspcnt && (--dspcnt % (nbits + 2) == 0)) { +// pipe[pipeptr] = bit ? .5 : -.5; + pipe[pipeptr] = f / shift; +// pipe[pipeptr] = fin / shift;//f / shift; // display filtered signal + pipeptr = (pipeptr + 1) % symbollen; + } + // if (!progdefaults.RTTY_USB) // bit = !bit; - + rxflag = rx (reverse ? !bit : bit); if (rxflag == 2) { @@ -547,7 +558,7 @@ int rtty::rx_process(const double *buf, int len) if (poscnt && negcnt) { poserr = posfreq/poscnt; negerr = negfreq/negcnt; - + // compute the frequency error as the median of + and - relative freq's if (sigsearch) sigsearch--; @@ -618,7 +629,7 @@ double rtty::FSKnco() void rtty::send_symbol(int symbol) { double freq; - + if (reverse) symbol = !symbol; @@ -626,7 +637,7 @@ void rtty::send_symbol(int symbol) freq = get_txfreq_woffset() + shift / 2.0; else freq = get_txfreq_woffset() - shift / 2.0; - + for (int i = 0; i < symbollen; i++) { outbuf[i] = nco(freq); if (symbol) @@ -645,7 +656,7 @@ void rtty::send_stop() { double freq; bool invert = reverse; - + if (invert) freq = get_txfreq_woffset() - shift / 2.0; else @@ -693,7 +704,7 @@ void rtty::send_char(int c) c = letters[c]; else c = figures[c]; - if (c) + if (c) put_echo_char(c); } } @@ -800,7 +811,7 @@ int rtty::tx_process() return 0; // switch case if necessary - + if ((c & 0x300) != txmode) {// == 0) { if (txmode == FIGURES) { send_char(LETTERS); diff --git a/src/dialogs/confdialog.cxx b/src/dialogs/confdialog.cxx index 3b30585b..e06708db 100644 --- a/src/dialogs/confdialog.cxx +++ b/src/dialogs/confdialog.cxx @@ -3249,6 +3249,7 @@ an merging")); cntCWdash2dot->type(1); cntCWdash2dot->minimum(2.5); cntCWdash2dot->maximum(4); + cntCWdash2dot->step(0.1); cntCWdash2dot->value(3); cntCWdash2dot->callback((Fl_Callback*)cb_cntCWdash2dot); cntCWdash2dot->align(FL_ALIGN_RIGHT); @@ -3259,6 +3260,7 @@ an merging")); cntCWrisetime->type(1); cntCWrisetime->minimum(0); cntCWrisetime->maximum(15); + cntCWrisetime->step(0.1); cntCWrisetime->value(4); cntCWrisetime->callback((Fl_Callback*)cb_cntCWrisetime); cntCWrisetime->align(FL_ALIGN_RIGHT); @@ -3362,6 +3364,7 @@ an merging")); valDominoEX_BW->type(1); valDominoEX_BW->minimum(1); valDominoEX_BW->maximum(2); + valDominoEX_BW->step(0.1); valDominoEX_BW->value(1.5); valDominoEX_BW->callback((Fl_Callback*)cb_valDominoEX_BW); valDominoEX_BW->align(FL_ALIGN_RIGHT); @@ -3376,6 +3379,7 @@ an merging")); { Fl_Value_Slider* o = valDomCWI = new Fl_Value_Slider(15, 207, 260, 20, _("CWI threshold")); valDomCWI->tooltip(_("CWI detection and suppression")); valDomCWI->type(1); + valDomCWI->step(0.01); valDomCWI->textsize(14); valDomCWI->callback((Fl_Callback*)cb_valDomCWI); valDomCWI->align(FL_ALIGN_TOP); @@ -3862,10 +3866,10 @@ an merging")); { Fl_Value_Slider* o = sldrRTTYbandwidth = new Fl_Value_Slider(100, 295, 300, 20, _("Receive filter bandwidth")); sldrRTTYbandwidth->tooltip(_("Adjust the DSP bandwidth")); sldrRTTYbandwidth->type(1); - sldrRTTYbandwidth->minimum(50); - sldrRTTYbandwidth->maximum(1200); - sldrRTTYbandwidth->step(5); - sldrRTTYbandwidth->value(400); + sldrRTTYbandwidth->minimum(5); + sldrRTTYbandwidth->maximum(200); + sldrRTTYbandwidth->step(1); + sldrRTTYbandwidth->value(25); sldrRTTYbandwidth->textsize(14); sldrRTTYbandwidth->callback((Fl_Callback*)cb_sldrRTTYbandwidth); sldrRTTYbandwidth->align(FL_ALIGN_TOP_LEFT); @@ -3906,6 +3910,7 @@ an merging")); valTHOR_BW->type(1); valTHOR_BW->minimum(1); valTHOR_BW->maximum(2); + valTHOR_BW->step(0.1); valTHOR_BW->value(1.5); valTHOR_BW->callback((Fl_Callback*)cb_valTHOR_BW); valTHOR_BW->align(FL_ALIGN_RIGHT); @@ -3914,6 +3919,7 @@ an merging")); { Fl_Value_Slider* o = valThorCWI = new Fl_Value_Slider(15, 194, 260, 20, _("CWI threshold")); valThorCWI->tooltip(_("CWI detection and suppression")); valThorCWI->type(1); + valThorCWI->step(0.01); valThorCWI->textsize(14); valThorCWI->callback((Fl_Callback*)cb_valThorCWI); valThorCWI->align(FL_ALIGN_TOP); @@ -4503,6 +4509,7 @@ ll with your audio device.")); valPCMvolume->tooltip(_("Set the sound card PCM level")); valPCMvolume->type(1); valPCMvolume->selection_color((Fl_Color)FL_SELECTION_COLOR); + valPCMvolume->step(0.01); valPCMvolume->value(0.8); valPCMvolume->textsize(14); valPCMvolume->callback((Fl_Callback*)cb_valPCMvolume); diff --git a/src/dialogs/confdialog.fl b/src/dialogs/confdialog.fl index ea18c713..ae033d94 100644 --- a/src/dialogs/confdialog.fl +++ b/src/dialogs/confdialog.fl @@ -1605,7 +1605,7 @@ progdefaults.changed = true;} Fl_Value_Slider sldrRTTYbandwidth { label {Receive filter bandwidth} callback {progdefaults.RTTY_BW = o->value();} - tooltip {Adjust the DSP bandwidth} xywh {100 295 300 20} type Horizontal align 5 minimum 50 maximum 1200 step 5 value 400 textsize 14 + tooltip {Adjust the DSP bandwidth} xywh {100 295 300 20} type Horizontal align 5 minimum 5 maximum 200 step 1 value 25 textsize 14 code0 {o->value(progdefaults.RTTY_BW);} } } diff --git a/src/include/rtty.h b/src/include/rtty.h index a200faf5..0527639c 100644 --- a/src/include/rtty.h +++ b/src/include/rtty.h @@ -95,6 +95,10 @@ private: Cmovavg *bitfilt; fftfilt *bpfilt; + int bflen; + double bp_filt_lo; + double bp_filt_hi; + double *pipe; double *dsppipe; int pipeptr; diff --git a/src/waterfall/waterfall.cxx b/src/waterfall/waterfall.cxx index d70b0c73..21368a4b 100644 --- a/src/waterfall/waterfall.cxx +++ b/src/waterfall/waterfall.cxx @@ -67,6 +67,7 @@ #include "confdialog.h" #include "flmisc.h" #include "gettext.h" +#include "rtty.h" using namespace std; @@ -199,13 +200,46 @@ void WFdisp::initMarkers() { // draw a marker of specified width and colour centred at freq and clrM inline void WFdisp::makeMarker_(int width, const RGB* color, int freq, const RGB* clrMin, RGB* clrM, const RGB* clrMax) { - // clamp marker to image width + if (active_modem->get_mode() == MODE_RTTY) { + // rtty has two bandwidth indicators on the waterfall + // upper and lower frequency + int bw_limit_hi = (int)((_SHIFT[progdefaults.rtty_shift]/2 + progdefaults.RTTY_BW)); + int bw_limit_lo = (int)((_SHIFT[progdefaults.rtty_shift]/2 - progdefaults.RTTY_BW)); + int bw_freq = static_cast(freq + 0.5); + int bw_lower1 = -bw_limit_hi; + int bw_upper1 = -bw_limit_lo; + int bw_lower2 = bw_limit_lo; + int bw_upper2 = bw_limit_hi; + if (bw_lower1 + bw_freq < 0) + bw_lower1 -= bw_lower1 + bw_freq; + if (bw_upper1 + bw_freq < 0) + bw_lower2 -= bw_lower2 + bw_freq; + if (bw_upper2 + bw_freq > scale_width) + bw_upper2 -= bw_upper2 + bw_freq - scale_width; + if (bw_lower2 + bw_freq > scale_width) + bw_lower2 -= bw_lower2 + bw_freq - scale_width; + // draw it + RGB* clrPos; + for (int y = 0; y < WFMARKER - 2; y++) { + for (int x = bw_lower1; x < bw_upper1; x++) { + clrPos = clrM + x + y * scale_width; + if (clrPos > clrMin && clrPos < clrMax) + *clrPos = *color; + } + for (int x = bw_lower2; x < bw_upper2; x++) { + clrPos = clrM + x + y * scale_width; + if (clrPos > clrMin && clrPos < clrMax) + *clrPos = *color; + } + } + return; + } + int bw_lower = -width, bw_upper = width; if (bw_lower + static_cast(freq+0.5) < 0) bw_lower -= bw_lower + static_cast(freq+0.5); if (bw_upper + static_cast(freq+0.5) > scale_width) bw_upper -= bw_upper + static_cast(freq+0.5) - scale_width; - // draw it RGB* clrPos; for (int y = 0; y < WFMARKER - 2; y++) { @@ -236,7 +270,7 @@ void WFdisp::makeMarker() else if (mode >= MODE_FELDHELL && mode <= MODE_HELL80) marker_width = (int)progdefaults.HELL_BW; else if (mode == MODE_RTTY) - marker_width = (int)progdefaults.RTTY_BW; + marker_width = (int)_SHIFT[progdefaults.rtty_shift]; marker_width = (int)(marker_width / 2.0 + 1); RGBmarker.R = progdefaults.bwTrackRGBI.R; @@ -244,6 +278,7 @@ void WFdisp::makeMarker() RGBmarker.B = progdefaults.bwTrackRGBI.B; makeMarker_(marker_width, &RGBmarker, carrierfreq, clrMin, clrM, clrMax); + if (unlikely(active_modem->freqlocked())) { int txfreq = active_modem->get_txfreq(); adjust_color_inv(RGBmarker.R, RGBmarker.G, RGBmarker.B, FL_BLACK, FL_RED);