diff --git a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp index e8eb759c8..d3642adc4 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutput.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutput.cpp @@ -250,6 +250,24 @@ void SoapySDROutput::initGainSettings(SoapySDROutputSettings& settings) updateGains(m_deviceShared.m_device, m_deviceShared.m_channel, settings); } +bool SoapySDROutput::hasDCAutoCorrection() +{ + const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getTxChannelSettings(m_deviceShared.m_channel); + return channelSettings->m_hasDCAutoCorrection; +} + +bool SoapySDROutput::hasDCCorrectionValue() +{ + const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getTxChannelSettings(m_deviceShared.m_channel); + return channelSettings->m_hasDCOffsetValue; +} + +bool SoapySDROutput::hasIQCorrectionValue() +{ + const DeviceSoapySDRParams::ChannelSettings* channelSettings = m_deviceShared.m_deviceParams->getTxChannelSettings(m_deviceShared.m_channel); + return channelSettings->m_hasIQBalanceValue; +} + void SoapySDROutput::init() { applySettings(m_settings, true); @@ -909,6 +927,54 @@ bool SoapySDROutput::applySettings(const SoapySDROutputSettings& settings, bool } } + if ((m_settings.m_autoDCCorrection != settings.m_autoDCCorrection) || force) + { + if ((dev != 0) && hasDCAutoCorrection()) + { + try + { + dev->setDCOffsetMode(SOAPY_SDR_TX, requestedChannel, settings.m_autoDCCorrection); + qDebug("SoapySDROutput::applySettings: %s DC auto correction", settings.m_autoDCCorrection ? "set" : "unset"); + } + catch (const std::exception &ex) + { + qCritical("SoapySDROutput::applySettings: cannot %s DC auto correction", settings.m_autoDCCorrection ? "set" : "unset"); + } + } + } + + if ((m_settings.m_dcCorrection != settings.m_dcCorrection) || force) + { + if ((dev != 0) && hasDCCorrectionValue()) + { + try + { + dev->setDCOffset(SOAPY_SDR_TX, requestedChannel, settings.m_dcCorrection); + qDebug("SoapySDROutput::applySettings: DC offset correction set to (%lf, %lf)", settings.m_dcCorrection.real(), settings.m_dcCorrection.imag()); + } + catch (const std::exception &ex) + { + qCritical("SoapySDROutput::applySettings: cannot set DC offset correction to (%lf, %lf)", settings.m_dcCorrection.real(), settings.m_dcCorrection.imag()); + } + } + } + + if ((m_settings.m_iqCorrection != settings.m_iqCorrection) || force) + { + if ((dev != 0) && hasIQCorrectionValue()) + { + try + { + dev->setIQBalance(SOAPY_SDR_TX, requestedChannel, settings.m_iqCorrection); + qDebug("SoapySDROutput::applySettings: IQ balance correction set to (%lf, %lf)", settings.m_iqCorrection.real(), settings.m_iqCorrection.imag()); + } + catch (const std::exception &ex) + { + qCritical("SoapySDROutput::applySettings: cannot set IQ balance correction to (%lf, %lf)", settings.m_iqCorrection.real(), settings.m_iqCorrection.imag()); + } + } + } + if (forwardChangeOwnDSP) { int sampleRate = settings.m_devSampleRate/(1<& getTunableElements(); const std::vector& getIndividualGainsRanges(); void initGainSettings(SoapySDROutputSettings& settings); + bool hasDCAutoCorrection(); + bool hasDCCorrectionValue(); + bool hasIQAutoCorrection() { return false; } // not in SoapySDR interface + bool hasIQCorrectionValue(); private: DeviceSinkAPI *m_deviceAPI; diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp index 38b745e1d..b57f4b56b 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.cpp @@ -29,6 +29,7 @@ #include "soapygui/stringrangegui.h" #include "soapygui/dynamicitemsettinggui.h" #include "soapygui/intervalslidergui.h" +#include "soapygui/complexfactorgui.h" #include "soapysdroutputgui.h" @@ -45,7 +46,11 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleRateGUI(0), m_bandwidthGUI(0), m_gainSliderGUI(0), - m_autoGain(0) + m_autoGain(0), + m_dcCorrectionGUI(0), + m_iqCorrectionGUI(0), + m_autoDCCorrection(0), + m_autoIQCorrection(0) { m_sampleSink = (SoapySDROutput*) m_deviceUISet->m_deviceSinkAPI->getSampleSink(); ui->setupUi(this); @@ -55,6 +60,7 @@ SoapySDROutputGui::SoapySDROutputGui(DeviceUISet *deviceUISet, QWidget* parent) m_sampleSink->getFrequencyRange(f_min, f_max); ui->centerFrequency->setValueRange(7, f_min/1000, f_max/1000); + createCorrectionsControl(); createAntennasControl(m_sampleSink->getAntennas()); createRangesControl(&m_sampleRateGUI, m_sampleSink->getRateRanges(), "SR", "S/s"); createRangesControl(&m_bandwidthGUI, m_sampleSink->getBandwidthRanges(), "BW", "Hz"); @@ -240,6 +246,57 @@ void SoapySDROutputGui::createIndividualGainsControl(const std::vectorscrollAreaWidgetContents->layout(); + + if (m_sampleSink->hasDCCorrectionValue()) // complex GUI + { + m_dcCorrectionGUI = new ComplexFactorGUI(this); + m_dcCorrectionGUI->setLabel(QString("DC")); + m_dcCorrectionGUI->setAutomaticEnable(m_sampleSink->hasDCAutoCorrection()); + layout->addWidget(m_dcCorrectionGUI); + + connect(m_dcCorrectionGUI, SIGNAL(moduleChanged(double)), this, SLOT(dcCorrectionModuleChanged(double))); + connect(m_dcCorrectionGUI, SIGNAL(argumentChanged(double)), this, SLOT(dcCorrectionArgumentChanged(double))); + + if (m_sampleSink->hasDCAutoCorrection()) { + connect(m_dcCorrectionGUI, SIGNAL(automaticChanged(bool)), this, SLOT(autoDCCorrectionChanged(bool))); + } + } + else if (m_sampleSink->hasDCAutoCorrection()) // simple checkbox + { + m_autoDCCorrection = new QCheckBox(this); + m_autoDCCorrection->setText(QString("DC corr")); + layout->addWidget(m_autoDCCorrection); + + connect(m_autoDCCorrection, SIGNAL(toggled(bool)), this, SLOT(autoDCCorrectionChanged(bool))); + } + + if (m_sampleSink->hasIQCorrectionValue()) // complex GUI + { + m_iqCorrectionGUI = new ComplexFactorGUI(this); + m_iqCorrectionGUI->setLabel(QString("IQ")); + m_iqCorrectionGUI->setAutomaticEnable(m_sampleSink->hasIQAutoCorrection()); + layout->addWidget(m_iqCorrectionGUI); + + connect(m_iqCorrectionGUI, SIGNAL(moduleChanged(double)), this, SLOT(iqCorrectionModuleChanged(double))); + connect(m_iqCorrectionGUI, SIGNAL(argumentChanged(double)), this, SLOT(iqCorrectionArgumentChanged(double))); + + if (m_sampleSink->hasIQAutoCorrection()) { + connect(m_iqCorrectionGUI, SIGNAL(automaticChanged(bool)), this, SLOT(autoIQCorrectionChanged(bool))); + } + } + else if (m_sampleSink->hasIQAutoCorrection()) // simple checkbox + { + m_autoIQCorrection = new QCheckBox(this); + m_autoIQCorrection->setText(QString("IQ corr")); + layout->addWidget(m_autoIQCorrection); + + connect(m_autoIQCorrection, SIGNAL(toggled(bool)), this, SLOT(autoIQCorrectionChanged(bool))); + } +} + void SoapySDROutputGui::setName(const QString& name) { setObjectName(name); @@ -404,6 +461,48 @@ void SoapySDROutputGui::individualGainChanged(QString name, double value) sendSettings(); } +void SoapySDROutputGui::autoDCCorrectionChanged(bool set) +{ + m_settings.m_autoDCCorrection = set; + sendSettings(); +} + +void SoapySDROutputGui::autoIQCorrectionChanged(bool set) +{ + m_settings.m_autoIQCorrection = set; + sendSettings(); +} + +void SoapySDROutputGui::dcCorrectionModuleChanged(double value) +{ + std::complex dcCorrection = std::polar(value, arg(m_settings.m_dcCorrection)); + m_settings.m_dcCorrection = dcCorrection; + sendSettings(); +} + +void SoapySDROutputGui::dcCorrectionArgumentChanged(double value) +{ + double angleInRadians = (value / 180.0) * M_PI; + std::complex dcCorrection = std::polar(abs(m_settings.m_dcCorrection), angleInRadians); + m_settings.m_dcCorrection = dcCorrection; + sendSettings(); +} + +void SoapySDROutputGui::iqCorrectionModuleChanged(double value) +{ + std::complex iqCorrection = std::polar(value, arg(m_settings.m_iqCorrection)); + m_settings.m_iqCorrection = iqCorrection; + sendSettings(); +} + +void SoapySDROutputGui::iqCorrectionArgumentChanged(double value) +{ + double angleInRadians = (value / 180.0) * M_PI; + std::complex iqCorrection = std::polar(abs(m_settings.m_iqCorrection), angleInRadians); + m_settings.m_iqCorrection = iqCorrection; + sendSettings(); +} + void SoapySDROutputGui::on_centerFrequency_changed(quint64 value) { m_settings.m_centerFrequency = value * 1000; @@ -473,6 +572,7 @@ void SoapySDROutputGui::displaySettings() displayTunableElementsControlSettings(); displayIndividualGainsControlSettings(); + displayCorrectionsSettings(); blockApplySettings(false); } @@ -501,6 +601,31 @@ void SoapySDROutputGui::displayIndividualGainsControlSettings() } } +void SoapySDROutputGui::displayCorrectionsSettings() +{ + if (m_dcCorrectionGUI) + { + m_dcCorrectionGUI->setAutomatic(m_settings.m_autoDCCorrection); + m_dcCorrectionGUI->setModule(abs(m_settings.m_dcCorrection)); + m_dcCorrectionGUI->setArgument(arg(m_settings.m_dcCorrection)*(180.0/M_PI)); + } + + if (m_iqCorrectionGUI) + { + m_iqCorrectionGUI->setAutomatic(m_settings.m_autoIQCorrection); + m_iqCorrectionGUI->setModule(abs(m_settings.m_iqCorrection)); + m_iqCorrectionGUI->setArgument(arg(m_settings.m_iqCorrection)*(180.0/M_PI)); + } + + if (m_autoDCCorrection) { + m_autoDCCorrection->setChecked(m_settings.m_autoDCCorrection); + } + + if (m_autoIQCorrection) { + m_autoIQCorrection->setChecked(m_settings.m_autoIQCorrection); + } +} + void SoapySDROutputGui::sendSettings() { if (!m_updateTimer.isActive()) { diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h index c108fa837..1ac196323 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputgui.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputgui.h @@ -33,6 +33,7 @@ class StringRangeGUI; class DynamicItemSettingGUI; class IntervalSliderGUI; class QCheckBox; +class ComplexFactorGUI; namespace Ui { class SoapySDROutputGui; @@ -67,6 +68,7 @@ private: void createTunableElementsControl(const std::vector& tunableElementsList); void createGlobalGainControl(); void createIndividualGainsControl(const std::vector& individualGainsList); + void createCorrectionsControl(); Ui::SoapySDROutputGui* ui; @@ -89,11 +91,16 @@ private: IntervalSliderGUI *m_gainSliderGUI; std::vector m_individualGainsGUIs; QCheckBox *m_autoGain; + ComplexFactorGUI *m_dcCorrectionGUI; + ComplexFactorGUI *m_iqCorrectionGUI; + QCheckBox *m_autoDCCorrection; + QCheckBox *m_autoIQCorrection; void blockApplySettings(bool block) { m_doApplySettings = !block; } void displaySettings(); void displayTunableElementsControlSettings(); void displayIndividualGainsControlSettings(); + void displayCorrectionsSettings(); void sendSettings(); void updateSampleRateAndFrequency(); void updateFrequencyLimits(); @@ -101,6 +108,7 @@ private: private slots: void handleInputMessages(); + void antennasChanged(); void sampleRateChanged(double sampleRate); void bandwidthChanged(double bandwidth); @@ -108,6 +116,13 @@ private slots: void globalGainChanged(double gain); void autoGainChanged(bool set); void individualGainChanged(QString name, double value); + void autoDCCorrectionChanged(bool set); + void autoIQCorrectionChanged(bool set); + void dcCorrectionModuleChanged(double value); + void dcCorrectionArgumentChanged(double value); + void iqCorrectionModuleChanged(double value); + void iqCorrectionArgumentChanged(double value); + void on_centerFrequency_changed(quint64 value); void on_LOppm_valueChanged(int value); void on_interp_currentIndexChanged(int index); diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp index 2be673c1e..9abc87b96 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.cpp @@ -38,6 +38,10 @@ void SoapySDROutputSettings::resetToDefaults() m_bandwidth = 1000000; m_globalGain = 0; m_autoGain = false; + m_autoDCCorrection = false; + m_autoIQCorrection = false; + m_dcCorrection = std::complex{0,0}; + m_iqCorrection = std::complex{0,0}; } QByteArray SoapySDROutputSettings::serialize() const @@ -55,6 +59,12 @@ QByteArray SoapySDROutputSettings::serialize() const s.writeS32(12, m_globalGain); s.writeBlob(13, serializeNamedElementMap(m_individualGains)); s.writeBool(14, m_autoGain); + s.writeBool(15, m_autoDCCorrection); + s.writeBool(16, m_autoIQCorrection); + s.writeDouble(17, m_dcCorrection.real()); + s.writeDouble(18, m_dcCorrection.imag()); + s.writeDouble(19, m_iqCorrection.real()); + s.writeDouble(20, m_iqCorrection.imag()); return s.final(); } @@ -72,10 +82,11 @@ bool SoapySDROutputSettings::deserialize(const QByteArray& data) if (d.getVersion() == 1) { QByteArray blob; + double realval, imagval; - d.readS32(1, &m_devSampleRate); - d.readS32(2, &m_LOppmTenths); - d.readU32(3, &m_log2Interp); + d.readS32(1, &m_devSampleRate, 1024000); + d.readS32(2, &m_LOppmTenths, 0); + d.readU32(3, &m_log2Interp, 0); d.readBool(4, &m_transverterMode, false); d.readS64(5, &m_transverterDeltaFrequency, 0); d.readString(6, &m_antenna, "NONE"); @@ -86,6 +97,14 @@ bool SoapySDROutputSettings::deserialize(const QByteArray& data) d.readBlob(13, &blob); deserializeNamedElementMap(blob, m_individualGains); d.readBool(14, &m_autoGain, false); + d.readBool(15, &m_autoDCCorrection, false); + d.readBool(16, &m_autoIQCorrection, false); + d.readDouble(17, &realval, 0); + d.readDouble(18, &imagval, 0); + m_dcCorrection = std::complex{realval, imagval}; + d.readDouble(19, &realval, 0); + d.readDouble(20, &imagval, 0); + m_iqCorrection = std::complex{realval, imagval}; return true; } diff --git a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h index e5ee04b83..d0cf8f448 100644 --- a/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h +++ b/plugins/samplesink/soapysdroutput/soapysdroutputsettings.h @@ -33,6 +33,10 @@ struct SoapySDROutputSettings { qint32 m_globalGain; QMap m_individualGains; bool m_autoGain; + bool m_autoDCCorrection; + bool m_autoIQCorrection; + std::complex m_dcCorrection; + std::complex m_iqCorrection; SoapySDROutputSettings(); void resetToDefaults(); diff --git a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp index fa591af87..198c9d822 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinput.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinput.cpp @@ -980,11 +980,11 @@ bool SoapySDRInput::applySettings(const SoapySDRInputSettings& settings, bool fo try { dev->setDCOffsetMode(SOAPY_SDR_RX, requestedChannel, settings.m_autoDCCorrection); - qDebug("SoapySDRInput::applySettings: %s DC auto correction", settings.m_autoGain ? "set" : "unset"); + qDebug("SoapySDRInput::applySettings: %s DC auto correction", settings.m_autoDCCorrection ? "set" : "unset"); } catch (const std::exception &ex) { - qCritical("SoapySDRInput::applySettings: cannot %s DC auto correction", settings.m_autoGain ? "set" : "unset"); + qCritical("SoapySDRInput::applySettings: cannot %s DC auto correction", settings.m_autoDCCorrection ? "set" : "unset"); } } } diff --git a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp index 2b5bf4364..6218f6b54 100644 --- a/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp +++ b/plugins/samplesource/soapysdrinput/soapysdrinputgui.cpp @@ -61,6 +61,7 @@ SoapySDRInputGui::SoapySDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : m_sampleSource->getFrequencyRange(f_min, f_max); ui->centerFrequency->setValueRange(7, f_min/1000, f_max/1000); + createCorrectionsControl(); createAntennasControl(m_sampleSource->getAntennas()); createRangesControl(&m_sampleRateGUI, m_sampleSource->getRateRanges(), "SR", "S/s"); createRangesControl(&m_bandwidthGUI, m_sampleSource->getBandwidthRanges(), "BW", "Hz");