From 8f7afed13286c271964ef54dd73ab969d4739f52 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Sat, 28 Feb 2026 12:13:06 +0100 Subject: [PATCH] [fix](trx-backend-soapysdr,trx-frontend-http): keep rds state on bw changes Co-authored-by: Codex Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 32 ++++++++++++--- .../trx-frontend-http/assets/web/index.html | 8 ++-- .../trx-backend-soapysdr/src/dsp.rs | 39 +++++++++---------- 3 files changed, 48 insertions(+), 31 deletions(-) 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 141725c..71c87ae 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 @@ -366,9 +366,10 @@ let sigMeasurePeak = null; let lastFreqHz = null; let centerFreqDirty = false; let jogUnit = loadSetting("jogUnit", 1000); // base unit: 1, 1000, 1000000 -let jogMult = loadSetting("jogMult", 1); // multiplier: 1, 10, 100 -let jogStep = Math.max(jogUnit * jogMult, 1); +let jogMult = loadSetting("jogMult", 1); // divisor: 1, 10, 100 +let jogStep = Math.max(Math.round(jogUnit / jogMult), 1); let minFreqStepHz = 1; +let lastModeName = ""; const VFO_COLORS = ["var(--accent-green)", "var(--accent-yellow)"]; function vfoColor(idx) { if (idx < VFO_COLORS.length) return VFO_COLORS[idx]; @@ -1053,7 +1054,7 @@ function updateJogStepSupport(cap) { Number.isFinite(current) && current >= minFreqStepHz ? current : Math.max(steps[0], minFreqStepHz); jogUnit = steps.reduce((best, s) => (Math.abs(s - desired) < Math.abs(best - desired) ? s : best), steps[0]); - jogStep = Math.max(jogUnit * jogMult, minFreqStepHz); + jogStep = Math.max(Math.round(jogUnit / jogMult), minFreqStepHz); saveSetting("jogUnit", jogUnit); saveSetting("jogStep", jogStep); @@ -1240,7 +1241,12 @@ function render(update) { } if (update.status && update.status.mode) { const mode = normalizeMode(update.status.mode); - modeEl.value = mode ? mode.toUpperCase() : ""; + const modeUpper = mode ? mode.toUpperCase() : ""; + modeEl.value = modeUpper; + if (modeUpper === "WFM" && lastModeName !== "WFM") { + setJogDivisor(10); + } + lastModeName = modeUpper; updateWfmControls(); // When filter panel is active (SDR backend), update the BW slider range // to match the new mode — but only if the server hasn't already sent a @@ -1716,7 +1722,7 @@ const jogStepEl = document.getElementById("jog-step"); const jogMultEl = document.getElementById("jog-mult"); function applyJogStep() { - jogStep = Math.max(jogUnit * jogMult, minFreqStepHz); + jogStep = Math.max(Math.round(jogUnit / jogMult), minFreqStepHz); saveSetting("jogUnit", jogUnit); saveSetting("jogMult", jogMult); saveSetting("jogStep", jogStep); @@ -1724,6 +1730,17 @@ function applyJogStep() { refreshCenterFreqDisplay(); } +function setJogDivisor(divisor) { + const next = divisor === 10 || divisor === 100 ? divisor : 1; + jogMult = next; + if (jogMultEl) { + jogMultEl.querySelectorAll("button[data-mult]").forEach((b) => { + b.classList.toggle("active", parseInt(b.dataset.mult, 10) === jogMult); + }); + } + applyJogStep(); +} + async function jogFreq(direction) { if (lastLocked) { showHint("Locked", 1500); return; } if (lastFreqHz === null) return; @@ -1835,7 +1852,7 @@ if (jogMultEl) { multBtns.forEach((b) => b.classList.toggle("active", b === activeMult)); } } - jogStep = Math.max(jogUnit * jogMult, minFreqStepHz); + jogStep = Math.max(Math.round(jogUnit / jogMult), minFreqStepHz); } async function applyModeFromPicker() { @@ -1850,6 +1867,9 @@ async function applyModeFromPicker() { try { await postPath(`/set_mode?mode=${encodeURIComponent(mode)}`); showHint("Mode set", 1500); + if (mode.toUpperCase() === "WFM") { + setJogDivisor(10); + } // Apply sensible default bandwidth for the new mode and push to server. await applyBwDefaultForMode(mode, true); } catch (err) { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html index 40a7553..fa976a8 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html @@ -124,11 +124,11 @@
- - - + + +
-
Unit Multiplier
+
Unit Divisor
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 75affe8..2497ee6 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 @@ -332,8 +332,8 @@ impl ChannelDsp { (decim_factor, channel_sample_rate) } - fn rebuild_filters(&mut self) { - let (_, channel_sample_rate) = Self::pipeline_rates( + fn rebuild_filters(&mut self, reset_wfm_decoder: bool) { + let (next_decim_factor, channel_sample_rate) = Self::pipeline_rates( &self.mode, self.sdr_sample_rate, self.audio_sample_rate, @@ -354,13 +354,8 @@ impl ChannelDsp { }; self.lpf_i = BlockFirFilter::new(cutoff_norm, self.fir_taps, IQ_BLOCK_SIZE); self.lpf_q = BlockFirFilter::new(cutoff_norm, self.fir_taps, IQ_BLOCK_SIZE); - self.decim_factor = Self::pipeline_rates( - &self.mode, - self.sdr_sample_rate, - self.audio_sample_rate, - self.audio_bandwidth_hz, - ) - .0; + let rate_changed = self.decim_factor != next_decim_factor; + self.decim_factor = next_decim_factor; self.decim_counter = 0; self.resample_phase = 0.0; self.resample_phase_inc = if self.sdr_sample_rate == 0 { @@ -368,16 +363,18 @@ impl ChannelDsp { } else { self.audio_sample_rate as f64 / self.sdr_sample_rate as f64 }; - self.wfm_decoder = if self.mode == RigMode::WFM { - Some(WfmStereoDecoder::new( - channel_sample_rate, - self.audio_sample_rate, - self.output_channels, - self.wfm_deemphasis_us, - )) + if self.mode == RigMode::WFM { + if reset_wfm_decoder || rate_changed || self.wfm_decoder.is_none() { + self.wfm_decoder = Some(WfmStereoDecoder::new( + channel_sample_rate, + self.audio_sample_rate, + self.output_channels, + self.wfm_deemphasis_us, + )); + } } else { - None - }; + self.wfm_decoder = None; + } self.frame_buf.clear(); } @@ -466,7 +463,7 @@ impl ChannelDsp { self.mode = mode.clone(); self.audio_bandwidth_hz = default_bandwidth_for_mode(mode); self.demodulator = Demodulator::for_mode(mode); - self.rebuild_filters(); + self.rebuild_filters(true); } } @@ -490,12 +487,12 @@ impl ChannelDsp { pub fn set_filter(&mut self, bandwidth_hz: u32, taps: usize) { self.audio_bandwidth_hz = bandwidth_hz; self.fir_taps = taps.max(1); - self.rebuild_filters(); + self.rebuild_filters(false); } pub fn set_wfm_deemphasis(&mut self, deemphasis_us: u32) { self.wfm_deemphasis_us = deemphasis_us; - self.rebuild_filters(); + self.rebuild_filters(true); } pub fn rds_data(&self) -> Option {