diff --git a/ChangeLog b/ChangeLog index 75ff453c..4c406b9c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -36,6 +36,13 @@ Change Log: 2) fading fixed length vector display 3) fading variable length vector display; length proportional to signal quality. + 18) Completely new DominoEX decoder - similar in theory to ZL2AFP + DominoEX, which has FFT bin resolution 4x the tone spacing. No AFC is + required for proper tracking of the Rx signal. + 19) Removed ZOH and Linear interpolator choices from the resampling choice + menu under Sound Card configuration. + 20) Fixed a bug in the global.cxx structures. Missing field in "CW" + definition. 2.10.3) 1) Corrected memory leak bug. diff --git a/configure.ac b/configure.ac index a2f15dcf..177ee857 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ dnl major and minor must be integers; patch may dnl contain other characters or be empty m4_define(FLDIGI_MAJOR, [2]) m4_define(FLDIGI_MINOR, [11]) -m4_define(FLDIGI_PATCH, [S]) +m4_define(FLDIGI_PATCH, [U]) AC_INIT([fldigi], FLDIGI_MAJOR.FLDIGI_MINOR[FLDIGI_PATCH], [w1hkj AT w1hkj DOT com]) diff --git a/src/dialogs/confdialog.cxx b/src/dialogs/confdialog.cxx index 6a0be772..01700c70 100644 --- a/src/dialogs/confdialog.cxx +++ b/src/dialogs/confdialog.cxx @@ -881,8 +881,9 @@ Fl_Group *tabDomEX=(Fl_Group *)0; Fl_Input *txtSecondary=(Fl_Input *)0; -static void cb_txtSecondary(Fl_Input*, void*) { - progdefaults.changed = true; +static void cb_txtSecondary(Fl_Input* o, void*) { + progdefaults.secText = o->value(); +progdefaults.changed = true; } Fl_Button *btnRestartDomEX=(Fl_Button *)0; @@ -895,7 +896,15 @@ resetDOMEX(); Fl_Counter *valDominoEX_BW=(Fl_Counter *)0; static void cb_valDominoEX_BW(Fl_Counter* o, void*) { - progdefaults.DOMINOEX_BW=o->value(); + progdefaults.DOMINOEX_BW = o->value(); +resetDOMEX(); +progdefaults.changed = true; +} + +Fl_Check_Button *valDominoEX_FILTER=(Fl_Check_Button *)0; + +static void cb_valDominoEX_FILTER(Fl_Check_Button* o, void*) { + progdefaults.DOMINOEX_FILTER = o->value(); progdefaults.changed = true; } @@ -1669,6 +1678,7 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600 { tabAudio = new Fl_Group(0, 50, 400, 170, "Audio devices"); tabAudio->color((Fl_Color)51); tabAudio->selection_color((Fl_Color)51); + tabAudio->hide(); { AudioOSS = new Fl_Group(5, 58, 391, 35); AudioOSS->box(FL_ENGRAVED_FRAME); { btnAudioIO[0] = new Fl_Round_Button(5, 63, 100, 25, "OSS"); @@ -1727,7 +1737,6 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600 { tabAudioOpt = new Fl_Group(0, 50, 400, 170, "Audio settings"); tabAudioOpt->color((Fl_Color)51); tabAudioOpt->selection_color((Fl_Color)51); - tabAudioOpt->hide(); { Fl_Spinner* o = cntRxRateCorr = new Fl_Spinner(5, 160, 85, 25, "RX ppm"); cntRxRateCorr->value(1); cntRxRateCorr->callback((Fl_Callback*)cb_cntRxRateCorr); @@ -2019,24 +2028,30 @@ l with your sound hardware."); { tabDomEX = new Fl_Group(0, 50, 400, 170, "DomEX"); tabDomEX->color((Fl_Color)51); tabDomEX->selection_color((Fl_Color)51); - tabDomEX->hide(); { txtSecondary = new Fl_Input(20, 75, 360, 44, "Secondary Text"); txtSecondary->type(4); txtSecondary->callback((Fl_Callback*)cb_txtSecondary); txtSecondary->align(FL_ALIGN_TOP_LEFT); + txtSecondary->when(FL_WHEN_CHANGED); } // Fl_Input* txtSecondary { btnRestartDomEX = new Fl_Button(300, 172, 79, 28, "Restart"); btnRestartDomEX->callback((Fl_Callback*)cb_btnRestartDomEX); + btnRestartDomEX->hide(); } // Fl_Button* btnRestartDomEX { Fl_Counter* o = valDominoEX_BW = new Fl_Counter(25, 134, 63, 21, "BW factor:"); valDominoEX_BW->type(1); valDominoEX_BW->minimum(1); - valDominoEX_BW->maximum(3); + valDominoEX_BW->maximum(2); valDominoEX_BW->step(0.1); valDominoEX_BW->value(2); valDominoEX_BW->callback((Fl_Callback*)cb_valDominoEX_BW); o->value(progdefaults.DOMINOEX_BW); } // Fl_Counter* valDominoEX_BW + { Fl_Check_Button* o = valDominoEX_FILTER = new Fl_Check_Button(107, 136, 83, 19, "Filter ON"); + valDominoEX_FILTER->down_box(FL_DOWN_BOX); + valDominoEX_FILTER->callback((Fl_Callback*)cb_valDominoEX_FILTER); + o->value(progdefaults.DOMINOEX_FILTER); + } // Fl_Check_Button* valDominoEX_FILTER tabDomEX->end(); } // Fl_Group* tabDomEX { tabFeld = new Fl_Group(0, 50, 400, 170, "Feld"); @@ -2250,6 +2265,7 @@ l with your sound hardware."); { tabRTTY = new Fl_Group(0, 50, 400, 170, "RTTY"); tabRTTY->color((Fl_Color)51); tabRTTY->selection_color((Fl_Color)51); + tabRTTY->hide(); { Fl_Choice* o = selShift = new Fl_Choice(48, 60, 77, 22, "Shift"); selShift->down_box(FL_BORDER_BOX); selShift->callback((Fl_Callback*)cb_selShift); @@ -2374,10 +2390,10 @@ l with your sound hardware."); } // Fl_Group* tabModems tabsConfigure->end(); } // Fl_Tabs* tabsConfigure - { btnCloseConfig = new Fl_Return_Button(285, 225, 100, 25, "Close"); + { btnCloseConfig = new Fl_Return_Button(285, 222, 100, 25, "Close"); btnCloseConfig->callback((Fl_Callback*)cb_btnCloseConfig); } // Fl_Return_Button* btnCloseConfig - { btnSaveConfig = new Fl_Button(15, 225, 100, 25, "Save Config"); + { btnSaveConfig = new Fl_Button(15, 222, 100, 25, "Save Config"); btnSaveConfig->callback((Fl_Callback*)cb_btnSaveConfig); } // Fl_Button* btnSaveConfig o->end(); diff --git a/src/dialogs/confdialog.fl b/src/dialogs/confdialog.fl index 30a10891..5f32a5ce 100644 --- a/src/dialogs/confdialog.fl +++ b/src/dialogs/confdialog.fl @@ -661,7 +661,7 @@ o->label((inpQRZuserpassword->type() & FL_SECRET_INPUT) ? "Show" : "Hide");} } { Fl_Group tabAudio { label {Audio devices} open - xywh {0 50 400 170} color 51 selection_color 51 + xywh {0 50 400 170} color 51 selection_color 51 hide } { Fl_Group AudioOSS {open xywh {5 58 391 35} box ENGRAVED_FRAME @@ -743,7 +743,7 @@ resetSoundCard();} } Fl_Group tabAudioOpt { label {Audio settings} open - xywh {0 50 400 170} color 51 selection_color 51 hide + xywh {0 50 400 170} color 51 selection_color 51 } { Fl_Spinner cntRxRateCorr { label {RX ppm} @@ -1037,26 +1037,35 @@ progdefaults.changed = true;} } Fl_Group tabDomEX { label DomEX open - xywh {0 50 400 170} color 51 selection_color 51 hide + xywh {0 50 400 170} color 51 selection_color 51 } { Fl_Input txtSecondary { label {Secondary Text} - callback {progdefaults.changed = true;} - xywh {20 75 360 44} type Multiline align 5 + callback {progdefaults.secText = o->value(); +progdefaults.changed = true;} + xywh {20 75 360 44} type Multiline align 5 when 1 } Fl_Button btnRestartDomEX { label Restart callback {progdefaults.storeDefaults(); resetDOMEX();} - xywh {300 172 79 28} + xywh {300 172 79 28} hide } Fl_Counter valDominoEX_BW { label {BW factor:} - callback {progdefaults.DOMINOEX_BW=o->value(); + callback {progdefaults.DOMINOEX_BW = o->value(); +resetDOMEX(); progdefaults.changed = true;} - xywh {25 134 63 21} type Simple minimum 1 maximum 3 step 0.1 value 2 + xywh {25 134 63 21} type Simple minimum 1 maximum 2 step 0.1 value 2 code0 {o->value(progdefaults.DOMINOEX_BW);} } + Fl_Check_Button valDominoEX_FILTER { + label {Filter ON} + callback {progdefaults.DOMINOEX_FILTER = o->value(); +progdefaults.changed = true;} selected + xywh {107 136 83 19} down_box DOWN_BOX + code0 {o->value(progdefaults.DOMINOEX_FILTER);} + } } Fl_Group tabFeld { label Feld open @@ -1289,7 +1298,7 @@ progdefaults.changed = true;} } Fl_Group tabRTTY { label RTTY open - xywh {0 50 400 170} color 51 selection_color 51 + xywh {0 50 400 170} color 51 selection_color 51 hide } { Fl_Choice selShift { label Shift @@ -1327,7 +1336,7 @@ resetRTTY();} open label Stop callback {progdefaults.changed = true; progdefaults.storeDefaults(); -resetRTTY();} open selected +resetRTTY();} open xywh {48 158 77 22} down_box BORDER_BOX when 1 code0 {o->add(szStopBits);} } {} @@ -1423,12 +1432,12 @@ progdefaults.changed = true;} Fl_Return_Button btnCloseConfig { label Close callback {closeDialog();} - xywh {285 225 100 25} + xywh {285 222 100 25} } Fl_Button btnSaveConfig { label {Save Config} callback {progdefaults.saveDefaults();} - xywh {15 225 100 25} + xywh {15 222 100 25} } } } diff --git a/src/dialogs/fl_digi.cxx b/src/dialogs/fl_digi.cxx index a1bfb82d..ec67e00d 100644 --- a/src/dialogs/fl_digi.cxx +++ b/src/dialogs/fl_digi.cxx @@ -1727,7 +1727,7 @@ void put_sec_char( char chr ) { if (chr >= ' ' && chr <= 'z') { strSecText.append(1, chr); - if (strSecText.length() > 60) + if (strSecText.length() > 50) strSecText.erase(0,1); FL_LOCK_D(); REQ(static_cast(&Fl_Box::label), StatusBar, strSecText.c_str()); @@ -1930,7 +1930,7 @@ void resetDOMEX() { md == MODE_DOMINOEX16 || md == MODE_DOMINOEX22 ) { - trx_reset(); +// trx_reset(); active_modem->restart(); } } diff --git a/src/dominoex/dominoex.cxx b/src/dominoex/dominoex.cxx index b6bcdff4..51a5f7cf 100644 --- a/src/dominoex/dominoex.cxx +++ b/src/dominoex/dominoex.cxx @@ -1,12 +1,11 @@ // -// dominoex.cxx -- DominoEX modem +// dominoex.cxx -- DominoEX modem // -// Copyright (C) 2001, 2002, 2003 -// Tomi Manninen (oh2bns@sral.fi) +// Copyright (C) 2008 +// David Freese (w1hkj@w1hkj.com) +// based on code in gmfsk // Copyright (C) 2006 // Hamish Moffatt (hamish@debian.org) -// Copyright (C) 2006 -// David Freese (w1hkj@w1hkj.com) // 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 2 of the License, or @@ -40,10 +39,6 @@ using namespace std; -#define RESOLUTION 1 - -#define AFC_COUNT 64 - char dommsg[80]; void dominoex::tx_init(SoundBase *sc) @@ -52,7 +47,7 @@ void dominoex::tx_init(SoundBase *sc) txstate = TX_STATE_PREAMBLE; txprevtone = 0; counter = 0; - phaseacc = 0.0; + txphase = 0; videoText(); } @@ -63,8 +58,7 @@ void dominoex::rx_init() met1 = 0.0; met2 = 0.0; counter = 0; - phaseacc = 0.0; - freqerr = 0.0; + phase[0] = phase[1] = phase[2] = phase[3] = 0.0; put_MODEstatus(mode); put_sec_char(0); } @@ -72,25 +66,17 @@ void dominoex::rx_init() void dominoex::restart() { double flo, fhi, bw, cf; - -// basetone is always 1000 Hz -// mid frequency is always 1000 Hz + bandwidth / 2 - bw = bandwidth * progdefaults.DOMINOEX_BW; - cf = 1000.0 + bandwidth / 2.0; - flo = (cf - bw/2) / samplerate; - fhi = (cf + bw/2) / samplerate; - - if (filt) - filt->init_bandpass (127, 1, flo, fhi); - - strSecXmtText = txtSecondary->value(); - if (strSecXmtText.length() == 0) - strSecXmtText = "fldigi "PACKAGE_VERSION" "; + cf = BASEFREQ + bandwidth / 2.0; + flo = (cf - 0.8 * bw) / samplerate; + fhi = (cf + 0.8 * bw) / samplerate; + for (int i = 0; i < NUMFFTS; i++) + if (filt[i]) + filt[i]->init_bandpass (127, 1, flo, fhi); + prev1symbol = prev2symbol = 0; - prev1vector = prev2vector = complex(0.0, 0.0); } @@ -99,116 +85,103 @@ void dominoex::init() modem::init(); restart(); rx_init(); + + strSecXmtText = txtSecondary->value(); + if (strSecXmtText.length() == 0) + strSecXmtText = "fldigi "PACKAGE_VERSION" "; + set_scope_mode(Digiscope::DOMDATA); } dominoex::~dominoex() { - if (binsfft) delete binsfft; if (hilbert) delete hilbert; + + for (int i = 0; i < NUMFFTS; i++) { + if (binsfft[i]) delete binsfft[i]; + if (filt[i]) delete filt[i]; + } + if (pipe) delete [] pipe; - if (filt) delete filt; } dominoex::dominoex(trx_mode md) { - double cf, bw, flo, fhi; - - numtones = DOMNUMTONES; + int basetone, lotone, hitone; + mode = md; switch (mode) { -// 8kHz modes - case MODE_DOMINOEX4: - symlen = 2048; - basetone = 256; // = 1000 * symlen / samplerate - doublespaced = 1; - samplerate = 8000; - break; - - case MODE_DOMINOEX8: - symlen = 1024; - basetone = 128; // = 1000 * symlen / samplerate - doublespaced = 1; - samplerate = 8000; - break; - - case MODE_DOMINOEX16: - symlen = 512; - basetone = 64; // = 1000 * symlen / samplerate - doublespaced = 0; - samplerate = 8000; - break; - // 11.025 kHz modes case MODE_DOMINOEX5: symlen = 2048; - basetone = 186; // 1001.3 Hz doublespaced = 1; samplerate = 11025; break; case MODE_DOMINOEX11: symlen = 1024; - basetone = 93; // 1001.3 Hz doublespaced = 0; samplerate = 11025; break; case MODE_DOMINOEX22: symlen = 512; - basetone = 46; // 990 Hz doublespaced = 0; samplerate = 11025; break; +// 8kHz modes + case MODE_DOMINOEX4: + symlen = 2048; + doublespaced = 1; + samplerate = 8000; + break; + + case MODE_DOMINOEX16: + symlen = 512; + doublespaced = 0; + samplerate = 8000; + break; + + case MODE_DOMINOEX8: default: - // case MODE_DOMINOEX8: symlen = 1024; - basetone = 128; // 1000 Hz doublespaced = 1; samplerate = 8000; } + basetone = (int)floor(BASEFREQ * symlen / samplerate + 0.5); + lotone = basetone - NUMTONES * (doublespaced ? 2 : 1); + hitone = basetone + 2 * NUMTONES * (doublespaced ? 2 : 1); + tonespacing = (double) (samplerate * ((doublespaced) ? 2 : 1)) / symlen; -// binsfft = new sfft( symlen, -// basetone - numtones*(doublespaced?2:1), -// basetone + 2*numtones*(doublespaced ? 2 : 1) ); -//exp - binsfft = new sfft( RESOLUTION * symlen, - RESOLUTION * (basetone - numtones*(doublespaced ? 2 : 1)), - RESOLUTION * (basetone + 2*numtones*(doublespaced ? 2 : 1)) ); + bandwidth = NUMTONES * tonespacing; hilbert = new C_FIR_filter(); hilbert->init_hilbert(37, 1); - afcfilt = new Cmovavg(AFC_COUNT); + + for (int i = 0; i < NUMFFTS; i++) { + binsfft[i] = new sfft (symlen, lotone, hitone); + filt[i] = new C_FIR_filter(); + } twosym = 2 * symlen; pipe = new domrxpipe[twosym]; scopedata.alloc(twosym); - videodata.alloc(RESOLUTION * numtones * 6); + videodata.alloc((4 * NUMTONES * 3 * (doublespaced?2:1) )); + pipeptr = 0; symcounter = 0; metric = 0.0; - bandwidth = numtones * tonespacing; - bw = bandwidth * progdefaults.DOMINOEX_BW; - - cf = 1000.0 + bandwidth / 2.0; - flo = (cf - bw/2) / samplerate; - fhi = (cf + bw/2) / samplerate; - - filt = new C_FIR_filter(); - filt->init_bandpass (127, 1, flo, fhi); - fragmentsize = symlen; s2n = 0.0; prev1symbol = prev2symbol = 0; - prev1vector = prev2vector = complex(0.0, 0.0); init(); } @@ -216,19 +189,20 @@ dominoex::dominoex(trx_mode md) //===================================================================== // rx modules -complex dominoex::mixer(complex in, double f) +complex dominoex::mixer(int n, complex in) { complex z; -// Basetone is always 1000 Hz - f -= (1000.0 + bandwidth/2); - z.re = cos(phaseacc); - z.im = sin(phaseacc); + double f; +// 4 mixers are supported each separated by 1/4 bin size + f = frequency - BASEFREQ - bandwidth/2 + (samplerate / symlen) * (n / 4.0); + z.re = cos(phase[n]); + z.im = sin(phase[n]); z = z * in; - phaseacc -= twopi * f / samplerate; - if (phaseacc > M_PI) - phaseacc -= twopi; - else if (phaseacc < M_PI) - phaseacc += twopi; + phase[n] -= twopi * f / samplerate; + if (phase[n] > M_PI) + phase[n] -= twopi; + else if (phase[n] < M_PI) + phase[n] += twopi; return z; } @@ -242,20 +216,19 @@ void dominoex::recvchar(int c) put_rx_char(c & 0xFF); } -void dominoex::decodesymbol(unsigned char curtone, unsigned char prevtone) +void dominoex::decodesymbol() { int c, sym, ch; int diff; // Decode the IFK+ sequence, which results in a single nibble - diff = curtone - prevtone; + diff = currsymbol - prev1symbol; if (reverse) diff = -diff; + diff /= 4; // 4 sets of interleaved bins if (doublespaced) diff /= 2; - diff /= RESOLUTION; diff -= 2; - if (diff < 0) diff += numtones; - + if (diff < 0) diff += NUMTONES; c = diff; // If the new symbol is the start of a new character (MSB is low), complete the previous character @@ -283,13 +256,12 @@ void dominoex::decodesymbol(unsigned char curtone, unsigned char prevtone) } -int dominoex::harddecode(complex *in) +int dominoex::harddecode() { double x, max = 0.0; int symbol = 0; - int count = RESOLUTION * numtones * 3 *(doublespaced?2:1); - for (int i = 0; i < count; i++) { - x = in[i].mag(); + for (int i = 0; i < (4 * NUMTONES * 3 * (doublespaced?2:1) ); i++) { + x = pipe[pipeptr].vector[i].mag(); if (x > max) { max = x; symbol = i; @@ -298,43 +270,44 @@ int dominoex::harddecode(complex *in) return symbol; } -void dominoex::update_syncscope(complex *bins) +void dominoex::update_syncscope() { - double max = 0, min = 1e6, range, mag; - int numbins = RESOLUTION * (numtones * 3 * (doublespaced ? 2 : 1)); //exp -// dom waterfall - for (int i = 0; i < numbins; i++ ) { - mag = bins[i].mag(); - if (max < mag) max = mag; - if (min > mag) min = mag; - } - range = max - min; - memset(videodata, 0, numbins * sizeof(double)); - for (int i = 0; i < numbins; i++ ) { - if (range > 2) { - mag = (bins[i].mag() - min) / range; - mag = 1 + log10(mag); - if (mag < 0) mag = 0; - } else - mag = 0; - videodata[i] = 255*mag; - } + double max = 0, min = 1e6, range, mag; + +// dom waterfall + memset(videodata, 0, (4 * NUMTONES * 3 * (doublespaced?2:1) ) * sizeof(double)); + if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) { - set_video(videodata, numbins); - videodata.next(); // change buffers + for (int i = 0; i < (4 * NUMTONES * 3 * (doublespaced?2:1) ); i++ ) { + mag = pipe[pipeptr].vector[i].mag(); + if (max < mag) max = mag; + if (min > mag) min = mag; + } + range = max - min; + for (int i = 0; i < (4 * NUMTONES * 3 * (doublespaced?2:1) ); i++ ) { + if (range > 2) { + mag = (pipe[pipeptr].vector[i].mag() - min) / range; + mag = 1 + log10(mag); + if (mag < 0) mag = 0; + } else + mag = 0; + videodata[i] = 255*mag; + } } + set_video(videodata, (4 * NUMTONES * 3 * (doublespaced?2:1) )); + videodata.next(); // dom symbol synch data memset(scopedata, 0, twosym * sizeof(double)); if (!progStatus.sqlonoff || metric >= progStatus.sldrSquelchValue) { - for (int i = 0, j = 0; i < twosym; i++) { + for (unsigned int i = 0, j = 0; i < twosym; i++) { j = (pipeptr + i + 1) % (twosym); scopedata[i] = pipe[j].vector[prev1symbol].mag(); } - set_scope(scopedata, twosym); - scopedata.next(); // change buffers } + set_scope(scopedata, twosym); + scopedata.next(); } void dominoex::synchronize() @@ -347,7 +320,7 @@ void dominoex::synchronize() if (prev1symbol == prev2symbol) return; - for (int i = 0, j = pipeptr; i < twosym; i++) { + for (unsigned int i = 0, j = pipeptr; i < twosym; i++) { val = (pipe[j].vector[prev1symbol]).mag(); if (val > max) { max = val; @@ -355,112 +328,65 @@ void dominoex::synchronize() } j = (j + 1) % twosym; } - synccounter += (int) floor(1.0 * (syn - symlen) / numtones + 0.5); + synccounter += (int) floor(1.0 * (syn - symlen) / NUMTONES + 0.5); } -void dominoex::reset_afc() { - freqerr = 0.0; -// for (int i = 0; i < AFC_COUNT; i++) afcfilt->run(0.0); - afcfilt->reset(); - return; -} -void dominoex::afc() +void dominoex::eval_s2n() { - complex z; - complex vec1, vec2; - double f, fsym, err; - double ds = doublespaced ? 2 : 1; + if (currsymbol != prev1symbol && prev1symbol != prev2symbol) { + sig = pipe[pipeptr].vector[currsymbol].mag(); + noise = pipe[pipeptr].vector[prev2symbol].mag(); - if (sigsearch) { - reset_afc(); - sigsearch = 0; - } + if (noise < 1.0e-6) noise = 1e-6; + + s2n = decayavg( s2n, sig / noise, 8); - vec1 = pipe[pipeptr].vector[currsymbol]; - if (pipeptr == 0) - vec2 = pipe[2*symlen].vector[currsymbol]; - else - vec2 = pipe[pipeptr - 1].vector[currsymbol]; - -// z = prevvector % currvector; - z = vec2 % vec1; - - f = z.arg() * samplerate / twopi; - fsym = (currsymbol/RESOLUTION - numtones * ds) * samplerate / symlen; - fsym += 1000; - err = fsym - f; -// freqerr = afcfilt->run(err / numtones); - freqerr = decayavg(freqerr, err / numtones, 32); -//std::cout << currsymbol << ", " << fsym << ", " << f << ", " << err << ", " << freqerr << std::endl; - if (progStatus.afconoff && (metric > progStatus.sldrSquelchValue || progStatus.sqlonoff == false)) { - set_freq(frequency - freqerr); + metric = 20*log10(s2n); + + display_metric(metric); + + snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", metric); + put_Status1(dommsg); } } -void dominoex::eval_s2n(complex curr, complex n) -{ - sig = curr.mag(); // signal + noise energy - noise = n.mag();// + 1e-10; // noise energy - if (noise < 1e-20) noise = 1e-20; - - s2n = decayavg( s2n, sig / noise, 8); - - metric = 20*log10(s2n); - - display_metric(metric); - - snprintf(dommsg, sizeof(dommsg), "s/n %3.0f dB", metric); - put_Status1(dommsg); - -} - -int testcount = 2; - int dominoex::rx_process(const double *buf, int len) { - complex z, *bins, noise; + complex zref, z, *bins; while (len) { -// create analytic signal...shift in frequency to base band & bandpass filter - z.re = z.im = *buf++; - hilbert->run(z, z); - z = mixer(z, frequency); - filt->run(z, z); - -// feed it to the sliding FFT - bins = binsfft->run(z); +// create analytic signal + zref.re = zref.im = *buf++; + hilbert->run(zref, zref); -// copy current vector to the pipe - for (int i = 0; i < RESOLUTION * numtones * 3 * (doublespaced?2:1); i++) { - pipe[pipeptr].vector[i] = bins[i]; +// process 4 sets of sliding FFTs spaced at 1/4 bin intervals each of which +// is a matched filter for the current symbol length + for (int n = 0; n < 4; n++) { +// shift in frequency to base band & bandpass filter + z = mixer(n, zref); + if (progdefaults.DOMINOEX_FILTER) + filt[n]->run(z, z); +// feed it to the sliding FFTs + bins = binsfft[n]->run(z); +// copy current vector to the pipe interleaving the FFT vectors + for (int i = 0; i < NUMTONES * 3 * (doublespaced ? 2 : 1); i++) { + pipe[pipeptr].vector[n + 4 * i] = bins[i]; + } } if (--synccounter <= 0) { synccounter = symlen; - currsymbol = harddecode(bins); -//std::cout << currsymbol << " "; std::cout.flush(); - currvector = bins[currsymbol]; -// decode symbol - decodesymbol(currsymbol, prev1symbol); -// update the scope -// update_syncscope(bins); -// symbol sync + currsymbol = harddecode(); + decodesymbol(); synchronize(); -// update the scope - update_syncscope(bins); -// frequency tracking - afc(); - eval_s2n(currvector, bins[RESOLUTION * (numtones + 2) * (doublespaced ? 2 : 1)]); - + update_syncscope(); + eval_s2n(); prev2symbol = prev1symbol; - prev2vector = prev1vector; prev1symbol = currsymbol; - prev1vector = currvector; } pipeptr++; if (pipeptr >= twosym) pipeptr = 0; - --len; } return 0; @@ -486,22 +412,22 @@ void dominoex::sendsymbol(int sym) int tone; double f, phaseincr; - tone = (txprevtone + 2 + sym) % numtones; + tone = (txprevtone + 2 + sym) % NUMTONES; txprevtone = tone; if (reverse) - tone = (numtones - 1) - tone; + tone = (NUMTONES - 1) - tone; f = tone * tonespacing + get_txfreq_woffset() - bandwidth / 2; phaseincr = twopi * f / samplerate; for (int i = 0; i < symlen; i++) { - outbuf[i] = cos(phaseacc); - phaseacc -= phaseincr; - if (phaseacc > M_PI) - phaseacc -= twopi; - else if (phaseacc < M_PI) - phaseacc += twopi; + outbuf[i] = cos(txphase); + txphase -= phaseincr; + if (txphase > M_PI) + txphase -= twopi; + else if (txphase < M_PI) + txphase += twopi; } ModulateXmtr(outbuf, symlen); diff --git a/src/globals/globals.cxx b/src/globals/globals.cxx index 7221a50d..ad871ce9 100644 --- a/src/globals/globals.cxx +++ b/src/globals/globals.cxx @@ -46,8 +46,7 @@ const char *state_names[] = { // Elements are in enum trx_mode order. Mode name video-id uses the // first string (sname), so its length should be a multiple of 2. const struct mode_info_t mode_info[NUM_MODES] = { - { MODE_CW, &cw_modem, "CW", "CW", "CW" }, - + { MODE_CW, &cw_modem, "CW", "CW", "CW", "CW" }, { MODE_DOMINOEX4, &dominoex4_modem, "DomEX4", "DominoEX 4", "DOMINOEX4", "DOMINO" }, // These aren't Domino FEC, right? { MODE_DOMINOEX5, &dominoex5_modem, "DomEX5", "DominoEX 5", "DOMINOEX5", "DOMINO" }, { MODE_DOMINOEX8, &dominoex8_modem, "DomEX8", "DominoEX 8", "DOMINOEX8", "DOMINO" }, diff --git a/src/include/confdialog.h b/src/include/confdialog.h index 47c60353..4066b244 100644 --- a/src/include/confdialog.h +++ b/src/include/confdialog.h @@ -140,6 +140,7 @@ extern Fl_Group *tabDomEX; extern Fl_Input *txtSecondary; extern Fl_Button *btnRestartDomEX; extern Fl_Counter *valDominoEX_BW; +extern Fl_Check_Button *valDominoEX_FILTER; extern Fl_Group *tabFeld; #include "fontdef.h" extern Fl_Choice *selHellFont; diff --git a/src/include/configuration.h b/src/include/configuration.h index 947d83b8..9d7413cc 100644 --- a/src/include/configuration.h +++ b/src/include/configuration.h @@ -26,7 +26,6 @@ struct configuration { int ServerOffset; double ACQsn; // RTTY -// double rtty_squelch; int rtty_shift; int rtty_baud; int rtty_bits; @@ -38,9 +37,6 @@ struct configuration { bool rtty_autocrlf; int rtty_autocount; int rtty_afcspeed; -// bool afconoff; -// bool sqlonoff; -// double sldrSquelchValue; bool useFSKkeyline; // use RTS for FSK bool useFSKkeylineDTR; // use DTR for FSK bool FSKisLSB; @@ -79,6 +75,7 @@ struct configuration { bool olivia8bit; // DOMINOEX double DOMINOEX_BW; + bool DOMINOEX_FILTER; // MT63 bool mt63_8bit; int mt63_interleave; diff --git a/src/include/dominoex.h b/src/include/dominoex.h index 0101b2ea..62aacafd 100644 --- a/src/include/dominoex.h +++ b/src/include/dominoex.h @@ -38,11 +38,12 @@ using namespace std; -#define MAXRESOLUTION 4 -#define DOMNUMTONES 18 +#define NUMTONES 18 +#define NUMFFTS 4 +#define BASEFREQ 1000.0 struct domrxpipe { - complex vector[MAXRESOLUTION * DOMNUMTONES * 6]; /* numtones <= 18 */ + complex vector[NUMFFTS * NUMTONES * 6]; }; class dominoex : public modem { @@ -56,10 +57,9 @@ public: }; protected: // common variables - double phaseacc; + double phase[4]; + double txphase; int symlen; - int numtones; - int basetone; int doublespaced; double tonespacing; int counter; @@ -67,9 +67,8 @@ protected: // rx variables C_FIR_filter *hilbert; - C_FIR_filter *filt; - sfft *binsfft; - Cmovavg *afcfilt; + C_FIR_filter *filt[NUMFFTS]; + sfft *binsfft[NUMFFTS]; domrxpipe *pipe; unsigned int pipeptr; @@ -78,8 +77,6 @@ protected: mbuffer videodata; complex currvector; - complex prev1vector; - complex prev2vector; int currsymbol; int prev1symbol; @@ -105,15 +102,15 @@ protected: string strSecXmtText; private: - complex mixer(complex in, double f); + complex mixer(int n, complex in); void recvchar(int c); - void decodesymbol(unsigned char curtone, unsigned char prevtone); - int harddecode(complex *in); - void update_syncscope(complex *); + void decodesymbol(); + int harddecode(); + void update_syncscope(); void synchronize(); void afc(); void reset_afc(); - void eval_s2n(complex, complex); + void eval_s2n(); void sendsymbol(int sym); void sendchar(unsigned char c, int secondary); void sendidle(); diff --git a/src/mfsk/mfsk.cxx b/src/mfsk/mfsk.cxx index 0142cf21..d90f9ba6 100644 --- a/src/mfsk/mfsk.cxx +++ b/src/mfsk/mfsk.cxx @@ -525,9 +525,12 @@ void mfsk::afc() void mfsk::eval_s2n(complex c, complex n) { sig = c.mag(); // signal + noise energy - noise = n.mag() + 1e-10; // noise energy + noise = n.mag() + 1e-20; // noise energy - s2n = decayavg( s2n, fabs((sig - noise) / noise), 8); + if (metric > progStatus.sldrSquelchValue) + s2n = decayavg( s2n, fabs(sig / noise), 16 ); + else + s2n = decayavg( s2n, 1.0, 16 ); } int mfsk::rx_process(const double *buf, int len) @@ -601,7 +604,7 @@ int mfsk::rx_process(const double *buf, int len) // frequency tracking afc(); - eval_s2n(currvector, bins[numtones + 2]); + eval_s2n(currvector, bins[(currsymbol + numtones/2) % numtones]); // decode symbol softdecode(bins); // symbol sync diff --git a/src/misc/configuration.cxx b/src/misc/configuration.cxx index f0dc2c3b..dc64d497 100644 --- a/src/misc/configuration.cxx +++ b/src/misc/configuration.cxx @@ -80,6 +80,7 @@ configuration progdefaults = { false, // bool olivia8bit // DOMINOEX 2.0, // double DOMINOEX_BW; + false, // bool DOMINOEX_FILTER // MT63 false, // bool mt63_8bit; 32, // int mt63_interleave; @@ -241,7 +242,7 @@ enum TAG { \ CWTRACK, CWRISETIME, CWDASH2DOT, XQSK, CWPRE, CWPOST, CWID, CWIDWPM, OLIVIATONES, OLIVIABW, OLIVIASMARGIN, OLIVIASINTEG, OLIVIA8BIT, - DOMINOEXBW, + DOMINOEXBW, DOMINOEXFILTER, FELDFONTNBR, FELDIDLE, WFPREFILTER, USECURSORLINES, USECURSORCENTERLINE, USEBWTRACKS, @@ -381,6 +382,7 @@ void configuration::writeDefaultsXML() writeXMLint(f, "OLIVIASINTEG", oliviasinteg); writeXMLbool(f, "OLIVIA8BIT", olivia8bit); writeXMLdbl(f, "DOMINOEXBW", DOMINOEX_BW); + writeXMLbool(f, "DOMINOEXFILTER", DOMINOEX_FILTER); writeXMLint(f, "FELDFONTNBR", feldfontnbr); writeXMLbool(f, "FELDIDLE", FELD_IDLE); @@ -670,6 +672,9 @@ bool configuration::readDefaultsXML() case DOMINOEXBW : DOMINOEX_BW = atof(xml->getNodeData()); break; + case DOMINOEXFILTER : + DOMINOEX_FILTER = atoi(xml->getNodeData()); + break; case FELDFONTNBR : feldfontnbr = atoi(xml->getNodeData()); break; @@ -1018,6 +1023,7 @@ bool configuration::readDefaultsXML() else if (!strcmp("OLIVIASINTEG", nodeName)) tag = OLIVIASINTEG; else if (!strcmp("OLIVIA8BIT", nodeName)) tag = OLIVIA8BIT; else if (!strcmp("DOMINOEXBW", nodeName)) tag = DOMINOEXBW; + else if (!strcmp("DOMINOEXFILTER", nodeName)) tag = DOMINOEXFILTER; else if (!strcmp("FELDFONTNBR", nodeName)) tag = FELDFONTNBR; else if (!strcmp("FELDIDLE", nodeName)) tag = FELDIDLE; else if (!strcmp("WFPREFILTER", nodeName)) tag = WFPREFILTER; @@ -1226,6 +1232,7 @@ int configuration::setDefaults() { txtSecondary->value(secText.c_str()); valDominoEX_BW->value(DOMINOEX_BW); + valDominoEX_FILTER->value(DOMINOEX_FILTER); for (int i = 0; i < 5; i++) { btnPTT[i]->value(0); diff --git a/src/misc/status.cxx b/src/misc/status.cxx index 08248203..5c7eb2ea 100644 --- a/src/misc/status.cxx +++ b/src/misc/status.cxx @@ -6,6 +6,7 @@ #include "status.h" #include "configuration.h" #include "fl_digi.h" +#include "versions.h" #include "waterfall.h" @@ -115,10 +116,16 @@ void status::saveLastState() scopeW = scopeview->w(); scopeH = scopeview->h(); } - + + string str = PACKAGE_NAME; + str.append(" "); + str.append(PACKAGE_VERSION); + string deffname = HomeDir; deffname.append("fldigi.status"); ofstream deffile(deffname.c_str(), ios::out); + + deffile << str.c_str() << endl; deffile << lastmode << endl; deffile << mainX << endl; deffile << mainY << endl; @@ -154,43 +161,51 @@ void status::saveLastState() void status::loadLastState() { + char line[255]; + string str = PACKAGE_NAME; + str.append(" "); + str.append(PACKAGE_VERSION); + string deffname = HomeDir; deffname.append("fldigi.status"); ifstream deffile(deffname.c_str(), ios::in); if (deffile) { - deffile >> lastmode; - deffile >> mainX; - deffile >> mainY; - deffile >> mainW; - deffile >> mainH; - deffile >> rigShown; - deffile >> rigX; - deffile >> rigY; - deffile >> RxTextHeight; - deffile >> carrier; - deffile >> mag; - deffile >> speed; - deffile >> reflevel; - deffile >> ampspan; - deffile >> VIEWERnchars; - deffile >> VIEWERxpos; - deffile >> VIEWERypos; - deffile >> VIEWERvisible; - deffile >> LOGenabled; - deffile >> sldrSquelchValue; - deffile >> afconoff; - deffile >> sqlonoff; - deffile >> RcvMixer; - deffile >> XmtMixer; - deffile >> scopeX; - deffile >> scopeY; - deffile >> scopeVisible; - deffile >> scopeW; - deffile >> scopeH; - deffile.close(); - progdefaults.wfRefLevel = reflevel; - progdefaults.wfAmpSpan = ampspan; - bLastStateRead = true; + deffile.getline(line, 255); + if (str == line) { + deffile >> lastmode; + deffile >> mainX; + deffile >> mainY; + deffile >> mainW; + deffile >> mainH; + deffile >> rigShown; + deffile >> rigX; + deffile >> rigY; + deffile >> RxTextHeight; + deffile >> carrier; + deffile >> mag; + deffile >> speed; + deffile >> reflevel; + deffile >> ampspan; + deffile >> VIEWERnchars; + deffile >> VIEWERxpos; + deffile >> VIEWERypos; + deffile >> VIEWERvisible; + deffile >> LOGenabled; + deffile >> sldrSquelchValue; + deffile >> afconoff; + deffile >> sqlonoff; + deffile >> RcvMixer; + deffile >> XmtMixer; + deffile >> scopeX; + deffile >> scopeY; + deffile >> scopeVisible; + deffile >> scopeW; + deffile >> scopeH; + deffile.close(); + progdefaults.wfRefLevel = reflevel; + progdefaults.wfAmpSpan = ampspan; + bLastStateRead = true; + } } } diff --git a/src/soundcard/sound.cxx b/src/soundcard/sound.cxx index c182201b..342471eb 100644 --- a/src/soundcard/sound.cxx +++ b/src/soundcard/sound.cxx @@ -1062,7 +1062,7 @@ void SoundPort::src_data_reset(int mode) rbsize = ceil2((unsigned)(2 * CHANNELS * SCBLOCKSIZE * MAX(req_sample_rate, sd[0].dev_sample_rate) / MIN(req_sample_rate, sd[0].dev_sample_rate))); - rbsize = MAX(rbsize, 4096); + rbsize = 2 * MAX(rbsize, 4096); #ifndef NDEBUG cerr << "input rbsize=" << rbsize << endl; #endif diff --git a/src/soundcard/soundconf.cxx b/src/soundcard/soundconf.cxx index 0ec73dd9..14e672d0 100644 --- a/src/soundcard/soundconf.cxx +++ b/src/soundcard/soundconf.cxx @@ -193,8 +193,11 @@ static void sound_init_options(void) menuOutSampleRate->menu(sample_rate_menu); const char* cname; - for (int i = 0; (cname = src_get_name(i)); i++) + for (int i = 0; (cname = src_get_name(i)); i++) { + if (strstr( cname, "ZOH") != 0) continue; + if (strstr( cname, "Linear") != 0) continue; menuSampleConverter->add(cname); + } menuSampleConverter->value(progdefaults.sample_converter); menuSampleConverter->tooltip(src_get_description(progdefaults.sample_converter)); diff --git a/src/waterfall/digiscope.cxx b/src/waterfall/digiscope.cxx index 691409c6..1a9ba8be 100644 --- a/src/waterfall/digiscope.cxx +++ b/src/waterfall/digiscope.cxx @@ -407,7 +407,14 @@ void Digiscope::draw() case XHAIRS : draw_xy(); break; case DOMDATA : draw_scope(); break; case BLANK : - default: break; + default: + fl_clip(x()+2,y()+2,w()-4,h()-4); + fl_color(FL_BLACK); + fl_rectf(x()+2,y()+2,w()-4,h()-4); + fl_push_matrix(); + fl_pop_matrix(); + fl_pop_clip(); + break; } } }