[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 <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -565,7 +565,7 @@
|
||||
</div>
|
||||
<div class="cw-tone-picker">
|
||||
<div class="cw-tone-picker-head">
|
||||
<span>CW Tone Picker</span>
|
||||
<span>CW Audio Tone Picker</span>
|
||||
<small id="cw-tone-range">--</small>
|
||||
</div>
|
||||
<canvas id="cw-tone-waterfall" width="320" height="56" aria-label="CW tone selector"></canvas>
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user