From 1ebe65e0f257950cad0f709b2c202518274c725a Mon Sep 17 00:00:00 2001 From: Stan Grams Date: Wed, 4 Mar 2026 22:46:08 +0100 Subject: [PATCH] [fix](trx-frontend): fix CW audio picker and bookmark add button Use an audio-window tone picker for CW with exact click-to-tone mapping. Make + Add Bookmark inherit the shared button style. Co-authored-by: Codex Signed-off-by: Stan Grams --- .../trx-frontend-http/assets/web/index.html | 2 +- .../assets/web/plugins/cw.js | 78 +++++++++++-------- .../trx-frontend-http/assets/web/style.css | 13 +--- 3 files changed, 46 insertions(+), 47 deletions(-) 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 4a0d4da..592b53e 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 @@ -565,7 +565,7 @@
- CW Tone Picker + CW Audio Tone Picker --
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/cw.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/cw.js index b012d15..5f924d2 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/cw.js +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/cw.js @@ -48,34 +48,60 @@ function clampCwTone(tone) { } function currentCwToneRange() { - const centerHz = Number.isFinite(window.lastFreqHz) ? Number(window.lastFreqHz) : NaN; + const tunedHz = Number.isFinite(window.lastFreqHz) ? Number(window.lastFreqHz) : NaN; const bandwidthHz = Number.isFinite(window.currentBandwidthHz) ? Number(window.currentBandwidthHz) : NaN; - if (!Number.isFinite(centerHz) || !Number.isFinite(bandwidthHz) || bandwidthHz <= 0) { + if (!Number.isFinite(tunedHz) || !Number.isFinite(bandwidthHz) || bandwidthHz <= 0) { return null; } const mode = String(document.getElementById("mode")?.value || "").toUpperCase(); const lowerSideband = mode === "CWR"; const upperSideband = mode === "CW"; if (!lowerSideband && !upperSideband) return null; - const lowHz = lowerSideband ? centerHz - bandwidthHz : centerHz; - const highHz = lowerSideband ? centerHz : centerHz + bandwidthHz; + + const spectrumSampleRateHz = Number(window.lastSpectrumData?.sample_rate); + const spectrumCenterHz = Number(window.lastSpectrumData?.center_hz); + let maxToneFromSpectrumHz = Number.POSITIVE_INFINITY; + if ( + Number.isFinite(spectrumSampleRateHz) + && spectrumSampleRateHz > 0 + && Number.isFinite(spectrumCenterHz) + ) { + const spectrumLoHz = spectrumCenterHz - spectrumSampleRateHz / 2; + const spectrumHiHz = spectrumCenterHz + spectrumSampleRateHz / 2; + maxToneFromSpectrumHz = lowerSideband + ? tunedHz - spectrumLoHz + : spectrumHiHz - tunedHz; + } + const toneMinHz = CW_TONE_MIN_HZ; - const toneMaxHz = Math.min(CW_TONE_MAX_HZ, Math.round(bandwidthHz)); + const toneMaxHz = Math.min( + CW_TONE_MAX_HZ, + Math.round(bandwidthHz), + Number.isFinite(maxToneFromSpectrumHz) + ? Math.floor(maxToneFromSpectrumHz) + : CW_TONE_MAX_HZ, + ); if (toneMaxHz < toneMinHz) { return null; } return { - lowHz, - highHz, - centerHz, + tunedHz, bandwidthHz, toneMinHz, toneMaxHz, + toneSpanHz: Math.max(1, toneMaxHz - toneMinHz), lowerSideband, mode, }; } +function cwToneToRfHz(range, toneHz) { + if (!range) return NaN; + return range.lowerSideband + ? range.tunedHz - toneHz + : range.tunedHz + toneHz; +} + function toneClampForRange(tone, range) { const clamped = clampCwTone(tone); if (!range) return clamped; @@ -108,13 +134,15 @@ function drawCwTonePicker() { ctx.clearRect(0, 0, width, height); const range = currentCwToneRange(); - if (!range || !window.lastSpectrumData || !Array.isArray(window.lastSpectrumData.bins) || !window.lastSpectrumData.bins.length) { + if (!window.lastSpectrumData || !Array.isArray(window.lastSpectrumData.bins) || !window.lastSpectrumData.bins.length || !range) { if (cwToneRangeEl) { const mode = String(document.getElementById("mode")?.value || "").toUpperCase(); if (mode !== "CW" && mode !== "CWR") { cwToneRangeEl.textContent = "CW/CWR mode required"; - } else { + } else if (!window.lastSpectrumData || !Array.isArray(window.lastSpectrumData.bins) || !window.lastSpectrumData.bins.length) { cwToneRangeEl.textContent = "Waiting for spectrum"; + } else { + cwToneRangeEl.textContent = "Audio tone window is outside spectrum"; } } ctx.fillStyle = "rgba(130, 150, 165, 0.22)"; @@ -124,7 +152,7 @@ function drawCwTonePicker() { if (cwToneRangeEl) { const side = range.lowerSideband ? "Lower side" : "Upper side"; - cwToneRangeEl.textContent = `${side} · Tone ${range.toneMinHz}-${range.toneMaxHz} Hz`; + cwToneRangeEl.textContent = `Audio ${range.toneMinHz}-${range.toneMaxHz} Hz · ${side}`; } const bins = window.lastSpectrumData.bins; @@ -137,8 +165,9 @@ function drawCwTonePicker() { let minPower = Number.POSITIVE_INFINITY; for (let x = 0; x < width; x += 1) { const frac = width <= 1 ? 0 : x / (width - 1); - const toneHz = range.lowHz + frac * (range.highHz - range.lowHz); - const idx = Math.max(0, Math.min(maxIdx, Math.round((((toneHz - fullLoHz) / sampleRate) * maxIdx)))); + const toneHz = range.toneMinHz + frac * range.toneSpanHz; + const rfHz = cwToneToRfHz(range, toneHz); + const idx = Math.max(0, Math.min(maxIdx, Math.round((((rfHz - fullLoHz) / sampleRate) * maxIdx)))); const power = Number.isFinite(Number(bins[idx])) ? Number(bins[idx]) : -140; tones[x] = power; if (power > maxPower) maxPower = power; @@ -155,26 +184,11 @@ function drawCwTonePicker() { } const currentTone = toneClampForRange(cwToneInput ? cwToneInput.value : 700, range); - const markerHz = range.lowerSideband - ? range.centerHz - currentTone - : range.centerHz + currentTone; - const markerFrac = (markerHz - range.lowHz) / Math.max(1, (range.highHz - range.lowHz)); + const markerFrac = (currentTone - range.toneMinHz) / range.toneSpanHz; const markerX = Math.max(0, Math.min(width - 1, Math.round(markerFrac * (width - 1)))); ctx.fillStyle = "rgba(255, 255, 255, 0.9)"; ctx.fillRect(markerX, 0, 2, height); - const lowLimitHz = range.lowerSideband - ? range.centerHz - range.toneMaxHz - : range.centerHz + range.toneMinHz; - const highLimitHz = range.lowerSideband - ? range.centerHz - range.toneMinHz - : range.centerHz + range.toneMaxHz; - const limitLowX = Math.max(0, Math.min(width - 1, Math.round(((lowLimitHz - range.lowHz) / Math.max(1, range.highHz - range.lowHz)) * (width - 1)))); - const limitHighX = Math.max(0, Math.min(width - 1, Math.round(((highLimitHz - range.lowHz) / Math.max(1, range.highHz - range.lowHz)) * (width - 1)))); - ctx.fillStyle = "rgba(255, 255, 255, 0.22)"; - ctx.fillRect(limitLowX, 0, 1, height); - ctx.fillRect(limitHighX, 0, 1, height); - if (cwAutoInput?.checked) { ctx.fillStyle = "rgba(0, 0, 0, 0.22)"; ctx.fillRect(0, 0, width, height); @@ -233,11 +247,7 @@ if (cwToneCanvas) { const range = currentCwToneRange(); if (!range) return; const frac = Math.max(0, Math.min(1, (event.clientX - rect.left) / rect.width)); - const rfHz = range.lowHz + frac * (range.highHz - range.lowHz); - const signedOffsetHz = range.lowerSideband - ? range.centerHz - rfHz - : rfHz - range.centerHz; - const tone = Math.max(range.toneMinHz, Math.min(range.toneMaxHz, signedOffsetHz)); + const tone = range.toneMinHz + frac * range.toneSpanHz; await setCwTone(tone); }); } diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css index 2eaca42..18c10bd 100644 --- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css +++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css @@ -2716,18 +2716,7 @@ button:focus-visible, input:focus-visible, select:focus-visible { } .bm-add-btn { - background: var(--accent-green); - color: #fff; - border: none; - border-radius: 0.35rem; - padding: 0.4rem 0.85rem; - font-size: 0.85rem; - font-weight: 600; - cursor: pointer; -} - -.bm-add-btn:hover { - opacity: 0.88; + white-space: nowrap; } #bm-form-wrap {