diff --git a/resources/icons.qrc b/resources/icons.qrc
index 8f535b0992..71d341c7e7 100644
--- a/resources/icons.qrc
+++ b/resources/icons.qrc
@@ -1,25 +1,28 @@
-
- icons/audio-card.svg
- icons/bookmark-new.svg
- icons/clear.svg
- icons/clock.svg
- icons/close.svg
- icons/document.svg
- icons/flash.svg
- icons/folder.svg
- icons/fullscreen.svg
- icons/floppy.svg
- icons/gqrx.svg
- icons/help.svg
- icons/info.svg
- icons/play.svg
- icons/power-off.svg
- icons/record.svg
- icons/refresh.svg
- icons/settings.svg
- icons/signal.svg
- icons/tangeo-network-idle.svg
- icons/terminal.svg
-
+
+ icons/lock.svg
+ icons/remove.svg
+ icons/add.svg
+ icons/audio-card.svg
+ icons/bookmark-new.svg
+ icons/clear.svg
+ icons/clock.svg
+ icons/close.svg
+ icons/document.svg
+ icons/flash.svg
+ icons/folder.svg
+ icons/fullscreen.svg
+ icons/floppy.svg
+ icons/gqrx.svg
+ icons/help.svg
+ icons/info.svg
+ icons/play.svg
+ icons/power-off.svg
+ icons/record.svg
+ icons/refresh.svg
+ icons/settings.svg
+ icons/signal.svg
+ icons/tangeo-network-idle.svg
+ icons/terminal.svg
+
diff --git a/resources/icons/add.svg b/resources/icons/add.svg
new file mode 100644
index 0000000000..de7746458f
--- /dev/null
+++ b/resources/icons/add.svg
@@ -0,0 +1,25 @@
+
+
+
\ No newline at end of file
diff --git a/resources/icons/lock.svg b/resources/icons/lock.svg
new file mode 100644
index 0000000000..b84551c887
--- /dev/null
+++ b/resources/icons/lock.svg
@@ -0,0 +1,402 @@
+
+
+
+
diff --git a/resources/icons/remove.svg b/resources/icons/remove.svg
new file mode 100644
index 0000000000..3280a35948
--- /dev/null
+++ b/resources/icons/remove.svg
@@ -0,0 +1,133 @@
+
+
+
diff --git a/src/applications/gqrx/mainwindow.cpp b/src/applications/gqrx/mainwindow.cpp
index 2950ee5d5d..7345358089 100644
--- a/src/applications/gqrx/mainwindow.cpp
+++ b/src/applications/gqrx/mainwindow.cpp
@@ -67,6 +67,7 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
ui(new Ui::MainWindow),
d_lnb_lo(0),
d_hw_freq(0),
+ d_auto_bookmarks(false),
d_fftAvg(0.25),
d_have_audio(true),
dec_afsk1200(nullptr)
@@ -94,7 +95,7 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
ui->freqCtrl->setup(0, 0, 9999e6, 1, FCTL_UNIT_NONE);
ui->freqCtrl->setFrequency(144500000);
- d_filter_shape = receiver::FILTER_SHAPE_NORMAL;
+ d_filter_shape = Modulations::FILTER_SHAPE_NORMAL;
/* create receiver object */
rx = new receiver("", "", 1);
@@ -198,7 +199,14 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
ui->menu_View->addSeparator();
ui->menu_View->addAction(ui->actionFullScreen);
+ /* Setup demodulator switching SpinBox */
+ rxSpinBox = new QSpinBox(ui->mainToolBar);
+ rxSpinBox->setMaximum(255);
+ rxSpinBox->setValue(0);
+ ui->mainToolBar->insertWidget(ui->actionAddDemodulator, rxSpinBox);
+
/* connect signals and slots */
+ connect(rxSpinBox, SIGNAL(valueChanged(int)), this, SLOT(rxSpinBox_valueChanged(int)));
connect(ui->freqCtrl, SIGNAL(newFrequency(qint64)), this, SLOT(setNewFrequency(qint64)));
connect(ui->freqCtrl, SIGNAL(newFrequency(qint64)), remote, SLOT(setNewFrequency(qint64)));
connect(ui->freqCtrl, SIGNAL(newFrequency(qint64)), uiDockAudio, SLOT(setRxFrequency(qint64)));
@@ -216,11 +224,12 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(uiDockInputCtl, SIGNAL(antennaSelected(QString)), this, SLOT(setAntenna(QString)));
connect(uiDockInputCtl, SIGNAL(freqCtrlResetChanged(bool)), this, SLOT(setFreqCtrlReset(bool)));
connect(uiDockInputCtl, SIGNAL(invertScrollingChanged(bool)), this, SLOT(setInvertScrolling(bool)));
- connect(uiDockRxOpt, SIGNAL(rxFreqChanged(qint64)), ui->freqCtrl, SLOT(setFrequency(qint64)));
+ connect(uiDockInputCtl, SIGNAL(autoBookmarksChanged(bool)), this, SLOT(setAutoBookmarks(bool)));
+ connect(uiDockRxOpt, SIGNAL(rxFreqChanged(qint64)), this, SLOT(setNewFrequency(qint64)));
connect(uiDockRxOpt, SIGNAL(filterOffsetChanged(qint64)), this, SLOT(setFilterOffset(qint64)));
connect(uiDockRxOpt, SIGNAL(filterOffsetChanged(qint64)), remote, SLOT(setFilterOffset(qint64)));
- connect(uiDockRxOpt, SIGNAL(demodSelected(int)), this, SLOT(selectDemod(int)));
- connect(uiDockRxOpt, SIGNAL(demodSelected(int)), remote, SLOT(setMode(int)));
+ connect(uiDockRxOpt, SIGNAL(demodSelected(Modulations::idx)), this, SLOT(selectDemod(Modulations::idx)));
+ connect(uiDockRxOpt, SIGNAL(demodSelected(Modulations::idx)), remote, SLOT(setMode(Modulations::idx)));
connect(uiDockRxOpt, SIGNAL(fmMaxdevSelected(float)), this, SLOT(setFmMaxdev(float)));
connect(uiDockRxOpt, SIGNAL(fmEmphSelected(double)), this, SLOT(setFmEmph(double)));
connect(uiDockRxOpt, SIGNAL(amDcrToggled(bool)), this, SLOT(setAmDcr(bool)));
@@ -234,9 +243,13 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(uiDockRxOpt, SIGNAL(agcAttackChanged(int)), this, SLOT(setAgcAttack(int)));
connect(uiDockRxOpt, SIGNAL(agcDecayChanged(int)), this, SLOT(setAgcDecay(int)));
connect(uiDockRxOpt, SIGNAL(agcHangChanged(int)), this, SLOT(setAgcHang(int)));
+ connect(uiDockRxOpt, SIGNAL(agcPanningChanged(int)), this, SLOT(setAgcPanning(int)));
+ connect(uiDockRxOpt, SIGNAL(agcPanningAuto(bool)), this, SLOT(setAgcPanningAuto(bool)));
connect(uiDockRxOpt, SIGNAL(noiseBlankerChanged(int,bool,float)), this, SLOT(setNoiseBlanker(int,bool,float)));
connect(uiDockRxOpt, SIGNAL(sqlLevelChanged(double)), this, SLOT(setSqlLevel(double)));
- connect(uiDockRxOpt, SIGNAL(sqlAutoClicked()), this, SLOT(setSqlLevelAuto()));
+ connect(uiDockRxOpt, SIGNAL(sqlAutoClicked(bool)), this, SLOT(setSqlLevelAuto(bool)));
+ connect(uiDockRxOpt, SIGNAL(sqlResetAllClicked()), this, SLOT(resetSqlLevelGlobal()));
+ connect(uiDockRxOpt, SIGNAL(freqLock(bool, bool)), this, SLOT(setFreqLock(bool, bool)));
connect(uiDockAudio, SIGNAL(audioGainChanged(float)), this, SLOT(setAudioGain(float)));
connect(uiDockAudio, SIGNAL(audioMuteChanged(bool)), this, SLOT(setAudioMute(bool)));
connect(uiDockAudio, SIGNAL(audioStreamingStarted(QString,int,bool)), this, SLOT(startAudioStream(QString,int,bool)));
@@ -252,6 +265,7 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(uiDockAudio, SIGNAL(recMinTimeChanged(int)), this, SLOT(recMinTimeChanged(int)));
connect(uiDockAudio, SIGNAL(recMaxGapChanged(int)), this, SLOT(recMaxGapChanged(int)));
connect(uiDockAudio, SIGNAL(fftRateChanged(int)), this, SLOT(setAudioFftRate(int)));
+ connect(uiDockAudio, SIGNAL(copyRecSettingsToAllVFOs()), this, SLOT(copyRecSettingsToAllVFOs()));
connect(uiDockFft, SIGNAL(fftSizeChanged(int)), this, SLOT(setIqFftSize(int)));
connect(uiDockFft, SIGNAL(fftRateChanged(int)), this, SLOT(setIqFftRate(int)));
connect(uiDockFft, SIGNAL(fftWindowChanged(int)), this, SLOT(setIqFftWindow(int)));
@@ -275,6 +289,7 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(ui->plotter, SIGNAL(newZoomLevel(float)),
uiDockFft, SLOT(setZoomLevel(float)));
connect(ui->plotter, SIGNAL(newSize()), this, SLOT(setWfSize()));
+ connect(ui->plotter, SIGNAL(selectVfo(int)), this, SLOT(on_plotter_selectVfo(int)));
connect(uiDockFft, SIGNAL(fftColorChanged(QColor)), this, SLOT(setFftColor(QColor)));
connect(uiDockFft, SIGNAL(fftFillToggled(bool)), this, SLOT(setFftFill(bool)));
@@ -283,8 +298,12 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(uiDockRDS, SIGNAL(rdsDecoderToggled(bool)), this, SLOT(setRdsDecoder(bool)));
// Bookmarks
- connect(uiDockBookmarks, SIGNAL(newBookmarkActivated(qint64, QString, int)), this, SLOT(onBookmarkActivated(qint64, QString, int)));
+ connect(uiDockBookmarks, SIGNAL(newBookmarkActivated(BookmarkInfo &)), this, SLOT(onBookmarkActivated(BookmarkInfo &)));
+ //FIXME: create a new slot that would avoid changing hw frequency if the bookmark is in the current bandwidth
+ connect(uiDockBookmarks, SIGNAL(newBookmarkActivated(qint64)), this, SLOT(setNewFrequency(qint64)));
+ connect(uiDockBookmarks, SIGNAL(newBookmarkActivatedAddDemod(BookmarkInfo &)), this, SLOT(onBookmarkActivatedAddDemod(BookmarkInfo &)));
connect(uiDockBookmarks->actionAddBookmark, SIGNAL(triggered()), this, SLOT(on_actionAddBookmark_triggered()));
+ connect(&Bookmarks::Get(), SIGNAL(BookmarksChanged()), ui->plotter, SLOT(updateOverlay()));
//DXC Spots
connect(&DXCSpots::Get(), SIGNAL(dxcSpotsUpdated()), this, SLOT(updateClusterSpots()));
@@ -303,8 +322,8 @@ MainWindow::MainWindow(const QString& cfgfile, bool edit_conf, QWidget *parent)
connect(remote, SIGNAL(newFrequency(qint64)), ui->freqCtrl, SLOT(setFrequency(qint64)));
connect(remote, SIGNAL(newLnbLo(double)), uiDockInputCtl, SLOT(setLnbLo(double)));
connect(remote, SIGNAL(newLnbLo(double)), this, SLOT(setLnbLo(double)));
- connect(remote, SIGNAL(newMode(int)), this, SLOT(selectDemod(int)));
- connect(remote, SIGNAL(newMode(int)), uiDockRxOpt, SLOT(setCurrentDemod(int)));
+ connect(remote, SIGNAL(newMode(Modulations::idx)), this, SLOT(selectDemod(Modulations::idx)));
+ connect(remote, SIGNAL(newMode(Modulations::idx)), uiDockRxOpt, SLOT(setCurrentDemod(Modulations::idx)));
connect(remote, SIGNAL(newSquelchLevel(double)), this, SLOT(setSqlLevel(double)));
connect(remote, SIGNAL(newSquelchLevel(double)), uiDockRxOpt, SLOT(setSquelchLevel(double)));
connect(uiDockRxOpt, SIGNAL(sqlLevelChanged(double)), remote, SLOT(setSquelchLevel(double)));
@@ -418,6 +437,7 @@ MainWindow::~MainWindow()
delete [] d_realFftData;
delete [] d_iirFftData;
delete qsvg_dummy;
+ delete rxSpinBox;
}
/**
@@ -445,6 +465,8 @@ bool MainWindow::loadConfig(const QString& cfgfile, bool check_crash,
bool conf_ok = false;
bool conv_ok;
bool skip_loading_cfg = false;
+ int ver = 0;
+ qint64 hw_freq = 0;
qDebug() << "Loading configuration from:" << cfgfile;
@@ -496,6 +518,7 @@ bool MainWindow::loadConfig(const QString& cfgfile, bool check_crash,
// manual reconf (FIXME: check status)
conv_ok = false;
+ ver = m_settings->value("configversion").toInt(&conv_ok);
// hide toolbar
bool_val = m_settings->value("gui/hide_toolbar", false).toBool();
if (bool_val)
@@ -631,36 +654,36 @@ bool MainWindow::loadConfig(const QString& cfgfile, bool check_crash,
}
uiDockInputCtl->readSettings(m_settings); // this will also update freq range
- uiDockRxOpt->readSettings(m_settings);
- uiDockFft->readSettings(m_settings);
- uiDockAudio->readSettings(m_settings);
- dxc_options->readSettings(m_settings);
- {
- int64_val = m_settings->value("input/frequency", 14236000).toLongLong(&conv_ok);
+ int64_val = m_settings->value("input/frequency", 14236000).toLongLong(&conv_ok);
- // If frequency is out of range set frequency to the center of the range.
- qint64 hw_freq = int64_val - d_lnb_lo - (qint64)(rx->get_filter_offset());
- if (hw_freq < d_hw_freq_start || hw_freq > d_hw_freq_stop)
- {
- int64_val = (d_hw_freq_stop - d_hw_freq_start) / 2 +
- (qint64)(rx->get_filter_offset()) + d_lnb_lo;
- }
+ // If frequency is out of range set frequency to the center of the range.
+ hw_freq = int64_val - d_lnb_lo;
+ if (hw_freq < d_hw_freq_start || hw_freq > d_hw_freq_stop)
+ {
+ hw_freq = (d_hw_freq_stop - d_hw_freq_start) / 2;
+ int64_val = hw_freq + d_lnb_lo;
+ }
- ui->freqCtrl->setFrequency(int64_val);
+ rx->set_rf_freq(hw_freq);
+ if (ver >= 4)
+ {
+ ui->freqCtrl->setFrequency(int64_val + (qint64)(rx->get_filter_offset()));
setNewFrequency(ui->freqCtrl->getFrequency()); // ensure all GUI and RF is updated
}
-
+ readRXSettings(ver);
+ if (ver < 4)
{
- int flo = m_settings->value("receiver/filter_low_cut", 0).toInt(&conv_ok);
- int fhi = m_settings->value("receiver/filter_high_cut", 0).toInt(&conv_ok);
-
- if (conv_ok && uiDockRxOpt->currentDemod() != DockRxOpt::MODE_OFF && flo != fhi)
- {
- on_plotter_newFilterFreq(flo, fhi);
- }
+ rx->set_rf_freq(hw_freq - rx->get_filter_offset());
+ ui->freqCtrl->setFrequency(hw_freq + d_lnb_lo);
+ setNewFrequency(hw_freq + d_lnb_lo);
}
+ uiDockFft->readSettings(m_settings);
+ uiDockBookmarks->readSettings(m_settings);
+ uiDockAudio->readSettings(m_settings);
+ dxc_options->readSettings(m_settings);
+
iq_tool->readSettings(m_settings);
/*
@@ -744,29 +767,329 @@ void MainWindow::storeSession()
{
if (m_settings)
{
- m_settings->setValue("input/frequency", ui->freqCtrl->getFrequency());
+ int rx_count = rx->get_rx_count();
+ m_settings->setValue("configversion", (rx_count <= 1) ? 3 : 4);
+ for (int i = 0; true; i++)
+ {
+ QString grp = QString("rx%1").arg(i);
+ QString offset = QString("rx%1/offset").arg(i);
+ if (m_settings->contains(offset))
+ m_settings->remove(grp);
+ else
+ break;
+ }
+ m_settings->remove("audio");
+ m_settings->remove("receiver");
+ if (rx_count <= 1)
+ m_settings->setValue("input/frequency", qint64(rx->get_rf_freq() + d_lnb_lo + rx->get_filter_offset()));
+ else
+ m_settings->setValue("input/frequency", qint64(rx->get_rf_freq() + d_lnb_lo));
uiDockInputCtl->saveSettings(m_settings);
- uiDockRxOpt->saveSettings(m_settings);
uiDockFft->saveSettings(m_settings);
uiDockAudio->saveSettings(m_settings);
+ uiDockBookmarks->saveSettings(m_settings);
remote->saveSettings(m_settings);
iq_tool->saveSettings(m_settings);
dxc_options->saveSettings(m_settings);
+ int old_current = rx->get_current();
+ int int_val;
+ for (int i = 0; i < rx_count; i++)
{
+ if (rx_count <= 1)
+ m_settings->beginGroup("receiver");
+ else
+ m_settings->beginGroup(QString("rx%1").arg(i));
+ m_settings->remove("");
+ rx->fake_select_rx(i);
+
+ m_settings->setValue("demod", Modulations::GetStringForModulationIndex(rx->get_demod()));
+
+ int cwofs = rx->get_cw_offset();
+ if (cwofs == 700)
+ m_settings->remove("cwoffset");
+ else
+ m_settings->setValue("cwoffset", cwofs);
+
+ // currently we do not need the decimal
+ int_val = (int)rx->get_fm_maxdev();
+ if (int_val == 2500)
+ m_settings->remove("fm_maxdev");
+ else
+ m_settings->setValue("fm_maxdev", int_val);
+
+ // save as usec
+ int_val = (int)(1.0e6 * rx->get_fm_deemph());
+ if (int_val == 75)
+ m_settings->remove("fm_deemph");
+ else
+ m_settings->setValue("fm_deemph", int_val);
+
+ qint64 offs = rx->get_filter_offset();
+ if (offs)
+ {
+ m_settings->setValue("offset", offs);
+ }
+ else
+ m_settings->remove("offset");
+
+ if (rx->get_freq_lock())
+ m_settings->setValue("freq_locked", true);
+ else
+ m_settings->remove("freq_locked");
+
+ double sql_lvl = rx->get_sql_level();
+ if (sql_lvl > -150.0)
+ m_settings->setValue("sql_level", sql_lvl);
+ else
+ m_settings->remove("sql_level");
+
+ // AGC settings
+ int_val = rx->get_agc_target_level();
+ if (int_val != 0)
+ m_settings->setValue("agc_target_level", int_val);
+ else
+ m_settings->remove("agc_target_level");
+
+ int_val = rx->get_agc_attack();
+ if (int_val != 20)
+ m_settings->setValue("agc_attack", int_val);
+ else
+ m_settings->remove("agc_decay");
+
+ int_val = rx->get_agc_decay();
+ if (int_val != 500)
+ m_settings->setValue("agc_decay", int_val);
+ else
+ m_settings->remove("agc_decay");
+
+ int_val = rx->get_agc_hang();
+ if (int_val != 0)
+ m_settings->setValue("agc_hang", int_val);
+ else
+ m_settings->remove("agc_hang");
+
+ int_val = rx->get_agc_panning();
+ if (int_val != 0)
+ m_settings->setValue("agc_panning", int_val);
+ else
+ m_settings->remove("agc_panning");
+
+ if (rx->get_agc_panning_auto())
+ m_settings->setValue("agc_panning_auto", true);
+ else
+ m_settings->remove("agc_panning_auto");
+
+ int_val = rx->get_agc_max_gain();
+ if (int_val != 100)
+ m_settings->setValue("agc_maxgain", int_val);
+ else
+ m_settings->remove("agc_maxgain");
+
+ // AGC Off
+ if (!rx->get_agc_on())
+ m_settings->setValue("agc_off", true);
+ else
+ m_settings->remove("agc_off");
+ //filter
int flo, fhi;
- ui->plotter->getHiLowCutFrequencies(&flo, &fhi);
+ receiver::filter_shape fdw;
+ rx->get_filter(flo, fhi, fdw);
if (flo != fhi)
{
- m_settings->setValue("receiver/filter_low_cut", flo);
- m_settings->setValue("receiver/filter_high_cut", fhi);
+ m_settings->setValue("filter_low_cut", flo);
+ m_settings->setValue("filter_high_cut", fhi);
+ m_settings->setValue("filter_shape", fdw);
}
+
+ if (rx_count <= 1)
+ {
+ m_settings->endGroup();
+ m_settings->beginGroup("audio");
+ }
+ if (rx->get_audio_rec_dir() != QDir::homePath().toStdString())
+ m_settings->setValue("rec_dir", rx->get_audio_rec_dir().data());
+ else
+ m_settings->remove("rec_dir");
+
+ if (rx->get_audio_rec_sql_triggered() != false)
+ m_settings->setValue("squelch_triggered_recording", true);
+ else
+ m_settings->remove("squelch_triggered_recording");
+
+ int_val = rx->get_audio_rec_min_time();
+ if (int_val != 0)
+ m_settings->setValue("rec_min_time", int_val);
+ else
+ m_settings->remove("rec_min_time");
+
+ int_val = rx->get_audio_rec_max_gap();
+ if (int_val != 0)
+ m_settings->setValue("rec_max_gap", int_val);
+ else
+ m_settings->remove("rec_max_gap");
+
+ m_settings->endGroup();
+ if (rx_count <= 1)
+ break;
}
+ rx->fake_select_rx(old_current);
+ if (rx_count > 1)
+ m_settings->setValue("gui/current_rx", old_current);
+ else
+ m_settings->remove("gui/current_rx");
}
}
+void MainWindow::readRXSettings(int ver)
+{
+ bool conv_ok;
+ int int_val;
+ double dbl_val;
+ int i = 0;
+ rxSpinBox->setMaximum(0);
+ while (rx->get_rx_count() > 1)
+ rx->delete_rx();
+ ui->plotter->setCurrentVfo(0);
+ ui->plotter->clearVfos();
+ QString grp = (ver >= 4) ? QString("rx%1").arg(i) : "receiver";
+ while (1)
+ {
+ m_settings->beginGroup(grp);
+
+ qint64 offs = m_settings->value("offset", 0).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_filter_offset(offs);
+
+ if (m_settings->value("freq_locked", false).toBool())
+ rx->set_freq_lock(true);
+ else
+ rx->set_freq_lock(false);
+
+ int_val = Modulations::MODE_AM;
+ if (m_settings->contains("demod")) {
+ if (ver >= 3) {
+ int_val = Modulations::GetEnumForModulationString(m_settings->value("demod").toString());
+ } else {
+ int_val = Modulations::ConvertFromOld(m_settings->value("demod").toInt(&conv_ok));
+ }
+ }
+ rx->set_demod(Modulations::idx(int_val));
+
+ int_val = m_settings->value("cwoffset", 700).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_cw_offset(int_val);
+
+ int_val = m_settings->value("fm_maxdev", 2500).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_fm_maxdev(int_val);
+
+ dbl_val = m_settings->value("fm_deemph", 75).toDouble(&conv_ok);
+ if (conv_ok && dbl_val >= 0)
+ rx->set_fm_deemph(1.0e-6 * dbl_val); // was stored as usec
+
+ dbl_val = m_settings->value("sql_level", 1.0).toDouble(&conv_ok);
+ if (conv_ok && dbl_val < 1.0)
+ rx->set_sql_level(dbl_val);
+
+ // AGC settings
+ int_val = m_settings->value("agc_target_level", 0).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_agc_target_level(int_val);
+
+ //TODO: store/restore the preset correctly
+ int_val = m_settings->value("agc_decay", 500).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_agc_decay(int_val);
+
+ int_val = m_settings->value("agc_attack", 20).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_agc_attack(int_val);
+
+ int_val = m_settings->value("agc_hang", 0).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_agc_hang(int_val);
+
+ int_val = m_settings->value("agc_panning", 0).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_agc_panning(int_val);
+
+ if (m_settings->value("agc_panning_auto", false).toBool())
+ rx->set_agc_panning_auto(true);
+ else
+ rx->set_agc_panning_auto(false);
+
+ int_val = m_settings->value("agc_maxgain", 100).toInt(&conv_ok);
+ if (conv_ok)
+ rx->set_agc_max_gain(int_val);
+
+ if (m_settings->value("agc_off", false).toBool())
+ rx->set_agc_on(false);
+ else
+ rx->set_agc_on(true);
+
+ bool flo_ok = false;
+ bool fhi_ok = false;
+ int flo = m_settings->value("filter_low_cut", 0).toInt(&flo_ok);
+ int fhi = m_settings->value("filter_high_cut", 0).toInt(&fhi_ok);
+ int_val = m_settings->value("filter_shape", Modulations::FILTER_SHAPE_NORMAL).toInt(&conv_ok);
+
+ if (flo != fhi)
+ rx->set_filter(flo, fhi, receiver::filter_shape(int_val));
+
+ if (ver < 4)
+ {
+ m_settings->endGroup();
+ m_settings->beginGroup("audio");
+ }
+ int_val = m_settings->value("gain", QVariant(-60)).toInt(&conv_ok);
+ if (conv_ok)
+ if (!rx->get_agc_on())
+ rx->set_agc_manual_gain(int_val);
+
+ QString rec_dir = m_settings->value("rec_dir", QDir::homePath()).toString();
+ rx->set_audio_rec_dir(rec_dir.toStdString());
+
+ bool squelch_triggered = m_settings->value("squelch_triggered_recording", false).toBool();
+ rx->set_audio_rec_sql_triggered(squelch_triggered);
+
+ int_val = m_settings->value("rec_min_time", 0).toInt(&conv_ok);
+ if (!conv_ok)
+ int_val = 0;
+ rx->set_audio_rec_min_time(int_val);
+
+ int_val = m_settings->value("rec_max_gap", 0).toInt(&conv_ok);
+ if (!conv_ok)
+ int_val = 0;
+ rx->set_audio_rec_max_gap(int_val);
+
+ m_settings->endGroup();
+ ui->plotter->addVfo(rx->get_current_vfo());
+ i++;
+ if (ver < 4)
+ break;
+ grp = QString("rx%1").arg(i);
+ if (!m_settings->contains(grp + "/offset"))
+ break;
+ rx->add_rx();
+ }
+ if (ver >= 4)
+ int_val = m_settings->value("gui/current_rx", 0).toInt(&conv_ok);
+ else
+ conv_ok = false;
+ if (!conv_ok)
+ int_val = 0;
+ rxSpinBox->setMaximum(rx->get_rx_count() - 1);
+ ui->plotter->removeVfo(rx->get_vfo(int_val));
+ rx->select_rx(int_val);
+ ui->plotter->setCurrentVfo(int_val);
+ if (rxSpinBox->value() != int_val)
+ rxSpinBox->setValue(int_val);
+ loadRxToGUI();
+}
+
/**
* @brief Update hardware RF frequency range.
* @param ignore_limits Whether ignore the hardware specd and allow DC-to-light
@@ -862,19 +1185,152 @@ void MainWindow::updateGainStages(bool read_from_device)
*/
void MainWindow::setNewFrequency(qint64 rx_freq)
{
- auto hw_freq = (double)(rx_freq - d_lnb_lo) - rx->get_filter_offset();
- auto center_freq = rx_freq - (qint64)rx->get_filter_offset();
-
- d_hw_freq = (qint64)hw_freq;
+ auto new_offset = rx->get_filter_offset();
+ auto hw_freq = (double)(rx_freq - d_lnb_lo) - new_offset;
+ auto center_freq = rx_freq - (qint64)new_offset;
+ auto delta_freq = d_hw_freq;
+ QList bml;
// set receiver frequency
rx->set_rf_freq(hw_freq);
+ d_hw_freq = d_ignore_limits ? hw_freq : (qint64)rx->get_rf_freq();
+ if (rx->is_playing_iq() || (d_hw_freq != (qint64)hw_freq))
+ {
+ new_offset = rx_freq - d_lnb_lo - d_hw_freq;
+ if (d_hw_freq != (qint64)hw_freq)
+ {
+ center_freq = d_hw_freq + d_lnb_lo;
+ // set RX filter
+ rx->set_filter_offset((double)new_offset);
+
+ // update RF freq label and channel filter offset
+ rx_freq = center_freq + new_offset;
+ }
+ }
+ delta_freq -= d_hw_freq;
// update widgets
ui->plotter->setCenterFreq(center_freq);
uiDockRxOpt->setHwFreq(d_hw_freq);
ui->freqCtrl->setFrequency(rx_freq);
uiDockBookmarks->setNewFrequency(rx_freq);
+ remote->setNewFrequency(rx_freq);
+ uiDockAudio->setRxFrequency(rx_freq);
+ if (rx->is_rds_decoder_active())
+ rx->reset_rds_parser();
+ if (delta_freq)
+ {
+ std::set del_list;
+ if (rx->get_rx_count() > 1)
+ {
+ std::vector locked_vfos;
+ int offset_lim = (int)(ui->plotter->getSampleRate() / 2);
+ ui->plotter->getLockedVfos(locked_vfos);
+ for (auto& cvfo : locked_vfos)
+ {
+ ui->plotter->removeVfo(cvfo);
+ int new_offset = cvfo->get_offset() + delta_freq;
+ if ((new_offset > offset_lim) || (new_offset < -offset_lim))
+ del_list.insert(cvfo->get_index());
+ else
+ {
+ rx->set_filter_offset(cvfo->get_index(), new_offset);
+ ui->plotter->addVfo(cvfo);
+ }
+ }
+ }
+
+ if (d_auto_bookmarks)
+ {
+ //calculate frequency range to search for auto bookmarks
+ qint64 from = 0, to = 0;
+ qint64 sr = ui->plotter->getSampleRate();
+ if (delta_freq > 0)
+ {
+ if (delta_freq > sr)
+ {
+ from = center_freq - sr / 2;
+ to = center_freq + sr / 2;
+ }
+ else
+ {
+ from = center_freq - sr / 2;
+ to = center_freq - sr / 2 + delta_freq;
+ }
+ }
+ else
+ {
+ if (-delta_freq > sr)
+ {
+ from = center_freq - sr / 2;
+ to = center_freq + sr / 2;
+ }
+ else
+ {
+ from = center_freq + sr / 2 + delta_freq;
+ to = center_freq + sr / 2;
+ }
+ }
+ bml = Bookmarks::Get().getBookmarksInRange(from, to, true);
+ }
+
+ if ((del_list.size() > 0)||(bml.size() > 0))
+ {
+ int current = rx->get_current();
+ if (ui->actionDSP->isChecked())
+ rx->stop();
+ for (auto& bm : bml)
+ {
+ int n = rx->add_rx();
+ if (n > 0)
+ {
+ rxSpinBox->setMaximum(rx->get_rx_count() - 1);
+ rx->set_demod(bm.get_demod());
+ // preserve squelch level, force locked state
+ auto old_vfo = rx->get_current_vfo();
+ auto old_sql = old_vfo->get_sql_level();
+ old_vfo->restore_settings(bm, false);
+ old_vfo->set_sql_level(old_sql);
+ old_vfo->set_offset(bm.frequency - center_freq);
+ old_vfo->set_freq_lock(true);
+ ui->plotter->addVfo(old_vfo);
+ rx->select_rx(current);
+ }
+ }
+ if (del_list.size() > 0)
+ {
+ int lastCurrent = rx->get_current();
+ for (auto i = del_list.rbegin(); i != del_list.rend(); ++i)
+ {
+ int last = rx->get_rx_count() - 1;
+ rx->select_rx(*i);
+ if (lastCurrent == last)
+ {
+ lastCurrent = *i;
+ last = -1;
+ }
+ else
+ if (*i != last)
+ {
+ ui->plotter->removeVfo(rx->get_vfo(last));
+ last = *i;
+ }
+ else
+ last = -1;
+ rx->delete_rx();
+ if (last != -1)
+ ui->plotter->addVfo(rx->get_vfo(last));
+ }
+ rx->select_rx(lastCurrent);
+ ui->plotter->setCurrentVfo(lastCurrent);
+ rxSpinBox->setMaximum(rx->get_rx_count() - 1);
+ rxSpinBox->setValue(lastCurrent);
+ }
+ if (ui->actionDSP->isChecked())
+ rx->start();
+ ui->plotter->updateOverlay();
+ }
+ }
}
/**
@@ -1037,15 +1493,21 @@ void MainWindow::setInvertScrolling(bool enabled)
uiDockAudio->setInvertScrolling(enabled);
}
+/** Invert scroll wheel direction */
+void MainWindow::setAutoBookmarks(bool enabled)
+{
+ d_auto_bookmarks = enabled;
+}
+
/**
* @brief Select new demodulator.
* @param demod New demodulator.
*/
void MainWindow::selectDemod(const QString& strModulation)
{
- int iDemodIndex;
+ Modulations::idx iDemodIndex;
- iDemodIndex = DockRxOpt::GetEnumForModulationString(strModulation);
+ iDemodIndex = Modulations::GetEnumForModulationString(strModulation);
qDebug() << "selectDemod(str):" << strModulation << "-> IDX:" << iDemodIndex;
return selectDemod(iDemodIndex);
@@ -1059,134 +1521,177 @@ void MainWindow::selectDemod(const QString& strModulation)
* and configures the default channel filter.
*
*/
-void MainWindow::selectDemod(int mode_idx)
+void MainWindow::selectDemod(Modulations::idx mode_idx)
{
- double cwofs = 0.0;
int filter_preset = uiDockRxOpt->currentFilter();
- int flo=0, fhi=0, click_res=100;
+ int flo=0, fhi=0;
+ Modulations::filter_shape filter_shape;
bool rds_enabled;
// validate mode_idx
- if (mode_idx < DockRxOpt::MODE_OFF || mode_idx >= DockRxOpt::MODE_LAST)
+ if (mode_idx < Modulations::MODE_OFF || mode_idx >= Modulations::MODE_LAST)
{
qDebug() << "Invalid mode index:" << mode_idx;
- mode_idx = DockRxOpt::MODE_OFF;
+ mode_idx = Modulations::MODE_OFF;
}
qDebug() << "New mode index:" << mode_idx;
- uiDockRxOpt->getFilterPreset(mode_idx, filter_preset, &flo, &fhi);
d_filter_shape = (receiver::filter_shape)uiDockRxOpt->currentFilterShape();
+ rx->get_filter(flo, fhi, filter_shape);
+ if (filter_preset == FILTER_PRESET_USER)
+ {
+ if (((rx->get_demod() == Modulations::MODE_USB) &&
+ (mode_idx == Modulations::MODE_LSB))
+ ||
+ ((rx->get_demod() == Modulations::MODE_LSB) &&
+ (mode_idx == Modulations::MODE_USB)))
+ {
+ std::swap(flo, fhi);
+ flo = -flo;
+ fhi = -fhi;
+ filter_preset = FILTER_PRESET_USER;
+ }
+ Modulations::UpdateFilterRange(mode_idx, flo, fhi);
+ }
+ if (filter_preset != FILTER_PRESET_USER)
+ {
+ Modulations::GetFilterPreset(mode_idx, filter_preset, flo, fhi);
+ }
- rds_enabled = rx->is_rds_decoder_active();
- if (rds_enabled)
- setRdsDecoder(false);
- uiDockRDS->setDisabled();
+ if (mode_idx != rx->get_demod())
+ {
+ rds_enabled = rx->is_rds_decoder_active();
+ if (rds_enabled)
+ setRdsDecoder(false);
+ uiDockRDS->setDisabled();
+
+ if ((mode_idx >=Modulations::MODE_OFF) && (mode_idx set_demod(mode_idx);
+
+ switch (mode_idx) {
+ case Modulations::MODE_OFF:
+ /* Spectrum analyzer only */
+ if (rx->is_recording_audio())
+ {
+ stopAudioRec();
+ uiDockAudio->setAudioRecButtonState(false);
+ }
+ break;
+ case Modulations::MODE_AM:
+ case Modulations::MODE_AM_SYNC:
+ case Modulations::MODE_USB:
+ case Modulations::MODE_LSB:
+ case Modulations::MODE_CWL:
+ case Modulations::MODE_CWU:
+ break;
+
+ case Modulations::MODE_NFM:
+ rx->set_fm_maxdev(uiDockRxOpt->currentMaxdev());
+ rx->set_fm_deemph(uiDockRxOpt->currentEmph());
+ break;
+
+ case Modulations::MODE_WFM_MONO:
+ case Modulations::MODE_WFM_STEREO:
+ case Modulations::MODE_WFM_STEREO_OIRT:
+ /* Broadcast FM */
+ uiDockRDS->setEnabled();
+ if (rds_enabled)
+ setRdsDecoder(true);
+ break;
+
+ default:
+ qDebug() << "Unsupported mode selection (can't happen!): " << mode_idx;
+ flo = -5000;
+ fhi = 5000;
+ break;
+ }
+ }
+ rx->set_filter(flo, fhi, d_filter_shape);
+ updateDemodGUIRanges();
+}
+
+/**
+ * @brief Update GUI after demodulator selection.
+ *
+ * Update plotter demod ranges
+ * Update audio dock fft range
+ * Update plotter cut frequencies
+ * Update plotter click resolution
+ * Update plotter filter click resolution
+ * Update remote settings too
+ *
+ */
+void MainWindow::updateDemodGUIRanges()
+{
+ int click_res=100;
+ int flo=0, fhi=0, loMin, loMax, hiMin,hiMax;
+ Modulations::filter_shape filter_shape;
+ rx->get_filter(flo, fhi, filter_shape);
+ Modulations::idx mode_idx = rx->get_demod();
+ Modulations::GetFilterRanges(mode_idx, loMin, loMax, hiMin, hiMax);
+ ui->plotter->setDemodRanges(loMin, loMax, hiMin, hiMax, hiMax == -loMin);
switch (mode_idx) {
- case DockRxOpt::MODE_OFF:
+ case Modulations::MODE_OFF:
/* Spectrum analyzer only */
- if (rx->is_recording_audio())
- {
- stopAudioRec();
- uiDockAudio->setAudioRecButtonState(false);
- }
- if (dec_afsk1200 != nullptr)
- {
- dec_afsk1200->close();
- }
- rx->set_demod(receiver::RX_DEMOD_OFF);
click_res = 1000;
break;
- case DockRxOpt::MODE_RAW:
+ case Modulations::MODE_RAW:
/* Raw I/Q; max 96 ksps*/
- rx->set_demod(receiver::RX_DEMOD_NONE);
- ui->plotter->setDemodRanges(-40000, -200, 200, 40000, true);
uiDockAudio->setFftRange(0,24000);
click_res = 100;
break;
- case DockRxOpt::MODE_AM:
- rx->set_demod(receiver::RX_DEMOD_AM);
- ui->plotter->setDemodRanges(-40000, -200, 200, 40000, true);
+ case Modulations::MODE_AM:
uiDockAudio->setFftRange(0,6000);
click_res = 100;
break;
- case DockRxOpt::MODE_AM_SYNC:
- rx->set_demod(receiver::RX_DEMOD_AMSYNC);
- ui->plotter->setDemodRanges(-40000, -200, 200, 40000, true);
+ case Modulations::MODE_AM_SYNC:
uiDockAudio->setFftRange(0,6000);
click_res = 100;
break;
- case DockRxOpt::MODE_NFM:
- ui->plotter->setDemodRanges(-40000, -1000, 1000, 40000, true);
+ case Modulations::MODE_NFM:
uiDockAudio->setFftRange(0, 5000);
- rx->set_demod(receiver::RX_DEMOD_NFM);
- rx->set_fm_maxdev(uiDockRxOpt->currentMaxdev());
- rx->set_fm_deemph(uiDockRxOpt->currentEmph());
click_res = 100;
break;
- case DockRxOpt::MODE_WFM_MONO:
- case DockRxOpt::MODE_WFM_STEREO:
- case DockRxOpt::MODE_WFM_STEREO_OIRT:
+ case Modulations::MODE_WFM_MONO:
+ case Modulations::MODE_WFM_STEREO:
+ case Modulations::MODE_WFM_STEREO_OIRT:
/* Broadcast FM */
- ui->plotter->setDemodRanges(-120e3, -10000, 10000, 120e3, true);
uiDockAudio->setFftRange(0,24000); /** FIXME: get audio rate from rx **/
click_res = 1000;
- if (mode_idx == DockRxOpt::MODE_WFM_MONO)
- rx->set_demod(receiver::RX_DEMOD_WFM_M);
- else if (mode_idx == DockRxOpt::MODE_WFM_STEREO_OIRT)
- rx->set_demod(receiver::RX_DEMOD_WFM_S_OIRT);
- else
- rx->set_demod(receiver::RX_DEMOD_WFM_S);
-
- uiDockRDS->setEnabled();
- if (rds_enabled)
- setRdsDecoder(true);
break;
- case DockRxOpt::MODE_LSB:
+ case Modulations::MODE_LSB:
/* LSB */
- rx->set_demod(receiver::RX_DEMOD_SSB);
- ui->plotter->setDemodRanges(-40000, -100, -5000, 0, false);
uiDockAudio->setFftRange(0,3000);
click_res = 100;
break;
- case DockRxOpt::MODE_USB:
+ case Modulations::MODE_USB:
/* USB */
- rx->set_demod(receiver::RX_DEMOD_SSB);
- ui->plotter->setDemodRanges(0, 5000, 100, 40000, false);
uiDockAudio->setFftRange(0,3000);
click_res = 100;
break;
- case DockRxOpt::MODE_CWL:
+ case Modulations::MODE_CWL:
/* CW-L */
- rx->set_demod(receiver::RX_DEMOD_SSB);
- cwofs = -uiDockRxOpt->getCwOffset();
- ui->plotter->setDemodRanges(-5000, -100, 100, 5000, true);
uiDockAudio->setFftRange(0,1500);
click_res = 10;
break;
- case DockRxOpt::MODE_CWU:
+ case Modulations::MODE_CWU:
/* CW-U */
- rx->set_demod(receiver::RX_DEMOD_SSB);
- cwofs = uiDockRxOpt->getCwOffset();
- ui->plotter->setDemodRanges(-5000, -100, 100, 5000, true);
uiDockAudio->setFftRange(0,1500);
click_res = 10;
break;
default:
- qDebug() << "Unsupported mode selection (can't happen!): " << mode_idx;
- flo = -5000;
- fhi = 5000;
click_res = 100;
break;
}
@@ -1195,23 +1700,12 @@ void MainWindow::selectDemod(int mode_idx)
ui->plotter->setHiLowCutFrequencies(flo, fhi);
ui->plotter->setClickResolution(click_res);
ui->plotter->setFilterClickResolution(click_res);
- rx->set_filter((double)flo, (double)fhi, d_filter_shape);
- rx->set_cw_offset(cwofs);
- rx->set_sql_level(uiDockRxOpt->currentSquelchLevel());
-
- //Call wrapper to update enable/disabled state
- setAgcOn(uiDockRxOpt->getAgcOn());
- rx->set_agc_target_level(uiDockRxOpt->getAgcTargetLevel());
- rx->set_agc_manual_gain(uiDockAudio->audioGain() / 10.0);
- rx->set_agc_max_gain(uiDockRxOpt->getAgcMaxGain());
- rx->set_agc_attack(uiDockRxOpt->getAgcAttack());
- rx->set_agc_decay(uiDockRxOpt->getAgcDecay());
- rx->set_agc_hang(uiDockRxOpt->getAgcHang());
+ uiDockRxOpt->setFilterParam(flo, fhi);
remote->setMode(mode_idx);
remote->setPassband(flo, fhi);
- d_have_audio = (mode_idx != DockRxOpt::MODE_OFF);
+ d_have_audio = (mode_idx != Modulations::MODE_OFF);
uiDockRxOpt->setCurrentDemod(mode_idx);
}
@@ -1339,6 +1833,18 @@ void MainWindow::setAgcDecay(int msec)
rx->set_agc_decay(msec);
}
+/** AGC panning changed. */
+void MainWindow::setAgcPanning(int panning)
+{
+ rx->set_agc_panning(panning);
+}
+
+/** AGC panning auto changed. */
+void MainWindow::setAgcPanningAuto(bool panningAuto)
+{
+ rx->set_agc_panning_auto(panningAuto);
+}
+
/**
* @brief Noise blanker configuration changed.
* @param nb1 Noise blanker 1 ON/OFF.
@@ -1368,8 +1874,10 @@ void MainWindow::setSqlLevel(double level_db)
* @brief Squelch level auto clicked.
* @return The new squelch level.
*/
-double MainWindow::setSqlLevelAuto()
+double MainWindow::setSqlLevelAuto(bool global)
{
+ if (global)
+ rx->set_sql_level(3.0, true, true);
double level = rx->get_signal_pwr() + 3.0;
if (level > -10.0) // avoid 0 dBFS
level = uiDockRxOpt->getSqlLevel();
@@ -1378,6 +1886,14 @@ double MainWindow::setSqlLevelAuto()
return level;
}
+/**
+ * @brief Squelch level reset all clicked.
+ */
+void MainWindow::resetSqlLevelGlobal()
+{
+ rx->set_sql_level(-150.0, true, false);
+}
+
/** Signal strength meter timeout. */
void MainWindow::meterTimeout()
{
@@ -1558,11 +2074,13 @@ void MainWindow::stopAudioRec()
/** Audio recording is started or stopped. */
void MainWindow::audioRecEvent(const QString filename, bool is_running)
{
- if(is_running)
+ if (is_running)
{
ui->statusBar->showMessage(tr("Recording audio to %1").arg(filename));
uiDockAudio->audioRecStarted(QString(filename));
- }else{
+ }
+ else
+ {
/* reset state of record button */
uiDockAudio->audioRecStopped();
ui->statusBar->showMessage(tr("Audio recorder stopped"), 5000);
@@ -1602,6 +2120,18 @@ void MainWindow::stopAudioPlayback()
}
}
+void MainWindow::copyRecSettingsToAllVFOs()
+{
+ std::vector vfos = rx->get_vfos();
+ for (auto& cvfo : vfos)
+ if (cvfo->get_index() != rx->get_current())
+ {
+ cvfo->set_audio_rec_dir(rx->get_audio_rec_dir());
+ cvfo->set_audio_rec_min_time(rx->get_audio_rec_min_time());
+ cvfo->set_audio_rec_max_gap(rx->get_audio_rec_max_gap());
+ }
+}
+
/** Start streaming audio over UDP. */
void MainWindow::startAudioStream(const QString& udp_host, int udp_port, bool stereo)
{
@@ -1744,7 +2274,7 @@ void MainWindow::stopIqPlayback()
qint64 oldOffset = m_settings->value("receiver/offset", 0).toLongLong(&offsetOK);
if (centerOK && offsetOK)
{
- on_plotter_newDemodFreq(oldCenter, oldOffset);
+ on_plotter_newDemodFreq(oldCenter + oldOffset, oldOffset);
}
if (ui->actionDSP->isChecked())
@@ -2078,6 +2608,42 @@ void MainWindow::on_plotter_newDemodFreq(qint64 freq, qint64 delta)
rx->reset_rds_parser();
}
+/* CPlotter::NewDemodFreqLoad() is emitted */
+/* tune and load demodulator settings */
+void MainWindow::on_plotter_newDemodFreqLoad(qint64 freq, qint64 delta)
+{
+ // set RX filter
+ if (delta != qint64(rx->get_filter_offset()))
+ {
+ rx->set_filter_offset((double) delta);
+ updateFrequencyRange();
+ }
+
+ QList tags =
+ Bookmarks::Get().getBookmarksInRange(freq, freq);
+ if (tags.size() > 0)
+ {
+ onBookmarkActivated(tags.first());
+ }
+ else
+ setNewFrequency(freq);
+}
+
+/* CPlotter::NewDemodFreqLoad() is emitted */
+/* new demodulator here */
+void MainWindow::on_plotter_newDemodFreqAdd(qint64 freq, qint64 delta)
+{
+ vfo::sptr found = rx->find_vfo(freq - d_lnb_lo);
+ if (!found)
+ on_actionAddDemodulator_triggered();
+ else
+ {
+ rxSpinBox->setValue(found->get_index());
+ rxSpinBox_valueChanged(found->get_index());
+ }
+ on_plotter_newDemodFreqLoad(freq, delta);
+}
+
/* CPlotter::NewfilterFreq() is emitted or bookmark activated */
void MainWindow::on_plotter_newFilterFreq(int low, int high)
{ /* parameter correctness will be checked in receiver class */
@@ -2238,33 +2804,28 @@ void MainWindow::setRdsDecoder(bool checked)
remote->setRDSstatus(checked);
}
-void MainWindow::onBookmarkActivated(qint64 freq, const QString& demod, int bandwidth)
+void MainWindow::onBookmarkActivated(BookmarkInfo & bm)
{
- setNewFrequency(freq);
- selectDemod(demod);
-
- /* Check if filter is symmetric or not by checking the presets */
- auto mode = uiDockRxOpt->currentDemod();
- auto preset = uiDockRxOpt->currentFilter();
-
- int lo, hi;
- uiDockRxOpt->getFilterPreset(mode, preset, &lo, &hi);
+ setNewFrequency(bm.frequency);
+ selectDemod(bm.get_demod());
+ // preserve offset, squelch level, force locked state
+ auto old_vfo = rx->get_current_vfo();
+ auto old_offset = old_vfo->get_offset();
+ auto old_sql = old_vfo->get_sql_level();
+ rx->get_current_vfo()->restore_settings(bm, false);
+ old_vfo->set_sql_level(old_sql);
+ old_vfo->set_offset(old_offset);
+ old_vfo->set_freq_lock(true);
+ loadRxToGUI();
+}
- if(lo + hi == 0)
- {
- lo = -bandwidth / 2;
- hi = bandwidth / 2;
- }
- else if(lo >= 0 && hi >= 0)
- {
- hi = lo + bandwidth;
- }
- else if(lo <= 0 && hi <= 0)
+void MainWindow::onBookmarkActivatedAddDemod(BookmarkInfo & bm)
+{
+ if (!rx->find_vfo(bm.frequency - d_lnb_lo))
{
- lo = hi - bandwidth;
+ on_actionAddDemodulator_triggered();
+ onBookmarkActivated(bm);
}
-
- on_plotter_newFilterFreq(lo, hi);
}
void MainWindow::setPassband(int bandwidth)
@@ -2274,18 +2835,18 @@ void MainWindow::setPassband(int bandwidth)
auto preset = uiDockRxOpt->currentFilter();
int lo, hi;
- uiDockRxOpt->getFilterPreset(mode, preset, &lo, &hi);
+ Modulations::GetFilterPreset(mode, preset, lo, hi);
- if(lo + hi == 0)
+ if (lo + hi == 0)
{
lo = -bandwidth / 2;
hi = bandwidth / 2;
}
- else if(lo >= 0 && hi >= 0)
+ else if (lo >= 0 && hi >= 0)
{
hi = lo + bandwidth;
}
- else if(lo <= 0 && hi <= 0)
+ else if (lo <= 0 && hi <= 0)
{
lo = hi - bandwidth;
}
@@ -2295,6 +2856,11 @@ void MainWindow::setPassband(int bandwidth)
on_plotter_newFilterFreq(lo, hi);
}
+void MainWindow::setFreqLock(bool lock, bool all)
+{
+ rx->set_freq_lock(lock, all);
+}
+
/** Launch Gqrx google group website. */
void MainWindow::on_actionUserGroup_triggered()
{
@@ -2429,7 +2995,9 @@ void MainWindow::on_actionAddBookmark_triggered()
bool ok=false;
QString name;
QStringList tags;
+ const qint64 freq = ui->freqCtrl->getFrequency();
+ QList bookmarkFound = Bookmarks::Get().getBookmarksInRange(freq, freq);
// Create and show the Dialog for a new Bookmark.
// Write the result into variable 'name'.
{
@@ -2462,6 +3030,11 @@ void MainWindow::on_actionAddBookmark_triggered()
mainLayout->addWidget(taglist);
mainLayout->addWidget(buttonBox);
+ if (bookmarkFound.size())
+ {
+ textfield->setText(bookmarkFound.first().name);
+ taglist->setSelectedTags(bookmarkFound.first().tags);
+ }
ok = dialog.exec();
if (ok)
{
@@ -2477,13 +3050,13 @@ void MainWindow::on_actionAddBookmark_triggered()
}
// Add new Bookmark to Bookmarks.
- if(ok)
+ if (ok)
{
int i;
BookmarkInfo info;
+ info.restore_settings(*rx->get_current_vfo().get());
info.frequency = ui->freqCtrl->getFrequency();
- info.bandwidth = ui->plotter->getFilterBw();
info.modulation = uiDockRxOpt->currentDemodAsString();
info.name=name;
info.tags.clear();
@@ -2494,10 +3067,134 @@ void MainWindow::on_actionAddBookmark_triggered()
for (i = 0; i < tags.size(); ++i)
info.tags.append(&Bookmarks::Get().findOrAddTag(tags[i]));
+ //FIXME: implement Bookmarks::replace(&BookmarkInfo, &BookmarkInfo) method
+ if (bookmarkFound.size())
+ {
+ info.set_freq_lock(bookmarkFound.first().get_freq_lock());
+ Bookmarks::Get().remove(bookmarkFound.first());
+ }
+ else
+ info.set_freq_lock(false);
Bookmarks::Get().add(info);
uiDockBookmarks->updateTags();
- uiDockBookmarks->updateBookmarks();
- ui->plotter->updateOverlay();
+ }
+}
+
+void MainWindow::on_actionAddDemodulator_triggered()
+{
+ ui->plotter->addVfo(rx->get_current_vfo());
+ int n = rx->add_rx();
+ ui->plotter->setCurrentVfo(rx->get_rx_count() - 1);
+ rxSpinBox->setMaximum(rx->get_rx_count() - 1);
+ rxSpinBox->setValue(n);
+}
+
+void MainWindow::on_actionRemoveDemodulator_triggered()
+{
+ int old_current = rx->get_current();
+ if (old_current != rx->get_rx_count() - 1)
+ ui->plotter->removeVfo(rx->get_vfo(rx->get_rx_count() - 1));
+ int n = rx->delete_rx();
+ rxSpinBox->setValue(n);
+ rxSpinBox->setMaximum(rx->get_rx_count() - 1);
+ loadRxToGUI();
+ if (old_current != n)
+ ui->plotter->removeVfo(rx->get_vfo(n));
+ ui->plotter->setCurrentVfo(n);
+}
+
+void MainWindow::rxSpinBox_valueChanged(int i)
+{
+ if (i == rx->get_current())
+ return;
+ ui->plotter->addVfo(rx->get_current_vfo());
+ int n = rx->select_rx(i);
+ ui->plotter->removeVfo(rx->get_current_vfo());
+ ui->plotter->setCurrentVfo(i);
+ if (n == receiver::STATUS_OK)
+ loadRxToGUI();
+}
+
+void MainWindow::on_plotter_selectVfo(int i)
+{
+ rxSpinBox->setValue(i);
+}
+
+void MainWindow::loadRxToGUI()
+{
+ auto rf_freq = rx->get_rf_freq();
+ auto new_offset = rx->get_filter_offset();
+ auto rx_freq = (double)(rf_freq + d_lnb_lo + new_offset);
+
+ int low, high;
+ receiver::filter_shape fs;
+ auto mode_idx = rx->get_demod();
+
+ ui->plotter->setFilterOffset(new_offset);
+ uiDockRxOpt->setRxFreq(rx_freq);
+ uiDockRxOpt->setHwFreq(d_hw_freq);
+ uiDockRxOpt->setFilterOffset(new_offset);
+
+ ui->freqCtrl->setFrequency(rx_freq);
+ uiDockBookmarks->setNewFrequency(rx_freq);
+ remote->setNewFrequency(rx_freq);
+ uiDockAudio->setRxFrequency(rx_freq);
+
+ if (rx->is_rds_decoder_active())
+ rx->reset_rds_parser();
+
+ rx->get_filter(low, high, fs);
+ updateDemodGUIRanges();
+ uiDockRxOpt->setFreqLock(rx->get_freq_lock());
+ uiDockRxOpt->setCurrentFilterShape(fs);
+ uiDockRxOpt->setFilterParam(low, high);
+
+ uiDockRxOpt->setSquelchLevel(rx->get_sql_level());
+
+ uiDockRxOpt->setAgcOn(rx->get_agc_on());
+ uiDockAudio->setGainEnabled(!rx->get_agc_on());
+ uiDockRxOpt->setAgcTargetLevel(rx->get_agc_target_level());
+ uiDockRxOpt->setAgcMaxGain(rx->get_agc_max_gain());
+ uiDockRxOpt->setAgcAttack(rx->get_agc_attack());
+ uiDockRxOpt->setAgcDecay(rx->get_agc_decay());
+ uiDockRxOpt->setAgcHang(rx->get_agc_hang());
+ uiDockRxOpt->setAgcPanning(rx->get_agc_panning());
+ uiDockRxOpt->setAgcPanningAuto(rx->get_agc_panning_auto());
+ if (!rx->get_agc_on())
+ uiDockAudio->setAudioGain(rx->get_agc_manual_gain() * 10.0);
+
+ uiDockRxOpt->setAmDcr(rx->get_am_dcr());
+ uiDockRxOpt->setAmSyncDcr(rx->get_amsync_dcr());
+ uiDockRxOpt->setAmSyncPllBw(rx->get_amsync_pll_bw());
+ uiDockRxOpt->setFmMaxdev(rx->get_fm_maxdev());
+ uiDockRxOpt->setFmEmph(rx->get_fm_deemph());
+ uiDockRxOpt->setCwOffset(rx->get_cw_offset());
+
+ for (int k = 1; k < 3; k++)
+ uiDockRxOpt->setNoiseBlanker(k,rx->get_nb_on(k), rx->get_nb_threshold(k));
+
+ uiDockAudio->setRecDir(QString(rx->get_audio_rec_dir().data()));
+ uiDockAudio->setSquelchTriggered(rx->get_audio_rec_sql_triggered());
+ uiDockAudio->setRecMinTime(rx->get_audio_rec_min_time());
+ uiDockAudio->setRecMaxGap(rx->get_audio_rec_max_gap());
+
+ //FIXME Prevent playing incomplete audio or remove audio player
+ if (rx->is_recording_audio())
+ uiDockAudio->audioRecStarted(QString(rx->get_last_audio_filename().data()));
+ else
+ uiDockAudio->audioRecStopped();
+ d_have_audio = (mode_idx != Modulations::MODE_OFF);
+ switch (mode_idx)
+ {
+ case Modulations::MODE_WFM_MONO:
+ case Modulations::MODE_WFM_STEREO:
+ case Modulations::MODE_WFM_STEREO_OIRT:
+ uiDockRDS->setEnabled();
+ setRdsDecoder(rx->is_rds_decoder_active());
+ break;
+ default:
+ uiDockRDS->setDisabled();
+ setRdsDecoder(false);
}
}
diff --git a/src/applications/gqrx/mainwindow.h b/src/applications/gqrx/mainwindow.h
index d1a22c5c0b..7aac5c3a35 100644
--- a/src/applications/gqrx/mainwindow.h
+++ b/src/applications/gqrx/mainwindow.h
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include "qtgui/dockrxopt.h"
#include "qtgui/dockaudio.h"
@@ -64,6 +65,7 @@ class MainWindow : public QMainWindow
bool loadConfig(const QString& cfgfile, bool check_crash, bool restore_mainwindow);
bool saveConfig(const QString& cfgfile);
+ void readRXSettings(int ver);
void storeSession();
bool configOk; /*!< Main app uses this flag to know whether we should abort or continue. */
@@ -84,7 +86,10 @@ public slots:
qint64 d_hw_freq_start{};
qint64 d_hw_freq_stop{};
- enum receiver::filter_shape d_filter_shape;
+ bool d_ignore_limits;
+ bool d_auto_bookmarks;
+
+ Modulations::filter_shape d_filter_shape;
std::complex* d_fftData;
float *d_realFftData;
float *d_iirFftData;
@@ -120,6 +125,7 @@ public slots:
std::map devList;
+ QSpinBox *rxSpinBox;
// dummy widget to enforce linking to QtSvg
QSvgWidget *qsvg_dummy;
@@ -133,6 +139,7 @@ public slots:
void frequencyFocusShortcut();
void audioRecEventEmitter(std::string filename, bool is_running);
static void audio_rec_event(MainWindow *self, std::string filename, bool is_running);
+ void loadRxToGUI();
private slots:
/* RecentConfig */
@@ -153,8 +160,10 @@ private slots:
void setIgnoreLimits(bool ignore_limits);
void setFreqCtrlReset(bool enabled);
void setInvertScrolling(bool enabled);
+ void setAutoBookmarks(bool enabled);
void selectDemod(const QString& demod);
- void selectDemod(int index);
+ void selectDemod(Modulations::idx index);
+ void updateDemodGUIRanges();
void setFmMaxdev(float max_dev);
void setFmEmph(double tau);
void setAmDcr(bool enabled);
@@ -168,12 +177,16 @@ private slots:
void setAgcDecay(int msec);
void setAgcGain(int gain);
void setAgcMaxGain(int gain);
+ void setAgcPanning(int panning);
+ void setAgcPanningAuto(bool panningAuto);
void setNoiseBlanker(int nbid, bool on, float threshold);
void setSqlLevel(double level_db);
- double setSqlLevelAuto();
+ double setSqlLevelAuto(bool global);
+ void resetSqlLevelGlobal();
void setAudioGain(float gain);
void setAudioMute(bool mute);
void setPassband(int bandwidth);
+ void setFreqLock(bool lock, bool all);
/* audio recording and playback */
void recDirChanged(const QString dir);
@@ -185,6 +198,7 @@ private slots:
void audioRecEvent(const QString filename, bool is_running);
void startAudioPlayback(const QString& filename);
void stopAudioPlayback();
+ void copyRecSettingsToAllVFOs();
void startAudioStream(const QString& udp_host, int udp_port, bool stereo);
void stopAudioStreaming();
@@ -211,14 +225,18 @@ private slots:
void setWfSize();
/* FFT plot */
- void on_plotter_newDemodFreq(qint64 freq, qint64 delta); /*! New demod freq (aka. filter offset). */
+ void on_plotter_newDemodFreq(qint64 freq, qint64 delta); /*! New demod freq (aka. filter offset). */
+ void on_plotter_newDemodFreqLoad(qint64 freq, qint64 delta);/* tune and load demodulator settings */
+ void on_plotter_newDemodFreqAdd(qint64 freq, qint64 delta); /* new demodulator here */
void on_plotter_newFilterFreq(int low, int high); /*! New filter width */
+ void on_plotter_selectVfo(int i);
/* RDS */
void setRdsDecoder(bool checked);
/* Bookmarks */
- void onBookmarkActivated(qint64 freq, const QString& demod, int bandwidth);
+ void onBookmarkActivated(BookmarkInfo & bm);
+ void onBookmarkActivatedAddDemod(BookmarkInfo & bm);
/* DXC Spots */
void updateClusterSpots();
@@ -242,6 +260,9 @@ private slots:
void on_actionAboutQt_triggered();
void on_actionAddBookmark_triggered();
void on_actionDX_Cluster_triggered();
+ void on_actionAddDemodulator_triggered();
+ void on_actionRemoveDemodulator_triggered();
+ void rxSpinBox_valueChanged(int i);
/* window close signals */
diff --git a/src/applications/gqrx/mainwindow.ui b/src/applications/gqrx/mainwindow.ui
index bc4b09cdda..9c80f27099 100644
--- a/src/applications/gqrx/mainwindow.ui
+++ b/src/applications/gqrx/mainwindow.ui
@@ -195,7 +195,7 @@
0
0
521
- 24
+ 23
@@ -575,6 +577,36 @@
Ctrl+C
+
+
+
+ :/icons/icons/add.svg:/icons/icons/add.svg
+
+
+ Add demodulator
+
+
+ Add demodulator
+
+
+ Ins
+
+
+
+
+
+ :/icons/icons/remove.svg:/icons/icons/remove.svg
+
+
+ Remove demodulator
+
+
+ Remove current selected demodulator
+
+
+ Alt+Del
+
+
diff --git a/src/applications/gqrx/receiver.cpp b/src/applications/gqrx/receiver.cpp
index 3b34c109a7..ad552c731d 100644
--- a/src/applications/gqrx/receiver.cpp
+++ b/src/applications/gqrx/receiver.cpp
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
@@ -46,7 +47,6 @@
#include
#endif
-#define TARGET_QUAD_RATE 1e6
/**
* @brief Public constructor.
@@ -57,21 +57,19 @@
receiver::receiver(const std::string input_device,
const std::string audio_device,
unsigned int decimation)
- : d_running(false),
+ : d_current(-1),
+ d_active(0),
+ d_running(false),
d_input_rate(96000.0),
d_audio_rate(48000),
d_decim(decimation),
d_rf_freq(144800000.0),
- d_filter_offset(0.0),
- d_cw_offset(0.0),
d_recording_iq(false),
- d_recording_wav(false),
d_sniffer_active(false),
d_iq_rev(false),
d_dc_cancel(false),
d_iq_balance(false),
- d_mute(false),
- d_demod(RX_DEMOD_OFF)
+ d_mute(false)
{
tb = gr::make_top_block("gqrx");
@@ -108,12 +106,18 @@ receiver::receiver(const std::string input_device,
d_decim_rate = d_input_rate;
}
- d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
- d_quad_rate = d_decim_rate / d_ddc_decim;
- ddc = make_downconverter_cc(d_ddc_decim, 0.0, d_decim_rate);
- rx = make_nbrx(d_quad_rate, d_audio_rate);
+ rx.reserve(RX_MAX);
+ rx.clear();
+ d_current = 0;
+ rx.push_back(make_nbrx(d_decim_rate, d_audio_rate));
+ rx[d_current]->set_index(d_current);
+ rx[d_current]->set_rec_event_handler(std::bind(audio_rec_event, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
iq_swap = make_iq_swap_cc(false);
+ iq_src = iq_swap;
dc_corr = make_dc_corr_cc(d_decim_rate, 1.0);
iq_fft = make_rx_fft_c(8192u, d_decim_rate, gr::fft::window::WIN_HANN);
@@ -121,6 +125,12 @@ receiver::receiver(const std::string input_device,
audio_udp_sink = make_udp_sink_f();
+ add0 = gr::blocks::add_ff::make(1);
+ add1 = gr::blocks::add_ff::make(1);
+ mc0 = gr::blocks::multiply_const_ff::make(1.0, 1);
+ mc1 = gr::blocks::multiply_const_ff::make(1.0, 1);
+ null_src = gr::blocks::null_source::make(sizeof(float));
+
#ifdef WITH_PULSEAUDIO
audio_snk = make_pa_sink(audio_device, d_audio_rate, "GQRX", "Audio output");
#elif WITH_PORTAUDIO
@@ -137,14 +147,13 @@ receiver::receiver(const std::string input_device,
sniffer = make_sniffer_f();
/* sniffer_rr is created at each activation. */
- set_demod(RX_DEMOD_NFM);
+ set_demod(Modulations::MODE_OFF);
+ reconnect_all();
gr::prefs pref;
qDebug() << "Using audio backend:"
<< pref.get_string("audio", "audio_module", "N/A").c_str();
- rx->set_rec_event_handler(std::bind(audio_rec_event, this,
- std::placeholders::_1,
- std::placeholders::_2));
+
}
receiver::~receiver()
@@ -198,25 +207,20 @@ void receiver::set_input_device(const std::string device)
tb->wait();
}
- if (d_decim >= 2)
- {
- tb->disconnect(src, 0, input_decim, 0);
- tb->disconnect(input_decim, 0, iq_swap, 0);
- }
- else
- {
- tb->disconnect(src, 0, iq_swap, 0);
- }
+ tb->disconnect_all();
+ for (auto& rxc : rx)
+ rxc->connected(false);
#if GNURADIO_VERSION < 0x030802
//Work around GNU Radio bug #3184
//temporarily connect dummy source to ensure that previous device is closed
src = osmosdr::source::make("file="+escape_filename(get_zero_file())+",freq=428e6,rate=96000,repeat=true,throttle=true");
tb->connect(src, 0, iq_swap, 0);
+ tb->connect(iq_swap, 0, iq_fft, 0);
tb->start();
tb->stop();
tb->wait();
- tb->disconnect(src, 0, iq_swap, 0);
+ tb->disconnect_all();
#else
src.reset();
#endif
@@ -230,20 +234,10 @@ void receiver::set_input_device(const std::string device)
error = x.what();
src = osmosdr::source::make("file="+escape_filename(get_zero_file())+",freq=428e6,rate=96000,repeat=true,throttle=true");
}
-
- if(src->get_sample_rate() != 0)
+ reconnect_all(true);
+ if (src->get_sample_rate() != 0)
set_input_rate(src->get_sample_rate());
- if (d_decim >= 2)
- {
- tb->connect(src, 0, input_decim, 0);
- tb->connect(input_decim, 0, iq_swap, 0);
- }
- else
- {
- tb->connect(src, 0, iq_swap, 0);
- }
-
if (d_running)
tb->start();
@@ -268,14 +262,11 @@ void receiver::set_output_device(const std::string device)
tb->lock();
- if (d_demod != RX_DEMOD_OFF)
+ if (d_active > 0)
{
- try
- {
+ try {
tb->disconnect(audio_snk);
- }
- catch(std::exception &x)
- {
+ } catch(std::exception &x) {
}
}
audio_snk.reset();
@@ -289,10 +280,10 @@ void receiver::set_output_device(const std::string device)
audio_snk = gr::audio::sink::make(d_audio_rate, device, true);
#endif
- if ((d_demod != RX_DEMOD_OFF) && !d_mute)
+ if (d_active > 0)
{
- tb->connect(rx, 0, audio_snk, 0);
- tb->connect(rx, 1, audio_snk, 1);
+ tb->connect(mc0, 0, audio_snk, 0);
+ tb->connect(mc1, 0, audio_snk, 1);
}
tb->unlock();
@@ -361,11 +352,9 @@ double receiver::set_input_rate(double rate)
}
d_decim_rate = d_input_rate / (double)d_decim;
- d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
- d_quad_rate = d_decim_rate / d_ddc_decim;
dc_corr->set_sample_rate(d_decim_rate);
- ddc->set_decim_and_samp_rate(d_ddc_decim, d_decim_rate);
- rx->set_quad_rate(d_quad_rate);
+ for (auto& rxc : rx)
+ rxc->set_quad_rate(d_decim_rate);
iq_fft->set_quad_rate(d_decim_rate);
tb->unlock();
@@ -418,11 +407,9 @@ unsigned int receiver::set_input_decim(unsigned int decim)
}
// update quadrature rate
- d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
- d_quad_rate = d_decim_rate / d_ddc_decim;
dc_corr->set_sample_rate(d_decim_rate);
- ddc->set_decim_and_samp_rate(d_ddc_decim, d_decim_rate);
- rx->set_quad_rate(d_quad_rate);
+ for (auto& rxc : rx)
+ rxc->set_quad_rate(d_decim_rate);
iq_fft->set_quad_rate(d_decim_rate);
if (d_decim >= 2)
@@ -495,7 +482,7 @@ void receiver::set_dc_cancel(bool enable)
// until we have a way to switch on/off
// inside the dc_corr_cc we do a reconf
- set_demod(d_demod, true);
+ reconnect_all(true);
}
/**
@@ -543,7 +530,8 @@ receiver::status receiver::set_rf_freq(double freq_hz)
d_rf_freq = freq_hz;
src->set_center_freq(d_rf_freq);
- rx->set_center_freq(d_rf_freq);//to generate audio filename
+ for (auto& rxc : rx)
+ rxc->set_center_freq(d_rf_freq);//to generate audio filename
// FIXME: read back frequency?
return STATUS_OK;
@@ -643,6 +631,156 @@ receiver::status receiver::set_auto_gain(bool automatic)
return STATUS_OK;
}
+/**
+ * @brief Add new demodulator and select it
+ * @return current rx index or -1 if an error occurs.
+ */
+int receiver::add_rx()
+{
+ if (rx.size() == RX_MAX)
+ return -1;
+ //tb->lock(); does not allow block input count changing
+ if (d_running)
+ {
+ tb->stop();
+ tb->wait();
+ }
+ if (d_current >= 0)
+ background_rx();
+ rx.push_back(make_nbrx(d_decim_rate, d_audio_rate));
+ int old = d_current;
+ d_current = rx.size() - 1;
+ rx[d_current]->set_index(d_current);
+ rx[d_current]->set_rec_event_handler(std::bind(audio_rec_event, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
+ set_demod_locked(rx[old]->get_demod(), old);
+ if (d_running)
+ tb->start();
+ return d_current;
+}
+
+/**
+ * @brief Get demodulator count
+ * @return demodulator count.
+ */
+int receiver::get_rx_count()
+{
+ return rx.size();
+}
+
+/**
+ * @brief Delete selected demodulator, select nearest remaining demodulator.
+ * @return selected rx index or -1 if there are 0 demodulators remaining.
+ */
+int receiver::delete_rx()
+{
+ if (d_current == -1)
+ return -1;
+ if (rx.size() <= 1)
+ return 0;
+ //tb->lock(); does not allow block input count changing
+ if (d_running)
+ {
+ tb->stop();
+ tb->wait();
+ }
+ background_rx();
+ disconnect_rx();
+ rx[d_current].reset();
+ if (rx.size() > 1)
+ {
+ if (d_current != int(rx.size()) - 1)
+ {
+ disconnect_rx(rx.back()->get_index());
+ rx[d_current] = rx.back();
+ rx[d_current]->set_index(d_current);
+ rx.back().reset();
+ connect_rx();
+ }
+ else
+ d_current--;
+ }
+ else
+ {
+ d_current = -1;
+ }
+ rx.pop_back();
+ foreground_rx();
+ if (d_running)
+ tb->start();
+ return d_current;
+}
+
+/**
+ * @brief Selects a demodulator.
+ * @return STATUS_OK or STATUS_ERROR (if requested demodulator does not exist).
+ */
+receiver::status receiver::select_rx(int no)
+{
+ if (no == d_current)
+ return STATUS_OK;
+ if (no < int(rx.size()))
+ {
+ tb->lock();
+ if (d_current >= 0)
+ background_rx();
+ d_current = no;
+ foreground_rx();
+ tb->unlock();
+ return STATUS_OK;
+ }
+ return STATUS_ERROR;
+}
+
+receiver::status receiver::fake_select_rx(int no)
+{
+ if (no == d_current)
+ return STATUS_OK;
+ if (no < int(rx.size()))
+ {
+ d_current = no;
+ return STATUS_OK;
+ }
+ return STATUS_ERROR;
+}
+
+int receiver::get_current()
+{
+ return d_current;
+}
+
+vfo::sptr receiver::get_current_vfo()
+{
+ return get_vfo(d_current);
+}
+
+vfo::sptr receiver::find_vfo(int64_t freq)
+{
+ vfo::sptr notfound;
+ int64_t offset = freq - d_rf_freq;
+ //FIXME: speedup with index???
+ for (auto& rxc : rx)
+ if (rxc->get_offset() == offset)
+ return rxc;
+ return notfound;
+}
+
+vfo::sptr receiver::get_vfo(int n)
+{
+ return rx[n];
+}
+
+std::vector receiver::get_vfos()
+{
+ std::vector vfos;
+ vfos.reserve(rx.size());
+ for (auto& rxc : rx)
+ vfos.push_back(rxc);
+ return vfos;
+}
+
/**
* @brief Set filter offset.
* @param offset_hz The desired filter offset in Hz.
@@ -659,9 +797,15 @@ receiver::status receiver::set_auto_gain(bool automatic)
*/
receiver::status receiver::set_filter_offset(double offset_hz)
{
- d_filter_offset = offset_hz;
- ddc->set_center_freq(d_filter_offset - d_cw_offset);
- rx->set_offset(offset_hz);//to generate audio filename from
+ set_filter_offset(d_current, offset_hz);
+ return STATUS_OK;
+}
+
+receiver::status receiver::set_filter_offset(int rx_index, double offset_hz)
+{
+ rx[rx_index]->set_offset(offset_hz);//to generate audio filename from
+ if (rx[rx_index]->get_agc_panning_auto())
+ rx[rx_index]->set_agc_panning(offset_hz * 200.0 / d_decim_rate);
return STATUS_OK;
}
@@ -673,50 +817,51 @@ receiver::status receiver::set_filter_offset(double offset_hz)
*/
double receiver::get_filter_offset(void) const
{
- return d_filter_offset;
+ return rx[d_current]->get_offset();
}
+void receiver::set_freq_lock(bool on, bool all)
+{
+ if (all)
+ for (auto& rxc : rx)
+ rxc->set_freq_lock(on);
+ else
+ rx[d_current]->set_freq_lock(on);
+}
+
+bool receiver::get_freq_lock()
+{
+ return rx[d_current]->get_freq_lock();
+}
+
+
/* CW offset can serve as a "BFO" if the GUI needs it */
receiver::status receiver::set_cw_offset(double offset_hz)
{
- d_cw_offset = offset_hz;
- ddc->set_center_freq(d_filter_offset - d_cw_offset);
- rx->set_cw_offset(d_cw_offset);
+ rx[d_current]->set_cw_offset(offset_hz);
return STATUS_OK;
}
double receiver::get_cw_offset(void) const
{
- return d_cw_offset;
+ return rx[d_current]->get_cw_offset();
}
-receiver::status receiver::set_filter(double low, double high, filter_shape shape)
+receiver::status receiver::set_filter(int low, int high, filter_shape shape)
{
- double trans_width;
-
if ((low >= high) || (std::abs(high-low) < RX_FILTER_MIN_WIDTH))
return STATUS_ERROR;
- switch (shape) {
-
- case FILTER_SHAPE_SOFT:
- trans_width = std::abs(high - low) * 0.5;
- break;
-
- case FILTER_SHAPE_SHARP:
- trans_width = std::abs(high - low) * 0.1;
- break;
-
- case FILTER_SHAPE_NORMAL:
- default:
- trans_width = std::abs(high - low) * 0.2;
- break;
-
- }
-
- rx->set_filter(low, high, trans_width);
+ rx[d_current]->set_filter(low, high, Modulations::TwFromFilterShape(low, high, shape));
+ return STATUS_OK;
+}
+receiver::status receiver::get_filter(int &low, int &high, filter_shape &shape)
+{
+ int tw;
+ rx[d_current]->get_filter(low, high, tw);
+ shape = Modulations::FilterShapeFromTw(low, high, tw);
return STATUS_OK;
}
@@ -737,7 +882,7 @@ receiver::status receiver::set_freq_corr(double ppm)
*/
float receiver::get_signal_pwr() const
{
- return rx->get_signal_level();
+ return rx[d_current]->get_signal_level();
}
/** Set new FFT size. */
@@ -765,41 +910,68 @@ void receiver::get_audio_fft_data(std::complex* fftPoints, unsigned int &
receiver::status receiver::set_nb_on(int nbid, bool on)
{
- if (rx->has_nb())
- rx->set_nb_on(nbid, on);
+ rx[d_current]->set_nb_on(nbid, on);
return STATUS_OK; // FIXME
}
+bool receiver::get_nb_on(int nbid)
+{
+ return rx[d_current]->get_nb_on(nbid);
+}
+
receiver::status receiver::set_nb_threshold(int nbid, float threshold)
{
- if (rx->has_nb())
- rx->set_nb_threshold(nbid, threshold);
+ rx[d_current]->set_nb_threshold(nbid, threshold);
return STATUS_OK; // FIXME
}
+float receiver::get_nb_threshold(int nbid)
+{
+ return rx[d_current]->get_nb_threshold(nbid);
+}
+
/**
* @brief Set squelch level.
* @param level_db The new level in dBFS.
*/
receiver::status receiver::set_sql_level(double level_db)
{
- if (rx->has_sql())
- rx->set_sql_level(level_db);
+ rx[d_current]->set_sql_level(level_db);
return STATUS_OK; // FIXME
}
+receiver::status receiver::set_sql_level(double level_offset, bool global, bool relative)
+{
+ if (global)
+ for (auto& rxc: rx)
+ rxc->set_sql_level((relative ? rxc->get_signal_level() : 0) + level_offset);
+ else
+ rx[d_current]->set_sql_level((relative ? rx[d_current]->get_signal_level() : 0) + level_offset);
+
+ return STATUS_OK; // FIXME
+}
+
+double receiver::get_sql_level()
+{
+ return rx[d_current]->get_sql_level();
+}
+
/** Set squelch alpha */
receiver::status receiver::set_sql_alpha(double alpha)
{
- if (rx->has_sql())
- rx->set_sql_alpha(alpha);
+ rx[d_current]->set_sql_alpha(alpha);
return STATUS_OK; // FIXME
}
+double receiver::get_sql_alpha()
+{
+ return rx[d_current]->get_sql_alpha();
+}
+
/**
* @brief Enable/disable receiver AGC.
*
@@ -807,73 +979,126 @@ receiver::status receiver::set_sql_alpha(double alpha)
*/
receiver::status receiver::set_agc_on(bool agc_on)
{
- if (rx->has_agc())
- rx->set_agc_on(agc_on);
+ rx[d_current]->set_agc_on(agc_on);
return STATUS_OK; // FIXME
}
+bool receiver::get_agc_on()
+{
+ return rx[d_current]->get_agc_on();
+}
+
/** Set AGC hang. */
receiver::status receiver::set_agc_hang(int hang_ms)
{
- if (rx->has_agc())
- rx->set_agc_hang(hang_ms);
+ rx[d_current]->set_agc_hang(hang_ms);
return STATUS_OK; // FIXME
}
+int receiver::get_agc_hang()
+{
+ return rx[d_current]->get_agc_hang();
+}
+
/** Set AGC target level. */
receiver::status receiver::set_agc_target_level(int target_level)
{
- if (rx->has_agc())
- rx->set_agc_target_level(target_level);
+ rx[d_current]->set_agc_target_level(target_level);
return STATUS_OK; // FIXME
}
+int receiver::get_agc_target_level()
+{
+ return rx[d_current]->get_agc_target_level();
+}
+
/** Set fixed gain used when AGC is OFF. */
receiver::status receiver::set_agc_manual_gain(float gain)
{
- if (rx->has_agc())
- rx->set_agc_manual_gain(gain);
+ rx[d_current]->set_agc_manual_gain(gain);
return STATUS_OK; // FIXME
}
+float receiver::get_agc_manual_gain()
+{
+ return rx[d_current]->get_agc_manual_gain();
+}
+
/** Set maximum gain used when AGC is ON. */
receiver::status receiver::set_agc_max_gain(int gain)
{
- if (rx->has_agc())
- rx->set_agc_max_gain(gain);
+ rx[d_current]->set_agc_max_gain(gain);
return STATUS_OK; // FIXME
}
+int receiver::get_agc_max_gain()
+{
+ return rx[d_current]->get_agc_max_gain();
+}
+
/** Set AGC attack. */
receiver::status receiver::set_agc_attack(int attack_ms)
{
- if (rx->has_agc())
- rx->set_agc_attack(attack_ms);
+ rx[d_current]->set_agc_attack(attack_ms);
return STATUS_OK; // FIXME
}
+int receiver::get_agc_attack()
+{
+ return rx[d_current]->get_agc_attack();
+}
+
/** Set AGC decay time. */
receiver::status receiver::set_agc_decay(int decay_ms)
{
- if (rx->has_agc())
- rx->set_agc_decay(decay_ms);
+ rx[d_current]->set_agc_decay(decay_ms);
+
+ return STATUS_OK; // FIXME
+}
+
+int receiver::get_agc_decay()
+{
+ return rx[d_current]->get_agc_decay();
+}
+
+/** Set AGC panning. */
+receiver::status receiver::set_agc_panning(int panning)
+{
+ rx[d_current]->set_agc_panning(panning);
return STATUS_OK; // FIXME
}
+int receiver::get_agc_panning()
+{
+ return rx[d_current]->get_agc_panning();
+}
+
+/** Set AGC panning auto mode. */
+receiver::status receiver::set_agc_panning_auto(bool mode)
+{
+ rx[d_current]->set_agc_panning_auto(mode);
+ if (mode)
+ rx[d_current]->set_agc_panning(rx[d_current]->get_offset() * 200.0 / d_decim_rate);
+
+ return STATUS_OK; // FIXME
+}
+
+bool receiver::get_agc_panning_auto()
+{
+ return rx[d_current]->get_agc_panning_auto();
+}
+
/** Get AGC current gain. */
float receiver::get_agc_gain()
{
- if (rx->has_agc())
- return rx->get_agc_gain();
- else
- return 0;
+ return rx[d_current]->get_agc_gain();
}
/** Set audio mute. */
@@ -881,19 +1106,18 @@ receiver::status receiver::set_mute(bool mute)
{
if (d_mute == mute)
return STATUS_OK;
- tb->lock();
- if (mute)
+ d_mute = mute;
+ if (d_mute)
{
- tb->disconnect(rx, 0, audio_snk, 0);
- tb->disconnect(rx, 1, audio_snk, 1);
+ mc0->set_k(0);
+ mc1->set_k(0);
}
else
{
- tb->connect(rx, 0, audio_snk, 0);
- tb->connect(rx, 1, audio_snk, 1);
+ float mul_k = get_rx_count() ? 1.0 / float(get_rx_count()) : 1.0;
+ mc0->set_k(mul_k);
+ mc1->set_k(mul_k);
}
- tb->unlock();
- d_mute = mute;
return STATUS_OK;
}
@@ -903,74 +1127,143 @@ bool receiver::get_mute()
return d_mute;
}
-receiver::status receiver::set_demod(rx_demod demod, bool force)
+receiver::status receiver::set_demod_locked(Modulations::idx demod, int old_idx)
{
status ret = STATUS_OK;
-
- if (!force && (demod == d_demod))
- return ret;
-
- // tb->lock() seems to hang occasionally
- if (d_running)
+ rx_chain rxc = RX_CHAIN_NONE;
+ if (old_idx == -1)
{
- tb->stop();
- tb->wait();
+ background_rx();
+ disconnect_rx();
}
- tb->disconnect_all();
-
switch (demod)
{
- case RX_DEMOD_OFF:
- connect_all(RX_CHAIN_NONE);
- break;
-
- case RX_DEMOD_NONE:
- connect_all(RX_CHAIN_NBRX);
- rx->set_demod(nbrx::NBRX_DEMOD_NONE);
- break;
-
- case RX_DEMOD_AM:
- connect_all(RX_CHAIN_NBRX);
- rx->set_demod(nbrx::NBRX_DEMOD_AM);
- break;
-
- case RX_DEMOD_AMSYNC:
- connect_all(RX_CHAIN_NBRX);
- rx->set_demod(nbrx::NBRX_DEMOD_AMSYNC);
- break;
-
- case RX_DEMOD_NFM:
- connect_all(RX_CHAIN_NBRX);
- rx->set_demod(nbrx::NBRX_DEMOD_FM);
- break;
-
- case RX_DEMOD_WFM_M:
- connect_all(RX_CHAIN_WFMRX);
- rx->set_demod(wfmrx::WFMRX_DEMOD_MONO);
- break;
-
- case RX_DEMOD_WFM_S:
- connect_all(RX_CHAIN_WFMRX);
- rx->set_demod(wfmrx::WFMRX_DEMOD_STEREO);
+ case Modulations::MODE_OFF:
+ rxc = RX_CHAIN_NONE;
break;
- case RX_DEMOD_WFM_S_OIRT:
- connect_all(RX_CHAIN_WFMRX);
- rx->set_demod(wfmrx::WFMRX_DEMOD_STEREO_UKW);
+ case Modulations::MODE_RAW:
+ case Modulations::MODE_AM:
+ case Modulations::MODE_AM_SYNC:
+ case Modulations::MODE_NFM:
+ case Modulations::MODE_LSB:
+ case Modulations::MODE_USB:
+ case Modulations::MODE_CWL:
+ case Modulations::MODE_CWU:
+ rxc = RX_CHAIN_NBRX;
break;
- case RX_DEMOD_SSB:
- connect_all(RX_CHAIN_NBRX);
- rx->set_demod(nbrx::NBRX_DEMOD_SSB);
+ case Modulations::MODE_WFM_MONO:
+ case Modulations::MODE_WFM_STEREO:
+ case Modulations::MODE_WFM_STEREO_OIRT:
+ rxc = RX_CHAIN_WFMRX;
break;
default:
ret = STATUS_ERROR;
break;
}
+ if (ret != STATUS_ERROR)
+ {
+ receiver_base_cf_sptr old_rx = rx[(old_idx == -1) ? d_current : old_idx];
+ // RX demod chain
+ switch (rxc)
+ {
+ case RX_CHAIN_NBRX:
+ case RX_CHAIN_NONE:
+ if (rx[d_current]->name() != "NBRX")
+ {
+ rx[d_current].reset();
+ rx[d_current] = make_nbrx(d_decim_rate, d_audio_rate);
+ rx[d_current]->set_index(d_current);
+ rx[d_current]->set_rec_event_handler(std::bind(audio_rec_event, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
+ }
+ break;
+
+ case RX_CHAIN_WFMRX:
+ if (rx[d_current]->name() != "WFMRX")
+ {
+ rx[d_current].reset();
+ rx[d_current] = make_wfmrx(d_decim_rate, d_audio_rate);
+ rx[d_current]->set_index(d_current);
+ rx[d_current]->set_rec_event_handler(std::bind(audio_rec_event, this,
+ std::placeholders::_1,
+ std::placeholders::_2,
+ std::placeholders::_3));
+ }
+ break;
+
+ default:
+ break;
+ }
+ //Temporary workaround for https://github.com/gnuradio/gnuradio/issues/5436
+ tb->connect(iq_src, 0, rx[d_current], 0);
+ // End temporary workaronud
+ if (old_rx.get() != rx[d_current].get())
+ {
+ rx[d_current]->restore_settings(*old_rx.get());
+ // Recorders
+ if (old_rx.get() != rx[d_current].get())
+ {
+ if (old_idx == -1)
+ if (old_rx->get_audio_recording())
+ rx[d_current]->continue_audio_recording(old_rx);
+ }
+ if (demod == Modulations::MODE_OFF)
+ {
+ if (rx[d_current]->get_audio_recording())
+ {
+ rx[d_current]->stop_audio_recording();
+ }
+ }
+ }
+ rx[d_current]->set_demod(demod);
+ //Temporary workaround for https://github.com/gnuradio/gnuradio/issues/5436
+ tb->disconnect(iq_src, 0, rx[d_current], 0);
+ // End temporary workaronud
+ }
+ connect_rx();
+ foreground_rx();
+ return ret;
+}
- d_demod = demod;
+receiver::status receiver::set_demod(Modulations::idx demod, int old_idx)
+{
+ status ret = STATUS_OK;
+ if (rx[d_current]->get_demod() == demod)
+ return ret;
+ if (d_running)
+ {
+ tb->stop();
+ tb->wait();
+ }
+ ret = set_demod_locked(demod, old_idx);
+ if (d_running)
+ tb->start();
+
+ return ret;
+}
+
+receiver::status receiver::reconnect_all(bool force)
+{
+ status ret = STATUS_OK;
+ // tb->lock() seems to hang occasionally
+ if (d_running)
+ {
+ tb->stop();
+ tb->wait();
+ }
+ if (force)
+ {
+ tb->disconnect_all();
+ for (auto& rxc : rx)
+ rxc->connected(false);
+ }
+ connect_all();
if (d_running)
tb->start();
@@ -978,74 +1271,117 @@ receiver::status receiver::set_demod(rx_demod demod, bool force)
return ret;
}
+
/**
* @brief Set maximum deviation of the FM demodulator.
* @param maxdev_hz The new maximum deviation in Hz.
*/
receiver::status receiver::set_fm_maxdev(float maxdev_hz)
{
- if (rx->has_fm())
- rx->set_fm_maxdev(maxdev_hz);
+ rx[d_current]->set_fm_maxdev(maxdev_hz);
return STATUS_OK;
}
+float receiver::get_fm_maxdev()
+{
+ return rx[d_current]->get_fm_maxdev();
+}
+
receiver::status receiver::set_fm_deemph(double tau)
{
- if (rx->has_fm())
- rx->set_fm_deemph(tau);
+ rx[d_current]->set_fm_deemph(tau);
return STATUS_OK;
}
+double receiver::get_fm_deemph()
+{
+ return rx[d_current]->get_fm_deemph();
+}
+
receiver::status receiver::set_am_dcr(bool enabled)
{
- if (rx->has_am())
- rx->set_am_dcr(enabled);
+ rx[d_current]->set_am_dcr(enabled);
return STATUS_OK;
}
+bool receiver::get_am_dcr()
+{
+ return rx[d_current]->get_am_dcr();
+}
+
receiver::status receiver::set_amsync_dcr(bool enabled)
{
- if (rx->has_amsync())
- rx->set_amsync_dcr(enabled);
+ rx[d_current]->set_amsync_dcr(enabled);
return STATUS_OK;
}
+bool receiver::get_amsync_dcr()
+{
+ return rx[d_current]->get_amsync_dcr();
+}
+
receiver::status receiver::set_amsync_pll_bw(float pll_bw)
{
- if (rx->has_amsync())
- rx->set_amsync_pll_bw(pll_bw);
+ rx[d_current]->set_amsync_pll_bw(pll_bw);
return STATUS_OK;
}
+float receiver::get_amsync_pll_bw()
+{
+ return rx[d_current]->get_amsync_pll_bw();
+}
+
receiver::status receiver::set_audio_rec_dir(const std::string dir)
{
- rx->set_rec_dir(dir);
+ //FIXME is it a global option, that should be set with for-loop?
+ rx[d_current]->set_audio_rec_dir(dir);
return STATUS_OK;
}
+std::string receiver::get_audio_rec_dir()
+{
+ //FIXME is it a global option, that should be set with for-loop?
+ return rx[d_current]->get_audio_rec_dir();
+}
+
receiver::status receiver::set_audio_rec_sql_triggered(const bool enabled)
{
- rx->set_audio_rec_sql_triggered(enabled);
+ rx[d_current]->set_audio_rec_sql_triggered(enabled);
return STATUS_OK;
}
+bool receiver::get_audio_rec_sql_triggered()
+{
+ return rx[d_current]->get_audio_rec_sql_triggered();
+}
+
receiver::status receiver::set_audio_rec_min_time(const int time_ms)
{
- rx->set_audio_rec_min_time(time_ms);
+ rx[d_current]->set_audio_rec_min_time(time_ms);
return STATUS_OK;
}
+int receiver::get_audio_rec_min_time()
+{
+ return rx[d_current]->get_audio_rec_min_time();
+}
+
receiver::status receiver::set_audio_rec_max_gap(const int time_ms)
{
- rx->set_audio_rec_max_gap(time_ms);
+ rx[d_current]->set_audio_rec_max_gap(time_ms);
return STATUS_OK;
}
+int receiver::get_audio_rec_max_gap()
+{
+ return rx[d_current]->get_audio_rec_max_gap();
+}
+
/**
* @brief Start WAV file recorder.
* @param filename The filename where to record.
@@ -1057,7 +1393,7 @@ receiver::status receiver::set_audio_rec_max_gap(const int time_ms)
*/
receiver::status receiver::start_audio_recording()
{
- if (d_recording_wav)
+ if (is_recording_audio())
{
/* error - we are already recording */
std::cout << "ERROR: Can not start audio recorder (already recording)" << std::endl;
@@ -1072,10 +1408,8 @@ receiver::status receiver::start_audio_recording()
return STATUS_ERROR;
}
- if(rx->start_audio_recording() == 0)
- {
+ if (rx[d_current]->start_audio_recording() == 0)
return STATUS_OK;
- }
else
return STATUS_ERROR;
}
@@ -1083,7 +1417,7 @@ receiver::status receiver::start_audio_recording()
/** Stop WAV file recorder. */
receiver::status receiver::stop_audio_recording()
{
- if (!d_recording_wav) {
+ if (!is_recording_audio()){
/* error: we are not recording */
std::cout << "ERROR: Can not stop audio recorder (not recording)" << std::endl;
@@ -1096,7 +1430,7 @@ receiver::status receiver::stop_audio_recording()
return STATUS_ERROR;
}
- rx->stop_audio_recording();
+ rx[d_current]->stop_audio_recording();
return STATUS_OK;
}
@@ -1104,7 +1438,7 @@ receiver::status receiver::stop_audio_recording()
/** get last recorded audio file name. */
std::string receiver::get_last_audio_filename()
{
- return rx->get_last_audio_filename();
+ return rx[d_current]->get_last_audio_filename();
}
/** Start audio playback. */
@@ -1148,21 +1482,18 @@ receiver::status receiver::start_audio_playback(const std::string filename)
stop();
/* route demodulator output to null sink */
- if (!d_mute)
- {
- tb->disconnect(rx, 0, audio_snk, 0);
- tb->disconnect(rx, 1, audio_snk, 1);
- }
- tb->disconnect(rx, 0, audio_fft, 0);
- tb->disconnect(rx, 0, audio_udp_sink, 0);
- tb->disconnect(rx, 1, audio_udp_sink, 1);
- tb->connect(rx, 0, audio_null_sink0, 0); /** FIXME: other channel? */
- tb->connect(rx, 1, audio_null_sink1, 0); /** FIXME: other channel? */
- if (!d_mute)
+ if (d_active > 0)
{
- tb->connect(wav_src, 0, audio_snk, 0);
- tb->connect(wav_src, 1, audio_snk, 1);
- }
+ tb->disconnect(mc0, 0, audio_snk, 0);
+ tb->disconnect(mc1, 0, audio_snk, 1);
+ }
+ tb->disconnect(rx[d_current], 0, audio_fft, 0);
+ tb->disconnect(rx[d_current], 0, audio_udp_sink, 0);
+ tb->disconnect(rx[d_current], 1, audio_udp_sink, 1);
+ tb->connect(rx[d_current], 0, audio_null_sink0, 0); /** FIXME: other channel? */
+ tb->connect(rx[d_current], 1, audio_null_sink1, 0); /** FIXME: other channel? */
+ tb->connect(wav_src, 0, audio_snk, 0);
+ tb->connect(wav_src, 1, audio_snk, 1);
tb->connect(wav_src, 0, audio_fft, 0);
tb->connect(wav_src, 0, audio_udp_sink, 0);
tb->connect(wav_src, 1, audio_udp_sink, 1);
@@ -1178,24 +1509,21 @@ receiver::status receiver::stop_audio_playback()
{
/* disconnect wav source and reconnect receiver */
stop();
- if (!d_mute)
- {
- tb->disconnect(wav_src, 0, audio_snk, 0);
- tb->disconnect(wav_src, 1, audio_snk, 1);
- }
+ tb->disconnect(wav_src, 0, audio_snk, 0);
+ tb->disconnect(wav_src, 1, audio_snk, 1);
tb->disconnect(wav_src, 0, audio_fft, 0);
tb->disconnect(wav_src, 0, audio_udp_sink, 0);
tb->disconnect(wav_src, 1, audio_udp_sink, 1);
- tb->disconnect(rx, 0, audio_null_sink0, 0);
- tb->disconnect(rx, 1, audio_null_sink1, 0);
- if (!d_mute)
+ tb->disconnect(rx[d_current], 0, audio_null_sink0, 0);
+ tb->disconnect(rx[d_current], 1, audio_null_sink1, 0);
+ if (d_active > 0)
{
- tb->connect(rx, 0, audio_snk, 0);
- tb->connect(rx, 1, audio_snk, 1);
+ tb->connect(mc0, 0, audio_snk, 0);
+ tb->connect(mc1, 0, audio_snk, 1);
}
- tb->connect(rx, 0, audio_fft, 0); /** FIXME: other channel? */
- tb->connect(rx, 0, audio_udp_sink, 0);
- tb->connect(rx, 1, audio_udp_sink, 1);
+ tb->connect(rx[d_current], 0, audio_fft, 0); /** FIXME: other channel? */
+ tb->connect(rx[d_current], 0, audio_udp_sink, 0);
+ tb->connect(rx[d_current], 1, audio_udp_sink, 1);
start();
/* delete wav_src since we can not change file name */
@@ -1314,7 +1642,7 @@ receiver::status receiver::start_sniffer(unsigned int samprate, int buffsize)
sniffer->set_buffer_size(buffsize);
sniffer_rr = make_resampler_ff((float)samprate/(float)d_audio_rate);
tb->lock();
- tb->connect(rx, 0, sniffer_rr, 0);
+ tb->connect(rx[d_current], 0, sniffer_rr, 0);
tb->connect(sniffer_rr, 0, sniffer, 0);
tb->unlock();
d_sniffer_active = true;
@@ -1333,11 +1661,11 @@ receiver::status receiver::stop_sniffer()
}
tb->lock();
- tb->disconnect(rx, 0, sniffer_rr, 0);
+ tb->disconnect(rx[d_current], 0, sniffer_rr, 0);
// Temporary workaround for https://github.com/gnuradio/gnuradio/issues/5436
- tb->disconnect(ddc, 0, rx, 0);
- tb->connect(ddc, 0, rx, 0);
+ tb->disconnect(rx[d_current], 0, audio_fft, 0);
+ tb->connect(rx[d_current], 0, audio_fft, 0);
// End temporary workaronud
tb->disconnect(sniffer_rr, 0, sniffer, 0);
@@ -1357,7 +1685,7 @@ void receiver::get_sniffer_data(float * outbuff, unsigned int &num)
}
/** Convenience function to connect all blocks. */
-void receiver::connect_all(rx_chain type)
+void receiver::connect_all()
{
gr::basic_block_sptr b;
@@ -1388,85 +1716,179 @@ void receiver::connect_all(rx_chain type)
// Visualization
tb->connect(b, 0, iq_fft, 0);
+ iq_src = b;
+
+ // Audio path (if there is a receiver)
+ d_active = 0;
+ std::cerr<<"connect_all "<get_index());
+ foreground_rx();
+}
- receiver_base_cf_sptr old_rx = rx;
- // RX demod chain
- switch (type)
+void receiver::connect_rx()
+{
+ connect_rx(d_current);
+}
+
+void receiver::connect_rx(int n)
+{
+ if (!rx[n])
+ return;
+ if (rx[n]->connected())
+ return;
+ std::cerr<<"connect_rx "<get_demod()<get_demod() != Modulations::MODE_OFF)
{
- case RX_CHAIN_NBRX:
- if (rx->name() != "NBRX")
+ if (d_active == 0)
{
- rx.reset();
- rx = make_nbrx(d_quad_rate, d_audio_rate);
- rx->set_rec_event_handler(std::bind(audio_rec_event, this,
- std::placeholders::_1,
- std::placeholders::_2));
+ std::cerr<<"connect_rx d_active == 0"<get_index()<get_demod() != Modulations::MODE_OFF)
+ {
+ std::cerr<<"connect_rx loop !MODE_OFF"<connect(iq_src, 0, rxc, 0);
+ tb->connect(rxc, 0, add0, rxc->get_index());
+ tb->connect(rxc, 1, add1, rxc->get_index());
+ d_active++;
+ }
+ else
+ {
+ std::cerr<<"connect_rx loop MODE_OFF"<connect(null_src, 0, add0, rxc->get_index());
+ tb->connect(null_src, 0, add1, rxc->get_index());
+ }
+ rxc->connected(true);
+ }
+ std::cerr<<"connect_rx connect add"<connect(add0, 0, mc0, 0);
+ tb->connect(add1, 0, mc1, 0);
+ std::cerr<<"connect audio_snk "<connect(mc0, 0, audio_snk, 0);
+ tb->connect(mc1, 0, audio_snk, 1);
}
- break;
-
- case RX_CHAIN_WFMRX:
- if (rx->name() != "WFMRX")
+ else
{
- rx.reset();
- rx = make_wfmrx(d_quad_rate, d_audio_rate);
- rx->set_rec_event_handler(std::bind(audio_rec_event, this,
- std::placeholders::_1,
- std::placeholders::_2));
+ std::cerr<<"connect_rx d_active > 0"<connect(iq_src, 0, rx[n], 0);
+ tb->connect(rx[n], 0, add0, n);
+ tb->connect(rx[n], 1, add1, n);
+ rx[n]->connected(true);
+ d_active++;
}
- break;
-
- default:
- break;
}
+ else
+ {
+ if (d_active > 0)
+ {
+ std::cerr<<"connect_rx MODE_OFF d_active > 0"<connect(null_src, 0, add0, n);
+ tb->connect(null_src, 0, add1, n);
+ rx[n]->connected(true);
+ }
+ }
+}
- if(old_rx.get() != rx.get())
+void receiver::disconnect_rx()
+{
+ disconnect_rx(d_current);
+}
+
+void receiver::disconnect_rx(int n)
+{
+ std::cerr<<"disconnect_rx "<get_demod()<get_demod() != Modulations::MODE_OFF)
{
- //Temporary workaround for https://github.com/gnuradio/gnuradio/issues/5436
- tb->connect(ddc, 0, rx, 0);
- // End temporary workaronud
- rx->set_center_freq(d_rf_freq);
- rx->set_offset(d_filter_offset);
- rx->set_audio_rec_sql_triggered(old_rx->get_audio_rec_sql_triggered());
- rx->set_audio_rec_min_time(old_rx->get_audio_rec_min_time());
- rx->set_audio_rec_max_gap(old_rx->get_audio_rec_max_gap());
- rx->set_rec_dir(old_rx->get_rec_dir());
- //Temporary workaround for https://github.com/gnuradio/gnuradio/issues/5436
- tb->disconnect(ddc, 0, rx, 0);
- // End temporary workaronud
+ d_active--;
+ if (d_active == 0)
+ {
+ std::cerr<<"disconnect_rx d_active == 0"<get_index()<connected())
+ {
+ if (rxc->get_demod() != Modulations::MODE_OFF)
+ {
+ std::cerr<<"disconnect_rx loop !MODE_OFF"<disconnect(iq_src, 0, rxc, 0);
+ tb->disconnect(rxc, 0, add0, rxc->get_index());
+ tb->disconnect(rxc, 1, add1, rxc->get_index());
+ }
+ else
+ {
+ std::cerr<<"disconnect_rx loop MODE_OFF"<disconnect(null_src, 0, add0, rxc->get_index());
+ tb->disconnect(null_src, 0, add1, rxc->get_index());
+ }
+ rxc->connected(false);
+ }
+ }
+ std::cerr<<"disconnect_rx disconnect add"<disconnect(add0, 0, mc0, 0);
+ tb->disconnect(add1, 0, mc1, 0);
+ std::cerr<<"disconnect audio_snk "<disconnect(mc0, 0, audio_snk, 0);
+ tb->disconnect(mc1, 0, audio_snk, 1);
+ }
+ else
+ {
+ std::cerr<<"disconnect_rx d_active > 0"<disconnect(iq_src, 0, rx[n], 0);
+ tb->disconnect(rx[n], 0, add0, n);
+ tb->disconnect(rx[n], 1, add1, n);
+ }
}
- // Audio path (if there is a receiver)
- if (type != RX_CHAIN_NONE)
+ else
{
- tb->connect(b, 0, ddc, 0);
- tb->connect(ddc, 0, rx, 0);
- tb->connect(rx, 0, audio_fft, 0);
- tb->connect(rx, 0, audio_udp_sink, 0);
- tb->connect(rx, 1, audio_udp_sink, 1);
- if (!d_mute)
+ if (d_active > 0)
{
- tb->connect(rx, 0, audio_snk, 0);
- tb->connect(rx, 1, audio_snk, 1);
+ std::cerr<<"disconnect_rx MODE_OFF d_active > 0"<disconnect(null_src, 0, add0, n);
+ tb->disconnect(null_src, 0, add1, n);
}
- // Recorders and sniffers
- if(old_rx.get() != rx.get())
+ }
+ rx[n]->connected(false);
+}
+
+void receiver::background_rx()
+{
+ std::cerr<<"background_rx "<get_demod()<get_demod() != Modulations::MODE_OFF)
+ {
+ tb->disconnect(rx[d_current], 0, audio_fft, 0);
+ tb->disconnect(rx[d_current], 0, audio_udp_sink, 0);
+ tb->disconnect(rx[d_current], 1, audio_udp_sink, 1);
+ if (d_sniffer_active)
{
- if (d_recording_wav)
- rx->continue_audio_recording(old_rx);
+ tb->disconnect(rx[d_current], 0, sniffer_rr, 0);
+ tb->disconnect(sniffer_rr, 0, sniffer, 0);
}
+ }
+}
+void receiver::foreground_rx()
+{
+ std::cerr<<"foreground_rx "<get_demod()<get_demod() != Modulations::MODE_OFF)
+ {
+ tb->connect(rx[d_current], 0, audio_fft, 0);
+ tb->connect(rx[d_current], 0, audio_udp_sink, 0);
+ tb->connect(rx[d_current], 1, audio_udp_sink, 1);
if (d_sniffer_active)
{
- tb->connect(rx, 0, sniffer_rr, 0);
+ tb->connect(rx[d_current], 0, sniffer_rr, 0);
tb->connect(sniffer_rr, 0, sniffer, 0);
}
}
else
{
- if (d_recording_wav)
- {
- rx->stop_audio_recording();
- d_recording_wav = false;
- }
if (d_sniffer_active)
{
@@ -1476,49 +1898,55 @@ void receiver::connect_all(rx_chain type)
sniffer_rr.reset();
}
}
+ d_mute = !d_mute;
+ set_mute(!d_mute);
}
void receiver::get_rds_data(std::string &outbuff, int &num)
{
- rx->get_rds_data(outbuff, num);
+ rx[d_current]->get_rds_data(outbuff, num);
}
void receiver::start_rds_decoder(void)
{
+ if (is_rds_decoder_active())
+ return;
if (d_running)
{
stop();
- rx->start_rds_decoder();
+ rx[d_current]->start_rds_decoder();
start();
}
else
{
- rx->start_rds_decoder();
+ rx[d_current]->start_rds_decoder();
}
}
void receiver::stop_rds_decoder(void)
{
+ if (!is_rds_decoder_active())
+ return;
if (d_running)
{
stop();
- rx->stop_rds_decoder();
+ rx[d_current]->stop_rds_decoder();
start();
}
else
{
- rx->stop_rds_decoder();
+ rx[d_current]->stop_rds_decoder();
}
}
bool receiver::is_rds_decoder_active(void) const
{
- return rx->is_rds_decoder_active();
+ return rx[d_current]->is_rds_decoder_active();
}
void receiver::reset_rds_parser(void)
{
- rx->reset_rds_parser();
+ rx[d_current]->reset_rds_parser();
}
std::string receiver::escape_filename(std::string filename)
@@ -1531,19 +1959,14 @@ std::string receiver::escape_filename(std::string filename)
return ss2.str();
}
-void receiver::audio_rec_event(receiver * self, std::string filename, bool is_running)
+void receiver::audio_rec_event(receiver * self, int idx, std::string filename, bool is_running)
{
if (is_running)
- {
- self->d_recording_wav = true;
std::cout << "Recording audio to " << filename << std::endl;
- }
else
- {
- self->d_recording_wav = false;
std::cout << "Audio recorder stopped" << std::endl;
- }
- if(self->d_audio_rec_event_handler)
- self->d_audio_rec_event_handler(filename, is_running);
+ if (self->d_audio_rec_event_handler)
+ if (idx == self->d_current)
+ self->d_audio_rec_event_handler(filename, is_running);
}
diff --git a/src/applications/gqrx/receiver.h b/src/applications/gqrx/receiver.h
index 30d90cd728..68fcc15529 100644
--- a/src/applications/gqrx/receiver.h
+++ b/src/applications/gqrx/receiver.h
@@ -25,19 +25,23 @@
#if GNURADIO_VERSION < 0x030800
#include
+#include
#else
#include
+#include
#endif
#include
#include
+//temporary workaround to make add_ff happy
+#include
+//emd workaround
#include
#include
#include
#include
#include "dsp/correct_iq_cc.h"
-#include "dsp/downconverter.h"
#include "dsp/filter/fir_decim.h"
#include "dsp/rx_noise_blanker_cc.h"
#include "dsp/rx_filter.h"
@@ -82,19 +86,6 @@ class receiver
STATUS_ERROR = 1 /*!< There was an error. */
};
- /** Available demodulators. */
- enum rx_demod {
- RX_DEMOD_OFF = 0, /*!< No receiver. */
- RX_DEMOD_NONE = 1, /*!< No demod. Raw I/Q to audio. */
- RX_DEMOD_AM = 2, /*!< Amplitude modulation. */
- RX_DEMOD_NFM = 3, /*!< Frequency modulation. */
- RX_DEMOD_WFM_M = 4, /*!< Frequency modulation (wide, mono). */
- RX_DEMOD_WFM_S = 5, /*!< Frequency modulation (wide, stereo). */
- RX_DEMOD_WFM_S_OIRT = 6, /*!< Frequency modulation (wide, stereo oirt). */
- RX_DEMOD_SSB = 7, /*!< Single Side Band. */
- RX_DEMOD_AMSYNC = 8 /*!< Amplitude modulation (synchronous demod). */
- };
-
/** Supported receiver types. */
enum rx_chain {
RX_CHAIN_NONE = 0, /*!< No receiver, just spectrum analyzer. */
@@ -103,11 +94,7 @@ class receiver
};
/** Filter shape (convenience wrappers for "transition width"). */
- enum filter_shape {
- FILTER_SHAPE_SOFT = 0, /*!< Soft: Transition band is TBD of width. */
- FILTER_SHAPE_NORMAL = 1, /*!< Normal: Transition band is TBD of width. */
- FILTER_SHAPE_SHARP = 2 /*!< Sharp: Transition band is TBD of width. */
- };
+ typedef Modulations::filter_shape filter_shape;
typedef std::function audio_rec_event_handler_t;
@@ -157,11 +144,27 @@ class receiver
status set_gain(std::string name, double value);
double get_gain(std::string name) const;
+ int add_rx();
+ int get_rx_count();
+ int delete_rx();
+ status select_rx(int no);
+ status fake_select_rx(int no);
+ int get_current();
+ vfo::sptr get_current_vfo();
+ vfo::sptr get_vfo(int n);
+ vfo::sptr find_vfo(int64_t freq);
+ std::vector get_vfos();
+
status set_filter_offset(double offset_hz);
+ status set_filter_offset(int rx_index, double offset_hz);
double get_filter_offset(void) const;
+ void set_freq_lock(bool on, bool all = false);
+ bool get_freq_lock();
+
status set_cw_offset(double offset_hz);
double get_cw_offset(void) const;
- status set_filter(double low, double high, filter_shape shape);
+ status set_filter(int low, int high, filter_shape shape);
+ status get_filter(int &low, int &high, filter_shape &shape);
status set_freq_corr(double ppm);
float get_signal_pwr() const;
void set_iq_fft_size(int newsize);
@@ -173,43 +176,73 @@ class receiver
/* Noise blanker */
status set_nb_on(int nbid, bool on);
+ bool get_nb_on(int nbid);
status set_nb_threshold(int nbid, float threshold);
+ float get_nb_threshold(int nbid);
/* Squelch parameter */
status set_sql_level(double level_db);
+ status set_sql_level(double level_offset, bool global, bool relative);
+ double get_sql_level();
status set_sql_alpha(double alpha);
+ double get_sql_alpha();
/* AGC */
status set_agc_on(bool agc_on);
+ bool get_agc_on();
status set_agc_target_level(int target_level);
+ int get_agc_target_level();
status set_agc_manual_gain(float gain);
+ float get_agc_manual_gain();
status set_agc_max_gain(int gain);
+ int get_agc_max_gain();
status set_agc_attack(int attack_ms);
+ int get_agc_attack();
status set_agc_decay(int decay_ms);
+ int get_agc_decay();
status set_agc_hang(int hang_ms);
+ int get_agc_hang();
+ status set_agc_panning(int panning);
+ int get_agc_panning();
+ status set_agc_panning_auto(bool mode);
+ bool get_agc_panning_auto();
float get_agc_gain();
+ /* Mute */
status set_mute(bool mute);
bool get_mute();
- status set_demod(rx_demod demod, bool force=false);
+ /* Demod */
+ status set_demod_locked(Modulations::idx demod, int old_idx = -1);
+ status set_demod(Modulations::idx demod, int old_idx = -1);
+ Modulations::idx get_demod() {return rx[d_current]->get_demod();}
+ status reconnect_all(bool force = false);
/* FM parameters */
status set_fm_maxdev(float maxdev_hz);
+ float get_fm_maxdev();
status set_fm_deemph(double tau);
+ double get_fm_deemph();
/* AM parameters */
status set_am_dcr(bool enabled);
+ bool get_am_dcr();
/* AM-Sync parameters */
status set_amsync_dcr(bool enabled);
+ bool get_amsync_dcr();
status set_amsync_pll_bw(float pll_bw);
+ float get_amsync_pll_bw();
/* Audio parameters */
status set_audio_rec_dir(const std::string dir);
+ std::string get_audio_rec_dir();
status set_audio_rec_sql_triggered(const bool enabled);
+ bool get_audio_rec_sql_triggered();
status set_audio_rec_min_time(const int time_ms);
+ int get_audio_rec_min_time();
status set_audio_rec_max_gap(const int time_ms);
+ int get_audio_rec_max_gap();
status start_audio_recording();
status stop_audio_recording();
std::string get_last_audio_filename();
@@ -223,13 +256,14 @@ class receiver
status start_iq_recording(const std::string filename);
status stop_iq_recording();
status seek_iq_file(long pos);
+ bool is_playing_iq() const { return input_devstr.find("file=") != std::string::npos; }
/* sample sniffer */
status start_sniffer(unsigned int samplrate, int buffsize);
status stop_sniffer();
void get_sniffer_data(float * outbuff, unsigned int &num);
- bool is_recording_audio(void) const { return d_recording_wav; }
+ bool is_recording_audio(void) const { return rx[d_current]->get_audio_recording(); }
bool is_snifffer_active(void) const { return d_sniffer_active; }
/* rds functions */
@@ -247,37 +281,46 @@ class receiver
}
private:
- void connect_all(rx_chain type);
+ void connect_all();
+ void connect_rx();
+ void connect_rx(int n);
+ void disconnect_rx();
+ void disconnect_rx(int n);
+ void foreground_rx();
+ void background_rx();
+ status connect_iq_recorder();
private:
+ int d_current; /*!< Current selected demodulator. */
+ int d_active; /*!< Active demodulator count. */
bool d_running; /*!< Whether receiver is running or not. */
double d_input_rate; /*!< Input sample rate. */
double d_decim_rate; /*!< Rate after decimation (input_rate / decim) */
- double d_quad_rate; /*!< Quadrature rate (after down-conversion) */
double d_audio_rate; /*!< Audio output rate. */
unsigned int d_decim; /*!< input decimation. */
- unsigned int d_ddc_decim; /*!< Down-conversion decimation. */
double d_rf_freq; /*!< Current RF frequency. */
- double d_filter_offset; /*!< Current filter offset */
- double d_cw_offset; /*!< CW offset */
bool d_recording_iq; /*!< Whether we are recording I/Q file. */
- bool d_recording_wav; /*!< Whether we are recording WAV file. */
bool d_sniffer_active; /*!< Only one data decoder allowed. */
bool d_iq_rev; /*!< Whether I/Q is reversed or not. */
bool d_dc_cancel; /*!< Enable automatic DC removal. */
bool d_iq_balance; /*!< Enable automatic IQ balance. */
bool d_mute; /*!< Enable audio mute. */
- std::string input_devstr; /*!< Current input device string. */
- std::string output_devstr; /*!< Current output device string. */
+ std::string input_devstr; /*!< Current input device string. */
+ std::string output_devstr; /*!< Current output device string. */
- rx_demod d_demod; /*!< Current demodulator. */
+ gr::basic_block_sptr iq_src; /*!< Points to the block, connected to rx[]. */
gr::top_block_sptr tb; /*!< The GNU Radio top block. */
osmosdr::source::sptr src; /*!< Real time I/Q source. */
fir_decim_cc_sptr input_decim; /*!< Input decimator. */
- receiver_base_cf_sptr rx; /*!< receiver. */
+ std::vector rx; /*!< receiver. */
+ gr::blocks::add_ff::sptr add0; /* Audio downmix */
+ gr::blocks::add_ff::sptr add1; /* Audio downmix */
+ gr::blocks::multiply_const_ff::sptr mc0; /* Audio downmix */
+ gr::blocks::multiply_const_ff::sptr mc1; /* Audio downmix */
+ gr::blocks::null_source::sptr null_src; /* temporary workaround */
dc_corr_cc_sptr dc_corr; /*!< DC corrector block. */
iq_swap_cc_sptr iq_swap; /*!< I/Q swapping block. */
@@ -285,8 +328,6 @@ class receiver
rx_fft_c_sptr iq_fft; /*!< Baseband FFT block. */
rx_fft_f_sptr audio_fft; /*!< Audio FFT block. */
- downconverter_cc_sptr ddc; /*!< Digital down-converter for demod chain. */
-
gr::blocks::file_sink::sptr iq_sink; /*!< I/Q file sink. */
gr::blocks::wavfile_source::sptr wav_src; /*!< WAV file source for playback. */
gr::blocks::null_sink::sptr audio_null_sink0; /*!< Audio null sink used during playback. */
@@ -306,7 +347,7 @@ class receiver
audio_rec_event_handler_t d_audio_rec_event_handler;
//! Get a path to a file containing random bytes
static std::string get_zero_file(void);
- static void audio_rec_event(receiver * self, std::string filename,
+ static void audio_rec_event(receiver * self, int idx, std::string filename,
bool is_running);
};
diff --git a/src/applications/gqrx/remote_control.cpp b/src/applications/gqrx/remote_control.cpp
index 167c6608f8..e5b473049b 100644
--- a/src/applications/gqrx/remote_control.cpp
+++ b/src/applications/gqrx/remote_control.cpp
@@ -39,7 +39,7 @@ RemoteControl::RemoteControl(QObject *parent) :
rc_filter_offset = 0;
bw_half = 740e3;
rc_lnb_lo_mhz = 0.0;
- rc_mode = 0;
+ rc_mode = Modulations::MODE_OFF;
rc_passband_lo = 0;
rc_passband_hi = 0;
rc_program_id = "0000";
@@ -302,11 +302,11 @@ void RemoteControl::setSignalLevel(float level)
}
/*! \brief Set demodulator (from mainwindow). */
-void RemoteControl::setMode(int mode)
+void RemoteControl::setMode(Modulations::idx mode)
{
rc_mode = mode;
- if (rc_mode == 0)
+ if (rc_mode == Modulations::MODE_OFF)
audio_recorder_status = false;
}
@@ -354,7 +354,7 @@ void RemoteControl::setSquelchLevel(double level)
/*! \brief Start audio recorder (from mainwindow). */
void RemoteControl::startAudioRecorder()
{
- if (rc_mode > 0)
+ if (rc_mode > Modulations::MODE_OFF)
audio_recorder_status = true;
}
@@ -419,69 +419,69 @@ int RemoteControl::modeStrToInt(QString mode_str)
if (mode_str.compare("OFF", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_OFF;
+ mode_int = Modulations::MODE_OFF;
}
else if (mode_str.compare("RAW", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_RAW;
+ mode_int = Modulations::MODE_RAW;
}
else if (mode_str.compare("AM", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_AM;
+ mode_int = Modulations::MODE_AM;
}
else if (mode_str.compare("AMS", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_AM_SYNC;
+ mode_int = Modulations::MODE_AM_SYNC;
}
else if (mode_str.compare("LSB", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_LSB;
+ mode_int = Modulations::MODE_LSB;
}
else if (mode_str.compare("USB", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_USB;
+ mode_int = Modulations::MODE_USB;
}
else if (mode_str.compare("CWL", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_CWL;
+ mode_int = Modulations::MODE_CWL;
hamlib_compatible = false;
}
else if (mode_str.compare("CWR", Qt::CaseInsensitive) == 0) // "CWR" : "CWL"
{
- mode_int = DockRxOpt::MODE_CWL;
+ mode_int = Modulations::MODE_CWL;
hamlib_compatible = true;
}
else if (mode_str.compare("CWU", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_CWU;
+ mode_int = Modulations::MODE_CWU;
hamlib_compatible = false;
}
else if (mode_str.compare("CW", Qt::CaseInsensitive) == 0) // "CW" : "CWU"
{
- mode_int = DockRxOpt::MODE_CWU;
+ mode_int = Modulations::MODE_CWU;
hamlib_compatible = true;
}
else if (mode_str.compare("FM", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_NFM;
+ mode_int = Modulations::MODE_NFM;
}
else if (mode_str.compare("WFM", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_WFM_MONO;
+ mode_int = Modulations::MODE_WFM_MONO;
}
else if (mode_str.compare("WFM_ST", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_WFM_STEREO;
+ mode_int = Modulations::MODE_WFM_STEREO;
}
else if (mode_str.compare("WFM_ST_OIRT", Qt::CaseInsensitive) == 0)
{
- mode_int = DockRxOpt::MODE_WFM_STEREO_OIRT;
+ mode_int = Modulations::MODE_WFM_STEREO_OIRT;
}
return mode_int;
}
/*! \brief Convert mode enum to string.
- * \param mode The mode ID c.f. DockRxOpt::rxopt_mode_idx
+ * \param mode The mode ID c.f. Modulations::rxopt_mode_idx
* \returns The mode string.
*/
QString RemoteControl::intToModeStr(int mode)
@@ -490,51 +490,51 @@ QString RemoteControl::intToModeStr(int mode)
switch (mode)
{
- case DockRxOpt::MODE_OFF:
+ case Modulations::MODE_OFF:
mode_str = "OFF";
break;
- case DockRxOpt::MODE_RAW:
+ case Modulations::MODE_RAW:
mode_str = "RAW";
break;
- case DockRxOpt::MODE_AM:
+ case Modulations::MODE_AM:
mode_str = "AM";
break;
- case DockRxOpt::MODE_AM_SYNC:
+ case Modulations::MODE_AM_SYNC:
mode_str = "AMS";
break;
- case DockRxOpt::MODE_LSB:
+ case Modulations::MODE_LSB:
mode_str = "LSB";
break;
- case DockRxOpt::MODE_USB:
+ case Modulations::MODE_USB:
mode_str = "USB";
break;
- case DockRxOpt::MODE_CWL:
+ case Modulations::MODE_CWL:
mode_str = (hamlib_compatible) ? "CWR" : "CWL";
break;
- case DockRxOpt::MODE_CWU:
+ case Modulations::MODE_CWU:
mode_str = (hamlib_compatible) ? "CW" : "CWU";
break;
- case DockRxOpt::MODE_NFM:
+ case Modulations::MODE_NFM:
mode_str = "FM";
break;
- case DockRxOpt::MODE_WFM_MONO:
+ case Modulations::MODE_WFM_MONO:
mode_str = "WFM";
break;
- case DockRxOpt::MODE_WFM_STEREO:
+ case Modulations::MODE_WFM_STEREO:
mode_str = "WFM_ST";
break;
- case DockRxOpt::MODE_WFM_STEREO_OIRT:
+ case Modulations::MODE_WFM_STEREO_OIRT:
mode_str = "WFM_ST_OIRT";
break;
@@ -593,7 +593,7 @@ QString RemoteControl::cmd_set_mode(QStringList cmdlist)
}
else
{
- rc_mode = mode;
+ rc_mode = Modulations::idx(mode);
emit newMode(rc_mode);
int passband = cmdlist.value(2, "0").toInt();
@@ -732,7 +732,7 @@ QString RemoteControl::cmd_set_func(QStringList cmdlist)
}
else if ((func.compare("RECORD", Qt::CaseInsensitive) == 0) && ok)
{
- if (rc_mode == 0 || !receiver_running)
+ if (rc_mode == Modulations::MODE_OFF || !receiver_running)
{
answer = QString("RPRT 1\n");
}
@@ -831,7 +831,7 @@ QString RemoteControl::cmd_get_info() const
/* Gpredict / Gqrx specific command: AOS - satellite AOS event */
QString RemoteControl::cmd_AOS()
{
- if (rc_mode > 0 && receiver_running)
+ if (rc_mode > Modulations::MODE_OFF && receiver_running)
{
emit startAudioRecorderEvent();
audio_recorder_status = true;
diff --git a/src/applications/gqrx/remote_control.h b/src/applications/gqrx/remote_control.h
index 3d29527f8d..bc0ce2e19d 100644
--- a/src/applications/gqrx/remote_control.h
+++ b/src/applications/gqrx/remote_control.h
@@ -30,6 +30,8 @@
#include
#include
#include
+#include "receivers/defines.h"
+#include "receivers/modulations.h"
/* For gain_t and gain_list_t */
#include "qtgui/dockinputctl.h"
@@ -89,7 +91,7 @@ public slots:
void setLnbLo(double freq_mhz);
void setBandwidth(qint64 bw);
void setSignalLevel(float level);
- void setMode(int mode);
+ void setMode(Modulations::idx mode);
void setPassband(int passband_lo, int passband_hi);
void setSquelchLevel(double level);
void startAudioRecorder();
@@ -102,7 +104,7 @@ public slots:
void newFrequency(qint64 freq);
void newFilterOffset(qint64 offset);
void newLnbLo(double freq_mhz);
- void newMode(int mode);
+ void newMode(Modulations::idx mode);
void newPassband(int passband);
void newSquelchLevel(double level);
void startAudioRecorderEvent();
@@ -127,7 +129,7 @@ private slots:
qint64 bw_half;
double rc_lnb_lo_mhz; /*!< Current LNB LO freq in MHz */
- int rc_mode; /*!< Current mode. */
+ Modulations::idx rc_mode; /*!< Current mode. */
int rc_passband_lo; /*!< Current low cutoff. */
int rc_passband_hi; /*!< Current high cutoff. */
bool rds_status; /*!< RDS decoder enabled */
diff --git a/src/dsp/rx_agc_xx.cpp b/src/dsp/rx_agc_xx.cpp
index 29a4ce8c6b..349e0fc5b1 100644
--- a/src/dsp/rx_agc_xx.cpp
+++ b/src/dsp/rx_agc_xx.cpp
@@ -34,13 +34,18 @@
#define MIN_GAIN_DB (-20.0f)
#define MIN_GAIN exp10f(MIN_GAIN_DB)
#define MAX_SAMPLE_RATE 96000
+#define PANNING_DELAY_K 4000.0
+#define PANNING_GAIN_K 100.0
+//TODO Make this user-configurable as extra peak history time
+#define AGC_AVG_BUF_SCALE 2
rx_agc_2f_sptr make_rx_agc_2f(double sample_rate, bool agc_on, int target_level,
- int manual_gain, int max_gain, int attack, int decay, int hang)
+ int manual_gain, int max_gain, int attack,
+ int decay, int hang, int panning)
{
return gnuradio::get_initial_sptr(new rx_agc_2f(sample_rate, agc_on, target_level,
manual_gain, max_gain, attack, decay,
- hang));
+ hang, panning));
}
/**
@@ -49,7 +54,8 @@ rx_agc_2f_sptr make_rx_agc_2f(double sample_rate, bool agc_on, int target_level,
* Use make_rx_agc_2f() instead.
*/
rx_agc_2f::rx_agc_2f(double sample_rate, bool agc_on, int target_level,
- int manual_gain, int max_gain, int attack, int decay, int hang)
+ int manual_gain, int max_gain, int attack,
+ int decay, int hang, int panning)
: gr::sync_block ("rx_agc_2f",
gr::io_signature::make(2, 2, sizeof(float)),
gr::io_signature::make(2, 2, sizeof(float))),
@@ -61,6 +67,7 @@ rx_agc_2f::rx_agc_2f(double sample_rate, bool agc_on, int target_level,
d_attack(attack),
d_decay(decay),
d_hang(hang),
+ d_panning(panning),
d_target_mag(1),
d_hang_samp(0),
d_buf_samples(0),
@@ -74,12 +81,16 @@ rx_agc_2f::rx_agc_2f(double sample_rate, bool agc_on, int target_level,
d_decay_step(1.01),
d_attack_step(0.99),
d_floor(0.0001),
+ d_gain_l(1.0),
+ d_gain_r(1.0),
+ d_delay_l(0),
+ d_delay_r(0),
d_refill(false),
d_running(false)
{
- set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang, true);
- set_history(MAX_SAMPLE_RATE + 1);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang, d_panning, true);
+ set_history(MAX_SAMPLE_RATE + 1 + MAX_SAMPLE_RATE * 100 / PANNING_DELAY_K);
}
rx_agc_2f::~rx_agc_2f()
@@ -148,15 +159,15 @@ int rx_agc_2f::work(int noutput_items,
float sample_in0 = in0[k_hist];
float sample_in1 = in1[k_hist];
mag_in = std::max(fabs(sample_in0),fabs(sample_in1));
- float sample_out0 = in0[k_hist - d_buf_samples];
- float sample_out1 = in1[k_hist - d_buf_samples];
+ float sample_out0 = in0[k_hist - d_buf_samples - d_delay_l];
+ float sample_out1 = in1[k_hist - d_buf_samples - d_delay_r];
d_mag_buf[d_buf_p] = mag_in;
update_buffer(d_buf_p);
max_out = get_peak();
int buf_p_next = d_buf_p + 1;
- if (buf_p_next >= d_buf_samples)
+ if (buf_p_next >= d_buf_size)
buf_p_next = 0;
if (max_out > d_floor)
@@ -197,14 +208,14 @@ int rx_agc_2f::work(int noutput_items,
d_hang_counter--;
if (d_current_gain < MIN_GAIN)
d_current_gain = MIN_GAIN;
- out0[k] = sample_out0 * d_current_gain;
- out1[k] = sample_out1 * d_current_gain;
+ out0[k] = sample_out0 * d_current_gain * d_gain_l;
+ out1[k] = sample_out1 * d_current_gain * d_gain_r;
d_buf_p = buf_p_next;
}
}
else{
- volk_32f_s32f_multiply_32f((float *)out0, (float *)&in0[history() - 1], d_current_gain, noutput_items);
- volk_32f_s32f_multiply_32f((float *)out1, (float *)&in1[history() - 1], d_current_gain, noutput_items);
+ volk_32f_s32f_multiply_32f((float *)out0, (float *)&in0[history() - 1 - d_delay_l], d_current_gain * d_gain_l, noutput_items);
+ volk_32f_s32f_multiply_32f((float *)out1, (float *)&in1[history() - 1 - d_delay_r], d_current_gain * d_gain_r, noutput_items);
}
#ifdef AGC_DEBUG2
static TYPEFLOAT d_prev_dbg = 0.0;
@@ -230,7 +241,7 @@ void rx_agc_2f::set_agc_on(bool agc_on)
{
if (agc_on != d_agc_on) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang);
+ set_parameters(d_sample_rate, agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang, d_panning);
#if GNURADIO_VERSION >= 0x030800
if(d_agc_on)
declare_sample_delay(d_sample_rate * d_attack / 1000);
@@ -251,7 +262,7 @@ void rx_agc_2f::set_sample_rate(double sample_rate)
{
if (sample_rate != d_sample_rate) {
std::lock_guard lock(d_mutex);
- set_parameters(sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang);
+ set_parameters(sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang, d_panning);
#if GNURADIO_VERSION >= 0x030800
if(d_agc_on)
declare_sample_delay(d_sample_rate * d_attack / 1000);
@@ -269,7 +280,7 @@ void rx_agc_2f::set_target_level(int target_level)
{
if ((target_level != d_target_level) && (target_level >= -160) && (target_level <= 0)) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, d_agc_on, target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang);
+ set_parameters(d_sample_rate, d_agc_on, target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang, d_panning);
}
}
@@ -285,7 +296,7 @@ void rx_agc_2f::set_manual_gain(float gain)
{
if ((gain != d_manual_gain) && (gain >= -160.0) && (gain <= 160.0)) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, d_agc_on, d_target_level, gain, d_max_gain, d_attack, d_decay, d_hang);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, gain, d_max_gain, d_attack, d_decay, d_hang, d_panning);
}
}
@@ -301,7 +312,7 @@ void rx_agc_2f::set_max_gain(int gain)
{
if ((gain != d_max_gain) && (gain >= 0) && (gain <= 160)) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, gain, d_attack, d_decay, d_hang);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, gain, d_attack, d_decay, d_hang, d_panning);
}
}
@@ -316,7 +327,7 @@ void rx_agc_2f::set_attack(int attack)
{
if ((attack != d_attack) && (attack >= 20) && (attack <= 5000)) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, attack, d_decay, d_hang);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, attack, d_decay, d_hang, d_panning);
#if GNURADIO_VERSION >= 0x030800
if(d_agc_on)
declare_sample_delay(d_sample_rate * d_attack / 1000);
@@ -332,7 +343,7 @@ void rx_agc_2f::set_decay(int decay)
{
if ((decay != d_decay) && (decay >= 20) && (decay <= 5000)) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, decay, d_hang);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, decay, d_hang, d_panning);
}
}
@@ -344,7 +355,15 @@ void rx_agc_2f::set_hang(int hang)
{
if ((hang != d_hang) && (hang >= 0) && (hang <= 5000)) {
std::lock_guard lock(d_mutex);
- set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, hang);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, hang, d_panning);
+ }
+}
+
+void rx_agc_2f::set_panning(int panning)
+{
+ if((panning != d_panning) && (panning >=-100) && (panning <= 100)) {
+ std::lock_guard lock(d_mutex);
+ set_parameters(d_sample_rate, d_agc_on, d_target_level, d_manual_gain, d_max_gain, d_attack, d_decay, d_hang, panning);
}
}
@@ -356,7 +375,7 @@ float rx_agc_2f::get_current_gain()
void rx_agc_2f::set_parameters(double sample_rate, bool agc_on, int target_level,
float manual_gain, int max_gain, int attack,
- int decay, int hang, bool force)
+ int decay, int hang, int panning, bool force)
{
bool samp_rate_changed = false;
bool agc_on_changed = false;
@@ -366,6 +385,7 @@ void rx_agc_2f::set_parameters(double sample_rate, bool agc_on, int target_level
bool attack_changed = false;
bool decay_changed = false;
bool hang_changed = false;
+
if (d_sample_rate != sample_rate || force)
{
d_sample_rate = sample_rate;
@@ -418,18 +438,35 @@ void rx_agc_2f::set_parameters(double sample_rate, bool agc_on, int target_level
d_hang = hang;
hang_changed = true;
}
+ if (d_panning != panning || force)
+ {
+ d_panning = panning;
+ d_gain_l = 1.0;
+ d_gain_r = 1.0;
+ d_delay_l = 0;
+ d_delay_r = 0;
+ if(panning < 0)
+ {
+ d_gain_r = exp10(panning / PANNING_GAIN_K);
+ d_delay_r = (sample_rate * -panning)/PANNING_DELAY_K;
+ }
+ if(panning > 0)
+ {
+ d_gain_l = exp10(-panning / PANNING_GAIN_K);
+ d_delay_l = (sample_rate * panning)/PANNING_DELAY_K;
+ }
+ }
if (samp_rate_changed || attack_changed)
{
d_buf_samples = sample_rate * d_attack / 1000.0;
+ int buf_samples = d_buf_samples * AGC_AVG_BUF_SCALE;
int buf_size = 1;
for(unsigned int k = 0; k < sizeof(int) * 8; k++)
{
buf_size *= 2;
- if(buf_size >= d_buf_samples)
+ if(buf_size >= buf_samples)
break;
}
- if (d_buf_p >= d_buf_samples)
- d_buf_p %= d_buf_samples;
if(d_buf_size != buf_size)
{
d_buf_size = buf_size;
@@ -440,6 +477,8 @@ void rx_agc_2f::set_parameters(double sample_rate, bool agc_on, int target_level
if(d_agc_on && d_running)
d_refill = true;
}
+ if (d_buf_p >= d_buf_size)
+ d_buf_p %= d_buf_size;
}
if ((manual_gain_changed || agc_on_changed) && !agc_on)
d_current_gain = exp10(TYPEFLOAT(d_manual_gain) / 20.0);
diff --git a/src/dsp/rx_agc_xx.h b/src/dsp/rx_agc_xx.h
index c3378be42b..be3b80cc1b 100644
--- a/src/dsp/rx_agc_xx.h
+++ b/src/dsp/rx_agc_xx.h
@@ -60,7 +60,7 @@ typedef std::shared_ptr rx_agc_2f_sptr;
*/
rx_agc_2f_sptr make_rx_agc_2f(double sample_rate, bool agc_on, int target_level,
int manual_gain, int max_gain, int attack,
- int decay, int hang);
+ int decay, int hang, int panning);
/**
* \brief Experimental AGC block for analog voice modes (AM, SSB, CW).
@@ -74,11 +74,12 @@ class rx_agc_2f : public gr::sync_block
friend rx_agc_2f_sptr make_rx_agc_2f(double sample_rate, bool agc_on,
int target_level, int manual_gain,
int max_gain, int attack, int decay,
- int hang);
+ int hang, int panning);
protected:
rx_agc_2f(double sample_rate, bool agc_on, int target_level,
- int manual_gain, int max_gain, int attack, int decay, int hang);
+ int manual_gain, int max_gain, int attack, int decay, int hang,
+ int panning);
public:
~rx_agc_2f();
@@ -97,11 +98,13 @@ class rx_agc_2f : public gr::sync_block
void set_attack(int attack);
void set_decay(int decay);
void set_hang(int hang);
+ void set_panning(int panning);
+
float get_current_gain();
private:
void set_parameters(double sample_rate, bool agc_on, int target_level,
float manual_gain, int max_gain, int attack, int decay,
- int hang, bool force = false);
+ int hang, int panning, bool force = false);
std::mutex d_mutex; /*! Used to lock internal data while processing or setting parameters. */
@@ -113,6 +116,7 @@ class rx_agc_2f : public gr::sync_block
int d_attack; /*! Current AGC attack (20...5000 ms). */
int d_decay; /*! Current AGC decay (20...5000 ms). */
int d_hang; /*! Current AGC hang (0...5000 ms). */
+ int d_panning; /*! Current AGC panning (-100...100). */
private:
float get_peak();
void update_buffer(int p);
@@ -130,6 +134,10 @@ class rx_agc_2f : public gr::sync_block
TYPEFLOAT d_decay_step;
TYPEFLOAT d_attack_step;
TYPEFLOAT d_floor;
+ TYPEFLOAT d_gain_l;
+ TYPEFLOAT d_gain_r;
+ int d_delay_l;
+ int d_delay_r;
std::vector d_mag_buf;
bool d_refill;
diff --git a/src/dsp/rx_demod_am.cpp b/src/dsp/rx_demod_am.cpp
index 7066ca60bf..26266d501b 100644
--- a/src/dsp/rx_demod_am.cpp
+++ b/src/dsp/rx_demod_am.cpp
@@ -86,20 +86,16 @@ void rx_demod_am::set_dcr(bool dcr)
if (d_dcr_enabled)
{
// Switching from ON to OFF
- lock();
disconnect(d_demod, 0, d_dcr, 0);
disconnect(d_dcr, 0, self(), 0);
connect(d_demod, 0, self(), 0);
- unlock();
}
else
{
// Switching from OFF to ON
- lock();
disconnect(d_demod, 0, self(), 0);
connect(d_demod, 0, d_dcr, 0);
connect(d_dcr, 0, self(), 0);
- unlock();
}
d_dcr_enabled = dcr;
@@ -164,20 +160,16 @@ void rx_demod_amsync::set_dcr(bool dcr)
if (d_dcr_enabled)
{
// Switching from ON to OFF
- lock();
disconnect(d_demod2, 0, d_dcr, 0);
disconnect(d_dcr, 0, self(), 0);
connect(d_demod2, 0, self(), 0);
- unlock();
}
else
{
// Switching from OFF to ON
- lock();
disconnect(d_demod2, 0, self(), 0);
connect(d_demod2, 0, d_dcr, 0);
connect(d_dcr, 0, self(), 0);
- unlock();
}
d_dcr_enabled = dcr;
diff --git a/src/dsp/rx_filter.h b/src/dsp/rx_filter.h
index f34db9653d..6d6c6b8fa9 100644
--- a/src/dsp/rx_filter.h
+++ b/src/dsp/rx_filter.h
@@ -33,8 +33,7 @@
#include
#endif
-
-#define RX_FILTER_MIN_WIDTH 100 /*! Minimum width of filter */
+#include "receivers/defines.h"
class rx_filter;
class rx_xlating_filter;
diff --git a/src/interfaces/wav_sink.cpp b/src/interfaces/wav_sink.cpp
index 158bb66633..ce53d9f73d 100644
--- a/src/interfaces/wav_sink.cpp
+++ b/src/interfaces/wav_sink.cpp
@@ -418,7 +418,7 @@ void wavfile_sink_gqrx::writeout(const int offset, const int writecount, const i
int nwritten = 0;
int bp = 0;
int errnum;
- while(nwritten < writecount)
+ while (nwritten < writecount)
{
for (bp = 0; (nwritten < writecount) && (bp < s_items_size); nwritten++, bp++)
{
@@ -445,9 +445,13 @@ void wavfile_sink_gqrx::writeout(const int offset, const int writecount, const i
void wavfile_sink_gqrx::set_sql_triggered(const bool enabled)
{
- std::unique_lock guard(d_mutex);
- d_squelch_triggered = enabled;
- d_prev_action = ACT_NONE;
+ if (d_squelch_triggered == enabled)
+ return;
+ {
+ std::unique_lock guard(d_mutex);
+ d_squelch_triggered = enabled;
+ d_prev_action = ACT_NONE;
+ }
}
void wavfile_sink_gqrx::set_bits_per_sample(int bits_per_sample)
diff --git a/src/interfaces/wav_sink.h b/src/interfaces/wav_sink.h
index ff8da03a29..a9b5d5151d 100644
--- a/src/interfaces/wav_sink.h
+++ b/src/interfaces/wav_sink.h
@@ -183,6 +183,7 @@ class wavfile_sink_gqrx : virtual public gr::sync_block
int get_rec_max_gap() { return d_max_gap_ms; }
int get_min_time();
int get_max_gap();
+ bool is_active() { return !! d_fp; }
private:
bool open_unlocked(const char* filename);
int open_new_unlocked();
diff --git a/src/qtgui/agc_options.cpp b/src/qtgui/agc_options.cpp
index 40ff5052d5..57a3f10dec 100644
--- a/src/qtgui/agc_options.cpp
+++ b/src/qtgui/agc_options.cpp
@@ -230,6 +230,33 @@ void CAgcOptions::enableHang(bool enabled)
ui->hangTitle->setEnabled(enabled);
}
+/*! \brief Get panning fixed position. */
+int CAgcOptions::panning()
+{
+ return ui->panningSlider->value();
+}
+
+/*! \brief Set panning fixed position. */
+void CAgcOptions::setPanning(int value)
+{
+ if(value < -100)
+ return;
+ if(value > 100)
+ return;
+ ui->panningSlider->setValue(value);
+}
+
+/*! \brief Get panning auto mode. */
+bool CAgcOptions::panningAuto()
+{
+ return (Qt::Checked == ui->panningAutoCheckBox->checkState());
+}
+
+/*! \brief Set panning auto mode. */
+void CAgcOptions::setPanningAuto(bool value)
+{
+ ui->panningAutoCheckBox->setCheckState(value ? Qt::Checked : Qt::Unchecked);
+}
/*! \brief AGC gain slider value has changed. */
@@ -273,4 +300,18 @@ void CAgcOptions::on_hangSlider_valueChanged(int value)
ui->hangLabel->setText(QString("%1 ms").arg(ui->hangSlider->value()));
emit hangChanged(value);
}
+/*! \brief Panning slider value has changed. */
+void CAgcOptions::on_panningSlider_valueChanged(int value)
+{
+ ui->panningLabel->setText(QString::number(value));
+ emit panningChanged(value);
+}
+/*! \brief Panning auto checkbox state changed. */
+void CAgcOptions::on_panningAutoCheckBox_stateChanged(int state)
+{
+ ui->panningSlider->setEnabled(!(state == Qt::Checked));
+ ui->panningTitle->setEnabled(!(state == Qt::Checked));
+ ui->panningLabel->setEnabled(!(state == Qt::Checked));
+ emit panningAutoChanged(state == Qt::Checked);
+}
diff --git a/src/qtgui/agc_options.h b/src/qtgui/agc_options.h
index f989b81279..b264470354 100644
--- a/src/qtgui/agc_options.h
+++ b/src/qtgui/agc_options.h
@@ -74,6 +74,10 @@ class CAgcOptions : public QDialog
void setHang(int value);
void enableHang(bool enabled);
+ int panning();
+ void setPanning(int value);
+ bool panningAuto();
+ void setPanningAuto(bool value);
enum agc_preset_e
{
@@ -93,6 +97,8 @@ class CAgcOptions : public QDialog
void attackChanged(int decay);
void decayChanged(int decay);
void hangChanged(int hang);
+ void panningChanged(int panning);
+ void panningAutoChanged(bool panningAuto);
private slots:
void on_gainSlider_valueChanged(int value);
@@ -101,6 +107,8 @@ private slots:
void on_attackSlider_valueChanged(int value);
void on_decaySlider_valueChanged(int value);
void on_hangSlider_valueChanged(int value);
+ void on_panningSlider_valueChanged(int value);
+ void on_panningAutoCheckBox_stateChanged(int state);
private:
Ui::CAgcOptions *ui;
diff --git a/src/qtgui/agc_options.ui b/src/qtgui/agc_options.ui
index ca8a787e8d..6d185922b9 100644
--- a/src/qtgui/agc_options.ui
+++ b/src/qtgui/agc_options.ui
@@ -7,7 +7,7 @@
0
0
293
- 253
+ 299
@@ -33,7 +33,7 @@
5
- -
+
-
Target
@@ -43,16 +43,6 @@
- -
-
-
- 0 dB
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
-
@@ -174,28 +164,6 @@
- -
-
-
- Qt::StrongFocus
-
-
- Target output level.
-
-
- -100
-
-
- 0
-
-
- 0
-
-
- Qt::Horizontal
-
-
-
-
@@ -344,6 +312,105 @@
+ -
+
+
+ 0 dB
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Enable auto stereo panning mode depending on offset frequency.
+
+
+ Auto panning
+
+
+
+ -
+
+
+ Qt::StrongFocus
+
+
+ Target output level.
+
+
+ -100
+
+
+ 0
+
+
+ 0
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ true
+
+
+ Qt::StrongFocus
+
+
+ AGC stereo panning position.
+
+
+ -100
+
+
+ 100
+
+
+ 10
+
+
+ 50
+
+
+ 0
+
+
+ 0
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Panning
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ true
+
+
+ 0
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
-
@@ -364,7 +431,6 @@
gainSlider
- targetLevelSlider
decaySlider
diff --git a/src/qtgui/audio_options.cpp b/src/qtgui/audio_options.cpp
index f4b2319b82..e0e8db029f 100644
--- a/src/qtgui/audio_options.cpp
+++ b/src/qtgui/audio_options.cpp
@@ -280,3 +280,8 @@ void CAudioOptions::on_udpStereo_stateChanged(int state)
{
emit newUdpStereo(state);
}
+
+void CAudioOptions::on_toAllVFOsButton_clicked()
+{
+ emit copyRecSettingsToAllVFOs();
+}
diff --git a/src/qtgui/audio_options.h b/src/qtgui/audio_options.h
index 6b5624e31b..5f6eca9d65 100644
--- a/src/qtgui/audio_options.h
+++ b/src/qtgui/audio_options.h
@@ -80,6 +80,7 @@ public slots:
void newSquelchTriggered(bool enabled);
void newRecMinTime(int time_ms);
void newRecMaxGap(int time_ms);
+ void copyRecSettingsToAllVFOs();
private slots:
void on_fftSplitSlider_valueChanged(int value);
@@ -94,6 +95,7 @@ private slots:
void on_squelchTriggered_stateChanged(int state);
void on_recMinTime_valueChanged(int value);
void on_recMaxGap_valueChanged(int value);
+ void on_toAllVFOsButton_clicked();
private:
Ui::CAudioOptions *ui; /*!< The user interface widget. */
diff --git a/src/qtgui/audio_options.ui b/src/qtgui/audio_options.ui
index 2a9fd32f24..f495d93ef3 100644
--- a/src/qtgui/audio_options.ui
+++ b/src/qtgui/audio_options.ui
@@ -7,7 +7,7 @@
0
0
315
- 209
+ 242
@@ -167,6 +167,19 @@
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
@@ -275,6 +288,23 @@
+ -
+
+
+ Copy to
+
+
+
+ -
+
+
+ Copy location, min time, max gap settings to all VFOs
+
+
+ All VFOs
+
+
+
diff --git a/src/qtgui/bookmarks.cpp b/src/qtgui/bookmarks.cpp
index c57ba74421..2bbe6c6368 100644
--- a/src/qtgui/bookmarks.cpp
+++ b/src/qtgui/bookmarks.cpp
@@ -29,6 +29,7 @@
#include
#include
#include "bookmarks.h"
+#include "qtgui/bookmarkstablemodel.h"
const QColor TagInfo::DefaultColor(Qt::lightGray);
const QString TagInfo::strUntagged("Untagged");
@@ -61,14 +62,18 @@ void Bookmarks::add(BookmarkInfo &info)
m_BookmarkList.append(info);
std::stable_sort(m_BookmarkList.begin(),m_BookmarkList.end());
save();
- emit( BookmarksChanged() );
}
void Bookmarks::remove(int index)
{
m_BookmarkList.removeAt(index);
save();
- emit BookmarksChanged();
+}
+
+void Bookmarks::remove(const BookmarkInfo &info)
+{
+ m_BookmarkList.removeOne(info);
+ save();
}
bool Bookmarks::load()
@@ -121,7 +126,21 @@ bool Bookmarks::load()
info.frequency = strings[0].toLongLong();
info.name = strings[1].trimmed();
info.modulation = strings[2].trimmed();
- info.bandwidth = strings[3].toInt();
+ info.set_demod(Modulations::GetEnumForModulationString(info.modulation));
+ int bandwidth = strings[3].toInt();
+ switch(info.get_demod())
+ {
+ case Modulations::MODE_LSB:
+ case Modulations::MODE_CWL:
+ info.set_filter(-100 - bandwidth, -100, bandwidth * 0.2);
+ break;
+ case Modulations::MODE_USB:
+ case Modulations::MODE_CWU:
+ info.set_filter(100, 100 + bandwidth, bandwidth * 0.2);
+ break;
+ default:
+ info.set_filter(-bandwidth / 2, bandwidth / 2, bandwidth * 0.2);
+ }
// Multiple Tags may be separated by comma.
QString strTags = strings[4];
QStringList TagList = strTags.split(",");
@@ -132,6 +151,51 @@ bool Bookmarks::load()
m_BookmarkList.append(info);
}
+ else if (strings.count() == BookmarksTableModel::COLUMN_COUNT)
+ {
+ BookmarkInfo info;
+ int i = 0;
+ info.frequency = strings[i++].toLongLong();
+ info.name = strings[i++].trimmed();
+ // Multiple Tags may be separated by comma.
+ QString strTags = strings[i++];
+ QStringList TagList = strTags.split(",");
+
+ for (int iTag = 0; iTag < TagList.size(); ++iTag)
+ info.tags.append(&findOrAddTag(TagList[iTag].trimmed()));
+
+ info.set_freq_lock(strings[i++].trimmed() == "true");
+ info.modulation = strings[i++].trimmed();
+ info.set_demod(Modulations::GetEnumForModulationString(info.modulation));
+ info.set_filter_low(strings[i++].toInt());
+ info.set_filter_high(strings[i++].toInt());
+ info.set_filter_tw(strings[i++].toInt());
+ info.set_agc_on(strings[i++].trimmed() == "true");
+ info.set_agc_target_level(strings[i++].toInt());
+ info.set_agc_manual_gain(strings[i++].toFloat());
+ info.set_agc_max_gain(strings[i++].toInt());
+ info.set_agc_attack(strings[i++].toInt());
+ info.set_agc_decay(strings[i++].toInt());
+ info.set_agc_hang(strings[i++].toInt());
+ info.set_agc_panning(strings[i++].toInt());
+ info.set_agc_panning_auto(strings[i++].trimmed() == "true");
+ info.set_cw_offset(strings[i++].toInt());
+ info.set_fm_maxdev(strings[i++].toFloat());
+ info.set_fm_deemph(1.0e-6 * strings[i++].toFloat());
+ info.set_am_dcr(strings[i++].trimmed() == "true");
+ info.set_amsync_dcr(strings[i++].trimmed() == "true");
+ info.set_amsync_pll_bw(strings[i++].toFloat());
+ info.set_nb_on(1, strings[i++].trimmed() == "true");
+ info.set_nb_threshold(1, strings[i++].toFloat());
+ info.set_nb_on(2, strings[i++].trimmed() == "true");
+ info.set_nb_threshold(2, strings[i++].toFloat());
+ info.set_audio_rec_dir(strings[i++].trimmed().toStdString());
+ info.set_audio_rec_sql_triggered(strings[i++].trimmed() == "true");
+ info.set_audio_rec_min_time(strings[i++].toInt());
+ info.set_audio_rec_max_gap(strings[i++].toInt());
+
+ m_BookmarkList.append(info);
+ }
else
{
std::cout << "Bookmarks: Ignoring Line:" << std::endl;
@@ -178,41 +242,93 @@ bool Bookmarks::save()
stream << '\n';
stream << QString("# Frequency").leftJustified(12) + "; " +
- QString("Name").leftJustified(25)+ "; " +
+ QString("Name").leftJustified(25) + "; " +
+ QString("Tags").leftJustified(25) + "; " +
+ QString("Autostart").rightJustified(10) + "; " +
QString("Modulation").leftJustified(20) + "; " +
- QString("Bandwidth").rightJustified(10) + "; " +
- QString("Tags") << '\n';
-
+ QString("Filter Low").rightJustified(16) + "; " +
+ QString("Filter High").rightJustified(16) + "; " +
+ QString("Filter TW").rightJustified(16) + "; " +
+ QString("AGC On").rightJustified(10) + "; " +
+ QString("AGC target level").rightJustified(16) + "; " +
+ QString("AGC manual gain").rightJustified(16) + "; " +
+ QString("AGC max gain").rightJustified(16) + "; " +
+ QString("AGC attack").rightJustified(16) + "; " +
+ QString("AGC decay").rightJustified(16) + "; " +
+ QString("AGC hang").rightJustified(16) + "; " +
+ QString("Panning").rightJustified(16) + "; " +
+ QString("Auto panning").rightJustified(16) + "; " +
+ QString("CW offset").rightJustified(16) + "; " +
+ QString("FM max deviation").rightJustified(16) + "; " +
+ QString("FM deemphasis").rightJustified(16) + "; " +
+ QString("AM DCR").rightJustified(16) + "; " +
+ QString("AM SYNC DCR").rightJustified(16) + "; " +
+ QString("AM SYNC PLL BW").rightJustified(16) + "; " +
+ QString("NB1 ON").rightJustified(16) + "; " +
+ QString("NB1 threshold").rightJustified(16) + "; " +
+ QString("NB2 ON").rightJustified(16) + "; " +
+ QString("NB2 threshold").rightJustified(16) + "; " +
+ QString("REC DIR").rightJustified(16) + "; " +
+ QString("REC SQL trig").rightJustified(16) + "; " +
+ QString("REC Min time").rightJustified(16) + "; " +
+ QString("REC Max gap").rightJustified(16)
+ << "\n";
for (int i = 0; i < m_BookmarkList.size(); i++)
{
BookmarkInfo& info = m_BookmarkList[i];
- QString line = QString::number(info.frequency).rightJustified(12) +
- "; " + info.name.leftJustified(25) + "; " +
- info.modulation.leftJustified(20)+ "; " +
- QString::number(info.bandwidth).rightJustified(10) + "; ";
- for(int iTag = 0; iTag Bookmarks::getBookmarksInRange(qint64 low, qint64 high)
+QList Bookmarks::getBookmarksInRange(qint64 low, qint64 high, bool autoAdded)
{
BookmarkInfo info;
- info.frequency=low;
+ info.frequency = low;
QList::const_iterator lb = std::lower_bound(m_BookmarkList.begin(), m_BookmarkList.end(), info);
info.frequency=high;
QList::const_iterator ub = std::upper_bound(m_BookmarkList.begin(), m_BookmarkList.end(), info);
@@ -222,10 +338,8 @@ QList Bookmarks::getBookmarksInRange(qint64 low, qint64 high)
while (lb != ub)
{
const BookmarkInfo& info = *lb;
- //if(info.IsActive())
- {
- found.append(info);
- }
+ if (!autoAdded || lb->get_freq_lock())
+ found.append(info);
lb++;
}
@@ -233,6 +347,12 @@ QList Bookmarks::getBookmarksInRange(qint64 low, qint64 high)
}
+int Bookmarks::find(const BookmarkInfo &info)
+{
+ return m_BookmarkList.indexOf(info);
+}
+
+
TagInfo &Bookmarks::findOrAddTag(QString tagName)
{
tagName = tagName.trimmed();
@@ -283,8 +403,8 @@ bool Bookmarks::removeTag(QString tagName)
// Delete Tag.
m_TagList.removeAt(idx);
- emit BookmarksChanged();
emit TagListChanged();
+ save();
return true;
}
diff --git a/src/qtgui/bookmarks.h b/src/qtgui/bookmarks.h
index cfb795e3a3..b6d2255f1f 100644
--- a/src/qtgui/bookmarks.h
+++ b/src/qtgui/bookmarks.h
@@ -30,6 +30,7 @@
#include
#include
#include
+#include "receivers/vfo.h"
struct TagInfo
{
@@ -57,18 +58,21 @@ struct TagInfo
}
};
-struct BookmarkInfo
+class BookmarkInfo:public vfo_s
{
- qint64 frequency;
- QString name;
- QString modulation;
- qint64 bandwidth;
- QList tags;
+ public:
+#if GNURADIO_VERSION < 0x030900
+ typedef boost::shared_ptr sptr;
+#else
+ typedef std::shared_ptr sptr;
+#endif
+ static sptr make()
+ {
+ return sptr(new BookmarkInfo());
+ }
- BookmarkInfo()
+ BookmarkInfo():vfo_s(),frequency(0)
{
- this->frequency = 0;
- this->bandwidth = 0;
}
/* BookmarkInfo( qint64 frequency, QString name, qint64 bandwidth, QString modulation )
@@ -83,6 +87,10 @@ struct BookmarkInfo
{
return frequency < other.frequency;
}
+ bool operator==(const BookmarkInfo &other) const
+ {
+ return frequency == other.frequency;
+ }
/*
void setTags(QString tagString);
QString getTagString();
@@ -92,6 +100,11 @@ struct BookmarkInfo
const QColor GetColor() const;
bool IsActive() const;
+
+ qint64 frequency;
+ QString name;
+ QString modulation;
+ QList tags;
};
class Bookmarks : public QObject
@@ -104,11 +117,13 @@ class Bookmarks : public QObject
void add(BookmarkInfo& info);
void remove(int index);
+ void remove(const BookmarkInfo &info);
bool load();
bool save();
int size() { return m_BookmarkList.size(); }
BookmarkInfo& getBookmark(int i) { return m_BookmarkList[i]; }
- QList getBookmarksInRange(qint64 low, qint64 high);
+ QList getBookmarksInRange(qint64 low, qint64 high, bool autoAdded = false);
+ int find(const BookmarkInfo &info);
//int lowerBound(qint64 low);
//int upperBound(qint64 high);
@@ -123,9 +138,9 @@ class Bookmarks : public QObject
private:
Bookmarks(); // Singleton Constructor is private.
QList m_BookmarkList;
- QList m_TagList;
- QString m_bookmarksFile;
- static Bookmarks* m_pThis;
+ QList m_TagList;
+ QString m_bookmarksFile;
+ static Bookmarks* m_pThis;
signals:
void BookmarksChanged(void);
diff --git a/src/qtgui/bookmarkstablemodel.cpp b/src/qtgui/bookmarkstablemodel.cpp
index 710eeed956..4dbc7aaf15 100644
--- a/src/qtgui/bookmarkstablemodel.cpp
+++ b/src/qtgui/bookmarkstablemodel.cpp
@@ -20,6 +20,7 @@
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
+#include
#include
#include
#include "bookmarks.h"
@@ -28,7 +29,9 @@
BookmarksTableModel::BookmarksTableModel(QObject *parent) :
- QAbstractTableModel(parent)
+ QAbstractTableModel(parent),
+ m_sortCol(0),
+ m_sortDir(Qt::AscendingOrder)
{
}
@@ -38,7 +41,7 @@ int BookmarksTableModel::rowCount ( const QModelIndex & /*parent*/ ) const
}
int BookmarksTableModel::columnCount ( const QModelIndex & /*parent*/ ) const
{
- return 5;
+ return COLUMN_COUNT;
}
QVariant BookmarksTableModel::headerData ( int section, Qt::Orientation orientation, int role ) const
@@ -53,14 +56,92 @@ QVariant BookmarksTableModel::headerData ( int section, Qt::Orientation orientat
case COL_NAME:
return QString("Name");
break;
+ case COL_TAGS:
+ return QString("Tag");
+ break;
+ case COL_LOCKED:
+ return QString("Auto");
+ break;
case COL_MODULATION:
return QString("Modulation");
break;
- case COL_BANDWIDTH:
- return QString("Bandwidth");
+ case COL_FILTER_LOW:
+ return QString("Filter Low");
break;
- case COL_TAGS:
- return QString("Tag");
+ case COL_FILTER_HIGH:
+ return QString("Filter High");
+ break;
+ case COL_FILTER_TW:
+ return QString("Filter Tw");
+ break;
+ case COL_AGC_ON:
+ return QString("AGC");
+ break;
+ case COL_AGC_TARGET:
+ return QString("AGC Target");
+ break;
+ case COL_AGC_MANUAL:
+ return QString("AGC Manual");
+ break;
+ case COL_AGC_MAX:
+ return QString("AGC Max Gain");
+ break;
+ case COL_AGC_ATTACK:
+ return QString("AGC Attack");
+ break;
+ case COL_AGC_DECAY:
+ return QString("AGC Decay");
+ break;
+ case COL_AGC_HANG:
+ return QString("AGC Hang");
+ break;
+ case COL_AGC_PANNING:
+ return QString("Panning");
+ break;
+ case COL_AGC_PANNING_AUTO:
+ return QString("Autopanning");
+ break;
+ case COL_CW_OFFSET:
+ return QString("CW Offset");
+ break;
+ case COL_FM_MAXDEV:
+ return QString("FM Deviation");
+ break;
+ case COL_FM_DEEMPH:
+ return QString("FM Deemphasis");
+ break;
+ case COL_AM_DCR:
+ return QString("AM DCR");
+ break;
+ case COL_AMSYNC_DCR:
+ return QString("AM SYNC DCR");
+ break;
+ case COL_AMSYNC_PLL_BW:
+ return QString("AM SYNC PLL BW");
+ break;
+ case COL_NB1_ON:
+ return QString("NB1 ON");
+ break;
+ case COL_NB1_THRESHOLD:
+ return QString("NB1 Threshold");
+ break;
+ case COL_NB2_ON:
+ return QString("NB2 ON");
+ break;
+ case COL_NB2_THRESHOLD:
+ return QString("NB2 Threshold");
+ break;
+ case COL_REC_DIR:
+ return QString("REC Directory");
+ break;
+ case COL_REC_SQL_TRIGGERED:
+ return QString("REC SQL-triggered");
+ break;
+ case COL_REC_MIN_TIME:
+ return QString("REC Min Time");
+ break;
+ case COL_REC_MAX_GAP:
+ return QString("REC Max Gap");
break;
}
}
@@ -71,29 +152,16 @@ QVariant BookmarksTableModel::headerData ( int section, Qt::Orientation orientat
return QVariant();
}
-QVariant BookmarksTableModel::data ( const QModelIndex & index, int role ) const
+QVariant BookmarksTableModel::dataFromBookmark(BookmarkInfo &info, int index)
{
- BookmarkInfo& info = *m_Bookmarks[index.row()];
-
- if(role==Qt::BackgroundRole)
+ switch(index)
{
- QColor bg(info.GetColor());
- bg.setAlpha(0x60);
- return bg;
- }
- else if(role == Qt::DisplayRole || role==Qt::EditRole)
- {
- switch(index.column())
+ case COL_FREQUENCY:
+ return info.frequency;
+ case COL_NAME:
+ return info.name;
+ case COL_TAGS:
{
- case COL_FREQUENCY:
- return info.frequency;
- case COL_NAME:
- return (role==Qt::EditRole)?QString(info.name):info.name;
- case COL_MODULATION:
- return info.modulation;
- case COL_BANDWIDTH:
- return (info.bandwidth==0)?QVariant(""):QVariant(info.bandwidth);
- case COL_TAGS:
QString strTags;
for(int iTag=0; iTag=
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toLongLong();
+ else
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toLongLong() <
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toLongLong();
+
+ case COL_FILTER_LOW://int
+ case COL_FILTER_HIGH:
+ case COL_FILTER_TW:
+ case COL_AGC_TARGET:
+ case COL_AGC_MAX:
+ case COL_AGC_ATTACK:
+ case COL_AGC_DECAY:
+ case COL_AGC_HANG:
+ case COL_AGC_PANNING:
+ case COL_CW_OFFSET:
+ case COL_REC_MIN_TIME:
+ case COL_REC_MAX_GAP:
+ if (order)
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toInt() >=
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toInt();
+ else
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toInt() <
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toInt();
+
+ case COL_LOCKED://bool
+ case COL_AGC_ON:
+ case COL_AGC_PANNING_AUTO:
+ case COL_AM_DCR:
+ case COL_AMSYNC_DCR:
+ case COL_NB1_ON:
+ case COL_NB2_ON:
+ case COL_REC_SQL_TRIGGERED:
+ if (order)
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toBool() >=
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toBool();
+ else
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toBool() <
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toBool();
+
+ case COL_AGC_MANUAL://float
+ case COL_FM_MAXDEV:
+ case COL_FM_DEEMPH:
+ case COL_AMSYNC_PLL_BW:
+ case COL_NB1_THRESHOLD:
+ case COL_NB2_THRESHOLD:
+ if (order)
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toFloat() >=
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toFloat();
+ else
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toFloat() <
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toFloat();
+
+ case COL_NAME://string
+ case COL_TAGS:
+ case COL_MODULATION:
+ case COL_REC_DIR:
+ default:
+ if (order)
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toString() >=
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toString();
+ else
+ return dataFromBookmark(Bookmarks::Get().getBookmark(a), column).toString() <
+ dataFromBookmark(Bookmarks::Get().getBookmark(b), column).toString();
+
+ }
+}
+
+void BookmarksTableModel::sort(int column, Qt::SortOrder order)
+{
+ if (column < 0)
+ return;
+ m_sortCol = column;
+ m_sortDir = order;
+ std::stable_sort(m_Bookmarks.begin(), m_Bookmarks.end(),
+ std::bind(bmCompare, std::placeholders::_1,
+ std::placeholders::_2, column, order));
+ emit layoutChanged();
}
diff --git a/src/qtgui/bookmarkstablemodel.h b/src/qtgui/bookmarkstablemodel.h
index 66c4046daf..6a7ad2cf14 100644
--- a/src/qtgui/bookmarkstablemodel.h
+++ b/src/qtgui/bookmarkstablemodel.h
@@ -25,10 +25,11 @@
#include
#include
+#include "receivers/defines.h"
+#include "receivers/modulations.h"
#include "bookmarks.h"
-
class BookmarksTableModel : public QAbstractTableModel
{
Q_OBJECT
@@ -36,28 +37,61 @@ class BookmarksTableModel : public QAbstractTableModel
public:
enum EColumns
{
- COL_FREQUENCY,
+ COL_FREQUENCY = 0,
COL_NAME,
+ COL_TAGS,
+ COL_LOCKED,
COL_MODULATION,
- COL_BANDWIDTH,
- COL_TAGS
+ COL_FILTER_LOW,
+ COL_FILTER_HIGH,
+ COL_FILTER_TW,
+ COL_AGC_ON,
+ COL_AGC_TARGET,
+ COL_AGC_MANUAL,
+ COL_AGC_MAX,
+ COL_AGC_ATTACK,
+ COL_AGC_DECAY,
+ COL_AGC_HANG,
+ COL_AGC_PANNING,
+ COL_AGC_PANNING_AUTO,
+ COL_CW_OFFSET,
+ COL_FM_MAXDEV,
+ COL_FM_DEEMPH,
+ COL_AM_DCR,
+ COL_AMSYNC_DCR,
+ COL_AMSYNC_PLL_BW,
+ COL_NB1_ON,
+ COL_NB1_THRESHOLD,
+ COL_NB2_ON,
+ COL_NB2_THRESHOLD,
+ COL_REC_DIR,
+ COL_REC_SQL_TRIGGERED,
+ COL_REC_MIN_TIME,
+ COL_REC_MAX_GAP,
+ COLUMN_COUNT
};
explicit BookmarksTableModel(QObject *parent = 0);
-
- int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
- int columnCount ( const QModelIndex & parent = QModelIndex() ) const;
- QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
- QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const;
- bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole );
- Qt::ItemFlags flags ( const QModelIndex & index ) const;
- BookmarkInfo* getBookmarkAtRow(int row);
+ int rowCount ( const QModelIndex & parent = QModelIndex() ) const override;
+ int columnCount ( const QModelIndex & parent = QModelIndex() ) const override;
+ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override;
+ QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const override;
+ bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ) override;
+ Qt::ItemFlags flags ( const QModelIndex & index ) const override;
+
+ BookmarkInfo* getBookmarkAtRow(int row) const;
int GetBookmarksIndexForRow(int iRow);
+ int GetRowForBookmarkIndex(int index);
+ void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
private:
- QList m_Bookmarks;
- QMap m_mapRowToBookmarksIndex;
+ static QVariant dataFromBookmark(BookmarkInfo &info, int index);
+ static bool bmCompare(const int a, const int b, int column, int order);
+private:
+ QList m_Bookmarks;
+ int m_sortCol;
+ Qt::SortOrder m_sortDir;
signals:
public slots:
diff --git a/src/qtgui/demod_options.cpp b/src/qtgui/demod_options.cpp
index c1f4b113a1..2fccd60e5b 100644
--- a/src/qtgui/demod_options.cpp
+++ b/src/qtgui/demod_options.cpp
@@ -231,3 +231,13 @@ void CDemodOptions::on_pllBwSelector_activated(int index)
{
emit amSyncPllBwSelected(pll_bw_from_index(index));
}
+
+void CDemodOptions::setAmDcr(bool on)
+{
+ ui->dcrCheckBox->setChecked(on);
+}
+
+void CDemodOptions::setAmSyncDcr(bool on)
+{
+ ui->syncdcrCheckBox->setChecked(on);
+}
diff --git a/src/qtgui/demod_options.h b/src/qtgui/demod_options.h
index 581ef81944..cb4a107f46 100644
--- a/src/qtgui/demod_options.h
+++ b/src/qtgui/demod_options.h
@@ -70,6 +70,9 @@ class CDemodOptions : public QDialog
void setPllBw(float pll_bw);
float getPllBw(void) const;
+ void setAmDcr(bool on);
+ void setAmSyncDcr(bool on);
+
signals:
/*! \brief Signal emitted when new FM deviation is selected. */
void fmMaxdevSelected(float max_dev);
diff --git a/src/qtgui/dockaudio.cpp b/src/qtgui/dockaudio.cpp
index ab2472d787..355050b55b 100644
--- a/src/qtgui/dockaudio.cpp
+++ b/src/qtgui/dockaudio.cpp
@@ -50,15 +50,16 @@ DockAudio::DockAudio(QWidget *parent) :
audioOptions = new CAudioOptions(this);
connect(audioOptions, SIGNAL(newFftSplit(int)), ui->audioSpectrum, SLOT(setPercent2DScreen(int)));
- connect(audioOptions, SIGNAL(newPandapterRange(int,int)), this, SLOT(setNewPandapterRange(int,int)));
- connect(audioOptions, SIGNAL(newWaterfallRange(int,int)), this, SLOT(setNewWaterfallRange(int,int)));
- connect(audioOptions, SIGNAL(newRecDirSelected(QString)), this, SLOT(setNewRecDir(QString)));
- connect(audioOptions, SIGNAL(newUdpHost(QString)), this, SLOT(setNewUdpHost(QString)));
- connect(audioOptions, SIGNAL(newUdpPort(int)), this, SLOT(setNewUdpPort(int)));
- connect(audioOptions, SIGNAL(newUdpStereo(bool)), this, SLOT(setNewUdpStereo(bool)));
- connect(audioOptions, SIGNAL(newSquelchTriggered(bool)), this, SLOT(setNewSquelchTriggered(bool)));
- connect(audioOptions, SIGNAL(newRecMinTime(int)), this, SLOT(setRecMinTime(int)));
- connect(audioOptions, SIGNAL(newRecMaxGap(int)), this, SLOT(setRecMaxGap(int)));
+ connect(audioOptions, SIGNAL(newPandapterRange(int,int)), this, SLOT(pandapterRange_changed(int,int)));
+ connect(audioOptions, SIGNAL(newWaterfallRange(int,int)), this, SLOT(waterfallRange_changed(int,int)));
+ connect(audioOptions, SIGNAL(newRecDirSelected(QString)), this, SLOT(recDir_changed(QString)));
+ connect(audioOptions, SIGNAL(newUdpHost(QString)), this, SLOT(udpHost_changed(QString)));
+ connect(audioOptions, SIGNAL(newUdpPort(int)), this, SLOT(udpPort_changed(int)));
+ connect(audioOptions, SIGNAL(newUdpStereo(bool)), this, SLOT(udpStereo_changed(bool)));
+ connect(audioOptions, SIGNAL(newSquelchTriggered(bool)), this, SLOT(squelchTriggered_changed(bool)));
+ connect(audioOptions, SIGNAL(newRecMinTime(int)), this, SLOT(recMinTime_changed(int)));
+ connect(audioOptions, SIGNAL(newRecMaxGap(int)), this, SLOT(recMaxGap_changed(int)));
+ connect(audioOptions, SIGNAL(copyRecSettingsToAllVFOs()), this, SLOT(copyRecSettingsToAllVFOs_clicked()));
connect(ui->audioSpectrum, SIGNAL(pandapterRangeChanged(float,float)), audioOptions, SLOT(setPandapterSliderValues(float,float)));
@@ -159,6 +160,30 @@ bool DockAudio::getSquelchTriggered()
return squelch_triggered;
}
+void DockAudio::setSquelchTriggered(bool mode)
+{
+ squelch_triggered = mode;
+ audioOptions->setSquelchTriggered(mode);
+}
+
+void DockAudio::setRecDir(const QString &dir)
+{
+ rec_dir = dir;
+ audioOptions->setRecDir(dir);
+}
+
+void DockAudio::setRecMinTime(int time_ms)
+{
+ recMinTime = time_ms;
+ audioOptions->setRecMinTime(time_ms);
+}
+
+void DockAudio::setRecMaxGap(int time_ms)
+{
+ recMaxGap = time_ms;
+ audioOptions->setRecMaxGap(time_ms);
+}
+
/*! Public slot to set new RX frequency in Hz. */
void DockAudio::setRxFrequency(qint64 freq)
{
@@ -300,8 +325,6 @@ void DockAudio::saveSettings(QSettings *settings)
settings->beginGroup("audio");
- settings->setValue("gain", audioGain());
-
ival = audioOptions->getFftSplit();
if (ival != DEFAULT_FFT_SPLIT)
settings->setValue("fft_split", ival);
@@ -333,11 +356,6 @@ void DockAudio::saveSettings(QSettings *settings)
else
settings->remove("db_ranges_locked");
- if (rec_dir != QDir::homePath())
- settings->setValue("rec_dir", rec_dir);
- else
- settings->remove("rec_dir");
-
if (udp_host.isEmpty())
settings->remove("udp_host");
else
@@ -353,21 +371,6 @@ void DockAudio::saveSettings(QSettings *settings)
else
settings->remove("udp_stereo");
- if (squelch_triggered != false)
- settings->setValue("squelch_triggered_recording", squelch_triggered);
- else
- settings->remove("squelch_triggered_recording");
-
- if(recMinTime != 0)
- settings->setValue("rec_min_time", recMinTime);
- else
- settings->remove("rec_min_time");
-
- if(recMaxGap != 0)
- settings->setValue("rec_max_gap", recMaxGap);
- else
- settings->remove("rec_max_gap");
-
settings->endGroup();
}
@@ -381,10 +384,6 @@ void DockAudio::readSettings(QSettings *settings)
settings->beginGroup("audio");
- ival = settings->value("gain", QVariant(-60)).toInt(&conv_ok);
- if (conv_ok)
- setAudioGain(ival);
-
ival = settings->value("fft_split", DEFAULT_FFT_SPLIT).toInt(&conv_ok);
if (conv_ok)
audioOptions->setFftSplit(ival);
@@ -408,10 +407,6 @@ void DockAudio::readSettings(QSettings *settings)
bool_val = settings->value("db_ranges_locked", false).toBool();
audioOptions->setLockButtonState(bool_val);
- // Location of audio recordings
- rec_dir = settings->value("rec_dir", QDir::homePath()).toString();
- audioOptions->setRecDir(rec_dir);
-
// Audio streaming host, port and stereo setting
udp_host = settings->value("udp_host", "localhost").toString();
udp_port = settings->value("udp_port", 7355).toInt(&conv_ok);
@@ -423,27 +418,15 @@ void DockAudio::readSettings(QSettings *settings)
audioOptions->setUdpPort(udp_port);
audioOptions->setUdpStereo(udp_stereo);
- squelch_triggered = settings->value("squelch_triggered_recording", false).toBool();
- audioOptions->setSquelchTriggered(squelch_triggered);
-
- recMinTime = settings->value("rec_min_time", 0).toInt(&conv_ok);
- if (!conv_ok)
- recMinTime = 0;
- audioOptions->setRecMinTime(recMinTime);
- recMaxGap = settings->value("rec_max_gap", 0).toInt(&conv_ok);
- if (!conv_ok)
- recMaxGap = 0;
- audioOptions->setRecMaxGap(recMaxGap);
-
settings->endGroup();
}
-void DockAudio::setNewPandapterRange(int min, int max)
+void DockAudio::pandapterRange_changed(int min, int max)
{
ui->audioSpectrum->setPandapterRange(min, max);
}
-void DockAudio::setNewWaterfallRange(int min, int max)
+void DockAudio::waterfallRange_changed(int min, int max)
{
ui->audioSpectrum->setWaterfallRange(min, max);
}
@@ -451,14 +434,14 @@ void DockAudio::setNewWaterfallRange(int min, int max)
/*! \brief Slot called when a new valid recording directory has been selected
* in the audio conf dialog.
*/
-void DockAudio::setNewRecDir(const QString &dir)
+void DockAudio::recDir_changed(const QString &dir)
{
rec_dir = dir;
emit recDirChanged(dir);
}
/*! \brief Slot called when a new network host has been entered. */
-void DockAudio::setNewUdpHost(const QString &host)
+void DockAudio::udpHost_changed(const QString &host)
{
if (host.isEmpty())
udp_host = "localhost";
@@ -467,13 +450,13 @@ void DockAudio::setNewUdpHost(const QString &host)
}
/*! \brief Slot called when a new network port has been entered. */
-void DockAudio::setNewUdpPort(int port)
+void DockAudio::udpPort_changed(int port)
{
udp_port = port;
}
/*! \brief Slot called when the mono/stereo streaming setting changes. */
-void DockAudio::setNewUdpStereo(bool enabled)
+void DockAudio::udpStereo_changed(bool enabled)
{
udp_stereo = enabled;
}
@@ -498,20 +481,20 @@ void DockAudio::audioRecStopped()
}
-void DockAudio::setNewSquelchTriggered(bool enabled)
+void DockAudio::squelchTriggered_changed(bool enabled)
{
squelch_triggered = enabled;
ui->audioRecButton->setStyleSheet(enabled?"color: rgb(255,0,0)":"");
emit recSquelchTriggeredChanged(enabled);
}
-void DockAudio::setRecMinTime(int time_ms)
+void DockAudio::recMinTime_changed(int time_ms)
{
recMinTime = time_ms;
emit recMinTimeChanged(time_ms);
}
-void DockAudio::setRecMaxGap(int time_ms)
+void DockAudio::recMaxGap_changed(int time_ms)
{
recMaxGap = time_ms;
emit recMaxGapChanged(time_ms);
@@ -533,3 +516,8 @@ void DockAudio::increaseAudioGainShortcut() {
void DockAudio::decreaseAudioGainShortcut() {
ui->audioGainSlider->triggerAction(QSlider::SliderPageStepSub);
}
+
+void DockAudio::copyRecSettingsToAllVFOs_clicked()
+{
+ emit copyRecSettingsToAllVFOs();
+}
diff --git a/src/qtgui/dockaudio.h b/src/qtgui/dockaudio.h
index 6ae277c591..4e952ba2bb 100644
--- a/src/qtgui/dockaudio.h
+++ b/src/qtgui/dockaudio.h
@@ -66,6 +66,12 @@ class DockAudio : public QDockWidget
void setFftFill(bool enabled);
bool getSquelchTriggered();
+ void setSquelchTriggered(bool mode);
+ void setRecDir(const QString &dir);
+ void setRecMinTime(int time_ms);
+ void setRecMaxGap(int time_ms);
+
+
void saveSettings(QSettings *settings);
void readSettings(QSettings *settings);
@@ -116,6 +122,9 @@ public slots:
/*! \brief Signal emitted when squelch triggered recording max gap time is changed. */
void recMaxGapChanged(int time_ms);
+ /*! \brief Signal emitted when toAllVFOs button is clicked. */
+ void copyRecSettingsToAllVFOs();
+
private slots:
void on_audioGainSlider_valueChanged(int value);
void on_audioStreamButton_clicked(bool checked);
@@ -123,15 +132,16 @@ private slots:
void on_audioPlayButton_clicked(bool checked);
void on_audioConfButton_clicked();
void on_audioMuteButton_clicked(bool checked);
- void setNewPandapterRange(int min, int max);
- void setNewWaterfallRange(int min, int max);
- void setNewRecDir(const QString &dir);
- void setNewUdpHost(const QString &host);
- void setNewUdpPort(int port);
- void setNewUdpStereo(bool enabled);
- void setNewSquelchTriggered(bool enabled);
- void setRecMinTime(int time_ms);
- void setRecMaxGap(int time_ms);
+ void pandapterRange_changed(int min, int max);
+ void waterfallRange_changed(int min, int max);
+ void recDir_changed(const QString &dir);
+ void udpHost_changed(const QString &host);
+ void udpPort_changed(int port);
+ void udpStereo_changed(bool enabled);
+ void squelchTriggered_changed(bool enabled);
+ void recMinTime_changed(int time_ms);
+ void recMaxGap_changed(int time_ms);
+ void copyRecSettingsToAllVFOs_clicked();
private:
diff --git a/src/qtgui/dockbookmarks.cpp b/src/qtgui/dockbookmarks.cpp
index d7378ef9d2..63ff6135ce 100644
--- a/src/qtgui/dockbookmarks.cpp
+++ b/src/qtgui/dockbookmarks.cpp
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
#include "bookmarks.h"
#include "bookmarkstaglist.h"
@@ -51,10 +52,12 @@ DockBookmarks::DockBookmarks(QWidget *parent) :
ui->tableViewFrequencyList->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->tableViewFrequencyList->setSelectionMode(QAbstractItemView::SingleSelection);
ui->tableViewFrequencyList->installEventFilter(this);
+ ui->tableViewFrequencyList->setSortingEnabled(true);
+ ui->tableViewFrequencyList->sortByColumn(0, Qt::AscendingOrder);
// Demod Selection in Frequency List Table.
ComboBoxDelegateModulation* delegateModulation = new ComboBoxDelegateModulation(this);
- ui->tableViewFrequencyList->setItemDelegateForColumn(2, delegateModulation);
+ ui->tableViewFrequencyList->setItemDelegateForColumn(BookmarksTableModel::COL_MODULATION, delegateModulation);
// Bookmarks Context menu
contextmenu = new QMenu(this);
@@ -64,11 +67,35 @@ DockBookmarks::DockBookmarks(QWidget *parent) :
contextmenu->addAction(action);
connect(action, SIGNAL(triggered()), this, SLOT(DeleteSelectedBookmark()));
}
+ // MenuItem Tune
+ {
+ QAction* action = new QAction("Tune", this);
+ contextmenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(tuneHere()));
+ }
+ // MenuItem Tune and load
+ {
+ QAction* action = new QAction("Tune and load settings", this);
+ contextmenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(tuneAndLoad()));
+ }
+ // MenuItem New demodulator
+ {
+ QAction* action = new QAction("New demodulator", this);
+ contextmenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(newDemod()));
+ }
// MenuItem Add
{
actionAddBookmark = new QAction("Add Bookmark", this);
contextmenu->addAction(actionAddBookmark);
}
+ // MenuItem Select Columns
+ {
+ QAction* action = new QAction("Select columns...", this);
+ contextmenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(changeVisibleColumns()));
+ }
ui->tableViewFrequencyList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->tableViewFrequencyList, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(ShowContextMenu(const QPoint&)));
@@ -103,25 +130,36 @@ DockBookmarks::~DockBookmarks()
void DockBookmarks::activated(const QModelIndex & index)
{
- BookmarkInfo *info = bookmarksTableModel->getBookmarkAtRow(index.row());
- emit newBookmarkActivated(info->frequency, info->modulation, info->bandwidth);
+ bool activate = false;
+ if (index.column() == BookmarksTableModel::COL_NAME)
+ activate = true;
+ if (index.column() == BookmarksTableModel::COL_FREQUENCY)
+ activate = true;
+ if (activate)
+ {
+ BookmarkInfo *info = bookmarksTableModel->getBookmarkAtRow(index.row());
+ emit newBookmarkActivated(*info);
+ }
}
void DockBookmarks::setNewFrequency(qint64 rx_freq)
{
- ui->tableViewFrequencyList->clearSelection();
- const int iRowCount = bookmarksTableModel->rowCount();
- for (int row = 0; row < iRowCount; ++row)
+ m_currentFrequency = rx_freq;
+ BookmarkInfo bi;
+ bi.frequency = rx_freq;
+ const int iBookmarkIndex = Bookmarks::Get().find(bi);
+ if (iBookmarkIndex > 0)
{
- BookmarkInfo& info = *(bookmarksTableModel->getBookmarkAtRow(row));
- if (std::abs(rx_freq - info.frequency) <= ((info.bandwidth / 2 ) + 1))
+ int iRow = bookmarksTableModel->GetRowForBookmarkIndex(iBookmarkIndex);
+ if (iRow > 0)
{
- ui->tableViewFrequencyList->selectRow(row);
- ui->tableViewFrequencyList->scrollTo(ui->tableViewFrequencyList->currentIndex(), QAbstractItemView::EnsureVisible );
- break;
+ ui->tableViewFrequencyList->selectRow(iRow);
+ ui->tableViewFrequencyList->scrollTo(ui->tableViewFrequencyList->currentIndex(), QAbstractItemView::EnsureVisible);
+ return;
}
}
- m_currentFrequency = rx_freq;
+ ui->tableViewFrequencyList->clearSelection();
+ return;
}
void DockBookmarks::updateTags()
@@ -187,6 +225,36 @@ bool DockBookmarks::DeleteSelectedBookmark()
return true;
}
+bool DockBookmarks::tuneHere()
+{
+ QModelIndexList selected = ui->tableViewFrequencyList->selectionModel()->selectedRows();
+ if (selected.empty())
+ return true;
+ BookmarkInfo *info = bookmarksTableModel->getBookmarkAtRow(selected.first().row());
+ emit newBookmarkActivated(info->frequency);
+ return true;
+}
+
+bool DockBookmarks::tuneAndLoad()
+{
+ QModelIndexList selected = ui->tableViewFrequencyList->selectionModel()->selectedRows();
+ if (selected.empty())
+ return true;
+ BookmarkInfo *info = bookmarksTableModel->getBookmarkAtRow(selected.first().row());
+ emit newBookmarkActivated(*info);
+ return true;
+}
+
+bool DockBookmarks::newDemod()
+{
+ QModelIndexList selected = ui->tableViewFrequencyList->selectionModel()->selectedRows();
+ if (selected.empty())
+ return true;
+ BookmarkInfo *info = bookmarksTableModel->getBookmarkAtRow(selected.first().row());
+ emit newBookmarkActivatedAddDemod(*info);
+ return true;
+}
+
void DockBookmarks::ShowContextMenu(const QPoint& pos)
{
contextmenu->popup(ui->tableViewFrequencyList->viewport()->mapToGlobal(pos));
@@ -208,9 +276,9 @@ ComboBoxDelegateModulation::ComboBoxDelegateModulation(QObject *parent)
QWidget *ComboBoxDelegateModulation::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &index) const
{
QComboBox* comboBox = new QComboBox(parent);
- for (int i = 0; i < DockRxOpt::ModulationStrings.size(); ++i)
+ for (int i = 0; i < Modulations::Strings.size(); ++i)
{
- comboBox->addItem(DockRxOpt::ModulationStrings[i]);
+ comboBox->addItem(Modulations::Strings[i]);
}
setEditorData(comboBox, index);
return comboBox;
@@ -220,7 +288,7 @@ void ComboBoxDelegateModulation::setEditorData(QWidget *editor, const QModelInde
{
QComboBox *comboBox = static_cast(editor);
QString value = index.model()->data(index, Qt::EditRole).toString();
- int iModulation = DockRxOpt::GetEnumForModulationString(value);
+ int iModulation = Modulations::GetEnumForModulationString(value);
comboBox->setCurrentIndex(iModulation);
}
@@ -277,3 +345,68 @@ void DockBookmarks::changeBookmarkTags(int row, int /*column*/)
}
}
}
+
+void DockBookmarks::changeVisibleColumns()
+{
+ QDialog dialog(this);
+ dialog.setWindowTitle("Change Visible Columns");
+
+ QListWidget* colList = new QListWidget(&dialog);
+ QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
+ | QDialogButtonBox::Cancel);
+ connect(buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
+
+ QVBoxLayout *mainLayout = new QVBoxLayout(&dialog);
+ mainLayout->addWidget(colList);
+ mainLayout->addWidget(buttonBox);
+ for (int k = 0 ; k < BookmarksTableModel::COLUMN_COUNT ; k++)
+ {
+ QListWidgetItem* qi = new QListWidgetItem(ui->tableViewFrequencyList->model()->headerData(k, Qt::Horizontal, Qt::DisplayRole).toString(), colList, 0);
+ qi->setCheckState(ui->tableViewFrequencyList->isColumnHidden(k) ? Qt::Unchecked : Qt::Checked);
+ if (k <= BookmarksTableModel::COL_NAME)
+ qi->setFlags(qi->flags() & ~Qt::ItemIsEnabled);
+ colList->addItem(qi);
+ }
+
+ if (dialog.exec())
+ {
+ for (int k = 0 ; k < BookmarksTableModel::COLUMN_COUNT ; k++)
+ ui->tableViewFrequencyList->setColumnHidden(k, colList->item(k)->checkState() == Qt::Unchecked);
+ }
+}
+
+void DockBookmarks::saveSettings(QSettings *settings)
+{
+ QStringList list;
+ if (!settings)
+ return;
+
+ settings->beginGroup("bookmarks");
+
+ for (int k = 0 ; k < BookmarksTableModel::COLUMN_COUNT ; k++)
+ if (ui->tableViewFrequencyList->isColumnHidden(k))
+ list.append(ui->tableViewFrequencyList->model()->headerData(k, Qt::Horizontal, Qt::DisplayRole).toString());
+ if (list.size() > 0)
+ settings->setValue("hidden_columns", list.join(","));
+ else
+ settings->remove("hidden_columns");
+ settings->setValue("splitter_sizes",ui->splitter->saveState());
+ settings->endGroup();
+}
+
+void DockBookmarks::readSettings(QSettings *settings)
+{
+ if (!settings)
+ return;
+
+ settings->beginGroup("bookmarks");
+
+ QString strval = settings->value("hidden_columns", "").toString();
+ QStringList list = strval.split(",");
+ for (int k = 0 ; k < BookmarksTableModel::COLUMN_COUNT ; k++)
+ ui->tableViewFrequencyList->setColumnHidden(k, list.contains(ui->tableViewFrequencyList->model()->headerData(k, Qt::Horizontal, Qt::DisplayRole).toString()));
+
+ ui->splitter->restoreState(settings->value("splitter_sizes").toByteArray());
+ settings->endGroup();
+}
diff --git a/src/qtgui/dockbookmarks.h b/src/qtgui/dockbookmarks.h
index baf3818b0f..7d426b93d5 100644
--- a/src/qtgui/dockbookmarks.h
+++ b/src/qtgui/dockbookmarks.h
@@ -26,6 +26,9 @@
#include
#include "qtgui/bookmarkstablemodel.h"
#include
+#include
+#include "receivers/defines.h"
+#include "receivers/modulations.h"
namespace Ui {
class DockBookmarks;
@@ -65,9 +68,13 @@ class DockBookmarks : public QDockWidget
void updateTags();
void updateBookmarks();
void changeBookmarkTags(int row, int /*column*/);
+ void saveSettings(QSettings *settings);
+ void readSettings(QSettings *settings);
signals:
- void newBookmarkActivated(qint64, QString, int);
+ void newBookmarkActivated(BookmarkInfo& bookmark);
+ void newBookmarkActivated(qint64);
+ void newBookmarkActivatedAddDemod(BookmarkInfo& bookmark);
public slots:
void setNewFrequency(qint64 rx_freq);
@@ -80,5 +87,9 @@ private slots:
void on_tableWidgetTagList_itemChanged(QTableWidgetItem* item);
void ShowContextMenu(const QPoint&pos);
bool DeleteSelectedBookmark();
+ bool tuneHere();
+ bool tuneAndLoad();
+ bool newDemod();
void doubleClicked(const QModelIndex & index);
+ void changeVisibleColumns();
};
diff --git a/src/qtgui/dockinputctl.cpp b/src/qtgui/dockinputctl.cpp
index d2518859bf..d2f9859fc9 100644
--- a/src/qtgui/dockinputctl.cpp
+++ b/src/qtgui/dockinputctl.cpp
@@ -114,6 +114,10 @@ void DockInputCtl::readSettings(QSettings * settings)
bool_val = settings->value("gui/invert_scrolling", false).toBool();
emit invertScrollingChanged(bool_val);
ui->invertScrollingButton->setChecked(bool_val);
+
+ bool_val = settings->value("gui/auto_bookmarks", false).toBool();
+ emit autoBookmarksChanged(bool_val);
+ ui->autoBookmarksButton->setChecked(bool_val);
}
void DockInputCtl::saveSettings(QSettings * settings)
@@ -181,6 +185,12 @@ void DockInputCtl::saveSettings(QSettings * settings)
settings->setValue("gui/invert_scrolling", true);
else
settings->remove("gui/invert_scrolling");
+
+ // Remember state of auto bookmarks button. Default is unchecked.
+ if (ui->autoBookmarksButton->isChecked())
+ settings->setValue("gui/auto_bookmarks", true);
+ else
+ settings->remove("gui/auto_bookmarks");
}
void DockInputCtl::readLnbLoFromSettings(QSettings * settings)
@@ -524,6 +534,12 @@ void DockInputCtl::on_invertScrollingButton_toggled(bool checked)
emit invertScrollingChanged(checked);
}
+/** Auto bookmarks box has changed */
+void DockInputCtl::on_autoBookmarksButton_toggled(bool checked)
+{
+ emit autoBookmarksChanged(checked);
+}
+
/** Remove all widgets from the lists. */
void DockInputCtl::clearWidgets()
{
diff --git a/src/qtgui/dockinputctl.h b/src/qtgui/dockinputctl.h
index c9a8fb47a1..1d93b34b96 100644
--- a/src/qtgui/dockinputctl.h
+++ b/src/qtgui/dockinputctl.h
@@ -117,6 +117,7 @@ public slots:
void antennaSelected(QString antenna);
void freqCtrlResetChanged(bool enabled);
void invertScrollingChanged(bool enabled);
+ void autoBookmarksChanged(bool checked);
public slots:
void setLnbLo(double freq_mhz);
@@ -132,6 +133,7 @@ private slots:
void on_antSelector_currentIndexChanged(int index);
void on_freqCtrlResetButton_toggled(bool checked);
void on_invertScrollingButton_toggled(bool checked);
+ void on_autoBookmarksButton_toggled(bool checked);
void sliderValueChanged(int value);
diff --git a/src/qtgui/dockinputctl.ui b/src/qtgui/dockinputctl.ui
index ece2d7d4ab..3cbcba55d6 100644
--- a/src/qtgui/dockinputctl.ui
+++ b/src/qtgui/dockinputctl.ui
@@ -7,13 +7,13 @@
0
0
240
- 240
+ 299
240
- 240
+ 299
@@ -238,6 +238,16 @@
+ -
+
+
+ Automatically create demodulators when 'automatic' bookmark comes into bandwidth
+
+
+ Enable automatic demodulators
+
+
+
-
diff --git a/src/qtgui/dockrds.cpp b/src/qtgui/dockrds.cpp
index 1ef688a7a5..89177af13c 100644
--- a/src/qtgui/dockrds.cpp
+++ b/src/qtgui/dockrds.cpp
@@ -106,17 +106,13 @@ void DockRDS::ClearTextFields()
void DockRDS::showEnabled()
{
ClearTextFields();
- if (!ui->rdsCheckbox->isChecked())
- {
- ui->rdsCheckbox->blockSignals(true);
- ui->rdsCheckbox->setChecked(true);
- ui->rdsCheckbox->blockSignals(false);
- }
+ ui->rdsCheckbox->setChecked(true);
}
void DockRDS::showDisabled()
{
ClearTextFields();
+ ui->rdsCheckbox->setChecked(false);
}
void DockRDS::setDisabled()
@@ -133,7 +129,7 @@ void DockRDS::setEnabled()
}
/** Enable/disable RDS decoder */
-void DockRDS::on_rdsCheckbox_toggled(bool checked)
+void DockRDS::on_rdsCheckbox_clicked(bool checked)
{
emit rdsDecoderToggled(checked);
}
diff --git a/src/qtgui/dockrds.h b/src/qtgui/dockrds.h
index be995b5f3e..7a87a07347 100644
--- a/src/qtgui/dockrds.h
+++ b/src/qtgui/dockrds.h
@@ -32,7 +32,7 @@ public slots:
void rdsPI(QString text);
private slots:
- void on_rdsCheckbox_toggled(bool checked);
+ void on_rdsCheckbox_clicked(bool checked);
private:
Ui::DockRDS *ui; /*! The Qt designer UI file. */
diff --git a/src/qtgui/dockrxopt.cpp b/src/qtgui/dockrxopt.cpp
index da943604fb..f15d95c5c0 100644
--- a/src/qtgui/dockrxopt.cpp
+++ b/src/qtgui/dockrxopt.cpp
@@ -27,42 +27,6 @@
#include "dockrxopt.h"
#include "ui_dockrxopt.h"
-
-QStringList DockRxOpt::ModulationStrings;
-
-// Lookup table for conversion from old settings
-static const int old2new[] = {
- DockRxOpt::MODE_OFF,
- DockRxOpt::MODE_RAW,
- DockRxOpt::MODE_AM,
- DockRxOpt::MODE_NFM,
- DockRxOpt::MODE_WFM_MONO,
- DockRxOpt::MODE_WFM_STEREO,
- DockRxOpt::MODE_LSB,
- DockRxOpt::MODE_USB,
- DockRxOpt::MODE_CWL,
- DockRxOpt::MODE_CWU,
- DockRxOpt::MODE_WFM_STEREO_OIRT,
- DockRxOpt::MODE_AM_SYNC
-};
-
-// Filter preset table per mode, preset and lo/hi
-static const int filter_preset_table[DockRxOpt::MODE_LAST][3][2] =
-{ // WIDE NORMAL NARROW
- {{ 0, 0}, { 0, 0}, { 0, 0}}, // MODE_OFF
- {{ -15000, 15000}, { -5000, 5000}, { -1000, 1000}}, // MODE_RAW
- {{ -10000, 10000}, { -5000, 5000}, { -2500, 2500}}, // MODE_AM
- {{ -10000, 10000}, { -5000, 5000}, { -2500, 2500}}, // MODE_AMSYNC
- {{ -4000, -100}, { -2800, -100}, { -2400, -300}}, // MODE_LSB
- {{ 100, 4000}, { 100, 2800}, { 300, 2400}}, // MODE_USB
- {{ -1000, 1000}, { -250, 250}, { -100, 100}}, // MODE_CWL
- {{ -1000, 1000}, { -250, 250}, { -100, 100}}, // MODE_CWU
- {{ -10000, 10000}, { -5000, 5000}, { -2500, 2500}}, // MODE_NFM
- {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}}, // MODE_WFM_MONO
- {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}}, // MODE_WFM_STEREO
- {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}} // MODE_WFM_STEREO_OIRT
-};
-
DockRxOpt::DockRxOpt(qint64 filterOffsetRange, QWidget *parent) :
QDockWidget(parent),
ui(new Ui::DockRxOpt),
@@ -71,23 +35,35 @@ DockRxOpt::DockRxOpt(qint64 filterOffsetRange, QWidget *parent) :
{
ui->setupUi(this);
- if (ModulationStrings.size() == 0)
+ ui->modeSelector->addItems(Modulations::Strings);
+ freqLockButtonMenu = new QMenu(this);
+ // MenuItem Lock all
{
- // Keep in sync with rxopt_mode_idx and filter_preset_table
- ModulationStrings.append("Demod Off");
- ModulationStrings.append("Raw I/Q");
- ModulationStrings.append("AM");
- ModulationStrings.append("AM-Sync");
- ModulationStrings.append("LSB");
- ModulationStrings.append("USB");
- ModulationStrings.append("CW-L");
- ModulationStrings.append("CW-U");
- ModulationStrings.append("Narrow FM");
- ModulationStrings.append("WFM (mono)");
- ModulationStrings.append("WFM (stereo)");
- ModulationStrings.append("WFM (oirt)");
+ QAction* action = new QAction("Lock all", this);
+ freqLockButtonMenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(menuFreqLockAll()));
}
- ui->modeSelector->addItems(ModulationStrings);
+ // MenuItem Unlock all
+ {
+ QAction* action = new QAction("Unlock all", this);
+ freqLockButtonMenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(menuFreqUnlockAll()));
+ }
+ ui->freqLockButton->setContextMenuPolicy(Qt::CustomContextMenu);
+ squelchButtonMenu = new QMenu(this);
+ // MenuItem Auto all
+ {
+ QAction* action = new QAction("AUTO all", this);
+ squelchButtonMenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(menuSquelchAutoAll()));
+ }
+ // MenuItem Reset all
+ {
+ QAction* action = new QAction("Reset all", this);
+ squelchButtonMenu->addAction(action);
+ connect(action, SIGNAL(triggered()), this, SLOT(menuSquelchResetAll()));
+ }
+ ui->autoSquelchButton->setContextMenuPolicy(Qt::CustomContextMenu);
#ifdef Q_OS_LINUX
ui->modeButton->setMinimumSize(32, 24);
@@ -124,6 +100,8 @@ DockRxOpt::DockRxOpt(qint64 filterOffsetRange, QWidget *parent) :
connect(agcOpt, SIGNAL(attackChanged(int)), this, SLOT(agcOpt_attackChanged(int)));
connect(agcOpt, SIGNAL(decayChanged(int)), this, SLOT(agcOpt_decayChanged(int)));
connect(agcOpt, SIGNAL(hangChanged(int)), this, SLOT(agcOpt_hangChanged(int)));
+ connect(agcOpt, SIGNAL(panningChanged(int)), this, SLOT(agcOpt_panningChanged(int)));
+ connect(agcOpt, SIGNAL(panningAutoChanged(bool)), this, SLOT(agcOpt_panningAutoChanged(bool)));
// Noise blanker options
nbOpt = new CNbOptions(this);
@@ -248,19 +226,8 @@ void DockRxOpt::updateHwFreq()
*/
unsigned int DockRxOpt::filterIdxFromLoHi(int lo, int hi) const
{
- int mode_index = ui->modeSelector->currentIndex();
-
- if (lo == filter_preset_table[mode_index][FILTER_PRESET_WIDE][0] &&
- hi == filter_preset_table[mode_index][FILTER_PRESET_WIDE][1])
- return FILTER_PRESET_WIDE;
- else if (lo == filter_preset_table[mode_index][FILTER_PRESET_NORMAL][0] &&
- hi == filter_preset_table[mode_index][FILTER_PRESET_NORMAL][1])
- return FILTER_PRESET_NORMAL;
- else if (lo == filter_preset_table[mode_index][FILTER_PRESET_NARROW][0] &&
- hi == filter_preset_table[mode_index][FILTER_PRESET_NARROW][1])
- return FILTER_PRESET_NARROW;
-
- return FILTER_PRESET_USER;
+ Modulations::idx mode_index = Modulations::idx(ui->modeSelector->currentIndex());
+ return Modulations::FindFilterPreset(mode_index, lo, hi);
}
/**
@@ -318,9 +285,9 @@ int DockRxOpt::currentFilterShape() const
* @brief Select new demodulator.
* @param demod Demodulator index corresponding to receiver::demod.
*/
-void DockRxOpt::setCurrentDemod(int demod)
+void DockRxOpt::setCurrentDemod(Modulations::idx demod)
{
- if ((demod >= MODE_OFF) && (demod < MODE_LAST))
+ if ((demod >= Modulations::MODE_OFF) && (demod < Modulations::MODE_LAST))
{
ui->modeSelector->setCurrentIndex(demod);
updateDemodOptPage(demod);
@@ -331,14 +298,14 @@ void DockRxOpt::setCurrentDemod(int demod)
* @brief Get current demodulator selection.
* @return The current demodulator corresponding to receiver::demod.
*/
-int DockRxOpt::currentDemod() const
+Modulations::idx DockRxOpt::currentDemod() const
{
- return ui->modeSelector->currentIndex();
+ return Modulations::idx(ui->modeSelector->currentIndex());
}
QString DockRxOpt::currentDemodAsString()
{
- return GetStringForModulationIndex(currentDemod());
+ return Modulations::GetStringForModulationIndex(currentDemod());
}
float DockRxOpt::currentMaxdev() const
@@ -374,27 +341,14 @@ double DockRxOpt::currentSquelchLevel() const
return ui->sqlSpinBox->value();
}
-
-/** Get filter lo/hi for a given mode and preset */
-void DockRxOpt::getFilterPreset(int mode, int preset, int * lo, int * hi) const
+int DockRxOpt::getCwOffset() const
{
- if (mode < 0 || mode >= MODE_LAST)
- {
- qDebug() << __func__ << ": Invalid mode:" << mode;
- mode = MODE_AM;
- }
- else if (preset < 0 || preset > 2)
- {
- qDebug() << __func__ << ": Invalid preset:" << preset;
- preset = FILTER_PRESET_NORMAL;
- }
- *lo = filter_preset_table[mode][preset][0];
- *hi = filter_preset_table[mode][preset][1];
+ return demodOpt->getCwOffset();
}
-int DockRxOpt::getCwOffset() const
+void DockRxOpt::setCwOffset(int offset)
{
- return demodOpt->getCwOffset();
+ demodOpt->setCwOffset(offset);
}
/** Get agc settings */
@@ -403,14 +357,23 @@ bool DockRxOpt::getAgcOn()
return agc_is_on;
}
+void DockRxOpt::setAgcOn(bool on)
+{
+ if (on)
+ setAgcPresetFromParams(getAgcDecay());
+ else
+ ui->agcPresetCombo->setCurrentIndex(4);
+ agc_is_on = on;
+}
+
int DockRxOpt::getAgcTargetLevel()
{
return agcOpt->targetLevel();
}
-int DockRxOpt::getAgcManualGain()
+void DockRxOpt::setAgcTargetLevel(int level)
{
- return agcOpt->gain();
+ agcOpt->setTargetLevel(level);
}
int DockRxOpt::getAgcMaxGain()
@@ -418,193 +381,116 @@ int DockRxOpt::getAgcMaxGain()
return agcOpt->maxGain();
}
+void DockRxOpt::setAgcMaxGain(int gain)
+{
+ agcOpt->setMaxGain(gain);
+}
+
int DockRxOpt::getAgcAttack()
{
return agcOpt->attack();
}
+void DockRxOpt::setAgcAttack(int attack)
+{
+ agcOpt->setAttack(attack);
+}
+
int DockRxOpt::getAgcDecay()
{
return agcOpt->decay();
}
+void DockRxOpt::setAgcDecay(int decay)
+{
+ agcOpt->setDecay(decay);
+ setAgcOn(agc_is_on);
+}
+
int DockRxOpt::getAgcHang()
{
return agcOpt->hang();
}
-/** Read receiver configuration from settings data. */
-void DockRxOpt::readSettings(QSettings *settings)
+void DockRxOpt::setAgcHang(int hang)
{
- bool conv_ok;
- int int_val;
- double dbl_val;
-
- int_val = settings->value("receiver/cwoffset", 700).toInt(&conv_ok);
- if (conv_ok)
- demodOpt->setCwOffset(int_val);
-
- int_val = settings->value("receiver/fm_maxdev", 2500).toInt(&conv_ok);
- if (conv_ok)
- demodOpt->setMaxDev(int_val);
-
- dbl_val = settings->value("receiver/fm_deemph", 75).toDouble(&conv_ok);
- if (conv_ok && dbl_val >= 0)
- demodOpt->setEmph(1.0e-6 * dbl_val); // was stored as usec
-
- qint64 offs = settings->value("receiver/offset", 0).toInt(&conv_ok);
- if (offs)
- {
- setFilterOffset(offs);
- emit filterOffsetChanged(offs);
- }
-
- dbl_val = settings->value("receiver/sql_level", 1.0).toDouble(&conv_ok);
- if (conv_ok && dbl_val < 1.0)
- ui->sqlSpinBox->setValue(dbl_val);
-
- // AGC settings
- //TODO: cleanup config
- #if 0
- int_val = settings->value("receiver/agc_threshold", -100).toInt(&conv_ok);
- if (conv_ok)
- agcOpt->setThreshold(int_val);
- #endif
- int_val = settings->value("receiver/agc_target_level", 0).toInt(&conv_ok);
- if (conv_ok)
- agcOpt->setTargetLevel(int_val);
-
- //TODO: store/restore the preset correctly
- int_val = settings->value("receiver/agc_decay", 500).toInt(&conv_ok);
- if (conv_ok)
- {
- agcOpt->setDecay(int_val);
- if (int_val == 100)
- ui->agcPresetCombo->setCurrentIndex(0);
- else if (int_val == 500)
- ui->agcPresetCombo->setCurrentIndex(1);
- else if (int_val == 2000)
- ui->agcPresetCombo->setCurrentIndex(2);
- else
- ui->agcPresetCombo->setCurrentIndex(3);
- }
-
- int_val = settings->value("receiver/agc_attack", 20).toInt(&conv_ok);
- if (conv_ok)
- agcOpt->setAttack(int_val);
-
- int_val = settings->value("receiver/agc_hang", 0).toInt(&conv_ok);
- if (conv_ok)
- agcOpt->setHang(int_val);
-
- int_val = settings->value("receiver/agc_gain", 0).toInt(&conv_ok);
- if (conv_ok)
- agcOpt->setGain(int_val);
-
- int_val = settings->value("receiver/agc_maxgain", 100).toInt(&conv_ok);
- if (conv_ok)
- agcOpt->setMaxGain(int_val);
-
- if (settings->value("receiver/agc_off", false).toBool())
- ui->agcPresetCombo->setCurrentIndex(4);
-
- int_val = MODE_AM;
- if (settings->contains("receiver/demod")) {
- if (settings->value("configversion").toInt(&conv_ok) >= 3) {
- int_val = GetEnumForModulationString(settings->value("receiver/demod").toString());
- } else {
- int_val = old2new[settings->value("receiver/demod").toInt(&conv_ok)];
- }
- }
-
- setCurrentDemod(int_val);
- emit demodSelected(int_val);
-
+ agcOpt->setHang(hang);
}
-/** Save receiver configuration to settings. */
-void DockRxOpt::saveSettings(QSettings *settings)
+int DockRxOpt::getAgcPanning()
{
- int int_val;
-
- settings->setValue("receiver/demod", currentDemodAsString());
+ return agcOpt->panning();
+}
- int cwofs = demodOpt->getCwOffset();
- if (cwofs == 700)
- settings->remove("receiver/cwoffset");
- else
- settings->setValue("receiver/cwoffset", cwofs);
+void DockRxOpt::setAgcPanning(int panning)
+{
+ agcOpt->setPanning(panning);
+}
- // currently we do not need the decimal
- int_val = (int)demodOpt->getMaxDev();
- if (int_val == 2500)
- settings->remove("receiver/fm_maxdev");
- else
- settings->setValue("receiver/fm_maxdev", int_val);
+bool DockRxOpt::getAgcPanningAuto()
+{
+ return agcOpt->panningAuto();
+}
- // save as usec
- int_val = (int)(1.0e6 * demodOpt->getEmph());
- if (int_val == 75)
- settings->remove("receiver/fm_deemph");
- else
- settings->setValue("receiver/fm_deemph", int_val);
+void DockRxOpt::setAgcPanningAuto(bool panningAuto)
+{
+ agcOpt->setPanningAuto(panningAuto);
+}
- qint64 offs = ui->filterFreq->getFrequency();
- if (offs)
- settings->setValue("receiver/offset", offs);
+void DockRxOpt::setAgcPresetFromParams(int decay)
+{
+ if (decay == 100)
+ ui->agcPresetCombo->setCurrentIndex(0);
+ else if (decay == 500)
+ ui->agcPresetCombo->setCurrentIndex(1);
+ else if (decay == 2000)
+ ui->agcPresetCombo->setCurrentIndex(2);
else
- settings->remove("receiver/offset");
+ ui->agcPresetCombo->setCurrentIndex(3);
+}
- qDebug() << __func__ << "*** FIXME_ SQL on/off";
- //int sql_lvl = double(ui->sqlSlider->value()); // note: dBFS*10 as int
- double sql_lvl = ui->sqlSpinBox->value();
- if (sql_lvl > -150.0)
- settings->setValue("receiver/sql_level", sql_lvl);
- else
- settings->remove("receiver/sql_level");
+void DockRxOpt::setAmDcr(bool on)
+{
+ demodOpt->setAmDcr(on);
+}
- // AGC settings
- int_val = agcOpt->targetLevel();
- if (int_val != 0)
- settings->setValue("receiver/agc_target_level", int_val);
- else
- settings->remove("receiver/agc_target_level");
+void DockRxOpt::setAmSyncDcr(bool on)
+{
+ demodOpt->setAmSyncDcr(on);
+}
- int_val = agcOpt->attack();
- if (int_val != 20)
- settings->setValue("receiver/agc_attack", int_val);
- else
- settings->remove("receiver/agc_decay");
+void DockRxOpt::setAmSyncPllBw(float bw)
+{
+ demodOpt->setPllBw(bw);
+}
- int_val = agcOpt->decay();
- if (int_val != 500)
- settings->setValue("receiver/agc_decay", int_val);
- else
- settings->remove("receiver/agc_decay");
+void DockRxOpt::setFmMaxdev(float max_hz)
+{
+ demodOpt->setMaxDev(max_hz);
+}
- int_val = agcOpt->hang();
- if (int_val != 0)
- settings->setValue("receiver/agc_hang", int_val);
- else
- settings->remove("receiver/agc_hang");
+void DockRxOpt::setFmEmph(double tau)
+{
+ demodOpt->setEmph(tau);
+}
- int_val = agcOpt->gain();
- if (int_val != 0)
- settings->setValue("receiver/agc_gain", int_val);
+void DockRxOpt::setNoiseBlanker(int nbid, bool on, float threshold)
+{
+ if (nbid == 1)
+ ui->nb1Button->setChecked(on);
else
- settings->remove("receiver/agc_gain");
+ ui->nb2Button->setChecked(on);
+ nbOpt->setNbThreshold(nbid, threshold);
+}
- int_val = agcOpt->maxGain();
- if (int_val != 100)
- settings->setValue("receiver/agc_maxgain", int_val);
- else
- settings->remove("receiver/agc_maxgain");
+void DockRxOpt::setFreqLock(bool lock)
+{
+ ui->freqLockButton->setChecked(lock);
+}
- // AGC Off
- if (ui->agcPresetCombo->currentIndex() == 4)
- settings->setValue("receiver/agc_off", true);
- else
- settings->remove("receiver/agc_off");
+bool DockRxOpt::getFreqLock()
+{
+ return ui->freqLockButton->isChecked();
}
/** RX frequency changed through spin box */
@@ -663,7 +549,7 @@ void DockRxOpt::on_filterCombo_activated(int index)
qDebug() << "New filter preset:" << ui->filterCombo->currentText();
qDebug() << " shape:" << ui->filterShapeCombo->currentIndex();
- emit demodSelected(ui->modeSelector->currentIndex());
+ emit demodSelected(Modulations::idx(ui->modeSelector->currentIndex()));
}
/**
@@ -679,20 +565,20 @@ void DockRxOpt::on_filterCombo_activated(int index)
*/
void DockRxOpt::on_modeSelector_activated(int index)
{
- updateDemodOptPage(index);
- emit demodSelected(index);
+ updateDemodOptPage(Modulations::idx(index));
+ emit demodSelected(Modulations::idx(index));
}
-void DockRxOpt::updateDemodOptPage(int demod)
+void DockRxOpt::updateDemodOptPage(Modulations::idx demod)
{
// update demodulator option widget
- if (demod == MODE_NFM)
+ if (demod == Modulations::MODE_NFM)
demodOpt->setCurrentPage(CDemodOptions::PAGE_FM_OPT);
- else if (demod == MODE_AM)
+ else if (demod == Modulations::MODE_AM)
demodOpt->setCurrentPage(CDemodOptions::PAGE_AM_OPT);
- else if (demod == MODE_CWL || demod == MODE_CWU)
+ else if (demod == Modulations::MODE_CWL || demod == Modulations::MODE_CWU)
demodOpt->setCurrentPage(CDemodOptions::PAGE_CW_OPT);
- else if (demod == MODE_AM_SYNC)
+ else if (demod == Modulations::MODE_AM_SYNC)
demodOpt->setCurrentPage(CDemodOptions::PAGE_AMSYNC_OPT);
else
demodOpt->setCurrentPage(CDemodOptions::PAGE_NO_OPT);
@@ -717,7 +603,18 @@ void DockRxOpt::on_agcButton_clicked()
*/
void DockRxOpt::on_autoSquelchButton_clicked()
{
- double newval = sqlAutoClicked(); // FIXME: We rely on signal only being connected to one slot
+ double newval = sqlAutoClicked(false); // FIXME: We rely on signal only being connected to one slot
+ ui->sqlSpinBox->setValue(newval);
+}
+
+void DockRxOpt::on_autoSquelchButton_customContextMenuRequested(const QPoint& pos)
+{
+ squelchButtonMenu->popup(ui->autoSquelchButton->mapToGlobal(pos));
+}
+
+void DockRxOpt::menuSquelchAutoAll()
+{
+ double newval = sqlAutoClicked(true); // FIXME: We rely on signal only being connected to one slot
ui->sqlSpinBox->setValue(newval);
}
@@ -726,6 +623,12 @@ void DockRxOpt::on_resetSquelchButton_clicked()
ui->sqlSpinBox->setValue(-150.0);
}
+void DockRxOpt::menuSquelchResetAll()
+{
+ ui->sqlSpinBox->setValue(-150.0);
+ emit sqlResetAllClicked();
+}
+
/** AGC preset has changed. */
void DockRxOpt::on_agcPresetCombo_currentIndexChanged(int index)
{
@@ -813,6 +716,24 @@ void DockRxOpt::agcOpt_maxGainChanged(int gain)
emit agcMaxGainChanged(gain);
}
+/**
+ * @brief AGC panning changed.
+ * @param value The new relative panning position.
+ */
+void DockRxOpt::agcOpt_panningChanged(int value)
+{
+ emit agcPanningChanged(value);
+}
+
+/**
+ * @brief AGC panning auto mode changed.
+ * @param value The new auto mode state.
+ */
+void DockRxOpt::agcOpt_panningAutoChanged(bool value)
+{
+ emit agcPanningAuto(value);
+}
+
/**
* @brief Squelch level change.
* @param value The new squelch level in dB.
@@ -884,96 +805,88 @@ void DockRxOpt::on_nb2Button_toggled(bool checked)
emit noiseBlankerChanged(2, checked, (float) nbOpt->nbThreshold(2));
}
-/** Noise blanker threshold has been changed. */
-void DockRxOpt::nbOpt_thresholdChanged(int nbid, double value)
+void DockRxOpt::on_freqLockButton_clicked()
{
- if (nbid == 1)
- emit noiseBlankerChanged(nbid, ui->nb1Button->isChecked(), (float) value);
- else
- emit noiseBlankerChanged(nbid, ui->nb2Button->isChecked(), (float) value);
+ emit freqLock(ui->freqLockButton->isChecked(), false);
}
-void DockRxOpt::on_nbOptButton_clicked()
+void DockRxOpt::on_freqLockButton_customContextMenuRequested(const QPoint& pos)
{
- nbOpt->show();
+ freqLockButtonMenu->popup(ui->freqLockButton->mapToGlobal(pos));
}
-int DockRxOpt::GetEnumForModulationString(QString param)
+void DockRxOpt::menuFreqLockAll()
{
- int iModulation = -1;
- for(int i=0; ifreqLockButton->setChecked(true);
+}
+
+void DockRxOpt::menuFreqUnlockAll()
+{
+ emit freqLock(false, true);
+ ui->freqLockButton->setChecked(false);
}
-bool DockRxOpt::IsModulationValid(QString strModulation)
+/** Noise blanker threshold has been changed. */
+void DockRxOpt::nbOpt_thresholdChanged(int nbid, double value)
{
- return DockRxOpt::ModulationStrings.contains(strModulation, Qt::CaseInsensitive);
+ if (nbid == 1)
+ emit noiseBlankerChanged(nbid, ui->nb1Button->isChecked(), (float) value);
+ else
+ emit noiseBlankerChanged(nbid, ui->nb2Button->isChecked(), (float) value);
}
-QString DockRxOpt::GetStringForModulationIndex(int iModulationIndex)
+void DockRxOpt::on_nbOptButton_clicked()
{
- return ModulationStrings[iModulationIndex];
+ nbOpt->show();
}
void DockRxOpt::modeOffShortcut() {
- on_modeSelector_activated(MODE_OFF);
+ on_modeSelector_activated(Modulations::MODE_OFF);
}
void DockRxOpt::modeRawShortcut() {
- on_modeSelector_activated(MODE_RAW);
+ on_modeSelector_activated(Modulations::MODE_RAW);
}
void DockRxOpt::modeAMShortcut() {
- on_modeSelector_activated(MODE_AM);
+ on_modeSelector_activated(Modulations::MODE_AM);
}
void DockRxOpt::modeNFMShortcut() {
- on_modeSelector_activated(MODE_NFM);
+ on_modeSelector_activated(Modulations::MODE_NFM);
}
void DockRxOpt::modeWFMmonoShortcut() {
- on_modeSelector_activated(MODE_WFM_MONO);
+ on_modeSelector_activated(Modulations::MODE_WFM_MONO);
}
void DockRxOpt::modeWFMstereoShortcut() {
- on_modeSelector_activated(MODE_WFM_STEREO);
+ on_modeSelector_activated(Modulations::MODE_WFM_STEREO);
}
void DockRxOpt::modeLSBShortcut() {
- on_modeSelector_activated(MODE_LSB);
+ on_modeSelector_activated(Modulations::MODE_LSB);
}
void DockRxOpt::modeUSBShortcut() {
- on_modeSelector_activated(MODE_USB);
+ on_modeSelector_activated(Modulations::MODE_USB);
}
void DockRxOpt::modeCWLShortcut() {
- on_modeSelector_activated(MODE_CWL);
+ on_modeSelector_activated(Modulations::MODE_CWL);
}
void DockRxOpt::modeCWUShortcut() {
- on_modeSelector_activated(MODE_CWU);
+ on_modeSelector_activated(Modulations::MODE_CWU);
}
void DockRxOpt::modeWFMoirtShortcut() {
- on_modeSelector_activated(MODE_WFM_STEREO_OIRT);
+ on_modeSelector_activated(Modulations::MODE_WFM_STEREO_OIRT);
}
void DockRxOpt::modeAMsyncShortcut() {
- on_modeSelector_activated(MODE_AM_SYNC);
+ on_modeSelector_activated(Modulations::MODE_AM_SYNC);
}
void DockRxOpt::filterNarrowShortcut() {
diff --git a/src/qtgui/dockrxopt.h b/src/qtgui/dockrxopt.h
index 00f0aaa589..c7614aee25 100644
--- a/src/qtgui/dockrxopt.h
+++ b/src/qtgui/dockrxopt.h
@@ -25,14 +25,12 @@
#include
#include
+#include
#include "qtgui/agc_options.h"
#include "qtgui/demod_options.h"
#include "qtgui/nb_options.h"
-
-#define FILTER_PRESET_WIDE 0
-#define FILTER_PRESET_NORMAL 1
-#define FILTER_PRESET_NARROW 2
-#define FILTER_PRESET_USER 3
+#include "receivers/defines.h"
+#include "receivers/modulations.h"
namespace Ui {
class DockRxOpt;
@@ -56,35 +54,9 @@ class DockRxOpt : public QDockWidget
public:
- /**
- * Mode selector entries.
- *
- * @note If you change this enum, remember to update the TCP interface.
- * @note Keep in same order as the Strings in ModulationStrings, see
- * DockRxOpt.cpp constructor.
- */
- enum rxopt_mode_idx {
- MODE_OFF = 0, /*!< Demodulator completely off. */
- MODE_RAW = 1, /*!< Raw I/Q passthrough. */
- MODE_AM = 2, /*!< Amplitude modulation. */
- MODE_AM_SYNC = 3, /*!< Amplitude modulation (synchronous demod). */
- MODE_LSB = 4, /*!< Lower side band. */
- MODE_USB = 5, /*!< Upper side band. */
- MODE_CWL = 6, /*!< CW using LSB filter. */
- MODE_CWU = 7, /*!< CW using USB filter. */
- MODE_NFM = 8, /*!< Narrow band FM. */
- MODE_WFM_MONO = 9, /*!< Broadcast FM (mono). */
- MODE_WFM_STEREO = 10, /*!< Broadcast FM (stereo). */
- MODE_WFM_STEREO_OIRT = 11, /*!< Broadcast FM (stereo oirt). */
- MODE_LAST = 12
- };
-
explicit DockRxOpt(qint64 filterOffsetRange = 90000, QWidget *parent = 0);
~DockRxOpt();
- void readSettings(QSettings *settings);
- void saveSettings(QSettings *settings);
-
void setFilterOffsetRange(qint64 range_hz);
void setFilterParam(int lo, int hi);
@@ -100,40 +72,54 @@ class DockRxOpt : public QDockWidget
void setResetLowerDigits(bool enabled);
void setInvertScrolling(bool enabled);
- int currentDemod() const;
+ Modulations::idx currentDemod() const;
QString currentDemodAsString();
float currentMaxdev() const;
double currentEmph() const;
double currentSquelchLevel() const;
- void getFilterPreset(int mode, int preset, int * lo, int * hi) const;
int getCwOffset() const;
+ void setCwOffset(int offset);
double getSqlLevel(void) const;
bool getAgcOn();
+ void setAgcOn(bool on);
int getAgcTargetLevel();
- int getAgcManualGain();
+ void setAgcTargetLevel(int level);
int getAgcMaxGain();
+ void setAgcMaxGain(int gain);
int getAgcAttack();
+ void setAgcAttack(int attack);
int getAgcDecay();
+ void setAgcDecay(int decay);
int getAgcHang();
-
- static QStringList ModulationStrings;
- static QString GetStringForModulationIndex(int iModulationIndex);
- static int GetEnumForModulationString(QString param);
- static bool IsModulationValid(QString strModulation);
+ void setAgcHang(int hang);
+ int getAgcPanning();
+ void setAgcPanning(int panning);
+ bool getAgcPanningAuto();
+ void setAgcPanningAuto(bool panningAuto);
+
+ void setAmDcr(bool on);
+ void setAmSyncDcr(bool on);
+ void setAmSyncPllBw(float bw);
+ void setFmMaxdev(float max_hz);
+ void setFmEmph(double tau);
+ void setNoiseBlanker(int nbid, bool on, float threshold);
+
+ void setFreqLock(bool lock);
+ bool getFreqLock();
public slots:
void setRxFreq(qint64 freq_hz);
- void setCurrentDemod(int demod);
+ void setCurrentDemod(Modulations::idx demod);
void setFilterOffset(qint64 freq_hz);
void setSquelchLevel(double level);
private:
void updateHwFreq();
- void updateDemodOptPage(int demod);
+ void updateDemodOptPage(Modulations::idx demod);
unsigned int filterIdxFromLoHi(int lo, int hi) const;
void modeOffShortcut();
@@ -151,6 +137,7 @@ public slots:
void filterNarrowShortcut();
void filterNormalShortcut();
void filterWideShortcut();
+ void setAgcPresetFromParams(int decay);
signals:
/** Signal emitted when receiver frequency has changed */
@@ -160,7 +147,7 @@ public slots:
void filterOffsetChanged(qint64 freq_hz);
/** Signal emitted when new demodulator is selected. */
- void demodSelected(int demod);
+ void demodSelected(Modulations::idx demod);
/** Signal emitted when new FM deviation is selected. */
void fmMaxdevSelected(float max_dev);
@@ -188,7 +175,10 @@ public slots:
*
* @note Need current signal/noise level returned
*/
- double sqlAutoClicked();
+ double sqlAutoClicked(bool global);
+
+ /** Signal emitted when squelch reset all popup menu item is clicked. */
+ void sqlResetAllClicked();
/** Signal emitted when AGC is togglen ON/OFF. */
void agcToggled(bool agc_on);
@@ -211,9 +201,18 @@ public slots:
/** Signal emitted when AGC hang is changed. Hang is in millisec.*/
void agcHangChanged(int hang);
+ /** Signal emitted when AGC panning is changed. Panning is relative position -100...100 */
+ void agcPanningChanged(int panning);
+
+ /** Signal emitted when AGC panning auto mode is changed. */
+ void agcPanningAuto(bool panningAuto);
+
/** Signal emitted when noise blanker status has changed. */
void noiseBlankerChanged(int nbid, bool on, float threshold);
+ /** Signal emitted when freq lock mode changed. */
+ void freqLock(bool lock, bool all);
+
void cwOffsetChanged(int offset);
private slots:
@@ -224,13 +223,20 @@ private slots:
void on_modeButton_clicked();
void on_agcButton_clicked();
void on_autoSquelchButton_clicked();
+ void on_autoSquelchButton_customContextMenuRequested(const QPoint& pos);
+ void menuSquelchAutoAll();
void on_resetSquelchButton_clicked();
+ void menuSquelchResetAll();
//void on_agcPresetCombo_activated(int index);
void on_agcPresetCombo_currentIndexChanged(int index);
void on_sqlSpinBox_valueChanged(double value);
void on_nb1Button_toggled(bool checked);
void on_nb2Button_toggled(bool checked);
void on_nbOptButton_clicked();
+ void on_freqLockButton_clicked();
+ void on_freqLockButton_customContextMenuRequested(const QPoint& pos);
+ void menuFreqLockAll();
+ void menuFreqUnlockAll();
// Signals coming from noise blanker pop-up
void nbOpt_thresholdChanged(int nbid, double value);
@@ -250,12 +256,16 @@ private slots:
void agcOpt_attackChanged(int value);
void agcOpt_decayChanged(int value);
void agcOpt_hangChanged(int value);
+ void agcOpt_panningChanged(int value);
+ void agcOpt_panningAutoChanged(bool value);
private:
Ui::DockRxOpt *ui; /** The Qt designer UI file. */
CDemodOptions *demodOpt; /** Demodulator options. */
CAgcOptions *agcOpt; /** AGC options. */
CNbOptions *nbOpt; /** Noise blanker options. */
+ QMenu *freqLockButtonMenu;
+ QMenu *squelchButtonMenu;
bool agc_is_on;
diff --git a/src/qtgui/dockrxopt.ui b/src/qtgui/dockrxopt.ui
index 7d6c91e11c..8fafddaaa2 100644
--- a/src/qtgui/dockrxopt.ui
+++ b/src/qtgui/dockrxopt.ui
@@ -6,8 +6,8 @@
0
0
- 280
- 330
+ 295
+ 355
@@ -18,8 +18,8 @@
- 280
- 330
+ 295
+ 355
@@ -588,6 +588,12 @@ This is an offset from the hardware RF frequency.</p></body></htm
-
+
+
+ 0
+ 0
+
+
Receiver frequency
@@ -597,6 +603,9 @@ This is an offset from the hardware RF frequency.</p></body></htm
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+ kHz
+
3
@@ -611,13 +620,6 @@ This is an offset from the hardware RF frequency.</p></body></htm
- -
-
-
- kHz
-
-
-
-
@@ -736,6 +738,53 @@ This is an offset from the hardware RF frequency.</p></body></htm
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 30
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Demodulator options
+
+
+ Mode options
+
+
+
+
+
+
+ :/icons/icons/lock.svg:/icons/icons/lock.svg
+
+
+
+ 16
+ 16
+
+
+
+ true
+
+
+ false
+
+
+
-
diff --git a/src/qtgui/nb_options.cpp b/src/qtgui/nb_options.cpp
index e6d037a9b6..029d3cae1f 100644
--- a/src/qtgui/nb_options.cpp
+++ b/src/qtgui/nb_options.cpp
@@ -54,6 +54,13 @@ double CNbOptions::nbThreshold(int nbid)
else
return ui->nb2Threshold->value();
}
+void CNbOptions::setNbThreshold(int nbid, double threshold)
+{
+ if (nbid == 1)
+ ui->nb1Threshold->setValue(threshold);
+ else
+ ui->nb2Threshold->setValue(threshold);
+}
void CNbOptions::on_nb1Threshold_valueChanged(double val)
{
diff --git a/src/qtgui/nb_options.h b/src/qtgui/nb_options.h
index 4a965a7fd0..61dc8508e0 100644
--- a/src/qtgui/nb_options.h
+++ b/src/qtgui/nb_options.h
@@ -41,6 +41,7 @@ class CNbOptions : public QDialog
void closeEvent(QCloseEvent *event);
double nbThreshold(int nbid);
+ void setNbThreshold(int nbid, double threshold);
signals:
void thresholdChanged(int nb, double val);
diff --git a/src/qtgui/plotter.cpp b/src/qtgui/plotter.cpp
index b7613fbc5f..4ed3e19dd2 100644
--- a/src/qtgui/plotter.cpp
+++ b/src/qtgui/plotter.cpp
@@ -37,7 +37,6 @@
#include
#include "plotter.h"
#include "bandplan.h"
-#include "bookmarks.h"
#include "dxc_spots.h"
Q_LOGGING_CATEGORY(plotter, "plotter")
@@ -161,6 +160,10 @@ CPlotter::CPlotter(QWidget *parent) : QFrame(parent)
wf_span = 0;
fft_rate = 15;
memset(m_wfbuf, 255, MAX_SCREENSIZE);
+ m_currentVfo = 0;
+ m_capturedVfo = 0;
+ m_lookup_vfo = vfo::make();
+ m_lookup_vfo->set_index(0);
}
CPlotter::~CPlotter()
@@ -199,6 +202,19 @@ void CPlotter::mouseMoveEvent(QMouseEvent* event)
}
}
}
+ if (!m_vfos.empty())
+ {
+ m_lookup_vfo->set_offset(freqFromX(pt.x()) - m_CenterFreq);
+ m_vfos_lb = m_vfos.lower_bound(m_lookup_vfo);
+ if (m_vfos_lb == m_vfos.end())
+ m_vfos_ub = --m_vfos_lb;
+ else
+ {
+ m_vfos_ub = m_vfos_lb--;
+ if(m_vfos_ub == m_vfos.begin())
+ m_vfos_lb = m_vfos_ub;
+ }
+ }
// if no mouse button monitor grab regions and change cursor icon
if (onTag)
{
@@ -226,9 +242,13 @@ void CPlotter::mouseMoveEvent(QMouseEvent* event)
// in move demod box center frequency region
if (CENTER != m_CursorCaptured)
setCursor(QCursor(Qt::SizeHorCursor));
+ m_capturedVfo = m_currentVfo;
m_CursorCaptured = CENTER;
if (m_TooltipsEnabled)
- showToolTip(event, QString("Demod: %1 kHz").arg(m_DemodCenterFreq/1.e3, 0, 'f', 3));
+ showToolTip(event,
+ QString("Current demod %1: %2 kHz")
+ .arg(m_currentVfo)
+ .arg(m_DemodCenterFreq/1.e3, 0, 'f', 3));
}
else if (isPointCloseTo(pt.x(), m_DemodHiCutFreqX, m_CursorCaptureDelta))
{
@@ -248,6 +268,30 @@ void CPlotter::mouseMoveEvent(QMouseEvent* event)
if (m_TooltipsEnabled)
showToolTip(event, QString("Low cut: %1 Hz").arg(m_DemodLowCutFreq));
}
+ else if (!m_vfos.empty() && isPointCloseTo(pt.x(), xFromFreq((*m_vfos_lb)->get_offset() + m_CenterFreq), m_CursorCaptureDelta))
+ {
+ if (CENTER != m_CursorCaptured)
+ setCursor(QCursor(Qt::SizeHorCursor));
+ m_CursorCaptured = CENTER;
+ m_capturedVfo = (*m_vfos_lb)->get_index();
+ if (m_TooltipsEnabled)
+ showToolTip(event,
+ QString("Demod %1: %2 kHz")
+ .arg((*m_vfos_lb)->get_index())
+ .arg(((*m_vfos_lb)->get_offset() + m_CenterFreq)/1.e3, 0, 'f', 3));
+ }
+ else if (!m_vfos.empty() && isPointCloseTo(pt.x(), xFromFreq((*m_vfos_ub)->get_offset() + m_CenterFreq), m_CursorCaptureDelta))
+ {
+ if (CENTER != m_CursorCaptured)
+ setCursor(QCursor(Qt::SizeHorCursor));
+ m_CursorCaptured = CENTER;
+ m_capturedVfo = (*m_vfos_ub)->get_index();
+ if (m_TooltipsEnabled)
+ showToolTip(event,
+ QString("Demod %1: %2 kHz")
+ .arg((*m_vfos_ub)->get_index())
+ .arg(((*m_vfos_ub)->get_offset() + m_CenterFreq)/1.e3, 0, 'f', 3));
+ }
else
{ //if not near any grab boundaries
if (NOCAP != m_CursorCaptured)
@@ -578,6 +622,35 @@ bool CPlotter::saveWaterfall(const QString & filename) const
return pixmap.save(filename, nullptr, -1);
}
+void CPlotter::setCurrentVfo(int current)
+{
+ m_currentVfo = current;
+ updateOverlay();
+}
+
+void CPlotter::addVfo(vfo::sptr n_vfo)
+{
+ m_vfos.insert(n_vfo);
+}
+
+void CPlotter::removeVfo(vfo::sptr n_vfo)
+{
+ m_vfos.erase(n_vfo);
+}
+
+void CPlotter::clearVfos()
+{
+ m_vfos.clear();
+}
+
+void CPlotter::getLockedVfos(std::vector &to)
+{
+ to.clear();
+ for (auto& cvfo : m_vfos)
+ if (cvfo->get_freq_lock())
+ to.push_back(cvfo);
+}
+
/** Get waterfall time resolution in milleconds / line. */
quint64 CPlotter::getWfTimeRes() const
{
@@ -675,12 +748,35 @@ void CPlotter::mousePressEvent(QMouseEvent * event)
{
if (tag.first.contains(event->pos()))
{
- m_DemodCenterFreq = tag.second;
- emit newDemodFreq(m_DemodCenterFreq, m_DemodCenterFreq - m_CenterFreq);
- break;
+ if (event->buttons() == Qt::LeftButton)
+ {
+ //just tune
+ m_DemodCenterFreq = tag.second;
+ emit newDemodFreq(m_DemodCenterFreq, m_DemodCenterFreq - m_CenterFreq);
+ break;
+ }
+ else if (event->buttons() == Qt::MiddleButton)
+ {
+ //tune and load settings
+ m_DemodCenterFreq = tag.second;
+ emit newDemodFreqAdd(m_DemodCenterFreq, m_DemodCenterFreq - m_CenterFreq);
+ break;
+ }
+ else if (event->buttons() == Qt::RightButton)
+ {
+ //new demod here
+ m_DemodCenterFreq = tag.second;
+ emit newDemodFreqLoad(m_DemodCenterFreq, m_DemodCenterFreq - m_CenterFreq);
+ break;
+ }
}
}
}
+ else if (m_CursorCaptured == CENTER)
+ {
+ if (m_currentVfo != m_capturedVfo)
+ emit selectVfo(m_capturedVfo);
+ }
}
}
@@ -1298,7 +1394,7 @@ void CPlotter::drawOverlay()
static const int fontHeight = fm.ascent() + 1;
static const int slant = 5;
static const int levelHeight = fontHeight + 5;
- static const int nLevels = h / (levelHeight + slant);
+ static const int nLevels = h / (levelHeight + slant) + 1;
if (m_BookmarksEnabled)
{
tags = Bookmarks::Get().getBookmarksInRange(m_CenterFreq + m_FftCenter - m_Span / 2,
@@ -1329,13 +1425,13 @@ void CPlotter::drawOverlay()
x = xFromFreq(tag.frequency);
int nameWidth = fm.boundingRect(tag.name).width();
- int level = 0;
+ int level = 1;
while(level < nLevels && tagEnd[level] > x)
level++;
if(level >= nLevels)
{
- level = 0;
+ level = 1;
if (tagEnd[level] > x)
continue; // no overwrite at level 0
}
@@ -1480,19 +1576,23 @@ void CPlotter::drawOverlay()
// Draw demod filter box
if (m_FilterBoxEnabled)
{
+ for(auto &vfoc : m_vfos)
+ {
+ const qint64 vfoFreq = m_CenterFreq + qint64(vfoc->get_offset());
+ const int demodFreqX = xFromFreq(vfoFreq);
+ const int demodLowCutFreqX = xFromFreq(vfoFreq + qint64(vfoc->get_filter_low()));
+ const int demodHiCutFreqX = xFromFreq(vfoFreq + qint64(vfoc->get_filter_high()));
+
+ const int dw = demodHiCutFreqX - demodLowCutFreqX;
+ drawVfo(painter, demodFreqX, demodLowCutFreqX, dw, h, vfoc->get_index(), false);
+ }
+
m_DemodFreqX = xFromFreq(m_DemodCenterFreq);
m_DemodLowCutFreqX = xFromFreq(m_DemodCenterFreq + m_DemodLowCutFreq);
m_DemodHiCutFreqX = xFromFreq(m_DemodCenterFreq + m_DemodHiCutFreq);
int dw = m_DemodHiCutFreqX - m_DemodLowCutFreqX;
-
- painter.setOpacity(0.3);
- painter.fillRect(m_DemodLowCutFreqX, 0, dw, h,
- QColor(PLOTTER_FILTER_BOX_COLOR));
-
- painter.setOpacity(1.0);
- painter.setPen(QColor(PLOTTER_FILTER_LINE_COLOR));
- painter.drawLine(m_DemodFreqX, 0, m_DemodFreqX, h);
+ drawVfo(painter, m_DemodFreqX, m_DemodLowCutFreqX, dw, h, m_currentVfo, true);
}
if (!m_Running)
@@ -1508,6 +1608,22 @@ void CPlotter::drawOverlay()
painter.end();
}
+void CPlotter::drawVfo(QPainter &painter, const int demodFreqX, const int demodLowCutFreqX, const int dw, const int h, const int index, const bool is_selected)
+{
+ QFontMetrics metrics(m_Font);
+ QRect br = metrics.boundingRect("+256+");
+ painter.setOpacity(0.3);
+ painter.fillRect(demodLowCutFreqX, br.height(), dw, h,
+ QColor(PLOTTER_FILTER_BOX_COLOR));
+
+ painter.setOpacity(1.0);
+ painter.setPen(QColor(is_selected ? PLOTTER_FILTER_LINE_COLOR : PLOTTER_TEXT_COLOR));
+ painter.drawLine(demodFreqX, br.height(), demodFreqX, h);
+ painter.drawText(demodFreqX - br.width() / 2, 0, br.width(), br.height(),
+ Qt::AlignVCenter | Qt::AlignHCenter,
+ QString::number(index));
+}
+
// Create frequency division strings based on start frequency, span frequency,
// and frequency units.
// Places in QString array m_HDivText
diff --git a/src/qtgui/plotter.h b/src/qtgui/plotter.h
index ebb3cdbec9..3c020d9c0a 100644
--- a/src/qtgui/plotter.h
+++ b/src/qtgui/plotter.h
@@ -7,7 +7,11 @@
#include
#include
#include
+#include
#include
+#include "bookmarks.h"
+#include "receivers/defines.h"
+#include "receivers/vfo.h"
#define HORZ_DIVS_MAX 12 //50
#define VERT_DIVS_MIN 5
@@ -88,7 +92,7 @@ class CPlotter : public QFrame
m_Span = (qint32)s;
setFftCenterFreq(m_FftCenter);
}
- drawOverlay();
+ updateOverlay();
}
void setHdivDelta(int delta) { m_HdivDelta = delta; }
@@ -122,15 +126,23 @@ class CPlotter : public QFrame
void setFftRate(int rate_hz);
void clearWaterfall();
bool saveWaterfall(const QString & filename) const;
+ void setCurrentVfo(int current);
+ void addVfo(vfo::sptr n_vfo);
+ void removeVfo(vfo::sptr n_vfo);
+ void clearVfos();
+ void getLockedVfos(std::vector &to);
signals:
void newDemodFreq(qint64 freq, qint64 delta); /* delta is the offset from the center */
+ void newDemodFreqLoad(qint64 freq, qint64 delta);/* tune and load demodulator settings */
+ void newDemodFreqAdd(qint64 freq, qint64 delta);/* new demodulator here */
void newLowCutFreq(int f);
void newHighCutFreq(int f);
void newFilterFreq(int low, int high); /* substitute for NewLow / NewHigh */
void pandapterRangeChanged(float min, float max);
void newZoomLevel(float level);
void newSize();
+ void selectVfo(int);
public slots:
// zoom functions
@@ -179,6 +191,9 @@ public slots:
};
void drawOverlay();
+ void drawVfo(QPainter &painter, const int demodFreqX,
+ const int demodLowCutFreqX, const int dw, const int h,
+ const int index, const bool is_selected);
void makeFrequencyStrs();
int xFromFreq(qint64 freq);
qint64 freqFromX(int x);
@@ -279,6 +294,12 @@ public slots:
QMap m_Peaks;
QList< QPair > m_Taglist;
+ vfo::set m_vfos;
+ vfo::set::iterator m_vfos_ub;
+ vfo::set::iterator m_vfos_lb;
+ vfo::sptr m_lookup_vfo;
+ int m_currentVfo;
+ int m_capturedVfo;
// Waterfall averaging
quint64 tlast_wf_ms; // last time waterfall has been updated
diff --git a/src/receivers/CMakeLists.txt b/src/receivers/CMakeLists.txt
index 65c9dc97f0..8bc9236712 100644
--- a/src/receivers/CMakeLists.txt
+++ b/src/receivers/CMakeLists.txt
@@ -7,4 +7,9 @@ add_source_files(SRCS_LIST
receiver_base.h
wfmrx.cpp
wfmrx.h
+ defines.h
+ modulations.h
+ modulations.cpp
+ vfo.h
+ vfo.cpp
)
diff --git a/src/receivers/defines.h b/src/receivers/defines.h
new file mode 100644
index 0000000000..88ea53c3f1
--- /dev/null
+++ b/src/receivers/defines.h
@@ -0,0 +1,47 @@
+/* -*- c++ -*- */
+/*
+ * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
+ * https://gqrx.dk/
+ *
+ * Copyright 2011-2016 Alexandru Csete OZ9AEC.
+ *
+ * Gqrx 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, or (at your option)
+ * any later version.
+ *
+ * Gqrx 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 Gqrx; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef DEFINES_H
+#define DEFINES_H
+
+/* Maximum number of receivers */
+#define RX_MAX 256
+
+
+#define TARGET_QUAD_RATE 1e6
+
+/* Number of noice blankers */
+#define RECEIVER_NB_COUNT 2
+
+// NB: Remember to adjust filter ranges in MainWindow
+#define NB_PREF_QUAD_RATE 96000.f
+
+#define WFM_PREF_QUAD_RATE 240e3 // Nominal channel spacing is 200 kHz
+
+#define RX_FILTER_MIN_WIDTH 100 /*! Minimum width of filter */
+
+#include
+#include
+#include
+
+
+#endif // DEFINES_H
diff --git a/src/receivers/modulations.cpp b/src/receivers/modulations.cpp
new file mode 100644
index 0000000000..f643a5e9a6
--- /dev/null
+++ b/src/receivers/modulations.cpp
@@ -0,0 +1,295 @@
+/* -*- c++ -*- */
+/*
+ * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
+ * https://gqrx.dk/
+ *
+ * Copyright 2011-2016 Alexandru Csete OZ9AEC.
+ * Copyright 2022 vladisslav2011@gmail.com.
+ *
+ * Gqrx 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, or (at your option)
+ * any later version.
+ *
+ * Gqrx 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 Gqrx; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "receivers/defines.h"
+#include "receivers/modulations.h"
+
+class ModulationsInitializer:public Modulations
+{
+public:
+ ModulationsInitializer():Modulations()
+ {
+ }
+ ~ModulationsInitializer()
+ {
+ }
+};
+
+QStringList Modulations::Strings;
+static ModulationsInitializer modulations = ModulationsInitializer();
+// Lookup table for conversion from old settings
+static const Modulations::idx old2new[] = {
+ Modulations::MODE_OFF,
+ Modulations::MODE_RAW,
+ Modulations::MODE_AM,
+ Modulations::MODE_NFM,
+ Modulations::MODE_WFM_MONO,
+ Modulations::MODE_WFM_STEREO,
+ Modulations::MODE_LSB,
+ Modulations::MODE_USB,
+ Modulations::MODE_CWL,
+ Modulations::MODE_CWU,
+ Modulations::MODE_WFM_STEREO_OIRT,
+ Modulations::MODE_AM_SYNC
+};
+
+// Filter preset table per mode, preset and lo/hi
+static const int filter_preset_table[Modulations::MODE_LAST][3][2] =
+{ // WIDE NORMAL NARROW
+ {{ 0, 0}, { 0, 0}, { 0, 0}}, // MODE_OFF
+ {{ -15000, 15000}, { -5000, 5000}, { -1000, 1000}}, // MODE_RAW
+ {{ -10000, 10000}, { -5000, 5000}, { -2500, 2500}}, // MODE_AM
+ {{ -10000, 10000}, { -5000, 5000}, { -2500, 2500}}, // MODE_AMSYNC
+ {{ -4000, -100}, { -2800, -100}, { -2400, -300}}, // MODE_LSB
+ {{ 100, 4000}, { 100, 2800}, { 300, 2400}}, // MODE_USB
+ {{ -1000, 1000}, { -250, 250}, { -100, 100}}, // MODE_CWL
+ {{ -1000, 1000}, { -250, 250}, { -100, 100}}, // MODE_CWU
+ {{ -10000, 10000}, { -5000, 5000}, { -2500, 2500}}, // MODE_NFM
+ {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}}, // MODE_WFM_MONO
+ {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}}, // MODE_WFM_STEREO
+ {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}} // MODE_WFM_STEREO_OIRT
+};
+
+// Filter ranges table per mode
+static const int filter_ranges_table[Modulations::MODE_LAST][2][2] =
+{ //LOW MIN MAX HIGH MIN MAX
+ {{ 0, 0}, { 0, 0}}, // MODE_OFF
+ {{ -40000, -200}, { 200, 40000}}, // MODE_RAW
+ {{ -40000, -200}, { 200, 40000}}, // MODE_AM
+ {{ -40000, -200}, { 200, 40000}}, // MODE_AMSYNC
+ {{ -40000, -100}, { -5000, 0}}, // MODE_LSB
+ {{ 0, 5000}, { 100, 40000}}, // MODE_USB
+ {{ -5000, -100}, { 100, 5000}}, // MODE_CWL
+ {{ -5000, -100}, { 100, 5000}}, // MODE_CWU
+ {{ -40000, -200}, { 200, 40000}}, // MODE_NFM
+ {{-120000, -10000}, { 10000,120000}}, // MODE_WFM_MONO
+ {{-120000, -10000}, { 10000,120000}}, // MODE_WFM_STEREO
+ {{-120000, -10000}, { 10000,120000}} // MODE_WFM_STEREO_OIRT
+};
+
+
+
+QString Modulations::GetStringForModulationIndex(int iModulationIndex)
+{
+ return Modulations::Strings[iModulationIndex];
+}
+
+bool Modulations::IsModulationValid(QString strModulation)
+{
+ return Modulations::Strings.contains(strModulation, Qt::CaseInsensitive);
+}
+
+Modulations::idx Modulations::GetEnumForModulationString(QString param)
+{
+ int iModulation = -1;
+ for(int i = 0; i < Modulations::Strings.size(); ++i)
+ {
+ QString& strModulation = Modulations::Strings[i];
+ if (param.compare(strModulation, Qt::CaseInsensitive) == 0)
+ {
+ iModulation = i;
+ break;
+ }
+ }
+ if(iModulation == -1)
+ {
+ std::cout << "Modulation '" << param.toStdString() << "' is unknown." << std::endl;
+ iModulation = MODE_OFF;
+ }
+ return idx(iModulation);
+}
+
+bool Modulations::GetFilterPreset(Modulations::idx iModulationIndex, int preset, int& low, int& high)
+{
+ if (iModulationIndex >= MODE_LAST)
+ iModulationIndex = MODE_AM;
+ if (preset == FILTER_PRESET_USER)
+ return false;
+ low = filter_preset_table[iModulationIndex][preset][0];
+ high = filter_preset_table[iModulationIndex][preset][1];
+ return true;
+}
+
+int Modulations::FindFilterPreset(Modulations::idx mode_index, int lo, int hi)
+{
+ if (lo == filter_preset_table[mode_index][FILTER_PRESET_WIDE][0] &&
+ hi == filter_preset_table[mode_index][FILTER_PRESET_WIDE][1])
+ return FILTER_PRESET_WIDE;
+ else if (lo == filter_preset_table[mode_index][FILTER_PRESET_NORMAL][0] &&
+ hi == filter_preset_table[mode_index][FILTER_PRESET_NORMAL][1])
+ return FILTER_PRESET_NORMAL;
+ else if (lo == filter_preset_table[mode_index][FILTER_PRESET_NARROW][0] &&
+ hi == filter_preset_table[mode_index][FILTER_PRESET_NARROW][1])
+ return FILTER_PRESET_NARROW;
+
+ return FILTER_PRESET_USER;
+}
+
+void Modulations::GetFilterRanges(Modulations::idx iModulationIndex, int& lowMin, int& lowMax, int& highMin, int& highMax)
+{
+ if (iModulationIndex >= MODE_LAST)
+ iModulationIndex = MODE_AM;
+ lowMin = filter_ranges_table[iModulationIndex][0][0];
+ lowMax = filter_ranges_table[iModulationIndex][0][1];
+ highMin = filter_ranges_table[iModulationIndex][1][0];
+ highMax = filter_ranges_table[iModulationIndex][1][1];
+}
+
+bool Modulations::IsFilterSymmetric(idx iModulationIndex)
+{
+ if (iModulationIndex >= MODE_LAST)
+ iModulationIndex = MODE_AM;
+ return (-filter_ranges_table[iModulationIndex][0][0] == filter_ranges_table[iModulationIndex][1][1]);
+}
+
+bool Modulations::UpdateFilterRange(Modulations::idx iModulationIndex, int& low, int& high)
+{
+ bool updated = false;
+ if (iModulationIndex >= MODE_LAST)
+ iModulationIndex = MODE_AM;
+ if (-filter_ranges_table[iModulationIndex][0][0] == filter_ranges_table[iModulationIndex][1][1])
+ if (high != (high - low) / 2)
+ {
+ if (high > -low)
+ low = -high;
+ else
+ high = -low;
+ }
+ if (low < filter_ranges_table[iModulationIndex][0][0])
+ {
+ low = filter_ranges_table[iModulationIndex][0][0];
+ updated = true;
+ }
+ if (low > filter_ranges_table[iModulationIndex][0][1])
+ {
+ low = filter_ranges_table[iModulationIndex][0][1];
+ updated = true;
+ }
+ if (high < filter_ranges_table[iModulationIndex][1][0])
+ {
+ high = filter_ranges_table[iModulationIndex][1][0];
+ updated = true;
+ }
+ if (high > filter_ranges_table[iModulationIndex][1][1])
+ {
+ high = filter_ranges_table[iModulationIndex][1][1];
+ updated = true;
+ }
+ return updated;
+}
+
+Modulations::idx Modulations::ConvertFromOld(int old)
+{
+ if (old < 0)
+ return old2new[0];
+ if (old >= int(sizeof(old2new) / sizeof(old2new[0])))
+ return old2new[2];
+ return old2new[old];
+}
+
+bool Modulations::UpdateTw(const int low, const int high, int& tw)
+{
+ int sharp = std::abs(high - low) * 0.1;
+ if (tw == sharp)
+ return false;
+ if (tw < std::abs(high - low) * 0.15)
+ {
+ tw = sharp;
+ return true;
+ }
+ int normal = std::abs(high - low) * 0.2;
+ if (tw == normal)
+ return false;
+ if (tw < std::abs(high - low) * 0.25)
+ {
+ tw = normal;
+ return true;
+ }
+ int soft = std::abs(high - low) * 0.5;
+ if(tw == soft)
+ return false;
+ tw = soft;
+ return true;
+}
+
+Modulations::filter_shape Modulations::FilterShapeFromTw(const int low, const int high, const int tw)
+{
+ Modulations::filter_shape shape = FILTER_SHAPE_SOFT;
+
+ if (tw < std::abs(high - low) * 0.25)
+ shape = FILTER_SHAPE_NORMAL;
+ if (tw < std::abs(high - low) * 0.15)
+ shape = FILTER_SHAPE_SHARP;
+
+ return shape;
+}
+
+int Modulations::TwFromFilterShape(const int low, const int high, const Modulations::filter_shape shape)
+{
+ float trans_width = RX_FILTER_MIN_WIDTH * 0.1;
+ if ((low >= high) || (std::abs(high - low) < RX_FILTER_MIN_WIDTH))
+ return trans_width;
+
+ switch (shape) {
+
+ case Modulations::FILTER_SHAPE_SOFT:
+ trans_width = std::abs(high - low) * 0.5;
+ break;
+
+ case Modulations::FILTER_SHAPE_SHARP:
+ trans_width = std::abs(high - low) * 0.1;
+ break;
+
+ case Modulations::FILTER_SHAPE_NORMAL:
+ default:
+ trans_width = std::abs(high - low) * 0.2;
+ break;
+
+ }
+
+ return trans_width;
+}
+
+Modulations::Modulations()
+{
+ if (Modulations::Strings.size() == 0)
+ {
+ // Keep in sync with rxopt_mode_idx and filter_preset_table
+ Modulations::Strings.append("Demod Off");
+ Modulations::Strings.append("Raw I/Q");
+ Modulations::Strings.append("AM");
+ Modulations::Strings.append("AM-Sync");
+ Modulations::Strings.append("LSB");
+ Modulations::Strings.append("USB");
+ Modulations::Strings.append("CW-L");
+ Modulations::Strings.append("CW-U");
+ Modulations::Strings.append("Narrow FM");
+ Modulations::Strings.append("WFM (mono)");
+ Modulations::Strings.append("WFM (stereo)");
+ Modulations::Strings.append("WFM (oirt)");
+ }
+}
+
+Modulations::~Modulations()
+{
+}
diff --git a/src/receivers/modulations.h b/src/receivers/modulations.h
new file mode 100644
index 0000000000..8b57fd714c
--- /dev/null
+++ b/src/receivers/modulations.h
@@ -0,0 +1,87 @@
+/* -*- c++ -*- */
+/*
+ * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
+ * https://gqrx.dk/
+ *
+ * Copyright 2011-2016 Alexandru Csete OZ9AEC.
+ * Copyright 2022 vladisslav2011@gmail.com.
+ *
+ * Gqrx 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, or (at your option)
+ * any later version.
+ *
+ * Gqrx 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 Gqrx; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef MODULATIONS_H
+#define MODULATIONS_H
+#include
+#include
+
+//FIXME: Convert to enum?
+#define FILTER_PRESET_WIDE 0
+#define FILTER_PRESET_NORMAL 1
+#define FILTER_PRESET_NARROW 2
+#define FILTER_PRESET_USER 3
+
+class Modulations
+{
+public:
+ /** Filter shape (convenience wrappers for "transition width"). */
+ enum filter_shape {
+ FILTER_SHAPE_SOFT = 0, /*!< Soft: Transition band is TBD of width. */
+ FILTER_SHAPE_NORMAL = 1, /*!< Normal: Transition band is TBD of width. */
+ FILTER_SHAPE_SHARP = 2 /*!< Sharp: Transition band is TBD of width. */
+ };
+ /**
+ * Mode selector entries.
+ *
+ * @note If you change this enum, remember to update the TCP interface.
+ * @note Keep in same order as the Strings in Strings, see
+ * DockRxOpt.cpp constructor.
+ */
+ enum rxopt_mode_idx {
+ MODE_OFF = 0, /*!< Demodulator completely off. */
+ MODE_RAW = 1, /*!< Raw I/Q passthrough. */
+ MODE_AM = 2, /*!< Amplitude modulation. */
+ MODE_AM_SYNC = 3, /*!< Amplitude modulation (synchronous demod). */
+ MODE_LSB = 4, /*!< Lower side band. */
+ MODE_USB = 5, /*!< Upper side band. */
+ MODE_CWL = 6, /*!< CW using LSB filter. */
+ MODE_CWU = 7, /*!< CW using USB filter. */
+ MODE_NFM = 8, /*!< Narrow band FM. */
+ MODE_WFM_MONO = 9, /*!< Broadcast FM (mono). */
+ MODE_WFM_STEREO = 10, /*!< Broadcast FM (stereo). */
+ MODE_WFM_STEREO_OIRT = 11, /*!< Broadcast FM (stereo oirt). */
+ MODE_LAST = 12
+ };
+ typedef enum rxopt_mode_idx idx;
+
+ static QStringList Strings;
+
+ static QString GetStringForModulationIndex(int iModulationIndex);
+ static bool IsModulationValid(QString strModulation);
+ static idx GetEnumForModulationString(QString param);
+ static idx ConvertFromOld(int old);
+ static bool GetFilterPreset(idx iModulationIndex, int preset, int& low, int& high);
+ static int FindFilterPreset(idx mode_index, int lo, int hi);
+ static void GetFilterRanges(idx iModulationIndex, int& lowMin, int& lowMax, int& highMin, int& highMax);
+ static bool IsFilterSymmetric(idx iModulationIndex);
+ static bool UpdateFilterRange(idx iModulationIndex, int& low, int& high);
+ static bool UpdateTw(const int low, const int high, int& tw);
+ static filter_shape FilterShapeFromTw(const int low, const int high, const int tw);
+ static int TwFromFilterShape(const int low, const int high, const filter_shape shape);
+ ~Modulations();
+protected:
+ Modulations();
+};
+
+#endif // MODULATIONS_H
diff --git a/src/receivers/nbrx.cpp b/src/receivers/nbrx.cpp
index e6a9904499..643654b9fd 100644
--- a/src/receivers/nbrx.cpp
+++ b/src/receivers/nbrx.cpp
@@ -25,8 +25,6 @@
#include
#include "receivers/nbrx.h"
-// NB: Remember to adjust filter ranges in MainWindow
-#define PREF_QUAD_RATE 96000.f
nbrx_sptr make_nbrx(float quad_rate, float audio_rate)
{
@@ -34,51 +32,34 @@ nbrx_sptr make_nbrx(float quad_rate, float audio_rate)
}
nbrx::nbrx(float quad_rate, float audio_rate)
- : receiver_base_cf("NBRX", PREF_QUAD_RATE, quad_rate, audio_rate),
- d_running(false),
- d_demod(NBRX_DEMOD_FM)
+ : receiver_base_cf("NBRX", NB_PREF_QUAD_RATE, quad_rate, audio_rate),
+ d_running(false)
{
- nb = make_rx_nb_cc(PREF_QUAD_RATE, 3.3, 2.5);
- filter = make_rx_filter(PREF_QUAD_RATE, -5000.0, 5000.0, 1000.0);
+ nb = make_rx_nb_cc(NB_PREF_QUAD_RATE, 3.3, 2.5);
+ filter = make_rx_filter(NB_PREF_QUAD_RATE, -5000.0, 5000.0, 1000.0);
demod_raw = gr::blocks::complex_to_float::make(1);
demod_ssb = gr::blocks::complex_to_real::make(1);
- demod_fm = make_rx_demod_fm(PREF_QUAD_RATE, 5000.0, 75.0e-6);
- demod_am = make_rx_demod_am(PREF_QUAD_RATE, true);
- demod_amsync = make_rx_demod_amsync(PREF_QUAD_RATE, true, 0.001);
+ demod_fm = make_rx_demod_fm(NB_PREF_QUAD_RATE, 5000.0, 75.0e-6);
+ demod_am = make_rx_demod_am(NB_PREF_QUAD_RATE, true);
+ demod_amsync = make_rx_demod_amsync(NB_PREF_QUAD_RATE, true, 0.001);
audio_rr0.reset();
audio_rr1.reset();
- if (d_audio_rate != PREF_QUAD_RATE)
+ if (d_audio_rate != NB_PREF_QUAD_RATE)
{
- std::cout << "Resampling audio " << PREF_QUAD_RATE << " -> "
+ std::cout << "Resampling audio " << NB_PREF_QUAD_RATE << " -> "
<< d_audio_rate << std::endl;
- audio_rr0 = make_resampler_ff(d_audio_rate/PREF_QUAD_RATE);
- audio_rr1 = make_resampler_ff(d_audio_rate/PREF_QUAD_RATE);
+ audio_rr0 = make_resampler_ff(d_audio_rate/NB_PREF_QUAD_RATE);
+ audio_rr1 = make_resampler_ff(d_audio_rate/NB_PREF_QUAD_RATE);
}
- demod = demod_fm;
- connect(self(), 0, iq_resamp, 0);
+ demod = demod_raw;
+ connect(ddc, 0, iq_resamp, 0);
connect(iq_resamp, 0, nb, 0);
connect(nb, 0, filter, 0);
connect(filter, 0, meter, 0);
connect(filter, 0, sql, 0);
- connect(sql, 0, demod, 0);
-// connect(sql, 0, agc, 0);
-// connect(agc, 0, demod, 0);
-
- if (audio_rr0)
- {
- connect(demod, 0, audio_rr0, 0);
-
- connect(audio_rr0, 0, agc, 0); // left channel
- connect(audio_rr0, 0, agc, 1); // right channel
- }
- else
- {
- connect(demod, 0, agc, 0);
- connect(demod, 0, agc, 1);
- }
connect(agc, 0, self(), 0);
connect(agc, 1, self(), 1);
}
@@ -97,18 +78,51 @@ bool nbrx::stop()
return true;
}
-void nbrx::set_filter(double low, double high, double tw)
+void nbrx::set_filter(int low, int high, int tw)
{
- filter->set_param(low, high, tw);
+ receiver_base_cf::set_filter(low, high, tw);
+ filter->set_param(double(low), double(high), double(tw));
}
-void nbrx::set_cw_offset(double offset)
+void nbrx::set_cw_offset(int offset)
{
- filter->set_cw_offset(offset);
+ vfo_s::set_cw_offset(offset);
+ switch (get_demod())
+ {
+ case Modulations::MODE_CWL:
+ ddc->set_center_freq(get_offset() + get_cw_offset());
+ filter->set_cw_offset(-get_cw_offset());
+ break;
+ case Modulations::MODE_CWU:
+ ddc->set_center_freq(get_offset() - get_cw_offset());
+ filter->set_cw_offset(get_cw_offset());
+ break;
+ default:
+ ddc->set_center_freq(get_offset());
+ filter->set_cw_offset(0);
+ }
+}
+
+void nbrx::set_offset(int offset)
+{
+ vfo_s::set_offset(offset);
+ switch (get_demod())
+ {
+ case Modulations::MODE_CWL:
+ ddc->set_center_freq(offset + get_cw_offset());
+ break;
+ case Modulations::MODE_CWU:
+ ddc->set_center_freq(offset - get_cw_offset());
+ break;
+ default:
+ ddc->set_center_freq(offset);
+ }
+ wav_sink->set_offset(offset);
}
void nbrx::set_nb_on(int nbid, bool on)
{
+ receiver_base_cf::set_nb_on(nbid, on);
if (nbid == 1)
nb->set_nb1_on(on);
else if (nbid == 2)
@@ -117,142 +131,173 @@ void nbrx::set_nb_on(int nbid, bool on)
void nbrx::set_nb_threshold(int nbid, float threshold)
{
+ receiver_base_cf::set_nb_threshold(nbid, threshold);
if (nbid == 1)
nb->set_threshold1(threshold);
else if (nbid == 2)
nb->set_threshold2(threshold);
}
-void nbrx::set_demod(int rx_demod)
+void nbrx::set_demod(Modulations::idx new_demod)
{
- nbrx_demod current_demod = d_demod;
+ Modulations::idx current_demod = receiver_base_cf::get_demod();
- /* check if new demodulator selection is valid */
- if ((rx_demod < NBRX_DEMOD_NONE) || (rx_demod >= NBRX_DEMOD_NUM))
- return;
-
- if (rx_demod == current_demod) {
+ if (new_demod == current_demod) {
/* nothing to do */
return;
}
- disconnect(sql, 0, demod, 0);
- if (audio_rr0)
- {
- if (current_demod == NBRX_DEMOD_NONE)
- {
- disconnect(demod, 0, audio_rr0, 0);
- disconnect(demod, 1, audio_rr1, 0);
+ /* check if new demodulator selection is valid */
+ if ((new_demod < Modulations::MODE_OFF) || (new_demod > Modulations::MODE_NFM))
+ return;
- disconnect(audio_rr0, 0, agc, 0);
- disconnect(audio_rr1, 0, agc, 1);
- }
- else
- {
- disconnect(demod, 0, audio_rr0, 0);
- disconnect(audio_rr0, 0, agc, 0);
- disconnect(audio_rr0, 0, agc, 1);
- }
- }
- else
+ if (current_demod > Modulations::MODE_OFF)
{
- if (current_demod == NBRX_DEMOD_NONE)
+ disconnect(sql, 0, demod, 0);
+ if (audio_rr0)
{
- disconnect(demod, 0, agc, 0);
- disconnect(demod, 1, agc, 1);
+ if (current_demod == Modulations::MODE_RAW)
+ {
+ disconnect(demod, 0, audio_rr0, 0);
+ disconnect(demod, 1, audio_rr1, 0);
+
+ disconnect(audio_rr0, 0, agc, 0);
+ disconnect(audio_rr1, 0, agc, 1);
+ }
+ else
+ {
+ disconnect(demod, 0, audio_rr0, 0);
+
+ disconnect(audio_rr0, 0, agc, 0);
+ disconnect(audio_rr0, 0, agc, 1);
+ }
}
else
{
- disconnect(demod, 0, agc, 0);
- disconnect(demod, 0, agc, 1);
+ if (current_demod == Modulations::MODE_RAW)
+ {
+ disconnect(demod, 0, agc, 0);
+ disconnect(demod, 1, agc, 1);
+ }
+ else
+ {
+ disconnect(demod, 0, agc, 0);
+ disconnect(demod, 0, agc, 1);
+ }
}
}
- switch (rx_demod) {
+ switch (new_demod) {
- case NBRX_DEMOD_NONE:
- d_demod = NBRX_DEMOD_NONE;
+ case Modulations::MODE_RAW:
+ case Modulations::MODE_OFF:
demod = demod_raw;
break;
- case NBRX_DEMOD_SSB:
- d_demod = NBRX_DEMOD_SSB;
+ case Modulations::MODE_LSB:
+ case Modulations::MODE_USB:
+ case Modulations::MODE_CWL:
+ case Modulations::MODE_CWU:
demod = demod_ssb;
break;
- case NBRX_DEMOD_AM:
- d_demod = NBRX_DEMOD_AM;
+ case Modulations::MODE_AM:
demod = demod_am;
break;
- case NBRX_DEMOD_AMSYNC:
- d_demod = NBRX_DEMOD_AMSYNC;
+ case Modulations::MODE_AM_SYNC:
demod = demod_amsync;
break;
- case NBRX_DEMOD_FM:
+ case Modulations::MODE_NFM:
default:
- d_demod = NBRX_DEMOD_FM;
demod = demod_fm;
break;
}
- connect(sql, 0, demod, 0);
- if (audio_rr0)
+ if (new_demod > Modulations::MODE_OFF)
{
- if (d_demod == NBRX_DEMOD_NONE)
+ connect(sql, 0, demod, 0);
+ if (audio_rr0)
{
- connect(demod, 0, audio_rr0, 0);
- connect(demod, 1, audio_rr1, 0);
-
- connect(audio_rr0, 0, agc, 0);
- connect(audio_rr1, 0, agc, 1);
+ if (new_demod == Modulations::MODE_RAW)
+ {
+ connect(demod, 0, audio_rr0, 0);
+ connect(demod, 1, audio_rr1, 0);
+
+ connect(audio_rr0, 0, agc, 0);
+ connect(audio_rr1, 0, agc, 1);
+ }
+ else
+ {
+ connect(demod, 0, audio_rr0, 0);
+
+ connect(audio_rr0, 0, agc, 0);
+ connect(audio_rr0, 0, agc, 1);
+ }
}
else
{
- connect(demod, 0, audio_rr0, 0);
-
- connect(audio_rr0, 0, agc, 0);
- connect(audio_rr0, 0, agc, 1);
+ if (new_demod == Modulations::MODE_RAW)
+ {
+ connect(demod, 0, agc, 0);
+ connect(demod, 1, agc, 1);
+ }
+ else
+ {
+ connect(demod, 0, agc, 0);
+ connect(demod, 0, agc, 1);
+ }
}
}
- else
+ receiver_base_cf::set_demod(new_demod);
+ switch (get_demod())
{
- if (d_demod == NBRX_DEMOD_NONE)
- {
- connect(demod, 0, agc, 0);
- connect(demod, 1, agc, 1);
- }
- else
- {
- connect(demod, 0, agc, 0);
- connect(demod, 0, agc, 1);
- }
+ case Modulations::MODE_CWL:
+ ddc->set_center_freq(get_offset() + get_cw_offset());
+ filter->set_cw_offset(-get_cw_offset());
+ break;
+ case Modulations::MODE_CWU:
+ ddc->set_center_freq(get_offset() - get_cw_offset());
+ filter->set_cw_offset(get_cw_offset());
+ break;
+ default:
+ ddc->set_center_freq(get_offset());
+ filter->set_cw_offset(0);
}
}
void nbrx::set_fm_maxdev(float maxdev_hz)
{
+ receiver_base_cf::set_fm_maxdev(maxdev_hz);
demod_fm->set_max_dev(maxdev_hz);
}
void nbrx::set_fm_deemph(double tau)
{
+ receiver_base_cf::set_fm_deemph(tau);
demod_fm->set_tau(tau);
}
void nbrx::set_am_dcr(bool enabled)
{
+ receiver_base_cf::set_am_dcr(enabled);
+ lock();
demod_am->set_dcr(enabled);
+ unlock();
}
void nbrx::set_amsync_dcr(bool enabled)
{
+ receiver_base_cf::set_amsync_dcr(enabled);
+ lock();
demod_amsync->set_dcr(enabled);
+ unlock();
}
void nbrx::set_amsync_pll_bw(float pll_bw)
{
+ receiver_base_cf::set_amsync_pll_bw(pll_bw);
demod_amsync->set_pll_bw(pll_bw);
}
diff --git a/src/receivers/nbrx.h b/src/receivers/nbrx.h
index 55e0dd97b3..03a9f8ebd5 100644
--- a/src/receivers/nbrx.h
+++ b/src/receivers/nbrx.h
@@ -50,59 +50,38 @@ nbrx_sptr make_nbrx(float quad_rate, float audio_rate);
*/
class nbrx : public receiver_base_cf
{
-public:
- /*! \brief Available demodulators. */
- enum nbrx_demod {
- NBRX_DEMOD_NONE = 0, /*!< No demod. Raw I/Q to audio. */
- NBRX_DEMOD_AM = 1, /*!< Amplitude modulation. */
- NBRX_DEMOD_FM = 2, /*!< Frequency modulation. */
- NBRX_DEMOD_SSB = 3, /*!< Single Side Band. */
- NBRX_DEMOD_AMSYNC = 4, /*!< Amplitude modulation (synchronous demod). */
- NBRX_DEMOD_NUM = 5 /*!< Included for convenience. */
- };
-
public:
nbrx(float quad_rate, float audio_rate);
virtual ~nbrx() { };
- bool start();
- bool stop();
+ bool start() override;
+ bool stop() override;
- void set_filter(double low, double high, double tw);
- void set_cw_offset(double offset);
+ void set_filter(int low, int high, int tw) override;
+ void set_offset(int offset) override;
+ void set_cw_offset(int offset) override;
/* Noise blanker */
- bool has_nb() { return true; }
- void set_nb_on(int nbid, bool on);
- void set_nb_threshold(int nbid, float threshold);
-
- /* Squelch parameter */
- bool has_sql() { return true; }
+ bool has_nb() override { return true; }
+ void set_nb_on(int nbid, bool on) override;
+ void set_nb_threshold(int nbid, float threshold) override;
- /* AGC */
- bool has_agc() { return true; }
-
- void set_demod(int demod);
+ void set_demod(Modulations::idx new_demod) override;
/* FM parameters */
- bool has_fm() { return true; }
- void set_fm_maxdev(float maxdev_hz);
- void set_fm_deemph(double tau);
+ void set_fm_maxdev(float maxdev_hz) override;
+ void set_fm_deemph(double tau) override;
/* AM parameters */
- bool has_am() { return true; }
- void set_am_dcr(bool enabled);
+ void set_am_dcr(bool enabled) override;
/* AM-Sync parameters */
- bool has_amsync() { return true; }
- void set_amsync_dcr(bool enabled);
- void set_amsync_pll_bw(float pll_bw);
+ void set_amsync_dcr(bool enabled) override;
+ void set_amsync_pll_bw(float pll_bw) override;
private:
bool d_running; /*!< Whether receiver is running or not. */
- nbrx_demod d_demod; /*!< Current demodulator. */
-
rx_filter_sptr filter; /*!< Non-translating bandpass filter.*/
rx_nb_cc_sptr nb; /*!< Noise blanker. */
diff --git a/src/receivers/receiver_base.cpp b/src/receivers/receiver_base.cpp
index 7d222e503f..53a4c33fee 100644
--- a/src/receivers/receiver_base.cpp
+++ b/src/receivers/receiver_base.cpp
@@ -27,23 +27,34 @@
#include
#include
-
static const int MIN_IN = 1; /* Minimum number of input streams. */
static const int MAX_IN = 1; /* Maximum number of input streams. */
static const int MIN_OUT = 2; /* Minimum number of output streams. */
static const int MAX_OUT = 2; /* Maximum number of output streams. */
-receiver_base_cf::receiver_base_cf(std::string src_name, float pref_quad_rate, float quad_rate, int audio_rate)
+receiver_base_cf::receiver_base_cf(std::string src_name, float pref_quad_rate, double quad_rate, int audio_rate)
: gr::hier_block2 (src_name,
gr::io_signature::make (MIN_IN, MAX_IN, sizeof(gr_complex)),
gr::io_signature::make (MIN_OUT, MAX_OUT, sizeof(float))),
- d_quad_rate(quad_rate),
+ vfo_s(),
+ d_connected(false),
+ d_decim_rate(quad_rate),
+ d_quad_rate(0),
+ d_ddc_decim(1),
d_audio_rate(audio_rate),
+ d_center_freq(145500000.0),
d_pref_quad_rate(pref_quad_rate)
{
+ d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
+ d_quad_rate = d_decim_rate / d_ddc_decim;
+ ddc = make_downconverter_cc(d_ddc_decim, 0.0, d_decim_rate);
+ connect(self(), 0, ddc, 0);
+
iq_resamp = make_resampler_cc(d_pref_quad_rate/d_quad_rate);
- agc = make_rx_agc_2f(d_audio_rate, false, 0, 0, 100, 500, 500, 0);
- sql = make_rx_sql_cc(-150.0, 0.001);
+ agc = make_rx_agc_2f(d_audio_rate, d_agc_on, d_agc_target_level,
+ d_agc_manual_gain, d_agc_max_gain, d_agc_attack_ms,
+ d_agc_decay_ms, d_agc_hang_ms, d_agc_panning);
+ sql = make_rx_sql_cc(d_level_db, d_alpha);
meter = make_rx_meter_c(d_pref_quad_rate);
wav_sink = wavfile_sink_gqrx::make(0, 2, (unsigned int) d_audio_rate,
wavfile_sink_gqrx::FORMAT_WAV,
@@ -57,20 +68,40 @@ receiver_base_cf::receiver_base_cf(std::string src_name, float pref_quad_rate, f
receiver_base_cf::~receiver_base_cf()
{
//Prevent segfault
- if(wav_sink)
+ if (wav_sink)
wav_sink->set_rec_event_handler(nullptr);
}
-void receiver_base_cf::set_quad_rate(float quad_rate)
+void receiver_base_cf::set_demod(Modulations::idx demod)
{
- if (std::abs(d_quad_rate-quad_rate) > 0.5)
+ if ((get_demod() == Modulations::MODE_OFF) && (demod != Modulations::MODE_OFF))
{
- qDebug() << "Changing RX quad rate:" << d_quad_rate << "->" << quad_rate;
- d_quad_rate = quad_rate;
+ qDebug() << "Changing RX quad rate:" << d_decim_rate << "->" << d_quad_rate;
lock();
+ ddc->set_decim_and_samp_rate(d_ddc_decim, d_decim_rate);
iq_resamp->set_rate(d_pref_quad_rate/d_quad_rate);
unlock();
}
+ vfo_s::set_demod(demod);
+}
+
+void receiver_base_cf::set_quad_rate(double quad_rate)
+{
+ if (std::abs(d_decim_rate-quad_rate) > 0.5)
+ {
+ d_decim_rate = quad_rate;
+ d_ddc_decim = std::max(1, (int)(d_decim_rate / TARGET_QUAD_RATE));
+ d_quad_rate = d_decim_rate / d_ddc_decim;
+ //avoid triggering https://github.com/gnuradio/gnuradio/issues/5436
+ if (get_demod() != Modulations::MODE_OFF)
+ {
+ qDebug() << "Changing RX quad rate:" << d_decim_rate << "->" << d_quad_rate;
+ lock();
+ ddc->set_decim_and_samp_rate(d_ddc_decim, d_decim_rate);
+ iq_resamp->set_rate(d_pref_quad_rate/d_quad_rate);
+ unlock();
+ }
+ }
}
void receiver_base_cf::set_center_freq(double center_freq)
@@ -79,31 +110,40 @@ void receiver_base_cf::set_center_freq(double center_freq)
wav_sink->set_center_freq(center_freq);
}
-void receiver_base_cf::set_offset(double offset)
+void receiver_base_cf::set_offset(int offset)
{
- d_offset = offset;
+ vfo_s::set_offset(offset);
+ ddc->set_center_freq(offset);
wav_sink->set_offset(offset);
}
-void receiver_base_cf::set_rec_dir(std::string dir)
+void receiver_base_cf::set_cw_offset(int offset)
{
- d_rec_dir = dir;
+ vfo_s::set_cw_offset(offset);
+}
+
+void receiver_base_cf::set_audio_rec_dir(const std::string& dir)
+{
+ vfo_s::set_audio_rec_dir(dir);
wav_sink->set_rec_dir(dir);
}
void receiver_base_cf::set_audio_rec_sql_triggered(bool enabled)
{
+ vfo_s::set_audio_rec_sql_triggered(enabled);
sql->set_impl(enabled ? rx_sql_cc::SQL_PWR : rx_sql_cc::SQL_SIMPLE);
wav_sink->set_sql_triggered(enabled);
}
void receiver_base_cf::set_audio_rec_min_time(const int time_ms)
{
+ vfo_s::set_audio_rec_min_time(time_ms);
wav_sink->set_rec_min_time(time_ms);
}
void receiver_base_cf::set_audio_rec_max_gap(const int time_ms)
{
+ vfo_s::set_audio_rec_max_gap(time_ms);
wav_sink->set_rec_max_gap(time_ms);
}
@@ -117,116 +157,69 @@ bool receiver_base_cf::has_nb()
return false;
}
-void receiver_base_cf::set_nb_on(int nbid, bool on)
-{
- (void) nbid;
- (void) on;
-}
-
-void receiver_base_cf::set_nb_threshold(int nbid, float threshold)
-{
- (void) nbid;
- (void) threshold;
-}
-
-bool receiver_base_cf::has_sql()
-{
- return false;
-}
-
void receiver_base_cf::set_sql_level(double level_db)
{
sql->set_threshold(level_db);
+ vfo_s::set_sql_level(level_db);
}
void receiver_base_cf::set_sql_alpha(double alpha)
{
sql->set_alpha(alpha);
-}
-
-bool receiver_base_cf::has_agc()
-{
- return false;
+ vfo_s::set_sql_alpha(alpha);
}
void receiver_base_cf::set_agc_on(bool agc_on)
{
agc->set_agc_on(agc_on);
+ vfo_s::set_agc_on(agc_on);
}
void receiver_base_cf::set_agc_target_level(int target_level)
{
agc->set_target_level(target_level);
+ vfo_s::set_agc_target_level(target_level);
}
void receiver_base_cf::set_agc_manual_gain(float gain)
{
agc->set_manual_gain(gain);
+ vfo_s::set_agc_manual_gain(gain);
}
void receiver_base_cf::set_agc_max_gain(int gain)
{
agc->set_max_gain(gain);
+ vfo_s::set_agc_max_gain(gain);
}
void receiver_base_cf::set_agc_attack(int attack_ms)
{
agc->set_attack(attack_ms);
+ vfo_s::set_agc_attack(attack_ms);
}
void receiver_base_cf::set_agc_decay(int decay_ms)
{
agc->set_decay(decay_ms);
+ vfo_s::set_agc_decay(decay_ms);
}
void receiver_base_cf::set_agc_hang(int hang_ms)
{
agc->set_hang(hang_ms);
+ vfo_s::set_agc_hang(hang_ms);
}
-float receiver_base_cf::get_agc_gain()
-{
- return agc->get_current_gain();
-}
-
-bool receiver_base_cf::has_fm()
-{
- return false;
-}
-
-void receiver_base_cf::set_fm_maxdev(float maxdev_hz)
-{
- (void) maxdev_hz;
-}
-
-void receiver_base_cf::set_fm_deemph(double tau)
-{
- (void) tau;
-}
-
-bool receiver_base_cf::has_am()
-{
- return false;
-}
-
-void receiver_base_cf::set_am_dcr(bool enabled)
-{
- (void) enabled;
-}
-
-bool receiver_base_cf::has_amsync()
-{
- return false;
-}
-
-void receiver_base_cf::set_amsync_dcr(bool enabled)
+void receiver_base_cf::set_agc_panning(int panning)
{
- (void) enabled;
+ agc->set_panning(panning);
+ vfo_s::set_agc_panning(panning);
}
-void receiver_base_cf::set_amsync_pll_bw(float pll_bw)
+float receiver_base_cf::get_agc_gain()
{
- (void) pll_bw;
+ return agc->get_current_gain();
}
void receiver_base_cf::get_rds_data(std::string &outbuff, int &num)
@@ -257,6 +250,11 @@ int receiver_base_cf::start_audio_recording()
return wav_sink->open_new();
}
+bool receiver_base_cf::get_audio_recording()
+{
+ return wav_sink->is_active();
+}
+
void receiver_base_cf::stop_audio_recording()
{
wav_sink->close();
@@ -265,7 +263,7 @@ void receiver_base_cf::stop_audio_recording()
//FIXME Reimplement wavfile_sink correctly to make this work as expected
void receiver_base_cf::continue_audio_recording(receiver_base_cf_sptr from)
{
- if(from.get() == this)
+ if (from.get() == this)
return;
from->disconnect(from->agc, 0, from->wav_sink, 0);
from->disconnect(from->agc, 1, from->wav_sink, 1);
@@ -285,6 +283,16 @@ std::string receiver_base_cf::get_last_audio_filename()
void receiver_base_cf::rec_event(receiver_base_cf * self, std::string filename, bool is_running)
{
self->d_audio_filename = filename;
- if(self->d_rec_event)
- self->d_rec_event(filename, is_running);
+ if (self->d_rec_event)
+ {
+ self->d_rec_event(self->get_index(), filename, is_running);
+ std::cerr<<"d_rec_event("<get_index()<<","< receiver_base_cf_sptr;
#else
@@ -48,75 +65,54 @@ typedef std::shared_ptr receiver_base_cf_sptr;
* output audio (or other kind of float data).
*
*/
-class receiver_base_cf : public gr::hier_block2
+class receiver_base_cf : public gr::hier_block2, public vfo_s
{
public:
/*! \brief Public constructor.
* \param src_name Descriptive name used in the constructor of gr::hier_block2
*/
- typedef std::function rec_event_handler_t;
- receiver_base_cf(std::string src_name, float pref_quad_rate, float quad_rate, int audio_rate);
+ typedef std::function rec_event_handler_t;
+ receiver_base_cf(std::string src_name, float pref_quad_rate, double quad_rate, int audio_rate);
virtual ~receiver_base_cf();
virtual bool start() = 0;
virtual bool stop() = 0;
- virtual void set_quad_rate(float quad_rate);
+ virtual void set_quad_rate(double quad_rate);
virtual void set_center_freq(double center_freq);
- virtual void set_offset(double offset);
- virtual void set_rec_dir(std::string dir);
- virtual std::string get_rec_dir() { return d_rec_dir; }
- virtual void set_audio_rec_sql_triggered(bool enabled);
- virtual bool get_audio_rec_sql_triggered() { return wav_sink->get_sql_triggered(); }
- virtual void set_audio_rec_min_time(const int time_ms);
- virtual int get_audio_rec_min_time() { return wav_sink->get_min_time(); }
- virtual void set_audio_rec_max_gap(const int time_ms);
- virtual int get_audio_rec_max_gap() { return wav_sink->get_max_gap(); }
-
- virtual void set_filter(double low, double high, double tw) = 0;
- virtual void set_cw_offset(double offset) = 0;
+ void set_offset(int offset) override;
- virtual float get_signal_level();
+ /* Audio recording */
+ void set_audio_rec_dir(const std::string& dir) override;
+ void set_audio_rec_sql_triggered(bool enabled) override;
+ void set_audio_rec_min_time(const int time_ms) override;
+ void set_audio_rec_max_gap(const int time_ms) override;
- virtual void set_demod(int demod) = 0;
+ virtual float get_signal_level();
- /* the rest is optional */
+ void set_demod(Modulations::idx demod) override;
/* Noise blanker */
virtual bool has_nb();
- virtual void set_nb_on(int nbid, bool on);
- virtual void set_nb_threshold(int nbid, float threshold);
/* Squelch parameter */
- virtual bool has_sql();
- virtual void set_sql_level(double level_db);
- virtual void set_sql_alpha(double alpha);
+ void set_sql_level(double level_db) override;
+ void set_sql_alpha(double alpha) override;
/* AGC */
- virtual bool has_agc();
- virtual void set_agc_on(bool agc_on);
- virtual void set_agc_target_level(int target_level);
- virtual void set_agc_manual_gain(float gain);
- virtual void set_agc_max_gain(int gain);
- virtual void set_agc_attack(int attack_ms);
- virtual void set_agc_decay(int decay_ms);
- virtual void set_agc_hang(int hang_ms);
+ void set_agc_on(bool agc_on) override;
+ void set_agc_target_level(int target_level) override;
+ void set_agc_manual_gain(float gain) override;
+ void set_agc_max_gain(int gain) override;
+ void set_agc_attack(int attack_ms) override;
+ void set_agc_decay(int decay_ms) override;
+ void set_agc_hang(int hang_ms) override;
+ void set_agc_panning(int panning) override;
virtual float get_agc_gain();
- /* FM parameters */
- virtual bool has_fm();
- virtual void set_fm_maxdev(float maxdev_hz);
- virtual void set_fm_deemph(double tau);
-
- /* AM parameters */
- virtual bool has_am();
- virtual void set_am_dcr(bool enabled);
-
- /* AM-Sync parameters */
- virtual bool has_amsync();
- virtual void set_amsync_dcr(bool enabled);
- virtual void set_amsync_pll_bw(float pll_bw);
+ /* CW parameters */
+ void set_cw_offset(int offset) override;
virtual void get_rds_data(std::string &outbuff, int &num);
virtual void start_rds_decoder();
@@ -126,29 +122,37 @@ class receiver_base_cf : public gr::hier_block2
virtual int start_audio_recording();
virtual void stop_audio_recording();
virtual void continue_audio_recording(receiver_base_cf_sptr from);
+ virtual bool get_audio_recording();
virtual std::string get_last_audio_filename();
template void set_rec_event_handler(T handler)
{
d_rec_event = handler;
}
+ using vfo_s::restore_settings;
+ virtual void restore_settings(receiver_base_cf& from);
+ bool connected() { return d_connected; }
+ void connected(bool value) { d_connected = value; }
protected:
- float d_quad_rate; /*!< Input sample rate. */
- int d_audio_rate; /*!< Audio output rate. */
- double d_center_freq;
- double d_offset;
- std::string d_rec_dir;
- std::string d_audio_filename;
-
+ bool d_connected;
+ double d_decim_rate; /*!< Quadrature rate (before down-conversion) */
+ double d_quad_rate; /*!< Quadrature rate (after down-conversion) */
+ unsigned int d_ddc_decim; /*!< Down-conversion decimation. */
+ int d_audio_rate; /*!< Audio output rate. */
+ double d_center_freq;
+ std::string d_audio_filename;
+
+ downconverter_cc_sptr ddc; /*!< Digital down-converter for demod chain. */
resampler_cc_sptr iq_resamp; /*!< Baseband resampler. */
rx_meter_c_sptr meter; /*!< Signal strength. */
rx_agc_2f_sptr agc; /*!< Receiver AGC. */
rx_sql_cc_sptr sql; /*!< Squelch. */
- wavfile_sink_gqrx::sptr wav_sink; /*!< WAV file sink for recording. */
+ wavfile_sink_gqrx::sptr wav_sink; /*!< WAV file sink for recording. */
private:
float d_pref_quad_rate;
rec_event_handler_t d_rec_event;
static void rec_event(receiver_base_cf * self, std::string filename, bool is_running);
+
};
#endif // RECEIVER_BASE_H
diff --git a/src/receivers/vfo.cpp b/src/receivers/vfo.cpp
new file mode 100644
index 0000000000..2994ea16d4
--- /dev/null
+++ b/src/receivers/vfo.cpp
@@ -0,0 +1,244 @@
+/* -*- c++ -*- */
+/*
+ * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
+ * https://gqrx.dk/
+ *
+ * Copyright 2022 vladisslav2011@gmail.com.
+ *
+ * Gqrx 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, or (at your option)
+ * any later version.
+ *
+ * Gqrx 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 Gqrx; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#include "vfo.h"
+
+void vfo_s::set_offset(int offset)
+{
+ d_offset = offset;
+}
+
+void vfo_s::set_filter_low(int low)
+{
+ d_filter_low = low;
+}
+
+void vfo_s::set_filter_high(int high)
+{
+ d_filter_high = high;
+}
+
+void vfo_s::set_filter_tw(int tw)
+{
+ d_filter_tw = tw;
+}
+
+void vfo_s::set_filter(int low, int high, int tw)
+{
+ d_filter_low = low;
+ d_filter_high = high;
+ d_filter_tw = tw;
+}
+
+void vfo_s::filter_adjust()
+{
+ Modulations::UpdateFilterRange(d_demod, d_filter_low, d_filter_high);
+ Modulations::UpdateTw(d_filter_low, d_filter_high, d_filter_tw);
+}
+
+void vfo_s::set_demod(Modulations::idx demod)
+{
+ d_demod = demod;
+}
+
+void vfo_s::set_index(int index)
+{
+ d_index = index;
+}
+
+void vfo_s::set_freq_lock(bool on)
+{
+ d_locked = on;
+}
+
+void vfo_s::set_sql_level(double level_db)
+{
+ d_level_db = level_db;
+}
+
+void vfo_s::set_sql_alpha(double alpha)
+{
+ d_alpha = alpha;
+}
+
+void vfo_s::set_agc_on(bool agc_on)
+{
+ d_agc_on = agc_on;
+}
+
+void vfo_s::set_agc_target_level(int target_level)
+{
+ d_agc_target_level = target_level;
+}
+
+void vfo_s::set_agc_manual_gain(float gain)
+{
+ d_agc_manual_gain = gain;
+}
+
+void vfo_s::set_agc_max_gain(int gain)
+{
+ d_agc_max_gain = gain;
+}
+
+void vfo_s::set_agc_attack(int attack_ms)
+{
+ d_agc_attack_ms = attack_ms;
+}
+
+void vfo_s::set_agc_decay(int decay_ms)
+{
+ d_agc_decay_ms = decay_ms;
+}
+
+void vfo_s::set_agc_hang(int hang_ms)
+{
+ d_agc_hang_ms = hang_ms;
+}
+
+void vfo_s::set_agc_panning(int panning)
+{
+ d_agc_panning = panning;
+}
+
+void vfo_s::set_agc_panning_auto(bool mode)
+{
+ d_agc_panning_auto = mode;
+}
+
+void vfo_s::set_cw_offset(int offset)
+{
+ d_cw_offset = offset;
+}
+
+void vfo_s::set_fm_maxdev(float maxdev_hz)
+{
+ d_fm_maxdev = maxdev_hz;
+}
+
+void vfo_s::set_fm_deemph(double tau)
+{
+ d_fm_deemph = tau;
+}
+
+void vfo_s::set_am_dcr(bool enabled)
+{
+ d_am_dcr = enabled;
+}
+
+void vfo_s::set_amsync_dcr(bool enabled)
+{
+ d_amsync_dcr = enabled;
+}
+
+void vfo_s::set_amsync_pll_bw(float pll_bw)
+{
+ d_amsync_pll_bw = pll_bw;
+}
+
+void vfo_s::set_nb_on(int nbid, bool on)
+{
+ if (nbid - 1 < RECEIVER_NB_COUNT)
+ d_nb_on[nbid - 1] = on;
+}
+
+bool vfo_s::get_nb_on(int nbid)
+{
+ if (nbid - 1 < RECEIVER_NB_COUNT)
+ return d_nb_on[nbid - 1];
+ return false;
+}
+
+void vfo_s::set_nb_threshold(int nbid, float threshold)
+{
+ if (nbid - 1 < RECEIVER_NB_COUNT)
+ d_nb_threshold[nbid - 1] = threshold;
+}
+
+float vfo_s::get_nb_threshold(int nbid)
+{
+ if (nbid - 1 < RECEIVER_NB_COUNT)
+ return d_nb_threshold[nbid - 1];
+ return 0.0;
+}
+
+void vfo_s::set_audio_rec_dir(const std::string& dir)
+{
+ d_rec_dir = dir;
+}
+
+void vfo_s::set_audio_rec_sql_triggered(bool enabled)
+{
+ d_rec_sql_triggered = enabled;
+}
+
+void vfo_s::set_audio_rec_min_time(const int time_ms)
+{
+ d_rec_min_time = time_ms;
+}
+
+void vfo_s::set_audio_rec_max_gap(const int time_ms)
+{
+ d_rec_max_gap = time_ms;
+}
+
+void vfo_s::restore_settings(vfo_s& from, bool force)
+{
+ set_offset(from.get_offset());
+ set_filter(from.get_filter_low(), from.get_filter_high(), from.get_filter_tw());
+ set_freq_lock(from.get_freq_lock());
+ set_demod(from.get_demod());
+ set_sql_level(from.get_sql_level());
+ set_sql_alpha(from.get_sql_alpha());
+
+ set_agc_on(from.get_agc_on());
+ set_agc_target_level(from.get_agc_target_level());
+ set_agc_manual_gain(from.get_agc_manual_gain());
+ set_agc_max_gain(from.get_agc_max_gain());
+ set_agc_attack(from.get_agc_attack());
+ set_agc_decay(from.get_agc_decay());
+ set_agc_hang(from.get_agc_hang());
+ set_agc_panning(from.get_agc_panning());
+ set_agc_panning_auto(from.get_agc_panning_auto());
+
+ set_cw_offset(from.get_cw_offset());
+
+ set_fm_maxdev(from.get_fm_maxdev());
+ set_fm_deemph(from.get_fm_deemph());
+
+ set_am_dcr(from.get_am_dcr());
+ set_amsync_dcr(from.get_amsync_dcr());
+ set_amsync_pll_bw(from.get_amsync_pll_bw());
+
+ for (int k = 0; k < RECEIVER_NB_COUNT; k++)
+ {
+ set_nb_on(k + 1, from.get_nb_on(k + 1));
+ set_nb_threshold(k + 1, from.get_nb_threshold(k + 1));
+ }
+ if (force || (from.get_audio_rec_dir() != ""))
+ set_audio_rec_dir(from.get_audio_rec_dir());
+ if (force || (from.get_audio_rec_min_time() > 0))
+ set_audio_rec_min_time(from.get_audio_rec_min_time());
+ if (force || (from.get_audio_rec_max_gap() > 0))
+ set_audio_rec_max_gap(from.get_audio_rec_max_gap());
+ set_audio_rec_sql_triggered(from.get_audio_rec_sql_triggered());
+}
\ No newline at end of file
diff --git a/src/receivers/vfo.h b/src/receivers/vfo.h
new file mode 100644
index 0000000000..71c7ab2724
--- /dev/null
+++ b/src/receivers/vfo.h
@@ -0,0 +1,236 @@
+/* -*- c++ -*- */
+/*
+ * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
+ * https://gqrx.dk/
+ *
+ * Copyright 2022 vladisslav2011@gmail.com.
+ *
+ * Gqrx 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, or (at your option)
+ * any later version.
+ *
+ * Gqrx 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 Gqrx; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef VFO_H
+#define VFO_H
+
+#if GNURADIO_VERSION < 0x030900
+ #include
+#endif
+
+#include "receivers/defines.h"
+#include "receivers/modulations.h"
+
+
+class vfo_s;
+typedef class vfo_s
+{
+ public:
+#if GNURADIO_VERSION < 0x030900
+ typedef boost::shared_ptr sptr;
+#else
+ typedef std::shared_ptr sptr;
+#endif
+ static sptr make()
+ {
+ return sptr(new vfo_s());
+ }
+
+ vfo_s():
+ d_offset(0),
+ d_filter_low(-5000),
+ d_filter_high(5000),
+ d_filter_tw(100),
+ d_demod(Modulations::MODE_OFF),
+ d_index(-1),
+ d_locked(false),
+ d_level_db(-150),
+ d_alpha(0.001),
+ d_agc_on(true),
+ d_agc_target_level(0),
+ d_agc_manual_gain(0),
+ d_agc_max_gain(100),
+ d_agc_attack_ms(20),
+ d_agc_decay_ms(500),
+ d_agc_hang_ms(0),
+ d_agc_panning(0),
+ d_agc_panning_auto(false),
+ d_cw_offset(700),
+ d_fm_maxdev(2500),
+ d_fm_deemph(7.5e-5),
+ d_am_dcr(true),
+ d_amsync_dcr(true),
+ d_amsync_pll_bw(0.01),
+ d_rec_dir(""),
+ d_rec_sql_triggered(false),
+ d_rec_min_time(0),
+ d_rec_max_gap(0)
+ {
+ for (int k = 0; k < RECEIVER_NB_COUNT; k++)
+ {
+ d_nb_on[k] = false;
+ d_nb_threshold[k] = 2;
+ }
+ }
+
+ virtual ~vfo_s()
+ {
+ }
+
+ struct comp
+ {
+ inline bool operator()(const sptr lhs, const sptr rhs) const
+ {
+ const vfo_s *a = lhs.get();
+ const vfo_s *b = rhs.get();
+ return (a->d_offset == b->d_offset) ? (a->d_index < b->d_index) : (a->d_offset < b->d_offset);
+ }
+ };
+ typedef std::set set;
+ //getters
+ inline int get_offset() const { return d_offset; }
+ /* Filter parameter */
+ inline int get_filter_low() const { return d_filter_low;}
+ inline int get_filter_high() const { return d_filter_high;}
+ inline int get_filter_tw() const { return d_filter_tw; }
+ inline void get_filter(int &low, int &high, int &tw) const
+ {
+ low = d_filter_low;
+ high = d_filter_high;
+ tw = d_filter_tw;
+ }
+ inline Modulations::idx get_demod() const { return d_demod; }
+ inline int get_index() const { return d_index; }
+ inline bool get_freq_lock() const { return d_locked; }
+ /* Squelch parameter */
+ inline double get_sql_level() const { return d_level_db; }
+ inline double get_sql_alpha() const { return d_alpha; }
+ /* AGC */
+ inline bool get_agc_on() const { return d_agc_on; }
+ inline int get_agc_target_level() const { return d_agc_target_level; }
+ inline float get_agc_manual_gain() const { return d_agc_manual_gain; }
+ inline int get_agc_max_gain() const { return d_agc_max_gain; }
+ inline int get_agc_attack() const { return d_agc_attack_ms; }
+ inline int get_agc_decay() const { return d_agc_decay_ms; }
+ inline int get_agc_hang() const { return d_agc_hang_ms; }
+ inline int get_agc_panning() const { return d_agc_panning; }
+ inline bool get_agc_panning_auto() const { return d_agc_panning_auto; }
+ /* CW parameters */
+ inline int get_cw_offset() const { return d_cw_offset; }
+ /* FM parameters */
+ inline float get_fm_maxdev() const { return d_fm_maxdev; }
+ inline double get_fm_deemph() const { return d_fm_deemph; }
+ /* AM parameters */
+ inline bool get_am_dcr() const { return d_am_dcr; }
+
+ /* AM-Sync parameters */
+ inline bool get_amsync_dcr() const { return d_amsync_dcr; }
+ inline float get_amsync_pll_bw() const { return d_amsync_pll_bw; }
+ /* Noise blanker */
+ bool get_nb_on(int nbid);
+ float get_nb_threshold(int nbid);
+ /* Audio recorder */
+ inline const std::string& get_audio_rec_dir() const { return d_rec_dir; }
+ inline bool get_audio_rec_sql_triggered() const { return d_rec_sql_triggered; }
+ inline int get_audio_rec_min_time() const { return d_rec_min_time; }
+ inline int get_audio_rec_max_gap() const { return d_rec_max_gap; }
+
+ //setters
+ virtual void set_offset(int offset);
+ /* Filter parameter */
+ virtual void set_filter_low(int low);
+ virtual void set_filter_high(int high);
+ virtual void set_filter_tw(int tw);
+ virtual void set_filter(int low, int high, int tw);
+ virtual void filter_adjust();
+
+ virtual void set_demod(Modulations::idx demod);
+ virtual void set_index(int index);
+ virtual void set_freq_lock(bool on);
+ /* Squelch parameter */
+ virtual void set_sql_level(double level_db);
+ virtual void set_sql_alpha(double alpha);
+ /* AGC */
+ virtual void set_agc_on(bool agc_on);
+ virtual void set_agc_target_level(int target_level);
+ virtual void set_agc_manual_gain(float gain);
+ virtual void set_agc_max_gain(int gain);
+ virtual void set_agc_attack(int attack_ms);
+ virtual void set_agc_decay(int decay_ms);
+ virtual void set_agc_hang(int hang_ms);
+ virtual void set_agc_panning(int panning);
+ virtual void set_agc_panning_auto(bool mode);
+ /* CW parameters */
+ virtual void set_cw_offset(int offset);
+ /* FM parameters */
+ virtual void set_fm_maxdev(float maxdev_hz);
+ virtual void set_fm_deemph(double tau);
+ /* AM parameters */
+ virtual void set_am_dcr(bool enabled);
+ /* AM-Sync parameters */
+ virtual void set_amsync_dcr(bool enabled);
+ virtual void set_amsync_pll_bw(float pll_bw);
+ /* Noise blanker */
+ virtual void set_nb_on(int nbid, bool on);
+ virtual void set_nb_threshold(int nbid, float threshold);
+ /* Audio recorder */
+ virtual void set_audio_rec_dir(const std::string& dir);
+ virtual void set_audio_rec_sql_triggered(bool enabled);
+ virtual void set_audio_rec_min_time(const int time_ms);
+ virtual void set_audio_rec_max_gap(const int time_ms);
+
+ virtual void restore_settings(vfo_s& from, bool force = true);
+
+ protected:
+ int d_offset;
+ int d_filter_low;
+ int d_filter_high;
+ int d_filter_tw;
+ Modulations::idx d_demod;
+ int d_index;
+ bool d_locked;
+
+ double d_level_db;
+ double d_alpha;
+
+ bool d_agc_on;
+ int d_agc_target_level;
+ float d_agc_manual_gain;
+ int d_agc_max_gain;
+ int d_agc_attack_ms;
+ int d_agc_decay_ms;
+ int d_agc_hang_ms;
+ int d_agc_panning;
+ int d_agc_panning_auto;
+
+ int d_cw_offset; /*!< CW offset */
+
+ float d_fm_maxdev;
+ double d_fm_deemph;
+
+ bool d_am_dcr;
+ bool d_amsync_dcr;
+ float d_amsync_pll_bw;
+
+ bool d_nb_on[RECEIVER_NB_COUNT];
+ float d_nb_threshold[RECEIVER_NB_COUNT];
+
+ std::string d_rec_dir;
+ bool d_rec_sql_triggered;
+ int d_rec_min_time;
+ int d_rec_max_gap;
+
+
+} vfo;
+
+
+#endif //VFO_H
\ No newline at end of file
diff --git a/src/receivers/wfmrx.cpp b/src/receivers/wfmrx.cpp
index b35aa78089..09b53f3178 100644
--- a/src/receivers/wfmrx.cpp
+++ b/src/receivers/wfmrx.cpp
@@ -26,33 +26,30 @@
#include
#include "receivers/wfmrx.h"
-#define PREF_QUAD_RATE 240e3 // Nominal channel spacing is 200 kHz
-
wfmrx_sptr make_wfmrx(float quad_rate, float audio_rate)
{
return gnuradio::get_initial_sptr(new wfmrx(quad_rate, audio_rate));
}
wfmrx::wfmrx(float quad_rate, float audio_rate)
- : receiver_base_cf("WFMRX", PREF_QUAD_RATE, quad_rate, audio_rate),
- d_running(false),
- d_demod(WFMRX_DEMOD_MONO)
+ : receiver_base_cf("WFMRX", WFM_PREF_QUAD_RATE, quad_rate, audio_rate),
+ d_running(false)
{
- filter = make_rx_filter(PREF_QUAD_RATE, -80000.0, 80000.0, 20000.0);
- demod_fm = make_rx_demod_fm(PREF_QUAD_RATE, 75000.0, 0.0);
- stereo = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, true);
- stereo_oirt = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, true, true);
- mono = make_stereo_demod(PREF_QUAD_RATE, d_audio_rate, false);
+ filter = make_rx_filter(WFM_PREF_QUAD_RATE, -80000.0, 80000.0, 20000.0);
+ demod_fm = make_rx_demod_fm(WFM_PREF_QUAD_RATE, 75000.0, 0.0);
+ stereo = make_stereo_demod(WFM_PREF_QUAD_RATE, d_audio_rate, true);
+ stereo_oirt = make_stereo_demod(WFM_PREF_QUAD_RATE, d_audio_rate, true, true);
+ mono = make_stereo_demod(WFM_PREF_QUAD_RATE, d_audio_rate, false);
/* create rds blocks but dont connect them */
- rds = make_rx_rds(PREF_QUAD_RATE);
+ rds = make_rx_rds(WFM_PREF_QUAD_RATE);
rds_decoder = gr::rds::decoder::make(0, 0);
rds_parser = gr::rds::parser::make(0, 0, 0);
rds_store = make_rx_rds_store();
rds_enabled = false;
- connect(self(), 0, iq_resamp, 0);
+ connect(ddc, 0, iq_resamp, 0);
connect(iq_resamp, 0, filter, 0);
connect(filter, 0, meter, 0);
connect(filter, 0, sql, 0);
@@ -83,18 +80,19 @@ bool wfmrx::stop()
return true;
}
-void wfmrx::set_filter(double low, double high, double tw)
+void wfmrx::set_filter(int low, int high, int tw)
{
- filter->set_param(low, high, tw);
+ receiver_base_cf::set_filter(low, high, tw);
+ filter->set_param(double(low), double(high), double(tw));
}
-void wfmrx::set_demod(int demod)
+void wfmrx::set_demod(Modulations::idx demod)
{
/* check if new demodulator selection is valid */
- if ((demod < WFMRX_DEMOD_MONO) || (demod >= WFMRX_DEMOD_NUM))
+ if ((demod < Modulations::MODE_WFM_MONO) || (demod > Modulations::MODE_WFM_STEREO_OIRT))
return;
- if (demod == d_demod) {
+ if (demod == receiver_base_cf::get_demod()) {
/* nothing to do */
return;
}
@@ -103,22 +101,22 @@ void wfmrx::set_demod(int demod)
lock();
/* disconnect current demodulator */
- switch (d_demod) {
+ switch (receiver_base_cf::get_demod()) {
- case WFMRX_DEMOD_MONO:
+ case Modulations::MODE_WFM_MONO:
default:
disconnect(demod_fm, 0, mono, 0);
disconnect(mono, 0, agc, 0); // left channel
disconnect(mono, 1, agc, 1); // right channel
break;
- case WFMRX_DEMOD_STEREO:
+ case Modulations::MODE_WFM_STEREO:
disconnect(demod_fm, 0, stereo, 0);
disconnect(stereo, 0, agc, 0); // left channel
disconnect(stereo, 1, agc, 1); // right channel
break;
- case WFMRX_DEMOD_STEREO_UKW:
+ case Modulations::MODE_WFM_STEREO_OIRT:
disconnect(demod_fm, 0, stereo_oirt, 0);
disconnect(stereo_oirt, 0, agc, 0); // left channel
disconnect(stereo_oirt, 1, agc, 1); // right channel
@@ -127,41 +125,31 @@ void wfmrx::set_demod(int demod)
switch (demod) {
- case WFMRX_DEMOD_MONO:
+ case Modulations::MODE_WFM_MONO:
default:
connect(demod_fm, 0, mono, 0);
connect(mono, 0, agc, 0); // left channel
connect(mono, 1, agc, 1); // right channel
break;
- case WFMRX_DEMOD_STEREO:
+ case Modulations::MODE_WFM_STEREO:
connect(demod_fm, 0, stereo, 0);
connect(stereo, 0, agc, 0); // left channel
connect(stereo, 1, agc, 1); // right channel
break;
- case WFMRX_DEMOD_STEREO_UKW:
+ case Modulations::MODE_WFM_STEREO_OIRT:
connect(demod_fm, 0, stereo_oirt, 0);
connect(stereo_oirt, 0, agc, 0); // left channel
connect(stereo_oirt, 1, agc, 1); // right channel
break;
}
- d_demod = (wfmrx_demod) demod;
+ receiver_base_cf::set_demod(demod);
/* continue processing */
unlock();
}
-void wfmrx::set_fm_maxdev(float maxdev_hz)
-{
- demod_fm->set_max_dev(maxdev_hz);
-}
-
-void wfmrx::set_fm_deemph(double tau)
-{
- demod_fm->set_tau(tau);
-}
-
void wfmrx::get_rds_data(std::string &outbuff, int &num)
{
rds_store->get_message(outbuff, num);
diff --git a/src/receivers/wfmrx.h b/src/receivers/wfmrx.h
index 2c05a12fc1..5b1864629c 100644
--- a/src/receivers/wfmrx.h
+++ b/src/receivers/wfmrx.h
@@ -53,52 +53,29 @@ class wfmrx : public receiver_base_cf
{
public:
- /*! \brief Available demodulators. */
- enum wfmrx_demod {
- WFMRX_DEMOD_MONO = 0, /*!< Mono. */
- WFMRX_DEMOD_STEREO = 1, /*!< FM stereo. */
- WFMRX_DEMOD_STEREO_UKW = 2, /*!< UKW stereo. */
- WFMRX_DEMOD_NUM = 3 /*!< Included for convenience. */
- };
wfmrx(float quad_rate, float audio_rate);
~wfmrx();
- bool start();
- bool stop();
+ bool start() override;
+ bool stop() override;
- void set_filter(double low, double high, double tw);
- void set_cw_offset(double offset) { (void)offset; }
+ void set_filter(int low, int high, int tw) override;
/* Noise blanker */
- bool has_nb() { return false; }
- //void set_nb_on(int nbid, bool on);
- //void set_nb_threshold(int nbid, float threshold);
+ bool has_nb() override { return false; }
- /* Squelch parameter */
- bool has_sql() { return true; }
+ void set_demod(Modulations::idx demod) override;
- /* AGC */
- bool has_agc() { return true; }
-
- void set_demod(int demod);
-
- /* FM parameters */
- bool has_fm() {return true; }
- void set_fm_maxdev(float maxdev_hz);
- void set_fm_deemph(double tau);
-
- void get_rds_data(std::string &outbuff, int &num);
- void start_rds_decoder();
- void stop_rds_decoder();
- void reset_rds_parser();
- bool is_rds_decoder_active();
+ void get_rds_data(std::string &outbuff, int &num) override;
+ void start_rds_decoder() override;
+ void stop_rds_decoder() override;
+ void reset_rds_parser() override;
+ bool is_rds_decoder_active() override;
private:
bool d_running; /*!< Whether receiver is running or not. */
- wfmrx_demod d_demod; /*!< Current demodulator. */
-
rx_filter_sptr filter; /*!< Non-translating bandpass filter.*/
rx_demod_fm_sptr demod_fm; /*!< FM demodulator. */