[feat](trx-rs): expose WFM stereo denoise toggle

Add a server-side toggle for the multiband stereo denoiser so it can be
enabled or disabled at runtime without restarting the server.

Backend (trx-backend-soapysdr):
- Add `denoise_enabled: bool` to `WfmStereoDecoder`; gate multiband
  blend behind it (falls back to uniform single-band blend when off)
- Add `set_denoise_enabled()` method on `WfmStereoDecoder`
- Propagate `wfm_denoise: bool` through `ChannelDsp`, `SdrPipeline`,
  and `SoapySdrRig`; add `set_wfm_denoise()` at each layer
- Include `wfm_denoise` in `filter_state()` so it flows into snapshots

Protocol / core (trx-core, trx-protocol, trx-server):
- Add `SetWfmDenoise(bool)` to `RigCommand` and `ClientCommand`
- Add default `set_wfm_denoise()` trait method to `RigCat`
- Handle `SetWfmDenoise` in `rig_task.rs` and update `RigFilterState`
- Add `wfm_denoise: bool` (default `true`) to `RigFilterState`

Frontend (trx-frontend-http):
- Add `POST /toggle_wfm_denoise` endpoint
- Add "Denoise On/Off" button next to the stereo/mono audio picker
- Sync button state from SSE filter snapshot (`update.filter.wfm_denoise`)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-02-28 19:47:36 +01:00
parent ffdc193671
commit 95716a0fc3
14 changed files with 118 additions and 8 deletions
@@ -1252,6 +1252,12 @@ function render(update) {
if (wfmDeemphasisEl && typeof update.filter.wfm_deemphasis_us === "number") {
wfmDeemphasisEl.value = String(update.filter.wfm_deemphasis_us);
}
if (wfmDenoiseBtn && typeof update.filter.wfm_denoise === "boolean") {
const on = update.filter.wfm_denoise;
wfmDenoiseBtn.textContent = on ? "On" : "Off";
wfmDenoiseBtn.style.borderColor = on ? "" : "var(--accent-warn, #f0a500)";
wfmDenoiseBtn.style.color = on ? "" : "var(--accent-warn, #f0a500)";
}
}
if (update.status && update.status.freq && typeof update.status.freq.hz === "number") {
applyLocalTunedFrequency(update.status.freq.hz, true);
@@ -2546,6 +2552,7 @@ const audioRow = document.getElementById("audio-row");
const wfmControlsCol = document.getElementById("wfm-controls-col");
const wfmDeemphasisEl = document.getElementById("wfm-deemphasis");
const wfmAudioModeEl = document.getElementById("wfm-audio-mode");
const wfmDenoiseBtn = document.getElementById("wfm-denoise-btn");
// Hide audio row if audio is not configured on the server
fetch("/audio", { method: "GET" }).then((r) => {
@@ -2587,6 +2594,11 @@ if (wfmDeemphasisEl) {
postPath(`/set_wfm_deemphasis?us=${encodeURIComponent(wfmDeemphasisEl.value)}`).catch(() => {});
});
}
if (wfmDenoiseBtn) {
wfmDenoiseBtn.addEventListener("click", () => {
postPath("/toggle_wfm_denoise").catch(() => {});
});
}
function updateWfmControls() {
if (!wfmControlsCol) return;
@@ -165,6 +165,9 @@
<option value="mono">Mono</option>
</select>
</label>
<label class="wfm-control">Denoise
<button id="wfm-denoise-btn" type="button" class="status-input toggle-btn toggle-on">On</button>
</label>
</div>
<div class="label"><span>WFM</span></div>
</div>
@@ -486,6 +486,21 @@ pub async fn set_wfm_deemphasis(
send_command(&rig_tx, RigCommand::SetWfmDeemphasis(query.us)).await
}
#[post("/toggle_wfm_denoise")]
pub async fn toggle_wfm_denoise(
state: web::Data<watch::Receiver<RigState>>,
rig_tx: web::Data<mpsc::Sender<RigRequest>>,
) -> Result<HttpResponse, Error> {
let enabled = state
.get_ref()
.borrow()
.filter
.as_ref()
.map(|f| f.wfm_denoise)
.unwrap_or(true);
send_command(&rig_tx, RigCommand::SetWfmDenoise(!enabled)).await
}
#[post("/toggle_aprs_decode")]
pub async fn toggle_aprs_decode(
state: web::Data<watch::Receiver<RigState>>,
@@ -698,6 +713,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(set_bandwidth)
.service(set_fir_taps)
.service(set_wfm_deemphasis)
.service(toggle_wfm_denoise)
.service(toggle_aprs_decode)
.service(toggle_cw_decode)
.service(set_cw_auto)