Resume waterfall after listen and sync to mode frequency

This commit is contained in:
Smittix
2026-02-07 22:40:00 +00:00
parent 8eb8a2fe97
commit b312eb20aa
5 changed files with 113 additions and 23 deletions

View File

@@ -88,6 +88,9 @@ function startDmr() {
if (typeof reserveDevice === 'function') {
reserveDevice(parseInt(device), 'dmr');
}
if (typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(frequency, { autoStart: true, restartIfRunning: true, silent: true });
}
if (typeof showNotification === 'function') {
showNotification('DMR', `Decoding ${frequency} MHz (${protocol.toUpperCase()})`);
}

View File

@@ -2367,8 +2367,7 @@ async function _startDirectListenInternal() {
isWaterfallRunning = true;
const waterfallPanel = document.getElementById('waterfallPanel');
if (waterfallPanel) waterfallPanel.style.display = 'block';
document.getElementById('startWaterfallBtn').style.display = 'none';
document.getElementById('stopWaterfallBtn').style.display = 'block';
setWaterfallControlButtons(true);
startAudioWaterfall();
}
updateDirectListenUI(true, freq);
@@ -2537,7 +2536,7 @@ async function startWebSocketListen(config, audioPlayer) {
/**
* Stop direct listening
*/
function stopDirectListen() {
async function stopDirectListen() {
console.log('[LISTEN] Stopping');
// Clear all pending state
@@ -2572,7 +2571,7 @@ function stopDirectListen() {
}
// Also stop via HTTP (fallback)
fetch('/listening/audio/stop', { method: 'POST' }).catch(() => {});
const audioStopPromise = fetch('/listening/audio/stop', { method: 'POST' }).catch(() => {});
isDirectListening = false;
currentSignalLevel = 0;
@@ -2585,11 +2584,15 @@ function stopDirectListen() {
if (resumeRfWaterfallAfterListening) {
isWaterfallRunning = false;
setWaterfallControlButtons(false);
await Promise.race([
audioStopPromise,
new Promise(resolve => setTimeout(resolve, 400))
]);
scheduleWaterfallResume();
} else if (waterfallMode === 'audio' && isWaterfallRunning) {
isWaterfallRunning = false;
document.getElementById('startWaterfallBtn').style.display = 'block';
document.getElementById('stopWaterfallBtn').style.display = 'none';
setWaterfallControlButtons(false);
}
}
@@ -3072,6 +3075,7 @@ const WATERFALL_RESUME_MAX_ATTEMPTS = 8;
const WATERFALL_RESUME_RETRY_MS = 350;
const WATERFALL_ZOOM_MIN_MHZ = 0.1;
const WATERFALL_ZOOM_MAX_MHZ = 500;
const WATERFALL_DEFAULT_SPAN_MHZ = 2.0;
function resizeCanvasToDisplaySize(canvas) {
if (!canvas) return false;
@@ -3142,6 +3146,14 @@ function initWaterfallCanvas() {
}
}
function setWaterfallControlButtons(running) {
const startBtn = document.getElementById('startWaterfallBtn');
const stopBtn = document.getElementById('stopWaterfallBtn');
if (!startBtn || !stopBtn) return;
startBtn.style.display = running ? 'none' : 'block';
stopBtn.style.display = running ? 'block' : 'none';
}
function getWaterfallRangeFromInputs() {
const startInput = document.getElementById('waterfallStartFreq');
const endInput = document.getElementById('waterfallEndFreq');
@@ -3200,6 +3212,33 @@ function getWaterfallCenterForZoom(start, end) {
return (start + end) / 2;
}
async function syncWaterfallToFrequency(freq, options = {}) {
const { autoStart = false, restartIfRunning = true, silent = true } = options;
const numericFreq = parseFloat(freq);
if (!Number.isFinite(numericFreq) || numericFreq <= 0) return { started: false };
const { start, end } = getWaterfallRangeFromInputs();
const span = (Number.isFinite(start) && Number.isFinite(end) && end > start)
? (end - start)
: WATERFALL_DEFAULT_SPAN_MHZ;
setWaterfallRange(numericFreq, span);
if (!autoStart) return { started: false };
if (isDirectListening || waterfallMode === 'audio') return { started: false };
if (isWaterfallRunning && waterfallMode === 'rf' && restartIfRunning) {
await stopWaterfall();
return await startWaterfall({ silent: silent });
}
if (!isWaterfallRunning) {
return await startWaterfall({ silent: silent });
}
return { started: true };
}
async function zoomWaterfall(direction) {
const { start, end } = getWaterfallRangeFromInputs();
if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) return;
@@ -3497,9 +3536,9 @@ async function startWaterfall(options = {}) {
isWaterfallRunning = true;
const waterfallPanel = document.getElementById('waterfallPanel');
if (waterfallPanel) waterfallPanel.style.display = 'block';
document.getElementById('startWaterfallBtn').style.display = 'none';
document.getElementById('stopWaterfallBtn').style.display = 'block';
setWaterfallControlButtons(true);
startAudioWaterfall();
resumeRfWaterfallAfterListening = true;
return { started: true };
}
@@ -3544,13 +3583,15 @@ async function startWaterfall(options = {}) {
}
isWaterfallRunning = true;
document.getElementById('startWaterfallBtn').style.display = 'none';
document.getElementById('stopWaterfallBtn').style.display = 'block';
setWaterfallControlButtons(true);
const waterfallPanel = document.getElementById('waterfallPanel');
if (waterfallPanel) waterfallPanel.style.display = 'block';
lastWaterfallDraw = 0;
initWaterfallCanvas();
connectWaterfallSSE();
if (typeof reserveDevice === 'function') {
reserveDevice(parseInt(device), 'waterfall');
}
if (resume || resumeRfWaterfallAfterListening) {
resumeRfWaterfallAfterListening = false;
}
@@ -3572,8 +3613,7 @@ async function stopWaterfall() {
if (waterfallMode === 'audio') {
stopAudioWaterfall();
isWaterfallRunning = false;
document.getElementById('startWaterfallBtn').style.display = 'block';
document.getElementById('stopWaterfallBtn').style.display = 'none';
setWaterfallControlButtons(false);
return;
}
@@ -3581,8 +3621,10 @@ async function stopWaterfall() {
await fetch('/listening/waterfall/stop', { method: 'POST' });
isWaterfallRunning = false;
if (waterfallEventSource) { waterfallEventSource.close(); waterfallEventSource = null; }
document.getElementById('startWaterfallBtn').style.display = 'block';
document.getElementById('stopWaterfallBtn').style.display = 'none';
setWaterfallControlButtons(false);
if (typeof releaseDevice === 'function') {
releaseDevice('waterfall');
}
} catch (err) {
console.error('[WATERFALL] Stop error:', err);
}
@@ -3665,3 +3707,4 @@ window.guessSignal = guessSignal;
window.startWaterfall = startWaterfall;
window.stopWaterfall = stopWaterfall;
window.zoomWaterfall = zoomWaterfall;
window.syncWaterfallToFrequency = syncWaterfallToFrequency;

View File

@@ -98,6 +98,9 @@ const SSTVGeneral = (function() {
updateStatusUI('listening', `${frequency} MHz ${modulation.toUpperCase()}`);
startStream();
showNotification('SSTV', `Listening on ${frequency} MHz ${modulation.toUpperCase()}`);
if (typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(frequency, { autoStart: true, restartIfRunning: true, silent: true });
}
// Update strip
const stripFreq = document.getElementById('sstvGeneralStripFreq');

View File

@@ -537,15 +537,18 @@ const SSTV = (function() {
const data = await response.json();
if (data.status === 'started' || data.status === 'already_running') {
isRunning = true;
if (typeof reserveDevice === 'function') {
reserveDevice(device, 'sstv');
}
updateStatusUI('listening', `${frequency} MHz`);
startStream();
showNotification('SSTV', `Listening on ${frequency} MHz`);
} else {
if (data.status === 'started' || data.status === 'already_running') {
isRunning = true;
if (typeof reserveDevice === 'function') {
reserveDevice(device, 'sstv');
}
updateStatusUI('listening', `${frequency} MHz`);
startStream();
if (typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(frequency, { autoStart: true, restartIfRunning: true, silent: true });
}
showNotification('SSTV', `Listening on ${frequency} MHz`);
} else {
updateStatusUI('idle', 'Start failed');
showStatusMessage(data.message || 'Failed to start decoder', 'error');
}

View File

@@ -2859,6 +2859,23 @@
}
}
function getModeWaterfallFrequency(mode) {
const lookup = {
pager: 'frequency',
sensor: 'sensorFrequency',
rtlamr: 'rtlamrFrequency',
dmr: 'dmrFrequency',
sstv: 'sstvFrequency',
sstv_general: 'sstvGeneralFrequency',
listening: 'radioScanStart'
};
const id = lookup[mode];
if (!id) return NaN;
const el = document.getElementById(id);
const value = parseFloat(el?.value);
return Number.isFinite(value) ? value : NaN;
}
// Mode switching
function switchMode(mode, options = {}) {
const { updateUrl = true } = options;
@@ -3069,6 +3086,12 @@
const running = (typeof isWaterfallRunning !== 'undefined' && isWaterfallRunning);
waterfallPanel.style.display = (waterfallSupported && running) ? 'block' : 'none';
}
if (waterfallSupported && typeof syncWaterfallToFrequency === 'function' && typeof isWaterfallRunning !== 'undefined' && isWaterfallRunning) {
const modeFreq = getModeWaterfallFrequency(mode);
if (Number.isFinite(modeFreq)) {
syncWaterfallToFrequency(modeFreq, { autoStart: true, restartIfRunning: true, silent: true });
}
}
// Toggle mode-specific tool status displays
const toolStatusPager = document.getElementById('toolStatusPager');
@@ -3164,6 +3187,9 @@
// Sensor frequency
function setSensorFreq(freq) {
document.getElementById('sensorFrequency').value = freq;
if (typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(freq, { autoStart: typeof isWaterfallRunning !== 'undefined' && isWaterfallRunning });
}
if (isSensorRunning) {
fetch('/stop_sensor', { method: 'POST' })
.then(() => setTimeout(() => startSensorDecoding(), 500));
@@ -3248,6 +3274,9 @@
reserveDevice(parseInt(device), 'sensor');
setSensorRunning(true);
startSensorStream();
if (!remoteConfig && typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(freq, { autoStart: true, restartIfRunning: true, silent: true });
}
// Initialize sensor filter bar
const filterContainer = document.getElementById('filterBarContainer');
@@ -3501,6 +3530,9 @@
function setRtlamrFreq(freq) {
document.getElementById('rtlamrFrequency').value = freq;
if (typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(freq, { autoStart: typeof isWaterfallRunning !== 'undefined' && isWaterfallRunning });
}
}
// RTLAMR mode polling timer for agent mode
@@ -3556,6 +3588,9 @@
}
setRtlamrRunning(true);
startRtlamrStream(isAgentMode);
if (!isAgentMode && typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(freq, { autoStart: true, restartIfRunning: true, silent: true });
}
// Initialize meter filter bar (reuse sensor filter bar since same structure)
const filterContainer = document.getElementById('filterBarContainer');
@@ -4327,6 +4362,9 @@
}
setRunning(true);
startStream(isAgentMode);
if (!isAgentMode && !remoteConfig && typeof syncWaterfallToFrequency === 'function') {
syncWaterfallToFrequency(freq, { autoStart: true, restartIfRunning: true, silent: true });
}
// Initialize filter bar
const filterContainer = document.getElementById('filterBarContainer');