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