mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 22:59:59 -07:00
- Fix SSE fanout thread AttributeError when source queue is None during interpreter shutdown by snapshotting to local variable with null guard - Fix branded "i" logo rendering oversized on first page load (FOUC) by adding inline width/height to SVG elements across 10 templates - Bump version to 2.26.0 in config.py, pyproject.toml, and CHANGELOG.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
258 lines
6.6 KiB
Python
258 lines
6.6 KiB
Python
"""
|
|
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 .hackrf import HackRFCommandBuilder
|
|
from .limesdr import LimeSDRCommandBuilder
|
|
from .rtlsdr import RTLSDRCommandBuilder
|
|
|
|
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}")
|