fix: waterfall device claim fails on frequency change due to USB release lag

When restarting capture for a new frequency, the USB handle from the
just-killed process may not be released by the kernel in time for the
rtl_test probe inside claim_sdr_device. Add retry logic (up to 4
attempts with 0.4s backoff) matching the pattern already used by the
audio start endpoint.

Also clean up stale shared-monitor state in the frontend error handler
so the monitor button is not left disabled when the capture restart
fails.
This commit is contained in:
Smittix
2026-02-23 21:41:14 +00:00
parent 79a8795c55
commit abfad2f60e
2 changed files with 123 additions and 104 deletions
+10 -2
View File
@@ -462,8 +462,16 @@ def init_waterfall_websocket(app: Flask):
start_freq = center_freq_mhz - effective_span_mhz / 2 start_freq = center_freq_mhz - effective_span_mhz / 2
end_freq = center_freq_mhz + effective_span_mhz / 2 end_freq = center_freq_mhz + effective_span_mhz / 2
# Claim the device # Claim the device (retry when restarting to allow
claim_err = app_module.claim_sdr_device(device_index, 'waterfall') # the kernel time to release the USB handle).
max_claim_attempts = 4 if was_restarting else 1
claim_err = None
for _claim_attempt in range(max_claim_attempts):
claim_err = app_module.claim_sdr_device(device_index, 'waterfall')
if not claim_err:
break
if _claim_attempt < max_claim_attempts - 1:
time.sleep(0.4)
if claim_err: if claim_err:
ws.send(json.dumps({ ws.send(json.dumps({
'status': 'error', 'status': 'error',
+11
View File
@@ -2488,6 +2488,17 @@ const Waterfall = (function () {
} else if (msg.status === 'error') { } else if (msg.status === 'error') {
_running = false; _running = false;
_scanStartPending = false; _scanStartPending = false;
_pendingSharedMonitorRearm = false;
// If the monitor was using the shared IQ stream that
// just failed, tear down the stale monitor state so
// the button becomes clickable again after restart.
if (_monitoring && _monitorSource === 'waterfall') {
clearTimeout(_monitorRetuneTimer);
_monitoring = false;
_monitorSource = 'process';
_syncMonitorButtons();
_setMonitorState('Monitor stopped (waterfall error)');
}
if (_scanRunning) { if (_scanRunning) {
_scanAwaitingCapture = true; _scanAwaitingCapture = true;
_setScanState(msg.message || 'Waterfall retune error, retrying...', true); _setScanState(msg.message || 'Waterfall retune error, retrying...', true);