mirror of
https://github.com/smittix/intercept.git
synced 2026-06-12 16:03:29 -07:00
Forward rtl_fm stderr to Morse frontend diagnostic log
rtl_fm prints device info, tuning, and errors to stderr but the morse route only logged these server-side. Now stderr lines are forwarded to the morse queue as info events, displayed in a compact diagnostic log below the scope canvas. After 10s with no audio data, the scope text escalates to prompt the user to check the SDR log. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+11
-1
@@ -158,12 +158,17 @@ def start_morse() -> Response:
|
||||
morse_active_device = None
|
||||
return jsonify({'status': 'error', 'message': msg}), 500
|
||||
|
||||
# Monitor rtl_fm stderr
|
||||
# Forward rtl_fm stderr to queue so frontend can display diagnostics
|
||||
def monitor_stderr():
|
||||
for line in rtl_process.stderr:
|
||||
err_text = line.decode('utf-8', errors='replace').strip()
|
||||
if err_text:
|
||||
logger.debug(f"[rtl_fm/morse] {err_text}")
|
||||
with contextlib.suppress(queue.Full):
|
||||
app_module.morse_queue.put_nowait({
|
||||
'type': 'info',
|
||||
'text': f'[rtl_fm] {err_text}',
|
||||
})
|
||||
|
||||
stderr_thread = threading.Thread(target=monitor_stderr)
|
||||
stderr_thread.daemon = True
|
||||
@@ -190,6 +195,11 @@ def start_morse() -> Response:
|
||||
app_module.morse_process._decoder_thread = decoder_thread
|
||||
|
||||
app_module.morse_queue.put({'type': 'status', 'status': 'started'})
|
||||
with contextlib.suppress(queue.Full):
|
||||
app_module.morse_queue.put_nowait({
|
||||
'type': 'info',
|
||||
'text': f'[cmd] {full_cmd}',
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'status': 'started',
|
||||
|
||||
@@ -23,6 +23,7 @@ var MorseMode = (function () {
|
||||
var scopeThreshold = 0;
|
||||
var scopeToneOn = false;
|
||||
var scopeWaiting = false;
|
||||
var waitingStart = 0; // timestamp when waiting began
|
||||
|
||||
// ---- Initialization ----
|
||||
|
||||
@@ -152,9 +153,13 @@ var MorseMode = (function () {
|
||||
// Update scope data
|
||||
var amps = msg.amplitudes || [];
|
||||
if (msg.waiting && amps.length === 0 && scopeHistory.length === 0) {
|
||||
scopeWaiting = true;
|
||||
if (!scopeWaiting) {
|
||||
scopeWaiting = true;
|
||||
waitingStart = Date.now();
|
||||
}
|
||||
} else if (amps.length > 0) {
|
||||
scopeWaiting = false;
|
||||
waitingStart = 0;
|
||||
}
|
||||
for (var i = 0; i < amps.length; i++) {
|
||||
scopeHistory.push(amps[i]);
|
||||
@@ -178,6 +183,9 @@ var MorseMode = (function () {
|
||||
disconnectSSE();
|
||||
stopScope();
|
||||
}
|
||||
} else if (type === 'info') {
|
||||
appendDiagLine(msg.text);
|
||||
|
||||
} else if (type === 'error') {
|
||||
console.error('Morse error:', msg.text);
|
||||
}
|
||||
@@ -263,10 +271,14 @@ var MorseMode = (function () {
|
||||
|
||||
if (scopeHistory.length === 0) {
|
||||
if (scopeWaiting) {
|
||||
scopeCtx.fillStyle = '#556677';
|
||||
var elapsed = waitingStart ? (Date.now() - waitingStart) / 1000 : 0;
|
||||
var waitText = elapsed > 10
|
||||
? 'No audio data \u2014 check SDR log below'
|
||||
: 'Awaiting SDR data\u2026';
|
||||
scopeCtx.fillStyle = elapsed > 10 ? '#887744' : '#556677';
|
||||
scopeCtx.font = '12px monospace';
|
||||
scopeCtx.textAlign = 'center';
|
||||
scopeCtx.fillText('Awaiting SDR data\u2026', w / 2, h / 2);
|
||||
scopeCtx.fillText(waitText, w / 2, h / 2);
|
||||
scopeCtx.textAlign = 'start';
|
||||
}
|
||||
scopeAnim = requestAnimationFrame(draw);
|
||||
@@ -371,6 +383,30 @@ var MorseMode = (function () {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// ---- Diagnostic log ----
|
||||
|
||||
function appendDiagLine(text) {
|
||||
var log = document.getElementById('morseDiagLog');
|
||||
if (!log) return;
|
||||
log.style.display = 'block';
|
||||
var line = document.createElement('div');
|
||||
line.textContent = text;
|
||||
log.appendChild(line);
|
||||
// Limit to 20 entries
|
||||
while (log.children.length > 20) {
|
||||
log.removeChild(log.firstChild);
|
||||
}
|
||||
log.scrollTop = log.scrollHeight;
|
||||
}
|
||||
|
||||
function clearDiagLog() {
|
||||
var log = document.getElementById('morseDiagLog');
|
||||
if (log) {
|
||||
log.innerHTML = '';
|
||||
log.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// ---- UI ----
|
||||
|
||||
function updateUI(running) {
|
||||
@@ -398,6 +434,14 @@ var MorseMode = (function () {
|
||||
var scopeStatus = document.getElementById('morseScopeStatusLabel');
|
||||
if (scopeStatus) scopeStatus.textContent = running ? 'ACTIVE' : 'IDLE';
|
||||
if (scopeStatus) scopeStatus.style.color = running ? '#0f0' : '#444';
|
||||
|
||||
// Diagnostic log: clear on start, hide on stop
|
||||
if (running) {
|
||||
clearDiagLog();
|
||||
} else {
|
||||
var diagLog = document.getElementById('morseDiagLog');
|
||||
if (diagLog) diagLog.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function setFreq(mhz) {
|
||||
|
||||
@@ -3085,6 +3085,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="morseDiagLog" style="display: none; margin-bottom: 8px; max-height: 60px; overflow-y: auto;
|
||||
background: #080812; border: 1px solid #1a1a2e; border-radius: 4px; padding: 4px 8px;
|
||||
font-family: var(--font-mono); font-size: 10px; color: #556677; line-height: 1.6;">
|
||||
</div>
|
||||
|
||||
<!-- Morse Decoded Output -->
|
||||
<div id="morseOutputPanel" style="display: none; margin-bottom: 12px;">
|
||||
<div style="background: #0a0a0a; border: 1px solid #1a2e1a; border-radius: 6px; padding: 8px 10px;">
|
||||
@@ -4172,6 +4177,8 @@
|
||||
const morseOutputPanel = document.getElementById('morseOutputPanel');
|
||||
if (morseScopePanel && mode !== 'morse') morseScopePanel.style.display = 'none';
|
||||
if (morseOutputPanel && mode !== 'morse') morseOutputPanel.style.display = 'none';
|
||||
const morseDiagLog = document.getElementById('morseDiagLog');
|
||||
if (morseDiagLog && mode !== 'morse') morseDiagLog.style.display = 'none';
|
||||
|
||||
// Update output panel title based on mode
|
||||
const outputTitle = document.getElementById('outputTitle');
|
||||
|
||||
@@ -278,6 +278,7 @@ def morse_decoder_thread(
|
||||
CHUNK = 4096 # bytes per read (2048 samples at 16-bit mono)
|
||||
SCOPE_INTERVAL = 0.1 # scope updates at ~10 Hz
|
||||
last_scope = time.monotonic()
|
||||
waiting_since: float | None = None
|
||||
|
||||
decoder = MorseDecoder(
|
||||
sample_rate=sample_rate,
|
||||
@@ -293,6 +294,8 @@ def morse_decoder_thread(
|
||||
if not ready:
|
||||
# No data from SDR — emit diagnostic heartbeat
|
||||
now = time.monotonic()
|
||||
if waiting_since is None:
|
||||
waiting_since = now
|
||||
if now - last_scope >= SCOPE_INTERVAL:
|
||||
last_scope = now
|
||||
with contextlib.suppress(queue.Full):
|
||||
@@ -302,12 +305,14 @@ def morse_decoder_thread(
|
||||
'threshold': 0,
|
||||
'tone_on': False,
|
||||
'waiting': True,
|
||||
'waiting_seconds': round(now - waiting_since, 1),
|
||||
})
|
||||
continue
|
||||
|
||||
data = os.read(fd, CHUNK)
|
||||
if not data:
|
||||
break
|
||||
waiting_since = None
|
||||
|
||||
events = decoder.process_block(data)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user