[feat](trx-rs): expose live sdr gain control
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -1257,6 +1257,13 @@ function render(update) {
|
|||||||
if (update.filter && typeof update.filter.bandwidth_hz === "number") {
|
if (update.filter && typeof update.filter.bandwidth_hz === "number") {
|
||||||
currentBandwidthHz = update.filter.bandwidth_hz;
|
currentBandwidthHz = update.filter.bandwidth_hz;
|
||||||
syncBandwidthInput(currentBandwidthHz);
|
syncBandwidthInput(currentBandwidthHz);
|
||||||
|
if (
|
||||||
|
sdrGainEl
|
||||||
|
&& typeof update.filter.sdr_gain_db === "number"
|
||||||
|
&& document.activeElement !== sdrGainEl
|
||||||
|
) {
|
||||||
|
sdrGainEl.value = String(Math.round(update.filter.sdr_gain_db));
|
||||||
|
}
|
||||||
if (wfmDeemphasisEl && typeof update.filter.wfm_deemphasis_us === "number") {
|
if (wfmDeemphasisEl && typeof update.filter.wfm_deemphasis_us === "number") {
|
||||||
wfmDeemphasisEl.value = String(update.filter.wfm_deemphasis_us);
|
wfmDeemphasisEl.value = String(update.filter.wfm_deemphasis_us);
|
||||||
}
|
}
|
||||||
@@ -2567,6 +2574,8 @@ const audioRow = document.getElementById("audio-row");
|
|||||||
const wfmControlsCol = document.getElementById("wfm-controls-col");
|
const wfmControlsCol = document.getElementById("wfm-controls-col");
|
||||||
const wfmDeemphasisEl = document.getElementById("wfm-deemphasis");
|
const wfmDeemphasisEl = document.getElementById("wfm-deemphasis");
|
||||||
const wfmAudioModeEl = document.getElementById("wfm-audio-mode");
|
const wfmAudioModeEl = document.getElementById("wfm-audio-mode");
|
||||||
|
const sdrGainEl = document.getElementById("sdr-gain-db");
|
||||||
|
const sdrGainSetBtn = document.getElementById("sdr-gain-set");
|
||||||
const wfmStFlagEl = document.getElementById("wfm-st-flag");
|
const wfmStFlagEl = document.getElementById("wfm-st-flag");
|
||||||
|
|
||||||
// Hide audio row if audio is not configured on the server
|
// Hide audio row if audio is not configured on the server
|
||||||
@@ -2611,6 +2620,23 @@ if (wfmDeemphasisEl) {
|
|||||||
postPath(`/set_wfm_deemphasis?us=${encodeURIComponent(wfmDeemphasisEl.value)}`).catch(() => {});
|
postPath(`/set_wfm_deemphasis?us=${encodeURIComponent(wfmDeemphasisEl.value)}`).catch(() => {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function submitSdrGain() {
|
||||||
|
if (!sdrGainEl) return;
|
||||||
|
const parsed = Number.parseFloat(sdrGainEl.value);
|
||||||
|
if (!Number.isFinite(parsed) || parsed < 0) return;
|
||||||
|
postPath(`/set_sdr_gain?db=${encodeURIComponent(parsed)}`).catch(() => {});
|
||||||
|
}
|
||||||
|
if (sdrGainSetBtn) {
|
||||||
|
sdrGainSetBtn.addEventListener("click", submitSdrGain);
|
||||||
|
}
|
||||||
|
if (sdrGainEl) {
|
||||||
|
sdrGainEl.addEventListener("keydown", (ev) => {
|
||||||
|
if (ev.key === "Enter") {
|
||||||
|
ev.preventDefault();
|
||||||
|
submitSdrGain();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
function updateWfmControls() {
|
function updateWfmControls() {
|
||||||
if (!wfmControlsCol) return;
|
if (!wfmControlsCol) return;
|
||||||
const mode = (modeEl && modeEl.value ? modeEl.value : "").toUpperCase();
|
const mode = (modeEl && modeEl.value ? modeEl.value : "").toUpperCase();
|
||||||
|
|||||||
@@ -165,6 +165,10 @@
|
|||||||
<option value="mono">Mono</option>
|
<option value="mono">Mono</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="wfm-control">RF Gain
|
||||||
|
<input id="sdr-gain-db" class="status-input" type="number" min="0" max="60" step="1" inputmode="decimal">
|
||||||
|
</label>
|
||||||
|
<button id="sdr-gain-set" type="button" class="status-input">Set</button>
|
||||||
<label class="wfm-control wfm-st-flag-wrap" aria-label="Stereo pilot status">
|
<label class="wfm-control wfm-st-flag-wrap" aria-label="Stereo pilot status">
|
||||||
<span id="wfm-st-flag" class="wfm-st-flag wfm-st-flag-mono">MO</span>
|
<span id="wfm-st-flag" class="wfm-st-flag wfm-st-flag-mono">MO</span>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -186,6 +186,9 @@ input.status-input, select.status-input { width: 100%; padding: 0.45rem 0.5rem;
|
|||||||
width: auto;
|
width: auto;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
.wfm-control input.status-input {
|
||||||
|
width: 5rem;
|
||||||
|
}
|
||||||
.wfm-control button.status-input {
|
.wfm-control button.status-input {
|
||||||
padding: 0.45rem 0.5rem;
|
padding: 0.45rem 0.5rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|||||||
@@ -473,6 +473,19 @@ pub async fn set_fir_taps(
|
|||||||
send_command(&rig_tx, RigCommand::SetFirTaps(query.taps)).await
|
send_command(&rig_tx, RigCommand::SetFirTaps(query.taps)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct SdrGainQuery {
|
||||||
|
pub db: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/set_sdr_gain")]
|
||||||
|
pub async fn set_sdr_gain(
|
||||||
|
query: web::Query<SdrGainQuery>,
|
||||||
|
rig_tx: web::Data<mpsc::Sender<RigRequest>>,
|
||||||
|
) -> Result<HttpResponse, Error> {
|
||||||
|
send_command(&rig_tx, RigCommand::SetSdrGain(query.db)).await
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct WfmDeemphasisQuery {
|
pub struct WfmDeemphasisQuery {
|
||||||
pub us: u32,
|
pub us: u32,
|
||||||
@@ -710,6 +723,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
|||||||
.service(set_tx_limit)
|
.service(set_tx_limit)
|
||||||
.service(set_bandwidth)
|
.service(set_bandwidth)
|
||||||
.service(set_fir_taps)
|
.service(set_fir_taps)
|
||||||
|
.service(set_sdr_gain)
|
||||||
.service(set_wfm_deemphasis)
|
.service(set_wfm_deemphasis)
|
||||||
.service(set_wfm_stereo)
|
.service(set_wfm_stereo)
|
||||||
.service(toggle_aprs_decode)
|
.service(toggle_aprs_decode)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ pub enum RigCommand {
|
|||||||
ResetWsprDecoder,
|
ResetWsprDecoder,
|
||||||
SetBandwidth(u32),
|
SetBandwidth(u32),
|
||||||
SetFirTaps(u32),
|
SetFirTaps(u32),
|
||||||
|
SetSdrGain(f64),
|
||||||
SetWfmDeemphasis(u32),
|
SetWfmDeemphasis(u32),
|
||||||
SetWfmStereo(bool),
|
SetWfmStereo(bool),
|
||||||
GetSpectrum,
|
GetSpectrum,
|
||||||
|
|||||||
@@ -516,6 +516,7 @@ pub fn command_from_rig_command(cmd: RigCommand) -> Box<dyn RigCommandHandler> {
|
|||||||
| RigCommand::ResetWsprDecoder
|
| RigCommand::ResetWsprDecoder
|
||||||
| RigCommand::SetBandwidth(_)
|
| RigCommand::SetBandwidth(_)
|
||||||
| RigCommand::SetFirTaps(_)
|
| RigCommand::SetFirTaps(_)
|
||||||
|
| RigCommand::SetSdrGain(_)
|
||||||
| RigCommand::SetWfmDeemphasis(_)
|
| RigCommand::SetWfmDeemphasis(_)
|
||||||
| RigCommand::SetWfmStereo(_)
|
| RigCommand::SetWfmStereo(_)
|
||||||
| RigCommand::GetSpectrum => Box::new(GetSnapshotCommand),
|
| RigCommand::GetSpectrum => Box::new(GetSnapshotCommand),
|
||||||
|
|||||||
@@ -165,6 +165,16 @@ pub trait RigCat: Rig + Send {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_sdr_gain<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
_gain_db: f64,
|
||||||
|
) -> Pin<Box<dyn Future<Output = DynResult<()>> + Send + 'a>> {
|
||||||
|
Box::pin(std::future::ready(Err(
|
||||||
|
Box::new(response::RigError::not_supported("set_sdr_gain"))
|
||||||
|
as Box<dyn std::error::Error + Send + Sync>,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
fn set_wfm_stereo<'a>(
|
fn set_wfm_stereo<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
_enabled: bool,
|
_enabled: bool,
|
||||||
|
|||||||
@@ -269,6 +269,8 @@ pub struct RigFilterState {
|
|||||||
pub bandwidth_hz: u32,
|
pub bandwidth_hz: u32,
|
||||||
pub fir_taps: u32,
|
pub fir_taps: u32,
|
||||||
pub cw_center_hz: u32,
|
pub cw_center_hz: u32,
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub sdr_gain_db: Option<f64>,
|
||||||
#[serde(default = "default_wfm_deemphasis_us")]
|
#[serde(default = "default_wfm_deemphasis_us")]
|
||||||
pub wfm_deemphasis_us: u32,
|
pub wfm_deemphasis_us: u32,
|
||||||
#[serde(default = "default_wfm_stereo")]
|
#[serde(default = "default_wfm_stereo")]
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ mod tests {
|
|||||||
bandwidth_hz: 3000,
|
bandwidth_hz: 3000,
|
||||||
fir_taps: 64,
|
fir_taps: 64,
|
||||||
cw_center_hz: 700,
|
cw_center_hz: 700,
|
||||||
|
sdr_gain_db: Some(12.0),
|
||||||
wfm_deemphasis_us: 75,
|
wfm_deemphasis_us: 75,
|
||||||
wfm_stereo: true,
|
wfm_stereo: true,
|
||||||
wfm_stereo_detected: false,
|
wfm_stereo_detected: false,
|
||||||
@@ -335,6 +336,7 @@ mod tests {
|
|||||||
bandwidth_hz: 12000,
|
bandwidth_hz: 12000,
|
||||||
fir_taps: 128,
|
fir_taps: 128,
|
||||||
cw_center_hz: 700,
|
cw_center_hz: 700,
|
||||||
|
sdr_gain_db: Some(18.0),
|
||||||
wfm_deemphasis_us: 50,
|
wfm_deemphasis_us: 50,
|
||||||
wfm_stereo: true,
|
wfm_stereo: true,
|
||||||
wfm_stereo_detected: true,
|
wfm_stereo_detected: true,
|
||||||
@@ -346,6 +348,7 @@ mod tests {
|
|||||||
let f = decoded.filter.expect("filter should round-trip");
|
let f = decoded.filter.expect("filter should round-trip");
|
||||||
assert_eq!(f.bandwidth_hz, 12000);
|
assert_eq!(f.bandwidth_hz, 12000);
|
||||||
assert_eq!(f.fir_taps, 128);
|
assert_eq!(f.fir_taps, 128);
|
||||||
|
assert_eq!(f.sdr_gain_db, Some(18.0));
|
||||||
assert_eq!(f.wfm_deemphasis_us, 50);
|
assert_eq!(f.wfm_deemphasis_us, 50);
|
||||||
assert!(f.wfm_stereo_detected);
|
assert!(f.wfm_stereo_detected);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ pub fn client_command_to_rig(cmd: ClientCommand) -> RigCommand {
|
|||||||
ClientCommand::ResetWsprDecoder => RigCommand::ResetWsprDecoder,
|
ClientCommand::ResetWsprDecoder => RigCommand::ResetWsprDecoder,
|
||||||
ClientCommand::SetBandwidth { bandwidth_hz } => RigCommand::SetBandwidth(bandwidth_hz),
|
ClientCommand::SetBandwidth { bandwidth_hz } => RigCommand::SetBandwidth(bandwidth_hz),
|
||||||
ClientCommand::SetFirTaps { taps } => RigCommand::SetFirTaps(taps),
|
ClientCommand::SetFirTaps { taps } => RigCommand::SetFirTaps(taps),
|
||||||
|
ClientCommand::SetSdrGain { gain_db } => RigCommand::SetSdrGain(gain_db),
|
||||||
ClientCommand::SetWfmDeemphasis { deemphasis_us } => {
|
ClientCommand::SetWfmDeemphasis { deemphasis_us } => {
|
||||||
RigCommand::SetWfmDeemphasis(deemphasis_us)
|
RigCommand::SetWfmDeemphasis(deemphasis_us)
|
||||||
}
|
}
|
||||||
@@ -93,6 +94,7 @@ pub fn rig_command_to_client(cmd: RigCommand) -> ClientCommand {
|
|||||||
RigCommand::ResetWsprDecoder => ClientCommand::ResetWsprDecoder,
|
RigCommand::ResetWsprDecoder => ClientCommand::ResetWsprDecoder,
|
||||||
RigCommand::SetBandwidth(bandwidth_hz) => ClientCommand::SetBandwidth { bandwidth_hz },
|
RigCommand::SetBandwidth(bandwidth_hz) => ClientCommand::SetBandwidth { bandwidth_hz },
|
||||||
RigCommand::SetFirTaps(taps) => ClientCommand::SetFirTaps { taps },
|
RigCommand::SetFirTaps(taps) => ClientCommand::SetFirTaps { taps },
|
||||||
|
RigCommand::SetSdrGain(gain_db) => ClientCommand::SetSdrGain { gain_db },
|
||||||
RigCommand::SetWfmDeemphasis(deemphasis_us) => {
|
RigCommand::SetWfmDeemphasis(deemphasis_us) => {
|
||||||
ClientCommand::SetWfmDeemphasis { deemphasis_us }
|
ClientCommand::SetWfmDeemphasis { deemphasis_us }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ pub enum ClientCommand {
|
|||||||
ResetWsprDecoder,
|
ResetWsprDecoder,
|
||||||
SetBandwidth { bandwidth_hz: u32 },
|
SetBandwidth { bandwidth_hz: u32 },
|
||||||
SetFirTaps { taps: u32 },
|
SetFirTaps { taps: u32 },
|
||||||
|
SetSdrGain { gain_db: f64 },
|
||||||
SetWfmDeemphasis { deemphasis_us: u32 },
|
SetWfmDeemphasis { deemphasis_us: u32 },
|
||||||
SetWfmStereo { enabled: bool },
|
SetWfmStereo { enabled: bool },
|
||||||
GetSpectrum,
|
GetSpectrum,
|
||||||
|
|||||||
@@ -452,6 +452,14 @@ async fn process_command(
|
|||||||
let _ = ctx.state_tx.send(ctx.state.clone());
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
return snapshot_from(ctx.state);
|
return snapshot_from(ctx.state);
|
||||||
}
|
}
|
||||||
|
RigCommand::SetSdrGain(gain_db) => {
|
||||||
|
if let Err(e) = ctx.rig.set_sdr_gain(gain_db).await {
|
||||||
|
return Err(RigError::communication(format!("set_sdr_gain: {e}")));
|
||||||
|
}
|
||||||
|
ctx.state.filter = ctx.rig.filter_state();
|
||||||
|
let _ = ctx.state_tx.send(ctx.state.clone());
|
||||||
|
return snapshot_from(ctx.state);
|
||||||
|
}
|
||||||
RigCommand::SetWfmDeemphasis(deemphasis_us) => {
|
RigCommand::SetWfmDeemphasis(deemphasis_us) => {
|
||||||
if let Err(e) = ctx.rig.set_wfm_deemphasis(deemphasis_us).await {
|
if let Err(e) = ctx.rig.set_wfm_deemphasis(deemphasis_us).await {
|
||||||
return Err(RigError::communication(format!("set_wfm_deemphasis: {e}")));
|
return Err(RigError::communication(format!("set_wfm_deemphasis: {e}")));
|
||||||
|
|||||||
@@ -45,6 +45,12 @@ pub trait IqSource: Send + 'static {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply a new hardware receive gain in dB. Default implementation is a
|
||||||
|
/// no-op for non-hardware sources.
|
||||||
|
fn set_gain(&mut self, _gain_db: f64) -> Result<(), String> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Gives a source-specific implementation a chance to recover from a
|
/// Gives a source-specific implementation a chance to recover from a
|
||||||
/// read error (for example, by rearming a hardware stream after overflow).
|
/// read error (for example, by rearming a hardware stream after overflow).
|
||||||
/// Returns `true` when an active recovery action was attempted.
|
/// Returns `true` when an active recovery action was attempted.
|
||||||
@@ -740,6 +746,9 @@ pub struct SdrPipeline {
|
|||||||
/// Write `Some(hz)` here to retune the hardware center frequency.
|
/// Write `Some(hz)` here to retune the hardware center frequency.
|
||||||
/// The IQ read loop picks it up on the next iteration.
|
/// The IQ read loop picks it up on the next iteration.
|
||||||
pub retune_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
pub retune_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
||||||
|
/// Write `Some(gain_db)` here to adjust the hardware RX gain.
|
||||||
|
/// The IQ read loop picks it up on the next iteration.
|
||||||
|
pub gain_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SdrPipeline {
|
impl SdrPipeline {
|
||||||
@@ -785,6 +794,8 @@ impl SdrPipeline {
|
|||||||
let thread_spectrum_buf = spectrum_buf.clone();
|
let thread_spectrum_buf = spectrum_buf.clone();
|
||||||
let retune_cmd: Arc<std::sync::Mutex<Option<f64>>> = Arc::new(std::sync::Mutex::new(None));
|
let retune_cmd: Arc<std::sync::Mutex<Option<f64>>> = Arc::new(std::sync::Mutex::new(None));
|
||||||
let thread_retune_cmd = retune_cmd.clone();
|
let thread_retune_cmd = retune_cmd.clone();
|
||||||
|
let gain_cmd: Arc<std::sync::Mutex<Option<f64>>> = Arc::new(std::sync::Mutex::new(None));
|
||||||
|
let thread_gain_cmd = gain_cmd.clone();
|
||||||
|
|
||||||
std::thread::Builder::new()
|
std::thread::Builder::new()
|
||||||
.name("sdr-iq-read".to_string())
|
.name("sdr-iq-read".to_string())
|
||||||
@@ -796,6 +807,7 @@ impl SdrPipeline {
|
|||||||
iq_tx,
|
iq_tx,
|
||||||
thread_spectrum_buf,
|
thread_spectrum_buf,
|
||||||
thread_retune_cmd,
|
thread_retune_cmd,
|
||||||
|
thread_gain_cmd,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.expect("failed to spawn sdr-iq-read thread");
|
.expect("failed to spawn sdr-iq-read thread");
|
||||||
@@ -806,6 +818,7 @@ impl SdrPipeline {
|
|||||||
spectrum_buf,
|
spectrum_buf,
|
||||||
sdr_sample_rate,
|
sdr_sample_rate,
|
||||||
retune_cmd,
|
retune_cmd,
|
||||||
|
gain_cmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -829,6 +842,7 @@ fn iq_read_loop(
|
|||||||
iq_tx: broadcast::Sender<Vec<Complex<f32>>>,
|
iq_tx: broadcast::Sender<Vec<Complex<f32>>>,
|
||||||
spectrum_buf: Arc<Mutex<Option<Vec<f32>>>>,
|
spectrum_buf: Arc<Mutex<Option<Vec<f32>>>>,
|
||||||
retune_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
retune_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
||||||
|
gain_cmd: Arc<std::sync::Mutex<Option<f64>>>,
|
||||||
) {
|
) {
|
||||||
let mut block = vec![Complex::new(0.0_f32, 0.0_f32); IQ_BLOCK_SIZE];
|
let mut block = vec![Complex::new(0.0_f32, 0.0_f32); IQ_BLOCK_SIZE];
|
||||||
let block_duration_ms = if sdr_sample_rate > 0 {
|
let block_duration_ms = if sdr_sample_rate > 0 {
|
||||||
@@ -859,6 +873,15 @@ fn iq_read_loop(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Ok(mut cmd) = gain_cmd.try_lock() {
|
||||||
|
if let Some(gain_db) = cmd.take() {
|
||||||
|
if let Err(e) = source.set_gain(gain_db) {
|
||||||
|
tracing::warn!("SDR gain change to {:.1} dB failed: {}", gain_db, e);
|
||||||
|
} else {
|
||||||
|
tracing::info!("SDR gain updated to {:.1} dB", gain_db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let n = match source.read_into(&mut block) {
|
let n = match source.read_into(&mut block) {
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ pub struct SoapySdrRig {
|
|||||||
wfm_deemphasis_us: u32,
|
wfm_deemphasis_us: u32,
|
||||||
/// Whether WFM stereo decode is enabled.
|
/// Whether WFM stereo decode is enabled.
|
||||||
wfm_stereo: bool,
|
wfm_stereo: bool,
|
||||||
|
/// Requested hardware gain setting in dB.
|
||||||
|
gain_db: f64,
|
||||||
|
/// Optional hard ceiling for the applied hardware gain in dB.
|
||||||
|
max_gain_db: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SoapySdrRig {
|
impl SoapySdrRig {
|
||||||
@@ -200,6 +204,8 @@ impl SoapySdrRig {
|
|||||||
retune_cmd,
|
retune_cmd,
|
||||||
wfm_deemphasis_us,
|
wfm_deemphasis_us,
|
||||||
wfm_stereo: true,
|
wfm_stereo: true,
|
||||||
|
gain_db,
|
||||||
|
max_gain_db,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,6 +359,29 @@ impl RigCat for SoapySdrRig {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_sdr_gain<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
gain_db: f64,
|
||||||
|
) -> Pin<Box<dyn std::future::Future<Output = DynResult<()>> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if !gain_db.is_finite() {
|
||||||
|
return Err("gain must be finite".into());
|
||||||
|
}
|
||||||
|
if gain_db < 0.0 {
|
||||||
|
return Err("gain must be >= 0".into());
|
||||||
|
}
|
||||||
|
self.gain_db = gain_db;
|
||||||
|
let effective_gain_db = self
|
||||||
|
.max_gain_db
|
||||||
|
.map(|max_gain| gain_db.min(max_gain))
|
||||||
|
.unwrap_or(gain_db);
|
||||||
|
if let Ok(mut cmd) = self.pipeline.gain_cmd.lock() {
|
||||||
|
*cmd = Some(effective_gain_db);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_signal_strength<'a>(
|
fn get_signal_strength<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
) -> Pin<Box<dyn std::future::Future<Output = DynResult<u8>> + Send + 'a>> {
|
) -> Pin<Box<dyn std::future::Future<Output = DynResult<u8>> + Send + 'a>> {
|
||||||
@@ -503,6 +532,11 @@ impl RigCat for SoapySdrRig {
|
|||||||
bandwidth_hz: self.bandwidth_hz,
|
bandwidth_hz: self.bandwidth_hz,
|
||||||
fir_taps: self.fir_taps,
|
fir_taps: self.fir_taps,
|
||||||
cw_center_hz: 700,
|
cw_center_hz: 700,
|
||||||
|
sdr_gain_db: Some(
|
||||||
|
self.max_gain_db
|
||||||
|
.map(|max_gain| self.gain_db.min(max_gain))
|
||||||
|
.unwrap_or(self.gain_db),
|
||||||
|
),
|
||||||
wfm_deemphasis_us: self.wfm_deemphasis_us,
|
wfm_deemphasis_us: self.wfm_deemphasis_us,
|
||||||
wfm_stereo: self.wfm_stereo,
|
wfm_stereo: self.wfm_stereo,
|
||||||
wfm_stereo_detected,
|
wfm_stereo_detected,
|
||||||
|
|||||||
@@ -173,6 +173,12 @@ impl IqSource for RealIqSource {
|
|||||||
.map_err(|e| format!("Failed to retune SDR center frequency: {}", e))
|
.map_err(|e| format!("Failed to retune SDR center frequency: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_gain(&mut self, gain_db: f64) -> Result<(), String> {
|
||||||
|
self.device
|
||||||
|
.set_gain(soapysdr::Direction::Rx, 0, gain_db)
|
||||||
|
.map_err(|e| format!("Failed to set SDR gain: {}", e))
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_read_error(&mut self, err: &str) -> Result<bool, String> {
|
fn handle_read_error(&mut self, err: &str) -> Result<bool, String> {
|
||||||
let err_lc = err.to_ascii_lowercase();
|
let err_lc = err.to_ascii_lowercase();
|
||||||
let is_overrun = err_lc.contains("overflow") || err_lc.contains("overrun");
|
let is_overrun = err_lc.contains("overflow") || err_lc.contains("overrun");
|
||||||
|
|||||||
Reference in New Issue
Block a user