mirror of
https://github.com/smittix/intercept.git
synced 2026-05-04 19:29:12 -07:00
Switch Morse startup to IQ-first and harden timeout handling
This commit is contained in:
@@ -354,8 +354,17 @@ def start_morse() -> Response:
|
||||
|
||||
can_try_direct_sampling = bool(sdr_device.sdr_type == SDRType.RTL_SDR and freq < 24.0)
|
||||
if can_try_direct_sampling:
|
||||
# Keep rtl_fm attempts first (cheap), then switch to IQ capture fallback.
|
||||
# IQ-first strategy: avoid repeated rtl_fm/rtl_sdr handoffs that can
|
||||
# leave the tuner in a bad state on some Linux builds.
|
||||
command_attempts: list[dict[str, Any]] = [
|
||||
{
|
||||
'source': 'iq',
|
||||
'direct_sampling_mode': 2,
|
||||
},
|
||||
{
|
||||
'source': 'iq',
|
||||
'direct_sampling_mode': None,
|
||||
},
|
||||
{
|
||||
'source': 'rtl_fm',
|
||||
'use_direct_sampling': True,
|
||||
@@ -370,17 +379,13 @@ def start_morse() -> Response:
|
||||
'add_resample_rate': True,
|
||||
'add_dc_fast': True,
|
||||
},
|
||||
{
|
||||
'source': 'iq',
|
||||
'direct_sampling_mode': 2,
|
||||
},
|
||||
]
|
||||
else:
|
||||
command_attempts = [
|
||||
{
|
||||
'source': 'iq',
|
||||
'direct_sampling_mode': None,
|
||||
},
|
||||
]
|
||||
else:
|
||||
command_attempts = [
|
||||
{
|
||||
'source': 'rtl_fm',
|
||||
'use_direct_sampling': False,
|
||||
@@ -388,10 +393,6 @@ def start_morse() -> Response:
|
||||
'add_resample_rate': True,
|
||||
'add_dc_fast': True,
|
||||
},
|
||||
{
|
||||
'source': 'iq',
|
||||
'direct_sampling_mode': None,
|
||||
},
|
||||
]
|
||||
|
||||
rtl_process: subprocess.Popen | None = None
|
||||
@@ -571,7 +572,7 @@ def start_morse() -> Response:
|
||||
)
|
||||
decoder_thread.start()
|
||||
|
||||
startup_deadline = time.monotonic() + 1.2
|
||||
startup_deadline = time.monotonic() + (2.5 if source == 'iq' else 1.2)
|
||||
startup_ok = False
|
||||
startup_error = ''
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ var MorseMode = (function () {
|
||||
var SETTINGS_KEY = 'intercept.morse.settings.v3';
|
||||
var STATUS_POLL_MS = 5000;
|
||||
var LOCAL_STOP_TIMEOUT_MS = 2200;
|
||||
var START_TIMEOUT_MS = 20000;
|
||||
var START_TIMEOUT_MS = 60000;
|
||||
|
||||
var state = {
|
||||
initialized: false,
|
||||
@@ -324,12 +324,32 @@ var MorseMode = (function () {
|
||||
if (seq !== state.startSeq) {
|
||||
return { status: 'stale' };
|
||||
}
|
||||
var initialErrorMsg = String(err && err.message ? err.message : err);
|
||||
if (initialErrorMsg === 'Request timed out while waiting for decoder startup') {
|
||||
return fetch('/morse/status')
|
||||
.then(function (r) { return parseJsonSafe(r); })
|
||||
.then(function (statusData) {
|
||||
var statusError = statusData && (statusData.error || statusData.message);
|
||||
var resolvedError = statusError ? String(statusError) : initialErrorMsg;
|
||||
setLifecycle('error');
|
||||
setStatusText('Start failed');
|
||||
appendDiagLine('[start] failed: ' + resolvedError);
|
||||
notifyError('Failed to start Morse decoder: ' + resolvedError);
|
||||
return { status: 'error', message: resolvedError };
|
||||
})
|
||||
.catch(function () {
|
||||
setLifecycle('error');
|
||||
setStatusText('Start failed');
|
||||
appendDiagLine('[start] failed: ' + initialErrorMsg);
|
||||
notifyError('Failed to start Morse decoder: ' + initialErrorMsg);
|
||||
return { status: 'error', message: initialErrorMsg };
|
||||
});
|
||||
}
|
||||
setLifecycle('error');
|
||||
var errorMsg = String(err && err.message ? err.message : err);
|
||||
setStatusText('Start failed');
|
||||
appendDiagLine('[start] failed: ' + errorMsg);
|
||||
notifyError('Failed to start Morse decoder: ' + errorMsg);
|
||||
return { status: 'error', message: errorMsg };
|
||||
appendDiagLine('[start] failed: ' + initialErrorMsg);
|
||||
notifyError('Failed to start Morse decoder: ' + initialErrorMsg);
|
||||
return { status: 'error', message: initialErrorMsg };
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +266,13 @@ class TestMorseLifecycleRoutes:
|
||||
def build_fm_demod_command(self, **kwargs):
|
||||
return ['rtl_fm', '-f', '14060000']
|
||||
|
||||
def build_iq_capture_command(self, **kwargs):
|
||||
cmd = ['rtl_sdr', '-f', '14060000', '-s', '250000']
|
||||
if kwargs.get('gain') is not None:
|
||||
cmd.extend(['-g', str(kwargs['gain'])])
|
||||
cmd.append('-')
|
||||
return cmd
|
||||
|
||||
monkeypatch.setattr(morse_routes.SDRFactory, 'create_default_device', staticmethod(lambda sdr_type, index: DummyDevice()))
|
||||
monkeypatch.setattr(morse_routes.SDRFactory, 'get_builder', staticmethod(lambda sdr_type: DummyBuilder()))
|
||||
monkeypatch.setattr(morse_routes.time, 'sleep', lambda _secs: None)
|
||||
@@ -337,6 +344,10 @@ class TestMorseLifecycleRoutes:
|
||||
cmd.append('-')
|
||||
return cmd
|
||||
|
||||
def build_iq_capture_command(self, **kwargs):
|
||||
cmd = ['rtl_sdr', '-f', '14.0593M', '-s', '250000', '-']
|
||||
return cmd
|
||||
|
||||
monkeypatch.setattr(morse_routes.SDRFactory, 'create_default_device', staticmethod(lambda sdr_type, index: DummyDevice()))
|
||||
monkeypatch.setattr(morse_routes.SDRFactory, 'get_builder', staticmethod(lambda sdr_type: DummyBuilder()))
|
||||
|
||||
@@ -378,13 +389,11 @@ class TestMorseLifecycleRoutes:
|
||||
assert start_resp.status_code == 200
|
||||
assert start_resp.get_json()['status'] == 'started'
|
||||
assert len(popen_cmds) >= 2
|
||||
assert '-E' in popen_cmds[0] and 'direct2' in popen_cmds[0]
|
||||
assert '-r' in popen_cmds[0]
|
||||
assert '-A' in popen_cmds[0]
|
||||
assert '-E' in popen_cmds[1] and 'direct2' not in popen_cmds[1]
|
||||
assert '-r' in popen_cmds[1]
|
||||
assert '-A' in popen_cmds[1]
|
||||
assert 'dc' in popen_cmds[1]
|
||||
assert popen_cmds[0][0] == 'rtl_sdr'
|
||||
assert '-D' in popen_cmds[0]
|
||||
assert '2' in popen_cmds[0]
|
||||
assert popen_cmds[1][0] == 'rtl_sdr'
|
||||
assert '-D' not in popen_cmds[1]
|
||||
|
||||
stop_resp = client.post('/morse/stop')
|
||||
assert stop_resp.status_code == 200
|
||||
|
||||
Reference in New Issue
Block a user