diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js index f043566..0a16931 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js @@ -582,16 +582,16 @@ let sigStrengthUnitIdx = loadSetting("sigStrengthUnit", 0); function formatSigStrength(dbm) { if (!Number.isFinite(dbm)) return "--"; const unit = SIG_STRENGTH_UNITS[sigStrengthUnitIdx] || "dBFS"; - if (unit === "dBm") return `${dbm} dBm`; + if (unit === "dBm") return `${dbm.toFixed(1)} dBm`; if (unit === "dBf") { // dBf = dBm + 107 (referenced to 1 femtowatt across 50 Ω) const dbf = dbm + 107; - return `${dbf.toFixed(0)} dBf`; + return `${dbf.toFixed(1)} dBf`; } // dBFS: map receiver range to a full-scale reference // Typical receiver: -140 dBm (noise floor) to 0 dBm (full scale) const dbfs = Math.max(-140, Math.min(0, dbm)); - return `${dbfs} dBFS`; + return `${dbfs.toFixed(1)} dBFS`; } function refreshSigStrengthDisplay() { @@ -9199,7 +9199,7 @@ function startSpectrumStreaming() { vchanRdsById = next; vchanSignalDbById = nextSig; if (typeof vchanActiveId !== "undefined" && vchanActiveId && nextSig.has(vchanActiveId)) { - sigLastDbm = Math.round(nextSig.get(vchanActiveId)); + sigLastDbm = nextSig.get(vchanActiveId); refreshSigStrengthDisplay(); } updateRdsPsOverlay(primaryRds); diff --git a/src/trx-core/src/rig/controller/machine.rs b/src/trx-core/src/rig/controller/machine.rs index 9a003e0..9f27708 100644 --- a/src/trx-core/src/rig/controller/machine.rs +++ b/src/trx-core/src/rig/controller/machine.rs @@ -307,7 +307,7 @@ impl RigMachineState { tx_en: true, vfo: data.vfo.clone(), tx: data.tx.clone(), - rx: Some(RigRxStatus { sig: Some(0) }), + rx: Some(RigRxStatus { sig: Some(0.0) }), lock: Some(data.locked), }), _ => None, diff --git a/src/trx-core/src/rig/mod.rs b/src/trx-core/src/rig/mod.rs index d3359fe..4bef2e3 100644 --- a/src/trx-core/src/rig/mod.rs +++ b/src/trx-core/src/rig/mod.rs @@ -125,6 +125,15 @@ pub trait RigCat: Rig + Send { &'a mut self, ) -> Pin> + Send + 'a>>; + /// Return precise signal strength in dBm/dBFS as a float. + /// Backends with continuous measurements (e.g. SDR) override this + /// to bypass the coarse 0..15 quantisation of `get_signal_strength`. + fn get_signal_strength_db<'a>( + &'a mut self, + ) -> Pin> + Send + 'a>> { + Box::pin(std::future::ready(None)) + } + fn get_tx_power<'a>(&'a mut self) -> Pin> + Send + 'a>>; fn get_tx_limit<'a>(&'a mut self) -> Pin> + Send + 'a>>; @@ -343,9 +352,9 @@ pub struct RigTxStatus { pub alc: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct RigRxStatus { - pub sig: Option, + pub sig: Option, } /// Configurable control settings that can be pushed to the rig. diff --git a/src/trx-server/src/rig_task.rs b/src/trx-server/src/rig_task.rs index 4320cbc..3feaf4f 100644 --- a/src/trx-server/src/rig_task.rs +++ b/src/trx-server/src/rig_task.rs @@ -887,7 +887,9 @@ async fn refresh_state_from_cat(rig: &mut Box, state: &mut RigState) state.status.vfo = vfo; if state.status.tx_en { - state.status.rx.get_or_insert(RigRxStatus { sig: None }).sig = Some(0); + state.status.rx.get_or_insert(RigRxStatus { sig: None }).sig = Some(0.0); + } else if let Some(db) = rig.get_signal_strength_db().await { + state.status.rx.get_or_insert(RigRxStatus { sig: None }).sig = Some(db); } else if let Ok(meter) = rig.get_signal_strength().await { let sig = map_signal_strength(&state.status.mode, meter); state.status.rx.get_or_insert(RigRxStatus { sig: None }).sig = Some(sig); @@ -1015,12 +1017,12 @@ async fn prime_vfo_state( } /// Map raw signal strength to S-meter value based on mode. -fn map_signal_strength(mode: &RigMode, raw: u8) -> i32 { +fn map_signal_strength(mode: &RigMode, raw: u8) -> f64 { // FT-817 returns 0-15 for signal strength // Map to approximate dBm / S-units match mode { - RigMode::FM | RigMode::WFM | RigMode::AIS | RigMode::VDES => -120 + (raw as i32 * 6), - _ => -127 + (raw as i32 * 6), + RigMode::FM | RigMode::WFM | RigMode::AIS | RigMode::VDES => -120.0 + (raw as f64 * 6.0), + _ => -127.0 + (raw as f64 * 6.0), } } diff --git a/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp/channel.rs b/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp/channel.rs index c4698ec..bd8b294 100644 --- a/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp/channel.rs +++ b/src/trx-server/trx-backend/trx-backend-soapysdr/src/dsp/channel.rs @@ -757,7 +757,13 @@ impl ChannelDsp { } // Signal strength measurement (before AGC). + // + // The decimated samples carry the total power of the channel bandwidth, + // which is higher than the per-bin spectral density shown on the + // spectrum display by approximately 10·log10(decim_factor). Subtract + // that so the meter and the spectrum peak agree. { + let decim_correction = 10.0 * (self.decim_factor as f32).max(1.0).log10(); if self.mode == RigMode::WFM { // WFM: smooth envelope power directly. // FM is constant-envelope, so I²+Q² is inherently stable @@ -769,7 +775,8 @@ impl ChannelDsp { let pwr = s.re * s.re + s.im * s.im; self.carrier_iq_i += alpha * (pwr - self.carrier_iq_i); } - self.last_signal_db = 10.0 * self.carrier_iq_i.max(1e-12).log10(); + self.last_signal_db = + 10.0 * self.carrier_iq_i.max(1e-12).log10() - decim_correction; } else { // Other modes: peak IQ magnitude with EMA smoothing. const SIGNAL_EMA_ALPHA: f32 = 0.4; @@ -777,7 +784,7 @@ impl ChannelDsp { .iter() .map(|s| s.re * s.re + s.im * s.im) .fold(0.0_f32, f32::max); - let peak_db = 10.0 * peak_power.max(1e-12).log10(); + let peak_db = 10.0 * peak_power.max(1e-12).log10() - decim_correction; self.last_signal_db += SIGNAL_EMA_ALPHA * (peak_db - self.last_signal_db); } } 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 d73f402..5e38227 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 @@ -567,6 +567,19 @@ impl RigCat for SoapySdrRig { }) } + fn get_signal_strength_db<'a>( + &'a mut self, + ) -> Pin> + Send + 'a>> { + Box::pin(async move { + self.pipeline + .channel_dsps + .read() + .unwrap() + .get(self.primary_channel_idx) + .and_then(|dsp| dsp.lock().ok().map(|d| d.signal_db() as f64)) + }) + } + // -- TX / unsupported methods ------------------------------------------- fn set_ptt<'a>(