From 4bb7248257f8e1c202ddbc37d9d5d1e205a66065 Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Wed, 11 Mar 2026 07:27:51 +0100 Subject: [PATCH] [feat](trx-frontend-http): draw virtual channel markers on spectrum + OOB error - Draw sky-blue dashed/solid lines on spectrum overlay for each vchan - Active virtual channel gets a solid line; inactive ones are dashed - Validate freq against SDR capture window in vchanSetChannelFreq and show a showHint error when tuning out of bandwidth Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/app.js | 22 +++++++++++++++++++ .../assets/web/plugins/vchan.js | 15 +++++++++++++ 2 files changed, 37 insertions(+) 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 59af6cb..e9c225c 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 @@ -962,6 +962,28 @@ function drawSignalOverlay() { } } + // Virtual channel markers (sky-blue dashed lines, active one is solid). + if (typeof vchanChannels !== "undefined" && Array.isArray(vchanChannels)) { + vchanChannels.forEach(ch => { + if (!Number.isFinite(ch.freq_hz) || ch.freq_hz <= 0) return; + const xc = hzToX(ch.freq_hz); + if (xc < 0 || xc > W) return; + const isActive = ch.id === vchanActiveId; + const color = cssColorToRgba("#38bdf8"); + if (isActive) { + signalOverlayGl.drawSegments([xc, 0, xc, H], color, Math.max(1.5, dpr * 1.5)); + } else { + signalOverlayGl.drawDashedVerticalLine( + xc, 0, H, + Math.max(2, Math.round(4 * dpr)), + Math.max(3, Math.round(6 * dpr)), + color, + Math.max(1, dpr), + ); + } + }); + } + if (lastFreqHz != null) { const xf = hzToX(lastFreqHz); if (xf >= 0 && xf <= W) { diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js index fd686fb..a584d87 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/vchan.js @@ -217,6 +217,21 @@ let _origRefreshFreqDisplay = null; async function vchanSetChannelFreq(freqHz) { if (!vchanRigId || !vchanActiveId) return; + // Validate against current SDR capture window. + if (typeof lastSpectrumData !== "undefined" && lastSpectrumData && + lastSpectrumData.sample_rate > 0) { + const halfSpan = Number(lastSpectrumData.sample_rate) / 2; + const center = Number(lastSpectrumData.center_hz); + if (Math.abs(freqHz - center) > halfSpan) { + if (typeof showHint === "function") { + showHint( + `Out of SDR bandwidth (center ${(center / 1e6).toFixed(3)} MHz ±${(halfSpan / 1e3).toFixed(0)} kHz)`, + 3000 + ); + } + return; + } + } try { const resp = await fetch( `/channels/${encodeURIComponent(vchanRigId)}/${encodeURIComponent(vchanActiveId)}/freq`,