Files
intercept/utils/sdr/__init__.py
T
James Smith 96172ca593 style: apply ruff-format to entire codebase
First-time run of ruff-format via pre-commit hook normalises quote
style, trailing commas, and whitespace across 188 Python files.
No logic changes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-05 14:48:11 +01:00

227 lines
6.6 KiB
Python

"""
SDR Hardware Abstraction Layer.
This module provides a unified interface for multiple SDR hardware types
including RTL-SDR, LimeSDR, and HackRF. Use SDRFactory to detect devices
and get appropriate command builders.
Example usage:
from utils.sdr import SDRFactory, SDRType
# Detect all connected devices
devices = SDRFactory.detect_devices()
# Get a command builder for a specific device
builder = SDRFactory.get_builder_for_device(devices[0])
# Or get a builder by type
builder = SDRFactory.get_builder(SDRType.RTL_SDR)
# Build commands
cmd = builder.build_fm_demod_command(device, frequency_mhz=153.35)
"""
from __future__ import annotations
from typing import Optional
from .airspy import AirspyCommandBuilder
from .base import CommandBuilder, SDRCapabilities, SDRDevice, SDRType
from .detection import detect_all_devices, invalidate_device_cache, probe_rtlsdr_device
from .hackrf import HackRFCommandBuilder
from .limesdr import LimeSDRCommandBuilder
from .rtlsdr import RTLSDRCommandBuilder
from .sdrplay import SDRPlayCommandBuilder
from .validation import (
SDRValidationError,
get_capabilities_for_type,
validate_device_index,
validate_frequency,
validate_gain,
validate_ppm,
validate_sample_rate,
validate_squelch,
)
class SDRFactory:
"""Factory for creating SDR command builders and detecting devices."""
_builders: dict[SDRType, type[CommandBuilder]] = {
SDRType.RTL_SDR: RTLSDRCommandBuilder,
SDRType.LIME_SDR: LimeSDRCommandBuilder,
SDRType.HACKRF: HackRFCommandBuilder,
SDRType.AIRSPY: AirspyCommandBuilder,
SDRType.SDRPLAY: SDRPlayCommandBuilder,
}
@classmethod
def get_builder(cls, sdr_type: SDRType) -> CommandBuilder:
"""
Get a command builder for the specified SDR type.
Args:
sdr_type: The SDR hardware type
Returns:
CommandBuilder instance for the specified type
Raises:
ValueError: If the SDR type is not supported
"""
builder_class = cls._builders.get(sdr_type)
if not builder_class:
raise ValueError(f"Unsupported SDR type: {sdr_type}")
return builder_class()
@classmethod
def get_builder_for_device(cls, device: SDRDevice) -> CommandBuilder:
"""
Get a command builder for a specific device.
Args:
device: The SDR device
Returns:
CommandBuilder instance for the device's type
"""
return cls.get_builder(device.sdr_type)
@classmethod
def detect_devices(cls) -> list[SDRDevice]:
"""
Detect all available SDR devices.
Returns:
List of detected SDR devices
"""
return detect_all_devices()
@classmethod
def get_supported_types(cls) -> list[SDRType]:
"""
Get list of supported SDR types.
Returns:
List of supported SDRType values
"""
return list(cls._builders.keys())
@classmethod
def get_capabilities(cls, sdr_type: SDRType) -> SDRCapabilities:
"""
Get capabilities for an SDR type.
Args:
sdr_type: The SDR hardware type
Returns:
SDRCapabilities for the specified type
"""
builder = cls.get_builder(sdr_type)
return builder.get_capabilities()
@classmethod
def get_all_capabilities(cls) -> dict[str, dict]:
"""
Get capabilities for all supported SDR types.
Returns:
Dictionary mapping SDR type names to capability dicts
"""
capabilities = {}
for sdr_type in cls._builders:
caps = cls.get_capabilities(sdr_type)
capabilities[sdr_type.value] = {
"name": sdr_type.name.replace("_", " "),
"freq_min_mhz": caps.freq_min_mhz,
"freq_max_mhz": caps.freq_max_mhz,
"gain_min": caps.gain_min,
"gain_max": caps.gain_max,
"sample_rates": caps.sample_rates,
"supports_bias_t": caps.supports_bias_t,
"supports_ppm": caps.supports_ppm,
"tx_capable": caps.tx_capable,
}
return capabilities
@classmethod
def create_default_device(cls, sdr_type: SDRType, index: int = 0, serial: str = "N/A") -> SDRDevice:
"""
Create a default device object for a given SDR type.
Useful when device detection didn't provide full details but
you know the hardware type.
Args:
sdr_type: The SDR hardware type
index: Device index (default 0)
serial: Device serial (default 'N/A')
Returns:
SDRDevice with default capabilities for the type
"""
caps = cls.get_capabilities(sdr_type)
return SDRDevice(
sdr_type=sdr_type,
index=index,
name=f"{sdr_type.name.replace('_', ' ')} Device {index}",
serial=serial,
driver=sdr_type.value,
capabilities=caps,
)
@classmethod
def create_network_device(cls, host: str, port: int = 1234) -> SDRDevice:
"""
Create a network device for rtl_tcp connection.
Args:
host: rtl_tcp server hostname or IP address
port: rtl_tcp server port (default 1234)
Returns:
SDRDevice configured for rtl_tcp connection
"""
caps = cls.get_capabilities(SDRType.RTL_SDR)
return SDRDevice(
sdr_type=SDRType.RTL_SDR,
index=0,
name=f"{host}:{port}",
serial="rtl_tcp",
driver="rtl_tcp",
capabilities=caps,
rtl_tcp_host=host,
rtl_tcp_port=port,
)
# Export commonly used items at package level
__all__ = [
# Factory
"SDRFactory",
# Types and classes
"SDRType",
"SDRDevice",
"SDRCapabilities",
"CommandBuilder",
# Builders
"RTLSDRCommandBuilder",
"LimeSDRCommandBuilder",
"HackRFCommandBuilder",
"AirspyCommandBuilder",
"SDRPlayCommandBuilder",
# Validation
"SDRValidationError",
"validate_frequency",
"validate_gain",
"validate_sample_rate",
"validate_ppm",
"validate_device_index",
"validate_squelch",
"get_capabilities_for_type",
# Device probing
"probe_rtlsdr_device",
"invalidate_device_cache",
]