""" Hardware-specific parameter validation for SDR devices. Validates frequency, gain, sample rate, and other parameters against the capabilities of specific SDR hardware. """ from typing import Optional from .base import SDRCapabilities, SDRDevice, SDRType class SDRValidationError(ValueError): """Raised when SDR parameter validation fails.""" pass def validate_frequency( freq_mhz: float, device: Optional[SDRDevice] = None, capabilities: Optional[SDRCapabilities] = None ) -> float: """ Validate frequency against device capabilities. Args: freq_mhz: Frequency in MHz device: SDR device (optional, takes precedence) capabilities: SDR capabilities (used if device not provided) Returns: Validated frequency in MHz Raises: SDRValidationError: If frequency is out of range """ if device: caps = device.capabilities elif capabilities: caps = capabilities else: # Default RTL-SDR range for backwards compatibility caps = SDRCapabilities( sdr_type=SDRType.RTL_SDR, freq_min_mhz=24.0, freq_max_mhz=1766.0, gain_min=0.0, gain_max=50.0 ) if not caps.freq_min_mhz <= freq_mhz <= caps.freq_max_mhz: raise SDRValidationError( f"Frequency {freq_mhz} MHz out of range for {caps.sdr_type.value}. " f"Valid range: {caps.freq_min_mhz}-{caps.freq_max_mhz} MHz" ) return freq_mhz def validate_gain( gain: float, device: Optional[SDRDevice] = None, capabilities: Optional[SDRCapabilities] = None ) -> float: """ Validate gain against device capabilities. Args: gain: Gain in dB device: SDR device (optional, takes precedence) capabilities: SDR capabilities (used if device not provided) Returns: Validated gain in dB Raises: SDRValidationError: If gain is out of range """ if device: caps = device.capabilities elif capabilities: caps = capabilities else: # Default range for backwards compatibility caps = SDRCapabilities( sdr_type=SDRType.RTL_SDR, freq_min_mhz=24.0, freq_max_mhz=1766.0, gain_min=0.0, gain_max=50.0 ) # Allow 0 for auto gain if gain == 0: return gain if not caps.gain_min <= gain <= caps.gain_max: raise SDRValidationError( f"Gain {gain} dB out of range for {caps.sdr_type.value}. " f"Valid range: {caps.gain_min}-{caps.gain_max} dB" ) return gain def validate_sample_rate( rate: int, device: Optional[SDRDevice] = None, capabilities: Optional[SDRCapabilities] = None, snap_to_nearest: bool = True ) -> int: """ Validate sample rate against device capabilities. Args: rate: Sample rate in Hz device: SDR device (optional, takes precedence) capabilities: SDR capabilities (used if device not provided) snap_to_nearest: If True, return nearest valid rate instead of raising Returns: Validated sample rate in Hz Raises: SDRValidationError: If rate is invalid and snap_to_nearest is False """ if device: caps = device.capabilities elif capabilities: caps = capabilities else: return rate # No validation without capabilities if not caps.sample_rates: return rate # No restrictions if rate in caps.sample_rates: return rate if snap_to_nearest: # Find closest valid rate closest = min(caps.sample_rates, key=lambda x: abs(x - rate)) return closest raise SDRValidationError( f"Sample rate {rate} Hz not supported by {caps.sdr_type.value}. " f"Valid rates: {caps.sample_rates}" ) def validate_ppm( ppm: int, device: Optional[SDRDevice] = None, capabilities: Optional[SDRCapabilities] = None ) -> int: """ Validate PPM frequency correction. Args: ppm: PPM correction value device: SDR device (optional, takes precedence) capabilities: SDR capabilities (used if device not provided) Returns: Validated PPM value Raises: SDRValidationError: If PPM is out of range or not supported """ if device: caps = device.capabilities elif capabilities: caps = capabilities else: caps = None # Check if device supports PPM if caps and not caps.supports_ppm: if ppm != 0: # Warn but don't fail - some hardware just ignores PPM pass return 0 # Return 0 to indicate no correction # Standard PPM range if not -1000 <= ppm <= 1000: raise SDRValidationError( f"PPM correction {ppm} out of range. Valid range: -1000 to 1000" ) return ppm def validate_device_index(index: int) -> int: """ Validate device index. Args: index: Device index (0-255) Returns: Validated device index Raises: SDRValidationError: If index is out of range """ if not 0 <= index <= 255: raise SDRValidationError( f"Device index {index} out of range. Valid range: 0-255" ) return index def validate_squelch(squelch: int) -> int: """ Validate squelch level. Args: squelch: Squelch level (0-1000, 0 = off) Returns: Validated squelch level Raises: SDRValidationError: If squelch is out of range """ if not 0 <= squelch <= 1000: raise SDRValidationError( f"Squelch {squelch} out of range. Valid range: 0-1000" ) return squelch def get_capabilities_for_type(sdr_type: SDRType) -> SDRCapabilities: """ Get default capabilities for an SDR type. Args: sdr_type: The SDR type Returns: SDRCapabilities for the specified type """ from .rtlsdr import RTLSDRCommandBuilder from .limesdr import LimeSDRCommandBuilder from .hackrf import HackRFCommandBuilder builders = { SDRType.RTL_SDR: RTLSDRCommandBuilder, SDRType.LIME_SDR: LimeSDRCommandBuilder, SDRType.HACKRF: HackRFCommandBuilder, } builder_class = builders.get(sdr_type) if builder_class: return builder_class.CAPABILITIES raise SDRValidationError(f"Unknown SDR type: {sdr_type}")