Snapshot audio tune config when spawning demod process

This commit is contained in:
Smittix
2026-02-24 08:55:32 +00:00
parent a350c82893
commit 9cd7f1c0c8
+76 -52
View File
@@ -665,25 +665,41 @@ def scanner_loop_power():
logger.info("Power sweep scanner thread stopped") logger.info("Power sweep scanner thread stopped")
def _start_audio_stream(frequency: float, modulation: str): def _start_audio_stream(
"""Start audio streaming at given frequency.""" frequency: float,
global audio_process, audio_rtl_process, audio_running, audio_frequency, audio_modulation modulation: str,
*,
with audio_lock: device: int | None = None,
# Stop any existing stream sdr_type: str | None = None,
_stop_audio_stream_internal() gain: int | None = None,
squelch: int | None = None,
bias_t: bool | None = None,
):
"""Start audio streaming at given frequency."""
global audio_process, audio_rtl_process, audio_running, audio_frequency, audio_modulation
with audio_lock:
# Stop any existing stream
_stop_audio_stream_internal()
ffmpeg_path = find_ffmpeg() ffmpeg_path = find_ffmpeg()
if not ffmpeg_path: if not ffmpeg_path:
logger.error("ffmpeg not found") logger.error("ffmpeg not found")
return return
# Determine SDR type and build appropriate command # Snapshot runtime tuning config so the spawned demod command cannot
sdr_type_str = scanner_config.get('sdr_type', 'rtlsdr') # drift if shared scanner_config changes while startup is in-flight.
try: device_index = int(device if device is not None else scanner_config.get('device', 0))
sdr_type = SDRType(sdr_type_str) gain_value = int(gain if gain is not None else scanner_config.get('gain', 40))
except ValueError: squelch_value = int(squelch if squelch is not None else scanner_config.get('squelch', 0))
sdr_type = SDRType.RTL_SDR bias_t_enabled = bool(scanner_config.get('bias_t', False) if bias_t is None else bias_t)
sdr_type_str = str(sdr_type if sdr_type is not None else scanner_config.get('sdr_type', 'rtlsdr')).lower()
# Determine SDR type and build appropriate command
try:
sdr_type = SDRType(sdr_type_str)
except ValueError:
sdr_type = SDRType.RTL_SDR
# Set sample rates based on modulation # Set sample rates based on modulation
if modulation == 'wfm': if modulation == 'wfm':
@@ -707,41 +723,41 @@ def _start_audio_stream(frequency: float, modulation: str):
freq_hz = int(frequency * 1e6) freq_hz = int(frequency * 1e6)
sdr_cmd = [ sdr_cmd = [
rtl_fm_path, rtl_fm_path,
'-M', _rtl_fm_demod_mode(modulation), '-M', _rtl_fm_demod_mode(modulation),
'-f', str(freq_hz), '-f', str(freq_hz),
'-s', str(sample_rate), '-s', str(sample_rate),
'-r', str(resample_rate), '-r', str(resample_rate),
'-g', str(scanner_config['gain']), '-g', str(gain_value),
'-d', str(scanner_config['device']), '-d', str(device_index),
'-l', str(scanner_config['squelch']), '-l', str(squelch_value),
] ]
if scanner_config.get('bias_t', False): if bias_t_enabled:
sdr_cmd.append('-T') sdr_cmd.append('-T')
# Omit explicit filename: rtl_fm defaults to stdout. # Omit explicit filename: rtl_fm defaults to stdout.
# (Some builds intermittently stall when '-' is passed explicitly.) # (Some builds intermittently stall when '-' is passed explicitly.)
else: else:
# Use SDR abstraction layer for HackRF, Airspy, LimeSDR, SDRPlay # Use SDR abstraction layer for HackRF, Airspy, LimeSDR, SDRPlay
rx_fm_path = find_rx_fm() rx_fm_path = find_rx_fm()
if not rx_fm_path: if not rx_fm_path:
logger.error(f"rx_fm not found - required for {sdr_type.value}. Install SoapySDR utilities.") logger.error(f"rx_fm not found - required for {sdr_type.value}. Install SoapySDR utilities.")
return return
# Create device and get command builder # Create device and get command builder
device = SDRFactory.create_default_device(sdr_type, index=scanner_config['device']) sdr_device = SDRFactory.create_default_device(sdr_type, index=device_index)
builder = SDRFactory.get_builder(sdr_type) builder = SDRFactory.get_builder(sdr_type)
# Build FM demod command # Build FM demod command
sdr_cmd = builder.build_fm_demod_command( sdr_cmd = builder.build_fm_demod_command(
device=device, device=sdr_device,
frequency_mhz=frequency, frequency_mhz=frequency,
sample_rate=resample_rate, sample_rate=resample_rate,
gain=float(scanner_config['gain']), gain=float(gain_value),
modulation=modulation, modulation=modulation,
squelch=scanner_config['squelch'], squelch=squelch_value,
bias_t=scanner_config.get('bias_t', False) bias_t=bias_t_enabled,
) )
# Ensure we use the found rx_fm path # Ensure we use the found rx_fm path
sdr_cmd[0] = rx_fm_path sdr_cmd[0] = rx_fm_path
encoder_cmd = [ encoder_cmd = [
ffmpeg_path, ffmpeg_path,
@@ -762,11 +778,11 @@ def _start_audio_stream(frequency: float, modulation: str):
] ]
try: try:
# Use subprocess piping for reliable streaming. # Use subprocess piping for reliable streaming.
# Log stderr to temp files for error diagnosis. # Log stderr to temp files for error diagnosis.
rtl_stderr_log = '/tmp/rtl_fm_stderr.log' rtl_stderr_log = '/tmp/rtl_fm_stderr.log'
ffmpeg_stderr_log = '/tmp/ffmpeg_stderr.log' ffmpeg_stderr_log = '/tmp/ffmpeg_stderr.log'
logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={scanner_config['device']}") logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={device_index}")
# Retry loop for USB device contention (device may not be # Retry loop for USB device contention (device may not be
# released immediately after a previous process exits) # released immediately after a previous process exits)
@@ -1432,7 +1448,15 @@ def start_audio() -> Response:
}), 409 }), 409
receiver_active_device = device receiver_active_device = device
_start_audio_stream(frequency, modulation) _start_audio_stream(
frequency,
modulation,
device=device,
sdr_type=sdr_type,
gain=gain,
squelch=squelch,
bias_t=bias_t,
)
if audio_running: if audio_running:
audio_source = 'process' audio_source = 'process'