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")
def _start_audio_stream(frequency: float, modulation: str):
"""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()
def _start_audio_stream(
frequency: float,
modulation: str,
*,
device: int | None = None,
sdr_type: str | None = None,
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()
if not ffmpeg_path:
logger.error("ffmpeg not found")
return
# Determine SDR type and build appropriate command
sdr_type_str = scanner_config.get('sdr_type', 'rtlsdr')
try:
sdr_type = SDRType(sdr_type_str)
except ValueError:
sdr_type = SDRType.RTL_SDR
# Snapshot runtime tuning config so the spawned demod command cannot
# drift if shared scanner_config changes while startup is in-flight.
device_index = int(device if device is not None else scanner_config.get('device', 0))
gain_value = int(gain if gain is not None else scanner_config.get('gain', 40))
squelch_value = int(squelch if squelch is not None else scanner_config.get('squelch', 0))
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
if modulation == 'wfm':
@@ -707,41 +723,41 @@ def _start_audio_stream(frequency: float, modulation: str):
freq_hz = int(frequency * 1e6)
sdr_cmd = [
rtl_fm_path,
'-M', _rtl_fm_demod_mode(modulation),
'-f', str(freq_hz),
'-s', str(sample_rate),
'-r', str(resample_rate),
'-g', str(scanner_config['gain']),
'-d', str(scanner_config['device']),
'-l', str(scanner_config['squelch']),
]
if scanner_config.get('bias_t', False):
sdr_cmd.append('-T')
# Omit explicit filename: rtl_fm defaults to stdout.
# (Some builds intermittently stall when '-' is passed explicitly.)
else:
# Use SDR abstraction layer for HackRF, Airspy, LimeSDR, SDRPlay
'-M', _rtl_fm_demod_mode(modulation),
'-f', str(freq_hz),
'-s', str(sample_rate),
'-r', str(resample_rate),
'-g', str(gain_value),
'-d', str(device_index),
'-l', str(squelch_value),
]
if bias_t_enabled:
sdr_cmd.append('-T')
# Omit explicit filename: rtl_fm defaults to stdout.
# (Some builds intermittently stall when '-' is passed explicitly.)
else:
# Use SDR abstraction layer for HackRF, Airspy, LimeSDR, SDRPlay
rx_fm_path = find_rx_fm()
if not rx_fm_path:
logger.error(f"rx_fm not found - required for {sdr_type.value}. Install SoapySDR utilities.")
return
# Create device and get command builder
device = SDRFactory.create_default_device(sdr_type, index=scanner_config['device'])
builder = SDRFactory.get_builder(sdr_type)
# Build FM demod command
sdr_cmd = builder.build_fm_demod_command(
device=device,
frequency_mhz=frequency,
sample_rate=resample_rate,
gain=float(scanner_config['gain']),
modulation=modulation,
squelch=scanner_config['squelch'],
bias_t=scanner_config.get('bias_t', False)
)
# Ensure we use the found rx_fm path
sdr_cmd[0] = rx_fm_path
logger.error(f"rx_fm not found - required for {sdr_type.value}. Install SoapySDR utilities.")
return
# Create device and get command builder
sdr_device = SDRFactory.create_default_device(sdr_type, index=device_index)
builder = SDRFactory.get_builder(sdr_type)
# Build FM demod command
sdr_cmd = builder.build_fm_demod_command(
device=sdr_device,
frequency_mhz=frequency,
sample_rate=resample_rate,
gain=float(gain_value),
modulation=modulation,
squelch=squelch_value,
bias_t=bias_t_enabled,
)
# Ensure we use the found rx_fm path
sdr_cmd[0] = rx_fm_path
encoder_cmd = [
ffmpeg_path,
@@ -762,11 +778,11 @@ def _start_audio_stream(frequency: float, modulation: str):
]
try:
# Use subprocess piping 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'
logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={scanner_config['device']}")
# Use subprocess piping 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'
logger.info(f"Starting audio: {frequency} MHz, mod={modulation}, device={device_index}")
# Retry loop for USB device contention (device may not be
# released immediately after a previous process exits)
@@ -1432,7 +1448,15 @@ def start_audio() -> Response:
}), 409
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:
audio_source = 'process'