Add real-time squelch control and clean up diagnostic logging

- Add updateAirbandSquelch() to restart audio when squelch slider changes
- Remove verbose diagnostic logging from audio streaming
- Remove tee diagnostic for raw rtl_fm output
- Keep error logging for troubleshooting
- Simplify audio stream generator

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-15 17:17:15 +00:00
parent 97d5ec6b33
commit aae60e2037
3 changed files with 14 additions and 76 deletions
Binary file not shown.
+5 -75
View File
@@ -440,14 +440,12 @@ def _start_audio_stream(frequency: float, modulation: str):
]
try:
# Use shell pipe for reliable streaming (Python subprocess piping can be unreliable)
# Log stderr to temp files so we can see any errors from rtl_fm and ffmpeg
# Use shell pipe for reliable streaming
# Log stderr to temp files for error diagnosis
rtl_stderr_log = '/tmp/rtl_fm_stderr.log'
ffmpeg_stderr_log = '/tmp/ffmpeg_stderr.log'
rtl_raw_output = '/tmp/rtl_fm_raw.bin'
# Use tee to capture rtl_fm raw output for diagnostics while also piping to ffmpeg
shell_cmd = f"{' '.join(sdr_cmd)} 2>{rtl_stderr_log} | tee {rtl_raw_output} | {' '.join(encoder_cmd)} 2>{ffmpeg_stderr_log}"
logger.info(f"Starting audio pipeline: {shell_cmd}")
shell_cmd = f"{' '.join(sdr_cmd)} 2>{rtl_stderr_log} | {' '.join(encoder_cmd)} 2>{ffmpeg_stderr_log}"
logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={scanner_config['device']}")
audio_rtl_process = None # Not used in shell mode
audio_process = subprocess.Popen(
@@ -479,22 +477,6 @@ def _start_audio_stream(frequency: float, modulation: str):
logger.error(f"Audio pipeline exited immediately. rtl_fm stderr: {rtl_stderr}, ffmpeg stderr: {ffmpeg_stderr}")
return
# Also check for errors even if process is still running
try:
with open(rtl_stderr_log, 'r') as f:
rtl_stderr = f.read().strip()
if rtl_stderr:
logger.warning(f"rtl_fm stderr: {rtl_stderr}")
except:
pass
try:
with open(ffmpeg_stderr_log, 'r') as f:
ffmpeg_stderr = f.read().strip()
if ffmpeg_stderr:
logger.warning(f"ffmpeg stderr: {ffmpeg_stderr}")
except:
pass
audio_running = True
audio_frequency = frequency
audio_modulation = modulation
@@ -810,8 +792,6 @@ def start_audio() -> Response:
"""Start audio at specific frequency (manual mode)."""
global scanner_running
logger.info(f"Audio start request received: {request.json}")
# Stop scanner if running
if scanner_running:
scanner_running = False
@@ -858,8 +838,6 @@ def start_audio() -> Response:
scanner_config['device'] = device
scanner_config['sdr_type'] = sdr_type
logger.info(f"Starting audio: freq={frequency} MHz, mod={modulation}, device={device}, gain={gain}, squelch={squelch}")
_start_audio_stream(frequency, modulation)
if audio_running:
@@ -895,8 +873,6 @@ def audio_status() -> Response:
@listening_post_bp.route('/audio/stream')
def stream_audio() -> Response:
"""Stream MP3 audio."""
logger.info(f"Audio stream requested - running: {audio_running}, process: {audio_process is not None}")
# Wait for audio to be ready (up to 2 seconds for modulation/squelch changes)
for _ in range(40):
if audio_running and audio_process:
@@ -904,71 +880,25 @@ def stream_audio() -> Response:
time.sleep(0.05)
if not audio_running or not audio_process:
logger.warning("Audio stream requested but no audio running")
return Response(b'', mimetype='audio/mpeg', status=204)
# Check if process is still alive
poll_result = audio_process.poll()
logger.info(f"Audio process poll result: {poll_result} (None means running)")
# Check for rtl_fm and ffmpeg errors
try:
with open('/tmp/rtl_fm_stderr.log', 'r') as f:
rtl_stderr = f.read().strip()
if rtl_stderr:
logger.warning(f"rtl_fm stderr (at stream request): {rtl_stderr}")
except:
pass
try:
with open('/tmp/ffmpeg_stderr.log', 'r') as f:
ffmpeg_stderr = f.read().strip()
if ffmpeg_stderr:
logger.warning(f"ffmpeg stderr (at stream request): {ffmpeg_stderr}")
except:
pass
def generate():
bytes_sent = 0
iterations = 0
logger.info("Audio stream generator started")
try:
while audio_running and audio_process and audio_process.poll() is None:
iterations += 1
# Use select to avoid blocking forever
ready, _, _ = select.select([audio_process.stdout], [], [], 2.0)
if ready:
chunk = audio_process.stdout.read(4096)
if chunk:
bytes_sent += len(chunk)
if bytes_sent < 50000 or bytes_sent % 100000 < 4096:
logger.debug(f"Audio chunk: {len(chunk)} bytes, total: {bytes_sent}")
yield chunk
else:
logger.warning(f"Audio stream: empty chunk after {bytes_sent} bytes, iterations={iterations}")
break
else:
# Check raw rtl_fm output file size
raw_size = 0
try:
raw_size = os.path.getsize('/tmp/rtl_fm_raw.bin')
except:
pass
logger.warning(f"Audio stream: select timeout after {bytes_sent} bytes, iterations={iterations}, poll={audio_process.poll()}, rtl_raw_size={raw_size}")
# Check for errors on timeout
try:
with open('/tmp/ffmpeg_stderr.log', 'r') as f:
ffmpeg_err = f.read().strip()
if ffmpeg_err:
logger.warning(f"ffmpeg stderr (timeout): {ffmpeg_err}")
except:
pass
# Timeout - check if process died
if audio_process.poll() is not None:
logger.warning(f"Audio process died during streaming after {bytes_sent} bytes")
break
logger.info(f"Audio stream ended - sent {bytes_sent} bytes, iterations={iterations}, running={audio_running}, poll={audio_process.poll() if audio_process else 'N/A'}")
except GeneratorExit:
logger.info(f"Audio stream: client disconnected after {bytes_sent} bytes")
pass
except Exception as e:
logger.error(f"Audio stream error: {e}")
+9 -1
View File
@@ -257,7 +257,7 @@
</select>
<div class="airband-sliders">
<span title="Squelch">SQ</span>
<input type="range" id="airbandSquelch" min="0" max="100" value="20" class="airband-controls" title="Squelch">
<input type="range" id="airbandSquelch" min="0" max="100" value="20" class="airband-controls" title="Squelch" onchange="updateAirbandSquelch()">
<span title="Volume">VOL</span>
<input type="range" id="airbandVolume" min="0" max="100" value="80" class="airband-controls" oninput="updateAirbandVolume()" title="Volume">
</div>
@@ -3221,6 +3221,14 @@ sudo make install</code>
}
}
function updateAirbandSquelch() {
// If airband is playing, restart with new squelch value
if (isAirbandPlaying) {
stopAirband();
setTimeout(() => startAirband(), 300);
}
}
// Initialize airband on page load
document.addEventListener('DOMContentLoaded', initAirband);