diff --git a/routes/morse.py b/routes/morse.py index dae772c..a394f01 100644 --- a/routes/morse.py +++ b/routes/morse.py @@ -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', diff --git a/static/js/modes/morse.js b/static/js/modes/morse.js index eed62ef..12f6e52 100644 --- a/static/js/modes/morse.js +++ b/static/js/modes/morse.js @@ -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) { diff --git a/templates/index.html b/templates/index.html index 919f0da..b86aa44 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3085,6 +3085,11 @@ + +