mirror of
https://github.com/smittix/intercept.git
synced 2026-06-20 03:14:21 -07:00
Switch Morse startup to IQ-first and harden timeout handling
This commit is contained in:
+14
-13
@@ -354,8 +354,17 @@ def start_morse() -> Response:
|
|||||||
|
|
||||||
can_try_direct_sampling = bool(sdr_device.sdr_type == SDRType.RTL_SDR and freq < 24.0)
|
can_try_direct_sampling = bool(sdr_device.sdr_type == SDRType.RTL_SDR and freq < 24.0)
|
||||||
if can_try_direct_sampling:
|
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]] = [
|
command_attempts: list[dict[str, Any]] = [
|
||||||
|
{
|
||||||
|
'source': 'iq',
|
||||||
|
'direct_sampling_mode': 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'source': 'iq',
|
||||||
|
'direct_sampling_mode': None,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'source': 'rtl_fm',
|
'source': 'rtl_fm',
|
||||||
'use_direct_sampling': True,
|
'use_direct_sampling': True,
|
||||||
@@ -370,17 +379,13 @@ def start_morse() -> Response:
|
|||||||
'add_resample_rate': True,
|
'add_resample_rate': True,
|
||||||
'add_dc_fast': True,
|
'add_dc_fast': True,
|
||||||
},
|
},
|
||||||
{
|
]
|
||||||
'source': 'iq',
|
else:
|
||||||
'direct_sampling_mode': 2,
|
command_attempts = [
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'source': 'iq',
|
'source': 'iq',
|
||||||
'direct_sampling_mode': None,
|
'direct_sampling_mode': None,
|
||||||
},
|
},
|
||||||
]
|
|
||||||
else:
|
|
||||||
command_attempts = [
|
|
||||||
{
|
{
|
||||||
'source': 'rtl_fm',
|
'source': 'rtl_fm',
|
||||||
'use_direct_sampling': False,
|
'use_direct_sampling': False,
|
||||||
@@ -388,10 +393,6 @@ def start_morse() -> Response:
|
|||||||
'add_resample_rate': True,
|
'add_resample_rate': True,
|
||||||
'add_dc_fast': True,
|
'add_dc_fast': True,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'source': 'iq',
|
|
||||||
'direct_sampling_mode': None,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
rtl_process: subprocess.Popen | None = None
|
rtl_process: subprocess.Popen | None = None
|
||||||
@@ -571,7 +572,7 @@ def start_morse() -> Response:
|
|||||||
)
|
)
|
||||||
decoder_thread.start()
|
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_ok = False
|
||||||
startup_error = ''
|
startup_error = ''
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ var MorseMode = (function () {
|
|||||||
var SETTINGS_KEY = 'intercept.morse.settings.v3';
|
var SETTINGS_KEY = 'intercept.morse.settings.v3';
|
||||||
var STATUS_POLL_MS = 5000;
|
var STATUS_POLL_MS = 5000;
|
||||||
var LOCAL_STOP_TIMEOUT_MS = 2200;
|
var LOCAL_STOP_TIMEOUT_MS = 2200;
|
||||||
var START_TIMEOUT_MS = 20000;
|
var START_TIMEOUT_MS = 60000;
|
||||||
|
|
||||||
var state = {
|
var state = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
@@ -324,12 +324,32 @@ var MorseMode = (function () {
|
|||||||
if (seq !== state.startSeq) {
|
if (seq !== state.startSeq) {
|
||||||
return { status: 'stale' };
|
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');
|
setLifecycle('error');
|
||||||
var errorMsg = String(err && err.message ? err.message : err);
|
|
||||||
setStatusText('Start failed');
|
setStatusText('Start failed');
|
||||||
appendDiagLine('[start] failed: ' + errorMsg);
|
appendDiagLine('[start] failed: ' + initialErrorMsg);
|
||||||
notifyError('Failed to start Morse decoder: ' + errorMsg);
|
notifyError('Failed to start Morse decoder: ' + initialErrorMsg);
|
||||||
return { status: 'error', message: errorMsg };
|
return { status: 'error', message: initialErrorMsg };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-7
@@ -266,6 +266,13 @@ class TestMorseLifecycleRoutes:
|
|||||||
def build_fm_demod_command(self, **kwargs):
|
def build_fm_demod_command(self, **kwargs):
|
||||||
return ['rtl_fm', '-f', '14060000']
|
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, '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.SDRFactory, 'get_builder', staticmethod(lambda sdr_type: DummyBuilder()))
|
||||||
monkeypatch.setattr(morse_routes.time, 'sleep', lambda _secs: None)
|
monkeypatch.setattr(morse_routes.time, 'sleep', lambda _secs: None)
|
||||||
@@ -337,6 +344,10 @@ class TestMorseLifecycleRoutes:
|
|||||||
cmd.append('-')
|
cmd.append('-')
|
||||||
return cmd
|
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, '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.SDRFactory, 'get_builder', staticmethod(lambda sdr_type: DummyBuilder()))
|
||||||
|
|
||||||
@@ -378,13 +389,11 @@ class TestMorseLifecycleRoutes:
|
|||||||
assert start_resp.status_code == 200
|
assert start_resp.status_code == 200
|
||||||
assert start_resp.get_json()['status'] == 'started'
|
assert start_resp.get_json()['status'] == 'started'
|
||||||
assert len(popen_cmds) >= 2
|
assert len(popen_cmds) >= 2
|
||||||
assert '-E' in popen_cmds[0] and 'direct2' in popen_cmds[0]
|
assert popen_cmds[0][0] == 'rtl_sdr'
|
||||||
assert '-r' in popen_cmds[0]
|
assert '-D' in popen_cmds[0]
|
||||||
assert '-A' in popen_cmds[0]
|
assert '2' in popen_cmds[0]
|
||||||
assert '-E' in popen_cmds[1] and 'direct2' not in popen_cmds[1]
|
assert popen_cmds[1][0] == 'rtl_sdr'
|
||||||
assert '-r' in popen_cmds[1]
|
assert '-D' not in popen_cmds[1]
|
||||||
assert '-A' in popen_cmds[1]
|
|
||||||
assert 'dc' in popen_cmds[1]
|
|
||||||
|
|
||||||
stop_resp = client.post('/morse/stop')
|
stop_resp = client.post('/morse/stop')
|
||||||
assert stop_resp.status_code == 200
|
assert stop_resp.status_code == 200
|
||||||
|
|||||||
Reference in New Issue
Block a user