1 /* -*- c++ -*- */
2 /*
3  * Gqrx SDR: Software defined radio receiver powered by GNU Radio and Qt
4  *           https://gqrx.dk/
5  *
6  * Copyright 2011-2013 Alexandru Csete OZ9AEC.
7  *
8  * Gqrx is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3, or (at your option)
11  * any later version.
12  *
13  * Gqrx is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Gqrx; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street,
21  * Boston, MA 02110-1301, USA.
22  */
23 #include <QDebug>
24 #include <QVariant>
25 #include <QShortcut>
26 #include <iostream>
27 #include "dockrxopt.h"
28 #include "ui_dockrxopt.h"
29 
30 
31 QStringList DockRxOpt::ModulationStrings;
32 
33 // Lookup table for conversion from old settings
34 static const int old2new[] = {
35     DockRxOpt::MODE_OFF,
36     DockRxOpt::MODE_RAW,
37     DockRxOpt::MODE_AM,
38     DockRxOpt::MODE_NFM,
39     DockRxOpt::MODE_WFM_MONO,
40     DockRxOpt::MODE_WFM_STEREO,
41     DockRxOpt::MODE_LSB,
42     DockRxOpt::MODE_USB,
43     DockRxOpt::MODE_CWL,
44     DockRxOpt::MODE_CWU,
45     DockRxOpt::MODE_WFM_STEREO_OIRT,
46     DockRxOpt::MODE_AM_SYNC
47 };
48 
49 // Filter preset table per mode, preset and lo/hi
50 static const int filter_preset_table[DockRxOpt::MODE_LAST][3][2] =
51 {   //     WIDE             NORMAL            NARROW
52     {{      0,      0}, {     0,     0}, {     0,     0}},  // MODE_OFF
53     {{ -15000,  15000}, { -5000,  5000}, { -1000,  1000}},  // MODE_RAW
54     {{ -10000,  10000}, { -5000,  5000}, { -2500,  2500}},  // MODE_AM
55     {{ -10000,  10000}, { -5000,  5000}, { -2500,  2500}},  // MODE_AMSYNC
56     {{  -4000,   -100}, { -2800,  -100}, { -2400,  -300}},  // MODE_LSB
57     {{    100,   4000}, {   100,  2800}, {   300,  2400}},  // MODE_USB
58     {{  -1000,   1000}, {  -250,   250}, {  -100,   100}},  // MODE_CWL
59     {{  -1000,   1000}, {  -250,   250}, {  -100,   100}},  // MODE_CWU
60     {{ -10000,  10000}, { -5000,  5000}, { -2500,  2500}},  // MODE_NFM
61     {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}},  // MODE_WFM_MONO
62     {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}},  // MODE_WFM_STEREO
63     {{-100000, 100000}, {-80000, 80000}, {-60000, 60000}}   // MODE_WFM_STEREO_OIRT
64 };
65 
DockRxOpt(qint64 filterOffsetRange,QWidget * parent)66 DockRxOpt::DockRxOpt(qint64 filterOffsetRange, QWidget *parent) :
67     QDockWidget(parent),
68     ui(new Ui::DockRxOpt),
69     agc_is_on(true),
70     hw_freq_hz(144500000)
71 {
72     ui->setupUi(this);
73 
74     if (ModulationStrings.size() == 0)
75     {
76         // Keep in sync with rxopt_mode_idx and filter_preset_table
77         ModulationStrings.append("Demod Off");
78         ModulationStrings.append("Raw I/Q");
79         ModulationStrings.append("AM");
80         ModulationStrings.append("AM-Sync");
81         ModulationStrings.append("LSB");
82         ModulationStrings.append("USB");
83         ModulationStrings.append("CW-L");
84         ModulationStrings.append("CW-U");
85         ModulationStrings.append("Narrow FM");
86         ModulationStrings.append("WFM (mono)");
87         ModulationStrings.append("WFM (stereo)");
88         ModulationStrings.append("WFM (oirt)");
89     }
90     ui->modeSelector->addItems(ModulationStrings);
91 
92 #ifdef Q_OS_LINUX
93     ui->modeButton->setMinimumSize(32, 24);
94     ui->agcButton->setMinimumSize(32, 24);
95     ui->autoSquelchButton->setMinimumSize(32, 24);
96     ui->resetSquelchButton->setMinimumSize(32, 24);
97     ui->nbOptButton->setMinimumSize(32, 24);
98     ui->nb2Button->setMinimumSize(32, 24);
99     ui->nb1Button->setMinimumSize(32, 24);
100 #endif
101 
102     ui->filterFreq->setup(7, -filterOffsetRange/2, filterOffsetRange/2, 1,
103                           FCTL_UNIT_KHZ);
104     ui->filterFreq->setFrequency(0);
105 
106     // use same slot for filteCombo and filterShapeCombo
107     connect(ui->filterShapeCombo, SIGNAL(activated(int)), this, SLOT(on_filterCombo_activated(int)));
108 
109     // demodulator options dialog
110     demodOpt = new CDemodOptions(this);
111     demodOpt->setCurrentPage(CDemodOptions::PAGE_FM_OPT);
112     connect(demodOpt, SIGNAL(fmMaxdevSelected(float)), this, SLOT(demodOpt_fmMaxdevSelected(float)));
113     connect(demodOpt, SIGNAL(fmEmphSelected(double)), this, SLOT(demodOpt_fmEmphSelected(double)));
114     connect(demodOpt, SIGNAL(amDcrToggled(bool)), this, SLOT(demodOpt_amDcrToggled(bool)));
115     connect(demodOpt, SIGNAL(cwOffsetChanged(int)), this, SLOT(demodOpt_cwOffsetChanged(int)));
116     connect(demodOpt, SIGNAL(amSyncDcrToggled(bool)), this, SLOT(demodOpt_amSyncDcrToggled(bool)));
117     connect(demodOpt, SIGNAL(amSyncPllBwSelected(float)), this, SLOT(demodOpt_amSyncPllBwSelected(float)));
118 
119     // AGC options dialog
120     agcOpt = new CAgcOptions(this);
121     connect(agcOpt, SIGNAL(gainChanged(int)), this, SLOT(agcOpt_gainChanged(int)));
122     connect(agcOpt, SIGNAL(thresholdChanged(int)), this, SLOT(agcOpt_thresholdChanged(int)));
123     connect(agcOpt, SIGNAL(decayChanged(int)), this, SLOT(agcOpt_decayChanged(int)));
124     connect(agcOpt, SIGNAL(slopeChanged(int)), this, SLOT(agcOpt_slopeChanged(int)));
125     connect(agcOpt, SIGNAL(hangChanged(bool)), this, SLOT(agcOpt_hangToggled(bool)));
126 
127     // Noise blanker options
128     nbOpt = new CNbOptions(this);
129     connect(nbOpt, SIGNAL(thresholdChanged(int,double)), this, SLOT(nbOpt_thresholdChanged(int,double)));
130 
131     /* mode setting shortcuts */
132     QShortcut *mode_off_shortcut = new QShortcut(QKeySequence(Qt::Key_Exclam), this);
133     QShortcut *mode_raw_shortcut = new QShortcut(QKeySequence(Qt::Key_I), this);
134     QShortcut *mode_am_shortcut = new QShortcut(QKeySequence(Qt::Key_A), this);
135     QShortcut *mode_nfm_shortcut = new QShortcut(QKeySequence(Qt::Key_N), this);
136     QShortcut *mode_wfm_mono_shortcut = new QShortcut(QKeySequence(Qt::Key_W), this);
137     QShortcut *mode_wfm_stereo_shortcut = new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_W), this);
138     QShortcut *mode_lsb_shortcut = new QShortcut(QKeySequence(Qt::Key_S), this);
139     QShortcut *mode_usb_shortcut = new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_S), this);
140     QShortcut *mode_cwl_shortcut = new QShortcut(QKeySequence(Qt::Key_C), this);
141     QShortcut *mode_cwu_shortcut = new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_C), this);
142     QShortcut *mode_wfm_oirt_shortcut = new QShortcut(QKeySequence(Qt::Key_O), this);
143     QShortcut *mode_am_sync_shortcut = new QShortcut(QKeySequence(Qt::SHIFT + Qt::Key_A), this);
144 
145     QObject::connect(mode_off_shortcut, &QShortcut::activated, this, &DockRxOpt::modeOffShortcut);
146     QObject::connect(mode_raw_shortcut, &QShortcut::activated, this, &DockRxOpt::modeRawShortcut);
147     QObject::connect(mode_am_shortcut, &QShortcut::activated, this, &DockRxOpt::modeAMShortcut);
148     QObject::connect(mode_nfm_shortcut, &QShortcut::activated, this, &DockRxOpt::modeNFMShortcut);
149     QObject::connect(mode_wfm_mono_shortcut, &QShortcut::activated, this, &DockRxOpt::modeWFMmonoShortcut);
150     QObject::connect(mode_wfm_stereo_shortcut, &QShortcut::activated, this, &DockRxOpt::modeWFMstereoShortcut);
151     QObject::connect(mode_lsb_shortcut, &QShortcut::activated, this, &DockRxOpt::modeLSBShortcut);
152     QObject::connect(mode_usb_shortcut, &QShortcut::activated, this, &DockRxOpt::modeUSBShortcut);
153     QObject::connect(mode_cwl_shortcut, &QShortcut::activated, this, &DockRxOpt::modeCWLShortcut);
154     QObject::connect(mode_cwu_shortcut, &QShortcut::activated, this, &DockRxOpt::modeCWUShortcut);
155     QObject::connect(mode_wfm_oirt_shortcut, &QShortcut::activated, this, &DockRxOpt::modeWFMoirtShortcut);
156     QObject::connect(mode_am_sync_shortcut, &QShortcut::activated, this, &DockRxOpt::modeAMsyncShortcut);
157 
158     /* squelch shortcuts */
159     QShortcut *squelch_reset_shortcut = new QShortcut(QKeySequence(Qt::Key_QuoteLeft), this);
160     QShortcut *squelch_auto_shortcut = new QShortcut(QKeySequence(Qt::Key_AsciiTilde), this);
161 
162     QObject::connect(squelch_reset_shortcut, &QShortcut::activated, this, &DockRxOpt::on_resetSquelchButton_clicked);
163     QObject::connect(squelch_auto_shortcut, &QShortcut::activated, this, &DockRxOpt::on_autoSquelchButton_clicked);
164 
165     /* filter width shortcuts */
166     QShortcut *filter_narrow_shortcut = new QShortcut(QKeySequence(Qt::Key_Less), this);
167     QShortcut *filter_normal_shortcut = new QShortcut(QKeySequence(Qt::Key_Period), this);
168     QShortcut *filter_wide_shortcut = new QShortcut(QKeySequence(Qt::Key_Greater), this);
169 
170     QObject::connect(filter_narrow_shortcut, &QShortcut::activated, this, &DockRxOpt::filterNarrowShortcut);
171     QObject::connect(filter_normal_shortcut, &QShortcut::activated, this, &DockRxOpt::filterNormalShortcut);
172     QObject::connect(filter_wide_shortcut, &QShortcut::activated, this, &DockRxOpt::filterWideShortcut);
173 }
174 
~DockRxOpt()175 DockRxOpt::~DockRxOpt()
176 {
177     delete ui;
178     delete demodOpt;
179     delete agcOpt;
180     delete nbOpt;
181 }
182 
183 /**
184  * @brief Set value of channel filter offset selector.
185  * @param freq_hz The frequency in Hz
186  */
setFilterOffset(qint64 freq_hz)187 void DockRxOpt::setFilterOffset(qint64 freq_hz)
188 {
189     ui->filterFreq->setFrequency(freq_hz);
190 }
191 
192 /**
193  * @brief Set filter offset range.
194  * @param range_hz The new range in Hz.
195  */
setFilterOffsetRange(qint64 range_hz)196 void DockRxOpt::setFilterOffsetRange(qint64 range_hz)
197 {
198     int num_digits;
199 
200     if (range_hz <= 0)
201         return;
202 
203     range_hz /= 2;
204     if (range_hz < 100e3)
205         num_digits = 5;
206     else if (range_hz < 1e6)
207         num_digits = 6;
208     else if (range_hz < 1e7)
209         num_digits = 7;
210     else if (range_hz < 1e8)
211         num_digits = 8;
212     else
213         num_digits = 9;
214 
215     ui->filterFreq->setup(num_digits, -range_hz, range_hz, 1, FCTL_UNIT_KHZ);
216 }
217 
218 /**
219  * @brief Set new RF frequency
220  * @param freq_hz The frequency in Hz
221  *
222  * RF frequency is the frequency to which the device device is tuned to
223  * The actual RX frequency is the sum of the RF frequency and the filter
224  * offset.
225  */
setHwFreq(qint64 freq_hz)226 void DockRxOpt::setHwFreq(qint64 freq_hz)
227 {
228     hw_freq_hz = freq_hz;
229     updateHwFreq();
230 }
231 
232 /** Update RX frequency label. */
updateHwFreq()233 void DockRxOpt::updateHwFreq()
234 {
235     double hw_freq_mhz = hw_freq_hz / 1.0e6;
236     ui->hwFreq->setText(QString("%1 MHz").arg(hw_freq_mhz, 11, 'f', 6, ' '));
237 }
238 
239 /**
240  * Get filter index from filter LO / HI values.
241  * @param lo The filter low cut frequency.
242  * @param hi The filter high cut frequency.
243  *
244  * Given filter low and high cut frequencies, this function checks whether the
245  * filter settings correspond to one of the presets in filter_preset_table and
246  * returns the corresponding index to ui->filterCombo;
247  */
filterIdxFromLoHi(int lo,int hi) const248 unsigned int DockRxOpt::filterIdxFromLoHi(int lo, int hi) const
249 {
250     int mode_index = ui->modeSelector->currentIndex();
251 
252     if (lo == filter_preset_table[mode_index][FILTER_PRESET_WIDE][0] &&
253         hi == filter_preset_table[mode_index][FILTER_PRESET_WIDE][1])
254         return FILTER_PRESET_WIDE;
255     else if (lo == filter_preset_table[mode_index][FILTER_PRESET_NORMAL][0] &&
256              hi == filter_preset_table[mode_index][FILTER_PRESET_NORMAL][1])
257         return FILTER_PRESET_NORMAL;
258     else if (lo == filter_preset_table[mode_index][FILTER_PRESET_NARROW][0] &&
259              hi == filter_preset_table[mode_index][FILTER_PRESET_NARROW][1])
260         return FILTER_PRESET_NARROW;
261 
262     return FILTER_PRESET_USER;
263 }
264 
265 /**
266  * @brief Set filter parameters
267  * @param lo Low cutoff frequency in Hz
268  * @param hi High cutoff frequency in Hz.
269  *
270  * This function will automatically select the "User" preset in the
271  * combo box.
272  */
setFilterParam(int lo,int hi)273 void DockRxOpt::setFilterParam(int lo, int hi)
274 {
275     int filter_index = filterIdxFromLoHi(lo, hi);
276 
277     ui->filterCombo->setCurrentIndex(filter_index);
278     if (filter_index == FILTER_PRESET_USER)
279     {
280         float width_f;
281         width_f = fabs((hi-lo)/1000.f);
282         ui->filterCombo->setItemText(FILTER_PRESET_USER, QString("User (%1 k)")
283                                      .arg(width_f));
284     }
285 }
286 
287 /**
288  * @brief Select new filter preset.
289  * @param index Index of the new filter preset (0=wide, 1=normal, 2=narrow).
290  */
setCurrentFilter(int index)291 void DockRxOpt::setCurrentFilter(int index)
292 {
293     ui->filterCombo->setCurrentIndex(index);
294 }
295 
296 /**
297  * @brief Get current filter preset.
298  * @param The current filter preset (0=wide, 1=normal, 2=narrow).
299  */
currentFilter() const300 int  DockRxOpt::currentFilter() const
301 {
302     return ui->filterCombo->currentIndex();
303 }
304 
305 /** Select filter shape */
setCurrentFilterShape(int index)306 void DockRxOpt::setCurrentFilterShape(int index)
307 {
308     ui->filterShapeCombo->setCurrentIndex(index);
309 }
310 
currentFilterShape() const311 int  DockRxOpt::currentFilterShape() const
312 {
313     return ui->filterShapeCombo->currentIndex();
314 }
315 
316 /**
317  * @brief Select new demodulator.
318  * @param demod Demodulator index corresponding to receiver::demod.
319  */
setCurrentDemod(int demod)320 void DockRxOpt::setCurrentDemod(int demod)
321 {
322     if ((demod >= MODE_OFF) && (demod < MODE_LAST))
323     {
324         ui->modeSelector->setCurrentIndex(demod);
325         updateDemodOptPage(demod);
326     }
327 }
328 
329 /**
330  * @brief Get current demodulator selection.
331  * @return The current demodulator corresponding to receiver::demod.
332  */
currentDemod() const333 int  DockRxOpt::currentDemod() const
334 {
335     return ui->modeSelector->currentIndex();
336 }
337 
currentDemodAsString()338 QString DockRxOpt::currentDemodAsString()
339 {
340     return GetStringForModulationIndex(currentDemod());
341 }
342 
currentMaxdev() const343 float DockRxOpt::currentMaxdev() const
344 {
345     return demodOpt->getMaxDev();
346 }
347 
currentEmph() const348 double DockRxOpt::currentEmph() const
349 {
350     return demodOpt->getEmph();
351 }
352 
353 /**
354  * @brief Set squelch level.
355  * @param level Squelch level in dBFS
356  */
setSquelchLevel(double level)357 void DockRxOpt::setSquelchLevel(double level)
358 {
359     ui->sqlSpinBox->setValue(level);
360 }
361 
getSqlLevel(void) const362 double DockRxOpt::getSqlLevel(void) const
363 {
364     return ui->sqlSpinBox->value();
365 }
366 
367 /**
368  * @brief Get the current squelch level
369  * @returns The current squelch setting in dBFS
370  */
currentSquelchLevel() const371 double DockRxOpt::currentSquelchLevel() const
372 {
373     return ui->sqlSpinBox->value();
374 }
375 
376 
377 /** Get filter lo/hi for a given mode and preset */
getFilterPreset(int mode,int preset,int * lo,int * hi) const378 void DockRxOpt::getFilterPreset(int mode, int preset, int * lo, int * hi) const
379 {
380     if (mode < 0 || mode >= MODE_LAST)
381     {
382         qDebug() << __func__ << ": Invalid mode:" << mode;
383         mode = MODE_AM;
384     }
385     else if (preset < 0 || preset > 2)
386     {
387         qDebug() << __func__ << ": Invalid preset:" << preset;
388         preset = FILTER_PRESET_NORMAL;
389     }
390     *lo = filter_preset_table[mode][preset][0];
391     *hi = filter_preset_table[mode][preset][1];
392 }
393 
getCwOffset() const394 int DockRxOpt::getCwOffset() const
395 {
396     return demodOpt->getCwOffset();
397 }
398 
399 /** Read receiver configuration from settings data. */
readSettings(QSettings * settings)400 void DockRxOpt::readSettings(QSettings *settings)
401 {
402     bool    conv_ok;
403     int     int_val;
404     double  dbl_val;
405 
406     int_val = settings->value("receiver/cwoffset", 700).toInt(&conv_ok);
407     if (conv_ok)
408         demodOpt->setCwOffset(int_val);
409 
410     int_val = settings->value("receiver/fm_maxdev", 2500).toInt(&conv_ok);
411     if (conv_ok)
412         demodOpt->setMaxDev(int_val);
413 
414     dbl_val = settings->value("receiver/fm_deemph", 75).toDouble(&conv_ok);
415     if (conv_ok && dbl_val >= 0)
416         demodOpt->setEmph(1.0e-6 * dbl_val); // was stored as usec
417 
418     qint64 offs = settings->value("receiver/offset", 0).toInt(&conv_ok);
419     if (offs)
420     {
421         setFilterOffset(offs);
422         emit filterOffsetChanged(offs);
423     }
424 
425     dbl_val = settings->value("receiver/sql_level", 1.0).toDouble(&conv_ok);
426     if (conv_ok && dbl_val < 1.0)
427         ui->sqlSpinBox->setValue(dbl_val);
428 
429     // AGC settings
430     int_val = settings->value("receiver/agc_threshold", -100).toInt(&conv_ok);
431     if (conv_ok)
432         agcOpt->setThreshold(int_val);
433 
434     int_val = settings->value("receiver/agc_decay", 500).toInt(&conv_ok);
435     if (conv_ok)
436     {
437         agcOpt->setDecay(int_val);
438         if (int_val == 100)
439             ui->agcPresetCombo->setCurrentIndex(0);
440         else if (int_val == 500)
441             ui->agcPresetCombo->setCurrentIndex(1);
442         else if (int_val == 2000)
443             ui->agcPresetCombo->setCurrentIndex(2);
444         else
445             ui->agcPresetCombo->setCurrentIndex(3);
446     }
447 
448     int_val = settings->value("receiver/agc_slope", 0).toInt(&conv_ok);
449     if (conv_ok)
450         agcOpt->setSlope(int_val);
451 
452     int_val = settings->value("receiver/agc_gain", 0).toInt(&conv_ok);
453     if (conv_ok)
454         agcOpt->setGain(int_val);
455 
456     agcOpt->setHang(settings->value("receiver/agc_usehang", false).toBool());
457 
458     if (settings->value("receiver/agc_off", false).toBool())
459         ui->agcPresetCombo->setCurrentIndex(4);
460 
461     int_val = MODE_AM;
462     if (settings->contains("receiver/demod")) {
463         if (settings->value("configversion").toInt(&conv_ok) >= 3) {
464             int_val = GetEnumForModulationString(settings->value("receiver/demod").toString());
465         } else {
466             int_val = old2new[settings->value("receiver/demod").toInt(&conv_ok)];
467         }
468     }
469 
470     setCurrentDemod(int_val);
471     emit demodSelected(int_val);
472 
473 }
474 
475 /** Save receiver configuration to settings. */
saveSettings(QSettings * settings)476 void DockRxOpt::saveSettings(QSettings *settings)
477 {
478     int     int_val;
479 
480     settings->setValue("receiver/demod", currentDemodAsString());
481 
482     int cwofs = demodOpt->getCwOffset();
483     if (cwofs == 700)
484         settings->remove("receiver/cwoffset");
485     else
486         settings->setValue("receiver/cwoffset", cwofs);
487 
488     // currently we do not need the decimal
489     int_val = (int)demodOpt->getMaxDev();
490     if (int_val == 2500)
491         settings->remove("receiver/fm_maxdev");
492     else
493         settings->setValue("receiver/fm_maxdev", int_val);
494 
495     // save as usec
496     int_val = (int)(1.0e6 * demodOpt->getEmph());
497     if (int_val == 75)
498         settings->remove("receiver/fm_deemph");
499     else
500         settings->setValue("receiver/fm_deemph", int_val);
501 
502     qint64 offs = ui->filterFreq->getFrequency();
503     if (offs)
504         settings->setValue("receiver/offset", offs);
505     else
506         settings->remove("receiver/offset");
507 
508     qDebug() << __func__ << "*** FIXME_ SQL on/off";
509     //int sql_lvl = double(ui->sqlSlider->value());  // note: dBFS*10 as int
510     double sql_lvl = ui->sqlSpinBox->value();
511     if (sql_lvl > -150.0)
512         settings->setValue("receiver/sql_level", sql_lvl);
513     else
514         settings->remove("receiver/sql_level");
515 
516     // AGC settings
517     int_val = agcOpt->threshold();
518     if (int_val != -100)
519         settings->setValue("receiver/agc_threshold", int_val);
520     else
521         settings->remove("receiver/agc_threshold");
522 
523     int_val = agcOpt->decay();
524     if (int_val != 500)
525         settings->setValue("receiver/agc_decay", int_val);
526     else
527         settings->remove("receiver/agc_decay");
528 
529     int_val = agcOpt->slope();
530     if (int_val != 0)
531         settings->setValue("receiver/agc_slope", int_val);
532     else
533         settings->remove("receiver/agc_slope");
534 
535     int_val = agcOpt->gain();
536     if (int_val != 0)
537         settings->setValue("receiver/agc_gain", int_val);
538     else
539         settings->remove("receiver/agc_gain");
540 
541     if (agcOpt->hang())
542         settings->setValue("receiver/agc_usehang", true);
543     else
544         settings->remove("receiver/agc_usehang");
545 
546     // AGC Off
547     if (ui->agcPresetCombo->currentIndex() == 4)
548         settings->setValue("receiver/agc_off", true);
549     else
550         settings->remove("receiver/agc_off");
551 }
552 
553 /** RX frequency changed through spin box */
on_freqSpinBox_valueChanged(double freq)554 void DockRxOpt::on_freqSpinBox_valueChanged(double freq)
555 {
556     emit rxFreqChanged(1.e3 * freq);
557 }
558 
setRxFreq(qint64 freq_hz)559 void DockRxOpt::setRxFreq(qint64 freq_hz)
560 {
561     ui->freqSpinBox->blockSignals(true);
562     ui->freqSpinBox->setValue(1.e-3 * (double)freq_hz);
563     ui->freqSpinBox->blockSignals(false);
564 }
565 
setRxFreqRange(qint64 min_hz,qint64 max_hz)566 void DockRxOpt::setRxFreqRange(qint64 min_hz, qint64 max_hz)
567 {
568     ui->freqSpinBox->blockSignals(true);
569     ui->freqSpinBox->setRange(1.e-3 * (double)min_hz, 1.e-3 * (double)max_hz);
570     ui->freqSpinBox->blockSignals(false);
571 }
572 
setResetLowerDigits(bool enabled)573 void DockRxOpt::setResetLowerDigits(bool enabled)
574 {
575     ui->filterFreq->setResetLowerDigits(enabled);
576 }
577 
setInvertScrolling(bool enabled)578 void DockRxOpt::setInvertScrolling(bool enabled)
579 {
580     ui->filterFreq->setInvertScrolling(enabled);
581 }
582 
583 /**
584  * @brief Channel filter offset has changed
585  * @param freq The new filter offset in Hz
586  *
587  * This slot is activated when a new filter offset has been selected either
588  * using the mouse or using the keyboard.
589  */
on_filterFreq_newFrequency(qint64 freq)590 void DockRxOpt::on_filterFreq_newFrequency(qint64 freq)
591 {
592     updateHwFreq();
593 
594     emit filterOffsetChanged(freq);
595 }
596 
597 /**
598  * New filter preset selected.
599  *
600  * Instead of implementing a new signal, we simply emit demodSelected() since
601  * demodulator and filter preset are tightly coupled.
602  */
on_filterCombo_activated(int index)603 void DockRxOpt::on_filterCombo_activated(int index)
604 {
605     Q_UNUSED(index);
606 
607     qDebug() << "New filter preset:" << ui->filterCombo->currentText();
608     qDebug() << "            shape:" << ui->filterShapeCombo->currentIndex();
609     emit demodSelected(ui->modeSelector->currentIndex());
610 }
611 
612 /**
613  * @brief Mode selector activated.
614  * @param New mode selection.
615  *
616  * This slot is activated when the user selects a new demodulator (mode change).
617  * It is connected automatically by the UI constructor, and it emits the demodSelected()
618  * signal.
619  *
620  * Note that the modes listed in the selector are different from those defined by
621  * receiver::demod (we want to list LSB/USB separately but they have identical demods).
622  */
on_modeSelector_activated(int index)623 void DockRxOpt::on_modeSelector_activated(int index)
624 {
625     updateDemodOptPage(index);
626     emit demodSelected(index);
627 }
628 
updateDemodOptPage(int demod)629 void DockRxOpt::updateDemodOptPage(int demod)
630 {
631     // update demodulator option widget
632     if (demod == MODE_NFM)
633         demodOpt->setCurrentPage(CDemodOptions::PAGE_FM_OPT);
634     else if (demod == MODE_AM)
635         demodOpt->setCurrentPage(CDemodOptions::PAGE_AM_OPT);
636     else if (demod == MODE_CWL || demod == MODE_CWU)
637         demodOpt->setCurrentPage(CDemodOptions::PAGE_CW_OPT);
638     else if (demod == MODE_AM_SYNC)
639         demodOpt->setCurrentPage(CDemodOptions::PAGE_AMSYNC_OPT);
640     else
641         demodOpt->setCurrentPage(CDemodOptions::PAGE_NO_OPT);
642 }
643 
644 /** Show demodulator options. */
on_modeButton_clicked()645 void DockRxOpt::on_modeButton_clicked()
646 {
647     demodOpt->show();
648 }
649 
650 /** Show AGC options. */
on_agcButton_clicked()651 void DockRxOpt::on_agcButton_clicked()
652 {
653     agcOpt->show();
654 }
655 
656 /**
657  * @brief Auto-squelch button clicked.
658  *
659  * This slot is called when the user clicks on the auto-squelch button.
660  */
on_autoSquelchButton_clicked()661 void DockRxOpt::on_autoSquelchButton_clicked()
662 {
663     double newval = sqlAutoClicked(); // FIXME: We rely on signal only being connected to one slot
664     ui->sqlSpinBox->setValue(newval);
665 }
666 
on_resetSquelchButton_clicked()667 void DockRxOpt::on_resetSquelchButton_clicked()
668 {
669     ui->sqlSpinBox->setValue(-150.0);
670 }
671 
672 /** AGC preset has changed. */
on_agcPresetCombo_currentIndexChanged(int index)673 void DockRxOpt::on_agcPresetCombo_currentIndexChanged(int index)
674 {
675     CAgcOptions::agc_preset_e preset = (CAgcOptions::agc_preset_e) index;
676 
677     switch (preset)
678     {
679     case CAgcOptions::AGC_FAST:
680     case CAgcOptions::AGC_MEDIUM:
681     case CAgcOptions::AGC_SLOW:
682     case CAgcOptions::AGC_USER:
683         if (!agc_is_on)
684         {
685             emit agcToggled(true);
686             agc_is_on = true;
687         }
688         agcOpt->setPreset(preset);
689         break;
690 
691     case CAgcOptions::AGC_OFF:
692         if (agc_is_on)
693         {
694             emit agcToggled(false);
695             agc_is_on = false;
696         }
697         agcOpt->setPreset(preset);
698         break;
699 
700     default:
701         qDebug() << "Invalid AGC preset:" << index;
702     }
703 }
704 
agcOpt_hangToggled(bool checked)705 void DockRxOpt::agcOpt_hangToggled(bool checked)
706 {
707     emit agcHangToggled(checked);
708 }
709 
710 /**
711  * @brief AGC threshold ("knee") changed.
712  * @param value The new AGC threshold in dB.
713  */
agcOpt_thresholdChanged(int value)714 void DockRxOpt::agcOpt_thresholdChanged(int value)
715 {
716     emit agcThresholdChanged(value);
717 }
718 
719 /**
720  * @brief AGC slope factor changed.
721  * @param value The new slope factor in dB.
722  */
agcOpt_slopeChanged(int value)723 void DockRxOpt::agcOpt_slopeChanged(int value)
724 {
725     emit agcSlopeChanged(value);
726 }
727 
728 /**
729  * @brief AGC decay changed.
730  * @param value The new decay rate in ms (tbc).
731  */
agcOpt_decayChanged(int value)732 void DockRxOpt::agcOpt_decayChanged(int value)
733 {
734     emit agcDecayChanged(value);
735 }
736 
737 /**
738  * @brief AGC manual gain changed.
739  * @param gain The new gain in dB.
740  */
agcOpt_gainChanged(int gain)741 void DockRxOpt::agcOpt_gainChanged(int gain)
742 {
743     emit agcGainChanged(gain);
744 }
745 
746 /**
747  * @brief Squelch level change.
748  * @param value The new squelch level in dB.
749  */
on_sqlSpinBox_valueChanged(double value)750 void DockRxOpt::on_sqlSpinBox_valueChanged(double value)
751 {
752     emit sqlLevelChanged(value);
753 }
754 
755 /**
756  * @brief FM deviation changed by user.
757  * @param max_dev The new deviation in Hz.
758  */
demodOpt_fmMaxdevSelected(float max_dev)759 void DockRxOpt::demodOpt_fmMaxdevSelected(float max_dev)
760 {
761     emit fmMaxdevSelected(max_dev);
762 }
763 
764 /**
765  * @brief FM de-emphasis changed by user.
766  * @param tau The new time constant in uS.
767  */
demodOpt_fmEmphSelected(double tau)768 void DockRxOpt::demodOpt_fmEmphSelected(double tau)
769 {
770     emit fmEmphSelected(tau);
771 }
772 
773 /**
774  * @brief AM DC removal toggled by user.
775  * @param enabled Whether DCR is enabled or not.
776  */
demodOpt_amDcrToggled(bool enabled)777 void DockRxOpt::demodOpt_amDcrToggled(bool enabled)
778 {
779     emit amDcrToggled(enabled);
780 }
781 
demodOpt_cwOffsetChanged(int offset)782 void DockRxOpt::demodOpt_cwOffsetChanged(int offset)
783 {
784     emit cwOffsetChanged(offset);
785 }
786 
787 /**
788  * @brief AM-Sync DC removal toggled by user.
789  * @param enabled Whether DCR is enabled or not.
790  */
demodOpt_amSyncDcrToggled(bool enabled)791 void DockRxOpt::demodOpt_amSyncDcrToggled(bool enabled)
792 {
793     emit amSyncDcrToggled(enabled);
794 }
795 
796 /**
797  * @brief AM-Sync PLL BW changed by user.
798  * @param pll_bw The new PLL BW.
799  */
demodOpt_amSyncPllBwSelected(float pll_bw)800 void DockRxOpt::demodOpt_amSyncPllBwSelected(float pll_bw)
801 {
802     emit amSyncPllBwSelected(pll_bw);
803 }
804 
805 /** Noise blanker 1 button has been toggled. */
on_nb1Button_toggled(bool checked)806 void DockRxOpt::on_nb1Button_toggled(bool checked)
807 {
808     emit noiseBlankerChanged(1, checked, (float) nbOpt->nbThreshold(1));
809 }
810 
811 /** Noise blanker 2 button has been toggled. */
on_nb2Button_toggled(bool checked)812 void DockRxOpt::on_nb2Button_toggled(bool checked)
813 {
814     emit noiseBlankerChanged(2, checked, (float) nbOpt->nbThreshold(2));
815 }
816 
817 /** Noise blanker threshold has been changed. */
nbOpt_thresholdChanged(int nbid,double value)818 void DockRxOpt::nbOpt_thresholdChanged(int nbid, double value)
819 {
820     if (nbid == 1)
821         emit noiseBlankerChanged(nbid, ui->nb1Button->isChecked(), (float) value);
822     else
823         emit noiseBlankerChanged(nbid, ui->nb2Button->isChecked(), (float) value);
824 }
825 
on_nbOptButton_clicked()826 void DockRxOpt::on_nbOptButton_clicked()
827 {
828     nbOpt->show();
829 }
830 
GetEnumForModulationString(QString param)831 int DockRxOpt::GetEnumForModulationString(QString param)
832 {
833     int iModulation = -1;
834     for(int i=0; i<DockRxOpt::ModulationStrings.size(); ++i)
835     {
836         QString& strModulation = DockRxOpt::ModulationStrings[i];
837         if(param.compare(strModulation, Qt::CaseInsensitive)==0)
838         {
839             iModulation = i;
840             break;
841         }
842     }
843     if(iModulation == -1)
844     {
845         std::cout << "Modulation '" << param.toStdString() << "' is unknown." << std::endl;
846         iModulation = MODE_OFF;
847     }
848     return iModulation;
849 }
850 
IsModulationValid(QString strModulation)851 bool DockRxOpt::IsModulationValid(QString strModulation)
852 {
853     return DockRxOpt::ModulationStrings.contains(strModulation, Qt::CaseInsensitive);
854 }
855 
GetStringForModulationIndex(int iModulationIndex)856 QString DockRxOpt::GetStringForModulationIndex(int iModulationIndex)
857 {
858     return ModulationStrings[iModulationIndex];
859 }
860 
modeOffShortcut()861 void DockRxOpt::modeOffShortcut() {
862     on_modeSelector_activated(MODE_OFF);
863 }
864 
modeRawShortcut()865 void DockRxOpt::modeRawShortcut() {
866     on_modeSelector_activated(MODE_RAW);
867 }
868 
modeAMShortcut()869 void DockRxOpt::modeAMShortcut() {
870     on_modeSelector_activated(MODE_AM);
871 }
872 
modeNFMShortcut()873 void DockRxOpt::modeNFMShortcut() {
874     on_modeSelector_activated(MODE_NFM);
875 }
876 
modeWFMmonoShortcut()877 void DockRxOpt::modeWFMmonoShortcut() {
878     on_modeSelector_activated(MODE_WFM_MONO);
879 }
880 
modeWFMstereoShortcut()881 void DockRxOpt::modeWFMstereoShortcut() {
882     on_modeSelector_activated(MODE_WFM_STEREO);
883 }
884 
modeLSBShortcut()885 void DockRxOpt::modeLSBShortcut() {
886     on_modeSelector_activated(MODE_LSB);
887 }
888 
modeUSBShortcut()889 void DockRxOpt::modeUSBShortcut() {
890     on_modeSelector_activated(MODE_USB);
891 }
892 
modeCWLShortcut()893 void DockRxOpt::modeCWLShortcut() {
894     on_modeSelector_activated(MODE_CWL);
895 }
896 
modeCWUShortcut()897 void DockRxOpt::modeCWUShortcut() {
898     on_modeSelector_activated(MODE_CWU);
899 }
900 
modeWFMoirtShortcut()901 void DockRxOpt::modeWFMoirtShortcut() {
902     on_modeSelector_activated(MODE_WFM_STEREO_OIRT);
903 }
904 
modeAMsyncShortcut()905 void DockRxOpt::modeAMsyncShortcut() {
906     on_modeSelector_activated(MODE_AM_SYNC);
907 }
908 
filterNarrowShortcut()909 void DockRxOpt::filterNarrowShortcut() {
910     setCurrentFilter(FILTER_PRESET_NARROW);
911     on_filterCombo_activated(FILTER_PRESET_NARROW);
912 }
913 
filterNormalShortcut()914 void DockRxOpt::filterNormalShortcut() {
915     setCurrentFilter(FILTER_PRESET_NORMAL);
916     on_filterCombo_activated(FILTER_PRESET_NORMAL);
917 }
918 
filterWideShortcut()919 void DockRxOpt::filterWideShortcut() {
920     setCurrentFilter(FILTER_PRESET_WIDE);
921     on_filterCombo_activated(FILTER_PRESET_WIDE);
922 }
923