mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-03-21 11:48:54 -04:00
Freq Scanner: Add per-frequency settings. Fix freq > 2GHz.
This commit is contained in:
parent
cdcb73f33a
commit
64f33717d0
@ -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")
|
||||
@ -782,7 +841,7 @@ void FreqScanner::webapiUpdateChannelSettings(
|
||||
if (channelSettingsKeys.contains("threshold")) {
|
||||
settings.m_threshold = response.getFreqScannerSettings()->getThreshold();
|
||||
}
|
||||
if (channelSettingsKeys.contains("frequencies"))
|
||||
/*if (channelSettingsKeys.contains("frequencies"))
|
||||
{
|
||||
settings.m_frequencies.clear();
|
||||
settings.m_enabled.clear();
|
||||
@ -801,7 +860,7 @@ void FreqScanner::webapiUpdateChannelSettings(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
if (channelSettingsKeys.contains("rgbColor")) {
|
||||
settings.m_rgbColor = response.getFreqScannerSettings()->getRgbColor();
|
||||
}
|
||||
@ -837,7 +896,7 @@ 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_frequencies.size(); i++)
|
||||
{
|
||||
SWGSDRangel::SWGFreqScannerFrequency *frequency = new SWGSDRangel::SWGFreqScannerFrequency();
|
||||
frequency->init();
|
||||
@ -847,7 +906,7 @@ QList<SWGSDRangel::SWGFreqScannerFrequency *> *FreqScanner::createFrequencyList(
|
||||
frequency->setNotes(new QString(settings.m_notes[i]));
|
||||
}
|
||||
frequencies->append(frequency);
|
||||
}
|
||||
}*/
|
||||
return frequencies;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
59
sdrgui/gui/int64delegate.cpp
Normal file
59
sdrgui/gui/int64delegate.cpp
Normal 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;
|
||||
}
|
46
sdrgui/gui/int64delegate.h
Normal file
46
sdrgui/gui/int64delegate.h
Normal 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
|
47
sdrgui/gui/int64validator.cpp
Normal file
47
sdrgui/gui/int64validator.cpp
Normal 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;
|
||||
}
|
||||
}
|
75
sdrgui/gui/int64validator.h
Normal file
75
sdrgui/gui/int64validator.h
Normal 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;
|
||||
};
|
Loading…
Reference in New Issue
Block a user