From 1005d1d4e531b59707ab76d37fca4a19bd10ea09 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 11 Nov 2018 02:30:10 +0100 Subject: [PATCH] SoapySDR support: input: stream ArgInfo GUI --- .../soapysdrinput/soapysdrinput.cpp | 25 +++++ .../soapysdrinput/soapysdrinput.h | 2 + .../soapysdrinput/soapysdrinputgui.cpp | 105 ++++++++++++++++++ .../soapysdrinput/soapysdrinputgui.h | 5 + .../soapysdrinput/soapysdrinputsettings.cpp | 21 ++++ .../soapysdrinput/soapysdrinputsettings.h | 4 + sdrgui/CMakeLists.txt | 2 + sdrgui/soapygui/arginfogui.cpp | 34 +++++- sdrgui/soapygui/arginfogui.h | 2 + sdrgui/soapygui/arginfogui.ui | 18 ++- sdrgui/soapygui/dynamicargsettinggui.cpp | 88 +++++++++++++++ sdrgui/soapygui/dynamicargsettinggui.h | 52 +++++++++ 12 files changed, 353 insertions(+), 5 deletions(-) create mode 100644 sdrgui/soapygui/dynamicargsettinggui.cpp create mode 100644 sdrgui/soapygui/dynamicargsettinggui.h diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp index 9357854b9..711489128 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp @@ -269,6 +269,12 @@ const std::vector& SoapySDRInput::getIndividu return channelSettings->m_gainSettings; } +const SoapySDR::ArgInfoList& SoapySDRInput::getStreamArgInfoList() +{ + const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getRxChannelSettings(m_deviceShared.m_channel); + return channelSettings->m_streamSettingsArgs; +} + void SoapySDRInput::initGainSettings(SoapySDRInputSettings& settings) { const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getRxChannelSettings(m_deviceShared.m_channel); @@ -282,6 +288,25 @@ void SoapySDRInput::initGainSettings(SoapySDRInputSettings& settings) updateGains(m_deviceShared.m_device, m_deviceShared.m_channel, settings); } +void SoapySDRInput::initStreamArgSettings(SoapySDRInputSettings& settings) +{ + const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getRxChannelSettings(m_deviceShared.m_channel); + settings.m_streamArgSettings.clear(); + + for (const auto &it : channelSettings->m_streamSettingsArgs) + { + if (it.type == SoapySDR::ArgInfo::BOOL) { + settings.m_streamArgSettings[QString(it.key.c_str())] = QVariant(it.value == "true"); + } else if (it.type == SoapySDR::ArgInfo::INT) { + settings.m_streamArgSettings[QString(it.key.c_str())] = QVariant(atoi(it.value.c_str())); + } else if (it.type == SoapySDR::ArgInfo::FLOAT) { + settings.m_streamArgSettings[QString(it.key.c_str())] = QVariant(atof(it.value.c_str())); + } else if (it.type == SoapySDR::ArgInfo::STRING) { + settings.m_streamArgSettings[QString(it.key.c_str())] = QVariant(it.value.c_str()); + } + } +} + bool SoapySDRInput::hasDCAutoCorrection() { const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getRxChannelSettings(m_deviceShared.m_channel); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.h b/plugins/samplesource/soapysdrinput/soapysdrinput.h index d18c5194e..e5d4002b9 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.h @@ -155,7 +155,9 @@ public: int getAntennaIndex(const std::string& antenna); const std::vector& getTunableElements(); const std::vector& getIndividualGainsRanges(); + const SoapySDR::ArgInfoList& getStreamArgInfoList(); void initGainSettings(SoapySDRInputSettings& settings); + void initStreamArgSettings(SoapySDRInputSettings& settings); bool hasDCAutoCorrection(); bool hasDCCorrectionValue(); bool hasIQAutoCorrection() { return false; } // not in SoapySDR interface diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index da4a0dfb4..49a7f010d 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -29,6 +29,8 @@ #include "soapygui/dynamicitemsettinggui.h" #include "soapygui/intervalslidergui.h" #include "soapygui/complexfactorgui.h" +#include "soapygui/arginfogui.h" +#include "soapygui/dynamicargsettinggui.h" #include "ui_soapysdrinputgui.h" #include "soapysdrinputgui.h" @@ -68,7 +70,9 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : createTunableElementsControl(m_sampleSource->getTunableElements()); createGlobalGainControl(); createIndividualGainsControl(m_sampleSource->getIndividualGainsRanges()); + createStreamArgumentsControl(m_sampleSource->getStreamArgInfoList()); m_sampleSource->initGainSettings(m_settings); + m_sampleSource->initStreamArgSettings(m_settings); if (m_sampleRateGUI) { connect(m_sampleRateGUI, SIGNAL(valueChanged(double)), this, SLOT(sampleRateChanged(double))); @@ -306,6 +310,86 @@ void SoapySDRInputGui::createCorrectionsControl() } } +void SoapySDRInputGui::createStreamArgumentsControl(const SoapySDR::ArgInfoList& argInfoList) +{ + if (argInfoList.size() == 0) { // return early if list is empty + return; + } + + QVBoxLayout *layout = (QVBoxLayout *) ui->scrollAreaWidgetContents->layout(); + + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + layout->addWidget(line); + + std::vector::const_iterator it = argInfoList.begin(); + + for (; it != argInfoList.end(); ++it) + { + ArgInfoGUI::ArgInfoValueType valueType; + ArgInfoGUI *argGUI; + + if (it->type == SoapySDR::ArgInfo::BOOL) { + valueType = ArgInfoGUI::ArgInfoValueBool; + } else if (it->type == SoapySDR::ArgInfo::INT) { + valueType = ArgInfoGUI::ArgInfoValueInt; + } else if (it->type == SoapySDR::ArgInfo::FLOAT) { + valueType = ArgInfoGUI::ArgInfoValueFloat; + } else if (it->type == SoapySDR::ArgInfo::STRING) { + valueType = ArgInfoGUI::ArgInfoValueString; + } else { + continue; + } + + if (valueType == ArgInfoGUI::ArgInfoValueBool) + { + argGUI = new ArgInfoGUI(ArgInfoGUI::ArgInfoBinary, ArgInfoGUI::ArgInfoValueBool, this); + } + else if (it->options.size() == 0) + { + argGUI = new ArgInfoGUI(ArgInfoGUI::ArgInfoContinuous, valueType, this); + } + else + { + argGUI = new ArgInfoGUI(ArgInfoGUI::ArgInfoDiscrete, valueType, this); + std::vector::const_iterator optionIt = it->options.begin(); + std::vector::const_iterator optionNameIt = it->optionNames.begin(); + + for (int i = 0; optionIt != it->options.end(); ++optionIt, i++) + { + QString name(optionNameIt == it->optionNames.end() ? optionIt->c_str() : optionNameIt->c_str()); + ++optionNameIt; + + if (valueType == ArgInfoGUI::ArgInfoValueInt) { + argGUI->addIntValue(name, atoi(optionIt->c_str())); + } else if (valueType == ArgInfoGUI::ArgInfoValueFloat) { + argGUI->addFloatValue(name, atof(optionIt->c_str())); + } else if (valueType == ArgInfoGUI::ArgInfoValueString) { + argGUI->addStringValue(name, QString(optionIt->c_str())); + } + } + } + + if ((it->range.minimum() != 0) || (it->range.maximum() != 0)) { + argGUI->setRange(it->range.minimum(), it->range.maximum()); + } + + argGUI->setLabel(QString(it->name.size() == 0 ? it->key.c_str() : it->name.c_str())); + argGUI->setUnits(QString(it->units.c_str())); + + if (it->description.size() != 0) { + argGUI->setToolTip(QString(it->description.c_str())); + } + + layout->addWidget(argGUI); + + DynamicArgSettingGUI *gui = new DynamicArgSettingGUI(argGUI, QString(it->key.c_str())); + m_streamArgsGUIs.push_back(gui); + connect(gui, SIGNAL(valueChanged(QString, value)), this, SLOT(streamArgChanged(QString, QVariant))); + } +} + void SoapySDRInputGui::setName(const QString& name) { setObjectName(name); @@ -512,6 +596,12 @@ void SoapySDRInputGui::iqCorrectionArgumentChanged(double value) sendSettings(); } +void SoapySDRInputGui::streamArgChanged(QString itemName, QVariant value) +{ + m_settings.m_streamArgSettings[itemName] = value; + sendSettings(); +} + void SoapySDRInputGui::on_centerFrequency_changed(quint64 value) { m_settings.m_centerFrequency = value * 1000; @@ -631,6 +721,7 @@ void SoapySDRInputGui::displaySettings() displayTunableElementsControlSettings(); displayIndividualGainsControlSettings(); displayCorrectionsSettings(); + displayStreamArgsSettings(); blockApplySettings(false); } @@ -686,6 +777,20 @@ void SoapySDRInputGui::displayCorrectionsSettings() } } +void SoapySDRInputGui::displayStreamArgsSettings() +{ + for (const auto &it : m_streamArgsGUIs) + { + QMap::iterator elIt = m_settings.m_streamArgSettings.find(it->getName()); + + if (elIt != m_settings.m_streamArgSettings.end()) + { + it->setValue(*elIt); + *elIt = it->getValue(); + } + } +} + void SoapySDRInputGui::sendSettings() { if (!m_updateTimer.isActive()) { diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h index 7f65de91a..262cd9e8b 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.h @@ -29,6 +29,7 @@ class DeviceUISet; class ItemSettingGUI; class StringRangeGUI; class DynamicItemSettingGUI; +class DynamicArgSettingGUI; class IntervalSliderGUI; class QCheckBox; class ComplexFactorGUI; @@ -67,6 +68,7 @@ private: void createGlobalGainControl(); void createIndividualGainsControl(const std::vector& individualGainsList); void createCorrectionsControl(); + void createStreamArgumentsControl(const SoapySDR::ArgInfoList& argInfoList); Ui::SoapySDRInputGui* ui; @@ -93,11 +95,13 @@ private: ComplexFactorGUI *m_iqCorrectionGUI; QCheckBox *m_autoDCCorrection; QCheckBox *m_autoIQCorrection; + std::vector m_streamArgsGUIs; void displaySettings(); void displayTunableElementsControlSettings(); void displayIndividualGainsControlSettings(); void displayCorrectionsSettings(); + void displayStreamArgsSettings(); void sendSettings(); void updateSampleRateAndFrequency(); void updateFrequencyLimits(); @@ -119,6 +123,7 @@ private slots: void dcCorrectionArgumentChanged(double value); void iqCorrectionModuleChanged(double value); void iqCorrectionArgumentChanged(double value); + void streamArgChanged(QString itemName, QVariant value); void on_centerFrequency_changed(quint64 value); void on_LOppm_valueChanged(int value); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp index 7cceae2c4..fc793dccc 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.cpp @@ -71,6 +71,7 @@ QByteArray SoapySDRInputSettings::serialize() const s.writeDouble(18, m_dcCorrection.imag()); s.writeDouble(19, m_iqCorrection.real()); s.writeDouble(20, m_iqCorrection.imag()); + s.writeBlob(21, serializeArgumentMap(m_streamArgSettings)); return s.final(); } @@ -116,6 +117,8 @@ bool SoapySDRInputSettings::deserialize(const QByteArray& data) d.readDouble(19, &realval, 0); d.readDouble(20, &imagval, 0); m_iqCorrection = std::complex{realval, imagval}; + d.readBlob(21, &blob); + deserializeArgumentMap(blob, m_streamArgSettings); return true; } @@ -142,3 +145,21 @@ void SoapySDRInputSettings::deserializeNamedElementMap(const QByteArray& data, Q (*stream) >> map; delete stream; } + +QByteArray SoapySDRInputSettings::serializeArgumentMap(const QMap& map) const +{ + QByteArray data; + QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly); + (*stream) << map; + delete stream; + + return data; +} + +void deserializeArgumentMap(const QByteArray& data, QMap& map) +{ + QDataStream *stream = new QDataStream(data); + (*stream) >> map; + delete stream; +} + diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h index 4bbb2db47..1cc7e47be 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h +++ b/plugins/samplesource/soapysdrinput/soapysdrinputsettings.h @@ -19,6 +19,7 @@ #include #include +#include #include struct SoapySDRInputSettings { @@ -48,6 +49,7 @@ struct SoapySDRInputSettings { bool m_autoIQCorrection; std::complex m_dcCorrection; std::complex m_iqCorrection; + QMap m_streamArgSettings; SoapySDRInputSettings(); void resetToDefaults(); @@ -57,6 +59,8 @@ struct SoapySDRInputSettings { private: QByteArray serializeNamedElementMap(const QMap& map) const; void deserializeNamedElementMap(const QByteArray& data, QMap& map); + QByteArray serializeArgumentMap(const QMap& map) const; + void deserializeArgumentMap(const QByteArray& data, QMap& map); }; #endif /* PLUGINS_SAMPLESOURCE_SOAPYSDRINPUT_SOAPYSDRINPUTSETTINGS_H_ */ diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 6a5470142..9b2de21ab 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -59,6 +59,7 @@ set(sdrgui_SOURCES soapygui/itemsettinggui.cpp soapygui/stringrangegui.cpp soapygui/dynamicitemsettinggui.cpp + soapygui/dynamicargsettinggui.cpp soapygui/intervalslidergui.cpp soapygui/complexfactorgui.cpp soapygui/arginfogui.cpp @@ -125,6 +126,7 @@ set(sdrgui_HEADERS soapygui/itemsettinggui.h soapygui/stringrangegui.h soapygui/dynamicitemsettinggui.h + soapygui/dynamicargsettinggui.h soapygui/intervalslidergui.h soapygui/complexfactorgui.h soapygui/arginfogui.h diff --git a/sdrgui/soapygui/arginfogui.cpp b/sdrgui/soapygui/arginfogui.cpp index 802fa0ce5..d914df185 100644 --- a/sdrgui/soapygui/arginfogui.cpp +++ b/sdrgui/soapygui/arginfogui.cpp @@ -35,14 +35,31 @@ ArgInfoGUI::ArgInfoGUI(ArgInfoType type, ArgInfoValueType valueType, QWidget *pa ui->setupUi(this); QHBoxLayout *layout = ui->argLayout; - if (m_type != ArgInfoBinary) { + if (m_type != ArgInfoBinary) + { layout->removeWidget(ui->argCheck); + delete ui->argCheck; } - if (m_type != ArgInfoContinuous) { + + if (m_type != ArgInfoContinuous) + { layout->removeWidget(ui->argEdit); + delete ui->argEdit; } - if (m_type != ArgInfoDiscrete) { + + if (m_type != ArgInfoDiscrete) + { layout->removeWidget(ui->argCombo); + delete ui->argCombo; + } + + if ((m_valueType == ArgInfoValueInt) || (m_valueType == ArgInfoValueFloat)) + { + if (m_type == ArgInfoContinuous) { + ui->argEdit->setAlignment(Qt::AlignRight); + } else if (m_type == ArgInfoDiscrete) { + ui->argCombo->setLayoutDirection(Qt::RightToLeft); + } } } @@ -63,6 +80,17 @@ void ArgInfoGUI::setLabel(const QString& text) ui->argLabel->setText(text); } +void ArgInfoGUI::setToolTip(const QString& text) +{ + if (m_type == ArgInfoBinary) { + ui->argCheck->setToolTip(text); + } else if (m_type == ArgInfoContinuous) { + ui->argEdit->setToolTip(text); + } else if (m_type == ArgInfoDiscrete) { + ui->argCombo->setToolTip(text); + } +} + void ArgInfoGUI::setUnits(const QString& units) { ui->argUnits->setText(units); diff --git a/sdrgui/soapygui/arginfogui.h b/sdrgui/soapygui/arginfogui.h index b93777863..dda89ee92 100644 --- a/sdrgui/soapygui/arginfogui.h +++ b/sdrgui/soapygui/arginfogui.h @@ -49,9 +49,11 @@ public: ~ArgInfoGUI(); void setLabel(const QString& text); + void setToolTip(const QString& text); void setUnits(const QString& units); ArgInfoType getType() const { return m_type; } + ArgInfoValueType getValueType() const { return m_valueType; } void setRange(double min, double max); bool getBoolValue() const { return m_boolValue; } diff --git a/sdrgui/soapygui/arginfogui.ui b/sdrgui/soapygui/arginfogui.ui index bac0a3f0f..62fd6dd1f 100644 --- a/sdrgui/soapygui/arginfogui.ui +++ b/sdrgui/soapygui/arginfogui.ui @@ -56,10 +56,24 @@ - + + + + 120 + 16777215 + + + - + + + + 120 + 16777215 + + + diff --git a/sdrgui/soapygui/dynamicargsettinggui.cpp b/sdrgui/soapygui/dynamicargsettinggui.cpp new file mode 100644 index 000000000..1ede62c8f --- /dev/null +++ b/sdrgui/soapygui/dynamicargsettinggui.cpp @@ -0,0 +1,88 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// 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 // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "dynamicargsettinggui.h" + +DynamicArgSettingGUI::DynamicArgSettingGUI(ArgInfoGUI *argSettingGUI, const QString& name, QObject *parent) : + QObject(parent), + m_argSettingGUI(argSettingGUI), + m_name(name) +{ + connect(m_argSettingGUI, SIGNAL(valueChanged()), this, SLOT(processValueChanged())); +} + +DynamicArgSettingGUI::~DynamicArgSettingGUI() +{ + disconnect(m_argSettingGUI, SIGNAL(valueChanged()), this, SLOT(processValueChanged())); +} + +void DynamicArgSettingGUI::processValueChanged() +{ + if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueBool) { + emit valueChanged(m_name, QVariant(m_argSettingGUI->getBoolValue())); + } else if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueInt) { + emit valueChanged(m_name, QVariant(m_argSettingGUI->getIntValue())); + } else if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueFloat) { + emit valueChanged(m_name, QVariant(m_argSettingGUI->getFloatValue())); + } else if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueString) { + emit valueChanged(m_name, QVariant(m_argSettingGUI->getStringValue())); + } +} + +QVariant DynamicArgSettingGUI::getValue() const +{ + if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueBool) { + return QVariant(m_argSettingGUI->getBoolValue()); + } else if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueInt) { + return QVariant(m_argSettingGUI->getIntValue()); + } else if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueFloat) { + return QVariant(m_argSettingGUI->getFloatValue()); + } else if (m_argSettingGUI->getValueType() == ArgInfoGUI::ArgInfoValueString) { + return QVariant(m_argSettingGUI->getStringValue()); + } else { + return QVariant(false); + } +} + +void DynamicArgSettingGUI::setValue(const QVariant& value) +{ + bool ok = false; + + if (value.type() == QVariant::Bool) + { + m_argSettingGUI->setBoolValue(value.toBool()); + } + else if (value.type() == QVariant::Int) + { + int newValue = value.toInt(&ok); + + if (ok) { + m_argSettingGUI->setIntValue(newValue); + } + } + else if (value.type() == QVariant::Double) + { + double newValue = value.toDouble(&ok); + + if (ok) { + m_argSettingGUI->setFloatValue(newValue); + } + } + else if (value.type() == QVariant::String) + { + m_argSettingGUI->setStringValue(value.toString()); + } +} diff --git a/sdrgui/soapygui/dynamicargsettinggui.h b/sdrgui/soapygui/dynamicargsettinggui.h new file mode 100644 index 000000000..3d7f47e48 --- /dev/null +++ b/sdrgui/soapygui/dynamicargsettinggui.h @@ -0,0 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 Edouard Griffiths, F4EXB // +// // +// 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 // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_SOAPYGUI_DYNAMICARGSETTINGGUI_H_ +#define SDRGUI_SOAPYGUI_DYNAMICARGSETTINGGUI_H_ + +#include +#include +#include + +#include "export.h" +#include "arginfogui.h" + +class SDRGUI_API DynamicArgSettingGUI : public QObject +{ + Q_OBJECT +public: + DynamicArgSettingGUI(ArgInfoGUI *argSettingGUI, const QString& name, QObject *parent = 0); + ~DynamicArgSettingGUI(); + + const QString& getName() const { return m_name; } + QVariant getValue() const; + void setValue(const QVariant& value); + +signals: + void valueChanged(QString itemName, QVariant value); + +private slots: + void processValueChanged(); + +private: + ArgInfoGUI *m_argSettingGUI; + QString m_name; +}; + + + + +#endif /* SDRGUI_SOAPYGUI_DYNAMICARGSETTINGGUI_H_ */