""" RTL-SDR command builder implementation. Uses native rtl_* tools (rtl_fm, rtl_433) and dump1090 for maximum compatibility with existing RTL-SDR installations. No SoapySDR dependency required. """ from __future__ import annotations from typing import Optional from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType class RTLSDRCommandBuilder(CommandBuilder): """RTL-SDR command builder using native rtl_* tools.""" CAPABILITIES = SDRCapabilities( sdr_type=SDRType.RTL_SDR, freq_min_mhz=24.0, freq_max_mhz=1766.0, gain_min=0.0, gain_max=49.6, sample_rates=[250000, 1024000, 1800000, 2048000, 2400000], supports_bias_t=True, supports_ppm=True, tx_capable=False ) def _get_device_arg(self, device: SDRDevice) -> str: """Get device argument for rtl_* tools. Returns rtl_tcp connection string for network devices, or device index for local devices. """ if device.is_network: return f"rtl_tcp:{device.rtl_tcp_host}:{device.rtl_tcp_port}" return str(device.index) def build_fm_demod_command( self, device: SDRDevice, frequency_mhz: float, sample_rate: int = 22050, gain: Optional[float] = None, ppm: Optional[int] = None, modulation: str = "fm", squelch: Optional[int] = None, bias_t: bool = False ) -> list[str]: """ Build rtl_fm command for FM demodulation. Used for pager decoding. Supports local devices and rtl_tcp connections. """ cmd = [ 'rtl_fm', '-d', self._get_device_arg(device), '-f', f'{frequency_mhz}M', '-M', modulation, '-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 squelch is not None and squelch > 0: cmd.extend(['-l', str(squelch)]) if bias_t: cmd.extend(['-T']) # Output to stdout for piping cmd.append('-') return cmd def build_adsb_command( self, device: SDRDevice, gain: Optional[float] = None, bias_t: bool = False ) -> list[str]: """ Build dump1090 command for ADS-B decoding. Uses dump1090 with network output for SBS data streaming. Note: dump1090 does not support rtl_tcp. For remote SDR, connect to a remote dump1090's SBS output (port 30003) instead. """ if device.is_network: raise ValueError( "dump1090 does not support rtl_tcp. " "For remote ADS-B, run dump1090 on the remote machine and " "connect to its SBS output (port 30003)." ) cmd = [ 'dump1090', '--net', '--device-index', str(device.index), '--quiet' ] if gain is not None: cmd.extend(['--gain', str(int(gain))]) if bias_t: cmd.extend(['--enable-bias-t']) return cmd def build_ism_command( self, device: SDRDevice, frequency_mhz: float = 433.92, gain: Optional[float] = None, ppm: Optional[int] = None, bias_t: bool = False ) -> list[str]: """ Build rtl_433 command for ISM band sensor decoding. Outputs JSON for easy parsing. Supports local devices and rtl_tcp connections. """ cmd = [ 'rtl_433', '-d', self._get_device_arg(device), '-f', f'{frequency_mhz}M', '-F', 'json' ] if gain is not None and gain > 0: cmd.extend(['-g', str(int(gain))]) if ppm is not None and ppm != 0: cmd.extend(['-p', str(ppm)]) if bias_t: cmd.extend(['-T']) return cmd def get_capabilities(self) -> SDRCapabilities: """Return RTL-SDR capabilities.""" return self.CAPABILITIES @classmethod def get_sdr_type(cls) -> SDRType: """Return SDR type.""" return SDRType.RTL_SDR