diff --git a/commons.h b/commons.h index 15f1361..9cfd6c6 100644 --- a/commons.h +++ b/commons.h @@ -84,19 +84,18 @@ extern struct dec_data { short int d2[NTMAX*RX_SAMPLE_RATE]; // sample frame buffer for sample collection struct { - int nutc; //UTC as integer, HHMM - bool ndiskdat; //true ==> data read from *.wav file - int ntrperiod; //TR period (seconds) - int nQSOProgress; /* QSO state machine state */ - int nfqso; //User-selected QSO freq (kHz) - int nftx; /* Transmit audio offset where - replies might be expected */ - bool newdat; //true ==> new data, must do long FFT - int npts8; //npts for c0() array - int nfa; //Low decode limit (Hz) - int nfSplit; //JT65 | JT9 split frequency - int nfb; //High decode limit (Hz) - int ntol; //+/- decoding range around fQSO (Hz) + int nutc; // UTC as integer, HHMM + bool ndiskdat; // true ==> data read from *.wav file + int ntrperiod; // TR period (seconds) + int nQSOProgress; // QSO state machine state + int nfqso; // User-selected QSO freq (kHz) + int nftx; // Transmit audio offset where replies might be expected + bool newdat; // true ==> new data, must do long FFT + int npts8; // npts for c0() array + int nfa; // Low decode limit (Hz) (filter min) + int nfSplit; // JT65 | JT9 split frequency + int nfb; // High decode limit (Hz) (filter max) + int ntol; // +/- decoding range around fQSO (Hz) int kin; // number of frames written to d2 int kposA; // starting position of decode for submode A int kposB; // starting position of decode for submode B @@ -109,8 +108,8 @@ extern struct dec_data { int kszE; // number of frames for decode for submode E int kszI; // number of frames for decode for submode I int nzhsym; // half symbol stop index - int nsubmode; - int nsubmodes; + int nsubmode; // which submode to decode (-1 if using nsubmodes) + int nsubmodes; // which submodes to decode bool nagain; int ndepth; bool lft8apon; diff --git a/mainwindow.cpp b/mainwindow.cpp index 37004aa..7d3a75b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -2534,6 +2534,18 @@ int MainWindow::computePeriodForSubmode(int submode){ return 0; } +int MainWindow::computeBandwidthForSubmode(int submode){ + switch(submode){ + case Varicode::JS8CallNormal: return 8 * RX_SAMPLE_RATE / JS8A_SYMBOL_SAMPLES; + case Varicode::JS8CallFast: return 8 * RX_SAMPLE_RATE / JS8B_SYMBOL_SAMPLES; + case Varicode::JS8CallTurbo: return 8 * RX_SAMPLE_RATE / JS8C_SYMBOL_SAMPLES; + case Varicode::JS8CallSlow: return 8 * RX_SAMPLE_RATE / JS8E_SYMBOL_SAMPLES; + case Varicode::JS8CallUltra: return 8 * RX_SAMPLE_RATE / JS8I_SYMBOL_SAMPLES; + } + + return 0; +} + int MainWindow::computeStop(int submode, int period){ int stop = 0; @@ -4357,9 +4369,6 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ dec_data.params.nagain=0; dec_data.params.nzhsym=m_ihsym; - dec_data.params.nfa=m_wideGraph->nStartFreq(); - dec_data.params.nfb=m_wideGraph->Fmax(); - if(dec_data.params.nagain==0 && dec_data.params.newdat==1 && (!m_diskData)) { qint64 ms = DriftingDateTime::currentMSecsSinceEpoch() % 86400000; int imin=ms/60000; @@ -4407,6 +4416,13 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ dec_data.params.nfSplit=m_wideGraph->Fmin(); dec_data.params.nfb=m_wideGraph->Fmax(); + int filter = max(0, m_wideGraph->filter()); + if(filter){ + int f = currentFreqOffset() + computeBandwidthForSubmode(submode)/2; + dec_data.params.nfa=max(0, f - filter/2); + dec_data.params.nfb=min(f + filter/2, 5000); + } + //if(m_mode=="FT8" and m_config.bHound() and !ui->cbRxAll->isChecked()) dec_data.params.nfb=1000; //if(m_mode=="FT8" and m_config.bFox()) dec_data.params.nfqso=200; @@ -7534,6 +7550,8 @@ void MainWindow::on_actionJS8_triggered() updateModeButtonText(); m_wideGraph->setSubMode(m_nSubMode); + m_wideGraph->setFilterMinimum(computeBandwidthForSubmode(m_nSubMode)); + bool bVHF=m_config.enable_VHF_features(); enable_DXCC_entity (m_config.DXCC ()); switch_mode (Modes::JS8); diff --git a/mainwindow.h b/mainwindow.h index fd4e1cf..04ef26a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -943,6 +943,7 @@ private: void statusChanged(); void fixStop(); int computePeriodForSubmode(int submode); + int computeBandwidthForSubmode(int submode); int computeStop(int submode, int period); //int computeCurrentCycle(int period); //int computeCycleStartForDecode(int cycle, int period); diff --git a/plotter.cpp b/plotter.cpp index c65f7a0..0f1b38c 100644 --- a/plotter.cpp +++ b/plotter.cpp @@ -28,6 +28,7 @@ CPlotter::CPlotter(QWidget *parent) : //CPlotter Constructor m_plot2dGain {0}, m_plot2dZero {0}, m_nSubMode {0}, + m_filterWidth {0}, m_turbo {false}, m_Running {false}, m_paintEventBusy {false}, @@ -86,6 +87,8 @@ void CPlotter::resizeEvent(QResizeEvent* ) //resizeEvent() m_h1=m_h-m_h2; // m_line=0; + m_FilterOverlayPixmap = QPixmap(m_Size.width(), m_h); + m_FilterOverlayPixmap.fill(Qt::transparent); m_DialOverlayPixmap = QPixmap(m_Size.width(), m_h); m_DialOverlayPixmap.fill(Qt::transparent); m_HoverOverlayPixmap = QPixmap(m_Size.width(), m_h); @@ -120,6 +123,10 @@ void CPlotter::paintEvent(QPaintEvent *) // paint painter.drawPixmap(m_lastMouseX, 0, m_HoverOverlayPixmap); } + if(m_filterWidth > 0){ + painter.drawPixmap(0, 0, m_FilterOverlayPixmap); + } + m_paintEventBusy=false; } @@ -467,7 +474,7 @@ void CPlotter::DrawOverlay() //DrawOverlay() } - + // paint dials and filter overlays if(m_mode=="FT8"){ int fwidth=XfromFreq(m_rxFreq+bw)-XfromFreq(m_rxFreq); #if TEST_FOX_WAVE_GEN @@ -523,6 +530,28 @@ void CPlotter::DrawOverlay() //DrawOverlay() hoverPainter.setFont(Font); hoverPainter.drawText(fwidth + 5, m_h, QString("%1").arg(f)); #endif + + if(m_filterWidth > 0){ + int filterStart=XfromFreq(m_rxFreq+bw/2-m_filterWidth/2); + int filterEnd=XfromFreq(m_rxFreq+bw/2+m_filterWidth/2); + + // TODO: make sure filter is visible before painting... + + QPainter filterPainter(&m_FilterOverlayPixmap); + filterPainter.initFrom(this); + filterPainter.setCompositionMode(QPainter::CompositionMode_Source); + filterPainter.fillRect(0, 0, m_Size.width(), m_h, Qt::transparent); + + QPen thinYellow(Qt::yellow, 1); + filterPainter.setPen(thinYellow); + filterPainter.drawLine(filterStart, 30, filterStart, m_h); + filterPainter.drawLine(filterEnd, 30, filterEnd, m_h); + + QColor blackMask(0, 0, 0, 128); + filterPainter.fillRect(0, 30, filterStart, m_h, blackMask); + filterPainter.fillRect(filterEnd+1, 30, m_Size.width(), m_h, blackMask); + } + } } @@ -786,6 +815,13 @@ void CPlotter::setTurbo(bool turbo) update(); } +void CPlotter::setFilter(int width) +{ + m_filterWidth=width; + DrawOverlay(); + update(); +} + void CPlotter::setFlatten(bool b1, bool b2) { m_Flatten=0; diff --git a/plotter.h b/plotter.h index 9abe45d..9d8e269 100644 --- a/plotter.h +++ b/plotter.h @@ -82,6 +82,7 @@ public: void setTol(int n); void setRxBand(QString band); void setTurbo(bool turbo); + void setFilter(int width); #if JS8_USE_REFSPEC void setReference(bool b) {m_bReference = b;} bool Reference() const {return m_bReference;} @@ -135,6 +136,7 @@ private: qint32 m_ia; qint32 m_ib; + QPixmap m_FilterOverlayPixmap; QPixmap m_DialOverlayPixmap; QPixmap m_HoverOverlayPixmap; QPixmap m_WaterfallPixmap; @@ -150,6 +152,7 @@ private: QString m_rxBand; QString m_redFile; + int m_filterWidth; bool m_turbo; bool m_Running; bool m_paintEventBusy; diff --git a/widegraph.cpp b/widegraph.cpp index a64a6bf..80dc633 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -10,6 +10,7 @@ #include "SettingsGroup.hpp" #include "DriftingDateTime.h" +#include "keyeater.h" #include "moc_widegraph.cpp" @@ -35,6 +36,16 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : setMaximumWidth (MAX_SCREENSIZE); setMaximumHeight (880); + auto filterEscapeEater = new KeyPressEater(); + connect(filterEscapeEater, &KeyPressEater::keyPressed, this, [this](QObject */*obj*/, QKeyEvent *e, bool *pProcessed){ + if(e->key() != Qt::Key_Escape){ + return; + } + setFilter(0); + if(pProcessed) *pProcessed=true; + }); + ui->filterSpinBox->installEventFilter(filterEscapeEater); + ui->widePlot->setCursor(Qt::CrossCursor); ui->widePlot->setMaximumHeight(800); ui->widePlot->setCurrent(false); @@ -99,6 +110,9 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : setRxRange (); ui->controls_widget->setVisible(!m_settings->value("HideControls", false).toBool()); ui->cbControls->setChecked(!m_settings->value("HideControls", false).toBool()); + + setFilter(m_settings->value("FilterWidth", 0).toInt()); + setFilterEnabled(m_settings->value("FilterEnabled", false).toBool()); } int index=0; @@ -155,6 +169,8 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("HideControls", ui->controls_widget->isHidden ()); m_settings->setValue ("FminPerBand", m_fMinPerBand); m_settings->setValue ("CenterOffset", ui->centerSpinBox->value()); + m_settings->setValue ("FilterWidth", m_filterWidth); + m_settings->setValue ("FilterEnabled", m_filterEnabled); } void WideGraph::drawRed(int ia, int ib) @@ -309,6 +325,39 @@ int WideGraph::Fmax() //Fmax return std::min(5000,ui->widePlot->Fmax()); } +int WideGraph::filter() +{ + return m_filterEnabled ? m_filterWidth : 0; +} + +void WideGraph::setFilter(int width){ + if(width == m_filterWidth){ + return; + } + + // update the filter history + m_filterWidthPrev = m_filterWidth; + m_filterWidth = width; + + // update the spinner UI + ui->filterSpinBox->setValue(width); + + // update the wide plot UI + ui->widePlot->setFilter(width); +} + +void WideGraph::setFilterMinimum(int width){ + ui->filterSpinBox->setMinimum(width); +} + +void WideGraph::setFilterEnabled(bool enabled){ + m_filterEnabled = enabled; + + // update the wide plot with the + ui->widePlot->setFilter(enabled ? m_filterWidth : 0); + ui->filterSpinBox->setEnabled(enabled); +} + int WideGraph::fSpan() { return ui->widePlot->fSpan (); @@ -568,6 +617,14 @@ void WideGraph::on_sbPercent2dPlot_valueChanged(int n) ui->widePlot->SetPercent2DScreen(n); } +void WideGraph::on_filterSpinBox_valueChanged(int n){ + setFilter(n); +} + +void WideGraph::on_filterCheckBox_toggled(bool b){ + setFilterEnabled(b); +} + void WideGraph::setRedFile(QString fRed) { ui->widePlot->setRedFile(fRed); diff --git a/widegraph.h b/widegraph.h index b416832..6d39449 100644 --- a/widegraph.h +++ b/widegraph.h @@ -33,6 +33,10 @@ public: int nStartFreq(); int Fmin(); int Fmax(); + int filter(); + void setFilter(int width); + void setFilterMinimum(int width); + void setFilterEnabled(bool enabled); int fSpan(); void saveSettings(); void setFsample(int n); @@ -89,6 +93,8 @@ private slots: void on_zero2dSlider_valueChanged(int value); void on_smoSpinBox_valueChanged(int n); void on_sbPercent2dPlot_valueChanged(int n); + void on_filterSpinBox_valueChanged(int n); + void on_filterCheckBox_toggled(bool b); private: void readPalette (); @@ -102,6 +108,10 @@ private: WFPalette m_userPalette; QHash m_fMinPerBand; + bool m_filterEnabled; + + qint32 m_filterWidth; + qint32 m_filterWidthPrev; qint32 m_waterfallAvg; qint32 m_TRperiod; qint32 m_nsps; diff --git a/widegraph.ui b/widegraph.ui index c7d3097..a9342a1 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -6,7 +6,7 @@ 0 0 - 1083 + 1166 154 @@ -103,7 +103,14 @@ 0 - + + + + Select waterfall palette + + + + @@ -140,32 +147,24 @@ - - - - <html><head/><body><p>Set fractional size of spectrum in this window.</p></body></html> - - - Qt::AlignCenter - - - % - - - Spec - - - 100 - - - 5 - - - 0 + + + + Qt::Vertical - + + + + QSY + + + false + + + + <html><head/><body><p>Select data for spectral display</p></body></html> @@ -190,59 +189,7 @@ - - - - - - <html><head/><body><p>Flatten spectral baseline over the full displayed interval.</p></body></html> - - - Flatten - - - - - - - false - - - <html><head/><body><p>Compute and save a reference spectrum. (Not yet fully implemented.)</p></body></html> - - - Ref Spec - - - - - - - - - Compression factor for frequency scale - - - - - - Bins/Pixel - - - 1 - - - 1000 - - - 1 - - - 5 - - - - + @@ -301,51 +248,60 @@ - - - - Select waterfall palette - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - 200 - 16777215 - + + + + false - Waterfall gain + Smoothing of Linear Average spectrum + + + Qt::AlignCenter + + + + + + Smooth - -50 + 1 - 50 - - - Qt::Horizontal - - - QSlider::TicksAbove + 7 - + + + + false + + + <html><head/><body><p>Decode JT9 only above this frequency</p></body></html> + + + JT9 + + + JT65 + + + 0 + + + 5000 + + + 100 + + + 3000 + + + + @@ -382,135 +338,7 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Number of FFTs averaged (controls waterfall scrolling rate) - - - N Avg - - - 1 - - - 50 - - - - - - - false - - - Smoothing of Linear Average spectrum - - - Qt::AlignCenter - - - - - - Smooth - - - 1 - - - 7 - - - - - - - Hz - - - Offset - - - 5000 - - - 1 - - - 1500 - - - - - - - Qt::Vertical - - - - - - - Qt::Vertical - - - - - - - false - - - <html><head/><body><p>Decode JT9 only above this frequency</p></body></html> - - - JT9 - - - JT65 - - - 0 - - - 5000 - - - 100 - - - 3000 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + @@ -537,15 +365,45 @@ - - - - QSY + + + + Qt::Horizontal - - false + + + 40 + 20 + - + + + + + + + + <html><head/><body><p>Flatten spectral baseline over the full displayed interval.</p></body></html> + + + Flatten + + + + + + + false + + + <html><head/><body><p>Compute and save a reference spectrum. (Not yet fully implemented.)</p></body></html> + + + Ref Spec + + + + @@ -566,6 +424,180 @@ + + + + <html><head/><body><p>Set fractional size of spectrum in this window.</p></body></html> + + + Qt::AlignCenter + + + % + + + Spec + + + 100 + + + 5 + + + 0 + + + + + + + Hz + + + Offset + + + 5000 + + + 1 + + + 1500 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + Number of FFTs averaged (controls waterfall scrolling rate) + + + N Avg + + + 1 + + + 50 + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 200 + 16777215 + + + + Waterfall gain + + + -50 + + + 50 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + + + + + Compression factor for frequency scale + + + + + + Bins/Pixel + + + 1 + + + 1000 + + + 1 + + + 5 + + + + + + + Enable Filter + + + + + + + false + + + + + + Hz + + + Filter + + + 5000 + + + 10 + + + 500 + + + @@ -579,9 +611,6 @@ 1 - - offsetSpinBox -