Merge pull request #1905 from srcejon/freq_scanner

Frequency Scanner - Add per-frequency settings
pull/1906/head
Edouard Griffiths 2023-12-01 06:40:47 +01:00 zatwierdzone przez GitHub
commit 9307d7ad59
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
27 zmienionych plików z 1019 dodań i 159 usunięć

Wyświetl plik

@ -249,6 +249,7 @@ bool FreqScanner::handleMessage(const Message& cmd)
}
else if (MsgStartScan::match(cmd))
{
muteAll();
startScan();
return true;
@ -302,6 +303,9 @@ void FreqScanner::setDeviceCenterFrequency(qint64 frequency)
void FreqScanner::initScan()
{
if (m_scanChannelIndex < 0) {
applyChannelSetting(m_settings.m_channel);
}
ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, true);
if (m_centerFrequency != m_stepStartFrequency) {
@ -319,7 +323,6 @@ void FreqScanner::initScan()
void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<MsgScanResult::ScanResult>& results)
{
switch (m_state)
{
case IDLE:
@ -329,10 +332,10 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
{
// Create ordered list of frequencies to scan
QList<qint64> frequencies;
for (int i = 0; i < m_settings.m_frequencies.size(); i++)
for (int i = 0; i < m_settings.m_frequencySettings.size(); i++)
{
if (m_settings.m_enabled[i]) {
frequencies.append(m_settings.m_frequencies[i]);
if (m_settings.m_frequencySettings[i].m_enabled) {
frequencies.append(m_settings.m_frequencySettings[i].m_frequency);
}
}
std::sort(frequencies.begin(), frequencies.end());
@ -390,11 +393,11 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
}
// Are any frequencies in this new range?
for (int i = 0; i < m_settings.m_frequencies.size(); i++)
for (int i = 0; i < m_settings.m_frequencySettings.size(); i++)
{
if (m_settings.m_enabled[i]
&& (m_settings.m_frequencies[i] >= nextCenterFrequency - usableBW / 2)
&& (m_settings.m_frequencies[i] < nextCenterFrequency + usableBW / 2))
if (m_settings.m_frequencySettings[i].m_enabled
&& (m_settings.m_frequencySettings[i].m_frequency >= nextCenterFrequency - usableBW / 2)
&& (m_settings.m_frequencySettings[i].m_frequency < nextCenterFrequency + usableBW / 2))
{
freqInRange = true;
break;
@ -416,51 +419,52 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
m_guiMessageQueue->push(msg);
}
int frequency = m_scanResults[0].m_frequency;
Real maxPower = m_scanResults[0].m_power;
int frequency = -1;
FreqScannerSettings::FrequencySettings *frequencySettings = nullptr;
FreqScannerSettings::FrequencySettings *activeFrequencySettings = nullptr;
if (m_settings.m_priority == FreqScannerSettings::MAX_POWER)
{
// Find frequency with max power
for (int i = 1; i < m_scanResults.size(); i++)
Real maxPower = -200.0f;
// Find frequency with max power that exceeds thresholds
for (int i = 0; i < m_scanResults.size(); i++)
{
if (m_scanResults[i].m_power > maxPower)
frequencySettings = m_settings.getFrequencySettings(m_scanResults[i].m_frequency);
Real threshold = m_settings.getThreshold(frequencySettings);
if (m_scanResults[i].m_power >= threshold)
{
frequency = m_scanResults[i].m_frequency;
maxPower = m_scanResults[i].m_power;
if (!activeFrequencySettings || (m_scanResults[i].m_power > maxPower))
{
frequency = m_scanResults[i].m_frequency;
maxPower = m_scanResults[i].m_power;
activeFrequencySettings = frequencySettings;
}
}
}
}
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++)
{
for (int i = 0; i < m_scanResults.size(); i++)
frequencySettings = m_settings.getFrequencySettings(m_scanResults[i].m_frequency);
Real threshold = m_settings.getThreshold(frequencySettings);
if (m_scanResults[i].m_power >= threshold)
{
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;
}
}
frequency = m_scanResults[i].m_frequency;
activeFrequencySettings = frequencySettings;
break;
}
}
found_freq: ;
}
if (m_settings.m_mode != FreqScannerSettings::SCAN_ONLY)
{
// Is power above threshold
if (maxPower >= m_settings.m_threshold)
// Were any frequencies found to be active?
//if (maxPower >= m_settings.m_threshold)
if (activeFrequencySettings)
{
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgReportActiveFrequency::create(frequency));
}
// Tune device/channel to frequency
int offset;
if ((frequency < m_centerFrequency - usableBW / 2) || (frequency >= m_centerFrequency + usableBW / 2))
@ -494,11 +498,28 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
//qDebug() << "Tuning to active freq:" << frequency << "m_centerFrequency" << m_centerFrequency << "nextCenterFrequency" << nextCenterFrequency << "offset: " << offset << "deviceset: R" << m_scanDeviceSetIndex << ":" << m_scanChannelIndex;
QString channel = m_settings.m_channel;
if (!activeFrequencySettings->m_channel.isEmpty()) {
channel = activeFrequencySettings->m_channel;
}
applyChannelSetting(channel);
// Tune the channel
ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset);
// Unmute the channel
ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, false);
// Apply squelch
if (!activeFrequencySettings->m_squelch.isEmpty())
{
bool ok;
Real squelch = activeFrequencySettings->m_squelch.toFloat(&ok);
if (ok) {
ChannelWebAPIUtils::patchChannelSetting(m_scanDeviceSetIndex, m_scanChannelIndex, "squelch", squelch);
}
}
m_activeFrequency = frequency;
if (m_settings.m_mode == FreqScannerSettings::SINGLE)
@ -514,11 +535,16 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
// Wait for transmission to finish
m_state = WAIT_FOR_END_TX;
}
// Becareful to only do this at the end here, as it can recursively call handleMessage with new settings
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgReportActiveFrequency::create(m_activeFrequency));
}
}
else
{
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgStatus::create(QString("Scanning: No active channels - Max power %1 dB").arg(maxPower, 0, 'f', 1)));
m_guiMessageQueue->push(MsgStatus::create("Scanning..."));
}
}
}
@ -545,7 +571,9 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
}
// Wait until power drops below threshold
if (results[i].m_power < m_settings.m_threshold)
FreqScannerSettings::FrequencySettings *frequencySettings = m_settings.getFrequencySettings(m_activeFrequency);
Real threshold = m_settings.getThreshold(frequencySettings);
if (results[i].m_power < threshold)
{
m_timeoutTimer.setSingleShot(true);
m_timeoutTimer.start((int)(m_settings.m_retransmitTime * 1000.0));
@ -566,7 +594,9 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
}
// Check if power has returned to being above threshold
if (results[i].m_power >= m_settings.m_threshold)
FreqScannerSettings::FrequencySettings *frequencySettings = m_settings.getFrequencySettings(m_activeFrequency);
Real threshold = m_settings.getThreshold(frequencySettings);
if (results[i].m_power >= threshold)
{
m_timeoutTimer.stop();
m_state = WAIT_FOR_END_TX;
@ -598,6 +628,9 @@ void FreqScanner::calcScannerSampleRate(int channelBW, int basebandSampleRate, i
// But ensure we have several bins per channel
// Adjust sample rate, to ensure we don't get massive FFT size
scannerSampleRate = basebandSampleRate;
if (scannerSampleRate < channelBW) {
channelBW = scannerSampleRate; // Prevent divide by 0
}
while (fftSize / (scannerSampleRate / channelBW) < minBinsPerChannel)
{
if (fftSize == maxFFTSize) {
@ -623,6 +656,46 @@ void FreqScanner::setCenterFrequency(qint64 frequency)
}
}
// Mute all channels
void FreqScanner::muteAll()
{
QStringList channels;
channels.append(m_settings.m_channel);
for (int i = 0; i < m_settings.m_frequencySettings.size(); i++)
{
QString channel = m_settings.m_frequencySettings[i].m_channel;
if (!channel.isEmpty() && !channels.contains(channel)) {
channels.append(channel);
}
}
const QRegExp re("R([0-9]+):([0-9]+)");
for (const auto& channel : channels)
{
if (re.indexIn(channel) >= 0)
{
int deviceSetIndex = re.capturedTexts()[1].toInt();
int scanChannelIndex = re.capturedTexts()[2].toInt();
ChannelWebAPIUtils::setAudioMute(deviceSetIndex, scanChannelIndex, true);
}
}
}
void FreqScanner::applyChannelSetting(const QString& channel)
{
const QRegExp re("R([0-9]+):([0-9]+)");
if (re.indexIn(channel) >= 0)
{
m_scanDeviceSetIndex = re.capturedTexts()[1].toInt();
m_scanChannelIndex = re.capturedTexts()[2].toInt();
}
else
{
qDebug() << "FreqScanner::applySettings: Failed to parse channel" << channel;
}
}
void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force)
{
qDebug() << "FreqScanner::applySettings:"
@ -640,20 +713,6 @@ void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStri
}
}
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);
@ -670,7 +729,7 @@ void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStri
webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
}
if (settingsKeys.contains("frequencies")
if (settingsKeys.contains("frequencySettings")
|| settingsKeys.contains("priority")
|| settingsKeys.contains("measurement")
|| settingsKeys.contains("mode")
@ -784,21 +843,30 @@ void FreqScanner::webapiUpdateChannelSettings(
}
if (channelSettingsKeys.contains("frequencies"))
{
settings.m_frequencies.clear();
settings.m_enabled.clear();
settings.m_notes.clear();
settings.m_frequencySettings.clear();
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = response.getFreqScannerSettings()->getFrequencies();
if (frequencies)
{
for (const auto frequency : *frequencies)
{
settings.m_frequencies.append(frequency->getFrequency());
settings.m_enabled.append((bool)frequency->getEnabled());
FreqScannerSettings::FrequencySettings freqSetting;
freqSetting.m_frequency = frequency->getFrequency();
if (frequency->getNotes()) {
settings.m_notes.append(*frequency->getNotes());
} else {
settings.m_notes.append("");
freqSetting.m_notes = *frequency->getNotes();
}
if (frequency->getChannel()) {
freqSetting.m_channel = *frequency->getChannel();
}
if (frequency->getChannelBandwidth()) {
freqSetting.m_channelBandwidth = *frequency->getChannelBandwidth();
}
if (frequency->getThreshold()) {
freqSetting.m_threshold = *frequency->getThreshold();
}
if (frequency->getSquelch()) {
freqSetting.m_squelch = *frequency->getSquelch();
}
settings.m_frequencySettings.append(freqSetting);
}
}
}
@ -837,14 +905,26 @@ void FreqScanner::webapiUpdateChannelSettings(
QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(const FreqScannerSettings& settings)
{
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = new QList<SWGSDRangel::SWGFreqScannerFrequency *>();
for (int i = 0; i < settings.m_frequencies.size(); i++)
for (int i = 0; i < settings.m_frequencySettings.size(); i++)
{
SWGSDRangel::SWGFreqScannerFrequency *frequency = new SWGSDRangel::SWGFreqScannerFrequency();
frequency->init();
frequency->setFrequency(settings.m_frequencies[i]);
frequency->setEnabled(settings.m_enabled[i]);
if (!settings.m_notes[i].isEmpty()) {
frequency->setNotes(new QString(settings.m_notes[i]));
frequency->setFrequency(settings.m_frequencySettings[i].m_frequency);
frequency->setEnabled(settings.m_frequencySettings[i].m_enabled);
if (!settings.m_frequencySettings[i].m_notes.isEmpty()) {
frequency->setNotes(new QString(settings.m_frequencySettings[i].m_notes));
}
if (!settings.m_frequencySettings[i].m_channel.isEmpty()) {
frequency->setChannel(new QString(settings.m_frequencySettings[i].m_channel));
}
if (!settings.m_frequencySettings[i].m_channelBandwidth.isEmpty()) {
frequency->setChannelBandwidth(new QString(settings.m_frequencySettings[i].m_channelBandwidth));
}
if (!settings.m_frequencySettings[i].m_threshold.isEmpty()) {
frequency->setThreshold(new QString(settings.m_frequencySettings[i].m_threshold));
}
if (!settings.m_frequencySettings[i].m_squelch.isEmpty()) {
frequency->setSquelch(new QString(settings.m_frequencySettings[i].m_squelch));
}
frequencies->append(frequency);
}

Wyświetl plik

@ -407,6 +407,8 @@ private:
void initScan();
void processScanResults(const QDateTime& fftStartTime, const QList<MsgScanResult::ScanResult>& results);
void setDeviceCenterFrequency(qint64 frequency);
void applyChannelSetting(const QString& channel);
void muteAll();
static QList<SWGSDRangel::SWGFreqScannerFrequency *> *createFrequencyList(const FreqScannerSettings& settings);

Wyświetl plik

@ -22,6 +22,7 @@
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QRegExp>
#include <QComboBox>
#include "device/deviceset.h"
#include "device/deviceuiset.h"
@ -37,6 +38,7 @@
#include "gui/dialogpositioner.h"
#include "gui/decimaldelegate.h"
#include "gui/frequencydelegate.h"
#include "gui/int64delegate.h"
#include "gui/glspectrum.h"
#include "channel/channelwebapiutils.h"
@ -103,9 +105,9 @@ bool FreqScannerGUI::handleMessage(const Message& message)
m_basebandSampleRate = notif.getSampleRate();
if (m_basebandSampleRate != 0)
{
ui->deltaFrequency->setValueRange(true, 7, 0, m_basebandSampleRate/2);
ui->deltaFrequency->setValueRange(true, 8, 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);
ui->channelBandwidth->setValueRange(true, 8, 0, m_basebandSampleRate);
}
if (m_channelMarker.getBandwidth() == 0) {
m_channelMarker.setBandwidth(m_basebandSampleRate);
@ -116,7 +118,8 @@ bool FreqScannerGUI::handleMessage(const Message& message)
else if (FreqScanner::MsgReportChannels::match(message))
{
FreqScanner::MsgReportChannels& report = (FreqScanner::MsgReportChannels&)message;
updateChannelsList(report.getChannels());
m_availableChannels = report.getChannels();
updateChannelsList(m_availableChannels);
return true;
}
else if (FreqScanner::MsgStatus::match(message))
@ -187,11 +190,14 @@ bool FreqScannerGUI::handleMessage(const Message& message)
{
qint64 freq = results[i].m_frequency;
QList<QTableWidgetItem *> items = ui->table->findItems(QString::number(freq), Qt::MatchExactly);
for (auto item : items) {
for (auto item : items)
{
int row = item->row();
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;
FreqScannerSettings::FrequencySettings *frequencySettings = m_settings.getFrequencySettings(freq);
Real threshold = m_settings.getThreshold(frequencySettings);
bool active = results[i].m_power >= threshold;
if (active)
{
powerItem->setBackground(Qt::darkGreen);
@ -206,10 +212,13 @@ bool FreqScannerGUI::handleMessage(const Message& message)
return false;
}
void FreqScannerGUI::updateChannelsList(const QList<FreqScannerSettings::AvailableChannel>& channels)
void FreqScannerGUI::updateChannelsCombo(QComboBox *combo, const QList<FreqScannerSettings::AvailableChannel>& channels, const QString& channel, bool empty)
{
ui->channels->blockSignals(true);
ui->channels->clear();
combo->blockSignals(true);
combo->clear();
if (empty) {
combo->addItem("");
}
for (const auto& channel : channels)
{
@ -217,21 +226,32 @@ void FreqScannerGUI::updateChannelsList(const QList<FreqScannerSettings::Availab
if ((channel.m_deviceSetIndex == m_freqScanner->getDeviceSetIndex()) && (channel.m_channelIndex != m_freqScanner->getIndexInDeviceSet()))
{
QString name = QString("R%1:%2").arg(channel.m_deviceSetIndex).arg(channel.m_channelIndex);
ui->channels->addItem(name);
combo->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);
int channelIndex = combo->findText(channel);
if (channelIndex >= 0) {
ui->channels->setCurrentIndex(channelIndex);
combo->setCurrentIndex(channelIndex);
} else {
ui->channels->setCurrentIndex(-1); // return to nothing selected
combo->setCurrentIndex(-1); // return to nothing selected
}
ui->channels->blockSignals(false);
combo->blockSignals(false);
}
void FreqScannerGUI::updateChannelsList(const QList<FreqScannerSettings::AvailableChannel>& channels)
{
updateChannelsCombo(ui->channels, channels, m_settings.m_channel, false);
for (int row = 0; row < ui->table->rowCount(); row++)
{
QComboBox *combo = qobject_cast<QComboBox *>(ui->table->cellWidget(row, COL_CHANNEL));
updateChannelsCombo(combo, channels, m_settings.m_frequencySettings[row].m_channel, true);
}
}
void FreqScannerGUI::on_channels_currentIndexChanged(int index)
@ -412,10 +432,10 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(true, 7, 0, 9999999);
ui->deltaFrequency->setValueRange(true, 8, 0, 9999999);
ui->channelBandwidth->setColorMapper(ColorMapper(ColorMapper::GrayGreenYellow));
ui->channelBandwidth->setValueRange(true, 7, 0, 9999999);
ui->channelBandwidth->setValueRange(true, 8, 0, 9999999);
m_channelMarker.setColor(Qt::yellow);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
@ -464,6 +484,9 @@ FreqScannerGUI::FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B
ui->table->setItemDelegateForColumn(COL_FREQUENCY, new FrequencyDelegate("Auto", 3));
ui->table->setItemDelegateForColumn(COL_POWER, new DecimalDelegate(1));
ui->table->setItemDelegateForColumn(COL_CHANNEL_BW, new Int64Delegate(0, 10000000));
ui->table->setItemDelegateForColumn(COL_TH, new DecimalDelegate(1, -120.0, 0.0));
ui->table->setItemDelegateForColumn(COL_SQ, new DecimalDelegate(1, -120.0, 0.0));
connect(m_deviceUISet->m_spectrum->getSpectrumView(), &GLSpectrumView::updateAnnotations, this, &FreqScannerGUI::updateAnnotations);
}
@ -531,9 +554,9 @@ void FreqScannerGUI::displaySettings()
ui->table->blockSignals(true);
ui->table->setRowCount(0);
for (int i = 0; i < m_settings.m_frequencies.size(); i++)
for (int i = 0; i < m_settings.m_frequencySettings.size(); i++)
{
addRow(m_settings.m_frequencies[i], m_settings.m_enabled[i], m_settings.m_notes[i]);
addRow(m_settings.m_frequencySettings[i]);
updateAnnotation(i);
}
ui->table->blockSignals(false);
@ -584,7 +607,7 @@ void FreqScannerGUI::on_startStop_clicked(bool checked)
}
}
void FreqScannerGUI::addRow(qint64 frequency, bool enabled, const QString& notes)
void FreqScannerGUI::addRow(const FreqScannerSettings::FrequencySettings& frequencySettings)
{
int row = ui->table->rowCount();
ui->table->setRowCount(row + 1);
@ -594,11 +617,11 @@ void FreqScannerGUI::addRow(qint64 frequency, bool enabled, const QString& notes
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)));
ui->table->setItem(row, COL_FREQUENCY, new QTableWidgetItem(QString("%1").arg(frequencySettings.m_frequency)));
QTableWidgetItem *enableItem = new QTableWidgetItem();
enableItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
enableItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
enableItem->setCheckState(frequencySettings.m_enabled ? Qt::Checked : Qt::Unchecked);
ui->table->setItem(row, COL_ENABLE, enableItem);
QTableWidgetItem* powerItem = new QTableWidgetItem();
@ -610,13 +633,40 @@ void FreqScannerGUI::addRow(qint64 frequency, bool enabled, const QString& notes
ui->table->setItem(row, COL_ACTIVE_COUNT, activeCountItem);
activeCountItem->setData(Qt::DisplayRole, 0);
QTableWidgetItem* notesItem = new QTableWidgetItem(notes);
QTableWidgetItem* notesItem = new QTableWidgetItem(frequencySettings.m_notes);
ui->table->setItem(row, COL_NOTES, notesItem);
QComboBox *channelComboBox = new QComboBox();
updateChannelsCombo(channelComboBox, m_availableChannels, frequencySettings.m_channel, true);
ui->table->setCellWidget(row, COL_CHANNEL, channelComboBox);
connect(channelComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &FreqScannerGUI::on_table_channel_currentIndexChanged);
QTableWidgetItem* channelBandwidthItem = new QTableWidgetItem(frequencySettings.m_channelBandwidth);
ui->table->setItem(row, COL_CHANNEL_BW, channelBandwidthItem);
QTableWidgetItem* thresholdItem = new QTableWidgetItem(frequencySettings.m_threshold);
ui->table->setItem(row, COL_TH, thresholdItem);
QTableWidgetItem* squelchItem = new QTableWidgetItem(frequencySettings.m_squelch);
ui->table->setItem(row, COL_SQ, squelchItem);
}
void FreqScannerGUI::on_table_channel_currentIndexChanged(int index)
{
if (index >= 0)
{
QComboBox *combo = qobject_cast<QComboBox *>(sender());
QModelIndex tableIndex = ui->table->indexAt(combo->pos());
on_table_cellChanged(tableIndex.row(), tableIndex.column());
}
}
void FreqScannerGUI::on_addSingle_clicked()
{
addRow(0, true);
FreqScannerSettings::FrequencySettings frequencySettings;
frequencySettings.m_frequency = 0;
frequencySettings.m_enabled = true;
addRow(frequencySettings);
}
void FreqScannerGUI::on_addRange_clicked()
@ -626,11 +676,15 @@ void FreqScannerGUI::on_addRange_clicked()
if (dialog.exec())
{
blockApplySettings(true);
for (const auto f : dialog.m_frequencies) {
addRow(f, true);
for (const auto f : dialog.m_frequencies)
{
FreqScannerSettings::FrequencySettings frequencySettings;
frequencySettings.m_frequency = f;
frequencySettings.m_enabled = true;
addRow(frequencySettings);
}
blockApplySettings(false);
applySetting("frequencies");
applySetting("frequencySettings");
}
}
@ -642,11 +696,9 @@ void FreqScannerGUI::on_remove_clicked()
{
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);
m_settings.m_notes.removeAt(row);
m_settings.m_frequencySettings.removeAt(row);
}
applySetting("frequencies");
applySetting("frequencySettings");
}
void FreqScannerGUI::on_removeInactive_clicked()
@ -656,12 +708,10 @@ void FreqScannerGUI::on_removeInactive_clicked()
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);
m_settings.m_frequencySettings.removeAt(i);
}
}
applySetting("frequencies");
applySetting("frequencySettings");
}
static QList<QTableWidgetItem*> takeRow(QTableWidget* table, int row)
@ -730,26 +780,49 @@ void FreqScannerGUI::on_table_cellChanged(int row, int column)
if (column == COL_FREQUENCY)
{
qint64 value = item->text().toLongLong();
while (m_settings.m_frequencies.size() <= row)
while (m_settings.m_frequencySettings.size() <= row)
{
m_settings.m_frequencies.append(0);
m_settings.m_enabled.append(true);
m_settings.m_notes.append("");
FreqScannerSettings::FrequencySettings frequencySettings;
frequencySettings.m_frequency = 0;
frequencySettings.m_enabled = true;
m_settings.m_frequencySettings.append(frequencySettings);
}
m_settings.m_frequencies[row] = value;
m_settings.m_frequencySettings[row].m_frequency = value;
updateAnnotation(row);
applySetting("frequencies");
applySetting("frequencySettings");
}
else if (column == COL_ENABLE)
{
m_settings.m_enabled[row] = item->checkState() == Qt::Checked;
applySetting("frequencies");
m_settings.m_frequencySettings[row].m_enabled = item->checkState() == Qt::Checked;
applySetting("frequencySettings");
}
else if (column == COL_NOTES)
{
m_settings.m_notes[row] = item->text();
applySetting("frequencies");
m_settings.m_frequencySettings[row].m_notes = item->text();
applySetting("frequencySettings");
}
else if (column == COL_CHANNEL_BW)
{
m_settings.m_frequencySettings[row].m_channelBandwidth = item->text();
applySetting("frequencySettings");
}
else if (column == COL_TH)
{
m_settings.m_frequencySettings[row].m_threshold = item->text();
applySetting("frequencySettings");
}
else if (column == COL_SQ)
{
m_settings.m_frequencySettings[row].m_squelch = item->text();
applySetting("frequencySettings");
}
}
else if (column == COL_CHANNEL)
{
QComboBox *combo = qobject_cast<QComboBox *>(ui->table->cellWidget(row, COL_CHANNEL));
m_settings.m_frequencySettings[row].m_channel = combo->currentText();
qDebug() << "Setting row" << row << "to" << combo->currentText();
applySetting("frequencySettings");
}
}
@ -959,7 +1032,11 @@ void FreqScannerGUI::resizeTable()
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->setItem(row, COL_NOTES, new QTableWidgetItem("A channel name"));
ui->table->setItem(row, COL_CHANNEL, new QTableWidgetItem("Enter some notes"));
ui->table->setItem(row, COL_CHANNEL_BW, new QTableWidgetItem("100000000"));
ui->table->setItem(row, COL_TH, new QTableWidgetItem("-100.0"));
ui->table->setItem(row, COL_SQ, new QTableWidgetItem("-100.0"));
ui->table->resizeColumnsToContents();
ui->table->setRowCount(row);
}

Wyświetl plik

@ -32,6 +32,7 @@ class BasebandSampleSink;
class FreqScanner;
class FreqScannerGUI;
class QMenu;
class QComboBox;
namespace Ui {
class FreqScannerGUI;
@ -81,6 +82,8 @@ private:
QMenu *m_menu;
QList<FreqScannerSettings::AvailableChannel> m_availableChannels;
explicit FreqScannerGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~FreqScannerGUI();
@ -92,9 +95,10 @@ private:
bool handleMessage(const Message& message);
void makeUIConnections();
void updateAbsoluteCenterFrequency();
void addRow(qint64 frequency, bool enabled, const QString& notes = "");
void addRow(const FreqScannerSettings::FrequencySettings& frequencySettings);
void updateAnnotation(int row);
void updateAnnotations();
void updateChannelsCombo(QComboBox *combo, const QList<FreqScannerSettings::AvailableChannel>& channels, const QString& channel, bool empty);
void updateChannelsList(const QList<FreqScannerSettings::AvailableChannel>& channels);
void setAllEnabled(bool enable);
@ -110,7 +114,11 @@ private:
COL_ENABLE,
COL_POWER,
COL_ACTIVE_COUNT,
COL_NOTES
COL_NOTES,
COL_CHANNEL,
COL_CHANNEL_BW,
COL_TH,
COL_SQ
};
private slots:
@ -125,6 +133,7 @@ private slots:
void on_measurement_currentIndexChanged(int index);
void on_mode_currentIndexChanged(int index);
void on_table_cellChanged(int row, int column);
void on_table_channel_currentIndexChanged(int index);
void table_customContextMenuRequested(QPoint pos);
void table_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex);
void table_sectionResized(int logicalIndex, int oldSize, int newSize);

Wyświetl plik

@ -671,6 +671,46 @@
<string>User notes about this frequency</string>
</property>
</column>
<column>
<property name="text">
<string>Channel</string>
</property>
<property name="toolTip">
<string>Frequency specific channel to tune
Leave blank for common setting</string>
</property>
</column>
<column>
<property name="text">
<string>Ch BW (Hz)</string>
</property>
<property name="toolTip">
<string>Frequency specific channel BW
Leave blank to use common setting</string>
</property>
</column>
<column>
<property name="text">
<string>TH (dB)</string>
</property>
<property name="toolTip">
<string>Frequency specific threshold in dB
Leave blank to use common setting</string>
</property>
</column>
<column>
<property name="text">
<string>Sq (dB)</string>
</property>
<property name="toolTip">
<string>Frequency specific squelch in dB
Leave blank for no adjustment</string>
</property>
</column>
</widget>
</item>
<item>
@ -783,18 +823,18 @@
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>deltaFrequency</tabstop>

Wyświetl plik

@ -16,6 +16,7 @@
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include <QDebug>
#include "util/simpleserializer.h"
#include "settings/serializable.h"
@ -40,6 +41,7 @@ void FreqScannerSettings::resetToDefaults()
m_channelFrequencyOffset = 25000;
m_threshold = -60.0f;
m_channel = "";
m_frequencySettings = {};
m_scanTime = 0.1f;
m_retransmitTime = 2.0f;
m_tuneTime = 100;
@ -73,9 +75,6 @@ QByteArray FreqScannerSettings::serialize() const
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);
@ -83,6 +82,7 @@ QByteArray FreqScannerSettings::serialize() const
s.writeS32(12, (int)m_priority);
s.writeS32(13, (int)m_measurement);
s.writeS32(14, (int)m_mode);
s.writeList(15, m_frequencySettings);
s.writeList(20, m_columnIndexes);
s.writeList(21, m_columnSizes);
@ -128,22 +128,37 @@ bool FreqScannerSettings::deserialize(const QByteArray& data)
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(15, &m_frequencySettings);
if (m_frequencySettings.size() == 0)
{
// Try reading old settings
QList<bool> enabled;
QList<qint64> frequencies;
d.readList(6, &enabled);
d.readList(7, &frequencies);
if (frequencies.size() > 0)
{
for (int i = 0; i < frequencies.size(); i++)
{
FrequencySettings frequencySettings;
frequencySettings.m_frequency = frequencies[i];
if (i < enabled.size()) {
frequencySettings.m_enabled = enabled[i];
} else {
frequencySettings.m_enabled = true;
}
m_frequencySettings.append(frequencySettings);
}
}
}
d.readList(20, &m_columnIndexes);
d.readList(21, &m_columnSizes);
@ -200,10 +215,8 @@ void FreqScannerSettings::applySettings(const QStringList& settingsKeys, const F
if (settingsKeys.contains("threshold")) {
m_threshold = settings.m_threshold;
}
if (settingsKeys.contains("frequencies")) {
m_frequencies = settings.m_frequencies;
m_enabled = settings.m_enabled;
m_notes = settings.m_notes;
if (settingsKeys.contains("frequencySettings")) {
m_frequencySettings = settings.m_frequencySettings;
}
if (settingsKeys.contains("channel")) {
m_channel = settings.m_channel;
@ -280,13 +293,13 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo
if (settingsKeys.contains("threshold") || force) {
ostr << " m_threshold: " << m_threshold;
}
if (settingsKeys.contains("frequencies") || force)
if (settingsKeys.contains("frequencySettings") || force)
{
QStringList s;
for (auto f : m_frequencies) {
s.append(QString::number(f));
for (auto f : m_frequencySettings) {
s.append(QString::number(f.m_frequency));
}
ostr << " m_frequencies: " << s.join(",").toStdString();
ostr << " m_frequencySettings: " << s.join(",").toStdString();
}
if (settingsKeys.contains("channel") || force) {
ostr << " m_channel: " << m_channel.toStdString();
@ -348,3 +361,105 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo
return QString(ostr.str().c_str());
}
QByteArray FreqScannerSettings::FrequencySettings::serialize() const
{
SimpleSerializer s(1);
s.writeS64(1, m_frequency);
s.writeBool(2, m_enabled);
s.writeString(3, m_notes);
s.writeString(4, m_threshold);
s.writeString(5, m_channel);
s.writeString(6, m_channelBandwidth);
s.writeString(7, m_squelch);
return s.final();
}
bool FreqScannerSettings::FrequencySettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid()) {
return false;
}
if (d.getVersion() == 1)
{
QByteArray blob;
d.readS64(1, &m_frequency);
d.readBool(2, &m_enabled);
d.readString(3, &m_notes);
d.readString(4, &m_threshold);
d.readString(5, &m_channel);
d.readString(6, &m_channelBandwidth);
d.readString(7, &m_squelch);
return true;
}
else
{
return false;
}
}
QDataStream& operator<<(QDataStream& out, const FreqScannerSettings::FrequencySettings& settings)
{
out << settings.serialize();
return out;
}
QDataStream& operator>>(QDataStream& in, FreqScannerSettings::FrequencySettings& settings)
{
QByteArray data;
in >> data;
settings.deserialize(data);
return in;
}
Real FreqScannerSettings::getThreshold(FreqScannerSettings::FrequencySettings *frequencySettings) const
{
Real threshold = m_threshold;
if (!frequencySettings->m_threshold.isEmpty())
{
bool ok;
Real perFrequencyThreshold = frequencySettings->m_threshold.toFloat(&ok);
if (ok) {
threshold = perFrequencyThreshold;
} else {
qDebug() << "FreqScannerSettings::getThreshold: Failed to parse" << frequencySettings->m_threshold << "as a float";
}
}
return threshold;
}
int FreqScannerSettings::getChannelBandwidth(FreqScannerSettings::FrequencySettings *frequencySettings) const
{
int channelBandwidth = m_channelBandwidth;
if (!frequencySettings->m_channelBandwidth.isEmpty())
{
bool ok;
Real perFrequencyChannelBandwidth = frequencySettings->m_channelBandwidth.toInt(&ok);
if (ok) {
channelBandwidth = perFrequencyChannelBandwidth;
} else {
qDebug() << "FreqScannerSettings::getChannelBandwidth: Failed to parse" << frequencySettings->m_channelBandwidth << "as an int";
}
}
return channelBandwidth;
}
FreqScannerSettings::FrequencySettings *FreqScannerSettings::getFrequencySettings(qint64 frequency)
{
for (int i = 0; i < m_frequencySettings.size(); i++)
{
if (frequency == m_frequencySettings[i].m_frequency) {
return &this->m_frequencySettings[i];
}
}
return nullptr;
}

Wyświetl plik

@ -27,7 +27,7 @@ class Serializable;
class ChannelAPI;
// Number of columns in the table
#define FREQSCANNER_COLUMNS 6
#define FREQSCANNER_COLUMNS 10
struct FreqScannerSettings
{
@ -41,14 +41,25 @@ struct FreqScannerSettings
AvailableChannel& operator=(const AvailableChannel&) = default;
};
struct FrequencySettings {
qint64 m_frequency;
bool m_enabled;
QString m_notes;
QString m_threshold; // QStrings used, as we allow "" for no setting
QString m_channel;
QString m_channelBandwidth;
QString m_squelch;
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
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<qint64> m_frequencies; //!< Frequencies to scan
QList<bool> m_enabled; //!< Whether corresponding frequency is enabled
QList<QString> m_notes; //!< User editable notes about this frequency
QString m_channel; //!< Channel (E.g: R1:4) to tune to active frequency
QList<FrequencySettings> m_frequencySettings; //!< Frequencies to scan and corresponding settings
float m_scanTime; //!< In seconds
float m_retransmitTime; //!< In seconds
int m_tuneTime; //!< In milliseconds
@ -92,6 +103,9 @@ struct FreqScannerSettings
bool deserialize(const QByteArray& data);
void applySettings(const QStringList& settingsKeys, const FreqScannerSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force = false) const;
Real getThreshold(FreqScannerSettings::FrequencySettings *frequencySettings) const;
int getChannelBandwidth(FreqScannerSettings::FrequencySettings *frequencySettings) const;
FreqScannerSettings::FrequencySettings *getFrequencySettings(qint64 frequency);
};
#endif /* INCLUDE_FREQSCANNERSETTINGS_H */

Wyświetl plik

@ -107,11 +107,11 @@ void FreqScannerSink::processOneSample(Complex &ci)
FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(m_fftStartTime);
QList<FreqScanner::MsgScanResult::ScanResult>& results = msg->getScanResults();
for (int i = 0; i < m_settings.m_frequencies.size(); i++)
for (int i = 0; i < m_settings.m_frequencySettings.size(); i++)
{
if (m_settings.m_enabled[i])
if (m_settings.m_frequencySettings[i].m_enabled)
{
qint64 frequency = m_settings.m_frequencies[i];
qint64 frequency = m_settings.m_frequencySettings[i].m_frequency;
qint64 startFrequency = m_centerFrequency - m_scannerSampleRate / 2;
qint64 diff = frequency - startFrequency;
float binBW = m_scannerSampleRate / (float)m_fftSize;
@ -120,13 +120,24 @@ void FreqScannerSink::processOneSample(Complex &ci)
if ((diff < m_scannerSampleRate * 0.875f) && (diff >= m_scannerSampleRate * 0.125f))
{
int bin = std::round(diff / binBW);
int channelBins;
if (m_settings.m_frequencySettings[i].m_channelBandwidth.isEmpty())
{
channelBins = m_binsPerChannel;
}
else
{
int channelBW = m_settings.getChannelBandwidth(&m_settings.m_frequencySettings[i]);
channelBins = m_fftSize / (m_scannerSampleRate / (float)channelBW);
}
// Calculate power at that frequency
Real power;
if (m_settings.m_measurement == FreqScannerSettings::PEAK) {
power = peakPower(bin);
power = peakPower(bin, channelBins);
} else {
power = totalPower(bin);
power = totalPower(bin, channelBins);
}
//qDebug() << "startFrequency:" << startFrequency << "m_scannerSampleRate:" << m_scannerSampleRate << "m_centerFrequency:" << m_centerFrequency << "frequency" << frequency << "bin" << bin << "power" << power;
FreqScanner::MsgScanResult::ScanResult result = {frequency, power};
@ -144,13 +155,13 @@ void FreqScannerSink::processOneSample(Complex &ci)
}
// 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
Real FreqScannerSink::totalPower(int bin, int channelBins) 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;
int startBin = bin - channelBins / 2 + 1 + 1;
Real magSqSum = 0.0f;
for (int i = 0; i < m_binsPerChannel - 2 - 1; i++) {
for (int i = 0; i < channelBins - 2 - 1; i++) {
int idx = startBin + i;
if ((idx < 0) || (idx >= m_fftSize)) {
continue;
@ -162,13 +173,13 @@ Real FreqScannerSink::totalPower(int bin) const
}
// Calculate peak power in a channel containing the specified bin
Real FreqScannerSink::peakPower(int bin) const
Real FreqScannerSink::peakPower(int bin, int channelBins) 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;
int startBin = bin - channelBins/2 + 1 + 1;
Real maxMagSq = std::numeric_limits<Real>::min();
for (int i = 0; i < m_binsPerChannel - 2 - 1; i++)
for (int i = 0; i < channelBins - 2 - 1; i++)
{
int idx = startBin + i;
if ((idx < 0) || (idx >= m_fftSize)) {

Wyświetl plik

@ -79,8 +79,8 @@ private:
void processOneSample(Complex &ci);
MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; }
Real totalPower(int bin) const;
Real peakPower(int bin) const;
Real totalPower(int bin, int channelBins) const;
Real peakPower(int bin, int channelBins) const;
Real magSq(int bin) const;
};

Wyświetl plik

@ -97,6 +97,10 @@ The frequency table contains the list of frequencies to be scanned, along with r
- 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.
- Channel: Specifies the channel that should be tuned when this frequency is active. If blank, the common Channel setting (1) is used.
- Ch Bw (Hz): Specifies the channel bandwidth in Hertz. If blank, the common Channel Bandwidth setting (8) is used.
- TH (dB): Specifies the power threshold in dB that determines whether this frequency is active or not. If blank, the common Threshold setting (4) is used.
- Sq (dB): Specifies a squelch level in dB that will be applied to the Channel when active. If blank, the squelch level will not be changed.
When an active frequency is found after a scan, the corresponding row in the table will be selected.

Wyświetl plik

@ -1293,6 +1293,53 @@ bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsig
}
}
bool ChannelWebAPIUtils::patchChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, double value)
{
SWGSDRangel::SWGChannelSettings channelSettingsResponse;
QString errorResponse;
int httpRC;
ChannelAPI *channel;
if (getChannelSettings(deviceSetIndex, channelIndex, channelSettingsResponse, channel))
{
// Patch settings
QJsonObject *jsonObj = channelSettingsResponse.asJsonObject();
double oldValue;
if (WebAPIUtils::getSubObjectDouble(*jsonObj, setting, oldValue))
{
WebAPIUtils::setSubObjectDouble(*jsonObj, setting, value);
QStringList channelSettingsKeys;
channelSettingsKeys.append(setting);
channelSettingsResponse.init();
channelSettingsResponse.fromJsonObject(*jsonObj);
SWGSDRangel::SWGErrorResponse errorResponse2;
httpRC = channel->webapiSettingsPutPatch(false, channelSettingsKeys, channelSettingsResponse, *errorResponse2.getMessage());
if (httpRC/100 == 2)
{
qDebug("ChannelWebAPIUtils::patchChannelSetting: set feature setting %s to %f OK", qPrintable(setting), value);
return true;
}
else
{
qWarning("ChannelWebAPIUtils::patchChannelSetting: set feature setting %s to %f error %d: %s",
qPrintable(setting), value, httpRC, qPrintable(*errorResponse2.getMessage()));
return false;
}
}
else
{
qWarning("ChannelWebAPIUtils::patchChannelSetting: no key %s in feature settings", qPrintable(setting));
return false;
}
}
else
{
return false;
}
}
bool ChannelWebAPIUtils::patchChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, const QJsonArray& value)
{
SWGSDRangel::SWGChannelSettings channelSettingsResponse;

Wyświetl plik

@ -73,6 +73,7 @@ public:
static bool patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QString &value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double value);
static bool patchChannelSetting(unsigned int deviceSetIndex, unsigned int channeIndex, const QString &setting, double value);
static bool patchChannelSetting(unsigned int deviceSetIndex, unsigned int channeIndex, const QString &setting, const QJsonArray& value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double &value);

Wyświetl plik

@ -7007,6 +7007,18 @@ margin-bottom: 20px;
},
"notes" : {
"type" : "string"
},
"channel" : {
"type" : "string"
},
"channelBandwidth" : {
"type" : "string"
},
"threshold" : {
"type" : "string"
},
"squelch" : {
"type" : "string"
}
}
};
@ -58401,7 +58413,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2023-11-05T10:45:45.965+01:00
Generated 2023-11-30T13:04:24.913+01:00
</div>
</div>
</div>

Wyświetl plik

@ -69,3 +69,11 @@ FreqScannerFrequency:
type: integer
notes:
type: string
channel:
type: string
channelBandwidth:
type: string
threshold:
type: string
squelch:
type: string

Wyświetl plik

@ -105,7 +105,7 @@ public:
bool readBlob(quint32 id, QByteArray* result, const QByteArray& def = QByteArray()) const;
template<typename T>
bool readList(quint32 id, QList<T>* result)
bool readList(quint32 id, QList<T>* result, const QList<T>& def = {})
{
QByteArray data;
bool ok = readBlob(id, &data);
@ -115,10 +115,14 @@ public:
(*stream) >> *result;
delete stream;
}
else
{
*result = def;
}
return ok;
}
template<typename TK, typename TV>
bool readHash(quint32 id, QHash<TK,TV>* result)
bool readHash(quint32 id, QHash<TK,TV>* result, const QHash<TK,TV>& def = {})
{
QByteArray data;
bool ok = readBlob(id, &data);
@ -128,6 +132,10 @@ public:
(*stream) >> *result;
delete stream;
}
else
{
*result = def;
}
return ok;
}

Wyświetl plik

@ -63,6 +63,8 @@ set(sdrgui_SOURCES
gui/graphicsviewzoom.cpp
gui/httpdownloadmanagergui.cpp
gui/indicator.cpp
gui/int64delegate.cpp
gui/int64validator.cpp
gui/levelmeter.cpp
gui/loggingdialog.cpp
gui/logslider.cpp
@ -186,6 +188,8 @@ set(sdrgui_HEADERS
gui/graphicsviewzoom.h
gui/httpdownloadmanagergui.h
gui/indicator.h
gui/int64delegate.h
gui/int64validator.h
gui/levelmeter.h
gui/loggingdialog.h
gui/logslider.h

Wyświetl plik

@ -18,10 +18,40 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QLineEdit>
#include <QDoubleValidator>
#include "decimaldelegate.h"
// Allow "" or double
class DoubleOrEmptyValidator : public QDoubleValidator {
public:
DoubleOrEmptyValidator(double bottom, double top, int decimals, QObject *parent = nullptr) :
QDoubleValidator(bottom, top, decimals, parent)
{
}
QValidator::State validate(QString& input, int &pos) const
{
if (input == "") {
return QValidator::Acceptable;
} else {
return QDoubleValidator::validate(input, pos);
}
}
};
DecimalDelegate::DecimalDelegate(int precision) :
m_precision(precision)
m_precision(precision),
m_min(-std::numeric_limits<double>::max()),
m_max(std::numeric_limits<double>::max())
{
}
DecimalDelegate::DecimalDelegate(int precision, double min, double max) :
m_precision(precision),
m_min(min),
m_max(max)
{
}
@ -36,3 +66,22 @@ QString DecimalDelegate::displayText(const QVariant &value, const QLocale &local
return value.toString();
}
}
QWidget *DecimalDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
(void) option;
(void) index;
QLineEdit* editor = new QLineEdit(parent);
DoubleOrEmptyValidator *validator = new DoubleOrEmptyValidator(m_min, m_max, m_precision);
validator->setBottom(m_min);
validator->setTop(m_max);
editor->setValidator(validator);
return editor;
}
void DecimalDelegate::setRange(double min, double max)
{
m_min = min;
m_max = max;
}

Wyświetl plik

@ -26,17 +26,23 @@
#include "export.h"
// Deligate for table to control precision used to display floating point values - also supports strings
// Min and max values are constraints for editing
class SDRGUI_API DecimalDelegate : public QStyledItemDelegate {
public:
DecimalDelegate(int precision = 2);
DecimalDelegate(int precision, double min, double max);
virtual QString displayText(const QVariant &value, const QLocale &locale) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
int getPrecision() const { return m_precision; }
void setPrecision(int precision) { m_precision = precision; }
void setRange(double min, double max);
private:
int m_precision;
double m_min;
double m_max;
};

Wyświetl plik

@ -21,6 +21,7 @@
#include <QLineEdit>
#include "frequencydelegate.h"
#include "int64validator.h"
FrequencyDelegate::FrequencyDelegate(const QString& units, int precision, bool group) :
m_units(units),
@ -112,13 +113,14 @@ 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();
Int64Validator* validator = new Int64Validator();
validator->setBottom(0);
editor->setValidator(validator);
return editor;

Wyświetl plik

@ -0,0 +1,59 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QLineEdit>
#include "int64delegate.h"
#include "int64validator.h"
Int64Delegate::Int64Delegate() :
m_min(-std::numeric_limits<qint64>::max()),
m_max(std::numeric_limits<qint64>::max())
{
}
Int64Delegate::Int64Delegate(qint64 min, qint64 max) :
m_min(min),
m_max(max)
{
}
QString Int64Delegate::displayText(const QVariant &value, const QLocale &locale) const
{
(void) locale;
return value.toString();
}
QWidget *Int64Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
(void) option;
(void) index;
QLineEdit* editor = new QLineEdit(parent);
Int64Validator* validator = new Int64Validator();
validator->setBottom(m_min);
validator->setTop(m_max);
editor->setValidator(validator);
return editor;
}
void Int64Delegate::setRange(qint64 min, qint64 max)
{
m_min = min;
m_max = max;
}

Wyświetl plik

@ -0,0 +1,46 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRGUI_GUI_INT64DELGATE_H
#define SDRGUI_GUI_INT64DELGATE_H
#include <QStyledItemDelegate>
#include "export.h"
// Delegate for table to display a qint64 with input range validation
// Also supports "" as a value
class SDRGUI_API Int64Delegate : public QStyledItemDelegate {
public:
Int64Delegate();
Int64Delegate(qint64 min, qint64 max);
virtual QString displayText(const QVariant &value, const QLocale &locale) const override;
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setMin(qint64 min) { m_min = min; }
void setMax(qint64 max) { m_max = max; }
void setRange(qint64 min, qint64 max);
qint64 min() const { return m_min; }
qint64 max() const { return m_max; }
private:
qint64 m_min;
qint64 m_max;
};
#endif // SDRGUI_GUI_INT64DELGATE_H

Wyświetl plik

@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "int64validator.h"
QValidator::State Int64Validator::validate(QString& input, int &pos) const
{
if (input == "") {
return QValidator::Acceptable;
}
if ((m_bottom < 0) && (input == "-")) {
return QValidator::Intermediate;
}
QRegularExpression re("-?\\d+");
QRegularExpressionMatch match = re.match(input);
if (match.hasMatch())
{
qint64 value = input.toLongLong();
if (value < m_bottom) {
return QValidator::Invalid;
}
if (value > m_top) {
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
else
{
return QValidator::Invalid;
}
}

Wyświetl plik

@ -0,0 +1,75 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE <jon@beniston.com> //
// //
// 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <limits>
#include <QValidator>
#include <QRegularExpression>
// Like QIntValidator but for qint64
class Int64Validator : public QValidator
{
Q_OBJECT
public:
Int64Validator(QObject *parent = nullptr) :
QValidator(parent),
m_bottom(-std::numeric_limits<qint64>::max()),
m_top(std::numeric_limits<qint64>::max())
{
}
Int64Validator(qint64 bottom, qint64 top, QObject *parent = nullptr) :
QValidator(parent),
m_bottom(bottom),
m_top(top)
{
}
void setBottom(qint64 bottom)
{
m_bottom = bottom;
}
void setTop(qint64 top)
{
m_top = top;
}
void setRange(qint64 bottom, qint64 top)
{
m_bottom = bottom;
m_top = top;
}
qint64 bottom() const
{
return m_bottom;
}
qint64 top() const
{
return m_top;
}
QValidator::State validate(QString& input, int &pos) const;
private:
qint64 m_bottom;
qint64 m_top;
};

Wyświetl plik

@ -69,3 +69,11 @@ FreqScannerFrequency:
type: integer
notes:
type: string
channel:
type: string
channelBandwidth:
type: string
threshold:
type: string
squelch:
type: string

Wyświetl plik

@ -7007,6 +7007,18 @@ margin-bottom: 20px;
},
"notes" : {
"type" : "string"
},
"channel" : {
"type" : "string"
},
"channelBandwidth" : {
"type" : "string"
},
"threshold" : {
"type" : "string"
},
"squelch" : {
"type" : "string"
}
}
};
@ -58401,7 +58413,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2023-11-05T10:45:45.965+01:00
Generated 2023-11-30T13:04:24.913+01:00
</div>
</div>
</div>

Wyświetl plik

@ -34,6 +34,14 @@ SWGFreqScannerFrequency::SWGFreqScannerFrequency() {
m_enabled_isSet = false;
notes = nullptr;
m_notes_isSet = false;
channel = nullptr;
m_channel_isSet = false;
channel_bandwidth = nullptr;
m_channel_bandwidth_isSet = false;
threshold = nullptr;
m_threshold_isSet = false;
squelch = nullptr;
m_squelch_isSet = false;
}
SWGFreqScannerFrequency::~SWGFreqScannerFrequency() {
@ -48,6 +56,14 @@ SWGFreqScannerFrequency::init() {
m_enabled_isSet = false;
notes = new QString("");
m_notes_isSet = false;
channel = new QString("");
m_channel_isSet = false;
channel_bandwidth = new QString("");
m_channel_bandwidth_isSet = false;
threshold = new QString("");
m_threshold_isSet = false;
squelch = new QString("");
m_squelch_isSet = false;
}
void
@ -57,6 +73,18 @@ SWGFreqScannerFrequency::cleanup() {
if(notes != nullptr) {
delete notes;
}
if(channel != nullptr) {
delete channel;
}
if(channel_bandwidth != nullptr) {
delete channel_bandwidth;
}
if(threshold != nullptr) {
delete threshold;
}
if(squelch != nullptr) {
delete squelch;
}
}
SWGFreqScannerFrequency*
@ -76,6 +104,14 @@ SWGFreqScannerFrequency::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&notes, pJson["notes"], "QString", "QString");
::SWGSDRangel::setValue(&channel, pJson["channel"], "QString", "QString");
::SWGSDRangel::setValue(&channel_bandwidth, pJson["channelBandwidth"], "QString", "QString");
::SWGSDRangel::setValue(&threshold, pJson["threshold"], "QString", "QString");
::SWGSDRangel::setValue(&squelch, pJson["squelch"], "QString", "QString");
}
QString
@ -101,6 +137,18 @@ SWGFreqScannerFrequency::asJsonObject() {
if(notes != nullptr && *notes != QString("")){
toJsonValue(QString("notes"), notes, obj, QString("QString"));
}
if(channel != nullptr && *channel != QString("")){
toJsonValue(QString("channel"), channel, obj, QString("QString"));
}
if(channel_bandwidth != nullptr && *channel_bandwidth != QString("")){
toJsonValue(QString("channelBandwidth"), channel_bandwidth, obj, QString("QString"));
}
if(threshold != nullptr && *threshold != QString("")){
toJsonValue(QString("threshold"), threshold, obj, QString("QString"));
}
if(squelch != nullptr && *squelch != QString("")){
toJsonValue(QString("squelch"), squelch, obj, QString("QString"));
}
return obj;
}
@ -135,6 +183,46 @@ SWGFreqScannerFrequency::setNotes(QString* notes) {
this->m_notes_isSet = true;
}
QString*
SWGFreqScannerFrequency::getChannel() {
return channel;
}
void
SWGFreqScannerFrequency::setChannel(QString* channel) {
this->channel = channel;
this->m_channel_isSet = true;
}
QString*
SWGFreqScannerFrequency::getChannelBandwidth() {
return channel_bandwidth;
}
void
SWGFreqScannerFrequency::setChannelBandwidth(QString* channel_bandwidth) {
this->channel_bandwidth = channel_bandwidth;
this->m_channel_bandwidth_isSet = true;
}
QString*
SWGFreqScannerFrequency::getThreshold() {
return threshold;
}
void
SWGFreqScannerFrequency::setThreshold(QString* threshold) {
this->threshold = threshold;
this->m_threshold_isSet = true;
}
QString*
SWGFreqScannerFrequency::getSquelch() {
return squelch;
}
void
SWGFreqScannerFrequency::setSquelch(QString* squelch) {
this->squelch = squelch;
this->m_squelch_isSet = true;
}
bool
SWGFreqScannerFrequency::isSet(){
@ -149,6 +237,18 @@ SWGFreqScannerFrequency::isSet(){
if(notes && *notes != QString("")){
isObjectUpdated = true; break;
}
if(channel && *channel != QString("")){
isObjectUpdated = true; break;
}
if(channel_bandwidth && *channel_bandwidth != QString("")){
isObjectUpdated = true; break;
}
if(threshold && *threshold != QString("")){
isObjectUpdated = true; break;
}
if(squelch && *squelch != QString("")){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}

Wyświetl plik

@ -51,6 +51,18 @@ public:
QString* getNotes();
void setNotes(QString* notes);
QString* getChannel();
void setChannel(QString* channel);
QString* getChannelBandwidth();
void setChannelBandwidth(QString* channel_bandwidth);
QString* getThreshold();
void setThreshold(QString* threshold);
QString* getSquelch();
void setSquelch(QString* squelch);
virtual bool isSet() override;
@ -64,6 +76,18 @@ private:
QString* notes;
bool m_notes_isSet;
QString* channel;
bool m_channel_isSet;
QString* channel_bandwidth;
bool m_channel_bandwidth_isSet;
QString* threshold;
bool m_threshold_isSet;
QString* squelch;
bool m_squelch_isSet;
};
}