[fix](trx-frontend-http): move passband overlay to resolved DIG sideband
CI / lint (push) Failing after 2s
CI / test (push) Successful in 9m8s
CI / frontend (push) Successful in 5m24s
CI / reuse (push) Successful in 4s

The spectrum passband overlay is drawn one-sided per mode, but DIG was
hardcoded to upper sideband — so switching the DIG sideband (or tuning
DIG/Auto across 10 MHz) left the overlay on the wrong side of the carrier
even though the backend had flipped the demodulator.

Resolve the DIG overlay direction from the current sideband policy and dial
frequency, mirroring the backend: usb → upper, lsb → lower, auto → upper at
or above 10 MHz and lower below. The overlay repaints immediately on a
policy change (optimistically on the selector, and on confirmed filter
state) and tracks frequency as it already did. Non-SDR backends keep the
historical upper-sideband overlay.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UiK871ht2uPFBHtMbxy3wD
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
sjg
2026-08-16 10:24:30 +02:00
co-authored by Claude Opus 4.8
parent df7483fe30
commit 5084c19899
2 changed files with 51 additions and 2 deletions
@@ -3729,10 +3729,19 @@ function visibleBandwidthSpecs(freqHz = lastFreqHz, mode = modeEl ? modeEl.value
}
return [{ centerHz: freqHz, widthHz: currentBandwidthHz }];
}
function digSidebandDirection() {
if (sdrDigSidebandPolicy === "usb") return 1;
if (sdrDigSidebandPolicy === "lsb") return -1;
if (typeof lastFreqHz === "number" && isFiniteNumber(lastFreqHz)) {
return lastFreqHz >= DIG_AUTO_SIDEBAND_THRESHOLD_HZ ? 1 : -1;
}
return 1;
}
function sidebandDirectionForMode(mode = modeEl ? modeEl.value : "") {
const modeUpper = String(mode || "").toUpperCase();
if (modeUpper === "LSB" || modeUpper === "CWR") return -1;
if (modeUpper === "USB" || modeUpper === "CW" || modeUpper === "DIG") return 1;
if (modeUpper === "DIG") return sdrDigSidebandSupported ? digSidebandDirection() : 1;
if (modeUpper === "USB" || modeUpper === "CW") return 1;
return 0;
}
function displaySpanForBandwidthSpec(spec, mode = modeEl ? modeEl.value : "") {
@@ -4760,10 +4769,15 @@ function render(update) {
}
if (typeof update.filter.sdr_dig_sideband === "string") {
sdrDigSidebandSupported = true;
const prevPolicy = sdrDigSidebandPolicy;
sdrDigSidebandPolicy = update.filter.sdr_dig_sideband;
if (sdrDigSidebandEl && document.activeElement !== sdrDigSidebandEl) {
sdrDigSidebandEl.value = update.filter.sdr_dig_sideband;
}
updateWfmControls();
if (prevPolicy !== sdrDigSidebandPolicy && lastSpectrumData) {
scheduleSpectrumDraw();
}
}
}
if (typeof update.show_sdr_gain_control === "boolean") {
@@ -6634,6 +6648,8 @@ var sdrNbSupported = false;
var sdrDigSidebandWrapEl = document.getElementById("sdr-dig-sideband-wrap");
var sdrDigSidebandEl = document.getElementById("sdr-dig-sideband");
var sdrDigSidebandSupported = false;
var sdrDigSidebandPolicy = "auto";
var DIG_AUTO_SIDEBAND_THRESHOLD_HZ = 1e7;
fetch("/audio", { method: "GET" }).then((r) => {
if (r.status === 404) audioRow.style.display = "none";
}).catch(() => {
@@ -7033,6 +7049,8 @@ function submitSdrDigSideband() {
if (!sdrDigSidebandSupported || !sdrDigSidebandEl) return;
const policy = sdrDigSidebandEl.value || "auto";
if (policy !== "auto" && policy !== "usb" && policy !== "lsb") return;
sdrDigSidebandPolicy = policy;
if (lastSpectrumData) scheduleSpectrumDraw();
postPath(`/set_sdr_dig_sideband?policy=${encodeURIComponent(policy)}`).catch(() => {
});
}
@@ -2450,10 +2450,26 @@ function visibleBandwidthSpecs(freqHz: number | null = lastFreqHz, mode = modeEl
return [{ centerHz: freqHz, widthHz: currentBandwidthHz }];
}
// Resolve the DIG passband direction from the current policy and dial
// frequency, mirroring the backend's demodulator resolution so the spectrum
// overlay lands on the same sideband that is actually being demodulated.
function digSidebandDirection() {
if (sdrDigSidebandPolicy === "usb") return 1;
if (sdrDigSidebandPolicy === "lsb") return -1;
// "auto": USB at/above the threshold, LSB below.
if (typeof lastFreqHz === "number" && isFiniteNumber(lastFreqHz)) {
return lastFreqHz >= DIG_AUTO_SIDEBAND_THRESHOLD_HZ ? 1 : -1;
}
return 1;
}
function sidebandDirectionForMode(mode = modeEl ? modeEl.value : "") {
const modeUpper = String(mode || "").toUpperCase();
if (modeUpper === "LSB" || modeUpper === "CWR") return -1;
if (modeUpper === "USB" || modeUpper === "CW" || modeUpper === "DIG") return 1;
// On SDR the DIG sideband is configurable; resolve it. On other backends
// keep the historical upper-sideband overlay.
if (modeUpper === "DIG") return sdrDigSidebandSupported ? digSidebandDirection() : 1;
if (modeUpper === "USB" || modeUpper === "CW") return 1;
return 0;
}
@@ -3683,10 +3699,16 @@ function render(update: AppUpdate) {
}
if (typeof update.filter.sdr_dig_sideband === "string") {
sdrDigSidebandSupported = true;
const prevPolicy = sdrDigSidebandPolicy;
sdrDigSidebandPolicy = update.filter.sdr_dig_sideband;
if (sdrDigSidebandEl && document.activeElement !== sdrDigSidebandEl) {
sdrDigSidebandEl.value = update.filter.sdr_dig_sideband;
}
updateWfmControls();
// Repaint the passband overlay if the DIG sideband just changed.
if (prevPolicy !== sdrDigSidebandPolicy && lastSpectrumData) {
scheduleSpectrumDraw();
}
}
}
if (typeof update.show_sdr_gain_control === "boolean") {
@@ -5621,6 +5643,11 @@ let sdrNbSupported = false;
const sdrDigSidebandWrapEl = document.getElementById("sdr-dig-sideband-wrap");
const sdrDigSidebandEl = document.getElementById("sdr-dig-sideband") as HTMLSelectElement | null;
let sdrDigSidebandSupported = false;
// Current DIG sideband policy ("auto" | "usb" | "lsb"), mirrored from server
// filter state. Drives the spectrum passband overlay direction for DIG.
let sdrDigSidebandPolicy = "auto";
// Matches DigSidebandPolicy::AUTO_THRESHOLD_HZ on the backend.
const DIG_AUTO_SIDEBAND_THRESHOLD_HZ = 10_000_000;
// Hide audio row if audio is not configured on the server
fetch("/audio", { method: "GET" }).then((r) => {
@@ -6076,6 +6103,10 @@ function submitSdrDigSideband() {
if (!sdrDigSidebandSupported || !sdrDigSidebandEl) return;
const policy = sdrDigSidebandEl.value || "auto";
if (policy !== "auto" && policy !== "usb" && policy !== "lsb") return;
// Optimistically move the passband overlay to the chosen sideband so the
// display responds instantly, before the server round-trip confirms it.
sdrDigSidebandPolicy = policy;
if (lastSpectrumData) scheduleSpectrumDraw();
postPath(`/set_sdr_dig_sideband?policy=${encodeURIComponent(policy)}`).catch(() => {});
}
if (sdrDigSidebandEl) {