From 37521224c366dddeb4256d99c37263693e8e5f6f Mon Sep 17 00:00:00 2001 From: srcejon Date: Thu, 28 Sep 2023 16:45:35 +0100 Subject: [PATCH 01/23] Add frequency scanner channel plugin --- CMakeLists.txt | 1 + plugins/channelrx/freqscanner/CMakeLists.txt | 66 ++ plugins/channelrx/freqscanner/freqscanner.cpp | 1036 +++++++++++++++++ plugins/channelrx/freqscanner/freqscanner.h | 435 +++++++ .../freqscanner/freqscanneraddrangedialog.cpp | 48 + .../freqscanner/freqscanneraddrangedialog.h | 44 + .../freqscanner/freqscanneraddrangedialog.ui | 222 ++++ .../freqscanner/freqscannerbaseband.cpp | 215 ++++ .../freqscanner/freqscannerbaseband.h | 102 ++ .../channelrx/freqscanner/freqscannergui.cpp | 911 +++++++++++++++ .../channelrx/freqscanner/freqscannergui.h | 142 +++ .../channelrx/freqscanner/freqscannergui.ui | 776 ++++++++++++ .../freqscanner/freqscannerplugin.cpp | 93 ++ .../channelrx/freqscanner/freqscannerplugin.h | 50 + .../freqscanner/freqscannersettings.cpp | 366 ++++++ .../freqscanner/freqscannersettings.h | 96 ++ .../channelrx/freqscanner/freqscannersink.cpp | 253 ++++ .../channelrx/freqscanner/freqscannersink.h | 86 ++ .../freqscanner/freqscannerwebapiadapter.cpp | 52 + .../freqscanner/freqscannerwebapiadapter.h | 50 + plugins/channelrx/freqscanner/readme.md | 121 ++ sdrbase/channel/channelwebapiutils.cpp | 41 + sdrbase/channel/channelwebapiutils.h | 1 + sdrbase/webapi/webapirequestmapper.cpp | 9 + sdrbase/webapi/webapiutils.cpp | 2 + sdrgui/gui/frequencydelegate.cpp | 110 +- sdrgui/gui/frequencydelegate.h | 10 +- .../api/swagger/include/ChannelReport.yaml | 2 + .../api/swagger/include/ChannelSettings.yaml | 2 + .../api/swagger/include/FreqScanner.yaml | 70 ++ 30 files changed, 5395 insertions(+), 17 deletions(-) create mode 100644 plugins/channelrx/freqscanner/CMakeLists.txt create mode 100644 plugins/channelrx/freqscanner/freqscanner.cpp create mode 100644 plugins/channelrx/freqscanner/freqscanner.h create mode 100644 plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp create mode 100644 plugins/channelrx/freqscanner/freqscanneraddrangedialog.h create mode 100644 plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui create mode 100644 plugins/channelrx/freqscanner/freqscannerbaseband.cpp create mode 100644 plugins/channelrx/freqscanner/freqscannerbaseband.h create mode 100644 plugins/channelrx/freqscanner/freqscannergui.cpp create mode 100644 plugins/channelrx/freqscanner/freqscannergui.h create mode 100644 plugins/channelrx/freqscanner/freqscannergui.ui create mode 100644 plugins/channelrx/freqscanner/freqscannerplugin.cpp create mode 100644 plugins/channelrx/freqscanner/freqscannerplugin.h create mode 100644 plugins/channelrx/freqscanner/freqscannersettings.cpp create mode 100644 plugins/channelrx/freqscanner/freqscannersettings.h create mode 100644 plugins/channelrx/freqscanner/freqscannersink.cpp create mode 100644 plugins/channelrx/freqscanner/freqscannersink.h create mode 100644 plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp create mode 100644 plugins/channelrx/freqscanner/freqscannerwebapiadapter.h create mode 100644 plugins/channelrx/freqscanner/readme.md create mode 100644 swagger/sdrangel/api/swagger/include/FreqScanner.yaml diff --git a/CMakeLists.txt b/CMakeLists.txt index e6cf4cea8..f300ba8ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,7 @@ option(ENABLE_CHANNELRX_DEMODRTTY "Enable channelrx demodrtty plugin" ON) option(ENABLE_CHANNELRX_DEMODILS "Enable channelrx demodils plugin" ON) option(ENABLE_CHANNELRX_DEMODDSC "Enable channelrx demoddsc plugin" ON) option(ENABLE_CHANNELRX_HEATMAP "Enable channelrx heatmap plugin" ON) +option(ENABLE_CHANNELRX_FREQSCANNER "Enable channelrx freqscanner plugin" ON) # Channel Tx enablers option(ENABLE_CHANNELTX "Enable channeltx plugins" ON) diff --git a/plugins/channelrx/freqscanner/CMakeLists.txt b/plugins/channelrx/freqscanner/CMakeLists.txt new file mode 100644 index 000000000..b02ad52b0 --- /dev/null +++ b/plugins/channelrx/freqscanner/CMakeLists.txt @@ -0,0 +1,66 @@ +project(freqscanner) + +set(freqscanner_SOURCES + freqscanner.cpp + freqscannersettings.cpp + freqscannerbaseband.cpp + freqscannersink.cpp + freqscannerplugin.cpp + freqscannerwebapiadapter.cpp +) + +set(freqscanner_HEADERS + freqscanner.h + freqscannersettings.h + freqscannerbaseband.h + freqscannersink.h + freqscannerplugin.h + freqscannerwebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client +) + +if(NOT SERVER_MODE) + set(freqscanner_SOURCES + ${freqscanner_SOURCES} + freqscannergui.cpp + freqscannergui.ui + freqscanneraddrangedialog.cpp + freqscanneraddrangedialog.ui + ) + set(freqscanner_HEADERS + ${freqscanner_HEADERS} + freqscannergui.h + freqscanneraddrangedialog.h + ) + + set(TARGET_NAME freqscanner) + set(TARGET_LIB "Qt::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME freqscannersrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +add_library(${TARGET_NAME} SHARED + ${freqscanner_SOURCES} +) + +target_link_libraries(${TARGET_NAME} + Qt::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) + +# Install debug symbols +if (WIN32) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp new file mode 100644 index 000000000..904a94111 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -0,0 +1,1036 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "freqscanner.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" +//#include "SWGFreqScannerSettings.h" +#include "SWGChannelReport.h" +#include "SWGMapItem.h" + +#include "device/deviceset.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/morsedemod.h" +#include "device/deviceapi.h" +#include "feature/feature.h" +#include "settings/serializable.h" +#include "util/db.h" +#include "channel/channelwebapiutils.h" +#include "maincore.h" + +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgConfigureFreqScanner, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportChannels, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgStartScan, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgStopScan, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgScanComplete, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgScanResult, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgStatus, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportActiveFrequency, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportActivePower, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportScanning, Message) +MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportScanRange, Message) + +const char * const FreqScanner::m_channelIdURI = "sdrangel.channel.freqscanner"; +const char * const FreqScanner::m_channelId = "FreqScanner"; + +FreqScanner::FreqScanner(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), + m_deviceAPI(deviceAPI), + m_running(false), + m_basebandSampleRate(0), + m_scanDeviceSetIndex(-1), + m_scanChannelIndex(-1), + m_state(IDLE), + m_timeoutTimer(this) +{ + setObjectName(m_channelId); + + m_basebandSink = new FreqScannerBaseband(this); + m_basebandSink->setMessageQueueToChannel(getInputMessageQueue()); + m_basebandSink->setChannel(this); + m_basebandSink->moveToThread(&m_thread); + + applySettings(m_settings, QStringList(), true); + + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + + m_networkManager = new QNetworkAccessManager(); + QObject::connect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &FreqScanner::networkManagerFinished + ); + QObject::connect( + this, + &ChannelAPI::indexInDeviceSetChanged, + this, + &FreqScanner::handleIndexInDeviceSetChanged + ); + + scanAvailableChannels(); + QObject::connect( + MainCore::instance(), + &MainCore::channelAdded, + this, + &FreqScanner::handleChannelAdded + ); + QObject::connect( + MainCore::instance(), + &MainCore::channelRemoved, + this, + &FreqScanner::handleChannelRemoved + ); + + m_timeoutTimer.callOnTimeout(this, &FreqScanner::timeout); +} + +FreqScanner::~FreqScanner() +{ + qDebug("FreqScanner::~FreqScanner"); + QObject::disconnect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &FreqScanner::networkManagerFinished + ); + delete m_networkManager; + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + + if (m_basebandSink->isRunning()) { + stop(); + } + + delete m_basebandSink; +} + +void FreqScanner::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + +uint32_t FreqScanner::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSourceStreams(); +} + +void FreqScanner::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) +{ + (void) firstOfBurst; + + if (m_running) { + m_basebandSink->feed(begin, end); + } +} + +void FreqScanner::start() +{ + if (m_running) { + return; + } + + qDebug("FreqScanner::start"); + + m_basebandSink->reset(); + m_basebandSink->startWork(); + m_thread.start(); + // FIXME: Threading!! Compare to SSB + + DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency); + m_basebandSink->getInputMessageQueue()->push(dspMsg); + + FreqScannerBaseband::MsgConfigureFreqScannerBaseband *msg = FreqScannerBaseband::MsgConfigureFreqScannerBaseband::create(m_settings, QStringList(), true); + m_basebandSink->getInputMessageQueue()->push(msg); + + m_running = true; +} + +void FreqScanner::stop() +{ + if (!m_running) { + return; + } + + qDebug("FreqScanner::stop"); + m_running = false; + m_basebandSink->stopWork(); + m_thread.quit(); + m_thread.wait(); +} + +bool FreqScanner::handleMessage(const Message& cmd) +{ + if (MsgConfigureFreqScanner::match(cmd)) + { + MsgConfigureFreqScanner& cfg = (MsgConfigureFreqScanner&) cmd; + qDebug() << "FreqScanner::handleMessage: MsgConfigureFreqScanner"; + applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + m_centerFrequency = notif.getCenterFrequency(); + qDebug() << "FreqScanner::handleMessage: DSPSignalNotification"; + // Forward to the sink + if (m_running) + { + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + m_basebandSink->getInputMessageQueue()->push(rep); + } + // Forward to GUI if any + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); + } + + return true; + } + else if (MsgStartScan::match(cmd)) + { + qInfo() << "FreqScanner::handleMessage: StartScan"; + + startScan(); + + return true; + } + else if (MsgStopScan::match(cmd)) + { + qInfo() << "FreqScanner::handleMessage: StopScan"; + + stopScan(); + + return true; + } + else if (MsgScanResult::match(cmd)) + { + MsgScanResult& result = (MsgScanResult&)cmd; + const QList& results = result.getScanResults(); + + processScanResults(result.getFFTStartTime(), results); + + return true; + } + else + { + return false; + } +} + +void FreqScanner::startScan() +{ + // Start scan + m_state = START_SCAN; +} + +void FreqScanner::stopScan() +{ + // Stop scan + m_state = IDLE; + m_timeoutTimer.stop(); + + if (m_guiMessageQueue) { + m_guiMessageQueue->push(MsgStatus::create("")); + } +} + +void FreqScanner::initScan() +{ + ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, true); + + if (m_centerFrequency != m_stepStartFrequency) + { + qInfo() << "******************** Setting frequency to " << m_stepStartFrequency; + if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), m_stepStartFrequency)) { + qWarning() << "Scanner failed to set frequency" << m_stepStartFrequency; + } + m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); + qInfo() << "m_minFFTStartTime" << m_minFFTStartTime.toString("ss.z"); + } + + qDebug() << "********* initScan: Clear results"; + m_scanResults.clear(); + qDebug() << "********* initScan: Clear results done"; + + if (m_guiMessageQueue) { + m_guiMessageQueue->push(FreqScanner::MsgReportScanning::create()); + } + + m_state = SCAN_FOR_MAX_POWER; +} + +void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList& results) +{ + + switch (m_state) + { + + case START_SCAN: + { + // Create ordered list of frequencies to scan + QList frequencies; + for (int i = 0; i < m_settings.m_frequencies.size(); i++) + { + if (m_settings.m_enabled[i]) { + frequencies.append(m_settings.m_frequencies[i]); + } + } + qSort(frequencies); + + // Calculate how many channels can be scanned in one go + int fftSize; + int binsPerChannel; + FreqScanner::calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); + + // Align first frequency so we cover as many channels as possible, while avoiding DC bin + m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; + m_stepStopFrequency = frequencies.back(); + + qInfo() << "START_SCAN: Scanning from " << frequencies.front() << "to" << frequencies.back(); + initScan(); + } + break; + + case SCAN_FOR_MAX_POWER: + if (fftStartTime >= m_minFFTStartTime) + { + if (results.size() > 0) { + m_scanResults.append(results); + } + + bool complete = false; // Have all frequencies been scanned? + qint64 nextCenterFrequency = m_centerFrequency; + + if (m_stepStopFrequency < m_centerFrequency + m_scannerSampleRate / 2) + { + nextCenterFrequency = m_stepStartFrequency; + complete = true; + } + else + { + nextCenterFrequency = m_centerFrequency + m_scannerSampleRate; + complete = false; + } + + if (complete) + { + if (m_scanResults.size() > 0) + { + // Send scan results to GUI for display in table + if (m_guiMessageQueue) + { + FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(QDateTime()); + QList& guiResults = msg->getScanResults(); + guiResults.append(m_scanResults); + m_guiMessageQueue->push(msg); + } + + int frequency = m_scanResults[0].m_frequency; + Real maxPower = m_scanResults[0].m_power; + + if (m_settings.m_priority == FreqScannerSettings::MAX_POWER) + { + // Find frequency with max power + for (int i = 1; i < m_scanResults.size(); i++) + { + if (m_scanResults[i].m_power > maxPower) + { + frequency = m_scanResults[i].m_frequency; + maxPower = m_scanResults[i].m_power; + } + } + } + else + { + // Find first frequency in list above threshold + for (int j = 0; j < m_settings.m_frequencies.size(); j++) + { + for (int i = 0; i < m_scanResults.size(); i++) + { + if (m_scanResults[i].m_frequency == m_settings.m_frequencies[j]) + { + if (m_scanResults[i].m_power >= m_settings.m_threshold) + { + frequency = m_scanResults[i].m_frequency; + maxPower = m_scanResults[i].m_power; + goto found_freq; + } + } + } + } + found_freq: ; + } + + if (m_settings.m_mode != FreqScannerSettings::SCAN_ONLY) + { + // Is power above threshold + if (maxPower >= m_settings.m_threshold) + { + if (m_guiMessageQueue) { + m_guiMessageQueue->push(MsgReportActiveFrequency::create(frequency)); + } + + // Tune device/channel to frequency + int offset; + if ((frequency < m_centerFrequency - m_scannerSampleRate / 2) || (frequency >= m_centerFrequency + m_scannerSampleRate / 2)) + { + nextCenterFrequency = frequency; + offset = 0; + } + else + { + nextCenterFrequency = m_centerFrequency; + offset = frequency - m_centerFrequency; + } + + // Ensure we have minimum offset from DC + if (offset >= 0) + { + while (offset < m_settings.m_channelFrequencyOffset) + { + nextCenterFrequency -= m_settings.m_channelBandwidth; + offset += m_settings.m_channelBandwidth; + } + } + else + { + while (abs(offset) < m_settings.m_channelFrequencyOffset) + { + nextCenterFrequency += m_settings.m_channelBandwidth; + offset -= m_settings.m_channelBandwidth; + } + } + + //qInfo() << "Tuning to active freq:" << frequency << "m_centerFrequency" << m_centerFrequency << "nextCenterFrequency" << nextCenterFrequency << "offset: " << offset << "deviceset: R" << m_scanDeviceSetIndex << ":" << m_scanChannelIndex; + + ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset); + + // Unmute the channel + ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, false); + + m_activeFrequency = frequency; + + if (m_settings.m_mode == FreqScannerSettings::SINGLE) + { + // Scan complete + if (m_guiMessageQueue) { + m_guiMessageQueue->push(MsgScanComplete::create()); + } + m_state = IDLE; + } + else + { + // Wait for transmission to finish + m_state = WAIT_FOR_END_TX; + } + } + else + { + if (m_guiMessageQueue) { + m_guiMessageQueue->push(MsgStatus::create(QString("Scanning: No active channels - Max power %1 dB").arg(maxPower))); + } + } + } + } + } + + if (nextCenterFrequency != m_centerFrequency) + { + // For RTL SDR, setCenterFrequency takes ~50ms, which means tuneTime can be 0 + if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), nextCenterFrequency)) { + qWarning() << "Scanner failed to set frequency" << nextCenterFrequency; + } + m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); + } + + if (complete) { + m_scanResults.clear(); + } + } + break; + + case WAIT_FOR_END_TX: + for (int i = 0; i < results.size(); i++) + { + if (results[i].m_frequency == m_activeFrequency) + { + if (m_guiMessageQueue) { + m_guiMessageQueue->push(MsgReportActivePower::create(results[i].m_power)); + } + + // Wait until power drops below threshold + if (results[i].m_power < m_settings.m_threshold) + { + m_timeoutTimer.setSingleShot(true); + m_timeoutTimer.start((int)(m_settings.m_retransmitTime * 1000.0)); + m_state = WAIT_FOR_RETRANSMISSION; + break; + } + } + } + break; + + case WAIT_FOR_RETRANSMISSION: + for (int i = 0; i < results.size(); i++) + { + if (results[i].m_frequency == m_activeFrequency) + { + if (m_guiMessageQueue) { + m_guiMessageQueue->push(MsgReportActivePower::create(results[i].m_power)); + } + + // Check if power has returned to being above threshold + if (results[i].m_power >= m_settings.m_threshold) + { + m_timeoutTimer.stop(); + m_state = WAIT_FOR_END_TX; + } + } + } + break; + + } +} + +void FreqScanner::timeout() +{ + // Power hasn't returned above threshold - Restart scan + initScan(); +} + +void FreqScanner::setCenterFrequency(qint64 frequency) +{ + FreqScannerSettings settings = m_settings; + settings.m_inputFrequencyOffset = frequency; + applySettings(settings, {"inputFrequencyOffset"}, false); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureFreqScanner *msgToGUI = MsgConfigureFreqScanner::create(settings, {"inputFrequencyOffset"}, false); + m_guiMessageQueue->push(msgToGUI); + } +} + +void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) +{ + qDebug() << "FreqScanner::applySettings:" + << settings.getDebugString(settingsKeys, force) + << " force: " << force; + + if (settingsKeys.contains("streamIndex")) + { + if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSink(this, settings.m_streamIndex); + m_deviceAPI->addChannelSinkAPI(this); + } + } + + if (settingsKeys.contains("channel") || force) + { + const QRegExp re("R([0-9]+):([0-9]+)"); + if (re.indexIn(settings.m_channel) >= 0) + { + m_scanDeviceSetIndex = re.capturedTexts()[1].toInt(); + m_scanChannelIndex = re.capturedTexts()[2].toInt(); + } + else + { + qDebug() << "FreqScanner::applySettings: Failed to parse channel" << settings.m_channel; + } + } + + if (m_running) + { + FreqScannerBaseband::MsgConfigureFreqScannerBaseband *msg = FreqScannerBaseband::MsgConfigureFreqScannerBaseband::create(settings, settingsKeys, force); + m_basebandSink->getInputMessageQueue()->push(msg); + } + + if (settingsKeys.contains("useReverseAPI")) + { + bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) || + settingsKeys.contains("reverseAPIAddress") || + settingsKeys.contains("reverseAPIPort") || + settingsKeys.contains("reverseAPIDeviceIndex") || + settingsKeys.contains("reverseAPIChannelIndex"); + webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force); + } + + if (settingsKeys.contains("frequencies") + || settingsKeys.contains("enabled") + || settingsKeys.contains("priority") + || settingsKeys.contains("measurement") + || settingsKeys.contains("mode") + || force) + { + // Restart scan if any settings change + if (m_state != IDLE) { + m_state = START_SCAN; + } + } + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} + +QByteArray FreqScanner::serialize() const +{ + return m_settings.serialize(); +} + +bool FreqScanner::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + MsgConfigureFreqScanner *msg = MsgConfigureFreqScanner::create(m_settings, QStringList(), true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureFreqScanner *msg = MsgConfigureFreqScanner::create(m_settings, QStringList(), true); + m_inputMessageQueue.push(msg); + return false; + } +} + +int FreqScanner::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + /*response.ssetFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + response.gsetFreqScannerSettings()->init(); + webapiFormatChannelSettings(response, m_settings);*/ + return 200; +} + +int FreqScanner::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + +int FreqScanner::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + FreqScannerSettings settings = m_settings; + webapiUpdateChannelSettings(settings, channelSettingsKeys, response); + + MsgConfigureFreqScanner *msg = MsgConfigureFreqScanner::create(settings, channelSettingsKeys, force); + m_inputMessageQueue.push(msg); + + qDebug("FreqScanner::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureFreqScanner *msgToGUI = MsgConfigureFreqScanner::create(settings, channelSettingsKeys, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +int FreqScanner::webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage) +{ + (void) errorMessage; + /*response.ssetFreqScannerReport(new SWGSDRangel::SWGFreqScannerReport()); + response.gsetFreqScannerReport()->init(); + webapiFormatChannelReport(response);*/ + return 200; +} + +void FreqScanner::webapiUpdateChannelSettings( + FreqScannerSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response) +{ + /*if (channelSettingsKeys.contains("inputFrequencyOffset")) { + settings.m_inputFrequencyOffset = response.gsetFreqScannerSettings()->getInputFrequencyOffset(); + } + if (channelSettingsKeys.contains("rfBandwidth")) { + settings.m_channelBandwidth = response.gsetFreqScannerSettings()->getRfBandwidth(); + } + if (channelSettingsKeys.contains("audioMute")) { + settings.m_audioMute = response.gsetFreqScannerSettings()->getAudioMute(); + } + if (channelSettingsKeys.contains("threshold")) { + settings.m_threshold = response.gsetFreqScannerSettings()->getThreshold(); + } + if (channelSettingsKeys.contains("logFilename")) { + settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename(); + } + if (channelSettingsKeys.contains("logEnabled")) { + settings.m_logEnabled = response.getAdsbDemodSettings()->getLogEnabled(); + } + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.gsetFreqScannerSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.gsetFreqScannerSettings()->getTitle(); + } + if (channelSettingsKeys.contains("streamIndex")) { + settings.m_streamIndex = response.gsetFreqScannerSettings()->getStreamIndex(); + } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.gsetFreqScannerSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.gsetFreqScannerSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.gsetFreqScannerSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.gsetFreqScannerSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.gsetFreqScannerSettings()->getReverseApiChannelIndex(); + } + if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) { + settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.gsetFreqScannerSettings()->getScopeConfig()); + } + if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { + settings.m_channelMarker->updateFrom(channelSettingsKeys, response.gsetFreqScannerSettings()->getChannelMarker()); + } + if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) { + settings.m_rollupState->updateFrom(channelSettingsKeys, response.gsetFreqScannerSettings()->getRollupState()); + }*/ +} + +void FreqScanner::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FreqScannerSettings& settings) +{ + /*response.gsetFreqScannerSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.gsetFreqScannerSettings()->setRfBandwidth(settings.m_channelBandwidth); + response.gsetFreqScannerSettings()->setAudioMute(settings.m_audioMute); + response.gsetFreqScannerSettings()->setThreshold(settings.m_threshold); + response.gsetFreqScannerSettings()->setLogFilename(new QString(settings.m_logFilename)); + response.gsetFreqScannerSettings()->setLogEnabled(settings.m_logEnabled); + + response.gsetFreqScannerSettings()->setRgbColor(settings.m_rgbColor); + if (response.gsetFreqScannerSettings()->getTitle()) { + *response.gsetFreqScannerSettings()->getTitle() = settings.m_title; + } else { + response.gsetFreqScannerSettings()->setTitle(new QString(settings.m_title)); + } + + response.gsetFreqScannerSettings()->setStreamIndex(settings.m_streamIndex); + response.gsetFreqScannerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.gsetFreqScannerSettings()->getReverseApiAddress()) { + *response.gsetFreqScannerSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.gsetFreqScannerSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.gsetFreqScannerSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.gsetFreqScannerSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.gsetFreqScannerSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + + if (settings.m_scopeGUI) + { + if (response.gsetFreqScannerSettings()->getScopeConfig()) + { + settings.m_scopeGUI->formatTo(response.gsetFreqScannerSettings()->getScopeConfig()); + } + else + { + SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope(); + settings.m_scopeGUI->formatTo(swgGLScope); + response.gsetFreqScannerSettings()->setScopeConfig(swgGLScope); + } + } + if (settings.m_channelMarker) + { + if (response.gsetFreqScannerSettings()->getChannelMarker()) + { + settings.m_channelMarker->formatTo(response.gsetFreqScannerSettings()->getChannelMarker()); + } + else + { + SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); + settings.m_channelMarker->formatTo(swgChannelMarker); + response.gsetFreqScannerSettings()->setChannelMarker(swgChannelMarker); + } + } + + if (settings.m_rollupState) + { + if (response.gsetFreqScannerSettings()->getRollupState()) + { + settings.m_rollupState->formatTo(response.gsetFreqScannerSettings()->getRollupState()); + } + else + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + response.gsetFreqScannerSettings()->setRollupState(swgRollupState); + } + }*/ +} + +void FreqScanner::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +{ + /*response.gsetFreqScannerReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); + response.gsetFreqScannerReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());*/ +} + +void FreqScanner::webapiReverseSendSettings(const QStringList& channelSettingsKeys, const FreqScannerSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgChannelSettings; +} + +void FreqScanner::webapiFormatChannelSettings( + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const FreqScannerSettings& settings, + bool force +) +{ + /*swgChannelSettings->setDirection(0); // Single sink (Rx) + swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); + swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); + swgChannelSettings->setChannelType(new QString("FreqScanner")); + swgChannelSettings->ssetFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + SWGSDRangel::SWGFreqScannerSettings *swgFreqScannerSettings = swgChannelSettings->gsetFreqScannerSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgFreqScannerSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgFreqScannerSettings->setRfBandwidth(settings.m_channelBandwidth); + } + if (channelSettingsKeys.contains("audioMute") || force) { + swgFreqScannerSettings->setAudioMute(settings.m_audioMute); + } + if (channelSettingsKeys.contains("threshold") || force) { + swgFreqScannerSettings->setThreshold(settings.m_threshold); + } + if (channelSettingsKeys.contains("logFilename") || force) { + swgFreqScannerSettings->setLogFilename(new QString(settings.m_logFilename)); + } + if (channelSettingsKeys.contains("logEnabled") || force) { + swgFreqScannerSettings->setLogEnabled(settings.m_logEnabled); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgFreqScannerSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgFreqScannerSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("streamIndex") || force) { + swgFreqScannerSettings->setStreamIndex(settings.m_streamIndex); + } + + if (settings.m_scopeGUI && (channelSettingsKeys.contains("scopeConfig") || force)) + { + SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope(); + settings.m_scopeGUI->formatTo(swgGLScope); + swgFreqScannerSettings->setScopeConfig(swgGLScope); + } + + if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force)) + { + SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); + settings.m_channelMarker->formatTo(swgChannelMarker); + swgFreqScannerSettings->setChannelMarker(swgChannelMarker); + } + + if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force)) + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + swgFreqScannerSettings->setRollupState(swgRollupState); + }*/ +} + +void FreqScanner::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "FreqScanner::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("FreqScanner::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +void FreqScanner::handleIndexInDeviceSetChanged(int index) +{ + if (!m_running || (index < 0)) { + return; + } + + QString fifoLabel = QString("%1 [%2:%3]") + .arg(m_channelId) + .arg(m_deviceAPI->getDeviceSetIndex()) + .arg(index); + m_basebandSink->setFifoLabel(fifoLabel); +} + +void FreqScanner::scanAvailableChannels() +{ + MainCore* mainCore = MainCore::instance(); + MessagePipes& messagePipes = mainCore->getMessagePipes(); + std::vector& deviceSets = mainCore->getDeviceSets(); + m_availableChannels.clear(); + + for (const auto& deviceSet : deviceSets) + { + DSPDeviceSourceEngine* deviceSourceEngine = deviceSet->m_deviceSourceEngine; + + if (deviceSourceEngine) + { + for (int chi = 0; chi < deviceSet->getNumberOfChannels(); chi++) + { + ChannelAPI* channel = deviceSet->getChannelAt(chi); + + FreqScannerSettings::AvailableChannel availableChannel = + FreqScannerSettings::AvailableChannel{ channel->getDeviceSetIndex(), channel->getIndexInDeviceSet(), channel }; + m_availableChannels[channel] = availableChannel; + } + } + } + + notifyUpdateChannels(); +} + +void FreqScanner::handleChannelAdded(int deviceSetIndex, ChannelAPI* channel) +{ + qDebug("FreqScanner::handleChannelAdded: deviceSetIndex: %d:%d channel: %s (%p)", + deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel); + std::vector& deviceSets = MainCore::instance()->getDeviceSets(); + DeviceSet* deviceSet = deviceSets[deviceSetIndex]; + DSPDeviceSourceEngine* deviceSourceEngine = deviceSet->m_deviceSourceEngine; + + if (deviceSourceEngine) + { + FreqScannerSettings::AvailableChannel availableChannel = + FreqScannerSettings::AvailableChannel{ deviceSetIndex, channel->getIndexInDeviceSet(), channel }; + m_availableChannels[channel] = availableChannel; + } + + notifyUpdateChannels(); +} + +void FreqScanner::handleChannelRemoved(int deviceSetIndex, ChannelAPI* channel) +{ + qDebug("FreqScanner::handleChannelRemoved: deviceSetIndex: %d:%d channel: %s (%p)", + deviceSetIndex, channel->getIndexInDeviceSet(), qPrintable(channel->getURI()), channel); + std::vector& deviceSets = MainCore::instance()->getDeviceSets(); + DeviceSet* deviceSet = deviceSets[deviceSetIndex]; + DSPDeviceSourceEngine* deviceSourceEngine = deviceSet->m_deviceSourceEngine; + + if (deviceSourceEngine) { + m_availableChannels.remove(channel); + } + + notifyUpdateChannels(); +} + +void FreqScanner::notifyUpdateChannels() +{ + if (getMessageQueueToGUI()) + { + MsgReportChannels* msgToGUI = MsgReportChannels::create(); + QList& msgChannels = msgToGUI->getChannels(); + QHash::iterator it = m_availableChannels.begin(); + + for (; it != m_availableChannels.end(); ++it) + { + FreqScannerSettings::AvailableChannel msgChannel = + FreqScannerSettings::AvailableChannel{ + it->m_deviceSetIndex, + it->m_channelIndex + }; + msgChannels.push_back(msgChannel); + } + + getMessageQueueToGUI()->push(msgToGUI); + } +} diff --git a/plugins/channelrx/freqscanner/freqscanner.h b/plugins/channelrx/freqscanner/freqscanner.h new file mode 100644 index 000000000..ff294b161 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscanner.h @@ -0,0 +1,435 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015-2018 Edouard Griffiths, F4EXB. // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNER_H +#define INCLUDE_FREQSCANNER_H + +#include +#include +#include +#include +#include + +#include "dsp/basebandsamplesink.h" +#include "channel/channelapi.h" +#include "util/message.h" + +#include "freqscannerbaseband.h" +#include "freqscannersettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QThread; +class DeviceAPI; + +class FreqScanner : public BasebandSampleSink, public ChannelAPI { +public: + class MsgConfigureFreqScanner : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FreqScannerSettings& getSettings() const { return m_settings; } + const QStringList& getSettingsKeys() const { return m_settingsKeys; } + bool getForce() const { return m_force; } + + static MsgConfigureFreqScanner* create(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) + { + return new MsgConfigureFreqScanner(settings, settingsKeys, force); + } + + private: + FreqScannerSettings m_settings; + QStringList m_settingsKeys; + bool m_force; + + MsgConfigureFreqScanner(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) : + Message(), + m_settings(settings), + m_settingsKeys(settingsKeys), + m_force(force) + { } + }; + + class MsgReportChannels : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + QList& getChannels() { return m_channels; } + + static MsgReportChannels* create() { + return new MsgReportChannels(); + } + + private: + QList m_channels; + + MsgReportChannels() : + Message() + {} + }; + + class MsgStartScan : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgStartScan* create() + { + return new MsgStartScan(); + } + + private: + + MsgStartScan() : + Message() + { + } + }; + + class MsgStopScan : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgStopScan* create() + { + return new MsgStopScan(); + } + + private: + + MsgStopScan() : + Message() + { + } + }; + + class MsgScanComplete : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgScanComplete* create() + { + return new MsgScanComplete(); + } + + private: + + MsgScanComplete() : + Message() + { + } + }; + + class MsgScanResult : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + struct ScanResult { + qint64 m_frequency; + float m_power; + }; + + const QDateTime& getFFTStartTime() { return m_fftStartTime; } + QList& getScanResults() { return m_scanResults; } + + static MsgScanResult* create(const QDateTime& fftStartTime) { + return new MsgScanResult(fftStartTime); + } + + private: + QDateTime m_fftStartTime; + QList m_scanResults; + + MsgScanResult(const QDateTime& fftStartTime) : + Message(), + m_fftStartTime(fftStartTime) + {} + }; + + class MsgStatus : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + const QString& getText() const { return m_text; } + + static MsgStatus* create(const QString& text) + { + return new MsgStatus(text); + } + + private: + + QString m_text; + + MsgStatus(const QString& text) : + Message(), + m_text(text) + { + } + }; + + class MsgReportActiveFrequency : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + qint64 getCenterFrequency() const { return m_centerFrequency; } + + static MsgReportActiveFrequency* create(qint64 centerFrequency) + { + return new MsgReportActiveFrequency(centerFrequency); + } + + private: + + qint64 m_centerFrequency; + + MsgReportActiveFrequency(qint64 centerFrequency) : + Message(), + m_centerFrequency(centerFrequency) + { + } + }; + + class MsgReportActivePower : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + float getPower() const { return m_power; } + + static MsgReportActivePower* create(float power) + { + return new MsgReportActivePower(power); + } + + private: + + Real m_power; + + MsgReportActivePower(float power) : + Message(), + m_power(power) + { + } + }; + + class MsgReportScanning : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + static MsgReportScanning* create() + { + return new MsgReportScanning(); + } + + private: + + MsgReportScanning() : + Message() + { + } + }; + + class MsgReportScanRange : public Message { + MESSAGE_CLASS_DECLARATION + + public: + + qint64 getCenterFrequency() const { return m_centerFrequency; } + int getTotalBandwidth() const { return m_totalBandwidth; } + int getFftSize() const { return m_fftSize; } + + static MsgReportScanRange* create(qint64 centerFrequency, int totalBandwidth, int fftSize) + { + return new MsgReportScanRange(centerFrequency, totalBandwidth, fftSize); + } + + private: + + qint64 m_centerFrequency; + int m_totalBandwidth; + int m_fftSize; + + MsgReportScanRange(qint64 centerFrequency, int totalBandwidth, int fftSize) : + Message(), + m_centerFrequency(centerFrequency), + m_totalBandwidth(totalBandwidth), + m_fftSize(fftSize) + { + } + }; + + FreqScanner(DeviceAPI *deviceAPI); + virtual ~FreqScanner(); + virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } + + using BasebandSampleSink::feed; + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); + virtual void start(); + virtual void stop(); + virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); } + virtual QString getSinkName() { return objectName(); } + + virtual void getIdentifier(QString& id) { id = objectName(); } + virtual QString getIdentifier() const { return objectName(); } + virtual const QString& getURI() const { return getName(); } + virtual void getTitle(QString& title) { title = m_settings.m_title; } + virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; } + virtual void setCenterFrequency(qint64 frequency); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int getNbSinkStreams() const { return 1; } + virtual int getNbSourceStreams() const { return 0; } + + virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const + { + (void) streamIndex; + (void) sinkElseSource; + return 0; + } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage); + + static void webapiFormatChannelSettings( + SWGSDRangel::SWGChannelSettings& response, + const FreqScannerSettings& settings); + + static void webapiUpdateChannelSettings( + FreqScannerSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response); + + void setMessageQueueToGUI(MessageQueue* queue) override { + ChannelAPI::setMessageQueueToGUI(queue); + m_basebandSink->setMessageQueueToGUI(queue); + } + + uint32_t getNumberOfDeviceStreams() const; + + static void calcScannerSampleRate(int channelBW, int basebandSampleRate, int& scannerSampleRate, int& fftSize, int& binsPerChannel) + { + const int maxFFTSize = 2048; + const int maxBinsPerChannel = 32; + const int minBinsPerChannel = 8; + + // Use multiple bins per channel, to account for FFT spectral leakage + binsPerChannel = maxFFTSize / (basebandSampleRate / channelBW); + binsPerChannel = std::min(binsPerChannel, maxBinsPerChannel); + binsPerChannel = std::max(binsPerChannel, minBinsPerChannel); + double binBW = channelBW / (double)binsPerChannel; + + // Find next smallest power of 2 + fftSize = pow(2.0, floor(log2(basebandSampleRate / binBW))); + fftSize = std::min(maxFFTSize, fftSize); + scannerSampleRate = binBW * fftSize; + qInfo() << "binsPerChannel:" << binsPerChannel << "fftSize:" << fftSize << "scannerSampleRate:" << scannerSampleRate; + } + + static const char * const m_channelIdURI; + static const char * const m_channelId; + +private: + DeviceAPI *m_deviceAPI; + QThread m_thread; + FreqScannerBaseband* m_basebandSink; + bool m_running; + FreqScannerSettings m_settings; + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink + qint64 m_centerFrequency; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + QHash m_availableChannels; + + int m_scanDeviceSetIndex; + int m_scanChannelIndex; + qint64 m_activeFrequency; + QDateTime m_minFFTStartTime; + int m_scannerSampleRate; + bool m_stepping; + qint64 m_stepStartFrequency; + qint64 m_stepStopFrequency; + QList m_scanResults; + + enum State { + IDLE, + START_SCAN, + SCAN_FOR_MAX_POWER, + WAIT_FOR_END_TX, + WAIT_FOR_RETRANSMISSION + } m_state; + + QTimer m_timeoutTimer; + + virtual bool handleMessage(const Message& cmd); + void applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force = false); + void webapiReverseSendSettings(const QStringList& channelSettingsKeys, const FreqScannerSettings& settings, bool force); + void webapiFormatChannelSettings( + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const FreqScannerSettings& settings, + bool force + ); + void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + + void scanAvailableChannels(); + void notifyUpdateChannels(); + void startScan(); + void stopScan(); + void initScan(); + void processScanResults(const QDateTime& fftStartTime, const QList& results); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + void handleIndexInDeviceSetChanged(int index); + void handleChannelAdded(int deviceSetIndex, ChannelAPI* channel); + void handleChannelRemoved(int deviceSetIndex, ChannelAPI* channel); + void timeout(); + +}; + +#endif // INCLUDE_FREQSCANNER_H + diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp new file mode 100644 index 000000000..afdd7e1e6 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp @@ -0,0 +1,48 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "freqscanneraddrangedialog.h" +#include "ui_freqscanneraddrangedialog.h" + +FreqScannerAddRangeDialog::FreqScannerAddRangeDialog(QWidget* parent) : + QDialog(parent), + ui(new Ui::FreqScannerAddRangeDialog) +{ + ui->setupUi(this); + + ui->start->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->start->setValueRange(false, 11, 0, 99999999999); + ui->stop->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->stop->setValueRange(false, 11, 0, 99999999999); + + // Airband frequency range + ui->start->setValue(118000000); + ui->stop->setValue(137000000); +} + +FreqScannerAddRangeDialog::~FreqScannerAddRangeDialog() +{ + delete ui; +} + +void FreqScannerAddRangeDialog::accept() +{ + m_start = ui->start->getValue(); + m_stop = ui->stop->getValue(); + m_step = ui->step->currentText().toLongLong(); + QDialog::accept(); +} diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h new file mode 100644 index 000000000..8bacafe7f --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNERADDRANGEDIALOG_H +#define INCLUDE_FREQSCANNERADDRANGEDIALOG_H + +#include + +namespace Ui { + class FreqScannerAddRangeDialog; +} + +class FreqScannerAddRangeDialog : public QDialog { + Q_OBJECT +public: + explicit FreqScannerAddRangeDialog(QWidget* parent = nullptr); + ~FreqScannerAddRangeDialog(); + + qint64 m_start; + qint64 m_stop; + int m_step; + +private slots: + void accept(); + +private: + Ui::FreqScannerAddRangeDialog *ui; +}; + +#endif // INCLUDE_FREQSCANNERADDRANGEDIALOG_H diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui new file mode 100644 index 000000000..0cce5efe4 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui @@ -0,0 +1,222 @@ + + + FreqScannerAddRangeDialog + + + + 0 + 0 + 385 + 162 + + + + + 9 + + + + Add Range + + + + + + + 0 + 0 + + + + Add Frequency Range + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Stop frequency in Hertz + + + + + + + Stop Frequency + + + + + + + + 90 + 0 + + + + Start Frequency + + + + + + + Hz + + + + + + + true + + + + 25000 + + + + + 8333.3 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Start frequency in Hertz + + + + + + + Hz + + + + + + + Hz + + + + + + + Step + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+
+ + + + + + + buttonBox + accepted() + FreqScannerAddRangeDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FreqScannerAddRangeDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp new file mode 100644 index 000000000..1516505f1 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp @@ -0,0 +1,215 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downchannelizer.h" + +#include "freqscannerbaseband.h" +#include "freqscanner.h" + +MESSAGE_CLASS_DEFINITION(FreqScannerBaseband::MsgConfigureFreqScannerBaseband, Message) + +FreqScannerBaseband::FreqScannerBaseband(FreqScanner *freqScanner) : + m_sink(freqScanner), + m_messageQueueToGUI(nullptr), + m_running(false) +{ + qDebug("FreqScannerBaseband::FreqScannerBaseband"); + + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownChannelizer(&m_sink); + m_channelSampleRate = 0; + m_scannerSampleRate = 0; +} + +FreqScannerBaseband::~FreqScannerBaseband() +{ + m_inputMessageQueue.clear(); + delete m_channelizer; +} + +void FreqScannerBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_inputMessageQueue.clear(); + m_sampleFifo.reset(); + m_channelSampleRate = 0; +} + +void FreqScannerBaseband::startWork() +{ + QMutexLocker mutexLocker(&m_mutex); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &FreqScannerBaseband::handleData, + Qt::QueuedConnection + ); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + m_running = true; +} + +void FreqScannerBaseband::stopWork() +{ + QMutexLocker mutexLocker(&m_mutex); + disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + QObject::disconnect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &FreqScannerBaseband::handleData + ); + m_running = false; +} + +void FreqScannerBaseband::setChannel(ChannelAPI *channel) +{ + m_sink.setChannel(channel); +} + +void FreqScannerBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void FreqScannerBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void FreqScannerBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool FreqScannerBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureFreqScannerBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureFreqScannerBaseband& cfg = (MsgConfigureFreqScannerBaseband&) cmd; + qDebug() << "FreqScannerBaseband::handleMessage: MsgConfigureFreqScannerBaseband"; + + applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "FreqScannerBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + setBasebandSampleRate(notif.getSampleRate()); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + if (m_channelSampleRate != m_channelizer->getChannelSampleRate()) { + m_channelSampleRate = m_channelizer->getChannelSampleRate(); + } + m_sink.setCenterFrequency(notif.getCenterFrequency()); + + return true; + } + else + { + return false; + } +} + +void FreqScannerBaseband::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) +{ + if ((settings.m_channelBandwidth != m_settings.m_channelBandwidth) || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { + calcScannerSampleRate(m_channelizer->getBasebandSampleRate(), settings.m_channelBandwidth, settings.m_inputFrequencyOffset); + } + + m_sink.applySettings(settings, settingsKeys, force); + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} + +int FreqScannerBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + +void FreqScannerBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); + calcScannerSampleRate(sampleRate, m_settings.m_channelBandwidth, m_settings.m_inputFrequencyOffset); +} + +void FreqScannerBaseband::calcScannerSampleRate(int basebandSampleRate, float rfBandwidth, int inputFrequencyOffset) +{ + int fftSize; + int binsPerChannel; + + FreqScanner::calcScannerSampleRate(rfBandwidth, basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); + + m_channelizer->setChannelization(m_scannerSampleRate, inputFrequencyOffset); + m_channelSampleRate = m_channelizer->getChannelSampleRate(); + m_sink.applyChannelSettings(m_channelSampleRate, m_channelizer->getChannelFrequencyOffset(), m_scannerSampleRate, fftSize, binsPerChannel); + + qInfo() << "FreqScannerBaseband::calcScannerSampleRate" + << "basebandSampleRate:" << basebandSampleRate + << "channelSampleRate:" << m_channelSampleRate + << "scannerSampleRate:" << m_scannerSampleRate + << "rfBandwidth:" << rfBandwidth + << "fftSize:" << fftSize + << "binsPerChannel:" << binsPerChannel; + + if (getMessageQueueToGUI()) + { + FreqScanner::MsgReportScanRange* msg = FreqScanner::MsgReportScanRange::create(inputFrequencyOffset, m_scannerSampleRate, fftSize); + getMessageQueueToGUI()->push(msg); + } +} diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.h b/plugins/channelrx/freqscanner/freqscannerbaseband.h new file mode 100644 index 000000000..739a2cf8d --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.h @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNERBASEBAND_H +#define INCLUDE_FREQSCANNERBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "freqscannersink.h" + +class DownChannelizer; +class ChannelAPI; +class FreqScanner; + +class FreqScannerBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureFreqScannerBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FreqScannerSettings& getSettings() const { return m_settings; } + const QStringList& getSettingsKeys() const { return m_settingsKeys; } + bool getForce() const { return m_force; } + + static MsgConfigureFreqScannerBaseband* create(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) + { + return new MsgConfigureFreqScannerBaseband(settings, settingsKeys, force); + } + + private: + FreqScannerSettings m_settings; + QStringList m_settingsKeys; + bool m_force; + + MsgConfigureFreqScannerBaseband(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) : + Message(), + m_settings(settings), + m_settingsKeys(settingsKeys), + m_force(force) + { } + }; + + FreqScannerBaseband(FreqScanner *packetDemod); + ~FreqScannerBaseband(); + void reset(); + void startWork(); + void stopWork(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); } + void setMessageQueueToGUI(MessageQueue* messageQueue) { m_messageQueueToGUI = messageQueue; } + void setBasebandSampleRate(int sampleRate); + int getChannelSampleRate() const; + void setChannel(ChannelAPI *channel); + bool isRunning() const { return m_running; } + void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } + +private: + SampleSinkFifo m_sampleFifo; + DownChannelizer *m_channelizer; + int m_channelSampleRate; + int m_scannerSampleRate; + FreqScannerSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + MessageQueue *m_messageQueueToGUI; + FreqScannerSettings m_settings; + bool m_running; + QRecursiveMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force = false); + void calcScannerSampleRate(int basebandSampleRate, float rfBandwidth, int inputFrequencyOffset); + MessageQueue* getMessageQueueToGUI() { return m_messageQueueToGUI; } + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_FREQSCANNERBASEBAND_H diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp new file mode 100644 index 000000000..50f46a0b8 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -0,0 +1,911 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +#include "device/deviceset.h" +#include "device/deviceuiset.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "ui_freqscannergui.h" +#include "util/simpleserializer.h" +#include "util/db.h" +#include "gui/basicchannelsettingsdialog.h" +#include "dsp/dspengine.h" +#include "dsp/glscopesettings.h" +#include "gui/tabletapandhold.h" +#include "gui/dialogpositioner.h" +#include "gui/decimaldelegate.h" +#include "gui/frequencydelegate.h" +#include "gui/glspectrum.h" +#include "channel/channelwebapiutils.h" + +#include "freqscannergui.h" +#include "freqscanneraddrangedialog.h" +#include "freqscanner.h" +#include "freqscannersink.h" + +FreqScannerGUI* FreqScannerGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) +{ + FreqScannerGUI* gui = new FreqScannerGUI(pluginAPI, deviceUISet, rxChannel); + return gui; +} + +void FreqScannerGUI::destroy() +{ + delete this; +} + +void FreqScannerGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applyAllSettings(); +} + +QByteArray FreqScannerGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool FreqScannerGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) + { + displaySettings(); + applyAllSettings(); + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +bool FreqScannerGUI::handleMessage(const Message& message) +{ + if (FreqScanner::MsgConfigureFreqScanner::match(message)) + { + qDebug("FreqScannerGUI::handleMessage: FreqScanner::MsgConfigureFreqScanner"); + const FreqScanner::MsgConfigureFreqScanner& cfg = (FreqScanner::MsgConfigureFreqScanner&) message; + m_settings = cfg.getSettings(); + blockApplySettings(true); + m_channelMarker.updateSettings(static_cast(m_settings.m_channelMarker)); + displaySettings(); + blockApplySettings(false); + return true; + } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(true, 7, 0, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + ui->channelBandwidth->setValueRange(true, 7, 8, m_basebandSampleRate); + if (m_channelMarker.getBandwidth() == 0) { + m_channelMarker.setBandwidth(m_basebandSampleRate); + } + updateAbsoluteCenterFrequency(); + return true; + } + else if (FreqScanner::MsgReportChannels::match(message)) + { + FreqScanner::MsgReportChannels& report = (FreqScanner::MsgReportChannels&)message; + updateChannelsList(report.getChannels()); + return true; + } + else if (FreqScanner::MsgStatus::match(message)) + { + FreqScanner::MsgStatus& report = (FreqScanner::MsgStatus&)message; + ui->status->setText(report.getText()); + return true; + } + else if (FreqScanner::MsgReportScanning::match(message)) + { + ui->status->setText("Scanning"); + ui->table->clearSelection(); + ui->channelPower->setText("-"); + return true; + } + else if (FreqScanner::MsgScanComplete::match(message)) + { + ui->startStop->setChecked(false); + return true; + } + else if (FreqScanner::MsgReportActiveFrequency::match(message)) + { + FreqScanner::MsgReportActiveFrequency& report = (FreqScanner::MsgReportActiveFrequency&)message; + qint64 f = report.getCenterFrequency(); + QString frequency; + QString annotation; + QList items = ui->table->findItems(QString::number(f), Qt::MatchExactly); + if (items.size() > 0) + { + ui->table->selectRow(items[0]->row()); + frequency = ui->table->item(items[0]->row(), COL_FREQUENCY)->text(); + annotation = ui->table->item(items[0]->row(), COL_ANNOTATION)->text(); + } + FrequencyDelegate freqDelegate("Auto", 3); + QString formattedFrequency = freqDelegate.displayText(frequency, QLocale::system()); + ui->status->setText(QString("Active: %1 %2").arg(formattedFrequency).arg(annotation)); + return true; + } + else if (FreqScanner::MsgReportActivePower::match(message)) + { + FreqScanner::MsgReportActivePower& report = (FreqScanner::MsgReportActivePower&)message; + float power = report.getPower(); + ui->channelPower->setText(QString::number(power, 'f', 1)); + return true; + } + else if (FreqScanner::MsgReportScanRange::match(message)) + { + FreqScanner::MsgReportScanRange& report = (FreqScanner::MsgReportScanRange&)message; + m_channelMarker.setCenterFrequency(report.getCenterFrequency()); + m_channelMarker.setBandwidth(report.getTotalBandwidth()); + return true; + } + else if (FreqScanner::MsgScanResult::match(message)) + { + FreqScanner::MsgScanResult& report = (FreqScanner::MsgScanResult&)message; + QList results = report.getScanResults(); + + // Clear column + for (int i = 0; i < ui->table->rowCount(); i++) + { + QTableWidgetItem* item = ui->table->item(i, COL_POWER); + item->setText(""); + item->setBackground(QBrush()); + } + // Add results + for (int i = 0; i < results.size(); i++) + { + qint64 freq = results[i].m_frequency; + QList items = ui->table->findItems(QString::number(freq), Qt::MatchExactly); + for (auto item : items) { + int row = item->row(); + QTableWidgetItem* item = ui->table->item(row, COL_POWER); + item->setData(Qt::DisplayRole, results[i].m_power); + bool active = results[i].m_power >= m_settings.m_threshold; + if (active) + { + item->setBackground(Qt::darkGreen); + QTableWidgetItem* activeCountItem = ui->table->item(row, COL_ACTIVE_COUNT); + activeCountItem->setData(Qt::DisplayRole, activeCountItem->data(Qt::DisplayRole).toInt() + 1); + } + } + } + + return true; + } + return false; +} + +void FreqScannerGUI::updateChannelsList(const QList& channels) +{ + ui->channels->blockSignals(true); + ui->channels->clear(); + + for (const auto& channel : channels) + { + // Add channels in this device set, other than ourself + if ((channel.m_deviceSetIndex == getDeviceSetIndex()) && (channel.m_channelIndex != getIndex())) + { + QString name = QString("R%1:%2").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex); + ui->channels->addItem(name); + } + } + + // Channel can be created after this plugin, so select it + // if the chosen channel appears + int channelIndex = ui->channels->findText(m_settings.m_channel); + + if (channelIndex >= 0) { + ui->channels->setCurrentIndex(channelIndex); + } else { + ui->channels->setCurrentIndex(-1); // return to nothing selected + } + + ui->channels->blockSignals(false); +} + +void FreqScannerGUI::on_channels_currentIndexChanged(int index) +{ + if (index >= 0) + { + m_settings.m_channel = ui->channels->currentText(); + applySetting("channel"); + } +} + +void FreqScannerGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +void FreqScannerGUI::channelMarkerChangedByCursor() +{ +} + +void FreqScannerGUI::channelMarkerHighlightedByCursor() +{ + setHighlighted(m_channelMarker.getHighlighted()); +} + +void FreqScannerGUI::on_deltaFrequency_changed(qint64 value) +{ + m_settings.m_channelFrequencyOffset = value; + applySetting("channelFrequencyOffset"); +} + +void FreqScannerGUI::on_channelBandwidth_changed(qint64 value) +{ + m_settings.m_channelBandwidth = value; + applySetting("channelBandwidth"); +} + +void FreqScannerGUI::on_scanTime_valueChanged(int value) +{ + ui->scanTimeText->setText(QString("%1 s").arg(value / 10.0, 0, 'f', 1)); + m_settings.m_scanTime = value / 10.0; + applySetting("scanTime"); +} + +void FreqScannerGUI::on_retransmitTime_valueChanged(int value) +{ + ui->retransmitTimeText->setText(QString("%1 s").arg(value / 10.0, 0, 'f', 1)); + m_settings.m_retransmitTime = value / 10.0; + applySetting("retransmitTime"); +} + +void FreqScannerGUI::on_tuneTime_valueChanged(int value) +{ + ui->tuneTimeText->setText(QString("%1 ms").arg(value)); + m_settings.m_tuneTime = value; + applySetting("tuneTime"); +} + +void FreqScannerGUI::on_thresh_valueChanged(int value) +{ + ui->threshText->setText(QString("%1 dB").arg(value / 10.0, 0, 'f', 1)); + m_settings.m_threshold = value / 10.0; + applySetting("threshold"); +} + +void FreqScannerGUI::on_priority_currentIndexChanged(int index) +{ + m_settings.m_priority = (FreqScannerSettings::Priority)index; + applySetting("priority"); +} + +void FreqScannerGUI::on_measurement_currentIndexChanged(int index) +{ + m_settings.m_measurement = (FreqScannerSettings::Measurement)index; + applySetting("measurement"); +} + +void FreqScannerGUI::on_mode_currentIndexChanged(int index) +{ + m_settings.m_mode = (FreqScannerSettings::Mode)index; + applySetting("mode"); +} + +void FreqScannerGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; + + getRollupContents()->saveState(m_rollupState); + applySetting("rollupState"); +} + +void FreqScannerGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuChannelSettings) + { + BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_freqScanner->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + + dialog.move(p); + new DialogPositioner(&dialog, false); + dialog.exec(); + + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); + + setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); + setTitleColor(m_settings.m_rgbColor); + + QList settingsKeys({ + "m_rgbColor", + "title", + "useReverseAPI", + "reverseAPIAddress", + "reverseAPIPort", + "reverseAPIDeviceIndex", + "reverseAPIChannelIndex" + }); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } + + applySettings(settingsKeys); + } + + resetContextMenuType(); +} + +FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) : + ChannelGUI(parent), + ui(new Ui::FreqScannerGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_deviceCenterFrequency(0), + m_doApplySettings(true) +{ + setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channelrx/freqscanner/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + + m_freqScanner = reinterpret_cast(rxChannel); + m_freqScanner->setMessageQueueToGUI(getInputMessageQueue()); + + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(true, 7, 0, 9999999); + + ui->channelBandwidth->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); + ui->channelBandwidth->setValueRange(true, 7, 16, 9999999); + + m_channelMarker.setColor(Qt::yellow); + m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); + m_channelMarker.setTitle("Frequency Scanner"); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + setTitleColor(m_channelMarker.getColor()); + m_settings.setChannelMarker(&m_channelMarker); + m_settings.setRollupState(&m_rollupState); + + m_deviceUISet->addChannelMarker(&m_channelMarker); + + connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); + connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + // Resize the table using dummy data + resizeTable(); + // Allow user to reorder columns + ui->table->horizontalHeader()->setSectionsMovable(true); + // Add context menu to allow hiding/showing of columns + m_menu = new QMenu(ui->table); + for (int i = 0; i < ui->table->horizontalHeader()->count(); i++) + { + QString text = ui->table->horizontalHeaderItem(i)->text(); + m_menu->addAction(createCheckableItem(text, i, true)); + } + ui->table->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->table->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint))); + // Get signals when columns change + connect(ui->table->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(table_sectionMoved(int, int, int))); + connect(ui->table->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(table_sectionResized(int, int, int))); + // Context menu + ui->table->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->table, &QTableWidget::customContextMenuRequested, this, &FreqScannerGUI::table_customContextMenuRequested); + TableTapAndHold* tableTapAndHold = new TableTapAndHold(ui->table); + connect(tableTapAndHold, &TableTapAndHold::tapAndHold, this, &FreqScannerGUI::table_customContextMenuRequested); + + ui->startStop->setStyleSheet(QString("QToolButton{ background-color: blue; } QToolButton:checked{ background-color: green; }")); + + displaySettings(); + makeUIConnections(); + applyAllSettings(); + + ui->table->setItemDelegateForColumn(COL_FREQUENCY, new FrequencyDelegate("Auto", 3)); + ui->table->setItemDelegateForColumn(COL_POWER, new DecimalDelegate(1)); +} + +FreqScannerGUI::~FreqScannerGUI() +{ + delete ui; +} + +void FreqScannerGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void FreqScannerGUI::applySetting(const QString& settingsKey) +{ + applySettings({settingsKey}); +} + +void FreqScannerGUI::applySettings(const QStringList& settingsKeys, bool force) +{ + if (m_doApplySettings) + { + FreqScanner::MsgConfigureFreqScanner* message = FreqScanner::MsgConfigureFreqScanner::create(m_settings, settingsKeys, force); + m_freqScanner->getInputMessageQueue()->push(message); + } +} + +void FreqScannerGUI::applyAllSettings() +{ + applySettings(QStringList(), true); +} + +void FreqScannerGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + m_channelMarker.setBandwidth(m_basebandSampleRate); + m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.blockSignals(false); + m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only + + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); + + blockApplySettings(true); + + ui->deltaFrequency->setValue(m_settings.m_channelFrequencyOffset); + ui->channelBandwidth->setValue(m_settings.m_channelBandwidth); + ui->scanTime->setValue(m_settings.m_scanTime * 10.0); + ui->scanTimeText->setText(QString("%1 s").arg(m_settings.m_scanTime, 0, 'f', 1)); + ui->retransmitTime->setValue(m_settings.m_retransmitTime * 10.0); + ui->retransmitTimeText->setText(QString("%1 s").arg(m_settings.m_retransmitTime, 0, 'f', 1)); + ui->tuneTime->setValue(m_settings.m_tuneTime); + ui->tuneTimeText->setText(QString("%1 ms").arg(m_settings.m_tuneTime)); + ui->thresh->setValue(m_settings.m_threshold * 10.0); + ui->threshText->setText(QString("%1 dB").arg(m_settings.m_threshold, 0, 'f', 1)); + ui->priority->setCurrentIndex((int)m_settings.m_priority); + ui->measurement->setCurrentIndex((int)m_settings.m_measurement); + ui->mode->setCurrentIndex((int)m_settings.m_mode); + + for (int i = 0; i < m_settings.m_frequencies.size(); i++) { + addRow(m_settings.m_frequencies[i], m_settings.m_enabled[i], m_settings.m_notes[i]); + } + + // Order and size columns + QHeaderView* header = ui->table->horizontalHeader(); + for (int i = 0; i < m_settings.m_columnSizes.size(); i++) + { + bool hidden = m_settings.m_columnSizes[i] == 0; + header->setSectionHidden(i, hidden); + m_menu->actions().at(i)->setChecked(!hidden); + if (m_settings.m_columnSizes[i] > 0) { + ui->table->setColumnWidth(i, m_settings.m_columnSizes[i]); + } + header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]); + } + + updateIndexLabel(); + + getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); + blockApplySettings(false); +} + +void FreqScannerGUI::leaveEvent(QEvent* event) +{ + m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); +} + +void FreqScannerGUI::enterEvent(EnterEventType* event) +{ + m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); +} + +void FreqScannerGUI::on_startStop_clicked(bool checked) +{ + if (checked) + { + FreqScanner::MsgStartScan* message = FreqScanner::MsgStartScan::create(); + m_freqScanner->getInputMessageQueue()->push(message); + } + else + { + FreqScanner::MsgStopScan* message = FreqScanner::MsgStopScan::create(); + m_freqScanner->getInputMessageQueue()->push(message); + } +} + +void FreqScannerGUI::addRow(qint64 frequency, bool enabled, const QString& notes) +{ + int row = ui->table->rowCount(); + ui->table->setRowCount(row + 1); + + // Must create before frequency so updateAnnotation can work + QTableWidgetItem* annotationItem = new QTableWidgetItem(); + annotationItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + ui->table->setItem(row, COL_ANNOTATION, annotationItem); + + ui->table->setItem(row, COL_FREQUENCY, new QTableWidgetItem(QString("%1").arg(frequency))); + + QTableWidgetItem *enableItem = new QTableWidgetItem(); + enableItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + enableItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); + ui->table->setItem(row, COL_ENABLE, enableItem); + + QTableWidgetItem* powerItem = new QTableWidgetItem(); + powerItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + ui->table->setItem(row, COL_POWER, powerItem); + + QTableWidgetItem *activeCountItem = new QTableWidgetItem(); + activeCountItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + ui->table->setItem(row, COL_ACTIVE_COUNT, activeCountItem); + activeCountItem->setData(Qt::DisplayRole, 0); + + QTableWidgetItem* notesItem = new QTableWidgetItem(notes); + ui->table->setItem(row, COL_NOTES, notesItem); +} + +void FreqScannerGUI::on_addSingle_clicked() +{ + addRow(0, true); +} + +void FreqScannerGUI::on_addRange_clicked() +{ + FreqScannerAddRangeDialog dialog(this); + new DialogPositioner(&dialog, false); + if (dialog.exec()) + { + qint64 start = dialog.m_start; + qint64 stop = dialog.m_stop; + int step = dialog.m_step; + + if ((start <= stop) && (step > 0)) + { + for (qint64 f = start; f <= stop; f += step) { + addRow(f, true); + } + } + } +} + +void FreqScannerGUI::on_remove_clicked() +{ + QList items = ui->table->selectedItems(); + + for (auto item : items) + { + int row = ui->table->row(item); + ui->table->removeRow(row); + m_settings.m_frequencies.removeAt(row); // table_cellChanged isn't called for removeRow + m_settings.m_enabled.removeAt(row); + } +} + +static QList takeRow(QTableWidget* table, int row) +{ + QList rowItems; + + for (int col = 0; col < table->columnCount(); col++) { + rowItems.append(table->takeItem(row, col)); + } + return rowItems; +} + +static void setRow(QTableWidget* table, int row, const QList& rowItems) +{ + for (int col = 0; col < rowItems.size(); col++) { + table->setItem(row, col, rowItems.at(col)); + } +} + +void FreqScannerGUI::on_up_clicked() +{ + QList items = ui->table->selectedItems(); + for (auto item : items) + { + int row = ui->table->row(item); + if (row > 0) + { + QList sourceItems = takeRow(ui->table, row); + QList destItems = takeRow(ui->table, row - 1); + setRow(ui->table, row - 1, sourceItems); + setRow(ui->table, row, destItems); + ui->table->setCurrentCell(row - 1, 0); + } + } +} + +void FreqScannerGUI::on_down_clicked() +{ + QList items = ui->table->selectedItems(); + for (auto item : items) + { + int row = ui->table->row(item); + if (row < ui->table->rowCount() - 1) + { + QList sourceItems = takeRow(ui->table, row); + QList destItems = takeRow(ui->table, row + 1); + setRow(ui->table, row + 1, sourceItems); + setRow(ui->table, row, destItems); + ui->table->setCurrentCell(row + 1, 0); + } + } +} + +void FreqScannerGUI::on_table_cellChanged(int row, int column) +{ + QTableWidgetItem* item = ui->table->item(row, column); + if (item) + { + if (column == COL_FREQUENCY) + { + qint64 value = item->text().toLongLong(); + while (m_settings.m_frequencies.size() <= row) + { + m_settings.m_frequencies.append(0); + m_settings.m_enabled.append(true); + m_settings.m_notes.append(""); + } + m_settings.m_frequencies[row] = value; + updateAnnotation(row); + QList settingsKeys({ + "frequencies", + "enabled", + "notes" + }); + applySettings(settingsKeys); + } + else if (column == COL_ENABLE) + { + m_settings.m_enabled[row] = item->checkState() == Qt::Checked; + applySetting("enabled"); + } + else if (column == COL_NOTES) + { + m_settings.m_notes[row] = item->text(); + applySetting("notes"); + } + } +} + +void FreqScannerGUI::updateAnnotation(int row) +{ + QTableWidgetItem* item = ui->table->item(row, COL_FREQUENCY); + QTableWidgetItem* annotationItem = ui->table->item(row, COL_ANNOTATION); + if (item && annotationItem) + { + qint64 frequency = item->text().toLongLong(); + const QList& markers = m_deviceUISet->m_spectrum->getAnnotationMarkers(); + const SpectrumAnnotationMarker* closest = nullptr; + for (const auto& marker : markers) + { + if ((marker.m_startFrequency <= frequency) && (frequency < marker.m_startFrequency + marker.m_bandwidth)) + { + if (marker.m_bandwidth == m_settings.m_channelBandwidth) { + // Exact match + annotationItem->setText(marker.m_text); + return; + } else if (!closest) { + closest = ▮ + } else { + if (marker.m_bandwidth < closest->m_bandwidth) { + closest = ▮ + } + } + } + } + if (closest) { + annotationItem->setText(closest->m_text); + } + } +} + +void FreqScannerGUI::table_customContextMenuRequested(QPoint pos) +{ + QTableWidgetItem* item = ui->table->itemAt(pos); + if (item) + { + int row = item->row(); + + QMenu* tableContextMenu = new QMenu(ui->table); + connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater); + + // Copy current cell + + QAction* copyAction = new QAction("Copy", tableContextMenu); + const QString text = item->text(); + connect(copyAction, &QAction::triggered, this, [text]()->void { + QClipboard* clipboard = QGuiApplication::clipboard(); + clipboard->setText(text); + }); + tableContextMenu->addAction(copyAction); + + tableContextMenu->addSeparator(); + + // Tune to frequency + + const QRegExp re("R([0-9]+):([0-9]+)"); + if (re.indexIn(m_settings.m_channel) >= 0) + { + int scanDeviceSetIndex = re.capturedTexts()[1].toInt(); + int scanChannelIndex = re.capturedTexts()[2].toInt(); + qDebug() << "scanDeviceSetIndex" << scanDeviceSetIndex << "scanChannelIndex" << scanChannelIndex; + + qint64 frequency = ui->table->item(row, COL_FREQUENCY)->text().toLongLong(); + + QAction* findChannelMapAction = new QAction(QString("Tune R%1:%2 to %3").arg(scanDeviceSetIndex).arg(scanChannelIndex).arg(frequency), tableContextMenu); + connect(findChannelMapAction, &QAction::triggered, this, [this, scanDeviceSetIndex, scanChannelIndex, frequency]()->void { + + if ((frequency - m_settings.m_channelBandwidth / 2 < m_deviceCenterFrequency - m_basebandSampleRate / 2) + || (frequency + m_settings.m_channelBandwidth / 2 >= m_deviceCenterFrequency + m_basebandSampleRate / 2)) + { + qint64 centerFrequency = frequency; + int offset = 0; + while (frequency - centerFrequency < m_settings.m_channelFrequencyOffset) + { + centerFrequency -= m_settings.m_channelBandwidth; + offset += m_settings.m_channelBandwidth; + } + + if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), centerFrequency)) { + qWarning() << "Scanner failed to set frequency" << centerFrequency; + } + + ChannelWebAPIUtils::setFrequencyOffset(scanDeviceSetIndex, scanChannelIndex, offset); + } + else + { + int offset = frequency - m_deviceCenterFrequency; + ChannelWebAPIUtils::setFrequencyOffset(scanDeviceSetIndex, scanChannelIndex, offset); + } + + }); + tableContextMenu->addAction(findChannelMapAction); + } + else + { + qDebug() << "Failed to parse channel" << m_settings.m_channel; + } + + tableContextMenu->popup(ui->table->viewport()->mapToGlobal(pos)); + } +} + +// Columns in table reordered +void FreqScannerGUI::table_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ + (void)oldVisualIndex; + m_settings.m_columnIndexes[logicalIndex] = newVisualIndex; +} + +// Column in table resized (when hidden size is 0) +void FreqScannerGUI::table_sectionResized(int logicalIndex, int oldSize, int newSize) +{ + (void)oldSize; + m_settings.m_columnSizes[logicalIndex] = newSize; +} + +// Right click in ADSB table header - show column select menu +void FreqScannerGUI::columnSelectMenu(QPoint pos) +{ + m_menu->popup(ui->table->horizontalHeader()->viewport()->mapToGlobal(pos)); +} + +// Hide/show column when menu selected +void FreqScannerGUI::columnSelectMenuChecked(bool checked) +{ + (void)checked; + QAction* action = qobject_cast(sender()); + if (action != nullptr) + { + int idx = action->data().toInt(nullptr); + ui->table->setColumnHidden(idx, !action->isChecked()); + } +} + +// Create column select menu item +QAction* FreqScannerGUI::createCheckableItem(QString& text, int idx, bool checked) +{ + QAction* action = new QAction(text, this); + action->setCheckable(true); + action->setChecked(checked); + action->setData(QVariant(idx)); + connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked())); + return action; +} + +void FreqScannerGUI::resizeTable() +{ + // Fill table with a row of dummy data that will size the columns nicely + int row = ui->table->rowCount(); + ui->table->setRowCount(row + 1); + ui->table->setItem(row, COL_FREQUENCY, new QTableWidgetItem("999.000 MHz")); + ui->table->setItem(row, COL_ANNOTATION, new QTableWidgetItem("An annotation")); + ui->table->setItem(row, COL_ENABLE, new QTableWidgetItem("Enable")); + ui->table->setItem(row, COL_POWER, new QTableWidgetItem("-100.0")); + ui->table->setItem(row, COL_ACTIVE_COUNT, new QTableWidgetItem("10000")); + ui->table->setItem(row, COL_NOTES, new QTableWidgetItem("Enter some notes")); + ui->table->resizeColumnsToContents(); + ui->table->setRowCount(row); +} + +void FreqScannerGUI::makeUIConnections() +{ + QObject::connect(ui->channels, QOverload::of(&QComboBox::currentIndexChanged), this, &FreqScannerGUI::on_channels_currentIndexChanged); + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &FreqScannerGUI::on_deltaFrequency_changed); + QObject::connect(ui->channelBandwidth, &ValueDialZ::changed, this, &FreqScannerGUI::on_channelBandwidth_changed); + QObject::connect(ui->scanTime, &QDial::valueChanged, this, &FreqScannerGUI::on_scanTime_valueChanged); + QObject::connect(ui->retransmitTime, &QDial::valueChanged, this, &FreqScannerGUI::on_retransmitTime_valueChanged); + QObject::connect(ui->tuneTime, &QDial::valueChanged, this, &FreqScannerGUI::on_tuneTime_valueChanged); + QObject::connect(ui->thresh, &QDial::valueChanged, this, &FreqScannerGUI::on_thresh_valueChanged); + 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); + QObject::connect(ui->startStop, &ButtonSwitch::clicked, this, &FreqScannerGUI::on_startStop_clicked); + QObject::connect(ui->table, &QTableWidget::cellChanged, this, &FreqScannerGUI::on_table_cellChanged); + QObject::connect(ui->addSingle, &QToolButton::clicked, this, &FreqScannerGUI::on_addSingle_clicked); + QObject::connect(ui->addRange, &QToolButton::clicked, this, &FreqScannerGUI::on_addRange_clicked); + QObject::connect(ui->remove, &QToolButton::clicked, this, &FreqScannerGUI::on_remove_clicked); + QObject::connect(ui->up, &QToolButton::clicked, this, &FreqScannerGUI::on_up_clicked); + QObject::connect(ui->down, &QToolButton::clicked, this, &FreqScannerGUI::on_down_clicked); +} + +void FreqScannerGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h new file mode 100644 index 000000000..ef95ee212 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNERGUI_H +#define INCLUDE_FREQSCANNERGUI_H + +#include "channel/channelgui.h" +#include "dsp/channelmarker.h" +#include "util/messagequeue.h" +#include "settings/rollupstate.h" +#include "freqscanner.h" +#include "freqscannersettings.h" + +class PluginAPI; +class DeviceUISet; +class BasebandSampleSink; +class FreqScanner; +class FreqScannerGUI; +class QMenu; + +namespace Ui { + class FreqScannerGUI; +} +class FreqScannerGUI; + +class FreqScannerGUI : public ChannelGUI { + Q_OBJECT + +public: + static FreqScannerGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } + +public slots: + void channelMarkerChangedByCursor(); + void channelMarkerHighlightedByCursor(); + +private: + Ui::FreqScannerGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + RollupState m_rollupState; + FreqScannerSettings m_settings; + qint64 m_deviceCenterFrequency; + bool m_doApplySettings; + + FreqScanner* m_freqScanner; + int m_basebandSampleRate; + MessageQueue m_inputMessageQueue; + + QMenu *m_menu; + + explicit FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); + virtual ~FreqScannerGUI(); + + void blockApplySettings(bool block); + void applySetting(const QString& settingsKey); + void applySettings(const QStringList& settingsKeys, bool force = false); + void applyAllSettings(); + void displaySettings(); + bool handleMessage(const Message& message); + void makeUIConnections(); + void updateAbsoluteCenterFrequency(); + void addRow(qint64 frequency, bool enabled, const QString& notes = ""); + void updateAnnotation(int row); + void updateChannelsList(const QList& channels); + + void leaveEvent(QEvent*); + void enterEvent(EnterEventType*); + + void resizeTable(); + QAction* createCheckableItem(QString& text, int idx, bool checked); + + enum Col { + COL_FREQUENCY, + COL_ANNOTATION, + COL_ENABLE, + COL_POWER, + COL_ACTIVE_COUNT, + COL_NOTES + }; + +private slots: + void on_channels_currentIndexChanged(int index); + void on_deltaFrequency_changed(qint64 value); + void on_channelBandwidth_changed(qint64 index); + void on_scanTime_valueChanged(int value); + void on_retransmitTime_valueChanged(int value); + void on_tuneTime_valueChanged(int value); + void on_thresh_valueChanged(int value); + void on_priority_currentIndexChanged(int index); + void on_measurement_currentIndexChanged(int index); + void on_mode_currentIndexChanged(int index); + void on_table_cellChanged(int row, int column); + void table_customContextMenuRequested(QPoint pos); + void table_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); + void table_sectionResized(int logicalIndex, int oldSize, int newSize); + void columnSelectMenu(QPoint pos); + void columnSelectMenuChecked(bool checked = false); + void on_startStop_clicked(bool checked = false); + void on_addSingle_clicked(); + void on_addRange_clicked(); + void on_remove_clicked(); + void on_up_clicked(); + void on_down_clicked(); + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + void handleInputMessages(); +}; + +#endif // INCLUDE_FREQSCANNERGUI_H diff --git a/plugins/channelrx/freqscanner/freqscannergui.ui b/plugins/channelrx/freqscanner/freqscannergui.ui new file mode 100644 index 000000000..d00ed10e7 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannergui.ui @@ -0,0 +1,776 @@ + + + FreqScannerGUI + + + + 0 + 0 + 419 + 431 + + + + + 0 + 0 + + + + + 400 + 0 + + + + + Liberation Sans + 9 + + + + Qt::StrongFocus + + + Frequency Scanner + + + + + 0 + 0 + 411 + 411 + + + + + 0 + 0 + + + + + 350 + 0 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 2 + + + + + Channel + + + + + + + + 0 + 0 + + + + Channel to tune + + + + + + + Qt::Vertical + + + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Minimum demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Active frequency power + + + Qt::RightToLeft + + + - + + + + + + + dB + + + + + + + + + + + Qt::Horizontal + + + + + + + + + TH + + + + + + + + 24 + 24 + + + + Power threshold in dB + + + -1400 + + + 100 + + + 1 + + + + + + + + 55 + 0 + + + + -100.0 dB + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + + + + t<sub>Δf</sub> + + + + + + + + 24 + 24 + + + + Time in milliseconds to wait before starting measurement after changing frequency + + + 0 + + + 1000 + + + 1 + + + + + + + + 45 + 0 + + + + 100 ms + + + + + + + Qt::Vertical + + + + + + + t<sub>S</sub> + + + + + + + + 24 + 24 + + + + Scan power measurement time in seconds + + + 1 + + + 100 + + + 1 + + + + + + + + 35 + 0 + + + + 10.0 s + + + + + + + Qt::Vertical + + + + + + + t<sub>RTX</sub> + + + + + + + + 24 + 24 + + + + Time in seconds to wait for frequency to become active again, before restarting scan + + + 0 + + + 100 + + + 1 + + + + + + + + 35 + 0 + + + + 10.0 s + + + + + + + + + Qt::Horizontal + + + + + + + + + Ch BW + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Channel bandwidth + + + + + + + Hz + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Pri + + + + + + + + 85 + 0 + + + + Prioritisation. Select frequency with highest power or first in table. + + + + Max Power + + + + + Table Order + + + + + + + + Meas + + + + + + + Whether the power measurement is the peak power within the channel or total channel power + + + + Peak + + + + + Total + + + + + + + + + + Qt::Horizontal + + + + + + + + + + 90 + 0 + + + + Run mode + + + 1 + + + + Single + + + + + Continuous + + + + + Scan-only + + + + + + + + Start/stop frequency scanning + + + + + + + :/play.png + :/stop.png:/play.png + + + + + + + + 0 + 0 + + + + + + + + + + + + + Qt::Horizontal + + + + + + + + + + Freq (Hz) + + + Center frequency in Hertz of the channel + + + + + Annotation + + + + + Enable + + + Whether the channel is enabled + + + + + Power (dB) + + + Channel power in decibels during the previous scan + + + + + Active Count + + + Count of the number of times the channel is active when scanned + + + + + Notes + + + User notes about this frequency + + + + + + + + + + Add a single frequency + + + Add + + + + + + + Add a range of frequencies + + + Add Range + + + + + + + Remove selected items + + + Remove + + + + + + + Move selected rows up + + + Up + + + + :/arrow_up.png:/arrow_up.png + + + + + + + Move selected rows down + + + + + + + :/arrow_down.png:/arrow_down.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+
+ + deltaFrequency + + + + + +
diff --git a/plugins/channelrx/freqscanner/freqscannerplugin.cpp b/plugins/channelrx/freqscanner/freqscannerplugin.cpp new file mode 100644 index 000000000..37012c7eb --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannerplugin.cpp @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "freqscannergui.h" +#endif +#include "freqscanner.h" +#include "freqscannerwebapiadapter.h" +#include "freqscannerplugin.h" + +const PluginDescriptor FreqScannerPlugin::m_pluginDescriptor = { + FreqScanner::m_channelId, + QStringLiteral("Frequency Scanner"), + QStringLiteral("7.17.0"), + QStringLiteral("(c) Jon Beniston, M7RCE"), + QStringLiteral("https://github.com/f4exb/sdrangel"), + true, + QStringLiteral("https://github.com/f4exb/sdrangel") +}; + +FreqScannerPlugin::FreqScannerPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& FreqScannerPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void FreqScannerPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + m_pluginAPI->registerRxChannel(FreqScanner::m_channelIdURI, FreqScanner::m_channelId, this); +} + +void FreqScannerPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const +{ + if (bs || cs) + { + FreqScanner *instance = new FreqScanner(deviceAPI); + + if (bs) { + *bs = instance; + } + + if (cs) { + *cs = instance; + } + } +} + +#ifdef SERVER_MODE +ChannelGUI* FreqScannerPlugin::createRxChannelGUI( + DeviceUISet *deviceUISet, + BasebandSampleSink *rxChannel) const +{ + (void) deviceUISet; + (void) rxChannel; + return 0; +} +#else +ChannelGUI* FreqScannerPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const +{ + return FreqScannerGUI::create(m_pluginAPI, deviceUISet, rxChannel); +} +#endif + +ChannelWebAPIAdapter* FreqScannerPlugin::createChannelWebAPIAdapter() const +{ + return new FreqScannerWebAPIAdapter(); +} + diff --git a/plugins/channelrx/freqscanner/freqscannerplugin.h b/plugins/channelrx/freqscanner/freqscannerplugin.h new file mode 100644 index 000000000..403352b4c --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannerplugin.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2016 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNERPLUGIN_H +#define INCLUDE_FREQSCANNERPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSink; + +class FreqScannerPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.channel.freqscanner") + +public: + explicit FreqScannerPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const; + virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const; + virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_FREQSCANNERPLUGIN_H + diff --git a/plugins/channelrx/freqscanner/freqscannersettings.cpp b/plugins/channelrx/freqscanner/freqscannersettings.cpp new file mode 100644 index 000000000..d7ec2c6d9 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannersettings.cpp @@ -0,0 +1,366 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB. // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" +#include "freqscannersettings.h" + +FreqScannerSettings::FreqScannerSettings() : + m_channelMarker(nullptr), + m_rollupState(nullptr) +{ + for (int i = 0; i < FREQSCANNER_COLUMNS; i++) + { + m_columnIndexes.append(i); + m_columnSizes.append(-1); + } + resetToDefaults(); +} + +void FreqScannerSettings::resetToDefaults() +{ + m_inputFrequencyOffset = 0; + m_channelBandwidth = 25000; + m_channelFrequencyOffset = 25000; + m_threshold = -60.0f; + m_channel = ""; + m_scanTime = 0.1f; + m_retransmitTime = 2.0f; + m_tuneTime = 100; + m_priority = MAX_POWER; + m_measurement = PEAK; + m_mode = CONTINUOUS; + + for (int i = 0; i < FREQSCANNER_COLUMNS; i++) + { + m_columnIndexes[i] = i; + m_columnSizes[i] = -1; // Autosize + } + + m_rgbColor = QColor(0, 205, 200).rgb(); + m_title = "Frequency Scanner"; + m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; + m_hidden = false; +} + +QByteArray FreqScannerSettings::serialize() const +{ + SimpleSerializer s(1); + + s.writeS32(1, m_inputFrequencyOffset); + s.writeS32(2, m_channelBandwidth); + s.writeS32(3, m_channelFrequencyOffset); + s.writeFloat(4, m_threshold); + s.writeList(5, m_notes); + s.writeList(6, m_enabled); + s.writeList(7, m_frequencies); + s.writeString(8, m_channel); + s.writeFloat(9, m_scanTime); + s.writeFloat(10, m_retransmitTime); + s.writeS32(11, m_tuneTime); + s.writeS32(12, (int)m_priority); + s.writeS32(13, (int)m_measurement); + s.writeS32(14, (int)m_mode); + + s.writeList(20, m_columnIndexes); + s.writeList(21, m_columnSizes); + + s.writeU32(40, m_rgbColor); + s.writeString(41, m_title); + if (m_channelMarker) { + s.writeBlob(42, m_channelMarker->serialize()); + } + s.writeS32(44, m_streamIndex); + s.writeBool(45, m_useReverseAPI); + s.writeString(46, m_reverseAPIAddress); + s.writeU32(47, m_reverseAPIPort); + s.writeU32(48, m_reverseAPIDeviceIndex); + s.writeU32(49, m_reverseAPIChannelIndex); + if (m_rollupState) { + s.writeBlob(52, m_rollupState->serialize()); + } + s.writeS32(53, m_workspaceIndex); + s.writeBlob(54, m_geometryBytes); + s.writeBool(55, m_hidden); + + return s.final(); +} + +bool FreqScannerSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + uint32_t utmp; + QString strtmp; + + d.readS32(1, &m_inputFrequencyOffset, 0); + d.readS32(2, &m_channelBandwidth, 25000); + d.readS32(3, &m_channelFrequencyOffset, 25000); + d.readFloat(4, &m_threshold, -60.0f); + d.readList(5, &m_notes); + d.readList(6, &m_enabled); + d.readList(7, &m_frequencies); + d.readString(8, &m_channel); + while (m_notes.size() < m_frequencies.size()) { + m_notes.append(""); + } + while (m_enabled.size() < m_frequencies.size()) { + m_enabled.append(true); + } + d.readFloat(9, &m_scanTime, 0.1f); + d.readFloat(10, &m_retransmitTime, 2.0f); + d.readS32(11, &m_tuneTime, 100); + d.readS32(12, (int*)&m_priority, (int)MAX_POWER); + d.readS32(13, (int*)&m_measurement, (int)PEAK); + d.readS32(14, (int*)&m_mode, (int)CONTINUOUS); + + d.readList(20, &m_columnIndexes); + d.readList(21, &m_columnSizes); + + d.readU32(40, &m_rgbColor, QColor(0, 205, 200).rgb()); + d.readString(41, &m_title, "Frequency Scanner"); + if (m_channelMarker) + { + d.readBlob(42, &bytetmp); + m_channelMarker->deserialize(bytetmp); + } + d.readS32(44, &m_streamIndex, 0); + d.readBool(45, &m_useReverseAPI, false); + d.readString(46, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(47, &utmp, 0); + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + d.readU32(48, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(49, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + if (m_rollupState) + { + d.readBlob(52, &bytetmp); + m_rollupState->deserialize(bytetmp); + } + d.readS32(53, &m_workspaceIndex, 0); + d.readBlob(54, &m_geometryBytes); + d.readBool(55, &m_hidden, false); + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +void FreqScannerSettings::applySettings(const QStringList& settingsKeys, const FreqScannerSettings& settings) +{ + if (settingsKeys.contains("inputFrequencyOffset")) { + m_inputFrequencyOffset = settings.m_inputFrequencyOffset; + } + if (settingsKeys.contains("channelBandwidth")) { + m_channelBandwidth = settings.m_channelBandwidth; + } + if (settingsKeys.contains("channelFrequencyOffset")) { + m_channelFrequencyOffset = settings.m_channelFrequencyOffset; + } + if (settingsKeys.contains("threshold")) { + m_threshold = settings.m_threshold; + } + if (settingsKeys.contains("frequencies")) { + m_frequencies = settings.m_frequencies; + } + if (settingsKeys.contains("enabled")) { + m_enabled = settings.m_enabled; + } + if (settingsKeys.contains("notes")) { + m_notes = settings.m_notes; + } + if (settingsKeys.contains("channel")) { + m_channel = settings.m_channel; + } + if (settingsKeys.contains("scanTime")) { + m_scanTime = settings.m_scanTime; + } + if (settingsKeys.contains("retransmitTime")) { + m_retransmitTime = settings.m_retransmitTime; + } + if (settingsKeys.contains("tuneTime")) { + m_tuneTime = settings.m_tuneTime; + } + if (settingsKeys.contains("priority")) { + m_priority = settings.m_priority; + } + if (settingsKeys.contains("measurement")) { + m_measurement = settings.m_measurement; + } + if (settingsKeys.contains("mode")) { + m_mode = settings.m_mode; + } + if (settingsKeys.contains("columnIndexes")) { + m_columnIndexes = settings.m_columnIndexes; + } + if (settingsKeys.contains("columnSizes")) { + m_columnSizes = settings.m_columnSizes; + } + if (settingsKeys.contains("rgbColor")) { + m_rgbColor = settings.m_rgbColor; + } + if (settingsKeys.contains("title")) { + m_title = settings.m_title; + } + if (settingsKeys.contains("streamIndex")) { + m_streamIndex = settings.m_streamIndex; + } + if (settingsKeys.contains("useReverseAPI")) { + m_useReverseAPI = settings.m_useReverseAPI; + } + if (settingsKeys.contains("reverseAPIAddress")) { + m_reverseAPIAddress = settings.m_reverseAPIAddress; + } + if (settingsKeys.contains("reverseAPIPort")) { + m_reverseAPIPort = settings.m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIDeviceIndex")) { + m_reverseAPIDeviceIndex = settings.m_reverseAPIDeviceIndex; + } + if (settingsKeys.contains("reverseAPIChannelIndex")) { + m_reverseAPIChannelIndex = settings.m_reverseAPIChannelIndex; + } + if (settingsKeys.contains("workspaceIndex")) { + m_workspaceIndex = settings.m_workspaceIndex; + } + if (settingsKeys.contains("hidden")) { + m_hidden = settings.m_hidden; + } +} + +QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, bool force) const +{ + std::ostringstream ostr; + + if (settingsKeys.contains("inputFrequencyOffset") || force) { + ostr << " m_inputFrequencyOffset: " << m_inputFrequencyOffset; + } + if (settingsKeys.contains("channelBandwidth") || force) { + ostr << " m_channelBandwidth: " << m_channelBandwidth; + } + if (settingsKeys.contains("channelFrequencyOffset") || force) { + ostr << " m_channelFrequencyOffset: " << m_channelFrequencyOffset; + } + if (settingsKeys.contains("threshold") || force) { + ostr << " m_threshold: " << m_threshold; + } + if (settingsKeys.contains("frequencies") || force) + { + QStringList s; + for (auto f : m_frequencies) { + s.append(QString::number(f)); + } + ostr << " m_frequencies: " << s.join(",").toStdString(); + } + if (settingsKeys.contains("enabled") || force) + { + QStringList s; + for (auto e : m_enabled) { + s.append(e ? "true" : "false"); + } + ostr << " m_enabled: " << s.join(",").toStdString(); + } + if (settingsKeys.contains("notes") || force) { + ostr << " m_notes: " << m_notes.join(",").toStdString(); + } + if (settingsKeys.contains("channel") || force) { + ostr << " m_channel: " << m_channel.toStdString(); + } + if (settingsKeys.contains("scanTime") || force) { + ostr << " m_scanTime: " << m_scanTime; + } + if (settingsKeys.contains("retransmitTime") || force) { + ostr << " m_retransmitTime: " << m_retransmitTime; + } + if (settingsKeys.contains("tuneTime") || force) { + ostr << " m_tuneTime: " << m_tuneTime; + } + if (settingsKeys.contains("priority") || force) { + ostr << " m_priority: " << m_priority; + } + if (settingsKeys.contains("measurement") || force) { + ostr << " m_measurement: " << m_measurement; + } + if (settingsKeys.contains("mode") || force) { + ostr << " m_mode: " << m_mode; + } + if (settingsKeys.contains("columnIndexes") || force) { + // Don't display + } + if (settingsKeys.contains("columnSizes") || force) { + // Don't display + } + if (settingsKeys.contains("rgbColor") || force) { + ostr << " m_rgbColor: " << m_rgbColor; + } + if (settingsKeys.contains("title") || force) { + ostr << " m_title: " << m_title.toStdString(); + } + if (settingsKeys.contains("streamIndex") || force) { + ostr << " m_streamIndex: " << m_streamIndex; + } + if (settingsKeys.contains("useReverseAPI") || force) { + ostr << " m_useReverseAPI: " << m_useReverseAPI; + } + if (settingsKeys.contains("reverseAPIAddress") || force) { + ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString(); + } + if (settingsKeys.contains("reverseAPIPort") || force) { + ostr << " m_reverseAPIPort: " << m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIDeviceIndex") || force) { + ostr << " m_reverseAPIDeviceIndex: " << m_reverseAPIDeviceIndex; + } + if (settingsKeys.contains("reverseAPIChannelIndex") || force) { + ostr << " m_reverseAPIChannelIndex: " << m_reverseAPIChannelIndex; + } + if (settingsKeys.contains("workspaceIndex") || force) { + ostr << " m_workspaceIndex: " << m_workspaceIndex; + } + if (settingsKeys.contains("hidden") || force) { + ostr << " m_hidden: " << m_hidden; + } + + return QString(ostr.str().c_str()); +} diff --git a/plugins/channelrx/freqscanner/freqscannersettings.h b/plugins/channelrx/freqscanner/freqscannersettings.h new file mode 100644 index 000000000..f6dbd86e6 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannersettings.h @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB. // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNERSETTINGS_H +#define INCLUDE_FREQSCANNERSETTINGS_H + +#include + +class Serializable; +class ChannelAPI; + +// Number of columns in the table +#define FREQSCANNER_COLUMNS 6 + +struct FreqScannerSettings +{ + struct AvailableChannel + { + int m_deviceSetIndex; + int m_channelIndex; + ChannelAPI* m_channelAPI; + + AvailableChannel() = default; + AvailableChannel(const AvailableChannel&) = default; + AvailableChannel& operator=(const AvailableChannel&) = default; + }; + + qint32 m_inputFrequencyOffset; //!< Not modifable in GUI + qint32 m_channelBandwidth; //!< Channel bandwidth + qint32 m_channelFrequencyOffset;//!< Minium DC offset of tuned channel + Real m_threshold; //!< Power threshold in dB + QList m_frequencies; //!< Frequencies to scan + QList m_enabled; //!< Whether corresponding frequency is enabled + QList m_notes; //!< User editable notes about this frequency + QString m_channel; //!< Channel (E.g: R1:4) to tune to active frequency + float m_scanTime; //!< In seconds + float m_retransmitTime; //!< In seconds + int m_tuneTime; //!< In milliseconds + enum Priority { + MAX_POWER, + TABLE_ORDER + } m_priority; //!< Which frequency has priority when multiple frequencies are above threshold + enum Measurement { + PEAK, + TOTAL + } m_measurement; //!< How power is measured + enum Mode { + SINGLE, + CONTINUOUS, + SCAN_ONLY + } m_mode; //!< Whether to run a single or many scans + + QList m_columnIndexes;//!< How the columns are ordered in the table + QList m_columnSizes; //!< Size of the coumns in the table + + quint32 m_rgbColor; + QString m_title; + Serializable *m_channelMarker; + int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx). + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + + Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + bool m_hidden; + + FreqScannerSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + void applySettings(const QStringList& settingsKeys, const FreqScannerSettings& settings); + QString getDebugString(const QStringList& settingsKeys, bool force = false) const; +}; + +#endif /* INCLUDE_FREQSCANNERSETTINGS_H */ diff --git a/plugins/channelrx/freqscanner/freqscannersink.cpp b/plugins/channelrx/freqscanner/freqscannersink.cpp new file mode 100644 index 000000000..bc3625bed --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannersink.cpp @@ -0,0 +1,253 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include + +#include "dsp/dspengine.h" +#include "util/db.h" + +#include "freqscanner.h" +#include "freqscannersink.h" + +FreqScannerSink::FreqScannerSink(FreqScanner *ilsDemod) : + m_freqScanner(ilsDemod), + m_channel(nullptr), + m_channelSampleRate(48000), + m_channelFrequencyOffset(0), + m_scannerSampleRate(33320), + m_centerFrequency(0), + m_messageQueueToChannel(nullptr), + m_fftSequence(-1), + m_fft(nullptr), + m_fftCounter(0) +{ + applySettings(m_settings, QStringList(), true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, 16, 4, true); +} + +FreqScannerSink::~FreqScannerSink() +{ +} + +void FreqScannerSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + Complex ci; + + for (SampleVector::const_iterator it = begin; it != end; ++it) + { + Complex c(it->real(), it->imag()); + c *= m_nco.nextIQ(); + + if (m_interpolatorDistance == 1.0f) // Don't call decimate, as we don't want filter applied if possible + { + processOneSample(c); + } + else if (m_interpolatorDistance < 1.0f) // interpolate + { + while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + else // decimate + { + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } +} + +void FreqScannerSink::processOneSample(Complex &ci) +{ + ci /= SDR_RX_SCALEF; + + m_fft->in()[m_fftCounter] = ci; + m_fftCounter++; + if (m_fftCounter == m_fftSize) + { + // Apply windowing function + m_fftWindow.apply(m_fft->in()); + + // Perform FFT + m_fft->transform(); + + // Reorder (so negative frequencies are first) and average + int halfSize = m_fftSize / 2; + for (int i = 0; i < halfSize; i++) { + m_fftAverage.storeAndGetAvg(m_magSq[i], magSq(i + halfSize), i); + } + for (int i = 0; i < halfSize; i++) { + m_fftAverage.storeAndGetAvg(m_magSq[i + halfSize], magSq(i), i + halfSize); + } + + if (m_fftAverage.nextAverage()) + { + // Send results to channel + if (getMessageQueueToChannel()) + { + FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(m_fftStartTime); + QList& results = msg->getScanResults(); + + for (int i = 0; i < m_settings.m_frequencies.size(); i++) + { + if (m_settings.m_enabled[i]) + { + qint64 frequency = m_settings.m_frequencies[i]; + qint64 startFrequency = m_centerFrequency - m_scannerSampleRate / 2; + qint64 diff = frequency - startFrequency; + int binBW = m_settings.m_channelBandwidth / m_binsPerChannel; + + if ((diff < m_scannerSampleRate) && (diff >= 0)) + { + int bin = diff / binBW; + + // Calculate power at that frequency + Real power; + if (m_settings.m_measurement == FreqScannerSettings::PEAK) { + power = peakPower(bin); + } else { + power = totalPower(bin); + } + + FreqScanner::MsgScanResult::ScanResult result = {frequency, power}; + results.append(result); + } + } + } + getMessageQueueToChannel()->push(msg); + } + m_averageCount = 0; + m_fftStartTime = QDateTime::currentDateTime(); + } + m_fftCounter = 0; + } +} + +// Calculate total power in a channel containing the specified bin (i.e. sums adjacent bins in the same channel) +Real FreqScannerSink::totalPower(int bin) const +{ + // Skip bin between halfway between channels + // Then skip first and last bins, to avoid spectral leakage (particularly at DC) + int startBin = bin - m_binsPerChannel / 2 + 1 + 1; + Real magSqSum = 0.0f; + for (int i = 0; i < m_binsPerChannel - 2 - 1; i++) { + int idx = startBin + i; + if ((idx < 0) || (idx >= m_fftSize)) { + continue; + } + magSqSum += m_magSq[idx]; + } + Real db = CalcDb::dbPower(magSqSum); + return db; +} + +// Calculate peak power in a channel containing the specified bin +Real FreqScannerSink::peakPower(int bin) const +{ + // Skip bin between halfway between channels + // Then skip first and last bins, to avoid spectral leakage (particularly at DC) + int startBin = bin - m_binsPerChannel/2 + 1 + 1; + Real maxMagSq = m_magSq[startBin]; + for (int i = 1; i < m_binsPerChannel - 2 - 1; i++) { + int idx = startBin + i; + if ((idx < 0) || (idx >= m_fftSize)) { + continue; + } + maxMagSq = std::max(maxMagSq, m_magSq[idx]); + } + Real db = CalcDb::dbPower(maxMagSq); + return db; +} + +Real FreqScannerSink::magSq(int bin) const +{ + Complex c = m_fft->out()[bin]; + Real v = c.real() * c.real() + c.imag() * c.imag(); + Real magsq = v / (m_fftSize * m_fftSize); + return magsq; +} + +void FreqScannerSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, int scannerSampleRate, int fftSize, int binsPerChannel, bool force) +{ + qDebug() << "FreqScannerSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset + << " scannerSampleRate: " << scannerSampleRate + << " fftSize: " << fftSize + << " binsPerChannel: " << binsPerChannel; + + if ((m_channelFrequencyOffset != channelFrequencyOffset) || + (m_channelSampleRate != channelSampleRate) || force) + { + m_nco.setFreq(-channelFrequencyOffset, channelSampleRate); + } + + if ((m_channelSampleRate != channelSampleRate) || (m_scannerSampleRate != scannerSampleRate) || force) + { + m_interpolator.create(16, channelSampleRate, scannerSampleRate / 2.0); // Highest cutoff, so we don't attentuate first/last channel + m_interpolatorDistance = (Real) channelSampleRate / (Real)scannerSampleRate; + m_interpolatorDistanceRemain = m_interpolatorDistance; + } + + if ((m_fftSize != fftSize) || force) + { + FFTFactory* fftFactory = DSPEngine::instance()->getFFTFactory(); + if (m_fftSequence >= 0) { + fftFactory->releaseEngine(fftSize, false, m_fftSequence); + } + m_fftSequence = fftFactory->getEngine(fftSize, false, &m_fft); + m_fftCounter = 0; + m_fftStartTime = QDateTime::currentDateTime(); + m_fftWindow.create(FFTWindow::Hanning, fftSize); + + int averages = m_settings.m_scanTime * scannerSampleRate / 2 / fftSize; + m_fftAverage.resize(fftSize, averages); + m_magSq.resize(fftSize); + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; + m_scannerSampleRate = scannerSampleRate; + m_fftSize = fftSize; + m_binsPerChannel = binsPerChannel; +} + +void FreqScannerSink::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) +{ + qDebug() << "FreqScannerSink::applySettings:" + << settings.getDebugString(settingsKeys, force) + << " force: " << force; + + if (settingsKeys.contains("scanTime") || force) + { + int averages = settings.m_scanTime * m_scannerSampleRate / 2 / m_fftSize; + m_fftAverage.resize(m_fftSize, averages); + } + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} diff --git a/plugins/channelrx/freqscanner/freqscannersink.h b/plugins/channelrx/freqscanner/freqscannersink.h new file mode 100644 index 000000000..16b276d6e --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannersink.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNERSINK_H +#define INCLUDE_FREQSCANNERSINK_H + +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/nco.h" +#include "dsp/interpolator.h" +#include "dsp/fftfactory.h" +#include "dsp/fftengine.h" +#include "dsp/fftwindow.h" +#include "util/fixedaverage2d.h" +#include "util/messagequeue.h" + +#include "freqscannersettings.h" + +class ChannelAPI; +class FreqScanner; + +class FreqScannerSink : public ChannelSampleSink { +public: + FreqScannerSink(FreqScanner *packetDemod); + ~FreqScannerSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, int scannerSampleRate, int fftSize, int binsPerChannel, bool force = false); + void applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force = false); + void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; } + void setChannel(ChannelAPI *channel) { m_channel = channel; } + void setCenterFrequency(qint64 centerFrequency) { m_centerFrequency = centerFrequency; } + +private: + + FreqScanner *m_freqScanner; + FreqScannerSettings m_settings; + ChannelAPI *m_channel; + int m_channelSampleRate; + int m_channelFrequencyOffset; + int m_scannerSampleRate; // Sample rate scanner runs at + qint64 m_centerFrequency; + + NCO m_nco; + Interpolator m_interpolator; + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + + MessageQueue *m_messageQueueToChannel; + + int m_fftSequence; + FFTEngine *m_fft; + int m_fftCounter; + FFTWindow m_fftWindow; + int m_fftSize; + int m_binsPerChannel; + QDateTime m_fftStartTime; + FixedAverage2D m_fftAverage; // magSq average + QVector m_magSq; + int m_averageCount; + + void processOneSample(Complex &ci); + MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; } + Real totalPower(int bin) const; + Real peakPower(int bin) const; + Real magSq(int bin) const; +}; + +#endif // INCLUDE_FREQSCANNERSINK_H diff --git a/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp b/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp new file mode 100644 index 000000000..37dcddef6 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB. // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGChannelSettings.h" +#include "freqscanner.h" +#include "freqscannerwebapiadapter.h" + +FreqScannerWebAPIAdapter::FreqScannerWebAPIAdapter() +{} + +FreqScannerWebAPIAdapter::~FreqScannerWebAPIAdapter() +{} + +int FreqScannerWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + /*response.ssetFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + response.gsetFreqScannerSettings()->init(); + FreqScanner::webapiFormatChannelSettings(response, m_settings);*/ + + return 200; +} + +int FreqScannerWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) force; + (void) errorMessage; + FreqScanner::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + + return 200; +} diff --git a/plugins/channelrx/freqscanner/freqscannerwebapiadapter.h b/plugins/channelrx/freqscanner/freqscannerwebapiadapter.h new file mode 100644 index 000000000..e8afc6c50 --- /dev/null +++ b/plugins/channelrx/freqscanner/freqscannerwebapiadapter.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB. // +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// This program 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FREQSCANNER_WEBAPIADAPTER_H +#define INCLUDE_FREQSCANNER_WEBAPIADAPTER_H + +#include "channel/channelwebapiadapter.h" +#include "freqscannersettings.h" + +/** + * Standalone API adapter only for the settings + */ +class FreqScannerWebAPIAdapter : public ChannelWebAPIAdapter { +public: + FreqScannerWebAPIAdapter(); + virtual ~FreqScannerWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + +private: + FreqScannerSettings m_settings; +}; + +#endif // INCLUDE_FREQSCANNER_WEBAPIADAPTER_H diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md new file mode 100644 index 000000000..1b3d1ab5f --- /dev/null +++ b/plugins/channelrx/freqscanner/readme.md @@ -0,0 +1,121 @@ +

Frequency Scanner Plugin

+ +

Introduction

+ +This plugin can be used to scan a range of frequencies looking for a transmission and then tune another channel (such as an AM or DSD Demod) to that frequency. + +

Interface

+ +The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + +![Frequency Scanner plugin GUI](../../../doc/img/FreqScanner_plugin.png) + +

1: Channel

+ +Specifies the channel (such as an AM, NFM or DSD Demod), by device set and channel index, that should be tuned to the active frequency. + +

2: Minimum frequency shift from center frequency of reception for channel

+ +Use the wheels of keyboard to adjust the minimim frequency shift in Hz from the center frequency of reception for the channel (1). Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. + +

3: Active frequency power

+ +Average power in dB relative to a +/- 1.0 amplitude signal received for the active frequency. This is set to '-' while scanning. + +

4: TH - Threshold

+ +Power threshold in dB that determines whether a frequency is active or not. + +

5: t_delta_f - Tune time

+ +Specifies the time in milliseconds that the Frequency Scanner should wait after adjusting the device center frequency, before starting a measurement. +This time should take in to account PLL settle time and the device to host transfer latency, so that the measurement only starts when IQ data +that corresponds to the set frequency is being recieved. + +

6: t_s - Scan time

+ +Specifies the time in seconds that the Frequency Scanner will average its channel power measurement over. + +

7: t_rtx - Retransmission Time

+ +Specifies the time in seconds that the Frequency Scanner will wait after the power on the active frequency falls below the threshold, before restarting +scanning. This allows for a temporary break in transmission. + +

8: Ch BW - Channel Bandwidth

+ +This specifies the bandwidth of the channels to be scanned. + +

9: Pri - Priority

+ +Specifies which frequency will be chosen as the active frequency, when multiple frequencies exceed the threshold (4): + +- Max power: The frequency with the highest power will be chosen +- Table order: The frequency first in the frequency table (14) will be chosen. + +

10: Meas - Power Measurement

+ +Specifies how power is measured. In both cases, a FFT is used, with the channel bandwidth being spread over 8 to 32 bins, with the first and last bins being excluded from the measurement (to reduce spectral leakage from adjacent channels): + +- Peak: Power is the highest value in all of the bins, averaged over the scan time (6). +- Total: Power is the sum of power in all of the bins, averaged over the scan time (6). + +Peak can be used when you wish to set the threshold roughly according to the level displayed in the Main Spectrum. +Total is potentially more useful for wideband signals, that are close to the noise floor. + +

11: Run Mode

+ +Specifies the run mode: + +- Single: All frequencies are scanned once. Channel (1) is tuned to the active frequency at the end of the scan. The scan does not repeat. +- Continuous: All frequencies scanned, with channel (1) being tuned to active frequency at the end of the scan. Scan repeats once the power on the active frequency falls below the threshold (4) for longer than the retransmission time (7). +- Scan only: All frequencies are scanned repeatedly. The channel will not be tuned. This mode is just for counting how often frequencies are active, which can be seen in the Active Count column in the frequency table (14). + +

12: Start/Stop Scanning

+ +Press this button to start or stop scanning. + +

13: Status Text

+ +Displays the current status of the Frequency Scanner. + +- "Scanning": When scanning for active frequencies. +- Frequency and annotation for active frequency. + +

14: Frequency Table

+ +The frequency table contains the list of frequencies to be scanned, along with results of a scan. The columns are: + +- Freq (Hz): Specifies the channel center frequencies to be scanned. These should be spaced by integer multiples of the channel bandwidth (8). Values should be entered in Hertz. +- Annotation: An annotation (description) for the frequency, that is based on the closest matching [annotation marker](../../../sdrgui/gui/spectrummarkers.md) in the Main Spectrum. +- Enable: Determines whether the frequency will be scanned. This can be used to temporaily disable frequencies you aren't interested in. +- Power (dB): Displays the measured power in decibels during the previous scan. The cell will have a green background if the power was above the threshold (4). +- Active Count: Displays the number of scans in which the power for this frequency was above the threshold (4). This allows you to see which frequencies are commonly in use. +- Notes: Available for user-entry of notes/information about this frequency. + +When an active frequency is found after a scan, the corresponding row in the table will be selected. + +Right clicking on a cell will display a popup menu: + +- Copy contents of cell to clipboard. +- Tune selected channel (1) to the frequency in the row clicked on. + +

15: Add

+ +Press to add a single row to the frequency table (14). + +

16: Add Range

+ +Press to add a range of frequencies to the frequency table (14). A dialog is displayed with start and stop frequencies, as well as a step value. +The step value should typically be an integer multiple of the channel bandwidth (8). + +

17: Remove

+ +Removes the selected rows from the frequency table (14). + +

18: Up

+ +Moves the selected rows up the frequency table (14). + +

19: Down

+ +Moves the selected rows the the frequency table (14). diff --git a/sdrbase/channel/channelwebapiutils.cpp b/sdrbase/channel/channelwebapiutils.cpp index 51bb68d95..25527223d 100644 --- a/sdrbase/channel/channelwebapiutils.cpp +++ b/sdrbase/channel/channelwebapiutils.cpp @@ -937,6 +937,47 @@ bool ChannelWebAPIUtils::setFrequencyOffset(unsigned int deviceIndex, int channe return false; } +bool ChannelWebAPIUtils::setAudioMute(unsigned int deviceIndex, int channelIndex, bool mute) +{ + SWGSDRangel::SWGChannelSettings channelSettingsResponse; + QString errorResponse; + int httpRC; + QJsonObject* jsonObj; + + ChannelAPI* channel = MainCore::instance()->getChannel(deviceIndex, channelIndex); + if (channel != nullptr) + { + httpRC = channel->webapiSettingsGet(channelSettingsResponse, errorResponse); + if (httpRC / 100 != 2) + { + qWarning("ChannelWebAPIUtils::setAudioMute: get channel settings error %d: %s", + httpRC, qPrintable(errorResponse)); + return false; + } + + jsonObj = channelSettingsResponse.asJsonObject(); + + if (WebAPIUtils::setSubObjectInt(*jsonObj, "audioMute", (int)mute)) + { + QStringList keys; + keys.append("audioMute"); + channelSettingsResponse.init(); + channelSettingsResponse.fromJsonObject(*jsonObj); + httpRC = channel->webapiSettingsPutPatch(false, keys, channelSettingsResponse, errorResponse); + if (httpRC / 100 != 2) + { + qWarning("ChannelWebAPIUtils::setAudioMute: patch channel settings error %d: %s", + httpRC, qPrintable(errorResponse)); + return false; + } + + return true; + } + } + return false; +} + + // Start or stop all file sinks in a given device set bool ChannelWebAPIUtils::startStopFileSinks(unsigned int deviceIndex, bool start) { diff --git a/sdrbase/channel/channelwebapiutils.h b/sdrbase/channel/channelwebapiutils.h index cce4fe7e4..2de9b5eac 100644 --- a/sdrbase/channel/channelwebapiutils.h +++ b/sdrbase/channel/channelwebapiutils.h @@ -60,6 +60,7 @@ public: static bool stop(unsigned int deviceIndex, int subsystemIndex=0); static bool getFrequencyOffset(unsigned int deviceIndex, int channelIndex, int& offset); static bool setFrequencyOffset(unsigned int deviceIndex, int channelIndex, int offset); + static bool setAudioMute(unsigned int deviceIndex, int channelIndex, bool mute); static bool startStopFileSinks(unsigned int deviceIndex, bool start); static bool satelliteAOS(const QString name, bool northToSouthPass, const QString &tle, QDateTime dateTime); static bool satelliteLOS(const QString name); diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 06ae1ebf1..34441fe4e 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4523,6 +4523,11 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->setFreeDvModSettings(new SWGSDRangel::SWGFreeDVModSettings()); channelSettings->getFreeDvModSettings()->fromJsonObject(settingsJsonObject); } + else if (channelSettingsKey == "FreqScannerSettings") + { + //channelSettings->setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + //channelSettings->getFreqScannerSettings()->fromJsonObject(settingsJsonObject); + } else if (channelSettingsKey == "FreqTrackerSettings") { channelSettings->setFreqTrackerSettings(new SWGSDRangel::SWGFreqTrackerSettings()); @@ -5442,6 +5447,8 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setDatvModSettings(nullptr); channelSettings.setDabDemodSettings(nullptr); channelSettings.setDsdDemodSettings(nullptr); + //channelSettings.setFreqScannerSettings(nullptr); + channelSettings.setFreqTrackerSettings(nullptr); channelSettings.setHeatMapSettings(nullptr); channelSettings.setIeee802154ModSettings(nullptr); channelSettings.setIlsDemodSettings(nullptr); @@ -5484,6 +5491,8 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setBfmDemodReport(nullptr); channelReport.setDatvModReport(nullptr); channelReport.setDsdDemodReport(nullptr); + //channelReport.setFreqScannerReport(nullptr); + channelReport.setFreqTrackerReport(nullptr); channelReport.setHeatMapReport(nullptr); channelReport.setIlsDemodReport(nullptr); channelReport.setNavtexDemodReport(nullptr); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 680366844..d8360d73b 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -47,6 +47,7 @@ const QMap WebAPIUtils::m_channelURIToSettingsKey = { {"sdrangel.channeltx.filesource", "FileSourceSettings"}, {"sdrangel.channel.freedvdemod", "FreeDVDemodSettings"}, {"sdrangel.channeltx.freedvmod", "FreeDVModSettings"}, + {"sdrangel.channel.freqscanner", "FreqScannerSettings"}, {"sdrangel.channel.freqtracker", "FreqTrackerSettings"}, {"sdrangel.channel.heatmap", "HeatMapSettings"}, {"sdrangel.channel.ilsdemod", "ILSDemodSettings"}, @@ -168,6 +169,7 @@ const QMap WebAPIUtils::m_channelTypeToSettingsKey = { {"FileSource", "FileSourceSettings"}, {"FreeDVDemod", "FreeDVDemodSettings"}, {"FreeDVMod", "FreeDVModSettings"}, + {"FreqScanner", "FreqScannerSettings"}, {"FreqTracker", "FreqTrackerSettings"}, {"HeatMap", "HeatMapSettings"}, {"IEEE_802_15_4_Mod", "IEEE_802_15_4_ModSettings"}, diff --git a/sdrgui/gui/frequencydelegate.cpp b/sdrgui/gui/frequencydelegate.cpp index 3b063ad81..cb4a2b470 100644 --- a/sdrgui/gui/frequencydelegate.cpp +++ b/sdrgui/gui/frequencydelegate.cpp @@ -15,9 +15,11 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +#include + #include "frequencydelegate.h" -FrequencyDelegate::FrequencyDelegate(QString units, int precision, bool group) : +FrequencyDelegate::FrequencyDelegate(const QString& units, int precision, bool group) : m_units(units), m_precision(precision), m_group(group) @@ -30,29 +32,107 @@ QString FrequencyDelegate::displayText(const QVariant &value, const QLocale &loc qlonglong v = value.toLongLong(&ok); if (ok) { - double d; - if (m_units == "GHz") { - d = v / 1000000000.0; - } else if (m_units == "MHz") { - d = v / 1000000.0; - } else if (m_units == "kHz") { - d = v / 1000.0; - } else { - d = v; - } - QLocale l(locale); if (m_group) { l.setNumberOptions(l.numberOptions() & ~QLocale::OmitGroupSeparator); - } else { + } + else { l.setNumberOptions(l.numberOptions() | QLocale::OmitGroupSeparator); } - QString s = l.toString(d, 'f', m_precision); - return QString("%1 %2").arg(s).arg(m_units); + if (m_units == "Auto") + { + if (v == 0) + { + return "0 Hz"; + } + else + { + QString s = QString::number(v); + int scale = 1; + while (s.endsWith("000")) + { + s.chop(3); + scale *= 1000; + } + v /= scale; + double d = v; + if ((abs(v) >= 1000) && (m_precision >= 3)) + { + scale *= 1000; + d /= 1000.0; + } + QString units; + if (scale == 1) { + units = "Hz"; + } else if (scale == 1000) { + units = "kHz"; + } else if (scale == 1000000) { + units = "MHz"; + } else if (scale == 1000000000) { + units = "GHz"; + } + if (scale == 1) { + s = l.toString(d, 'f', 0); + } else { + s = l.toString(d, 'f', m_precision); + } + + return QString("%1 %2").arg(s).arg(units); + } + } + else + { + double d; + if (m_units == "GHz") { + d = v / 1000000000.0; + } + else if (m_units == "MHz") { + d = v / 1000000.0; + } + else if (m_units == "kHz") { + d = v / 1000.0; + } + else { + d = v; + } + + + QString s = l.toString(d, 'f', m_precision); + + return QString("%1 %2").arg(s).arg(m_units); + } } else { return value.toString(); } } + +QWidget* FrequencyDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + QLineEdit* editor = new QLineEdit(parent); + QIntValidator* validator = new QIntValidator(); + validator->setBottom(0); + editor->setValidator(validator); + return editor; +} + +void FrequencyDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const +{ + QString value = index.model()->data(index, Qt::EditRole).toString(); + QLineEdit* line = static_cast(editor); + line->setText(value); +} + +void FrequencyDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const +{ + QLineEdit* line = static_cast(editor); + QString value = line->text(); + model->setData(index, value); +} + +void FrequencyDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + editor->setGeometry(option.rect); +} diff --git a/sdrgui/gui/frequencydelegate.h b/sdrgui/gui/frequencydelegate.h index 4899bbee6..8520c5907 100644 --- a/sdrgui/gui/frequencydelegate.h +++ b/sdrgui/gui/frequencydelegate.h @@ -26,8 +26,14 @@ class SDRGUI_API FrequencyDelegate : public QStyledItemDelegate { public: - FrequencyDelegate(QString units = "kHz", int precision=1, bool group=true); - virtual QString displayText(const QVariant &value, const QLocale &locale) const override; + FrequencyDelegate(const QString& units = "kHz", int precision=1, bool group=true); + QString displayText(const QVariant &value, const QLocale &locale) const override; + +protected: + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void setEditorData(QWidget* editor, const QModelIndex& index) const; + void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const; + void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; private: QString m_units; diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index 01b5d29b8..59aa2633c 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -49,6 +49,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/FreeDVDemod.yaml#/FreeDVDemodReport" FreeDVModReport: $ref: "http://swgserver:8081/api/swagger/include/FreeDVMod.yaml#/FreeDVModReport" + FreqScannerReport: + $ref: "http://swgserver:8081/api/swagger/include/FreqScanner.yaml#/FreqScannerReport" FreqTrackerReport: $ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerReport" FT8DemodReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index 42e566dfb..f19c7ffc5 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -63,6 +63,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/FreeDVDemod.yaml#/FreeDVDemodSettings" FreeDVModSettings: $ref: "http://swgserver:8081/api/swagger/include/FreeDVMod.yaml#/FreeDVModSettings" + FreqScannerSettings: + $ref: "http://swgserver:8081/api/swagger/include/FreqScanner.yaml#/FreqScannerSettings" FreqTrackerSettings: $ref: "http://swgserver:8081/api/swagger/include/FreqTracker.yaml#/FreqTrackerSettings" FT8DemodSettings: diff --git a/swagger/sdrangel/api/swagger/include/FreqScanner.yaml b/swagger/sdrangel/api/swagger/include/FreqScanner.yaml new file mode 100644 index 000000000..82d69a185 --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/FreqScanner.yaml @@ -0,0 +1,70 @@ +FreqTrackerSettings: + description: FreqTracker + properties: + channelBandwidth: + description: channel RF bandwidth in Hz + type: integer + channelFrequencyOffset: + description: channel center frequency shift from baseband center in Hz + type: integer + threshold: + type: number + format: float + m_frequencies: + type: array + items: + type: integer + format: int64 + m_enabled: + type: array + items: + type: integer + m_notes: + type: array + items: + type: string + channel: + type: string + scanTime: + type: number + format: float + retransmitTime: + type: number + format: float + tuneTime: + type: number + format: float + priority: + type: integer + measurement: + type: integer + mode: + type: integer + rgbColor: + type: integer + title: + type: string + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + channelMarker: + $ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker" + rollupState: + $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" + +FreqScannerReport: + description: FreqScanner + properties: + channelSampleRate: + type: integer From b42b6be910f63fd3075a743cdc3fe90ba5dc05e5 Mon Sep 17 00:00:00 2001 From: srcejon Date: Thu, 28 Sep 2023 18:23:15 +0100 Subject: [PATCH 02/23] Add Freq Scanner --- plugins/channelrx/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 7721c1960..c2b2ae395 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -1,5 +1,9 @@ project(demod) +if (ENABLE_CHANNELRX_FREQSCANNER) + add_subdirectory(freqscanner) +endif() + if (ENABLE_CHANNELRX_DEMODADSB AND Qt${QT_DEFAULT_MAJOR_VERSION}Quick_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}QuickWidgets_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}Positioning_FOUND AND Qt${QT_DEFAULT_MAJOR_VERSION}TextToSpeech_FOUND) add_subdirectory(demodadsb) # add_subdirectory(demodvormc) From 2192a054ed81f8d0a5db1c8475c51015d03df65c Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Thu, 28 Sep 2023 19:02:58 +0100 Subject: [PATCH 03/23] Generate swagger files for Freq Scanner --- plugins/channelrx/freqscanner/freqscanner.cpp | 162 ++--- .../channelrx/freqscanner/freqscannergui.cpp | 8 +- .../freqscanner/freqscannersettings.h | 1 - .../channelrx/freqscanner/freqscannersink.cpp | 5 +- .../freqscanner/freqscannerwebapiadapter.cpp | 6 +- sdrbase/webapi/webapirequestmapper.cpp | 8 +- .../api/swagger/include/FreqScanner.yaml | 4 +- .../code/qt5/client/SWGChannelReport.cpp | 25 + .../code/qt5/client/SWGChannelReport.h | 7 + .../code/qt5/client/SWGChannelSettings.cpp | 25 + .../code/qt5/client/SWGChannelSettings.h | 7 + .../code/qt5/client/SWGFreqScannerReport.cpp | 108 +++ .../code/qt5/client/SWGFreqScannerReport.h | 58 ++ .../qt5/client/SWGFreqScannerSettings.cpp | 636 ++++++++++++++++++ .../code/qt5/client/SWGFreqScannerSettings.h | 194 ++++++ .../code/qt5/client/SWGModelFactory.h | 12 + 16 files changed, 1147 insertions(+), 119 deletions(-) create mode 100644 swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.h create mode 100644 swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp create mode 100644 swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 904a94111..38b36a106 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -30,7 +30,7 @@ #include "SWGChannelSettings.h" #include "SWGWorkspaceInfo.h" -//#include "SWGFreqScannerSettings.h" +#include "SWGFreqScannerSettings.h" #include "SWGChannelReport.h" #include "SWGMapItem.h" @@ -285,9 +285,7 @@ void FreqScanner::initScan() qInfo() << "m_minFFTStartTime" << m_minFFTStartTime.toString("ss.z"); } - qDebug() << "********* initScan: Clear results"; m_scanResults.clear(); - qDebug() << "********* initScan: Clear results done"; if (m_guiMessageQueue) { m_guiMessageQueue->push(FreqScanner::MsgReportScanning::create()); @@ -301,6 +299,8 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< switch (m_state) { + case IDLE: + break; case START_SCAN: { @@ -642,9 +642,9 @@ int FreqScanner::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - /*response.ssetFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); - response.gsetFreqScannerSettings()->init(); - webapiFormatChannelSettings(response, m_settings);*/ + response.setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + response.getFreqScannerSettings()->init(); + webapiFormatChannelSettings(response, m_settings); return 200; } @@ -687,9 +687,9 @@ int FreqScanner::webapiReportGet( QString& errorMessage) { (void) errorMessage; - /*response.ssetFreqScannerReport(new SWGSDRangel::SWGFreqScannerReport()); - response.gsetFreqScannerReport()->init(); - webapiFormatChannelReport(response);*/ + response.setFreqScannerReport(new SWGSDRangel::SWGFreqScannerReport()); + response.getFreqScannerReport()->init(); + webapiFormatChannelReport(response); return 200; } @@ -698,134 +698,105 @@ void FreqScanner::webapiUpdateChannelSettings( const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response) { - /*if (channelSettingsKeys.contains("inputFrequencyOffset")) { - settings.m_inputFrequencyOffset = response.gsetFreqScannerSettings()->getInputFrequencyOffset(); + if (channelSettingsKeys.contains("channelFrequencyOffset")) { + settings.m_channelFrequencyOffset = response.getFreqScannerSettings()->getChannelFrequencyOffset(); } - if (channelSettingsKeys.contains("rfBandwidth")) { - settings.m_channelBandwidth = response.gsetFreqScannerSettings()->getRfBandwidth(); - } - if (channelSettingsKeys.contains("audioMute")) { - settings.m_audioMute = response.gsetFreqScannerSettings()->getAudioMute(); + if (channelSettingsKeys.contains("channelBandwidth")) { + settings.m_channelBandwidth = response.getFreqScannerSettings()->getChannelBandwidth(); } if (channelSettingsKeys.contains("threshold")) { - settings.m_threshold = response.gsetFreqScannerSettings()->getThreshold(); - } - if (channelSettingsKeys.contains("logFilename")) { - settings.m_logFilename = *response.getAdsbDemodSettings()->getLogFilename(); - } - if (channelSettingsKeys.contains("logEnabled")) { - settings.m_logEnabled = response.getAdsbDemodSettings()->getLogEnabled(); + settings.m_threshold = response.getFreqScannerSettings()->getThreshold(); } if (channelSettingsKeys.contains("rgbColor")) { - settings.m_rgbColor = response.gsetFreqScannerSettings()->getRgbColor(); + settings.m_rgbColor = response.getFreqScannerSettings()->getRgbColor(); } if (channelSettingsKeys.contains("title")) { - settings.m_title = *response.gsetFreqScannerSettings()->getTitle(); + settings.m_title = *response.getFreqScannerSettings()->getTitle(); } if (channelSettingsKeys.contains("streamIndex")) { - settings.m_streamIndex = response.gsetFreqScannerSettings()->getStreamIndex(); + settings.m_streamIndex = response.getFreqScannerSettings()->getStreamIndex(); } if (channelSettingsKeys.contains("useReverseAPI")) { - settings.m_useReverseAPI = response.gsetFreqScannerSettings()->getUseReverseApi() != 0; + settings.m_useReverseAPI = response.getFreqScannerSettings()->getUseReverseApi() != 0; } if (channelSettingsKeys.contains("reverseAPIAddress")) { - settings.m_reverseAPIAddress = *response.gsetFreqScannerSettings()->getReverseApiAddress(); + settings.m_reverseAPIAddress = *response.getFreqScannerSettings()->getReverseApiAddress(); } if (channelSettingsKeys.contains("reverseAPIPort")) { - settings.m_reverseAPIPort = response.gsetFreqScannerSettings()->getReverseApiPort(); + settings.m_reverseAPIPort = response.getFreqScannerSettings()->getReverseApiPort(); } if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { - settings.m_reverseAPIDeviceIndex = response.gsetFreqScannerSettings()->getReverseApiDeviceIndex(); + settings.m_reverseAPIDeviceIndex = response.getFreqScannerSettings()->getReverseApiDeviceIndex(); } if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { - settings.m_reverseAPIChannelIndex = response.gsetFreqScannerSettings()->getReverseApiChannelIndex(); - } - if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) { - settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.gsetFreqScannerSettings()->getScopeConfig()); + settings.m_reverseAPIChannelIndex = response.getFreqScannerSettings()->getReverseApiChannelIndex(); } if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { - settings.m_channelMarker->updateFrom(channelSettingsKeys, response.gsetFreqScannerSettings()->getChannelMarker()); + settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getFreqScannerSettings()->getChannelMarker()); } if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) { - settings.m_rollupState->updateFrom(channelSettingsKeys, response.gsetFreqScannerSettings()->getRollupState()); - }*/ + settings.m_rollupState->updateFrom(channelSettingsKeys, response.getFreqScannerSettings()->getRollupState()); + } } void FreqScanner::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const FreqScannerSettings& settings) { - /*response.gsetFreqScannerSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); - response.gsetFreqScannerSettings()->setRfBandwidth(settings.m_channelBandwidth); - response.gsetFreqScannerSettings()->setAudioMute(settings.m_audioMute); - response.gsetFreqScannerSettings()->setThreshold(settings.m_threshold); - response.gsetFreqScannerSettings()->setLogFilename(new QString(settings.m_logFilename)); - response.gsetFreqScannerSettings()->setLogEnabled(settings.m_logEnabled); + response.getFreqScannerSettings()->setChannelFrequencyOffset(settings.m_channelFrequencyOffset); + response.getFreqScannerSettings()->setChannelBandwidth(settings.m_channelBandwidth); + response.getFreqScannerSettings()->setThreshold(settings.m_threshold); - response.gsetFreqScannerSettings()->setRgbColor(settings.m_rgbColor); - if (response.gsetFreqScannerSettings()->getTitle()) { - *response.gsetFreqScannerSettings()->getTitle() = settings.m_title; + response.getFreqScannerSettings()->setRgbColor(settings.m_rgbColor); + if (response.getFreqScannerSettings()->getTitle()) { + *response.getFreqScannerSettings()->getTitle() = settings.m_title; } else { - response.gsetFreqScannerSettings()->setTitle(new QString(settings.m_title)); + response.getFreqScannerSettings()->setTitle(new QString(settings.m_title)); } - response.gsetFreqScannerSettings()->setStreamIndex(settings.m_streamIndex); - response.gsetFreqScannerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + response.getFreqScannerSettings()->setStreamIndex(settings.m_streamIndex); + response.getFreqScannerSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); - if (response.gsetFreqScannerSettings()->getReverseApiAddress()) { - *response.gsetFreqScannerSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + if (response.getFreqScannerSettings()->getReverseApiAddress()) { + *response.getFreqScannerSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; } else { - response.gsetFreqScannerSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + response.getFreqScannerSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); } - response.gsetFreqScannerSettings()->setReverseApiPort(settings.m_reverseAPIPort); - response.gsetFreqScannerSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); - response.gsetFreqScannerSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + response.getFreqScannerSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getFreqScannerSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getFreqScannerSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); - if (settings.m_scopeGUI) - { - if (response.gsetFreqScannerSettings()->getScopeConfig()) - { - settings.m_scopeGUI->formatTo(response.gsetFreqScannerSettings()->getScopeConfig()); - } - else - { - SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope(); - settings.m_scopeGUI->formatTo(swgGLScope); - response.gsetFreqScannerSettings()->setScopeConfig(swgGLScope); - } - } if (settings.m_channelMarker) { - if (response.gsetFreqScannerSettings()->getChannelMarker()) + if (response.getFreqScannerSettings()->getChannelMarker()) { - settings.m_channelMarker->formatTo(response.gsetFreqScannerSettings()->getChannelMarker()); + settings.m_channelMarker->formatTo(response.getFreqScannerSettings()->getChannelMarker()); } else { SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); settings.m_channelMarker->formatTo(swgChannelMarker); - response.gsetFreqScannerSettings()->setChannelMarker(swgChannelMarker); + response.getFreqScannerSettings()->setChannelMarker(swgChannelMarker); } } if (settings.m_rollupState) { - if (response.gsetFreqScannerSettings()->getRollupState()) + if (response.getFreqScannerSettings()->getRollupState()) { - settings.m_rollupState->formatTo(response.gsetFreqScannerSettings()->getRollupState()); + settings.m_rollupState->formatTo(response.getFreqScannerSettings()->getRollupState()); } else { SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); settings.m_rollupState->formatTo(swgRollupState); - response.gsetFreqScannerSettings()->setRollupState(swgRollupState); + response.getFreqScannerSettings()->setRollupState(swgRollupState); } - }*/ + } } void FreqScanner::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) { - /*response.gsetFreqScannerReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.gsetFreqScannerReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate());*/ + response.getFreqScannerReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate()); } void FreqScanner::webapiReverseSendSettings(const QStringList& channelSettingsKeys, const FreqScannerSettings& settings, bool force) @@ -860,33 +831,24 @@ void FreqScanner::webapiFormatChannelSettings( bool force ) { - /*swgChannelSettings->setDirection(0); // Single sink (Rx) + swgChannelSettings->setDirection(0); // Single sink (Rx) swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); swgChannelSettings->setChannelType(new QString("FreqScanner")); - swgChannelSettings->ssetFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); - SWGSDRangel::SWGFreqScannerSettings *swgFreqScannerSettings = swgChannelSettings->gsetFreqScannerSettings(); + swgChannelSettings->setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + SWGSDRangel::SWGFreqScannerSettings *swgFreqScannerSettings = swgChannelSettings->getFreqScannerSettings(); // transfer data that has been modified. When force is on transfer all data except reverse API data - if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { - swgFreqScannerSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + if (channelSettingsKeys.contains("channelFrequencyOffset") || force) { + swgFreqScannerSettings->setChannelFrequencyOffset(settings.m_channelFrequencyOffset); } - if (channelSettingsKeys.contains("rfBandwidth") || force) { - swgFreqScannerSettings->setRfBandwidth(settings.m_channelBandwidth); - } - if (channelSettingsKeys.contains("audioMute") || force) { - swgFreqScannerSettings->setAudioMute(settings.m_audioMute); + if (channelSettingsKeys.contains("channelBandwidth") || force) { + swgFreqScannerSettings->setChannelBandwidth(settings.m_channelBandwidth); } if (channelSettingsKeys.contains("threshold") || force) { swgFreqScannerSettings->setThreshold(settings.m_threshold); } - if (channelSettingsKeys.contains("logFilename") || force) { - swgFreqScannerSettings->setLogFilename(new QString(settings.m_logFilename)); - } - if (channelSettingsKeys.contains("logEnabled") || force) { - swgFreqScannerSettings->setLogEnabled(settings.m_logEnabled); - } if (channelSettingsKeys.contains("rgbColor") || force) { swgFreqScannerSettings->setRgbColor(settings.m_rgbColor); } @@ -897,13 +859,6 @@ void FreqScanner::webapiFormatChannelSettings( swgFreqScannerSettings->setStreamIndex(settings.m_streamIndex); } - if (settings.m_scopeGUI && (channelSettingsKeys.contains("scopeConfig") || force)) - { - SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope(); - settings.m_scopeGUI->formatTo(swgGLScope); - swgFreqScannerSettings->setScopeConfig(swgGLScope); - } - if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force)) { SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); @@ -916,7 +871,7 @@ void FreqScanner::webapiFormatChannelSettings( SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); settings.m_rollupState->formatTo(swgRollupState); swgFreqScannerSettings->setRollupState(swgRollupState); - }*/ + } } void FreqScanner::networkManagerFinished(QNetworkReply *reply) @@ -956,7 +911,6 @@ void FreqScanner::handleIndexInDeviceSetChanged(int index) void FreqScanner::scanAvailableChannels() { MainCore* mainCore = MainCore::instance(); - MessagePipes& messagePipes = mainCore->getMessagePipes(); std::vector& deviceSets = mainCore->getDeviceSets(); m_availableChannels.clear(); @@ -971,7 +925,7 @@ void FreqScanner::scanAvailableChannels() ChannelAPI* channel = deviceSet->getChannelAt(chi); FreqScannerSettings::AvailableChannel availableChannel = - FreqScannerSettings::AvailableChannel{ channel->getDeviceSetIndex(), channel->getIndexInDeviceSet(), channel }; + FreqScannerSettings::AvailableChannel{ channel->getDeviceSetIndex(), channel->getIndexInDeviceSet()}; m_availableChannels[channel] = availableChannel; } } @@ -991,7 +945,7 @@ void FreqScanner::handleChannelAdded(int deviceSetIndex, ChannelAPI* channel) if (deviceSourceEngine) { FreqScannerSettings::AvailableChannel availableChannel = - FreqScannerSettings::AvailableChannel{ deviceSetIndex, channel->getIndexInDeviceSet(), channel }; + FreqScannerSettings::AvailableChannel{ deviceSetIndex, channel->getIndexInDeviceSet()}; m_availableChannels[channel] = availableChannel; } diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 50f46a0b8..555bec411 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -185,12 +185,12 @@ bool FreqScannerGUI::handleMessage(const Message& message) QList items = ui->table->findItems(QString::number(freq), Qt::MatchExactly); for (auto item : items) { int row = item->row(); - QTableWidgetItem* item = ui->table->item(row, COL_POWER); - item->setData(Qt::DisplayRole, results[i].m_power); + QTableWidgetItem* powerItem = ui->table->item(row, COL_POWER); + powerItem->setData(Qt::DisplayRole, results[i].m_power); bool active = results[i].m_power >= m_settings.m_threshold; if (active) { - item->setBackground(Qt::darkGreen); + powerItem->setBackground(Qt::darkGreen); QTableWidgetItem* activeCountItem = ui->table->item(row, COL_ACTIVE_COUNT); activeCountItem->setData(Qt::DisplayRole, activeCountItem->data(Qt::DisplayRole).toInt() + 1); } @@ -736,7 +736,7 @@ void FreqScannerGUI::updateAnnotation(int row) { if ((marker.m_startFrequency <= frequency) && (frequency < marker.m_startFrequency + marker.m_bandwidth)) { - if (marker.m_bandwidth == m_settings.m_channelBandwidth) { + if (marker.m_bandwidth == (unsigned)m_settings.m_channelBandwidth) { // Exact match annotationItem->setText(marker.m_text); return; diff --git a/plugins/channelrx/freqscanner/freqscannersettings.h b/plugins/channelrx/freqscanner/freqscannersettings.h index f6dbd86e6..646c28c01 100644 --- a/plugins/channelrx/freqscanner/freqscannersettings.h +++ b/plugins/channelrx/freqscanner/freqscannersettings.h @@ -33,7 +33,6 @@ struct FreqScannerSettings { int m_deviceSetIndex; int m_channelIndex; - ChannelAPI* m_channelAPI; AvailableChannel() = default; AvailableChannel(const AvailableChannel&) = default; diff --git a/plugins/channelrx/freqscanner/freqscannersink.cpp b/plugins/channelrx/freqscanner/freqscannersink.cpp index bc3625bed..01d8ed203 100644 --- a/plugins/channelrx/freqscanner/freqscannersink.cpp +++ b/plugins/channelrx/freqscanner/freqscannersink.cpp @@ -36,7 +36,10 @@ FreqScannerSink::FreqScannerSink(FreqScanner *ilsDemod) : m_messageQueueToChannel(nullptr), m_fftSequence(-1), m_fft(nullptr), - m_fftCounter(0) + m_fftCounter(0), + m_fftSize(1024), + m_binsPerChannel(16), + m_averageCount(0) { applySettings(m_settings, QStringList(), true); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, 16, 4, true); diff --git a/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp b/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp index 37dcddef6..7d2302f26 100644 --- a/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp +++ b/plugins/channelrx/freqscanner/freqscannerwebapiadapter.cpp @@ -31,9 +31,9 @@ int FreqScannerWebAPIAdapter::webapiSettingsGet( QString& errorMessage) { (void) errorMessage; - /*response.ssetFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); - response.gsetFreqScannerSettings()->init(); - FreqScanner::webapiFormatChannelSettings(response, m_settings);*/ + response.setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + response.getFreqScannerSettings()->init(); + FreqScanner::webapiFormatChannelSettings(response, m_settings); return 200; } diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 34441fe4e..b14322f89 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4525,8 +4525,8 @@ bool WebAPIRequestMapper::getChannelSettings( } else if (channelSettingsKey == "FreqScannerSettings") { - //channelSettings->setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); - //channelSettings->getFreqScannerSettings()->fromJsonObject(settingsJsonObject); + channelSettings->setFreqScannerSettings(new SWGSDRangel::SWGFreqScannerSettings()); + channelSettings->getFreqScannerSettings()->fromJsonObject(settingsJsonObject); } else if (channelSettingsKey == "FreqTrackerSettings") { @@ -5447,7 +5447,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setDatvModSettings(nullptr); channelSettings.setDabDemodSettings(nullptr); channelSettings.setDsdDemodSettings(nullptr); - //channelSettings.setFreqScannerSettings(nullptr); + channelSettings.setFreqScannerSettings(nullptr); channelSettings.setFreqTrackerSettings(nullptr); channelSettings.setHeatMapSettings(nullptr); channelSettings.setIeee802154ModSettings(nullptr); @@ -5491,7 +5491,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setBfmDemodReport(nullptr); channelReport.setDatvModReport(nullptr); channelReport.setDsdDemodReport(nullptr); - //channelReport.setFreqScannerReport(nullptr); + channelReport.setFreqScannerReport(nullptr); channelReport.setFreqTrackerReport(nullptr); channelReport.setHeatMapReport(nullptr); channelReport.setIlsDemodReport(nullptr); diff --git a/swagger/sdrangel/api/swagger/include/FreqScanner.yaml b/swagger/sdrangel/api/swagger/include/FreqScanner.yaml index 82d69a185..34b669e46 100644 --- a/swagger/sdrangel/api/swagger/include/FreqScanner.yaml +++ b/swagger/sdrangel/api/swagger/include/FreqScanner.yaml @@ -1,5 +1,5 @@ -FreqTrackerSettings: - description: FreqTracker +FreqScannerSettings: + description: FreqScanner properties: channelBandwidth: description: channel RF bandwidth in Hz diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index 15761e378..9f7623373 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -70,6 +70,8 @@ SWGChannelReport::SWGChannelReport() { m_free_dv_demod_report_isSet = false; free_dv_mod_report = nullptr; m_free_dv_mod_report_isSet = false; + freq_scanner_report = nullptr; + m_freq_scanner_report_isSet = false; freq_tracker_report = nullptr; m_freq_tracker_report_isSet = false; ft8_demod_report = nullptr; @@ -176,6 +178,8 @@ SWGChannelReport::init() { m_free_dv_demod_report_isSet = false; free_dv_mod_report = new SWGFreeDVModReport(); m_free_dv_mod_report_isSet = false; + freq_scanner_report = new SWGFreqScannerReport(); + m_freq_scanner_report_isSet = false; freq_tracker_report = new SWGFreqTrackerReport(); m_freq_tracker_report_isSet = false; ft8_demod_report = new SWGFT8DemodReport(); @@ -297,6 +301,9 @@ SWGChannelReport::cleanup() { if(free_dv_mod_report != nullptr) { delete free_dv_mod_report; } + if(freq_scanner_report != nullptr) { + delete freq_scanner_report; + } if(freq_tracker_report != nullptr) { delete freq_tracker_report; } @@ -436,6 +443,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&free_dv_mod_report, pJson["FreeDVModReport"], "SWGFreeDVModReport", "SWGFreeDVModReport"); + ::SWGSDRangel::setValue(&freq_scanner_report, pJson["FreqScannerReport"], "SWGFreqScannerReport", "SWGFreqScannerReport"); + ::SWGSDRangel::setValue(&freq_tracker_report, pJson["FreqTrackerReport"], "SWGFreqTrackerReport", "SWGFreqTrackerReport"); ::SWGSDRangel::setValue(&ft8_demod_report, pJson["FT8DemodReport"], "SWGFT8DemodReport", "SWGFT8DemodReport"); @@ -571,6 +580,9 @@ SWGChannelReport::asJsonObject() { if((free_dv_mod_report != nullptr) && (free_dv_mod_report->isSet())){ toJsonValue(QString("FreeDVModReport"), free_dv_mod_report, obj, QString("SWGFreeDVModReport")); } + if((freq_scanner_report != nullptr) && (freq_scanner_report->isSet())){ + toJsonValue(QString("FreqScannerReport"), freq_scanner_report, obj, QString("SWGFreqScannerReport")); + } if((freq_tracker_report != nullptr) && (freq_tracker_report->isSet())){ toJsonValue(QString("FreqTrackerReport"), freq_tracker_report, obj, QString("SWGFreqTrackerReport")); } @@ -869,6 +881,16 @@ SWGChannelReport::setFreeDvModReport(SWGFreeDVModReport* free_dv_mod_report) { this->m_free_dv_mod_report_isSet = true; } +SWGFreqScannerReport* +SWGChannelReport::getFreqScannerReport() { + return freq_scanner_report; +} +void +SWGChannelReport::setFreqScannerReport(SWGFreqScannerReport* freq_scanner_report) { + this->freq_scanner_report = freq_scanner_report; + this->m_freq_scanner_report_isSet = true; +} + SWGFreqTrackerReport* SWGChannelReport::getFreqTrackerReport() { return freq_tracker_report; @@ -1217,6 +1239,9 @@ SWGChannelReport::isSet(){ if(free_dv_mod_report && free_dv_mod_report->isSet()){ isObjectUpdated = true; break; } + if(freq_scanner_report && freq_scanner_report->isSet()){ + isObjectUpdated = true; break; + } if(freq_tracker_report && freq_tracker_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index 7e16b278f..213b76f87 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -41,6 +41,7 @@ #include "SWGFileSourceReport.h" #include "SWGFreeDVDemodReport.h" #include "SWGFreeDVModReport.h" +#include "SWGFreqScannerReport.h" #include "SWGFreqTrackerReport.h" #include "SWGHeatMapReport.h" #include "SWGIEEE_802_15_4_ModReport.h" @@ -152,6 +153,9 @@ public: SWGFreeDVModReport* getFreeDvModReport(); void setFreeDvModReport(SWGFreeDVModReport* free_dv_mod_report); + SWGFreqScannerReport* getFreqScannerReport(); + void setFreqScannerReport(SWGFreqScannerReport* freq_scanner_report); + SWGFreqTrackerReport* getFreqTrackerReport(); void setFreqTrackerReport(SWGFreqTrackerReport* freq_tracker_report); @@ -303,6 +307,9 @@ private: SWGFreeDVModReport* free_dv_mod_report; bool m_free_dv_mod_report_isSet; + SWGFreqScannerReport* freq_scanner_report; + bool m_freq_scanner_report_isSet; + SWGFreqTrackerReport* freq_tracker_report; bool m_freq_tracker_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index d65f5fc3f..845e9fa3e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -82,6 +82,8 @@ SWGChannelSettings::SWGChannelSettings() { m_free_dv_demod_settings_isSet = false; free_dv_mod_settings = nullptr; m_free_dv_mod_settings_isSet = false; + freq_scanner_settings = nullptr; + m_freq_scanner_settings_isSet = false; freq_tracker_settings = nullptr; m_freq_tracker_settings_isSet = false; ft8_demod_settings = nullptr; @@ -212,6 +214,8 @@ SWGChannelSettings::init() { m_free_dv_demod_settings_isSet = false; free_dv_mod_settings = new SWGFreeDVModSettings(); m_free_dv_mod_settings_isSet = false; + freq_scanner_settings = new SWGFreqScannerSettings(); + m_freq_scanner_settings_isSet = false; freq_tracker_settings = new SWGFreqTrackerSettings(); m_freq_tracker_settings_isSet = false; ft8_demod_settings = new SWGFT8DemodSettings(); @@ -359,6 +363,9 @@ SWGChannelSettings::cleanup() { if(free_dv_mod_settings != nullptr) { delete free_dv_mod_settings; } + if(freq_scanner_settings != nullptr) { + delete freq_scanner_settings; + } if(freq_tracker_settings != nullptr) { delete freq_tracker_settings; } @@ -528,6 +535,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&free_dv_mod_settings, pJson["FreeDVModSettings"], "SWGFreeDVModSettings", "SWGFreeDVModSettings"); + ::SWGSDRangel::setValue(&freq_scanner_settings, pJson["FreqScannerSettings"], "SWGFreqScannerSettings", "SWGFreqScannerSettings"); + ::SWGSDRangel::setValue(&freq_tracker_settings, pJson["FreqTrackerSettings"], "SWGFreqTrackerSettings", "SWGFreqTrackerSettings"); ::SWGSDRangel::setValue(&ft8_demod_settings, pJson["FT8DemodSettings"], "SWGFT8DemodSettings", "SWGFT8DemodSettings"); @@ -693,6 +702,9 @@ SWGChannelSettings::asJsonObject() { if((free_dv_mod_settings != nullptr) && (free_dv_mod_settings->isSet())){ toJsonValue(QString("FreeDVModSettings"), free_dv_mod_settings, obj, QString("SWGFreeDVModSettings")); } + if((freq_scanner_settings != nullptr) && (freq_scanner_settings->isSet())){ + toJsonValue(QString("FreqScannerSettings"), freq_scanner_settings, obj, QString("SWGFreqScannerSettings")); + } if((freq_tracker_settings != nullptr) && (freq_tracker_settings->isSet())){ toJsonValue(QString("FreqTrackerSettings"), freq_tracker_settings, obj, QString("SWGFreqTrackerSettings")); } @@ -1069,6 +1081,16 @@ SWGChannelSettings::setFreeDvModSettings(SWGFreeDVModSettings* free_dv_mod_setti this->m_free_dv_mod_settings_isSet = true; } +SWGFreqScannerSettings* +SWGChannelSettings::getFreqScannerSettings() { + return freq_scanner_settings; +} +void +SWGChannelSettings::setFreqScannerSettings(SWGFreqScannerSettings* freq_scanner_settings) { + this->freq_scanner_settings = freq_scanner_settings; + this->m_freq_scanner_settings_isSet = true; +} + SWGFreqTrackerSettings* SWGChannelSettings::getFreqTrackerSettings() { return freq_tracker_settings; @@ -1495,6 +1517,9 @@ SWGChannelSettings::isSet(){ if(free_dv_mod_settings && free_dv_mod_settings->isSet()){ isObjectUpdated = true; break; } + if(freq_scanner_settings && freq_scanner_settings->isSet()){ + isObjectUpdated = true; break; + } if(freq_tracker_settings && freq_tracker_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index 9f652b0dd..6749bfff8 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -46,6 +46,7 @@ #include "SWGFileSourceSettings.h" #include "SWGFreeDVDemodSettings.h" #include "SWGFreeDVModSettings.h" +#include "SWGFreqScannerSettings.h" #include "SWGFreqTrackerSettings.h" #include "SWGHeatMapSettings.h" #include "SWGIEEE_802_15_4_ModSettings.h" @@ -180,6 +181,9 @@ public: SWGFreeDVModSettings* getFreeDvModSettings(); void setFreeDvModSettings(SWGFreeDVModSettings* free_dv_mod_settings); + SWGFreqScannerSettings* getFreqScannerSettings(); + void setFreqScannerSettings(SWGFreqScannerSettings* freq_scanner_settings); + SWGFreqTrackerSettings* getFreqTrackerSettings(); void setFreqTrackerSettings(SWGFreqTrackerSettings* freq_tracker_settings); @@ -367,6 +371,9 @@ private: SWGFreeDVModSettings* free_dv_mod_settings; bool m_free_dv_mod_settings_isSet; + SWGFreqScannerSettings* freq_scanner_settings; + bool m_freq_scanner_settings_isSet; + SWGFreqTrackerSettings* freq_tracker_settings; bool m_freq_tracker_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.cpp b/swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.cpp new file mode 100644 index 000000000..13c29211d --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.cpp @@ -0,0 +1,108 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGFreqScannerReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGFreqScannerReport::SWGFreqScannerReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGFreqScannerReport::SWGFreqScannerReport() { + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +SWGFreqScannerReport::~SWGFreqScannerReport() { + this->cleanup(); +} + +void +SWGFreqScannerReport::init() { + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +void +SWGFreqScannerReport::cleanup() { + +} + +SWGFreqScannerReport* +SWGFreqScannerReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGFreqScannerReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", ""); + +} + +QString +SWGFreqScannerReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGFreqScannerReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_sample_rate_isSet){ + obj->insert("channelSampleRate", QJsonValue(channel_sample_rate)); + } + + return obj; +} + +qint32 +SWGFreqScannerReport::getChannelSampleRate() { + return channel_sample_rate; +} +void +SWGFreqScannerReport::setChannelSampleRate(qint32 channel_sample_rate) { + this->channel_sample_rate = channel_sample_rate; + this->m_channel_sample_rate_isSet = true; +} + + +bool +SWGFreqScannerReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_sample_rate_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.h b/swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.h new file mode 100644 index 000000000..2c669ecff --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGFreqScannerReport.h @@ -0,0 +1,58 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGFreqScannerReport.h + * + * FreqScanner + */ + +#ifndef SWGFreqScannerReport_H_ +#define SWGFreqScannerReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGFreqScannerReport: public SWGObject { +public: + SWGFreqScannerReport(); + SWGFreqScannerReport(QString* json); + virtual ~SWGFreqScannerReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGFreqScannerReport* fromJson(QString &jsonString) override; + + qint32 getChannelSampleRate(); + void setChannelSampleRate(qint32 channel_sample_rate); + + + virtual bool isSet() override; + +private: + qint32 channel_sample_rate; + bool m_channel_sample_rate_isSet; + +}; + +} + +#endif /* SWGFreqScannerReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp new file mode 100644 index 000000000..f850678c2 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp @@ -0,0 +1,636 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGFreqScannerSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGFreqScannerSettings::SWGFreqScannerSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGFreqScannerSettings::SWGFreqScannerSettings() { + channel_bandwidth = 0; + m_channel_bandwidth_isSet = false; + channel_frequency_offset = 0; + m_channel_frequency_offset_isSet = false; + threshold = 0.0f; + m_threshold_isSet = false; + m_frequencies = new QList(); + m_m_frequencies_isSet = false; + m_enabled = new QList(); + m_m_enabled_isSet = false; + m_notes = nullptr; + m_m_notes_isSet = false; + channel = nullptr; + m_channel_isSet = false; + scan_time = 0.0f; + m_scan_time_isSet = false; + retransmit_time = 0.0f; + m_retransmit_time_isSet = false; + tune_time = 0.0f; + m_tune_time_isSet = false; + priority = 0; + m_priority_isSet = false; + measurement = 0; + m_measurement_isSet = false; + mode = 0; + m_mode_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = nullptr; + m_title_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; + channel_marker = nullptr; + m_channel_marker_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; +} + +SWGFreqScannerSettings::~SWGFreqScannerSettings() { + this->cleanup(); +} + +void +SWGFreqScannerSettings::init() { + channel_bandwidth = 0; + m_channel_bandwidth_isSet = false; + channel_frequency_offset = 0; + m_channel_frequency_offset_isSet = false; + threshold = 0.0f; + m_threshold_isSet = false; + m_frequencies = new QList(); + m_m_frequencies_isSet = false; + m_enabled = new QList(); + m_m_enabled_isSet = false; + m_notes = new QList(); + m_m_notes_isSet = false; + channel = new QString(""); + m_channel_isSet = false; + scan_time = 0.0f; + m_scan_time_isSet = false; + retransmit_time = 0.0f; + m_retransmit_time_isSet = false; + tune_time = 0.0f; + m_tune_time_isSet = false; + priority = 0; + m_priority_isSet = false; + measurement = 0; + m_measurement_isSet = false; + mode = 0; + m_mode_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = new QString(""); + m_title_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; + channel_marker = new SWGChannelMarker(); + m_channel_marker_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; +} + +void +SWGFreqScannerSettings::cleanup() { + + + + + + if(m_notes != nullptr) { + auto arr = m_notes; + for(auto o: *arr) { + delete o; + } + delete m_notes; + } + if(channel != nullptr) { + delete channel; + } + + + + + + + + if(title != nullptr) { + delete title; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + if(channel_marker != nullptr) { + delete channel_marker; + } + if(rollup_state != nullptr) { + delete rollup_state; + } +} + +SWGFreqScannerSettings* +SWGFreqScannerSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGFreqScannerSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_bandwidth, pJson["channelBandwidth"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_frequency_offset, pJson["channelFrequencyOffset"], "qint32", ""); + + ::SWGSDRangel::setValue(&threshold, pJson["threshold"], "float", ""); + + + ::SWGSDRangel::setValue(&m_frequencies, pJson["m_frequencies"], "QList", "qint64"); + + ::SWGSDRangel::setValue(&m_enabled, pJson["m_enabled"], "QList", "qint32"); + + ::SWGSDRangel::setValue(&m_notes, pJson["m_notes"], "QList", "QString"); + ::SWGSDRangel::setValue(&channel, pJson["channel"], "QString", "QString"); + + ::SWGSDRangel::setValue(&scan_time, pJson["scanTime"], "float", ""); + + ::SWGSDRangel::setValue(&retransmit_time, pJson["retransmitTime"], "float", ""); + + ::SWGSDRangel::setValue(&tune_time, pJson["tuneTime"], "float", ""); + + ::SWGSDRangel::setValue(&priority, pJson["priority"], "qint32", ""); + + ::SWGSDRangel::setValue(&measurement, pJson["measurement"], "qint32", ""); + + ::SWGSDRangel::setValue(&mode, pJson["mode"], "qint32", ""); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + +} + +QString +SWGFreqScannerSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGFreqScannerSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_bandwidth_isSet){ + obj->insert("channelBandwidth", QJsonValue(channel_bandwidth)); + } + if(m_channel_frequency_offset_isSet){ + obj->insert("channelFrequencyOffset", QJsonValue(channel_frequency_offset)); + } + if(m_threshold_isSet){ + obj->insert("threshold", QJsonValue(threshold)); + } + if(m_frequencies && m_frequencies->size() > 0){ + toJsonArray((QList*)m_frequencies, obj, "m_frequencies", ""); + } + if(m_enabled && m_enabled->size() > 0){ + toJsonArray((QList*)m_enabled, obj, "m_enabled", ""); + } + if(m_notes && m_notes->size() > 0){ + toJsonArray((QList*)m_notes, obj, "m_notes", "QString"); + } + if(channel != nullptr && *channel != QString("")){ + toJsonValue(QString("channel"), channel, obj, QString("QString")); + } + if(m_scan_time_isSet){ + obj->insert("scanTime", QJsonValue(scan_time)); + } + if(m_retransmit_time_isSet){ + obj->insert("retransmitTime", QJsonValue(retransmit_time)); + } + if(m_tune_time_isSet){ + obj->insert("tuneTime", QJsonValue(tune_time)); + } + if(m_priority_isSet){ + obj->insert("priority", QJsonValue(priority)); + } + if(m_measurement_isSet){ + obj->insert("measurement", QJsonValue(measurement)); + } + if(m_mode_isSet){ + obj->insert("mode", QJsonValue(mode)); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_stream_index_isSet){ + obj->insert("streamIndex", QJsonValue(stream_index)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } + if((channel_marker != nullptr) && (channel_marker->isSet())){ + toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } + + return obj; +} + +qint32 +SWGFreqScannerSettings::getChannelBandwidth() { + return channel_bandwidth; +} +void +SWGFreqScannerSettings::setChannelBandwidth(qint32 channel_bandwidth) { + this->channel_bandwidth = channel_bandwidth; + this->m_channel_bandwidth_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getChannelFrequencyOffset() { + return channel_frequency_offset; +} +void +SWGFreqScannerSettings::setChannelFrequencyOffset(qint32 channel_frequency_offset) { + this->channel_frequency_offset = channel_frequency_offset; + this->m_channel_frequency_offset_isSet = true; +} + +float +SWGFreqScannerSettings::getThreshold() { + return threshold; +} +void +SWGFreqScannerSettings::setThreshold(float threshold) { + this->threshold = threshold; + this->m_threshold_isSet = true; +} + +QList* +SWGFreqScannerSettings::getMFrequencies() { + return m_frequencies; +} +void +SWGFreqScannerSettings::setMFrequencies(QList* m_frequencies) { + this->m_frequencies = m_frequencies; + this->m_m_frequencies_isSet = true; +} + +QList* +SWGFreqScannerSettings::getMEnabled() { + return m_enabled; +} +void +SWGFreqScannerSettings::setMEnabled(QList* m_enabled) { + this->m_enabled = m_enabled; + this->m_m_enabled_isSet = true; +} + +QList* +SWGFreqScannerSettings::getMNotes() { + return m_notes; +} +void +SWGFreqScannerSettings::setMNotes(QList* m_notes) { + this->m_notes = m_notes; + this->m_m_notes_isSet = true; +} + +QString* +SWGFreqScannerSettings::getChannel() { + return channel; +} +void +SWGFreqScannerSettings::setChannel(QString* channel) { + this->channel = channel; + this->m_channel_isSet = true; +} + +float +SWGFreqScannerSettings::getScanTime() { + return scan_time; +} +void +SWGFreqScannerSettings::setScanTime(float scan_time) { + this->scan_time = scan_time; + this->m_scan_time_isSet = true; +} + +float +SWGFreqScannerSettings::getRetransmitTime() { + return retransmit_time; +} +void +SWGFreqScannerSettings::setRetransmitTime(float retransmit_time) { + this->retransmit_time = retransmit_time; + this->m_retransmit_time_isSet = true; +} + +float +SWGFreqScannerSettings::getTuneTime() { + return tune_time; +} +void +SWGFreqScannerSettings::setTuneTime(float tune_time) { + this->tune_time = tune_time; + this->m_tune_time_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getPriority() { + return priority; +} +void +SWGFreqScannerSettings::setPriority(qint32 priority) { + this->priority = priority; + this->m_priority_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getMeasurement() { + return measurement; +} +void +SWGFreqScannerSettings::setMeasurement(qint32 measurement) { + this->measurement = measurement; + this->m_measurement_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getMode() { + return mode; +} +void +SWGFreqScannerSettings::setMode(qint32 mode) { + this->mode = mode; + this->m_mode_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getRgbColor() { + return rgb_color; +} +void +SWGFreqScannerSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGFreqScannerSettings::getTitle() { + return title; +} +void +SWGFreqScannerSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getStreamIndex() { + return stream_index; +} +void +SWGFreqScannerSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGFreqScannerSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGFreqScannerSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGFreqScannerSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGFreqScannerSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGFreqScannerSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGFreqScannerSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGFreqScannerSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + +SWGChannelMarker* +SWGFreqScannerSettings::getChannelMarker() { + return channel_marker; +} +void +SWGFreqScannerSettings::setChannelMarker(SWGChannelMarker* channel_marker) { + this->channel_marker = channel_marker; + this->m_channel_marker_isSet = true; +} + +SWGRollupState* +SWGFreqScannerSettings::getRollupState() { + return rollup_state; +} +void +SWGFreqScannerSettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + + +bool +SWGFreqScannerSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_bandwidth_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_frequency_offset_isSet){ + isObjectUpdated = true; break; + } + if(m_threshold_isSet){ + isObjectUpdated = true; break; + } + if(m_m_frequencies_isSet){ + isObjectUpdated = true; break; + } + if(m_frequencies && (m_frequencies->size() > 0)){ + isObjectUpdated = true; break; + } + if(m_m_enabled_isSet){ + isObjectUpdated = true; break; + } + if(m_enabled && (m_enabled->size() > 0)){ + isObjectUpdated = true; break; + } + if(m_notes && (m_notes->size() > 0)){ + isObjectUpdated = true; break; + } + if(channel && *channel != QString("")){ + isObjectUpdated = true; break; + } + if(m_scan_time_isSet){ + isObjectUpdated = true; break; + } + if(m_retransmit_time_isSet){ + isObjectUpdated = true; break; + } + if(m_tune_time_isSet){ + isObjectUpdated = true; break; + } + if(m_priority_isSet){ + isObjectUpdated = true; break; + } + if(m_measurement_isSet){ + isObjectUpdated = true; break; + } + if(m_mode_isSet){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(m_stream_index_isSet){ + isObjectUpdated = true; break; + } + if(m_use_reverse_api_isSet){ + isObjectUpdated = true; break; + } + if(reverse_api_address && *reverse_api_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_reverse_api_port_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_device_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_channel_index_isSet){ + isObjectUpdated = true; break; + } + if(channel_marker && channel_marker->isSet()){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h new file mode 100644 index 000000000..a166b027b --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h @@ -0,0 +1,194 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGFreqScannerSettings.h + * + * FreqScanner + */ + +#ifndef SWGFreqScannerSettings_H_ +#define SWGFreqScannerSettings_H_ + +#include + + +#include "SWGChannelMarker.h" +#include "SWGRollupState.h" +#include +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGFreqScannerSettings: public SWGObject { +public: + SWGFreqScannerSettings(); + SWGFreqScannerSettings(QString* json); + virtual ~SWGFreqScannerSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGFreqScannerSettings* fromJson(QString &jsonString) override; + + qint32 getChannelBandwidth(); + void setChannelBandwidth(qint32 channel_bandwidth); + + qint32 getChannelFrequencyOffset(); + void setChannelFrequencyOffset(qint32 channel_frequency_offset); + + float getThreshold(); + void setThreshold(float threshold); + + QList* getMFrequencies(); + void setMFrequencies(QList* m_frequencies); + + QList* getMEnabled(); + void setMEnabled(QList* m_enabled); + + QList* getMNotes(); + void setMNotes(QList* m_notes); + + QString* getChannel(); + void setChannel(QString* channel); + + float getScanTime(); + void setScanTime(float scan_time); + + float getRetransmitTime(); + void setRetransmitTime(float retransmit_time); + + float getTuneTime(); + void setTuneTime(float tune_time); + + qint32 getPriority(); + void setPriority(qint32 priority); + + qint32 getMeasurement(); + void setMeasurement(qint32 measurement); + + qint32 getMode(); + void setMode(qint32 mode); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getTitle(); + void setTitle(QString* title); + + qint32 getStreamIndex(); + void setStreamIndex(qint32 stream_index); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + + SWGChannelMarker* getChannelMarker(); + void setChannelMarker(SWGChannelMarker* channel_marker); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + + + virtual bool isSet() override; + +private: + qint32 channel_bandwidth; + bool m_channel_bandwidth_isSet; + + qint32 channel_frequency_offset; + bool m_channel_frequency_offset_isSet; + + float threshold; + bool m_threshold_isSet; + + QList* m_frequencies; + bool m_m_frequencies_isSet; + + QList* m_enabled; + bool m_m_enabled_isSet; + + QList* m_notes; + bool m_m_notes_isSet; + + QString* channel; + bool m_channel_isSet; + + float scan_time; + bool m_scan_time_isSet; + + float retransmit_time; + bool m_retransmit_time_isSet; + + float tune_time; + bool m_tune_time_isSet; + + qint32 priority; + bool m_priority_isSet; + + qint32 measurement; + bool m_measurement_isSet; + + qint32 mode; + bool m_mode_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* title; + bool m_title_isSet; + + qint32 stream_index; + bool m_stream_index_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + + SWGChannelMarker* channel_marker; + bool m_channel_marker_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + +}; + +} + +#endif /* SWGFreqScannerSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index d830fba9b..f2ea14a39 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -151,6 +151,8 @@ #include "SWGFreeDVDemodSettings.h" #include "SWGFreeDVModReport.h" #include "SWGFreeDVModSettings.h" +#include "SWGFreqScannerReport.h" +#include "SWGFreqScannerSettings.h" #include "SWGFreqTrackerReport.h" #include "SWGFreqTrackerSettings.h" #include "SWGFrequency.h" @@ -1055,6 +1057,16 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGFreqScannerReport").compare(type) == 0) { + SWGFreqScannerReport *obj = new SWGFreqScannerReport(); + obj->init(); + return obj; + } + if(QString("SWGFreqScannerSettings").compare(type) == 0) { + SWGFreqScannerSettings *obj = new SWGFreqScannerSettings(); + obj->init(); + return obj; + } if(QString("SWGFreqTrackerReport").compare(type) == 0) { SWGFreqTrackerReport *obj = new SWGFreqTrackerReport(); obj->init(); From 7fe7f2aa868ac19973cbf3cdcf24123b34b530bb Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 08:42:25 +0100 Subject: [PATCH 04/23] Update threading to latest approach --- plugins/channelrx/freqscanner/freqscanner.cpp | 108 ++++++++++++------ plugins/channelrx/freqscanner/freqscanner.h | 3 +- .../freqscanner/freqscanneraddrangedialog.cpp | 6 +- .../freqscanner/freqscanneraddrangedialog.h | 2 +- .../freqscanner/freqscannerbaseband.cpp | 55 ++++----- .../freqscanner/freqscannerbaseband.h | 6 +- .../channelrx/freqscanner/freqscannergui.cpp | 4 +- 7 files changed, 105 insertions(+), 79 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 38b36a106..42c00a2b6 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -63,6 +63,8 @@ const char * const FreqScanner::m_channelId = "FreqScanner"; FreqScanner::FreqScanner(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), m_deviceAPI(deviceAPI), + m_thread(nullptr), + m_basebandSink(nullptr), m_running(false), m_basebandSampleRate(0), m_scanDeviceSetIndex(-1), @@ -72,11 +74,6 @@ FreqScanner::FreqScanner(DeviceAPI *deviceAPI) : { setObjectName(m_channelId); - m_basebandSink = new FreqScannerBaseband(this); - m_basebandSink->setMessageQueueToChannel(getInputMessageQueue()); - m_basebandSink->setChannel(this); - m_basebandSink->moveToThread(&m_thread); - applySettings(m_settings, QStringList(), true); m_deviceAPI->addChannelSink(this); @@ -96,6 +93,8 @@ FreqScanner::FreqScanner(DeviceAPI *deviceAPI) : &FreqScanner::handleIndexInDeviceSetChanged ); + start(); + scanAvailableChannels(); QObject::connect( MainCore::instance(), @@ -126,11 +125,7 @@ FreqScanner::~FreqScanner() m_deviceAPI->removeChannelSinkAPI(this); m_deviceAPI->removeChannelSink(this); - if (m_basebandSink->isRunning()) { - stop(); - } - - delete m_basebandSink; + stop(); } void FreqScanner::setDeviceAPI(DeviceAPI *deviceAPI) @@ -161,16 +156,38 @@ void FreqScanner::feed(const SampleVector::const_iterator& begin, const SampleVe void FreqScanner::start() { + QMutexLocker m_lock(&m_mutex); + if (m_running) { return; } qDebug("FreqScanner::start"); + m_thread = new QThread(); + m_basebandSink = new FreqScannerBaseband(this); + m_basebandSink->setFifoLabel(QString("%1 [%2:%3]") + .arg(m_channelId) + .arg(m_deviceAPI->getDeviceSetIndex()) + .arg(getIndexInDeviceSet()) + ); + m_basebandSink->setMessageQueueToChannel(getInputMessageQueue()); + m_basebandSink->setChannel(this); + m_basebandSink->moveToThread(m_thread); - m_basebandSink->reset(); - m_basebandSink->startWork(); - m_thread.start(); - // FIXME: Threading!! Compare to SSB + QObject::connect( + m_thread, + &QThread::finished, + m_basebandSink, + &QObject::deleteLater + ); + QObject::connect( + m_thread, + &QThread::finished, + m_thread, + &QThread::deleteLater + ); + + m_thread->start(); DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency); m_basebandSink->getInputMessageQueue()->push(dspMsg); @@ -183,15 +200,16 @@ void FreqScanner::start() void FreqScanner::stop() { + QMutexLocker m_lock(&m_mutex); + if (!m_running) { return; } qDebug("FreqScanner::stop"); m_running = false; - m_basebandSink->stopWork(); - m_thread.quit(); - m_thread.wait(); + m_thread->exit(); + m_thread->wait(); } bool FreqScanner::handleMessage(const Message& cmd) @@ -314,17 +332,20 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< } qSort(frequencies); - // Calculate how many channels can be scanned in one go - int fftSize; - int binsPerChannel; - FreqScanner::calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); + if ((frequencies.size() > 0) && (m_settings.m_channelBandwidth > 0) && (m_basebandSampleRate > 0)) + { + // Calculate how many channels can be scanned in one go + int fftSize; + int binsPerChannel; + FreqScanner::calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); - // Align first frequency so we cover as many channels as possible, while avoiding DC bin - m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; - m_stepStopFrequency = frequencies.back(); + // Align first frequency so we cover as many channels as possible, while avoiding DC bin + m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; + m_stepStopFrequency = frequencies.back(); - qInfo() << "START_SCAN: Scanning from " << frequencies.front() << "to" << frequencies.back(); - initScan(); + qInfo() << "START_SCAN: Scanning from " << frequencies.front() << "to" << frequencies.back(); + initScan(); + } } break; @@ -335,19 +356,36 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< m_scanResults.append(results); } + // Calculate next center frequency bool complete = false; // Have all frequencies been scanned? + bool freqInRange = false; qint64 nextCenterFrequency = m_centerFrequency; + do + { + if (nextCenterFrequency + m_scannerSampleRate / 2 > m_stepStopFrequency) + { + nextCenterFrequency = m_stepStartFrequency; + complete = true; + } + else + { + nextCenterFrequency += m_scannerSampleRate; + complete = false; + } - if (m_stepStopFrequency < m_centerFrequency + m_scannerSampleRate / 2) - { - nextCenterFrequency = m_stepStartFrequency; - complete = true; - } - else - { - nextCenterFrequency = m_centerFrequency + m_scannerSampleRate; - complete = false; + // Are any frequencies in this new range? + for (int i = 0; i < m_settings.m_frequencies.size(); i++) + { + if (m_settings.m_enabled[i] + && (m_settings.m_frequencies[i] >= nextCenterFrequency - m_scannerSampleRate / 2) + && (m_settings.m_frequencies[i] < nextCenterFrequency + m_scannerSampleRate / 2)) + { + freqInRange = true; + break; + } + } } + while (!complete && !freqInRange); if (complete) { diff --git a/plugins/channelrx/freqscanner/freqscanner.h b/plugins/channelrx/freqscanner/freqscanner.h index ff294b161..85a956a79 100644 --- a/plugins/channelrx/freqscanner/freqscanner.h +++ b/plugins/channelrx/freqscanner/freqscanner.h @@ -372,8 +372,9 @@ public: private: DeviceAPI *m_deviceAPI; - QThread m_thread; + QThread *m_thread; FreqScannerBaseband* m_basebandSink; + QRecursiveMutex m_mutex; bool m_running; FreqScannerSettings m_settings; int m_basebandSampleRate; //!< stored from device message used when starting baseband sink diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp index afdd7e1e6..c08998641 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp @@ -18,7 +18,7 @@ #include "freqscanneraddrangedialog.h" #include "ui_freqscanneraddrangedialog.h" -FreqScannerAddRangeDialog::FreqScannerAddRangeDialog(QWidget* parent) : +FreqScannerAddRangeDialog::FreqScannerAddRangeDialog(int step, QWidget* parent) : QDialog(parent), ui(new Ui::FreqScannerAddRangeDialog) { @@ -32,6 +32,8 @@ FreqScannerAddRangeDialog::FreqScannerAddRangeDialog(QWidget* parent) : // Airband frequency range ui->start->setValue(118000000); ui->stop->setValue(137000000); + + ui->step->setCurrentText(QString::number(step)); } FreqScannerAddRangeDialog::~FreqScannerAddRangeDialog() @@ -43,6 +45,6 @@ void FreqScannerAddRangeDialog::accept() { m_start = ui->start->getValue(); m_stop = ui->stop->getValue(); - m_step = ui->step->currentText().toLongLong(); + m_step = ui->step->currentText().toInt(); QDialog::accept(); } diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h index 8bacafe7f..7cb25b076 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h @@ -27,7 +27,7 @@ namespace Ui { class FreqScannerAddRangeDialog : public QDialog { Q_OBJECT public: - explicit FreqScannerAddRangeDialog(QWidget* parent = nullptr); + explicit FreqScannerAddRangeDialog(int step, QWidget* parent = nullptr); ~FreqScannerAddRangeDialog(); qint64 m_start; diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp index 1516505f1..8ad6fe125 100644 --- a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp @@ -29,15 +29,25 @@ MESSAGE_CLASS_DEFINITION(FreqScannerBaseband::MsgConfigureFreqScannerBaseband, M FreqScannerBaseband::FreqScannerBaseband(FreqScanner *freqScanner) : m_sink(freqScanner), - m_messageQueueToGUI(nullptr), - m_running(false) + m_messageQueueToGUI(nullptr) { qDebug("FreqScannerBaseband::FreqScannerBaseband"); m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &FreqScannerBaseband::handleData, + Qt::QueuedConnection + ); + m_channelizer = new DownChannelizer(&m_sink); m_channelSampleRate = 0; m_scannerSampleRate = 0; + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); } FreqScannerBaseband::~FreqScannerBaseband() @@ -54,33 +64,6 @@ void FreqScannerBaseband::reset() m_channelSampleRate = 0; } -void FreqScannerBaseband::startWork() -{ - QMutexLocker mutexLocker(&m_mutex); - QObject::connect( - &m_sampleFifo, - &SampleSinkFifo::dataReady, - this, - &FreqScannerBaseband::handleData, - Qt::QueuedConnection - ); - connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); - m_running = true; -} - -void FreqScannerBaseband::stopWork() -{ - QMutexLocker mutexLocker(&m_mutex); - disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); - QObject::disconnect( - &m_sampleFifo, - &SampleSinkFifo::dataReady, - this, - &FreqScannerBaseband::handleData - ); - m_running = false; -} - void FreqScannerBaseband::setChannel(ChannelAPI *channel) { m_sink.setChannel(channel); @@ -110,7 +93,7 @@ void FreqScannerBaseband::handleData() } // second part of FIFO data (used when block wraps around) - if(part2begin != part2end) { + if (part2begin != part2end) { m_channelizer->feed(part2begin, part2end); } @@ -164,8 +147,12 @@ bool FreqScannerBaseband::handleMessage(const Message& cmd) void FreqScannerBaseband::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) { - if ((settings.m_channelBandwidth != m_settings.m_channelBandwidth) || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) { - calcScannerSampleRate(m_channelizer->getBasebandSampleRate(), settings.m_channelBandwidth, settings.m_inputFrequencyOffset); + if ((settings.m_channelBandwidth != m_settings.m_channelBandwidth) || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + { + int basebandSampleRate = m_channelizer->getBasebandSampleRate(); + if ((basebandSampleRate != 0) && (settings.m_channelBandwidth != 0)) { + calcScannerSampleRate(basebandSampleRate, settings.m_channelBandwidth, settings.m_inputFrequencyOffset); + } } m_sink.applySettings(settings, settingsKeys, force); @@ -185,7 +172,9 @@ int FreqScannerBaseband::getChannelSampleRate() const void FreqScannerBaseband::setBasebandSampleRate(int sampleRate) { m_channelizer->setBasebandSampleRate(sampleRate); - calcScannerSampleRate(sampleRate, m_settings.m_channelBandwidth, m_settings.m_inputFrequencyOffset); + if ((sampleRate != 0) && (m_settings.m_channelBandwidth != 0)) { + calcScannerSampleRate(sampleRate, m_settings.m_channelBandwidth, m_settings.m_inputFrequencyOffset); + } } void FreqScannerBaseband::calcScannerSampleRate(int basebandSampleRate, float rfBandwidth, int inputFrequencyOffset) diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.h b/plugins/channelrx/freqscanner/freqscannerbaseband.h index 739a2cf8d..f638d7de9 100644 --- a/plugins/channelrx/freqscanner/freqscannerbaseband.h +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.h @@ -62,11 +62,9 @@ public: { } }; - FreqScannerBaseband(FreqScanner *packetDemod); + FreqScannerBaseband(FreqScanner *freqScanner); ~FreqScannerBaseband(); void reset(); - void startWork(); - void stopWork(); void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); } @@ -74,7 +72,6 @@ public: void setBasebandSampleRate(int sampleRate); int getChannelSampleRate() const; void setChannel(ChannelAPI *channel); - bool isRunning() const { return m_running; } void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } private: @@ -86,7 +83,6 @@ private: MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication MessageQueue *m_messageQueueToGUI; FreqScannerSettings m_settings; - bool m_running; QRecursiveMutex m_mutex; bool handleMessage(const Message& cmd); diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 555bec411..71381c03c 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -411,7 +411,7 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->deltaFrequency->setValueRange(true, 7, 0, 9999999); ui->channelBandwidth->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); - ui->channelBandwidth->setValueRange(true, 7, 16, 9999999); + ui->channelBandwidth->setValueRange(true, 7, 0, 9999999); m_channelMarker.setColor(Qt::yellow); m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); @@ -606,7 +606,7 @@ void FreqScannerGUI::on_addSingle_clicked() void FreqScannerGUI::on_addRange_clicked() { - FreqScannerAddRangeDialog dialog(this); + FreqScannerAddRangeDialog dialog(m_settings.m_channelBandwidth, this); new DialogPositioner(&dialog, false); if (dialog.exec()) { From f3582b95f5891c291ea5032e5ed4f395432e26ba Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 08:42:47 +0100 Subject: [PATCH 05/23] Set background colour for start/stop button --- plugins/channelrx/noisefigure/noisefiguregui.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index 62161c1a4..9df17d599 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -667,6 +667,8 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->results->setItemDelegateForColumn(RESULTS_COL_ENR, new DecimalDelegate(2)); ui->results->setItemDelegateForColumn(RESULTS_COL_FLOOR, new DecimalDelegate(1)); + ui->startStop->setStyleSheet(QString("QToolButton{ background-color: blue; } QToolButton:checked{ background-color: green; }")); + displaySettings(); makeUIConnections(); applySettings(true); From e9066fe2a44fa8155de807e3ed1ec69d9e69bd6a Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 08:43:29 +0100 Subject: [PATCH 06/23] Use frame geo rather than widget size, so dialogs aren't positioned off screen on Windows. --- sdrgui/gui/dialogpositioner.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sdrgui/gui/dialogpositioner.cpp b/sdrgui/gui/dialogpositioner.cpp index 5482f30c0..7c9e4df30 100644 --- a/sdrgui/gui/dialogpositioner.cpp +++ b/sdrgui/gui/dialogpositioner.cpp @@ -79,18 +79,18 @@ void DialogPositioner::positionDialog(QWidget *dialog) // Position so fully on screen QRect desktop = dialog->screen()->availableGeometry(); - QSize size = dialog->size(); + QRect geometry = dialog->frameGeometry(); QPoint pos = dialog->pos(); bool move = false; - if (pos.x() + size.width() > desktop.width()) + if (pos.x() + geometry.width() > desktop.width()) { - pos.setX(desktop.width() - size.width()); + pos.setX(desktop.width() - geometry.width()); move = true; } - if (pos.y() + size.height() > desktop.height()) + if (pos.y() + geometry.height() > desktop.height()) { - pos.setY(desktop.height() - size.height()); + pos.setY(desktop.height() - geometry.height()); move = true; } if (move) { From b3d3ca43ca61425e0958ebb0a5e352df0402a1bb Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 09:38:15 +0100 Subject: [PATCH 07/23] Fix filtering of Freq Scanner from list of channels. --- plugins/channelrx/freqscanner/freqscanner.cpp | 13 +++---------- plugins/channelrx/freqscanner/freqscanner.h | 1 - plugins/channelrx/freqscanner/freqscannergui.cpp | 4 ++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 42c00a2b6..8c85f5587 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -243,16 +243,12 @@ bool FreqScanner::handleMessage(const Message& cmd) } else if (MsgStartScan::match(cmd)) { - qInfo() << "FreqScanner::handleMessage: StartScan"; - startScan(); return true; } else if (MsgStopScan::match(cmd)) { - qInfo() << "FreqScanner::handleMessage: StopScan"; - stopScan(); return true; @@ -295,12 +291,10 @@ void FreqScanner::initScan() if (m_centerFrequency != m_stepStartFrequency) { - qInfo() << "******************** Setting frequency to " << m_stepStartFrequency; if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), m_stepStartFrequency)) { - qWarning() << "Scanner failed to set frequency" << m_stepStartFrequency; + qWarning() << "Freq Scanner failed to set frequency" << m_stepStartFrequency; } m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); - qInfo() << "m_minFFTStartTime" << m_minFFTStartTime.toString("ss.z"); } m_scanResults.clear(); @@ -343,7 +337,6 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; m_stepStopFrequency = frequencies.back(); - qInfo() << "START_SCAN: Scanning from " << frequencies.front() << "to" << frequencies.back(); initScan(); } } @@ -476,7 +469,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< } } - //qInfo() << "Tuning to active freq:" << frequency << "m_centerFrequency" << m_centerFrequency << "nextCenterFrequency" << nextCenterFrequency << "offset: " << offset << "deviceset: R" << m_scanDeviceSetIndex << ":" << m_scanChannelIndex; + //qDebug() << "Tuning to active freq:" << frequency << "m_centerFrequency" << m_centerFrequency << "nextCenterFrequency" << nextCenterFrequency << "offset: " << offset << "deviceset: R" << m_scanDeviceSetIndex << ":" << m_scanChannelIndex; ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset); @@ -513,7 +506,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< { // For RTL SDR, setCenterFrequency takes ~50ms, which means tuneTime can be 0 if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), nextCenterFrequency)) { - qWarning() << "Scanner failed to set frequency" << nextCenterFrequency; + qWarning() << "Freq Scanner failed to set frequency" << nextCenterFrequency; } m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); } diff --git a/plugins/channelrx/freqscanner/freqscanner.h b/plugins/channelrx/freqscanner/freqscanner.h index 85a956a79..b4ab200f1 100644 --- a/plugins/channelrx/freqscanner/freqscanner.h +++ b/plugins/channelrx/freqscanner/freqscanner.h @@ -364,7 +364,6 @@ public: fftSize = pow(2.0, floor(log2(basebandSampleRate / binBW))); fftSize = std::min(maxFFTSize, fftSize); scannerSampleRate = binBW * fftSize; - qInfo() << "binsPerChannel:" << binsPerChannel << "fftSize:" << fftSize << "scannerSampleRate:" << scannerSampleRate; } static const char * const m_channelIdURI; diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 71381c03c..81146f8cb 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -209,8 +209,8 @@ void FreqScannerGUI::updateChannelsList(const QListgetDeviceSetIndex()) && (channel.m_channelIndex != m_freqScanner->getIndexInDeviceSet())) { QString name = QString("R%1:%2").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex); ui->channels->addItem(name); From 3a8a0f0bf07094631a1adc2ab0f2d9d10af413cf Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 10:52:21 +0100 Subject: [PATCH 08/23] Reduce debug. Add screenshot. --- doc/img/FreqScanner_plugin.png | Bin 0 -> 24596 bytes .../freqscanner/freqscannerbaseband.cpp | 3 +-- .../channelrx/freqscanner/freqscannergui.cpp | 8 ++++++++ .../freqscanner/freqscannersettings.cpp | 8 +++++--- 4 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 doc/img/FreqScanner_plugin.png diff --git a/doc/img/FreqScanner_plugin.png b/doc/img/FreqScanner_plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..6bc7574e67e1e85a51b45157cdbf8615faaafcb5 GIT binary patch literal 24596 zcmc%xbyQVr+dqoau*k)tLtxR;9RdQ27AZxL?rxA45LlEn2!fO}DkUH#(%s#i(jeVu z;@lb z6qq6)7<^KYmQ?r9-~Hk9j%X}-e_#Bpcwzr`Z~r|BF9DrRtP_Oy=Q%O*uiGq|iU8(c zjOH>Q@+4(!$-yC}dpNr~RontdYWAt}Ptj%)2D2<7o$xS}fZ<3`ylJvla!vT!Za z4((B2e7}Ee_hvN}aHn)QSiZB?b^G>a;Z6@z3PU#l*?mV$FSZQfk+%s0VnAjB-A`#S ziX_hkWAU2y#Qj7%)c ztECC7%48*=@4Y&B|Dutv^roA`xpx;CcMom8Ax2cwr+FNP;D3Alv1){Q!@mNhgmI|~ z{2W%{{8DzaqaD1l%;F9ge%1)|HniPX0mEZ6_r34k)dJQU_#-I0j&g8nM&ykJ6vk)ua+8`YM*M@Jc#5j{4=$F+SBHXkn{6Hp(xf#82jyU2 zwFSpUrWf}^uyUvbp?(1@GIv?M#CMa|A9zQoxV| zqo~n6g)!m--&@a(AZ79Q>!lsfCsqcyWfbGjJLsYJ4@SoPevTgof&KeMY1dZHH$AeC zIB2$d?W*P&UDBekNz$0P=%LcCs!!bZW*<^)N<^l3_c4jLC6^g)RYrM=?IhLMH6=WU zCm?5l2pBB~n4ebBXDexO#fu1aj8fPM&JRD;cIv20&(-YKm|G7%I=25}q1>qoE7N|J z)d&PK)};X>etL0iHueUO#r%bev&;GQtV$dl9GSyUZ#rFJHKFG|_d;4q;&ZHHvX^ot zWLUr_uI@SCfn;0ZmV!O5&4%AwdblM$I}McC!ezZztHJkXy)R{-X$w0pLY;1DBwyt{ zR?Ii}EYC*wzzgKZ1xYW&y84W6vKnYWP;vz4dWr_bY#35cY+0duuj>KMv>3^nr|TQoS7To>1g?K#YN)qr9`e_?Ag5^K|plO zRG`frF|n-Mn_u0Xotyf%oJsDu5$eik&LNLRyr=8&t*3sR&41@HT+k90?wXu>@7}4% zs=4QJvGa2Bb7;d8=9mLhNOPr(ne-jqS)Elyiv&!ABbCSdbO(2|o^JPnQ2gw1GroFz ziuC;;HuIwGCOTH+2=3OF{A%}!#>CCpL~yFjFEGHVM`z)W`yeK1- z?niiT!pKy3^A9aHvDd2HH5QcJyY17mE06JT3uBdb+jryy@Q&@N@Wma3el1G4b9szf z;&!ty&6YWR_nWdHc!HtBkNlmDOCIKoqlxq_scu)Rg-aHXI;~7Pn-cGwzO_8l76j76 zT@+E)H{CEE{D@p3y(d*-ak7`EmMX8>vsZt$w}$8)!fFyy)zkiKZDyz#d*3Wyh=7T* zqojKbGB`hfJ8e^vR_c%HwwWo>OdyXz9LJgQt$Tbr$RA!<<9T^9QPSRV7dy|e41dwM zU9v#)Wa$L^{G+k+fY7^{UH*vn2hyBjZreV%fubI@I=b4!Jz^^EM;brY6C-sdb|<2) z*x_2DUt}S1CaYhhOOtkN>CZ)}PQdLA0i_#)q$)(V)mE**+p;WktF$0sC1G??x*5;I7Gobi3>3$`+G^C9&&whz(y`mNDsWtrZ;ArGJLh znZJQ+z6XDcSbkTO{H@!U2AW~O<8phQ%UM6oJ6!eZ_F80qF}Kv=_WJwP%ZIHXAXPD* zMM%}KlkZ#l3@m;A2?4u|JPz%4aSuHr6(sm$vAA+(P{zf8$IB^-L6EqZ*{mQ0yi{G$ z*J$EmG=8>ykE6UN_#Dqn67+98W^^p2=VeS3<$Kxd$QvzK6QY|Q)TKYBE1z=2(;Z94 z9xcP65Maadz-+tAEn0G!C-;4l{(a)J9y^ZBM6u7wn5Kc>jq7!oi`6&D<}Xh|=TXZ1 zWy&?CqP?)Rel6n@u8m!1m&1Kd#mnrG#WksG34DhQgwlr+L*GY`?60y1CZ&w~0joRm zjGg|KFtGi^e?IFX?(v8(CE;x>$_!w32)a4nDl)=rNBi1NwJ-ovS6MT!`6A*emR+k8 zk6%6h5~Njm!ceEfY!q7G9NM7G_>mGFk1ApqFGd13O8dm4ykk00e>8ezBiPrp&-W4J zwyV-oHghg6y5092dqAX>2y2b_Hul>|i=RscX@yR6joGq5F?D{(XMa=$`NvIfYvII0 zMVXW)YjmZxn`tZ9<_XS=@)!+K>Fp(^OJeVDf)mHoqWbNOpDhde*8~oXIAAL$A>SRw zaDCu?2MIuzg62pFcn}!V^apX3+UPvsl)ZjI`>_^Y{iSu{7rHo?#uOgCY3{Y_%ZodQ z#fA*8k?iO_3A51#xi5YpFZig^|FWzs~N~6Z)A5UMD$-W?BZ5Z*faS)*N7lK z9=6X)9-HNHqUhi}k@XE^fP%Wjy|||ytqiT?r!Rake#tNy?T8<^$X75dEAG}bEm#ak z$V7|7zd=nAM*P7!WtvNI*G5SdJTS75miAdk0dYyQNM5byk%))`&ZL++8O2T_&Y+kO zF=;J&$m-pZiG1>+c!f@?S22Vt=D-8gMC&OOGFCCpflY7P78!4Wsljpg@bO&v{;@ZU zoPb!_vk(5ae#3|3&V+s43v3%3#ECj5L-s-i4|-QRr(F{%DD38LQs!|e1s)V$0jUG) z#sDkQLg@`tEX6Dlb`gbw{Hp`eQm@&gcd=e4<0SZ$PXGCiC-p}c)>jy#GoX|g6goJQQ$nuxa;5X_imxyHvF{sZU`y_YmY>GWN66j52 zM+EY>-T;`Y>T|Y_eRnYb(PB#!f4M+!ecX4houOh|9X^{B$L*wVMr!YL=&7pUv3ZVH z|HFgemK;-0I%jPjUs!dS334Po?0&U@Z3O{(IdX7p@|}Tncgtp})UL^_`LS8M`p6XJ zZoAIL$*j|Eq$cmxJF-H}4eY6&SK&==ilkQBRb#wNz!XHn>EQ(cpW<2J-WhntjQ#*Y0F2BfG;2I$zmlY5UA6XHTp3_2kT`JGqXNcXZ2U3cQK78jPThRE78xc?U@m?$)qm%q z#tNPEfSw5`2p+|jgSp*H8ELj>Ttde(#Q$*j-&m`Mqwg-9>!F2nH<{}Tc^F1B^4p-J z6ef6HYF8%v5q}q^@J*;WgZx z&N%!yNwhpYJ8N00-7ZeyHVNL50DIETcrA(IF6L7@%{xa+F?Lzir1Htav^m&aW5Fmj zK3AuL3$KqxKRxe6eTNJw^FG^axVvh&8!PcWS)sloB@=%26Z|ew9p1@AHjcI7B=o8A zB8-4Z`_M@~BUPhU0hvCBXapGJM)@`~(~mCt`&PG_5*v-7c3e$5lb>IFi3yqzcBngS zAMiabzuIr0zO1pEsieO7DJLZ*bxz=UFAK@wkoTXi^Tsqh?YF+pytCa0H*oWm7!Y6oHv!kUCS3ZEL`K@CgO%A&0@e`HRIsL3-%rtDPp zB80T0_}Ib<({Bt}oowts3I;^k3*QErzduPotPe75_<;KAHN+X1COY#sZ}0{5MF4q5 z_7lDJsAeO5dv`Zz?OuXMJrD^#EA!r~vA8_m>};E@aWV6wZo+~@L8|C*F zgf&=ab+iNgqLro9mG*5^zJ9CyeSDJY=>8m-AS!eh7wb5OUl9t)sXNXP65svCO@Dn2 zrH&uw`1iqajRZ8B$qKE=W98-a@Qb()$K9lN>BFdAA4vYB&l2;!7HV!!nMmffIJiAL zDe=1rH@IHL^F2W3^E+>$rV{fad>n#DtzYd3g>bv9Kb=|#m-C>}38uqMyy3*BP_E#sk)h($HZf(8U_3U)X!R*Q2s7vsRLpl6;v5Ul@vlD@@s-g7Y3L{>bQH z@hq8Cgr>Xe4RODfNWlYIq?>CX+(_|QRLicj`ZF<)%5UGG_|Nl|6QtlvRnId&Y4UnU z?Gdh&l#MDNizIBAQ=eALz~$csJbmo!MU9TwFC!y=EL5r!T@a$<(?QQLm;%q2QuNrP}mL{3O2X~VV^c;99aF&EtWcC={Os%d2&!o z9OO|#uQ|dO3}o8_OwZinR^SwVV^Jywt5rjt=uylMlSl7&fzRc!_;ri8;lN55I58($ zis?=xcp=5FL;D85)V;=YX!_thN4Pi?#VT%v9sm5J*nFxwmI0W*NZYvN3p}*qddET> zSYI1nUS9gI58FbhynZe=1p?c5ym}b2ctx6OI1nMiPSy}4L5k1rhMXnSi%W~TaCduV z;JHR*x|I7~KOnJKr^+5su<18Y)|#u8*TTa0<7!ee?Kw)a`^yPg7{@n*@1I32R0@O>R}uAMuSfqnGnDSf?I- zh|i>wehp*+eo@hgEzp&a^D~Cm{@=`rSE9u@tY`I`deAsJ8Id#m(3QV*(TXE4_e$rW z1|0S|_#Q2VP~QTH?5dM4y6&JEtMe(lPSD%}iN{z@q&Pd7VrO#@7CIW*K{65|vNQ)+ zDF*@ax!m#mz^E>b?PV!~)&Uc&aTvc>PPzZXyIsW3`rq0KiWA31!kGzP(C3g4`QNA{ zWuJ!mQXVv}46y4|?#xszN=ogV1-98_vHu*=HAAc#+;p-4-c zKl3P;P8D)OVt|0AD(ze}v#ZxV)TbxCLf{h`P4pP^dJe-~ol`^w;0L6n0

{iV(RH50Gd)DC6x^BtmrGv*J92+>Q_@^j%zbAPF|%z(VHtu?UElbUH~xdM z3KR5Ag4-QmWChJUkXT60;r{P=Lp#n{^J~V9jN>()Yx5+=04P+XTLZy-XxyHGl$!Fe zF%T~J?LKv@h~FHi3cEXTEwU5tjC`*jWp-_ljvEzocE`7mc+hag3*Z(h>G- z@e_$NK@|3JfdfNnB-*1xxzYGMYIIvbPejiQh#p~EGhm{yt$0x$yH%dL%rXjQ7;or4 z{&AU!v@*Tt)U}kohy#D9V5!{5h946XLhXBQV{k>@e78)}5Q=mOz@{T518p%Dg ze{S&PO4e=gkxU}O5wCqXaZ7_{YB!L*^o2SMgwuE!#pg&VXzT|@$)!{{w*XEe@M``;K^09e9eh6cGn(OQz6mFc04O zK0dUt!f~zlC9JW+Y6Lvr6o~G3y+TdrjJQnla8Rp}DT+ZcG4#$ITLR6@FQ5}~=%o3^ z<0_C^`UwUXbx5{c^jm``wkI9X;-q%pPNF2?CiE?XyUPjiMAf_(RuJugumUagwfE0m zuzcvr?s%t_)B0dm0HEccN{08IaNT>KeeZ?9L|TtGY?Q*zC@E+v_+V&et)PM*yY$Nu5I=?b2DQPq_alKls6bQemvUMGKW!MH=GJtp*!O zY0u2ekg&acv$NQzn1c}G3%;PA@lq@8fX1&@=qi)bKtHYxKjQ=;0u(B4$+`6os~yFP z_J-z{!um;;e6*BCmsQa{gORYw&5eQ}fj4@Sd7()>uTY;29%mroOJkoDkOX;B@;p_> zTFWtlAZatRpCo{Bc(P&{13I}2NPZRKb~5?XDY46l#nFv4#pb1|O|at!^{lCB=<@M=Tk+S+{-Dd1~CIHq+7E1@Z*qK%Fed}`a_LGg*dXYs(FF$K>o%T!N zgJdqFfYXTPSBgB-W31-R=o?9oTHIFH`*Kic){6}Ma&5=#?1fbJ1-wsC?(KOVCOL@n zT!4y!1`c3&Xq?ashHQ)9Dg1SbC);lL#lI}iYNy?Nc~~fEtDl;#{rllSbs8uSKMwuf z7jXa6D0+_er@B5Fn=rM310TrDn{BR8|RHpj}DBkXr5S-ujEN_l> z7ZjW3=tBUW_qRYv)M*}?-v7LJdce2|rWC2j=hx;-)ETbHb=C3OPXJ71!Yq#>M(hs^ z&|jy*j17=7$F_T8bD`k4&daflW&WkBBYO9-$+m~dI2$mAECce`Ri@cRI#(<%HUy{g zRdO`Yl-isr2FLI=M?l!1*;fI!@z6(FTL5#3jNZWSnY=MkYOe=z(yb(KSe!<=Q&{gG+pkI#Tv4OWKoH^E!Zjodo@d zhv{=%FUWf43i_LeX+QHSfgQ?rf&AKt;U(*gLq z1i-5lF�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 I2(=3OFS#?uK>z>% literal 0 HcmV?d00001 diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp index 8ad6fe125..d717ee3a8 100644 --- a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp @@ -187,8 +187,7 @@ void FreqScannerBaseband::calcScannerSampleRate(int basebandSampleRate, float rf m_channelizer->setChannelization(m_scannerSampleRate, inputFrequencyOffset); m_channelSampleRate = m_channelizer->getChannelSampleRate(); m_sink.applyChannelSettings(m_channelSampleRate, m_channelizer->getChannelFrequencyOffset(), m_scannerSampleRate, fftSize, binsPerChannel); - - qInfo() << "FreqScannerBaseband::calcScannerSampleRate" + qDebug() << "FreqScannerBaseband::calcScannerSampleRate" << "basebandSampleRate:" << basebandSampleRate << "channelSampleRate:" << m_channelSampleRate << "scannerSampleRate:" << m_scannerSampleRate diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 81146f8cb..9e033df26 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -614,12 +614,20 @@ void FreqScannerGUI::on_addRange_clicked() qint64 stop = dialog.m_stop; int step = dialog.m_step; + blockApplySettings(true); if ((start <= stop) && (step > 0)) { for (qint64 f = start; f <= stop; f += step) { addRow(f, true); } } + blockApplySettings(false); + QList settingsKeys({ + "frequencies", + "enabled", + "notes" + }); + applySettings(settingsKeys); } } diff --git a/plugins/channelrx/freqscanner/freqscannersettings.cpp b/plugins/channelrx/freqscanner/freqscannersettings.cpp index d7ec2c6d9..8c6a00a15 100644 --- a/plugins/channelrx/freqscanner/freqscannersettings.cpp +++ b/plugins/channelrx/freqscanner/freqscannersettings.cpp @@ -295,14 +295,16 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo } if (settingsKeys.contains("enabled") || force) { - QStringList s; + // Don't display + /*QStringList s; for (auto e : m_enabled) { s.append(e ? "true" : "false"); } - ostr << " m_enabled: " << s.join(",").toStdString(); + ostr << " m_enabled: " << s.join(",").toStdString();*/ } if (settingsKeys.contains("notes") || force) { - ostr << " m_notes: " << m_notes.join(",").toStdString(); + // Don't display + //ostr << " m_notes: " << m_notes.join(",").toStdString(); } if (settingsKeys.contains("channel") || force) { ostr << " m_channel: " << m_channel.toStdString(); From 4462078ead7bcd84f49ae24991ab4d585efe5a9c Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 11:26:50 +0100 Subject: [PATCH 09/23] Update docs --- plugins/channelrx/freqscanner/readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md index 1b3d1ab5f..9cef14bcd 100644 --- a/plugins/channelrx/freqscanner/readme.md +++ b/plugins/channelrx/freqscanner/readme.md @@ -34,12 +34,12 @@ that corresponds to the set frequency is being recieved.

6: t_s - Scan time

-Specifies the time in seconds that the Frequency Scanner will average its channel power measurement over. +Specifies the time in seconds that the Frequency Scanner will average its power measurement over.

7: t_rtx - Retransmission Time

Specifies the time in seconds that the Frequency Scanner will wait after the power on the active frequency falls below the threshold, before restarting -scanning. This allows for a temporary break in transmission. +scanning. This enables the channel to remain tuned to a single frequency while there is a temporary break in transmission.

8: Ch BW - Channel Bandwidth

@@ -86,9 +86,9 @@ Displays the current status of the Frequency Scanner. The frequency table contains the list of frequencies to be scanned, along with results of a scan. The columns are: - Freq (Hz): Specifies the channel center frequencies to be scanned. These should be spaced by integer multiples of the channel bandwidth (8). Values should be entered in Hertz. -- Annotation: An annotation (description) for the frequency, that is based on the closest matching [annotation marker](../../../sdrgui/gui/spectrummarkers.md) in the Main Spectrum. +- Annotation: An annotation (description) for the frequency, that is obtained from the closest matching [annotation marker](../../../sdrgui/gui/spectrummarkers.md) in the Main Spectrum. - Enable: Determines whether the frequency will be scanned. This can be used to temporaily disable frequencies you aren't interested in. -- Power (dB): Displays the measured power in decibels during the previous scan. The cell will have a green background if the power was above the threshold (4). +- Power (dB): Displays the measured power in decibels from the last scan. The cell will have a green background if the power was above the threshold (4). - Active Count: Displays the number of scans in which the power for this frequency was above the threshold (4). This allows you to see which frequencies are commonly in use. - Notes: Available for user-entry of notes/information about this frequency. From 33629b77e281424617d42affb536559c96321fba Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 14:35:52 +0100 Subject: [PATCH 10/23] Center channels when possible. Fix channel setting initial display. Add clear active count button. --- plugins/channelrx/freqscanner/freqscanner.cpp | 10 ++++++++++ .../channelrx/freqscanner/freqscannergui.cpp | 19 +++++++++++++++++++ .../channelrx/freqscanner/freqscannergui.h | 1 + .../channelrx/freqscanner/freqscannergui.ui | 14 ++++++++++++++ plugins/channelrx/freqscanner/readme.md | 8 +++++++- 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 8c85f5587..0858dcbe0 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -337,6 +337,16 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; m_stepStopFrequency = frequencies.back(); + // If all frequencies fit within bandwidth, we can have the first frequency more central + int totalBW = frequencies.back() - frequencies.front() + 2 * m_settings.m_channelBandwidth; + if (totalBW < m_scannerSampleRate) + { + int spareBWEachSide = (m_scannerSampleRate - totalBW) / 2; + int spareChannelsEachSide = spareBWEachSide / m_settings.m_channelBandwidth; + int offset = spareChannelsEachSide * m_settings.m_channelBandwidth; + m_stepStartFrequency -= offset; + } + initScan(); } } diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 9e033df26..4fa3b914c 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -505,6 +505,10 @@ void FreqScannerGUI::displaySettings() blockApplySettings(true); + int channelIndex = ui->channels->findText(m_settings.m_channel); + if (channelIndex >= 0) { + ui->channels->setCurrentIndex(channelIndex); + } ui->deltaFrequency->setValue(m_settings.m_channelFrequencyOffset); ui->channelBandwidth->setValue(m_settings.m_channelBandwidth); ui->scanTime->setValue(m_settings.m_scanTime * 10.0); @@ -641,7 +645,14 @@ void FreqScannerGUI::on_remove_clicked() ui->table->removeRow(row); m_settings.m_frequencies.removeAt(row); // table_cellChanged isn't called for removeRow m_settings.m_enabled.removeAt(row); + m_settings.m_notes.removeAt(row); } + QList settingsKeys({ + "frequencies", + "enabled", + "notes" + }); + applySettings(settingsKeys); } static QList takeRow(QTableWidget* table, int row) @@ -695,6 +706,13 @@ void FreqScannerGUI::on_down_clicked() } } +void FreqScannerGUI::on_clearActiveCount_clicked() +{ + for (int i = 0; i < ui->table->rowCount(); i++) { + ui->table->item(i, COL_ACTIVE_COUNT)->setData(Qt::DisplayRole, 0); + } +} + void FreqScannerGUI::on_table_cellChanged(int row, int column) { QTableWidgetItem* item = ui->table->item(row, column); @@ -911,6 +929,7 @@ void FreqScannerGUI::makeUIConnections() QObject::connect(ui->remove, &QToolButton::clicked, this, &FreqScannerGUI::on_remove_clicked); QObject::connect(ui->up, &QToolButton::clicked, this, &FreqScannerGUI::on_up_clicked); QObject::connect(ui->down, &QToolButton::clicked, this, &FreqScannerGUI::on_down_clicked); + QObject::connect(ui->clearActiveCount, &QToolButton::clicked, this, &FreqScannerGUI::on_clearActiveCount_clicked); } void FreqScannerGUI::updateAbsoluteCenterFrequency() diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h index ef95ee212..35be2e571 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.h +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -134,6 +134,7 @@ private slots: void on_remove_clicked(); void on_up_clicked(); void on_down_clicked(); + void on_clearActiveCount_clicked(); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void handleInputMessages(); diff --git a/plugins/channelrx/freqscanner/freqscannergui.ui b/plugins/channelrx/freqscanner/freqscannergui.ui index d00ed10e7..cd887b094 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.ui +++ b/plugins/channelrx/freqscanner/freqscannergui.ui @@ -740,6 +740,20 @@ + + + + Clear Active Count in all rows + + + + + + + :/bin.png:/bin.png + + + diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md index 9cef14bcd..a6a71263a 100644 --- a/plugins/channelrx/freqscanner/readme.md +++ b/plugins/channelrx/freqscanner/readme.md @@ -16,7 +16,9 @@ Specifies the channel (such as an AM, NFM or DSD Demod), by device set and chann

2: Minimum frequency shift from center frequency of reception for channel

-Use the wheels of keyboard to adjust the minimim frequency shift in Hz from the center frequency of reception for the channel (1). Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. +Use the wheels of keyboard to adjust the minimim frequency shift in Hz from the center frequency of reception for the channel (1). + +This setting is typically used to avoid having the channel (1) centered at DC, which can be problematic for some demodulators used with SDRs with a DC spike.

3: Active frequency power

@@ -119,3 +121,7 @@ Moves the selected rows up the frequency table (14).

19: Down

Moves the selected rows the the frequency table (14). + +

20: Clear Active Count

+ +Press to reset the value in the Active Count column to 0 for all rows. From e89623f64d50565b68b63ba740dcd8e0ddae5d2b Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 20:58:19 +0100 Subject: [PATCH 11/23] Fix restoring frequencies. --- plugins/channelrx/freqscanner/freqscanner.cpp | 2 +- plugins/channelrx/freqscanner/freqscannergui.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 0858dcbe0..ef0afc145 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -505,7 +505,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< else { if (m_guiMessageQueue) { - m_guiMessageQueue->push(MsgStatus::create(QString("Scanning: No active channels - Max power %1 dB").arg(maxPower))); + m_guiMessageQueue->push(MsgStatus::create(QString("Scanning: No active channels - Max power %1 dB").arg(maxPower, 0, 'f', 1))); } } } diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 4fa3b914c..66a2f3d62 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -523,9 +523,11 @@ void FreqScannerGUI::displaySettings() ui->measurement->setCurrentIndex((int)m_settings.m_measurement); ui->mode->setCurrentIndex((int)m_settings.m_mode); + ui->table->blockSignals(true); for (int i = 0; i < m_settings.m_frequencies.size(); i++) { addRow(m_settings.m_frequencies[i], m_settings.m_enabled[i], m_settings.m_notes[i]); } + ui->table->blockSignals(false); // Order and size columns QHeaderView* header = ui->table->horizontalHeader(); From f8f9d270dfb16de01489f061b91e72a4cb050d61 Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 29 Sep 2023 21:18:27 +0100 Subject: [PATCH 12/23] Update annotations when restoring settings. --- plugins/channelrx/freqscanner/freqscannergui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 66a2f3d62..9cd79d20e 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -524,8 +524,10 @@ void FreqScannerGUI::displaySettings() ui->mode->setCurrentIndex((int)m_settings.m_mode); ui->table->blockSignals(true); - for (int i = 0; i < m_settings.m_frequencies.size(); i++) { + for (int i = 0; i < m_settings.m_frequencies.size(); i++) + { addRow(m_settings.m_frequencies[i], m_settings.m_enabled[i], m_settings.m_notes[i]); + updateAnnotation(i); } ui->table->blockSignals(false); From d2526cdc5bf7a6b687ce885ca877958916f9a268 Mon Sep 17 00:00:00 2001 From: srcejon Date: Mon, 2 Oct 2023 15:45:17 +0100 Subject: [PATCH 13/23] Fix loading settings. Add remove inactive button. Restart scan when sample rate changes. --- plugins/channelrx/freqscanner/freqscanner.cpp | 10 +- .../freqscanner/freqscanneraddrangedialog.cpp | 88 +++++++- .../freqscanner/freqscanneraddrangedialog.h | 5 +- .../freqscanner/freqscanneraddrangedialog.ui | 190 +++++++++++------- .../channelrx/freqscanner/freqscannergui.cpp | 45 ++++- .../channelrx/freqscanner/freqscannergui.h | 1 + .../channelrx/freqscanner/freqscannergui.ui | 20 +- plugins/channelrx/freqscanner/readme.md | 15 +- 8 files changed, 271 insertions(+), 103 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index ef0afc145..14b00720a 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -225,7 +225,13 @@ bool FreqScanner::handleMessage(const Message& cmd) else if (DSPSignalNotification::match(cmd)) { DSPSignalNotification& notif = (DSPSignalNotification&) cmd; - m_basebandSampleRate = notif.getSampleRate(); + int newSampleRate = notif.getSampleRate(); + if ((newSampleRate != m_basebandSampleRate) && (m_state != IDLE)) + { + // Restart scan if sample rate changes + startScan(); + } + m_basebandSampleRate = newSampleRate; m_centerFrequency = notif.getCenterFrequency(); qDebug() << "FreqScanner::handleMessage: DSPSignalNotification"; // Forward to the sink @@ -645,7 +651,7 @@ void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStri { // Restart scan if any settings change if (m_state != IDLE) { - m_state = START_SCAN; + startScan(); } } diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp index c08998641..cdf24d2cb 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp @@ -29,11 +29,9 @@ FreqScannerAddRangeDialog::FreqScannerAddRangeDialog(int step, QWidget* parent) ui->stop->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->stop->setValueRange(false, 11, 0, 99999999999); - // Airband frequency range - ui->start->setValue(118000000); - ui->stop->setValue(137000000); + on_preset_currentTextChanged("Airband"); - ui->step->setCurrentText(QString::number(step)); + //ui->step->setCurrentText(QString::number(step)); } FreqScannerAddRangeDialog::~FreqScannerAddRangeDialog() @@ -43,8 +41,84 @@ FreqScannerAddRangeDialog::~FreqScannerAddRangeDialog() void FreqScannerAddRangeDialog::accept() { - m_start = ui->start->getValue(); - m_stop = ui->stop->getValue(); - m_step = ui->step->currentText().toInt(); + if (ui->preset->currentText() == "Digital Selective Calling") + { + // From ITU M.541 + static const QList dscFreqs = { + 2177000, 2189500, + 4208000, 4208500, 4209000, + 6312500, 6313000, + 8415000, 8415500, 8416000, + 12577500, 12578000, 12578500, + 16805000, 16805500, 16806000, 18898500, 18899000, 18899500, + 22374500, 22375000, 22375500, + 25208500, 25209000, 25209500 + }; + m_frequencies.append(dscFreqs); + } + else if (ui->preset->currentText() == "DAB") + { + static const QList dabFreqs = { + 174928000, 176640000, 178352000, 180064000, + 181936000, 183648000, 185360000, 187072000, + 188928000, 190640000, 192352000, 194064000, + 195936000, 197648000, 199360000, 201072000, + 202928000, 204640000, 206352000, 208064000, + 209936000, 211648000, 213360000, 215072000, + 216928000, 218640000, 220352000, 222064000, + 223936000, 225648000, 227360000, 229072000, + 230784000, 232496000, 234208000, 235776000, + 237448000, 239200000 + }; + m_frequencies.append(dabFreqs); + } + else + { + qint64 start = ui->start->getValue(); + qint64 stop = ui->stop->getValue(); + int step = ui->step->currentText().toInt(); + + if ((start <= stop) && (step > 0)) + { + for (qint64 f = start; f <= stop; f += step) { + m_frequencies.append(f); + } + } + } + QDialog::accept(); } + +void FreqScannerAddRangeDialog::on_preset_currentTextChanged(const QString& text) +{ + bool enableManAdjust = true; + if (text == "Airband") + { + ui->start->setValue(118000000); + ui->stop->setValue(137000000); + ui->step->setCurrentText("25000"); + } + else if (text == "Broadcast FM") + { + ui->start->setValue(87500000); + ui->stop->setValue(108000000); + ui->step->setCurrentText("100000"); + } + else if (text == "DAB") + { + enableManAdjust = false; + } + else if (text == "Marine") + { + ui->start->setValue(156000000); + ui->stop->setValue(162150000); + ui->step->setCurrentText("25000"); + } + else if (text == "Digital Selective Calling") + { + enableManAdjust = false; + } + ui->start->setEnabled(enableManAdjust); + ui->stop->setEnabled(enableManAdjust); + ui->step->setEnabled(enableManAdjust); +} diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h index 7cb25b076..0a9f14e36 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.h @@ -30,12 +30,11 @@ public: explicit FreqScannerAddRangeDialog(int step, QWidget* parent = nullptr); ~FreqScannerAddRangeDialog(); - qint64 m_start; - qint64 m_stop; - int m_step; + QList m_frequencies; private slots: void accept(); + void on_preset_currentTextChanged(const QString& text); private: Ui::FreqScannerAddRangeDialog *ui; diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui index 0cce5efe4..73cd0a1aa 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui @@ -7,7 +7,7 @@ 0 0 385 - 162 + 190 @@ -31,82 +31,21 @@ Add Frequency Range - - - - - 0 - 0 - - - - - 32 - 16 - - - - - Liberation Mono - 12 - - - - PointingHandCursor - - - Qt::StrongFocus - - - Stop frequency in Hertz - - - - - + + - Stop Frequency + Step - - - - - 90 - 0 - - - - Start Frequency - - - - - + + Hz - - - - true - - - - 25000 - - - - - 8333.3 - - - - - + @@ -137,24 +76,129 @@ - + Hz - - + + Hz - + - Step + Stop Frequency + + + + + + + + 90 + 0 + + + + Start Frequency + + + + + + + true + + + + 25000 + + + + + 8333.3 + + + + + 100000 + + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + + + Stop frequency in Hertz + + + + + + + Select a preset range of frequencies + + + + Airband + + + + + Broadcast FM + + + + + DAB + + + + + Digital Selective Calling + + + + + Marine + + + + + + + + Preset diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 9cd79d20e..520d4dbbc 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -504,7 +504,6 @@ void FreqScannerGUI::displaySettings() setTitle(m_channelMarker.getTitle()); blockApplySettings(true); - int channelIndex = ui->channels->findText(m_settings.m_channel); if (channelIndex >= 0) { ui->channels->setCurrentIndex(channelIndex); @@ -524,6 +523,7 @@ void FreqScannerGUI::displaySettings() ui->mode->setCurrentIndex((int)m_settings.m_mode); ui->table->blockSignals(true); + ui->table->setRowCount(0); for (int i = 0; i < m_settings.m_frequencies.size(); i++) { addRow(m_settings.m_frequencies[i], m_settings.m_enabled[i], m_settings.m_notes[i]); @@ -618,16 +618,9 @@ void FreqScannerGUI::on_addRange_clicked() new DialogPositioner(&dialog, false); if (dialog.exec()) { - qint64 start = dialog.m_start; - qint64 stop = dialog.m_stop; - int step = dialog.m_step; - blockApplySettings(true); - if ((start <= stop) && (step > 0)) - { - for (qint64 f = start; f <= stop; f += step) { - addRow(f, true); - } + for (const auto f : dialog.m_frequencies) { + addRow(f, true); } blockApplySettings(false); QList settingsKeys({ @@ -659,6 +652,27 @@ void FreqScannerGUI::on_remove_clicked() applySettings(settingsKeys); } +void FreqScannerGUI::on_removeInactive_clicked() +{ + for (int i = ui->table->rowCount() - 1; i >= 0; i--) + { + if (ui->table->item(i, COL_ACTIVE_COUNT)->data(Qt::DisplayRole).toInt() == 0) + { + ui->table->removeRow(i); + m_settings.m_frequencies.removeAt(i); // table_cellChanged isn't called for removeRow + m_settings.m_enabled.removeAt(i); + m_settings.m_notes.removeAt(i); + } + } + QList settingsKeys({ + "frequencies", + "enabled", + "notes" + }); + applySettings(settingsKeys); +} + + static QList takeRow(QTableWidget* table, int row) { QList rowItems; @@ -805,6 +819,14 @@ void FreqScannerGUI::table_customContextMenuRequested(QPoint pos) }); tableContextMenu->addAction(copyAction); + // Remove selected rows + + QAction* removeAction = new QAction("Remove", tableContextMenu); + connect(removeAction, &QAction::triggered, this, [this]()->void { + on_remove_clicked(); + }); + tableContextMenu->addAction(removeAction); + tableContextMenu->addSeparator(); // Tune to frequency @@ -904,7 +926,7 @@ void FreqScannerGUI::resizeTable() // Fill table with a row of dummy data that will size the columns nicely int row = ui->table->rowCount(); ui->table->setRowCount(row + 1); - ui->table->setItem(row, COL_FREQUENCY, new QTableWidgetItem("999.000 MHz")); + ui->table->setItem(row, COL_FREQUENCY, new QTableWidgetItem("800,000.5 MHz")); ui->table->setItem(row, COL_ANNOTATION, new QTableWidgetItem("An annotation")); ui->table->setItem(row, COL_ENABLE, new QTableWidgetItem("Enable")); ui->table->setItem(row, COL_POWER, new QTableWidgetItem("-100.0")); @@ -931,6 +953,7 @@ void FreqScannerGUI::makeUIConnections() QObject::connect(ui->addSingle, &QToolButton::clicked, this, &FreqScannerGUI::on_addSingle_clicked); QObject::connect(ui->addRange, &QToolButton::clicked, this, &FreqScannerGUI::on_addRange_clicked); QObject::connect(ui->remove, &QToolButton::clicked, this, &FreqScannerGUI::on_remove_clicked); + QObject::connect(ui->removeInactive, &QToolButton::clicked, this, &FreqScannerGUI::on_removeInactive_clicked); QObject::connect(ui->up, &QToolButton::clicked, this, &FreqScannerGUI::on_up_clicked); QObject::connect(ui->down, &QToolButton::clicked, this, &FreqScannerGUI::on_down_clicked); QObject::connect(ui->clearActiveCount, &QToolButton::clicked, this, &FreqScannerGUI::on_clearActiveCount_clicked); diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h index 35be2e571..9113fe514 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.h +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -132,6 +132,7 @@ private slots: void on_addSingle_clicked(); void on_addRange_clicked(); void on_remove_clicked(); + void on_removeInactive_clicked(); void on_up_clicked(); void on_down_clicked(); void on_clearActiveCount_clicked(); diff --git a/plugins/channelrx/freqscanner/freqscannergui.ui b/plugins/channelrx/freqscanner/freqscannergui.ui index cd887b094..a22fa5160 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.ui +++ b/plugins/channelrx/freqscanner/freqscannergui.ui @@ -181,6 +181,12 @@ + + + 30 + 0 + + Active frequency power @@ -231,10 +237,10 @@ Power threshold in dB - -1400 + -1000 - 100 + 0 1 @@ -699,6 +705,16 @@ + + + + Remove rows with Active Count of 0 + + + Remove Inactive + + + diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md index a6a71263a..9f2de848f 100644 --- a/plugins/channelrx/freqscanner/readme.md +++ b/plugins/channelrx/freqscanner/readme.md @@ -87,7 +87,7 @@ Displays the current status of the Frequency Scanner. The frequency table contains the list of frequencies to be scanned, along with results of a scan. The columns are: -- Freq (Hz): Specifies the channel center frequencies to be scanned. These should be spaced by integer multiples of the channel bandwidth (8). Values should be entered in Hertz. +- Freq (Hz): Specifies the channel center frequencies to be scanned. Values should be entered in Hertz. - Annotation: An annotation (description) for the frequency, that is obtained from the closest matching [annotation marker](../../../sdrgui/gui/spectrummarkers.md) in the Main Spectrum. - Enable: Determines whether the frequency will be scanned. This can be used to temporaily disable frequencies you aren't interested in. - Power (dB): Displays the measured power in decibels from the last scan. The cell will have a green background if the power was above the threshold (4). @@ -99,6 +99,7 @@ When an active frequency is found after a scan, the corresponding row in the tab Right clicking on a cell will display a popup menu: - Copy contents of cell to clipboard. +- Remove selected rows. - Tune selected channel (1) to the frequency in the row clicked on.

15: Add

@@ -112,16 +113,20 @@ The step value should typically be an integer multiple of the channel bandwidth

17: Remove

-Removes the selected rows from the frequency table (14). +Removes the selected rows from the frequency table (14). Press Ctrl-A to select all rows. -

18: Up

+

18: Remove Inactive

+ +Removes all rows with Active Count of 0. + +

19: Up

Moves the selected rows up the frequency table (14). -

19: Down

+

20: Down

Moves the selected rows the the frequency table (14). -

20: Clear Active Count

+

21: Clear Active Count

Press to reset the value in the Active Count column to 0 for all rows. From 83ceae4ba3155bec7dd3c5383add4b80eccf7870 Mon Sep 17 00:00:00 2001 From: srcejon Date: Tue, 3 Oct 2023 16:09:54 +0100 Subject: [PATCH 14/23] Use full bandwidth --- plugins/channelrx/freqscanner/freqscanner.cpp | 32 ++++++++++++++++++- plugins/channelrx/freqscanner/freqscanner.h | 18 +---------- .../freqscanner/freqscannerbaseband.cpp | 5 +-- .../freqscanner/freqscannerbaseband.h | 1 + .../channelrx/freqscanner/freqscannergui.cpp | 24 +++++++++++--- .../channelrx/freqscanner/freqscannergui.h | 1 + .../channelrx/freqscanner/freqscannersink.cpp | 9 +++--- plugins/channelrx/freqscanner/readme.md | 4 ++- 8 files changed, 64 insertions(+), 30 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 14b00720a..392faa30d 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -44,6 +44,7 @@ #include "util/db.h" #include "channel/channelwebapiutils.h" #include "maincore.h" +#include "dsp/spectrumvis.h" MESSAGE_CLASS_DEFINITION(FreqScanner::MsgConfigureFreqScanner, Message) MESSAGE_CLASS_DEFINITION(FreqScanner::MsgReportChannels, Message) @@ -337,7 +338,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< // Calculate how many channels can be scanned in one go int fftSize; int binsPerChannel; - FreqScanner::calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); + calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); // Align first frequency so we cover as many channels as possible, while avoiding DC bin m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; @@ -351,6 +352,8 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< int spareChannelsEachSide = spareBWEachSide / m_settings.m_channelBandwidth; int offset = spareChannelsEachSide * m_settings.m_channelBandwidth; m_stepStartFrequency -= offset; + + qDebug() << "*********** Starting scan: m_stepStartFrequency:" << m_stepStartFrequency << "offset:" << offset; } initScan(); @@ -582,6 +585,32 @@ void FreqScanner::timeout() initScan(); } +void FreqScanner::calcScannerSampleRate(int channelBW, int basebandSampleRate, int& scannerSampleRate, int& fftSize, int& binsPerChannel) +{ + const int maxFFTSize = 16384; + const int minBinsPerChannel = 8; + + // Base FFT size on that used for main spectrum + std::vector& deviceSets = MainCore::instance()->getDeviceSets(); + DeviceSet* deviceSet = deviceSets[m_deviceAPI->getDeviceSetIndex()]; + const SpectrumSettings& spectrumSettings = deviceSet->m_spectrumVis->getSettings(); + fftSize = spectrumSettings.m_fftSize; + + // But ensure we have several bins per channel + // Adjust sample rate, to ensure we don't get massive FFT size + scannerSampleRate = basebandSampleRate; + while (fftSize / (scannerSampleRate / channelBW) < minBinsPerChannel) + { + if (fftSize == maxFFTSize) { + scannerSampleRate /= 2; + } else { + fftSize *= 2; + } + } + + binsPerChannel = fftSize / (scannerSampleRate / (float)channelBW); +} + void FreqScanner::setCenterFrequency(qint64 frequency) { FreqScannerSettings settings = m_settings; @@ -647,6 +676,7 @@ void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStri || settingsKeys.contains("priority") || settingsKeys.contains("measurement") || settingsKeys.contains("mode") + || settingsKeys.contains("channelBandwidth") || force) { // Restart scan if any settings change diff --git a/plugins/channelrx/freqscanner/freqscanner.h b/plugins/channelrx/freqscanner/freqscanner.h index b4ab200f1..80a53b79a 100644 --- a/plugins/channelrx/freqscanner/freqscanner.h +++ b/plugins/channelrx/freqscanner/freqscanner.h @@ -348,23 +348,7 @@ public: uint32_t getNumberOfDeviceStreams() const; - static void calcScannerSampleRate(int channelBW, int basebandSampleRate, int& scannerSampleRate, int& fftSize, int& binsPerChannel) - { - const int maxFFTSize = 2048; - const int maxBinsPerChannel = 32; - const int minBinsPerChannel = 8; - - // Use multiple bins per channel, to account for FFT spectral leakage - binsPerChannel = maxFFTSize / (basebandSampleRate / channelBW); - binsPerChannel = std::min(binsPerChannel, maxBinsPerChannel); - binsPerChannel = std::max(binsPerChannel, minBinsPerChannel); - double binBW = channelBW / (double)binsPerChannel; - - // Find next smallest power of 2 - fftSize = pow(2.0, floor(log2(basebandSampleRate / binBW))); - fftSize = std::min(maxFFTSize, fftSize); - scannerSampleRate = binBW * fftSize; - } + void calcScannerSampleRate(int channelBW, int basebandSampleRate, int& scannerSampleRate, int& fftSize, int& binsPerChannel); static const char * const m_channelIdURI; static const char * const m_channelId; diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp index d717ee3a8..189753c42 100644 --- a/plugins/channelrx/freqscanner/freqscannerbaseband.cpp +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.cpp @@ -28,6 +28,7 @@ MESSAGE_CLASS_DEFINITION(FreqScannerBaseband::MsgConfigureFreqScannerBaseband, Message) FreqScannerBaseband::FreqScannerBaseband(FreqScanner *freqScanner) : + m_freqScanner(freqScanner), m_sink(freqScanner), m_messageQueueToGUI(nullptr) { @@ -147,7 +148,7 @@ bool FreqScannerBaseband::handleMessage(const Message& cmd) void FreqScannerBaseband::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force) { - if ((settings.m_channelBandwidth != m_settings.m_channelBandwidth) || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + if (settingsKeys.contains("channelBandwidth") || settingsKeys.contains("inputFrequencyOffset") || force) { int basebandSampleRate = m_channelizer->getBasebandSampleRate(); if ((basebandSampleRate != 0) && (settings.m_channelBandwidth != 0)) { @@ -182,7 +183,7 @@ void FreqScannerBaseband::calcScannerSampleRate(int basebandSampleRate, float rf int fftSize; int binsPerChannel; - FreqScanner::calcScannerSampleRate(rfBandwidth, basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); + m_freqScanner->calcScannerSampleRate(rfBandwidth, basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); m_channelizer->setChannelization(m_scannerSampleRate, inputFrequencyOffset); m_channelSampleRate = m_channelizer->getChannelSampleRate(); diff --git a/plugins/channelrx/freqscanner/freqscannerbaseband.h b/plugins/channelrx/freqscanner/freqscannerbaseband.h index f638d7de9..bcd0ae6dc 100644 --- a/plugins/channelrx/freqscanner/freqscannerbaseband.h +++ b/plugins/channelrx/freqscanner/freqscannerbaseband.h @@ -75,6 +75,7 @@ public: void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } private: + FreqScanner *m_freqScanner; SampleSinkFifo m_sampleFifo; DownChannelizer *m_channelizer; int m_channelSampleRate; diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 520d4dbbc..73c019430 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -103,7 +103,7 @@ bool FreqScannerGUI::handleMessage(const Message& message) m_basebandSampleRate = notif.getSampleRate(); ui->deltaFrequency->setValueRange(true, 7, 0, m_basebandSampleRate/2); ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); - ui->channelBandwidth->setValueRange(true, 7, 8, m_basebandSampleRate); + ui->channelBandwidth->setValueRange(true, 7, 0, m_basebandSampleRate); if (m_channelMarker.getBandwidth() == 0) { m_channelMarker.setBandwidth(m_basebandSampleRate); } @@ -164,6 +164,7 @@ bool FreqScannerGUI::handleMessage(const Message& message) FreqScanner::MsgReportScanRange& report = (FreqScanner::MsgReportScanRange&)message; m_channelMarker.setCenterFrequency(report.getCenterFrequency()); m_channelMarker.setBandwidth(report.getTotalBandwidth()); + m_channelMarker.setVisible(report.getTotalBandwidth() < m_basebandSampleRate); // Hide marker if full bandwidth return true; } else if (FreqScanner::MsgScanResult::match(message)) @@ -417,7 +418,7 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); m_channelMarker.setTitle("Frequency Scanner"); m_channelMarker.blockSignals(false); - m_channelMarker.setVisible(true); // activate signal on the last setting only + m_channelMarker.setVisible(true); setTitleColor(m_channelMarker.getColor()); m_settings.setChannelMarker(&m_channelMarker); @@ -459,6 +460,8 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->table->setItemDelegateForColumn(COL_FREQUENCY, new FrequencyDelegate("Auto", 3)); ui->table->setItemDelegateForColumn(COL_POWER, new DecimalDelegate(1)); + + connect(m_deviceUISet->m_spectrum->getSpectrumView(), &GLSpectrumView::updateAnnotations, this, &FreqScannerGUI::updateAnnotations); } FreqScannerGUI::~FreqScannerGUI() @@ -784,9 +787,13 @@ void FreqScannerGUI::updateAnnotation(int row) // Exact match annotationItem->setText(marker.m_text); return; - } else if (!closest) { + } + else if (!closest) + { closest = ▮ - } else { + } + else + { if (marker.m_bandwidth < closest->m_bandwidth) { closest = ▮ } @@ -799,6 +806,13 @@ void FreqScannerGUI::updateAnnotation(int row) } } +void FreqScannerGUI::updateAnnotations() +{ + for (int i = 0; i < ui->table->rowCount(); i++) { + updateAnnotation(i); + } +} + void FreqScannerGUI::table_customContextMenuRequested(QPoint pos) { QTableWidgetItem* item = ui->table->itemAt(pos); @@ -927,7 +941,7 @@ void FreqScannerGUI::resizeTable() int row = ui->table->rowCount(); ui->table->setRowCount(row + 1); ui->table->setItem(row, COL_FREQUENCY, new QTableWidgetItem("800,000.5 MHz")); - ui->table->setItem(row, COL_ANNOTATION, new QTableWidgetItem("An annotation")); + 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_ACTIVE_COUNT, new QTableWidgetItem("10000")); diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h index 9113fe514..34979d1bf 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.h +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -94,6 +94,7 @@ private: void updateAbsoluteCenterFrequency(); void addRow(qint64 frequency, bool enabled, const QString& notes = ""); void updateAnnotation(int row); + void updateAnnotations(); void updateChannelsList(const QList& channels); void leaveEvent(QEvent*); diff --git a/plugins/channelrx/freqscanner/freqscannersink.cpp b/plugins/channelrx/freqscanner/freqscannersink.cpp index 01d8ed203..b4d9689dc 100644 --- a/plugins/channelrx/freqscanner/freqscannersink.cpp +++ b/plugins/channelrx/freqscanner/freqscannersink.cpp @@ -107,7 +107,7 @@ void FreqScannerSink::processOneSample(Complex &ci) if (m_fftAverage.nextAverage()) { // Send results to channel - if (getMessageQueueToChannel()) + if (getMessageQueueToChannel() && (m_settings.m_channelBandwidth != 0) && (m_binsPerChannel != 0)) { FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(m_fftStartTime); QList& results = msg->getScanResults(); @@ -119,11 +119,11 @@ void FreqScannerSink::processOneSample(Complex &ci) qint64 frequency = m_settings.m_frequencies[i]; qint64 startFrequency = m_centerFrequency - m_scannerSampleRate / 2; qint64 diff = frequency - startFrequency; - int binBW = m_settings.m_channelBandwidth / m_binsPerChannel; + float binBW = m_scannerSampleRate / (float)m_fftSize; if ((diff < m_scannerSampleRate) && (diff >= 0)) { - int bin = diff / binBW; + int bin = std::round(diff / binBW); // Calculate power at that frequency Real power; @@ -132,7 +132,7 @@ void FreqScannerSink::processOneSample(Complex &ci) } else { power = totalPower(bin); } - + //qDebug() << "startFrequency:" << startFrequency << "m_scannerSampleRate:" << m_scannerSampleRate << "m_centerFrequency:" << m_centerFrequency << "frequency" << frequency << "bin" << bin << "power" << power; FreqScanner::MsgScanResult::ScanResult result = {frequency, power}; results.append(result); } @@ -177,6 +177,7 @@ Real FreqScannerSink::peakPower(int bin) const if ((idx < 0) || (idx >= m_fftSize)) { continue; } + //qDebug() << "idx:" << idx << "power:" << CalcDb::dbPower(m_magSq[idx]); maxMagSq = std::max(maxMagSq, m_magSq[idx]); } Real db = CalcDb::dbPower(maxMagSq); diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md index 9f2de848f..737c8325b 100644 --- a/plugins/channelrx/freqscanner/readme.md +++ b/plugins/channelrx/freqscanner/readme.md @@ -56,7 +56,9 @@ Specifies which frequency will be chosen as the active frequency, when multiple

10: Meas - Power Measurement

-Specifies how power is measured. In both cases, a FFT is used, with the channel bandwidth being spread over 8 to 32 bins, with the first and last bins being excluded from the measurement (to reduce spectral leakage from adjacent channels): +Specifies how power is measured. In both cases, a FFT is used. +FFT size is typically the same as used for the Main Spectrum, but may be increased to ensure at least 8 bins cover the channel bandwidth (8). +The first and last bins are excluded from the measurement (to reduce spectral leakage from adjacent channels): - Peak: Power is the highest value in all of the bins, averaged over the scan time (6). - Total: Power is the sum of power in all of the bins, averaged over the scan time (6). From 2d8ae1329bcb2464e8b0344d1540b2a138e98ed9 Mon Sep 17 00:00:00 2001 From: srcejon Date: Tue, 3 Oct 2023 16:10:08 +0100 Subject: [PATCH 15/23] Add signal for when annotations change --- sdrgui/gui/glspectrumview.cpp | 2 ++ sdrgui/gui/glspectrumview.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/sdrgui/gui/glspectrumview.cpp b/sdrgui/gui/glspectrumview.cpp index c5c47c0b2..9e3fb7ae5 100644 --- a/sdrgui/gui/glspectrumview.cpp +++ b/sdrgui/gui/glspectrumview.cpp @@ -3599,6 +3599,8 @@ void GLSpectrumView::updateWaterfallMarkers() void GLSpectrumView::updateAnnotationMarkers() { + emit updateAnnotations(); // Notify other plugins we have updated annotations + if (!(m_markersDisplay & SpectrumSettings::MarkersDisplayAnnotations)) { return; } diff --git a/sdrgui/gui/glspectrumview.h b/sdrgui/gui/glspectrumview.h index 2d8a85c9a..40a080dbf 100644 --- a/sdrgui/gui/glspectrumview.h +++ b/sdrgui/gui/glspectrumview.h @@ -535,6 +535,9 @@ private slots: signals: // Emitted when user tries to scroll to frequency currently out of range void requestCenterFrequency(qint64 frequency); + // Emitted when annotations are changed + void updateAnnotations(); + }; #endif // INCLUDE_GLSPECTRUMVIEW_H From bb2655cd512e36ce8f41feca9b7fcc0ed1e0242f Mon Sep 17 00:00:00 2001 From: srcejon Date: Tue, 3 Oct 2023 16:57:45 +0100 Subject: [PATCH 16/23] Fix warnings --- plugins/channelrx/freqscanner/freqscanner.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 392faa30d..9e5aef364 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -331,7 +331,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< frequencies.append(m_settings.m_frequencies[i]); } } - qSort(frequencies); + std::sort(frequencies.begin(), frequencies.end()); if ((frequencies.size() > 0) && (m_settings.m_channelBandwidth > 0) && (m_basebandSampleRate > 0)) { @@ -352,8 +352,6 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< int spareChannelsEachSide = spareBWEachSide / m_settings.m_channelBandwidth; int offset = spareChannelsEachSide * m_settings.m_channelBandwidth; m_stepStartFrequency -= offset; - - qDebug() << "*********** Starting scan: m_stepStartFrequency:" << m_stepStartFrequency << "offset:" << offset; } initScan(); From 51ddd77aa3aa9c66827b63dc4c575a4ef2fa13a5 Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 13 Oct 2023 08:54:11 +0100 Subject: [PATCH 17/23] Fix 8333 channels. --- .../freqscanner/freqscanneraddrangedialog.cpp | 16 ++++++++++++++-- .../freqscanner/freqscanneraddrangedialog.ui | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp index cdf24d2cb..124322fa3 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.cpp @@ -15,6 +15,8 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +#include + #include "freqscanneraddrangedialog.h" #include "ui_freqscanneraddrangedialog.h" @@ -80,8 +82,18 @@ void FreqScannerAddRangeDialog::accept() if ((start <= stop) && (step > 0)) { - for (qint64 f = start; f <= stop; f += step) { - m_frequencies.append(f); + if (step == 8333) + { + double fstep = 8333 + 1.0/3.0; // float will give incorrect results + for (double f = start; f <= stop; f += fstep) { + m_frequencies.append(std::round(f)); + } + } + else + { + for (qint64 f = start; f <= stop; f += step) { + m_frequencies.append(f); + } } } } diff --git a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui index 73cd0a1aa..98cd619a9 100644 --- a/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui +++ b/plugins/channelrx/freqscanner/freqscanneraddrangedialog.ui @@ -122,7 +122,7 @@
- 8333.3 + 8333 From 515e19f20b7a6628ddb47e7563b53d1002243278 Mon Sep 17 00:00:00 2001 From: srcejon Date: Fri, 13 Oct 2023 08:54:43 +0100 Subject: [PATCH 18/23] Fix crash and tidy up UI. --- plugins/channelrx/freqscanner/freqscannergui.cpp | 11 +++++++---- plugins/channelrx/freqscanner/freqscannergui.ui | 4 ++-- plugins/channelrx/freqscanner/freqscannersink.cpp | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 73c019430..d1931f8f6 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -101,9 +101,12 @@ bool FreqScannerGUI::handleMessage(const Message& message) DSPSignalNotification& notif = (DSPSignalNotification&) message; m_deviceCenterFrequency = notif.getCenterFrequency(); m_basebandSampleRate = notif.getSampleRate(); - ui->deltaFrequency->setValueRange(true, 7, 0, m_basebandSampleRate/2); - ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); - ui->channelBandwidth->setValueRange(true, 7, 0, m_basebandSampleRate); + if (m_basebandSampleRate != 0) + { + ui->deltaFrequency->setValueRange(true, 7, 0, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + ui->channelBandwidth->setValueRange(true, 7, 0, m_basebandSampleRate); + } if (m_channelMarker.getBandwidth() == 0) { m_channelMarker.setBandwidth(m_basebandSampleRate); } @@ -363,7 +366,7 @@ void FreqScannerGUI::onMenuDialogCalled(const QPoint &p) setTitleColor(m_settings.m_rgbColor); QList settingsKeys({ - "m_rgbColor", + "rgbColor", "title", "useReverseAPI", "reverseAPIAddress", diff --git a/plugins/channelrx/freqscanner/freqscannergui.ui b/plugins/channelrx/freqscanner/freqscannergui.ui index a22fa5160..7d6da12a2 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.ui +++ b/plugins/channelrx/freqscanner/freqscannergui.ui @@ -313,12 +313,12 @@ - 45 + 54 0 - 100 ms + 1000 ms diff --git a/plugins/channelrx/freqscanner/freqscannersink.cpp b/plugins/channelrx/freqscanner/freqscannersink.cpp index b4d9689dc..ab1408967 100644 --- a/plugins/channelrx/freqscanner/freqscannersink.cpp +++ b/plugins/channelrx/freqscanner/freqscannersink.cpp @@ -171,8 +171,9 @@ Real FreqScannerSink::peakPower(int bin) const // Skip bin between halfway between channels // Then skip first and last bins, to avoid spectral leakage (particularly at DC) int startBin = bin - m_binsPerChannel/2 + 1 + 1; - Real maxMagSq = m_magSq[startBin]; - for (int i = 1; i < m_binsPerChannel - 2 - 1; i++) { + Real maxMagSq = std::numeric_limits::min(); + for (int i = 0; i < m_binsPerChannel - 2 - 1; i++) + { int idx = startBin + i; if ((idx < 0) || (idx >= m_fftSize)) { continue; From ce6b08b15ec904ed8a68cde6743254204ff5284c Mon Sep 17 00:00:00 2001 From: srcejon Date: Sun, 22 Oct 2023 10:01:29 +0100 Subject: [PATCH 19/23] Add menu to enable/disable all rows. --- .../channelrx/freqscanner/freqscannergui.cpp | 33 ++++++++++++++++++- .../channelrx/freqscanner/freqscannergui.h | 1 + plugins/channelrx/freqscanner/readme.md | 4 +++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index d1931f8f6..0710c4757 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -784,7 +784,13 @@ void FreqScannerGUI::updateAnnotation(int row) const SpectrumAnnotationMarker* closest = nullptr; for (const auto& marker : markers) { - if ((marker.m_startFrequency <= frequency) && (frequency < marker.m_startFrequency + marker.m_bandwidth)) + qint64 start1 = marker.m_startFrequency; + qint64 stop1 = marker.m_startFrequency + marker.m_bandwidth; + qint64 start2 = frequency - m_settings.m_channelBandwidth / 2; + qint64 stop2 = frequency + m_settings.m_channelBandwidth / 2; + if ( ((start2 >= start1) && (start2 <= stop1)) + || ((stop2 >= start1) && (stop2 <= stop1)) + ) { if (marker.m_bandwidth == (unsigned)m_settings.m_channelBandwidth) { // Exact match @@ -816,6 +822,13 @@ void FreqScannerGUI::updateAnnotations() } } +void FreqScannerGUI::setAllEnabled(bool enable) +{ + for (int i = 0; i < ui->table->rowCount(); i++) { + ui->table->item(i, COL_ENABLE)->setCheckState(enable ? Qt::Checked : Qt::Unchecked); + } +} + void FreqScannerGUI::table_customContextMenuRequested(QPoint pos) { QTableWidgetItem* item = ui->table->itemAt(pos); @@ -836,6 +849,24 @@ void FreqScannerGUI::table_customContextMenuRequested(QPoint pos) }); tableContextMenu->addAction(copyAction); + tableContextMenu->addSeparator(); + + // Enable all + + QAction* enableAllAction = new QAction("Enable all", tableContextMenu); + connect(enableAllAction, &QAction::triggered, this, [this]()->void { + setAllEnabled(true); + }); + tableContextMenu->addAction(enableAllAction); + + // Disable all + + QAction* disableAllAction = new QAction("Disable all", tableContextMenu); + connect(disableAllAction, &QAction::triggered, this, [this]()->void { + setAllEnabled(false); + }); + tableContextMenu->addAction(disableAllAction); + // Remove selected rows QAction* removeAction = new QAction("Remove", tableContextMenu); diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h index 34979d1bf..9e0ec56c0 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.h +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -96,6 +96,7 @@ private: void updateAnnotation(int row); void updateAnnotations(); void updateChannelsList(const QList& channels); + void setAllEnabled(bool enable); void leaveEvent(QEvent*); void enterEvent(EnterEventType*); diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md index 737c8325b..d5126484f 100644 --- a/plugins/channelrx/freqscanner/readme.md +++ b/plugins/channelrx/freqscanner/readme.md @@ -4,6 +4,8 @@ This plugin can be used to scan a range of frequencies looking for a transmission and then tune another channel (such as an AM or DSD Demod) to that frequency. +[Tutorial Video](https://www.youtube.com/watch?v=IpKP3t4Bmmg) +

Interface

The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) @@ -101,6 +103,8 @@ When an active frequency is found after a scan, the corresponding row in the tab Right clicking on a cell will display a popup menu: - Copy contents of cell to clipboard. +- Enable all rows. +- Disable all rows. - Remove selected rows. - Tune selected channel (1) to the frequency in the row clicked on. From 3e147ec804bbaa9d1884951dd9a3bfadf5596b77 Mon Sep 17 00:00:00 2001 From: srcejon Date: Sun, 22 Oct 2023 10:03:04 +0100 Subject: [PATCH 20/23] Avoid using channel guard bands, due to possible aliasing from half-band filters --- plugins/channelrx/freqscanner/freqscanner.cpp | 46 ++++++++++--------- plugins/channelrx/freqscanner/freqscanner.h | 1 + .../channelrx/freqscanner/freqscannersink.cpp | 13 ++---- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index 9e5aef364..31a45e8ef 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -292,16 +292,21 @@ void FreqScanner::stopScan() } } +void FreqScanner::setDeviceCenterFrequency(qint64 frequency) +{ + // For RTL SDR, ChannelWebAPIUtils::setCenterFrequency takes ~50ms, which means tuneTime can be 0 + if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), frequency)) { + qWarning() << "Freq Scanner failed to set frequency" << frequency; + } + m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); +} + void FreqScanner::initScan() { ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, true); - if (m_centerFrequency != m_stepStartFrequency) - { - if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), m_stepStartFrequency)) { - qWarning() << "Freq Scanner failed to set frequency" << m_stepStartFrequency; - } - m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); + if (m_centerFrequency != m_stepStartFrequency) { + setDeviceCenterFrequency(m_stepStartFrequency); } m_scanResults.clear(); @@ -340,13 +345,14 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< int binsPerChannel; calcScannerSampleRate(m_settings.m_channelBandwidth, m_basebandSampleRate, m_scannerSampleRate, fftSize, binsPerChannel); - // Align first frequency so we cover as many channels as possible, while avoiding DC bin - m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_settings.m_channelBandwidth + m_settings.m_channelBandwidth / 2; + // Align first frequency so we cover as many channels as possible, while channel guard band + // Can we adjust this to avoid DC bin? + m_stepStartFrequency = frequencies.front() + m_scannerSampleRate / 2 - m_scannerSampleRate * 0.125f; m_stepStopFrequency = frequencies.back(); - // If all frequencies fit within bandwidth, we can have the first frequency more central + // If all frequencies fit within usable bandwidth, we can have the first frequency more central int totalBW = frequencies.back() - frequencies.front() + 2 * m_settings.m_channelBandwidth; - if (totalBW < m_scannerSampleRate) + if (totalBW < m_scannerSampleRate * 0.75f) { int spareBWEachSide = (m_scannerSampleRate - totalBW) / 2; int spareChannelsEachSide = spareBWEachSide / m_settings.m_channelBandwidth; @@ -370,16 +376,17 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< bool complete = false; // Have all frequencies been scanned? bool freqInRange = false; qint64 nextCenterFrequency = m_centerFrequency; + float usableBW = m_scannerSampleRate * 0.75f; do { - if (nextCenterFrequency + m_scannerSampleRate / 2 > m_stepStopFrequency) + if (nextCenterFrequency + usableBW / 2 > m_stepStopFrequency) { nextCenterFrequency = m_stepStartFrequency; complete = true; } else { - nextCenterFrequency += m_scannerSampleRate; + nextCenterFrequency += usableBW; complete = false; } @@ -387,8 +394,8 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< for (int i = 0; i < m_settings.m_frequencies.size(); i++) { if (m_settings.m_enabled[i] - && (m_settings.m_frequencies[i] >= nextCenterFrequency - m_scannerSampleRate / 2) - && (m_settings.m_frequencies[i] < nextCenterFrequency + m_scannerSampleRate / 2)) + && (m_settings.m_frequencies[i] >= nextCenterFrequency - usableBW / 2) + && (m_settings.m_frequencies[i] < nextCenterFrequency + usableBW / 2)) { freqInRange = true; break; @@ -457,7 +464,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< // Tune device/channel to frequency int offset; - if ((frequency < m_centerFrequency - m_scannerSampleRate / 2) || (frequency >= m_centerFrequency + m_scannerSampleRate / 2)) + if ((frequency < m_centerFrequency - usableBW / 2) || (frequency >= m_centerFrequency + usableBW / 2)) { nextCenterFrequency = frequency; offset = 0; @@ -519,13 +526,8 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< } } - if (nextCenterFrequency != m_centerFrequency) - { - // For RTL SDR, setCenterFrequency takes ~50ms, which means tuneTime can be 0 - if (!ChannelWebAPIUtils::setCenterFrequency(getDeviceSetIndex(), nextCenterFrequency)) { - qWarning() << "Freq Scanner failed to set frequency" << nextCenterFrequency; - } - m_minFFTStartTime = QDateTime::currentDateTime().addMSecs(m_settings.m_tuneTime); + if (nextCenterFrequency != m_centerFrequency) { + setDeviceCenterFrequency(nextCenterFrequency); } if (complete) { diff --git a/plugins/channelrx/freqscanner/freqscanner.h b/plugins/channelrx/freqscanner/freqscanner.h index 80a53b79a..cd65b1fcf 100644 --- a/plugins/channelrx/freqscanner/freqscanner.h +++ b/plugins/channelrx/freqscanner/freqscanner.h @@ -405,6 +405,7 @@ private: void stopScan(); void initScan(); void processScanResults(const QDateTime& fftStartTime, const QList& results); + void setDeviceCenterFrequency(qint64 frequency); private slots: void networkManagerFinished(QNetworkReply *reply); diff --git a/plugins/channelrx/freqscanner/freqscannersink.cpp b/plugins/channelrx/freqscanner/freqscannersink.cpp index ab1408967..79488e0cc 100644 --- a/plugins/channelrx/freqscanner/freqscannersink.cpp +++ b/plugins/channelrx/freqscanner/freqscannersink.cpp @@ -58,11 +58,7 @@ void FreqScannerSink::feed(const SampleVector::const_iterator& begin, const Samp Complex c(it->real(), it->imag()); c *= m_nco.nextIQ(); - if (m_interpolatorDistance == 1.0f) // Don't call decimate, as we don't want filter applied if possible - { - processOneSample(c); - } - else if (m_interpolatorDistance < 1.0f) // interpolate + if (m_interpolatorDistance < 1.0f) // interpolate { while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { @@ -70,7 +66,7 @@ void FreqScannerSink::feed(const SampleVector::const_iterator& begin, const Samp m_interpolatorDistanceRemain += m_interpolatorDistance; } } - else // decimate + else // decimate (and filter) { if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) { @@ -121,7 +117,8 @@ void FreqScannerSink::processOneSample(Complex &ci) qint64 diff = frequency - startFrequency; float binBW = m_scannerSampleRate / (float)m_fftSize; - if ((diff < m_scannerSampleRate) && (diff >= 0)) + // Ignore results in uppper and lower 12.5%, as there may be aliasing here from half-band filters + if ((diff < m_scannerSampleRate * 0.875f) && (diff >= m_scannerSampleRate * 0.125f)) { int bin = std::round(diff / binBW); @@ -210,7 +207,7 @@ void FreqScannerSink::applyChannelSettings(int channelSampleRate, int channelFre if ((m_channelSampleRate != channelSampleRate) || (m_scannerSampleRate != scannerSampleRate) || force) { - m_interpolator.create(16, channelSampleRate, scannerSampleRate / 2.0); // Highest cutoff, so we don't attentuate first/last channel + m_interpolator.create(16, channelSampleRate, scannerSampleRate / 2.2); // Filter potential aliasing resulting from half-band filters m_interpolatorDistance = (Real) channelSampleRate / (Real)scannerSampleRate; m_interpolatorDistanceRemain = m_interpolatorDistance; } From 1b392ee53ceb771449a338ba1c79542d570aec43 Mon Sep 17 00:00:00 2001 From: srcejon Date: Sun, 22 Oct 2023 10:25:08 +0100 Subject: [PATCH 21/23] Fix warnings --- sdrgui/gui/frequencydelegate.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sdrgui/gui/frequencydelegate.cpp b/sdrgui/gui/frequencydelegate.cpp index cb4a2b470..d7ab73a0c 100644 --- a/sdrgui/gui/frequencydelegate.cpp +++ b/sdrgui/gui/frequencydelegate.cpp @@ -111,6 +111,9 @@ QString FrequencyDelegate::displayText(const QVariant &value, const QLocale &loc QWidget* FrequencyDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { + (void) option; + (void) index; + QLineEdit* editor = new QLineEdit(parent); QIntValidator* validator = new QIntValidator(); validator->setBottom(0); @@ -134,5 +137,6 @@ void FrequencyDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, void FrequencyDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { + (void) index; editor->setGeometry(option.rect); } From 3b0512bd29356474547d74fa0d7775217ed37944 Mon Sep 17 00:00:00 2001 From: srcejon Date: Sun, 22 Oct 2023 10:25:40 +0100 Subject: [PATCH 22/23] Fix crash if some columns in .csv file is missing --- sdrgui/gui/spectrummarkersdialog.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/sdrgui/gui/spectrummarkersdialog.cpp b/sdrgui/gui/spectrummarkersdialog.cpp index 25a765fcd..89fdbfc88 100644 --- a/sdrgui/gui/spectrummarkersdialog.cpp +++ b/sdrgui/gui/spectrummarkersdialog.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "gui/dialpopup.h" #include "util/db.h" @@ -772,15 +773,22 @@ void SpectrumMarkersDialog::on_aMarkersImport_clicked() while (CSV::readRow(in, &cols)) { - m_annotationMarkers.push_back(SpectrumAnnotationMarker()); - m_annotationMarkers.back().m_startFrequency = cols[startCol].toLongLong(); - m_annotationMarkers.back().m_bandwidth = cols[widthCol].toUInt(); - m_annotationMarkers.back().m_text = cols[textCol]; - m_annotationMarkers.back().m_show = (SpectrumAnnotationMarker::ShowState) cols[showCol].toInt(); - int r = cols[redCol].toInt(); - int g = cols[greenCol].toInt(); - int b = cols[blueCol].toInt(); - m_annotationMarkers.back().m_markerColor = QColor(r, g, b); + if (cols.size() >= 7) + { + m_annotationMarkers.push_back(SpectrumAnnotationMarker()); + m_annotationMarkers.back().m_startFrequency = cols[startCol].toLongLong(); + m_annotationMarkers.back().m_bandwidth = cols[widthCol].toUInt(); + m_annotationMarkers.back().m_text = cols[textCol]; + m_annotationMarkers.back().m_show = (SpectrumAnnotationMarker::ShowState) cols[showCol].toInt(); + int r = cols[redCol].toInt(); + int g = cols[greenCol].toInt(); + int b = cols[blueCol].toInt(); + m_annotationMarkers.back().m_markerColor = QColor(r, g, b); + } + else + { + qWarning() << "SpectrumMarkersDialog::on_aMarkersImport_clicked: Missing data in " << cols; + } } m_annotationMarkerIndex = 0; From 842d90f2687b5ae6b5849c756364d32d97695b96 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Sun, 22 Oct 2023 14:27:06 +0100 Subject: [PATCH 23/23] Update to MacOS 12, as brew complains about 11 being unsupported --- .github/workflows/sdrangel.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sdrangel.yml b/.github/workflows/sdrangel.yml index a26c2e792..a191081fa 100644 --- a/.github/workflows/sdrangel.yml +++ b/.github/workflows/sdrangel.yml @@ -100,7 +100,7 @@ jobs: files: ${{ github.workspace }}/build/sdrangel-${{ steps.get_version.outputs.version }}-win64.exe build_mac: - runs-on: macos-11 + runs-on: macos-12 steps: - uses: actions/checkout@v3 with: