1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-07-30 20:52:26 -04:00

Freq Scanner: Add per-frequency settings. Fix freq > 2GHz.

This commit is contained in:
srcejon 2023-11-29 17:13:34 +00:00
parent cdcb73f33a
commit 64f33717d0
21 changed files with 823 additions and 148 deletions

View File

@ -249,6 +249,7 @@ bool FreqScanner::handleMessage(const Message& cmd)
} }
else if (MsgStartScan::match(cmd)) else if (MsgStartScan::match(cmd))
{ {
muteAll();
startScan(); startScan();
return true; return true;
@ -302,6 +303,9 @@ void FreqScanner::setDeviceCenterFrequency(qint64 frequency)
void FreqScanner::initScan() void FreqScanner::initScan()
{ {
if (m_scanChannelIndex < 0) {
applyChannelSetting(m_settings.m_channel);
}
ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, true); ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, true);
if (m_centerFrequency != m_stepStartFrequency) { if (m_centerFrequency != m_stepStartFrequency) {
@ -319,7 +323,6 @@ void FreqScanner::initScan()
void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<MsgScanResult::ScanResult>& results) void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<MsgScanResult::ScanResult>& results)
{ {
switch (m_state) switch (m_state)
{ {
case IDLE: case IDLE:
@ -329,10 +332,10 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
{ {
// Create ordered list of frequencies to scan // Create ordered list of frequencies to scan
QList<qint64> frequencies; 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]) { if (m_settings.m_frequencySettings[i].m_enabled) {
frequencies.append(m_settings.m_frequencies[i]); frequencies.append(m_settings.m_frequencySettings[i].m_frequency);
} }
} }
std::sort(frequencies.begin(), frequencies.end()); 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? // 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] if (m_settings.m_frequencySettings[i].m_enabled
&& (m_settings.m_frequencies[i] >= nextCenterFrequency - usableBW / 2) && (m_settings.m_frequencySettings[i].m_frequency >= nextCenterFrequency - usableBW / 2)
&& (m_settings.m_frequencies[i] < nextCenterFrequency + usableBW / 2)) && (m_settings.m_frequencySettings[i].m_frequency < nextCenterFrequency + usableBW / 2))
{ {
freqInRange = true; freqInRange = true;
break; break;
@ -416,51 +419,52 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
m_guiMessageQueue->push(msg); m_guiMessageQueue->push(msg);
} }
int frequency = m_scanResults[0].m_frequency; int frequency = -1;
Real maxPower = m_scanResults[0].m_power; FreqScannerSettings::FrequencySettings *frequencySettings = nullptr;
FreqScannerSettings::FrequencySettings *activeFrequencySettings = nullptr;
if (m_settings.m_priority == FreqScannerSettings::MAX_POWER) if (m_settings.m_priority == FreqScannerSettings::MAX_POWER)
{ {
// Find frequency with max power Real maxPower = -200.0f;
for (int i = 1; i < m_scanResults.size(); i++)
// 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; if (!activeFrequencySettings || (m_scanResults[i].m_power > maxPower))
maxPower = m_scanResults[i].m_power; {
frequency = m_scanResults[i].m_frequency;
maxPower = m_scanResults[i].m_power;
activeFrequencySettings = frequencySettings;
}
} }
} }
} }
else else
{ {
// Find first frequency in list above threshold // 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]) frequency = m_scanResults[i].m_frequency;
{ activeFrequencySettings = frequencySettings;
if (m_scanResults[i].m_power >= m_settings.m_threshold) break;
{
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) if (m_settings.m_mode != FreqScannerSettings::SCAN_ONLY)
{ {
// Is power above threshold // Were any frequencies found to be active?
if (maxPower >= m_settings.m_threshold) //if (maxPower >= m_settings.m_threshold)
if (activeFrequencySettings)
{ {
if (m_guiMessageQueue) {
m_guiMessageQueue->push(MsgReportActiveFrequency::create(frequency));
}
// Tune device/channel to frequency // Tune device/channel to frequency
int offset; int offset;
if ((frequency < m_centerFrequency - usableBW / 2) || (frequency >= m_centerFrequency + usableBW / 2)) 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; //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); ChannelWebAPIUtils::setFrequencyOffset(m_scanDeviceSetIndex, m_scanChannelIndex, offset);
// Unmute the channel // Unmute the channel
ChannelWebAPIUtils::setAudioMute(m_scanDeviceSetIndex, m_scanChannelIndex, false); 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; m_activeFrequency = frequency;
if (m_settings.m_mode == FreqScannerSettings::SINGLE) if (m_settings.m_mode == FreqScannerSettings::SINGLE)
@ -514,11 +535,16 @@ void FreqScanner::processScanResults(const QDateTime& fftStartTime, const QList<
// Wait for transmission to finish // Wait for transmission to finish
m_state = WAIT_FOR_END_TX; 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 else
{ {
if (m_guiMessageQueue) { 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 // 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.setSingleShot(true);
m_timeoutTimer.start((int)(m_settings.m_retransmitTime * 1000.0)); 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 // 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_timeoutTimer.stop();
m_state = WAIT_FOR_END_TX; 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 // But ensure we have several bins per channel
// Adjust sample rate, to ensure we don't get massive FFT size // Adjust sample rate, to ensure we don't get massive FFT size
scannerSampleRate = basebandSampleRate; scannerSampleRate = basebandSampleRate;
if (scannerSampleRate < channelBW) {
channelBW = scannerSampleRate; // Prevent divide by 0
}
while (fftSize / (scannerSampleRate / channelBW) < minBinsPerChannel) while (fftSize / (scannerSampleRate / channelBW) < minBinsPerChannel)
{ {
if (fftSize == maxFFTSize) { 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) void FreqScanner::applySettings(const FreqScannerSettings& settings, const QStringList& settingsKeys, bool force)
{ {
qDebug() << "FreqScanner::applySettings:" 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) if (m_running)
{ {
FreqScannerBaseband::MsgConfigureFreqScannerBaseband *msg = FreqScannerBaseband::MsgConfigureFreqScannerBaseband::create(settings, settingsKeys, force); 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); webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
} }
if (settingsKeys.contains("frequencies") if (settingsKeys.contains("frequencySettings")
|| settingsKeys.contains("priority") || settingsKeys.contains("priority")
|| settingsKeys.contains("measurement") || settingsKeys.contains("measurement")
|| settingsKeys.contains("mode") || settingsKeys.contains("mode")
@ -782,7 +841,7 @@ void FreqScanner::webapiUpdateChannelSettings(
if (channelSettingsKeys.contains("threshold")) { if (channelSettingsKeys.contains("threshold")) {
settings.m_threshold = response.getFreqScannerSettings()->getThreshold(); settings.m_threshold = response.getFreqScannerSettings()->getThreshold();
} }
if (channelSettingsKeys.contains("frequencies")) /*if (channelSettingsKeys.contains("frequencies"))
{ {
settings.m_frequencies.clear(); settings.m_frequencies.clear();
settings.m_enabled.clear(); settings.m_enabled.clear();
@ -801,7 +860,7 @@ void FreqScanner::webapiUpdateChannelSettings(
} }
} }
} }
} }*/
if (channelSettingsKeys.contains("rgbColor")) { if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getFreqScannerSettings()->getRgbColor(); settings.m_rgbColor = response.getFreqScannerSettings()->getRgbColor();
} }
@ -837,7 +896,7 @@ void FreqScanner::webapiUpdateChannelSettings(
QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(const FreqScannerSettings& settings) QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(const FreqScannerSettings& settings)
{ {
QList<SWGSDRangel::SWGFreqScannerFrequency *> *frequencies = new QList<SWGSDRangel::SWGFreqScannerFrequency *>(); 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_frequencies.size(); i++)
{ {
SWGSDRangel::SWGFreqScannerFrequency *frequency = new SWGSDRangel::SWGFreqScannerFrequency(); SWGSDRangel::SWGFreqScannerFrequency *frequency = new SWGSDRangel::SWGFreqScannerFrequency();
frequency->init(); frequency->init();
@ -847,7 +906,7 @@ QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(
frequency->setNotes(new QString(settings.m_notes[i])); frequency->setNotes(new QString(settings.m_notes[i]));
} }
frequencies->append(frequency); frequencies->append(frequency);
} }*/
return frequencies; return frequencies;
} }

View File

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

View File

@ -22,6 +22,7 @@
#include <QTableWidget> #include <QTableWidget>
#include <QTableWidgetItem> #include <QTableWidgetItem>
#include <QRegExp> #include <QRegExp>
#include <QComboBox>
#include "device/deviceset.h" #include "device/deviceset.h"
#include "device/deviceuiset.h" #include "device/deviceuiset.h"
@ -37,6 +38,7 @@
#include "gui/dialogpositioner.h" #include "gui/dialogpositioner.h"
#include "gui/decimaldelegate.h" #include "gui/decimaldelegate.h"
#include "gui/frequencydelegate.h" #include "gui/frequencydelegate.h"
#include "gui/int64delegate.h"
#include "gui/glspectrum.h" #include "gui/glspectrum.h"
#include "channel/channelwebapiutils.h" #include "channel/channelwebapiutils.h"
@ -103,9 +105,9 @@ bool FreqScannerGUI::handleMessage(const Message& message)
m_basebandSampleRate = notif.getSampleRate(); m_basebandSampleRate = notif.getSampleRate();
if (m_basebandSampleRate != 0) 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->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) { if (m_channelMarker.getBandwidth() == 0) {
m_channelMarker.setBandwidth(m_basebandSampleRate); m_channelMarker.setBandwidth(m_basebandSampleRate);
@ -116,7 +118,8 @@ bool FreqScannerGUI::handleMessage(const Message& message)
else if (FreqScanner::MsgReportChannels::match(message)) else if (FreqScanner::MsgReportChannels::match(message))
{ {
FreqScanner::MsgReportChannels& report = (FreqScanner::MsgReportChannels&)message; FreqScanner::MsgReportChannels& report = (FreqScanner::MsgReportChannels&)message;
updateChannelsList(report.getChannels()); m_availableChannels = report.getChannels();
updateChannelsList(m_availableChannels);
return true; return true;
} }
else if (FreqScanner::MsgStatus::match(message)) else if (FreqScanner::MsgStatus::match(message))
@ -187,11 +190,14 @@ bool FreqScannerGUI::handleMessage(const Message& message)
{ {
qint64 freq = results[i].m_frequency; qint64 freq = results[i].m_frequency;
QList<QTableWidgetItem *> items = ui->table->findItems(QString::number(freq), Qt::MatchExactly); QList<QTableWidgetItem *> items = ui->table->findItems(QString::number(freq), Qt::MatchExactly);
for (auto item : items) { for (auto item : items)
{
int row = item->row(); int row = item->row();
QTableWidgetItem* powerItem = ui->table->item(row, COL_POWER); QTableWidgetItem* powerItem = ui->table->item(row, COL_POWER);
powerItem->setData(Qt::DisplayRole, results[i].m_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) if (active)
{ {
powerItem->setBackground(Qt::darkGreen); powerItem->setBackground(Qt::darkGreen);
@ -206,10 +212,13 @@ bool FreqScannerGUI::handleMessage(const Message& message)
return false; 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); combo->blockSignals(true);
ui->channels->clear(); combo->clear();
if (empty) {
combo->addItem("");
}
for (const auto& channel : channels) 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())) 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); 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 // Channel can be created after this plugin, so select it
// if the chosen channel appears // if the chosen channel appears
int channelIndex = ui->channels->findText(m_settings.m_channel); int channelIndex = combo->findText(channel);
if (channelIndex >= 0) { if (channelIndex >= 0) {
ui->channels->setCurrentIndex(channelIndex); combo->setCurrentIndex(channelIndex);
} else { } 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) 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->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); 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->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.setColor(Qt::yellow);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); 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_FREQUENCY, new FrequencyDelegate("Auto", 3));
ui->table->setItemDelegateForColumn(COL_POWER, new DecimalDelegate(1)); 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); connect(m_deviceUISet->m_spectrum->getSpectrumView(), &GLSpectrumView::updateAnnotations, this, &FreqScannerGUI::updateAnnotations);
} }
@ -531,9 +554,9 @@ void FreqScannerGUI::displaySettings()
ui->table->blockSignals(true); ui->table->blockSignals(true);
ui->table->setRowCount(0); 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); updateAnnotation(i);
} }
ui->table->blockSignals(false); 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(); int row = ui->table->rowCount();
ui->table->setRowCount(row + 1); 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); annotationItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
ui->table->setItem(row, COL_ANNOTATION, annotationItem); 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(); QTableWidgetItem *enableItem = new QTableWidgetItem();
enableItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); 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); ui->table->setItem(row, COL_ENABLE, enableItem);
QTableWidgetItem* powerItem = new QTableWidgetItem(); 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); ui->table->setItem(row, COL_ACTIVE_COUNT, activeCountItem);
activeCountItem->setData(Qt::DisplayRole, 0); activeCountItem->setData(Qt::DisplayRole, 0);
QTableWidgetItem* notesItem = new QTableWidgetItem(notes); QTableWidgetItem* notesItem = new QTableWidgetItem(frequencySettings.m_notes);
ui->table->setItem(row, COL_NOTES, notesItem); 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() 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() void FreqScannerGUI::on_addRange_clicked()
@ -626,11 +676,15 @@ void FreqScannerGUI::on_addRange_clicked()
if (dialog.exec()) if (dialog.exec())
{ {
blockApplySettings(true); blockApplySettings(true);
for (const auto f : dialog.m_frequencies) { for (const auto f : dialog.m_frequencies)
addRow(f, true); {
FreqScannerSettings::FrequencySettings frequencySettings;
frequencySettings.m_frequency = f;
frequencySettings.m_enabled = true;
addRow(frequencySettings);
} }
blockApplySettings(false); blockApplySettings(false);
applySetting("frequencies"); applySetting("frequencySettings");
} }
} }
@ -642,11 +696,9 @@ void FreqScannerGUI::on_remove_clicked()
{ {
int row = ui->table->row(item); int row = ui->table->row(item);
ui->table->removeRow(row); ui->table->removeRow(row);
m_settings.m_frequencies.removeAt(row); // table_cellChanged isn't called for removeRow m_settings.m_frequencySettings.removeAt(row);
m_settings.m_enabled.removeAt(row);
m_settings.m_notes.removeAt(row);
} }
applySetting("frequencies"); applySetting("frequencySettings");
} }
void FreqScannerGUI::on_removeInactive_clicked() 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) if (ui->table->item(i, COL_ACTIVE_COUNT)->data(Qt::DisplayRole).toInt() == 0)
{ {
ui->table->removeRow(i); ui->table->removeRow(i);
m_settings.m_frequencies.removeAt(i); // table_cellChanged isn't called for removeRow m_settings.m_frequencySettings.removeAt(i);
m_settings.m_enabled.removeAt(i);
m_settings.m_notes.removeAt(i);
} }
} }
applySetting("frequencies"); applySetting("frequencySettings");
} }
static QList<QTableWidgetItem*> takeRow(QTableWidget* table, int row) 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) if (column == COL_FREQUENCY)
{ {
qint64 value = item->text().toLongLong(); 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); FreqScannerSettings::FrequencySettings frequencySettings;
m_settings.m_enabled.append(true); frequencySettings.m_frequency = 0;
m_settings.m_notes.append(""); 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); updateAnnotation(row);
applySetting("frequencies"); applySetting("frequencySettings");
} }
else if (column == COL_ENABLE) else if (column == COL_ENABLE)
{ {
m_settings.m_enabled[row] = item->checkState() == Qt::Checked; m_settings.m_frequencySettings[row].m_enabled = item->checkState() == Qt::Checked;
applySetting("frequencies"); applySetting("frequencySettings");
} }
else if (column == COL_NOTES) else if (column == COL_NOTES)
{ {
m_settings.m_notes[row] = item->text(); m_settings.m_frequencySettings[row].m_notes = item->text();
applySetting("frequencies"); 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_ENABLE, new QTableWidgetItem("Enable"));
ui->table->setItem(row, COL_POWER, new QTableWidgetItem("-100.0")); 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_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->resizeColumnsToContents();
ui->table->setRowCount(row); ui->table->setRowCount(row);
} }

View File

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

View File

@ -671,6 +671,46 @@
<string>User notes about this frequency</string> <string>User notes about this frequency</string>
</property> </property>
</column> </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> </widget>
</item> </item>
<item> <item>
@ -783,18 +823,18 @@
<extends>QToolButton</extends> <extends>QToolButton</extends>
<header>gui/buttonswitch.h</header> <header>gui/buttonswitch.h</header>
</customwidget> </customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>ValueDialZ</class> <class>ValueDialZ</class>
<extends>QWidget</extends> <extends>QWidget</extends>
<header>gui/valuedialz.h</header> <header>gui/valuedialz.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>deltaFrequency</tabstop> <tabstop>deltaFrequency</tabstop>

View File

@ -16,6 +16,7 @@
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
#include <QColor> #include <QColor>
#include <QDebug>
#include "util/simpleserializer.h" #include "util/simpleserializer.h"
#include "settings/serializable.h" #include "settings/serializable.h"
@ -40,6 +41,7 @@ void FreqScannerSettings::resetToDefaults()
m_channelFrequencyOffset = 25000; m_channelFrequencyOffset = 25000;
m_threshold = -60.0f; m_threshold = -60.0f;
m_channel = ""; m_channel = "";
m_frequencySettings = {};
m_scanTime = 0.1f; m_scanTime = 0.1f;
m_retransmitTime = 2.0f; m_retransmitTime = 2.0f;
m_tuneTime = 100; m_tuneTime = 100;
@ -73,9 +75,6 @@ QByteArray FreqScannerSettings::serialize() const
s.writeS32(2, m_channelBandwidth); s.writeS32(2, m_channelBandwidth);
s.writeS32(3, m_channelFrequencyOffset); s.writeS32(3, m_channelFrequencyOffset);
s.writeFloat(4, m_threshold); 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.writeString(8, m_channel);
s.writeFloat(9, m_scanTime); s.writeFloat(9, m_scanTime);
s.writeFloat(10, m_retransmitTime); s.writeFloat(10, m_retransmitTime);
@ -83,6 +82,7 @@ QByteArray FreqScannerSettings::serialize() const
s.writeS32(12, (int)m_priority); s.writeS32(12, (int)m_priority);
s.writeS32(13, (int)m_measurement); s.writeS32(13, (int)m_measurement);
s.writeS32(14, (int)m_mode); s.writeS32(14, (int)m_mode);
s.writeList(15, m_frequencySettings);
s.writeList(20, m_columnIndexes); s.writeList(20, m_columnIndexes);
s.writeList(21, m_columnSizes); s.writeList(21, m_columnSizes);
@ -128,22 +128,37 @@ bool FreqScannerSettings::deserialize(const QByteArray& data)
d.readS32(2, &m_channelBandwidth, 25000); d.readS32(2, &m_channelBandwidth, 25000);
d.readS32(3, &m_channelFrequencyOffset, 25000); d.readS32(3, &m_channelFrequencyOffset, 25000);
d.readFloat(4, &m_threshold, -60.0f); 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); 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(9, &m_scanTime, 0.1f);
d.readFloat(10, &m_retransmitTime, 2.0f); d.readFloat(10, &m_retransmitTime, 2.0f);
d.readS32(11, &m_tuneTime, 100); d.readS32(11, &m_tuneTime, 100);
d.readS32(12, (int*)&m_priority, (int)MAX_POWER); d.readS32(12, (int*)&m_priority, (int)MAX_POWER);
d.readS32(13, (int*)&m_measurement, (int)PEAK); d.readS32(13, (int*)&m_measurement, (int)PEAK);
d.readS32(14, (int*)&m_mode, (int)CONTINUOUS); 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(20, &m_columnIndexes);
d.readList(21, &m_columnSizes); d.readList(21, &m_columnSizes);
@ -200,10 +215,8 @@ void FreqScannerSettings::applySettings(const QStringList& settingsKeys, const F
if (settingsKeys.contains("threshold")) { if (settingsKeys.contains("threshold")) {
m_threshold = settings.m_threshold; m_threshold = settings.m_threshold;
} }
if (settingsKeys.contains("frequencies")) { if (settingsKeys.contains("frequencySettings")) {
m_frequencies = settings.m_frequencies; m_frequencySettings = settings.m_frequencySettings;
m_enabled = settings.m_enabled;
m_notes = settings.m_notes;
} }
if (settingsKeys.contains("channel")) { if (settingsKeys.contains("channel")) {
m_channel = settings.m_channel; m_channel = settings.m_channel;
@ -280,13 +293,13 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo
if (settingsKeys.contains("threshold") || force) { if (settingsKeys.contains("threshold") || force) {
ostr << " m_threshold: " << m_threshold; ostr << " m_threshold: " << m_threshold;
} }
if (settingsKeys.contains("frequencies") || force) if (settingsKeys.contains("frequencySettings") || force)
{ {
QStringList s; QStringList s;
for (auto f : m_frequencies) { for (auto f : m_frequencySettings) {
s.append(QString::number(f)); s.append(QString::number(f.m_frequency));
} }
ostr << " m_frequencies: " << s.join(",").toStdString(); ostr << " m_frequencySettings: " << s.join(",").toStdString();
} }
if (settingsKeys.contains("channel") || force) { if (settingsKeys.contains("channel") || force) {
ostr << " m_channel: " << m_channel.toStdString(); ostr << " m_channel: " << m_channel.toStdString();
@ -348,3 +361,105 @@ QString FreqScannerSettings::getDebugString(const QStringList& settingsKeys, boo
return QString(ostr.str().c_str()); 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;
}

View File

@ -27,7 +27,7 @@ class Serializable;
class ChannelAPI; class ChannelAPI;
// Number of columns in the table // Number of columns in the table
#define FREQSCANNER_COLUMNS 6 #define FREQSCANNER_COLUMNS 10
struct FreqScannerSettings struct FreqScannerSettings
{ {
@ -41,14 +41,25 @@ struct FreqScannerSettings
AvailableChannel& operator=(const AvailableChannel&) = default; 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_inputFrequencyOffset; //!< Not modifable in GUI
qint32 m_channelBandwidth; //!< Channel bandwidth qint32 m_channelBandwidth; //!< Channel bandwidth
qint32 m_channelFrequencyOffset;//!< Minium DC offset of tuned channel qint32 m_channelFrequencyOffset;//!< Minium DC offset of tuned channel
Real m_threshold; //!< Power threshold in dB 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 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_scanTime; //!< In seconds
float m_retransmitTime; //!< In seconds float m_retransmitTime; //!< In seconds
int m_tuneTime; //!< In milliseconds int m_tuneTime; //!< In milliseconds
@ -92,6 +103,9 @@ struct FreqScannerSettings
bool deserialize(const QByteArray& data); bool deserialize(const QByteArray& data);
void applySettings(const QStringList& settingsKeys, const FreqScannerSettings& settings); void applySettings(const QStringList& settingsKeys, const FreqScannerSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force = false) const; 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 */ #endif /* INCLUDE_FREQSCANNERSETTINGS_H */

View File

@ -107,11 +107,11 @@ void FreqScannerSink::processOneSample(Complex &ci)
FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(m_fftStartTime); FreqScanner::MsgScanResult* msg = FreqScanner::MsgScanResult::create(m_fftStartTime);
QList<FreqScanner::MsgScanResult::ScanResult>& results = msg->getScanResults(); 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 startFrequency = m_centerFrequency - m_scannerSampleRate / 2;
qint64 diff = frequency - startFrequency; qint64 diff = frequency - startFrequency;
float binBW = m_scannerSampleRate / (float)m_fftSize; 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)) if ((diff < m_scannerSampleRate * 0.875f) && (diff >= m_scannerSampleRate * 0.125f))
{ {
int bin = std::round(diff / binBW); 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 // Calculate power at that frequency
Real power; Real power;
if (m_settings.m_measurement == FreqScannerSettings::PEAK) { if (m_settings.m_measurement == FreqScannerSettings::PEAK) {
power = peakPower(bin); power = peakPower(bin, channelBins);
} else { } 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; //qDebug() << "startFrequency:" << startFrequency << "m_scannerSampleRate:" << m_scannerSampleRate << "m_centerFrequency:" << m_centerFrequency << "frequency" << frequency << "bin" << bin << "power" << power;
FreqScanner::MsgScanResult::ScanResult result = {frequency, 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) // 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 // Skip bin between halfway between channels
// Then skip first and last bins, to avoid spectral leakage (particularly at DC) // 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; 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; int idx = startBin + i;
if ((idx < 0) || (idx >= m_fftSize)) { if ((idx < 0) || (idx >= m_fftSize)) {
continue; continue;
@ -162,13 +173,13 @@ Real FreqScannerSink::totalPower(int bin) const
} }
// Calculate peak power in a channel containing the specified bin // 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 // Skip bin between halfway between channels
// Then skip first and last bins, to avoid spectral leakage (particularly at DC) // 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(); 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; int idx = startBin + i;
if ((idx < 0) || (idx >= m_fftSize)) { if ((idx < 0) || (idx >= m_fftSize)) {

View File

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

View File

@ -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). - 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. - 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. - 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. When an active frequency is found after a scan, the corresponding row in the table will be selected.

View File

@ -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) bool ChannelWebAPIUtils::patchChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, const QJsonArray& value)
{ {
SWGSDRangel::SWGChannelSettings channelSettingsResponse; SWGSDRangel::SWGChannelSettings channelSettingsResponse;

View File

@ -73,6 +73,7 @@ public:
static bool patchDeviceSetting(unsigned int deviceIndex, const QString &setting, int value); 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, const QString &value);
static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double 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 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, int &value);
static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double &value); static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double &value);

View File

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

View File

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

View File

@ -18,10 +18,40 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // // along with this program. If not, see <http://www.gnu.org/licenses/>. //
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
#include <QLineEdit>
#include <QDoubleValidator>
#include "decimaldelegate.h" #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) : 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(); 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;
}

View File

@ -26,17 +26,23 @@
#include "export.h" #include "export.h"
// Deligate for table to control precision used to display floating point values - also supports strings // 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 { class SDRGUI_API DecimalDelegate : public QStyledItemDelegate {
public: public:
DecimalDelegate(int precision = 2); DecimalDelegate(int precision = 2);
DecimalDelegate(int precision, double min, double max);
virtual QString displayText(const QVariant &value, const QLocale &locale) const override; 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; } int getPrecision() const { return m_precision; }
void setPrecision(int precision) { m_precision = precision; } void setPrecision(int precision) { m_precision = precision; }
void setRange(double min, double max);
private: private:
int m_precision; int m_precision;
double m_min;
double m_max;
}; };

View File

@ -21,6 +21,7 @@
#include <QLineEdit> #include <QLineEdit>
#include "frequencydelegate.h" #include "frequencydelegate.h"
#include "int64validator.h"
FrequencyDelegate::FrequencyDelegate(const QString& units, int precision, bool group) : FrequencyDelegate::FrequencyDelegate(const QString& units, int precision, bool group) :
m_units(units), 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 QWidget* FrequencyDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{ {
(void) option; (void) option;
(void) index; (void) index;
QLineEdit* editor = new QLineEdit(parent); QLineEdit* editor = new QLineEdit(parent);
QIntValidator* validator = new QIntValidator(); Int64Validator* validator = new Int64Validator();
validator->setBottom(0); validator->setBottom(0);
editor->setValidator(validator); editor->setValidator(validator);
return editor; return editor;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
};