From 72d612d986a8268e7e7f5a880a663c63f712790a Mon Sep 17 00:00:00 2001 From: Robin Getz Date: Sun, 19 Jul 2026 17:48:53 -0400 Subject: [PATCH] plutosdr: Update RX gain limits dynamically from hardware Query the PlutoSDR hardware for the current RX gain range and use it to configure the GUI gain control dynamically. The AD936x gain limits vary with LO frequency. Previously the GUI used a fixed gain range, allowing users to select values that the hardware would reject after changing bands. The gain slider now refreshes its minimum, maximum, and step size whenever the device center frequency is updated. Also change the gain setting serialization to use a signed integer so negative gain values are preserved for operating modes that support them. Changes include: - Add DevicePlutoSDRBox::getGainRange() to read in_voltage0_hardwaregain_available. - Expose gain range through PlutoSDRInput. - Refresh GUI gain limits when the device frequency changes. - Avoid unnecessary widget updates when limits are unchanged. - Store/restore gain as a signed value. - Add default RX gain limit constants for fallback when the device cannot provide its range. Signed-off-by: Robin Getz --- devices/plutosdr/deviceplutosdr.cpp | 2 ++ devices/plutosdr/deviceplutosdr.h | 2 ++ devices/plutosdr/deviceplutosdrbox.cpp | 22 +++++++++++++++++++ devices/plutosdr/deviceplutosdrbox.h | 1 + .../plutosdrinput/plutosdrinput.cpp | 13 +++++++++++ .../plutosdrinput/plutosdrinput.h | 1 + .../plutosdrinput/plutosdrinputgui.cpp | 19 ++++++++++++++++ .../plutosdrinput/plutosdrinputgui.h | 1 + .../plutosdrinput/plutosdrinputsettings.cpp | 4 ++-- .../plutosdrinput/plutosdrinputsettings.h | 2 +- 10 files changed, 64 insertions(+), 3 deletions(-) diff --git a/devices/plutosdr/deviceplutosdr.cpp b/devices/plutosdr/deviceplutosdr.cpp index 59df8ed53..9c82f9c61 100644 --- a/devices/plutosdr/deviceplutosdr.cpp +++ b/devices/plutosdr/deviceplutosdr.cpp @@ -32,6 +32,8 @@ const uint32_t DevicePlutoSDR::bbLPRxLowLimitFreq = 200000U; // 200 kHz const uint32_t DevicePlutoSDR::bbLPRxHighLimitFreq = 14000000U; // 14 MHz const uint32_t DevicePlutoSDR::bbLPTxLowLimitFreq = 625000U; // 625 kHz const uint32_t DevicePlutoSDR::bbLPTxHighLimitFreq = 16000000U; // 16 MHz +const int32_t DevicePlutoSDR::rxMinGain = 0; // 0 dB +const int32_t DevicePlutoSDR::rxMaxGain = 77; // 77 dB const float DevicePlutoSDR::firBWLowLimitFactor = 0.05f; const float DevicePlutoSDR::firBWHighLimitFactor = 0.9f; diff --git a/devices/plutosdr/deviceplutosdr.h b/devices/plutosdr/deviceplutosdr.h index b38df629b..ce89b1adc 100644 --- a/devices/plutosdr/deviceplutosdr.h +++ b/devices/plutosdr/deviceplutosdr.h @@ -54,6 +54,8 @@ public: static const uint32_t bbLPTxHighLimitFreq; //!< Analog base band Tx high pass filter lower frequency limit (Hz) static const float firBWLowLimitFactor; //!< Factor by which the FIR working sample rate is multiplied to yield bandwidth lower limit static const float firBWHighLimitFactor; //!< Factor by which the FIR working sample rate is multiplied to yield bandwidth higher limit + static const int32_t rxMinGain; //!< Min gain (Steps or dB) + static const int32_t rxMaxGain; //!< Max gain (Steps or dB) protected: DevicePlutoSDR(); ~DevicePlutoSDR(); diff --git a/devices/plutosdr/deviceplutosdrbox.cpp b/devices/plutosdr/deviceplutosdrbox.cpp index 922297e7a..dbd891ad3 100644 --- a/devices/plutosdr/deviceplutosdrbox.cpp +++ b/devices/plutosdr/deviceplutosdrbox.cpp @@ -887,6 +887,28 @@ bool DevicePlutoSDRBox::getTxRSSI(std::string& rssiStr, unsigned int chan) return get_param(DEVICE_PHY, buff, rssiStr); } +void DevicePlutoSDRBox::getGainRange(qint64& minGain, qint64& stepGain, qint64& maxGain) +{ + std::string rangeStr; + + char buff[50]; + snprintf(buff, sizeof(buff), "in_voltage0_hardwaregain_available"); + if (get_param(DEVICE_PHY, buff, rangeStr) && (rangeStr.size() > 2)) + { + // _available is always [min step max] + std::istringstream in(rangeStr.substr(1, rangeStr.size()-2)); + in >> minGain; + in >> stepGain; + in >> maxGain; + } + else + { + minGain = DevicePlutoSDR::rxMinGain; + stepGain = 1; + maxGain = DevicePlutoSDR::rxMaxGain; + } +} + void DevicePlutoSDRBox::getRxLORange(uint64_t& minLimit, uint64_t& maxLimit) { // values are returned in Hz diff --git a/devices/plutosdr/deviceplutosdrbox.h b/devices/plutosdr/deviceplutosdrbox.h index 74a862a90..46b0ad517 100644 --- a/devices/plutosdr/deviceplutosdrbox.h +++ b/devices/plutosdr/deviceplutosdrbox.h @@ -119,6 +119,7 @@ public: bool getRxGain(int& gaindB, unsigned int chan); bool getRxRSSI(std::string& rssiStr, unsigned int chan); bool getTxRSSI(std::string& rssiStr, unsigned int chan); + void getGainRange(qint64& minGain, qint64& stepGain, qint64& maxGain); void getRxLORange(uint64_t& minLimit, uint64_t& maxLimit); void getTxLORange(uint64_t& minLimit, uint64_t& maxLimit); void getbbLPRxRange(uint32_t& minLimit, uint32_t& maxLimit); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp index 784dbb637..193380949 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinput.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinput.cpp @@ -721,6 +721,19 @@ void PlutoSDRInput::getbbLPRange(quint32& minLimit, quint32& maxLimit) maxLimit = max; } +void PlutoSDRInput::getGainRange(qint64& minGain, qint64& stepGain, qint64& maxGain) +{ + if (!m_open) + { + qDebug("PlutoSDRInput::getGainRange: device not open"); + return; + } + + DevicePlutoSDRBox *plutoBox = m_deviceShared.m_deviceParams->getBox(); + + plutoBox->getGainRange(minGain, stepGain, maxGain); +} + void PlutoSDRInput::getGain(int& gaindB) { if (!m_open) diff --git a/plugins/samplesource/plutosdrinput/plutosdrinput.h b/plugins/samplesource/plutosdrinput/plutosdrinput.h index 34a5bb25d..a1fa6384e 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinput.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinput.h @@ -142,6 +142,7 @@ public: void getRSSI(std::string& rssiStr); void getLORange(qint64& minLimit, qint64& maxLimit); void getbbLPRange(quint32& minLimit, quint32& maxLimit); + void getGainRange(qint64& minGain, qint64& stepGain, qint64& maxGain); void getGain(int& gainStr); bool fetchTemperature(); float getTemperature(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp index ce321f7b8..af79383cc 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.cpp @@ -70,6 +70,8 @@ PlutoSDRInputGui::PlutoSDRInputGui(DeviceUISet *deviceUISet, QWidget* parent) : ((PlutoSDRInput *) m_sampleSource)->getbbLPRange(minLimit, maxLimit); ui->lpf->setValueRange(5, minLimit/1000, maxLimit/1000); + refreshGainLimits(); + ui->lpFIR->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); ui->lpFIR->setValueRange(5, 1U, 56000U); // will be dynamically recalculated @@ -390,6 +392,22 @@ void PlutoSDRInputGui::displaySampleRate() ui->sampleRate->blockSignals(false); } +void PlutoSDRInputGui::refreshGainLimits() +{ + qint64 minGain, stepGain, maxGain; + ((PlutoSDRInput *) m_sampleSource)->getGainRange(minGain, stepGain, maxGain); + + // Only update the gui when necessary, avoid unnecessary widget updates + if (ui->gain->minimum() != minGain || + ui->gain->maximum() != maxGain || + ui->gain->singleStep() != stepGain) + { + ui->gain->setMinimum(minGain); + ui->gain->setMaximum(maxGain); + ui->gain->setSingleStep(stepGain); + } +} + void PlutoSDRInputGui::displayFcTooltip() { int32_t fShift = DeviceSampleSource::calculateFrequencyShift( @@ -600,6 +618,7 @@ void PlutoSDRInputGui::updateSampleRateAndFrequency() { m_deviceUISet->getSpectrum()->setSampleRate(m_sampleRate); m_deviceUISet->getSpectrum()->setCenterFrequency(m_deviceCenterFrequency); + refreshGainLimits(); displaySampleRate(); } diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h index b1873aeaa..883e121cb 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputgui.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputgui.h @@ -74,6 +74,7 @@ private: void sendSettings(bool forceSettings = false); void blockApplySettings(bool block); void updateSampleRateAndFrequency(); + void refreshGainLimits(); void setFIRBWLimits(); void setSampleRateLimits(); void updateFrequencyLimits(); diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp index dc8a62a5d..a69834c28 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.cpp @@ -71,7 +71,7 @@ QByteArray PlutoSDRInputSettings::serialize() const s.writeBool(10, m_lpfFIREnable); s.writeU32(11, m_lpfFIRBW); s.writeU64(12, m_devSampleRate); - s.writeU32(13, m_gain); + s.writeS32(13, m_gain); s.writeS32(14, (int) m_antennaPath); s.writeS32(15, (int) m_gainMode); s.writeBool(16, m_transverterMode); @@ -125,7 +125,7 @@ bool PlutoSDRInputSettings::deserialize(const QByteArray& data) d.readBool(10, &m_lpfFIREnable, false); d.readU32(11, &m_lpfFIRBW, 500000U); d.readU64(12, &m_devSampleRate, 1536000U); - d.readU32(13, &m_gain, 40); + d.readS32(13, &m_gain, 40); d.readS32(14, &intval, 0); if ((intval >= 0) && (intval < (int) RFPATH_END)) { m_antennaPath = (RFPath) intval; diff --git a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h index 8c4d99857..c767a16f3 100644 --- a/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h +++ b/plugins/samplesource/plutosdrinput/plutosdrinputsettings.h @@ -77,7 +77,7 @@ struct PlutoSDRInputSettings { bool m_hwIQCorrection; //!< Hardware IQ correction quint32 m_log2Decim; quint32 m_lpfBW; //!< analog lowpass filter bandwidth (Hz) - quint32 m_gain; //!< "hardware" gain + qint32 m_gain; //!< "hardware" gain RFPath m_antennaPath; GainMode m_gainMode; bool m_transverterMode;