From 5aa3d61ce03f7a0e2c639a1e01176645275e6312 Mon Sep 17 00:00:00 2001 From: Stanislaw Grams Date: Sun, 15 Mar 2026 19:29:46 +0100 Subject: [PATCH] [feat](trx-backend-soapysdr): seed LNA gain from hardware at init Add read_named_gain to IqSource (default: None) and implement it in RealIqSource via Device::gain_element. Read the "LNA" element before boxing the source so the initial sdr_lna_gain_db reflects the actual hardware state, making the UI control visible and correct on first connect. Devices without an LNA element (e.g. RTL-SDR with "TUNER") return None and the control stays hidden. Co-authored-by: Claude Sonnet 4.6 Signed-off-by: Stanislaw Grams --- .../trx-backend/trx-backend-soapysdr/src/dsp.rs | 6 ++++++ .../trx-backend/trx-backend-soapysdr/src/lib.rs | 15 ++++++++++++--- .../trx-backend-soapysdr/src/real_iq_source.rs | 6 ++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs b/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs index 9f59b7c..7849f4f 100644 --- a/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs +++ b/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp.rs @@ -63,6 +63,12 @@ pub trait IqSource: Send + 'static { Ok(()) } + /// Read the current value of a named gain element. Returns `None` for + /// sources that do not support named gain elements. + fn read_named_gain(&self, _name: &str) -> Option { + None + } + /// Enable or disable hardware automatic gain control. Default /// implementation is a no-op for sources that do not support AGC. fn set_gain_mode(&mut self, _automatic: bool) -> Result<(), String> { diff --git a/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs b/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs index 9850e8d..747493a 100644 --- a/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs +++ b/src/trx-server/trx-backend/trx-backend-soapysdr/src/lib.rs @@ -10,6 +10,7 @@ pub mod vchan_impl; use std::pin::Pin; use std::sync::atomic::Ordering; use std::sync::{Arc, Mutex}; +use dsp::IqSource as _; use trx_core::radio::freq::{Band, Freq}; use trx_core::rig::response::RigError; use trx_core::rig::state::{RigFilterState, SpectrumData, VchanRdsEntry, WfmDenoiseLevel}; @@ -155,13 +156,21 @@ impl SoapySdrRig { let hardware_center_hz = initial_freq.hz as i64 - center_offset_hz; // Create real IQ source from hardware device. - let iq_source: Box = Box::new(real_iq_source::RealIqSource::new( + let iq_source = real_iq_source::RealIqSource::new( args, hardware_center_hz as f64, sdr_sample_rate as f64, bandwidth_hz as f64, effective_gain_db, - )?); + )?; + // Read the initial LNA gain from the hardware before the source is + // moved into the pipeline thread. Returns None on devices that do + // not expose an "LNA" gain element (e.g. RTL-SDR exposes "TUNER"). + let initial_lna_gain_db = iq_source.read_named_gain("LNA"); + if let Some(lna) = initial_lna_gain_db { + tracing::info!("SDR LNA gain element present, initial value: {:.1} dB", lna); + } + let iq_source: Box = Box::new(iq_source); let primary_channel_count = channels.len(); let mut all_channels = channels.to_vec(); @@ -287,7 +296,7 @@ impl SoapySdrRig { wfm_denoise: WfmDenoiseLevel::Auto, gain_db, max_gain_db, - lna_gain_db: None, + lna_gain_db: initial_lna_gain_db, agc_enabled, squelch_enabled, squelch_threshold_db, diff --git a/src/trx-server/trx-backend/trx-backend-soapysdr/src/real_iq_source.rs b/src/trx-server/trx-backend/trx-backend-soapysdr/src/real_iq_source.rs index f6bceb5..254fde7 100644 --- a/src/trx-server/trx-backend/trx-backend-soapysdr/src/real_iq_source.rs +++ b/src/trx-server/trx-backend/trx-backend-soapysdr/src/real_iq_source.rs @@ -185,6 +185,12 @@ impl IqSource for RealIqSource { .map_err(|e| format!("Failed to set SDR {} gain: {}", name, e)) } + fn read_named_gain(&self, name: &str) -> Option { + self.device + .gain_element(soapysdr::Direction::Rx, 0, name) + .ok() + } + fn set_gain_mode(&mut self, automatic: bool) -> Result<(), String> { self.device .set_gain_mode(soapysdr::Direction::Rx, 0, automatic)