[fix](trx-frontend-http): snap tuning clicks to peaks
Improve spectrum click-to-tune by snapping the selected\nfrequency to a nearby dominant local peak, making signals\neasier to select.\n\nCo-authored-by: Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -2880,6 +2880,62 @@ function canvasXToHz(cssX, cssW, range) {
|
|||||||
return range.visLoHz + (cssX / cssW) * range.visSpanHz;
|
return range.visLoHz + (cssX / cssW) * range.visSpanHz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function nearestSpectrumPeakHz(cssX, cssW, data) {
|
||||||
|
if (!data || !Array.isArray(data.bins) || data.bins.length === 0 || cssW <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bins = data.bins;
|
||||||
|
const maxIdx = bins.length - 1;
|
||||||
|
const range = spectrumVisibleRange(data);
|
||||||
|
const fullLoHz = data.center_hz - data.sample_rate / 2;
|
||||||
|
const targetHz = canvasXToHz(cssX, cssW, range);
|
||||||
|
const targetIdx = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(maxIdx, Math.round(((targetHz - fullLoHz) / data.sample_rate) * maxIdx)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const visStartIdx = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(maxIdx, Math.floor(((range.visLoHz - fullLoHz) / data.sample_rate) * maxIdx)),
|
||||||
|
);
|
||||||
|
const visEndIdx = Math.max(
|
||||||
|
visStartIdx,
|
||||||
|
Math.min(maxIdx, Math.ceil(((range.visHiHz - fullLoHz) / data.sample_rate) * maxIdx)),
|
||||||
|
);
|
||||||
|
const visSpanBins = Math.max(1, visEndIdx - visStartIdx);
|
||||||
|
const searchRadius = Math.max(3, Math.min(80, Math.round((24 / cssW) * visSpanBins)));
|
||||||
|
const searchLo = Math.max(1, targetIdx - searchRadius);
|
||||||
|
const searchHi = Math.min(maxIdx - 1, targetIdx + searchRadius);
|
||||||
|
|
||||||
|
let windowMax = -Infinity;
|
||||||
|
const localPeaks = [];
|
||||||
|
for (let i = searchLo; i <= searchHi; i++) {
|
||||||
|
const val = bins[i];
|
||||||
|
if (val > windowMax) windowMax = val;
|
||||||
|
if (val >= bins[i - 1] && val >= bins[i + 1]) {
|
||||||
|
localPeaks.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidates = localPeaks.filter((i) => bins[i] >= windowMax - 6);
|
||||||
|
const ranked = (candidates.length ? candidates : localPeaks).sort((a, b) => {
|
||||||
|
const dist = Math.abs(a - targetIdx) - Math.abs(b - targetIdx);
|
||||||
|
if (dist !== 0) return dist;
|
||||||
|
return bins[b] - bins[a];
|
||||||
|
});
|
||||||
|
|
||||||
|
let snappedIdx = ranked[0];
|
||||||
|
if (snappedIdx == null) {
|
||||||
|
snappedIdx = targetIdx;
|
||||||
|
for (let i = searchLo; i <= searchHi; i++) {
|
||||||
|
if (bins[i] > bins[snappedIdx]) snappedIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(fullLoHz + (snappedIdx / maxIdx) * data.sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
// Format a frequency according to the current jog-step unit.
|
// Format a frequency according to the current jog-step unit.
|
||||||
function formatSpectrumFreq(hz) {
|
function formatSpectrumFreq(hz) {
|
||||||
if (jogStep >= 1_000_000) return (hz / 1e6).toFixed(3) + " MHz";
|
if (jogStep >= 1_000_000) return (hz / 1e6).toFixed(3) + " MHz";
|
||||||
@@ -3364,8 +3420,10 @@ if (spectrumCanvas) {
|
|||||||
if (_sDragMoved) { _sDragMoved = false; return; }
|
if (_sDragMoved) { _sDragMoved = false; return; }
|
||||||
if (!lastSpectrumData) return;
|
if (!lastSpectrumData) return;
|
||||||
const rect = spectrumCanvas.getBoundingClientRect();
|
const rect = spectrumCanvas.getBoundingClientRect();
|
||||||
|
const cssX = e.clientX - rect.left;
|
||||||
const range = spectrumVisibleRange(lastSpectrumData);
|
const range = spectrumVisibleRange(lastSpectrumData);
|
||||||
const targetHz = Math.round(canvasXToHz(e.clientX - rect.left, rect.width, range));
|
const targetHz = nearestSpectrumPeakHz(cssX, rect.width, lastSpectrumData)
|
||||||
|
?? Math.round(canvasXToHz(cssX, rect.width, range));
|
||||||
postPath(`/set_freq?hz=${targetHz}`).catch(() => {});
|
postPath(`/set_freq?hz=${targetHz}`).catch(() => {});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user