diff --git a/doc/img/FreqScanner_plugin.png b/doc/img/FreqScanner_plugin.png index 6bc7574e6..770401745 100644 Binary files a/doc/img/FreqScanner_plugin.png and b/doc/img/FreqScanner_plugin.png differ diff --git a/doc/img/FreqScanner_plugin.xcf b/doc/img/FreqScanner_plugin.xcf new file mode 100644 index 000000000..662488079 Binary files /dev/null and b/doc/img/FreqScanner_plugin.xcf differ diff --git a/plugins/channelrx/freqscanner/freqscanner.cpp b/plugins/channelrx/freqscanner/freqscanner.cpp index d320f5c67..f7d9cedc9 100644 --- a/plugins/channelrx/freqscanner/freqscanner.cpp +++ b/plugins/channelrx/freqscanner/freqscanner.cpp @@ -526,7 +526,7 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList< applyChannelSetting(channel); // Tune the channel - ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset); + ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset + m_settings.m_channelShift); // Unmute the channel ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, false); @@ -922,6 +922,9 @@ void FreqScanner::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("channelBandwidth")) { settings.m_channelBandwidth = response.getFreqScannerSettings()->getChannelBandwidth(); } + if (channelSettingsKeys.contains("channelShift")) { + settings.m_channelShift = response.getFreqScannerSettings()->getChannelShift(); + } if (channelSettingsKeys.contains("threshold")) { settings.m_threshold = response.getFreqScannerSettings()->getThreshold(); } @@ -1019,6 +1022,7 @@ void FreqScanner::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& r { response.getFreqScannerSettings()->setChannelFrequencyOffset(settings.m_channelFrequencyOffset); response.getFreqScannerSettings()->setChannelBandwidth(settings.m_channelBandwidth); + response.getFreqScannerSettings()->setChannelShift(settings.m_channelShift); response.getFreqScannerSettings()->setThreshold(settings.m_threshold); QList *frequencies = createFrequencyList(settings); @@ -1140,6 +1144,9 @@ void FreqScanner::webapiFormatChannelSettings( if (channelSettingsKeys.contains("channelBandwidth") || force) { swgFreqScannerSettings->setChannelBandwidth(settings.m_channelBandwidth); } + if (channelSettingsKeys.contains("channelShift") || force) { + swgFreqScannerSettings->setChannelShift(settings.m_channelShift); + } if (channelSettingsKeys.contains("threshold") || force) { swgFreqScannerSettings->setThreshold(settings.m_threshold); } diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index b0167df34..2dc1a39c1 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -327,6 +327,12 @@ void FreqScannerGUI::on_channelBandwidth_changed(qint64 value) applySetting("channelBandwidth"); } +void FreqScannerGUI::on_channelShift_changed(qint64 value) +{ + m_settings.m_channelShift = value; + applySetting("channelShift"); +} + void FreqScannerGUI::on_scanTime_valueChanged(int value) { ui->scanTimeText->setText(QString("%1 s").arg(value / 10.0, 0, 'f', 1)); @@ -484,6 +490,9 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->channelBandwidth->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow)); ui->channelBandwidth->setValueRange(true, 8, 0, 9999999); + ui->channelShift->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->channelShift->setValueRange(false, 5, -50000, 50000); + m_channelMarker.setColor(Qt::yellow); m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); m_channelMarker.setTitle("Frequency Scanner"); @@ -589,6 +598,7 @@ void FreqScannerGUI::displaySettings() } ui->deltaFrequency->setValue(m_settings.m_channelFrequencyOffset); ui->channelBandwidth->setValue(m_settings.m_channelBandwidth); + ui->channelShift->setValue(m_settings.m_channelShift); 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); @@ -1217,6 +1227,7 @@ 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->channelShift, &ValueDialZ::changed, this, &FreqScannerGUI::on_channelShift_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); diff --git a/plugins/channelrx/freqscanner/freqscannergui.h b/plugins/channelrx/freqscanner/freqscannergui.h index cc89175c8..cafa7e0e9 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.h +++ b/plugins/channelrx/freqscanner/freqscannergui.h @@ -126,6 +126,7 @@ private slots: void on_channels_currentIndexChanged(int index); void on_deltaFrequency_changed(qint64 value); void on_channelBandwidth_changed(qint64 index); + void on_channelShift_changed(qint64 index); void on_scanTime_valueChanged(int value); void on_retransmitTime_valueChanged(int value); void on_tuneTime_valueChanged(int value); diff --git a/plugins/channelrx/freqscanner/freqscannergui.ui b/plugins/channelrx/freqscanner/freqscannergui.ui index d2a730175..bb057abaa 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.ui +++ b/plugins/channelrx/freqscanner/freqscannergui.ui @@ -6,8 +6,8 @@ 0 0 - 419 - 431 + 516 + 423 @@ -29,7 +29,7 @@ - Qt::FocusPolicy::StrongFocus + Qt::NoFocus Frequency Scanner @@ -39,7 +39,7 @@ 0 0 - 411 + 511 411 @@ -102,7 +102,7 @@ - Qt::Orientation::Vertical + Qt::Vertical @@ -143,7 +143,7 @@ PointingHandCursor - Qt::FocusPolicy::StrongFocus + Qt::NoFocus Minimum demod shift frequency from center in Hz @@ -160,15 +160,12 @@ - Qt::Orientation::Vertical + Qt::Vertical - - Qt::Orientation::Horizontal - 40 @@ -191,14 +188,11 @@ Active frequency power - Qt::LayoutDirection::RightToLeft + Qt::LeftToRight -120.0 - - Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - @@ -215,7 +209,7 @@ - Qt::Orientation::Horizontal + Qt::Vertical @@ -265,9 +259,6 @@ - - Qt::Orientation::Horizontal - 40 @@ -279,7 +270,7 @@ - Qt::Orientation::Vertical + Qt::Vertical @@ -328,7 +319,7 @@ - Qt::Orientation::Vertical + Qt::Vertical @@ -377,7 +368,7 @@ - Qt::Orientation::Vertical + Qt::Vertical @@ -428,7 +419,7 @@ - Qt::Orientation::Horizontal + Qt::Vertical @@ -465,7 +456,7 @@ PointingHandCursor - Qt::FocusPolicy::StrongFocus + Qt::NoFocus Channel bandwidth @@ -480,10 +471,61 @@ - - - Qt::Orientation::Horizontal + + + + 40 + 0 + + + Shift + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::NoFocus + + + Frequency shift applied from scan frequency (for SSB) + + + + + + + Hz + + + + + 40 @@ -551,7 +593,7 @@ - Qt::Orientation::Horizontal + Qt::Vertical @@ -626,7 +668,7 @@ - Qt::Orientation::Horizontal + Qt::Vertical @@ -821,9 +863,6 @@ Leave blank for no adjustment - - Qt::Orientation::Horizontal - 40 diff --git a/plugins/channelrx/freqscanner/freqscannersettings.cpp b/plugins/channelrx/freqscanner/freqscannersettings.cpp index e9751eec8..ea7430cba 100644 --- a/plugins/channelrx/freqscanner/freqscannersettings.cpp +++ b/plugins/channelrx/freqscanner/freqscannersettings.cpp @@ -38,6 +38,7 @@ void FreqScannerSettings::resetToDefaults() { m_inputFrequencyOffset = 0; m_channelBandwidth = 25000; + m_channelShift = 0; m_channelFrequencyOffset = 25000; m_threshold = -60.0f; m_channel = ""; @@ -83,6 +84,7 @@ QByteArray FreqScannerSettings::serialize() const s.writeS32(13, (int)m_measurement); s.writeS32(14, (int)m_mode); s.writeList(15, m_frequencySettings); + s.writeS32(16, m_channelShift); s.writeList(20, m_columnIndexes); s.writeList(21, m_columnSizes); @@ -136,6 +138,8 @@ bool FreqScannerSettings::deserialize(const QByteArray& data) d.readS32(13, (int*)&m_measurement, (int)PEAK); d.readS32(14, (int*)&m_mode, (int)CONTINUOUS); d.readList(15, &m_frequencySettings); + d.readS32(16, &m_channelShift, 0); + if (m_frequencySettings.size() == 0) { // Try reading old settings @@ -212,6 +216,9 @@ void FreqScannerSettings::applySettings(const QStringList& settingsKeys, const F if (settingsKeys.contains("channelFrequencyOffset")) { m_channelFrequencyOffset = settings.m_channelFrequencyOffset; } + if (settingsKeys.contains("channelShift")) { + m_channelShift = settings.m_channelShift; + } if (settingsKeys.contains("threshold")) { m_threshold = settings.m_threshold; } @@ -290,6 +297,9 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo if (settingsKeys.contains("channelFrequencyOffset") || force) { ostr << " m_channelFrequencyOffset: " << m_channelFrequencyOffset; } + if (settingsKeys.contains("channelShift") || force) { + ostr << " m_channelShift: " << m_channelShift; + } if (settingsKeys.contains("threshold") || force) { ostr << " m_threshold: " << m_threshold; } @@ -471,4 +481,3 @@ FreqScannerSettings::FrequencySettings *FreqScannerSettings::getFrequencySetting } return nullptr; } - diff --git a/plugins/channelrx/freqscanner/freqscannersettings.h b/plugins/channelrx/freqscanner/freqscannersettings.h index 4d6784029..9a04b7103 100644 --- a/plugins/channelrx/freqscanner/freqscannersettings.h +++ b/plugins/channelrx/freqscanner/freqscannersettings.h @@ -47,6 +47,7 @@ struct FreqScannerSettings qint32 m_inputFrequencyOffset; //!< Not modifiable in GUI qint32 m_channelBandwidth; //!< Channel bandwidth qint32 m_channelFrequencyOffset;//!< Minimum DC offset of tuned channel + qint32 m_channelShift; //!< Channel frequency shift Real m_threshold; //!< Power threshold in dB QString m_channel; //!< Channel (E.g: R1:4) to tune to active frequency QList m_frequencySettings; //!< Frequencies to scan and corresponding settings diff --git a/plugins/channelrx/freqscanner/readme.md b/plugins/channelrx/freqscanner/readme.md index b8c3565e5..2a83693f8 100644 --- a/plugins/channelrx/freqscanner/readme.md +++ b/plugins/channelrx/freqscanner/readme.md @@ -9,9 +9,9 @@ This plugin can be used to scan a range of frequencies looking for a transmissio With the Run Mode (11) set to Multiplex, it can also repeatedly cycle through frequencies listening for a fixed period of time. This can be used, for example, to receive both AIS and ADS-B data via a single SDR. -Note that when scanning, the device centre frequency will often not be set exactly to the frequencies you enter. +Note that when scanning, the device centre frequency will often not be set exactly to the frequencies you enter. The Frequency Scanner will typically try to set the device centre frequency in order to -scan as many frequencies simultanously as possible, and also avoid having a DC offset within the bandwidth of a scanned frequency. +scan as many frequencies simultanously as possible, and also avoid having a DC offset within the bandwidth of a scanned frequency. The demodulator channel's frequency offset option will be used so that it receives at the specified frequency.

Interface

@@ -59,14 +59,18 @@ t_rx: When Run Mode (11) is Multiplex, specifies the time in seconds the channel This specifies the bandwidth of the channels to be scanned. -

9: Pri - Priority

+

9: Channel shift

+ +This shift is applied to the controlled channel from the scanned center frequency. This is useful for SSB or CW or generally whenever the signal of interest is only on one side of the controlled channel center frequency. + +

10: 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

+

11: Meas - Power Measurement

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). @@ -74,11 +78,11 @@ The first and last bins are excluded from the measurement (to reduce spectral le - 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

+

12: Run Mode

Specifies the run mode: @@ -87,18 +91,18 @@ Specifies the run mode: - 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). - Multiplex: Frequencies will be stepped through sequentially and repeatedly, with the channel (1) being tuned for the time specified by t_rx (7). -

12: Start/Stop Scanning

+

13: Start/Stop Scanning

Press this button to start or stop scanning. -

13: Status Text

+

14: 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

+

15: Frequency Table

The frequency table contains the list of frequencies to be scanned, along with results of a scan. The columns are: @@ -123,32 +127,32 @@ Right clicking on a cell will display a popup menu: - Remove selected rows. - Tune selected channel (1) to the frequency in the row clicked on. -

15: Add

+

16: Add

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

16: Add Range

+

17: 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

+

18: Remove

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

18: Remove Inactive

+

19: Remove Inactive

Removes all rows with Active Count of 0. -

19: Up

+

20: Up

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

20: Down

+

21: Down

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

21: Import Frequencies from .csv

+

22: Import Frequencies from .csv

Imports frequencies from a .csv file. @@ -156,11 +160,11 @@ The expected column names are "Freq (Hz)", "Enable", "Notes", "Channel", "Ch BW Annotations are not included. These should be imported via the Spectrum Markers dialog. -

22: Export Frequencies to .csv

+

23: Export Frequencies to .csv

Exports frequencies to a .csv file. Note that annotations are not included. These should be exported via the Spectrum Markers dialog. -

23: Clear Active Count

+

24: Clear Active Count

Press to reset the value in the Active Count column to 0 for all rows. diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 6020c9594..ff970f4ca 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -7497,6 +7497,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "channel RF bandwidth in Hz" }, + "channelShift" : { + "type" : "integer", + "description" : "controlled channel frequency shift in Hz" + }, "channelFrequencyOffset" : { "type" : "integer", "description" : "channel center frequency shift from baseband center in Hz" @@ -59978,7 +59982,7 @@ except ApiException as e:
- Generated 2026-01-27T20:31:15.585+01:00 + Generated 2026-02-07T23:22:00.482+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/FreqScanner.yaml b/sdrbase/resources/webapi/doc/swagger/include/FreqScanner.yaml index 74a9ea49e..b81b2adc8 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/FreqScanner.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/FreqScanner.yaml @@ -4,6 +4,9 @@ FreqScannerSettings: channelBandwidth: description: channel RF bandwidth in Hz type: integer + channelShift: + description: controlled channel frequency shift in Hz + type: integer channelFrequencyOffset: description: channel center frequency shift from baseband center in Hz type: integer diff --git a/swagger/sdrangel/api/swagger/include/FreqScanner.yaml b/swagger/sdrangel/api/swagger/include/FreqScanner.yaml index bb8c76ebf..c6d4110ed 100644 --- a/swagger/sdrangel/api/swagger/include/FreqScanner.yaml +++ b/swagger/sdrangel/api/swagger/include/FreqScanner.yaml @@ -4,6 +4,9 @@ FreqScannerSettings: channelBandwidth: description: channel RF bandwidth in Hz type: integer + channelShift: + description: controlled channel frequency shift in Hz + type: integer channelFrequencyOffset: description: channel center frequency shift from baseband center in Hz type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 6020c9594..ff970f4ca 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -7497,6 +7497,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "channel RF bandwidth in Hz" }, + "channelShift" : { + "type" : "integer", + "description" : "controlled channel frequency shift in Hz" + }, "channelFrequencyOffset" : { "type" : "integer", "description" : "channel center frequency shift from baseband center in Hz" @@ -59978,7 +59982,7 @@ except ApiException as e:
- Generated 2026-01-27T20:31:15.585+01:00 + Generated 2026-02-07T23:22:00.482+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp index 212bb1829..cec0454d9 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.cpp @@ -30,6 +30,8 @@ SWGFreqScannerSettings::SWGFreqScannerSettings(QString* json) { SWGFreqScannerSettings::SWGFreqScannerSettings() { channel_bandwidth = 0; m_channel_bandwidth_isSet = false; + channel_shift = 0; + m_channel_shift_isSet = false; channel_frequency_offset = 0; m_channel_frequency_offset_isSet = false; threshold = 0.0f; @@ -80,6 +82,8 @@ void SWGFreqScannerSettings::init() { channel_bandwidth = 0; m_channel_bandwidth_isSet = false; + channel_shift = 0; + m_channel_shift_isSet = false; channel_frequency_offset = 0; m_channel_frequency_offset_isSet = false; threshold = 0.0f; @@ -127,6 +131,7 @@ SWGFreqScannerSettings::cleanup() { + if(frequencies != nullptr) { auto arr = frequencies; for(auto o: *arr) { @@ -176,6 +181,8 @@ void SWGFreqScannerSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&channel_bandwidth, pJson["channelBandwidth"], "qint32", ""); + ::SWGSDRangel::setValue(&channel_shift, pJson["channelShift"], "qint32", ""); + ::SWGSDRangel::setValue(&channel_frequency_offset, pJson["channelFrequencyOffset"], "qint32", ""); ::SWGSDRangel::setValue(&threshold, pJson["threshold"], "float", ""); @@ -235,6 +242,9 @@ SWGFreqScannerSettings::asJsonObject() { if(m_channel_bandwidth_isSet){ obj->insert("channelBandwidth", QJsonValue(channel_bandwidth)); } + if(m_channel_shift_isSet){ + obj->insert("channelShift", QJsonValue(channel_shift)); + } if(m_channel_frequency_offset_isSet){ obj->insert("channelFrequencyOffset", QJsonValue(channel_frequency_offset)); } @@ -309,6 +319,16 @@ SWGFreqScannerSettings::setChannelBandwidth(qint32 channel_bandwidth) { this->m_channel_bandwidth_isSet = true; } +qint32 +SWGFreqScannerSettings::getChannelShift() { + return channel_shift; +} +void +SWGFreqScannerSettings::setChannelShift(qint32 channel_shift) { + this->channel_shift = channel_shift; + this->m_channel_shift_isSet = true; +} + qint32 SWGFreqScannerSettings::getChannelFrequencyOffset() { return channel_frequency_offset; @@ -517,6 +537,9 @@ SWGFreqScannerSettings::isSet(){ if(m_channel_bandwidth_isSet){ isObjectUpdated = true; break; } + if(m_channel_shift_isSet){ + isObjectUpdated = true; break; + } if(m_channel_frequency_offset_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h index f9bc3a81f..34b0de55e 100644 --- a/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGFreqScannerSettings.h @@ -49,6 +49,9 @@ public: qint32 getChannelBandwidth(); void setChannelBandwidth(qint32 channel_bandwidth); + qint32 getChannelShift(); + void setChannelShift(qint32 channel_shift); + qint32 getChannelFrequencyOffset(); void setChannelFrequencyOffset(qint32 channel_frequency_offset); @@ -116,6 +119,9 @@ private: qint32 channel_bandwidth; bool m_channel_bandwidth_isSet; + qint32 channel_shift; + bool m_channel_shift_isSet; + qint32 channel_frequency_offset; bool m_channel_frequency_offset_isSet;