From 98c703bfbf40e68a1fbc9dd10df853cee0583772 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 21 Feb 2026 03:17:15 +0100 Subject: [PATCH] Freq scanner: implemented device frequency locking --- plugins/channelrx/freqscanner/freqscanner.cpp | 141 ++++++++++++------ .../channelrx/freqscanner/freqscannergui.cpp | 16 ++ .../channelrx/freqscanner/freqscannergui.h | 2 + .../channelrx/freqscanner/freqscannergui.ui | 28 +++- .../freqscanner/freqscannersettings.cpp | 9 ++ .../freqscanner/freqscannersettings.h | 2 + 6 files changed, 148 insertions(+), 50 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 45d57f81d..cc3b359d7 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -314,7 +314,7 @@ void FreqScanner::initScan() // } mute(m_scanDeviceSetIndex, m_scanChannelIndex); - if (m_centerFrequency != m_stepStartFrequency) { + if (!m_settings.m_lockDeviceFrequency && (m_centerFrequency != m_stepStartFrequency)) { setDeviceCenterFrequency(m_stepStartFrequency); } @@ -386,54 +386,77 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< } // Calculate next center frequency - bool complete = false; // Have all frequencies been scanned? - bool freqInRange = false; + const bool lockDeviceFrequency = m_settings.m_lockDeviceFrequency; + const qint64 currentCenterFrequency = m_centerFrequency; + bool complete = lockDeviceFrequency; // Have all frequencies been scanned? + bool freqInRange = lockDeviceFrequency; qint64 nextCenterFrequency = m_centerFrequency; int usableBW = (m_scannerSampleRate * 3 / 4) & ~1; - int nextFrequencyIndex = 0; - do + int nextFrequencyIndex = -1; + const auto isInCurrentScanRange = [currentCenterFrequency, usableBW](qint64 frequency) { - if (nextCenterFrequency + usableBW / 2 > m_stepStopFrequency) - { - nextCenterFrequency = m_stepStartFrequency; - complete = true; - } - else - { - nextCenterFrequency += usableBW; - complete = false; - } + return (frequency >= currentCenterFrequency - usableBW / 2) + && (frequency < currentCenterFrequency + usableBW / 2); + }; - // Are any frequencies in this new range? + if (!lockDeviceFrequency) + { + do + { + if (nextCenterFrequency + usableBW / 2 > m_stepStopFrequency) + { + nextCenterFrequency = m_stepStartFrequency; + complete = true; + } + else + { + nextCenterFrequency += usableBW; + complete = false; + } + + // Are any frequencies in this new range? + for (int i = 0; i < m_settings.m_frequencySettings.size(); i++) + { + if (m_settings.m_frequencySettings[i].m_enabled + && (m_settings.m_frequencySettings[i].m_frequency >= nextCenterFrequency - usableBW / 2) + && (m_settings.m_frequencySettings[i].m_frequency < nextCenterFrequency + usableBW / 2)) + { + freqInRange = true; + nextFrequencyIndex = i; + + // Do we need to realign for frequencies with wider bandwidths than default + if (!m_settings.m_frequencySettings[i].m_channelBandwidth.isEmpty()) + { + bool ok; + int channelBW = m_settings.m_frequencySettings[i].m_channelBandwidth.toInt(&ok); + if (ok) + { + if (channelBW >= usableBW) { + nextCenterFrequency = m_settings.m_frequencySettings[i].m_frequency; + } else if (m_settings.m_frequencySettings[i].m_frequency - channelBW / 2 < nextCenterFrequency - usableBW / 2) { + nextCenterFrequency = m_settings.m_frequencySettings[i].m_frequency - channelBW / 2; + } + } + } + + break; + } + } + } + while (!complete && !freqInRange); + } + else + { for (int i = 0; i < m_settings.m_frequencySettings.size(); i++) { if (m_settings.m_frequencySettings[i].m_enabled - && (m_settings.m_frequencySettings[i].m_frequency >= nextCenterFrequency - usableBW / 2) - && (m_settings.m_frequencySettings[i].m_frequency < nextCenterFrequency + usableBW / 2)) + && isInCurrentScanRange(m_settings.m_frequencySettings[i].m_frequency)) { - freqInRange = true; nextFrequencyIndex = i; - - // Do we need to realign for frequencies with wider bandwidths than default - if (!m_settings.m_frequencySettings[i].m_channelBandwidth.isEmpty()) - { - bool ok; - int channelBW = m_settings.m_frequencySettings[i].m_channelBandwidth.toInt(&ok); - if (ok) - { - if (channelBW >= usableBW) { - nextCenterFrequency = m_settings.m_frequencySettings[i].m_frequency; - } else if (m_settings.m_frequencySettings[i].m_frequency - channelBW / 2 < nextCenterFrequency - usableBW / 2) { - nextCenterFrequency = m_settings.m_frequencySettings[i].m_frequency - channelBW / 2; - } - } - } - break; } } } - while (!complete && !freqInRange); if (complete || (m_settings.m_mode == FreqScannerSettings::MULTIPLEX)) { @@ -454,8 +477,11 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< if (m_settings.m_mode == FreqScannerSettings::MULTIPLEX) { - activeFrequencySettings = &m_settings.m_frequencySettings[nextFrequencyIndex]; - frequency = activeFrequencySettings->m_frequency; + if (nextFrequencyIndex >= 0) + { + activeFrequencySettings = &m_settings.m_frequencySettings[nextFrequencyIndex]; + frequency = activeFrequencySettings->m_frequency; + } } else if (m_settings.m_priority == FreqScannerSettings::MAX_POWER) { @@ -464,6 +490,10 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< // Find frequency with max power that exceeds thresholds for (int i = 0; i < m_scanResults.size(); i++) { + if (lockDeviceFrequency && !isInCurrentScanRange(m_scanResults[i].m_frequency)) { + continue; + } + frequencySettings = m_settings.getFrequencySettings(m_scanResults[i].m_frequency); Real threshold = m_settings.getThreshold(frequencySettings); @@ -486,6 +516,11 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< { int j = m_settings.m_voiceSquelchType == FreqScannerSettings::VoiceSquelchType::VoiceLsb ? m_scanResults.size()-1 - i : i; + + if (lockDeviceFrequency && !isInCurrentScanRange(m_scanResults[j].m_frequency)) { + continue; + } + frequencySettings = m_settings.getFrequencySettings(m_scanResults[j].m_frequency); Real threshold = m_settings.getThreshold(frequencySettings); @@ -520,20 +555,23 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< } // Ensure we have minimum offset from DC - if (offset >= 0) + if (!lockDeviceFrequency) { - while (offset < m_settings.m_channelFrequencyOffset) + if (offset >= 0) { - nextCenterFrequency -= m_settings.m_channelBandwidth; - offset += m_settings.m_channelBandwidth; + while (offset < m_settings.m_channelFrequencyOffset) + { + nextCenterFrequency -= m_settings.m_channelBandwidth; + offset += m_settings.m_channelBandwidth; + } } - } - else - { - while (abs(offset) < m_settings.m_channelFrequencyOffset) + else { - nextCenterFrequency += m_settings.m_channelBandwidth; - offset -= m_settings.m_channelBandwidth; + while (abs(offset) < m_settings.m_channelFrequencyOffset) + { + nextCenterFrequency += m_settings.m_channelBandwidth; + offset -= m_settings.m_channelBandwidth; + } } } @@ -599,7 +637,12 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< } } - if (nextCenterFrequency != m_centerFrequency) { + if (m_settings.m_lockDeviceFrequency) { + nextCenterFrequency = m_centerFrequency; + } + + if (nextCenterFrequency != m_centerFrequency) + { setDeviceCenterFrequency(nextCenterFrequency); } diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index dc26d7d0f..5b8c58499 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -197,6 +197,8 @@ bool FreqScannerGUI::handleMessage(const Message& message) int row = item->row(); QTableWidgetItem* powerItem = ui->table->item(row, COL_POWER); powerItem->setData(Qt::DisplayRole, results[i].m_power); + QTableWidgetItem* vadItem = ui->table->item(row, COL_VAD); + vadItem->setData(Qt::DisplayRole, results[i].m_voiceActivityLevel); FreqScannerSettings::FrequencySettings *frequencySettings = m_settings.getFrequencySettings(freq); Real threshold = m_settings.getThreshold(frequencySettings); bool active = results[i].m_power >= threshold; @@ -405,6 +407,12 @@ void FreqScannerGUI::on_voiceSquelchType_currentIndexChanged(int index) applySettings(settingsKeys); } +void FreqScannerGUI::on_lockDeviceFrequency_toggled(bool checked) +{ + m_settings.m_lockDeviceFrequency = checked; + applySetting("lockDeviceFrequency"); +} + void FreqScannerGUI::on_priority_currentIndexChanged(int index) { m_settings.m_priority = (FreqScannerSettings::Priority)index; @@ -584,6 +592,7 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->table->setItemDelegateForColumn(COL_FREQUENCY, new FrequencyDelegate("Auto", 3, true, ui->table)); ui->table->setItemDelegateForColumn(COL_POWER, new DecimalDelegate(1, ui->table)); + ui->table->setItemDelegateForColumn(COL_VAD, new DecimalDelegate(2, ui->table)); ui->table->setItemDelegateForColumn(COL_CHANNEL_BW, new Int64Delegate(0, 10000000, ui->table)); ui->table->setItemDelegateForColumn(COL_TH, new DecimalDelegate(1, -120.0, 0.0, ui->table)); ui->table->setItemDelegateForColumn(COL_SQ, new DecimalDelegate(1, -120.0, 0.0, ui->table)); @@ -641,6 +650,7 @@ void FreqScannerGUI::displaySettings() ui->channels->setCurrentIndex(channelIndex); } ui->deltaFrequency->setValue(m_settings.m_channelFrequencyOffset); + ui->deviceFreqLock->setChecked(m_settings.m_lockDeviceFrequency); ui->channelBandwidth->setValue(m_settings.m_channelBandwidth); ui->channelShift->setValue(m_settings.m_channelShift); ui->scanTime->setValue(m_settings.m_scanTime * 10.0); @@ -740,6 +750,10 @@ void FreqScannerGUI::addRow(const FreqScannerSettings::FrequencySettings& freque powerItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->table->setItem(row, COL_POWER, powerItem); + QTableWidgetItem* vadItem = new QTableWidgetItem(); + vadItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + ui->table->setItem(row, COL_VAD, vadItem); + QTableWidgetItem *activeCountItem = new QTableWidgetItem(); activeCountItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); ui->table->setItem(row, COL_ACTIVE_COUNT, activeCountItem); @@ -1296,6 +1310,7 @@ void FreqScannerGUI::resizeTable() ui->table->setItem(row, COL_ANNOTATION, new QTableWidgetItem("London VOLMET")); ui->table->setItem(row, COL_ENABLE, new QTableWidgetItem("Enable")); ui->table->setItem(row, COL_POWER, new QTableWidgetItem("-100.0")); + ui->table->setItem(row, COL_VAD, new QTableWidgetItem("0.00")); ui->table->setItem(row, COL_ACTIVE_COUNT, new QTableWidgetItem("10000")); ui->table->setItem(row, COL_NOTES, new QTableWidgetItem("A channel name")); ui->table->setItem(row, COL_CHANNEL, new QTableWidgetItem("Enter some notes")); @@ -1318,6 +1333,7 @@ void FreqScannerGUI::makeUIConnections() QObject::connect(ui->thresh, &QDial::valueChanged, this, &FreqScannerGUI::on_thresh_valueChanged); QObject::connect(ui->voiceSquelch, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqScannerGUI::on_voiceSquelchType_currentIndexChanged); QObject::connect(ui->voiceThreshold, &QDial::valueChanged, this, &FreqScannerGUI::on_voiceThreshold_valueChanged); + QObject::connect(ui->deviceFreqLock, &QToolButton::toggled, this, &FreqScannerGUI::on_lockDeviceFrequency_toggled); QObject::connect(ui->priority, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqScannerGUI::on_priority_currentIndexChanged); QObject::connect(ui->measurement, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqScannerGUI::on_measurement_currentIndexChanged); QObject::connect(ui->mode, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqScannerGUI::on_mode_currentIndexChanged); diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h index 50e8e3720..edb7d5f7a 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.h +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -115,6 +115,7 @@ private: COL_ANNOTATION, COL_ENABLE, COL_POWER, + COL_VAD, COL_ACTIVE_COUNT, COL_NOTES, COL_CHANNEL, @@ -134,6 +135,7 @@ private slots: void on_thresh_valueChanged(int value); void on_voiceThreshold_valueChanged(int value); void on_voiceSquelchType_currentIndexChanged(int index); + void on_lockDeviceFrequency_toggled(bool checked); void on_priority_currentIndexChanged(int index); void on_measurement_currentIndexChanged(int index); void on_mode_currentIndexChanged(int index); diff --git a/plugins/channelrx/freqscanner/freqscannergui.ui b/plugins/channelrx/freqscanner/freqscannergui.ui index 688b3d520..e7ef455bf 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.ui +++ b/plugins/channelrx/freqscanner/freqscannergui.ui @@ -157,6 +157,24 @@ + + + + Lock device frequency to current value + + + + + + + :/unlocked.png + :/locked.png:/unlocked.png + + + true + + + @@ -302,7 +320,7 @@ 0 - 2000 + 1000 1 @@ -795,6 +813,14 @@ Channel power in decibels during the previous scan + + + VAD + + + Voice level + + Active Count diff --git a/plugins/channelrx/freqscanner/freqscannersettings.cpp b/plugins/channelrx/freqscanner/freqscannersettings.cpp index 365d52874..11fe20143 100644 --- a/plugins/channelrx/freqscanner/freqscannersettings.cpp +++ b/plugins/channelrx/freqscanner/freqscannersettings.cpp @@ -48,6 +48,7 @@ void FreqScannerSettings::resetToDefaults() m_tuneTime = 100; m_voiceSquelchThreshold = 0.5f; m_voiceSquelchType = None; + m_lockDeviceFrequency = false; m_priority = MAX_POWER; m_measurement = PEAK; m_mode = CONTINUOUS; @@ -89,6 +90,7 @@ QByteArray FreqScannerSettings::serialize() const s.writeS32(14, (int)m_mode); s.writeList(15, m_frequencySettings); s.writeS32(16, m_channelShift); + s.writeBool(17, m_lockDeviceFrequency); s.writeList(20, m_columnIndexes); s.writeList(21, m_columnSizes); @@ -145,6 +147,7 @@ bool FreqScannerSettings::deserialize(const QByteArray& data) d.readS32(14, (int*)&m_mode, (int)CONTINUOUS); d.readList(15, &m_frequencySettings); d.readS32(16, &m_channelShift, 0); + d.readBool(17, &m_lockDeviceFrequency, false); if (m_frequencySettings.size() == 0) { @@ -234,6 +237,9 @@ void FreqScannerSettings::applySettings(const QStringList& settingsKeys, const F if (settingsKeys.contains("voiceSquelchType")) { m_voiceSquelchType = settings.m_voiceSquelchType; } + if (settingsKeys.contains("lockDeviceFrequency")) { + m_lockDeviceFrequency = settings.m_lockDeviceFrequency; + } if (settingsKeys.contains("frequencySettings")) { m_frequencySettings = settings.m_frequencySettings; } @@ -321,6 +327,9 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo if (settingsKeys.contains("voiceSquelchType") || force) { ostr << " m_voiceSquelchType: " << m_voiceSquelchType; } + if (settingsKeys.contains("lockDeviceFrequency") || force) { + ostr << " m_lockDeviceFrequency: " << m_lockDeviceFrequency; + } if (settingsKeys.contains("frequencySettings") || force) { QStringList s; diff --git a/plugins/channelrx/freqscanner/freqscannersettings.h b/plugins/channelrx/freqscanner/freqscannersettings.h index 7bfbec10d..042c439a9 100644 --- a/plugins/channelrx/freqscanner/freqscannersettings.h +++ b/plugins/channelrx/freqscanner/freqscannersettings.h @@ -74,6 +74,8 @@ struct FreqScannerSettings SCAN_ONLY, MULTIPLEX } m_mode; //!< Whether to run a single or many scans + bool m_lockDeviceFrequency; //!< Whether to lock device frequency to the initial center frequency of the first scan, + //!< or allow it to be shifted by the channel shift setting QList m_columnIndexes;//!< How the columns are ordered in the table QList m_columnSizes; //!< Size of the coumns in the table