mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Move waterfall controls to function bar and fix SDR claim race on tune
Move waterfall controls from the sidebar into a function-strip bar inside #listeningPostVisuals so they sit directly above the waterfall canvas. Also fix the "SDR device in use" error when clicking a waterfall frequency to listen — the WebSocket waterfall's device claim wasn't being released before the audio start request because the backend cleanup hadn't finished. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1305,11 +1305,20 @@ def start_audio() -> Response:
|
||||
scanner_config['device'] = device
|
||||
scanner_config['sdr_type'] = sdr_type
|
||||
|
||||
# Stop waterfall if it's using the same SDR
|
||||
# Stop waterfall if it's using the same SDR (SSE path)
|
||||
if waterfall_running and waterfall_active_device == device:
|
||||
_stop_waterfall_internal()
|
||||
time.sleep(0.2)
|
||||
|
||||
# Release waterfall device claim if the WebSocket waterfall is still
|
||||
# holding it. The JS client sends a stop command and closes the
|
||||
# WebSocket before requesting audio, but the backend handler may not
|
||||
# have finished its cleanup yet.
|
||||
device_status = app_module.get_sdr_device_status()
|
||||
if device_status.get(device) == 'waterfall':
|
||||
app_module.release_sdr_device(device)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Claim device for listening audio
|
||||
if listening_active_device is None or listening_active_device != device:
|
||||
if listening_active_device is not None:
|
||||
|
||||
@@ -3172,8 +3172,12 @@ 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';
|
||||
startBtn.style.display = running ? 'none' : 'inline-block';
|
||||
stopBtn.style.display = running ? 'inline-block' : 'none';
|
||||
const dot = document.getElementById('waterfallStripDot');
|
||||
if (dot) {
|
||||
dot.className = running ? 'status-dot sweeping' : 'status-dot inactive';
|
||||
}
|
||||
}
|
||||
|
||||
function getWaterfallRangeFromInputs() {
|
||||
@@ -3925,6 +3929,8 @@ async function stopWaterfall() {
|
||||
if (typeof releaseDevice === 'function') {
|
||||
releaseDevice('waterfall');
|
||||
}
|
||||
// Allow backend WebSocket handler to finish cleanup and release SDR
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -505,42 +505,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shared Waterfall Controls -->
|
||||
<div class="section" id="waterfallControlsSection" style="display: none;">
|
||||
<h3>Waterfall</h3>
|
||||
<div class="form-group" style="margin-bottom: 6px;">
|
||||
<label style="font-size: 10px;">Start (MHz)</label>
|
||||
<input type="number" id="waterfallStartFreq" value="88" step="0.1" style="width: 100%; padding: 5px; background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 4px; font-size: 11px;">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 6px;">
|
||||
<label style="font-size: 10px;">End (MHz)</label>
|
||||
<input type="number" id="waterfallEndFreq" value="108" step="0.1" style="width: 100%; padding: 5px; background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 4px; font-size: 11px;">
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 6px;">
|
||||
<label style="font-size: 10px;">Zoom</label>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<button class="tune-btn" type="button" onclick="zoomWaterfall('out')" style="padding: 4px 8px;">-</button>
|
||||
<button class="tune-btn" type="button" onclick="zoomWaterfall('in')" style="padding: 4px 8px;">+</button>
|
||||
<span id="waterfallZoomSpan" style="font-size: 10px; color: var(--text-muted);">20.0 MHz</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 6px;">
|
||||
<label style="font-size: 10px;">FFT Size</label>
|
||||
<select id="waterfallFftSize" style="width: 100%; padding: 5px; background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 4px; font-size: 11px;">
|
||||
<option value="512">512</option>
|
||||
<option value="1024" selected>1024</option>
|
||||
<option value="2048">2048</option>
|
||||
<option value="4096">4096</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 8px;">
|
||||
<label style="font-size: 10px;">Gain</label>
|
||||
<input type="number" id="waterfallGain" value="40" min="0" max="50" style="width: 100%; padding: 5px; background: var(--bg-secondary); border: 1px solid var(--border-color); color: var(--text-primary); border-radius: 4px; font-size: 11px;">
|
||||
</div>
|
||||
<button class="run-btn" id="startWaterfallBtn" onclick="startWaterfall()" style="width: 100%; padding: 8px;">Start Waterfall</button>
|
||||
<button class="stop-btn" id="stopWaterfallBtn" onclick="stopWaterfall()" style="display: none; width: 100%; padding: 8px; margin-top: 4px;">Stop Waterfall</button>
|
||||
</div>
|
||||
|
||||
{% include 'partials/modes/pager.html' %}
|
||||
|
||||
{% include 'partials/modes/sensor.html' %}
|
||||
@@ -607,16 +571,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WATERFALL / SPECTROGRAM PANEL -->
|
||||
<div id="waterfallPanel" class="radio-module-box" style="padding: 10px; display: none; margin-bottom: 12px;">
|
||||
<div class="module-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 10px;">
|
||||
<span>WATERFALL / SPECTROGRAM</span>
|
||||
<span id="waterfallFreqRange" style="font-size: 9px; color: var(--accent-cyan);"></span>
|
||||
</div>
|
||||
<canvas id="spectrumCanvas" width="800" height="120" style="width: 100%; height: 120px; border-radius: 4px; background: rgba(0,0,0,0.8);"></canvas>
|
||||
<canvas id="waterfallCanvas" width="800" height="400" style="width: 100%; height: 400px; border-radius: 4px; margin-top: 4px; background: rgba(0,0,0,0.9);"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- WiFi Layout Container -->
|
||||
<div class="wifi-layout-container" id="wifiLayoutContainer" style="display: none;">
|
||||
<!-- Status Bar -->
|
||||
@@ -1072,6 +1026,66 @@
|
||||
<!-- Listening Post Visualizations - Professional Ham Radio Scanner -->
|
||||
<div class="wifi-visuals" id="listeningPostVisuals" style="display: none;">
|
||||
|
||||
<!-- WATERFALL FUNCTION BAR -->
|
||||
<div class="function-strip listening-strip" style="grid-column: span 4;">
|
||||
<div class="function-strip-inner">
|
||||
<!-- Span display -->
|
||||
<div class="strip-stat">
|
||||
<span class="strip-value" id="waterfallZoomSpan">20.0 MHz</span>
|
||||
<span class="strip-label">SPAN</span>
|
||||
</div>
|
||||
<div class="strip-divider"></div>
|
||||
<!-- Frequency inputs -->
|
||||
<div class="strip-control">
|
||||
<span class="strip-input-label">START</span>
|
||||
<input type="number" id="waterfallStartFreq" class="strip-input wide" value="88" step="0.1">
|
||||
</div>
|
||||
<div class="strip-control">
|
||||
<span class="strip-input-label">END</span>
|
||||
<input type="number" id="waterfallEndFreq" class="strip-input wide" value="108" step="0.1">
|
||||
</div>
|
||||
<div class="strip-divider"></div>
|
||||
<!-- Zoom buttons -->
|
||||
<button type="button" class="strip-btn" onclick="zoomWaterfall('out')">−</button>
|
||||
<button type="button" class="strip-btn" onclick="zoomWaterfall('in')">+</button>
|
||||
<div class="strip-divider"></div>
|
||||
<!-- FFT Size -->
|
||||
<div class="strip-control">
|
||||
<span class="strip-input-label">FFT</span>
|
||||
<select id="waterfallFftSize" class="strip-select">
|
||||
<option value="512">512</option>
|
||||
<option value="1024" selected>1024</option>
|
||||
<option value="2048">2048</option>
|
||||
<option value="4096">4096</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Gain -->
|
||||
<div class="strip-control">
|
||||
<span class="strip-input-label">GAIN</span>
|
||||
<input type="number" id="waterfallGain" class="strip-input" value="40" min="0" max="50">
|
||||
</div>
|
||||
<div class="strip-divider"></div>
|
||||
<!-- Start / Stop -->
|
||||
<button type="button" class="strip-btn primary" id="startWaterfallBtn" onclick="startWaterfall()">▶ START</button>
|
||||
<button type="button" class="strip-btn stop" id="stopWaterfallBtn" onclick="stopWaterfall()" style="display: none;">◼ STOP</button>
|
||||
<!-- Status -->
|
||||
<div class="strip-status">
|
||||
<div class="status-dot inactive" id="waterfallStripDot"></div>
|
||||
<span id="waterfallFreqRange">STANDBY</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WATERFALL / SPECTROGRAM PANEL -->
|
||||
<div id="waterfallPanel" class="radio-module-box" style="grid-column: span 4; padding: 10px; display: none;">
|
||||
<div class="module-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 10px;">
|
||||
<span>WATERFALL / SPECTROGRAM</span>
|
||||
<span id="waterfallFreqRangeHeader" style="font-size: 9px; color: var(--accent-cyan);"></span>
|
||||
</div>
|
||||
<canvas id="spectrumCanvas" width="800" height="120" style="width: 100%; height: 120px; border-radius: 4px; background: rgba(0,0,0,0.8);"></canvas>
|
||||
<canvas id="waterfallCanvas" width="800" height="400" style="width: 100%; height: 400px; border-radius: 4px; margin-top: 4px; background: rgba(0,0,0,0.9);"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- TOP: FREQUENCY DISPLAY PANEL -->
|
||||
<div class="radio-module-box scanner-main" style="grid-column: span 4; padding: 12px;">
|
||||
<div style="display: flex; gap: 15px; align-items: stretch;">
|
||||
@@ -3122,15 +3136,11 @@
|
||||
const rtlDeviceSection = document.getElementById('rtlDeviceSection');
|
||||
if (rtlDeviceSection) rtlDeviceSection.style.display = (mode === 'pager' || mode === 'sensor' || mode === 'rtlamr' || mode === 'listening' || mode === 'aprs' || mode === 'sstv' || mode === 'sstv_general' || mode === 'dmr') ? 'block' : 'none';
|
||||
|
||||
// Show shared waterfall controls for supported modes
|
||||
const waterfallControlsSection = document.getElementById('waterfallControlsSection');
|
||||
// Show waterfall panel if running in listening mode
|
||||
const waterfallPanel = document.getElementById('waterfallPanel');
|
||||
const waterfallModes = ['listening'];
|
||||
const waterfallSupported = waterfallModes.includes(mode);
|
||||
if (waterfallControlsSection) waterfallControlsSection.style.display = waterfallSupported ? 'block' : 'none';
|
||||
if (waterfallPanel) {
|
||||
const running = (typeof isWaterfallRunning !== 'undefined' && isWaterfallRunning);
|
||||
waterfallPanel.style.display = (waterfallSupported && running) ? 'block' : 'none';
|
||||
waterfallPanel.style.display = (mode === 'listening' && running) ? 'block' : 'none';
|
||||
}
|
||||
|
||||
// Toggle mode-specific tool status displays
|
||||
|
||||
Reference in New Issue
Block a user