[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 <noreply@anthropic.com>
Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
2026-03-15 19:29:46 +01:00
parent b7a4b8a1df
commit 5aa3d61ce0
3 changed files with 24 additions and 3 deletions
@@ -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<f64> {
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> {
@@ -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<dyn dsp::IqSource> = 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<dyn dsp::IqSource> = 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,
@@ -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<f64> {
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)