mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Improve waterfall error handling and SDR tool path resolution
- 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>
This commit is contained in:
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import queue
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import threading
|
||||
@@ -546,6 +547,16 @@ def init_waterfall_websocket(app: Flask):
|
||||
}))
|
||||
continue
|
||||
|
||||
# Pre-flight: check the capture binary exists
|
||||
if not shutil.which(iq_cmd[0]):
|
||||
app_module.release_sdr_device(device_index)
|
||||
claimed_device = None
|
||||
ws.send(json.dumps({
|
||||
'status': 'error',
|
||||
'message': f'Required tool "{iq_cmd[0]}" not found. Install SoapySDR tools (rx_sdr).',
|
||||
}))
|
||||
continue
|
||||
|
||||
# Spawn I/Q capture process (retry to handle USB release lag)
|
||||
max_attempts = 3 if was_restarting else 1
|
||||
try:
|
||||
@@ -558,7 +569,7 @@ def init_waterfall_websocket(app: Flask):
|
||||
iq_process = subprocess.Popen(
|
||||
iq_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=0,
|
||||
)
|
||||
register_process(iq_process)
|
||||
@@ -566,17 +577,23 @@ def init_waterfall_websocket(app: Flask):
|
||||
# Brief check that process started
|
||||
time.sleep(0.3)
|
||||
if iq_process.poll() is not None:
|
||||
stderr_out = ''
|
||||
if iq_process.stderr:
|
||||
with suppress(Exception):
|
||||
stderr_out = iq_process.stderr.read().decode('utf-8', errors='replace').strip()
|
||||
unregister_process(iq_process)
|
||||
iq_process = None
|
||||
if attempt < max_attempts - 1:
|
||||
logger.info(
|
||||
f"I/Q process exited immediately, "
|
||||
f"retrying ({attempt + 1}/{max_attempts})..."
|
||||
+ (f" stderr: {stderr_out}" if stderr_out else "")
|
||||
)
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
detail = f": {stderr_out}" if stderr_out else ""
|
||||
raise RuntimeError(
|
||||
"I/Q capture process exited immediately"
|
||||
f"I/Q capture process exited immediately{detail}"
|
||||
)
|
||||
break # Process started successfully
|
||||
except Exception as e:
|
||||
|
||||
@@ -2515,6 +2515,10 @@ const Waterfall = (function () {
|
||||
_endMhz = msg.end_freq;
|
||||
_drawFreqAxis();
|
||||
}
|
||||
if (Number.isFinite(msg.effective_span_mhz)) {
|
||||
const spanEl = document.getElementById('wfSpanMhz');
|
||||
if (spanEl) spanEl.value = msg.effective_span_mhz;
|
||||
}
|
||||
_setStatus(`Streaming ${_startMhz.toFixed(4)} - ${_endMhz.toFixed(4)} MHz`);
|
||||
_setVisualStatus('RUNNING');
|
||||
if (_monitoring) {
|
||||
|
||||
@@ -10,6 +10,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from utils.dependencies import get_tool_path
|
||||
|
||||
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
||||
|
||||
|
||||
@@ -203,8 +205,9 @@ class AirspyCommandBuilder(CommandBuilder):
|
||||
device_str = self._build_device_string(device)
|
||||
freq_hz = int(frequency_mhz * 1e6)
|
||||
|
||||
rx_sdr_path = get_tool_path('rx_sdr') or 'rx_sdr'
|
||||
cmd = [
|
||||
'rx_sdr',
|
||||
rx_sdr_path,
|
||||
'-d', device_str,
|
||||
'-f', str(freq_hz),
|
||||
'-s', str(sample_rate),
|
||||
|
||||
@@ -9,6 +9,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from utils.dependencies import get_tool_path
|
||||
|
||||
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
||||
|
||||
|
||||
@@ -203,8 +205,9 @@ class HackRFCommandBuilder(CommandBuilder):
|
||||
device_str = self._build_device_string(device)
|
||||
freq_hz = int(frequency_mhz * 1e6)
|
||||
|
||||
rx_sdr_path = get_tool_path('rx_sdr') or 'rx_sdr'
|
||||
cmd = [
|
||||
'rx_sdr',
|
||||
rx_sdr_path,
|
||||
'-d', device_str,
|
||||
'-f', str(freq_hz),
|
||||
'-s', str(sample_rate),
|
||||
|
||||
@@ -9,6 +9,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from utils.dependencies import get_tool_path
|
||||
|
||||
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
||||
|
||||
|
||||
@@ -181,8 +183,9 @@ class LimeSDRCommandBuilder(CommandBuilder):
|
||||
device_str = self._build_device_string(device)
|
||||
freq_hz = int(frequency_mhz * 1e6)
|
||||
|
||||
rx_sdr_path = get_tool_path('rx_sdr') or 'rx_sdr'
|
||||
cmd = [
|
||||
'rx_sdr',
|
||||
rx_sdr_path,
|
||||
'-d', device_str,
|
||||
'-f', str(freq_hz),
|
||||
'-s', str(sample_rate),
|
||||
|
||||
@@ -9,6 +9,8 @@ from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from utils.dependencies import get_tool_path
|
||||
|
||||
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
|
||||
|
||||
|
||||
@@ -181,8 +183,9 @@ class SDRPlayCommandBuilder(CommandBuilder):
|
||||
device_str = self._build_device_string(device)
|
||||
freq_hz = int(frequency_mhz * 1e6)
|
||||
|
||||
rx_sdr_path = get_tool_path('rx_sdr') or 'rx_sdr'
|
||||
cmd = [
|
||||
'rx_sdr',
|
||||
rx_sdr_path,
|
||||
'-d', device_str,
|
||||
'-f', str(freq_hz),
|
||||
'-s', str(sample_rate),
|
||||
|
||||
Reference in New Issue
Block a user