Add live waterfall during pager and sensor decoding via IQ pipeline

Replace rtl_fm/rtl_433 with rtl_sdr for raw IQ capture when available,
enabling a Python IQ processor to compute FFT for the waterfall while
simultaneously feeding decoded data to multimon-ng (pager) or rtl_433
(sensor). Falls back to the legacy pipeline when rtl_sdr is unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-02-07 23:18:43 +00:00
parent b312eb20aa
commit f04ba7f143
9 changed files with 1053 additions and 434 deletions

View File

@@ -186,6 +186,36 @@ class CommandBuilder(ABC):
"""Return hardware capabilities for this SDR type."""
pass
def build_raw_capture_command(
self,
device: SDRDevice,
frequency_mhz: float,
sample_rate: int,
gain: Optional[float] = None,
ppm: Optional[int] = None,
bias_t: bool = False
) -> list[str]:
"""
Build raw IQ capture command (for IQ-based waterfall during decoding).
Args:
device: The SDR device to use
frequency_mhz: Center frequency in MHz
sample_rate: Sample rate in Hz
gain: Gain in dB (None for auto)
ppm: PPM frequency correction
bias_t: Enable bias-T power (for active antennas)
Returns:
Command as list of strings for subprocess
Raises:
NotImplementedError: If the SDR type does not support raw capture
"""
raise NotImplementedError(
f"Raw IQ capture not supported for {self.get_sdr_type().value}"
)
@classmethod
@abstractmethod
def get_sdr_type(cls) -> SDRType:

View File

@@ -197,6 +197,43 @@ class RTLSDRCommandBuilder(CommandBuilder):
return cmd
def build_raw_capture_command(
self,
device: SDRDevice,
frequency_mhz: float,
sample_rate: int,
gain: Optional[float] = None,
ppm: Optional[int] = None,
bias_t: bool = False
) -> list[str]:
"""
Build rtl_sdr command for raw IQ capture.
Outputs raw uint8 IQ to stdout for processing by IQ pipelines.
"""
rtl_sdr_path = get_tool_path('rtl_sdr') or 'rtl_sdr'
freq_hz = int(frequency_mhz * 1e6)
cmd = [
rtl_sdr_path,
'-d', self._get_device_arg(device),
'-f', str(freq_hz),
'-s', str(sample_rate),
]
if gain is not None and gain > 0:
cmd.extend(['-g', str(gain)])
if ppm is not None and ppm != 0:
cmd.extend(['-p', str(ppm)])
if bias_t:
cmd.extend(['-T'])
# Output to stdout
cmd.append('-')
return cmd
def build_ais_command(
self,
device: SDRDevice,