When an error occurred with an out-of-range span (e.g. 30 MHz on
RTL-SDR), the span input kept the invalid value. Track the last
effective span from successful starts and reset the input on error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add pre-flight check for I/Q capture binary before spawning process
- Capture stderr from I/Q process for better error diagnostics
- Sync effective span value back to UI when backend adjusts it
- Use get_tool_path('rx_sdr') in Airspy, HackRF, LimeSDR, and SDRPlay
command builders to support custom install locations
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Stop Monitor button was disabled during shared monitor retunes
because _syncMonitorButtons disabled the button whenever
_startingMonitor was true, even if the monitor was already active.
Now only disables during initial start (not retunes).
2. Click-to-tune was inconsistent because the shared monitor retune
(rearm after capture restart) captured the center frequency early
in _startMonitorInternal, then sent it via POST to /audio/start.
If the user clicked a new frequency during the async reconnect,
the POST carried the stale frequency and could override the click.
Now retunes use the live _monitorFreqMhz and send a WS tune sync
after reconnecting to ensure the backend has the latest VFO.
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.
When changing frequency with shared monitor active, the monitor retune
could be silently dropped if a previous retune was still in-flight,
leaving the UI stuck on "Starting <freq>". After stopping and restarting
the waterfall, the monitor button could remain disabled because
_startingMonitor was never reset and _monitorRetuneTimer was not cleared.
- Cancel in-flight monitor start when queuing a new retune
- Always clear _pendingSharedMonitorRearm in started handler
- Clear _monitorRetuneTimer and reset _startingMonitor in stop()
stop() sets _ws = null before the async onclose fires, so the handler
now early-returns when _ws is null instead of showing the misleading
"WebSocket closed before ready" retry message.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- _waitForPlayback now only succeeds on playing/timeupdate events, not
loadeddata/canplay which fire from just the WAV header before real
audio arrives
- stopMonitor() pauses audio and updates UI immediately instead of
blocking on the backend stop request (1+ second delay)
- Reduced backend audio stop sleep from 1.0s to 0.15s; the start
retry loop already handles USB contention
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>