feat(collector): add collector service
This commit is contained in:
114
services/collector/audio_validator.py
Normal file
114
services/collector/audio_validator.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
FR-2.2: Audio Data Validation
|
||||
Validates audio metrics against expected ranges
|
||||
"""
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class ValidationResult(NamedTuple):
|
||||
"""Validation result"""
|
||||
|
||||
valid: bool
|
||||
error: str = ""
|
||||
|
||||
|
||||
class AudioValidator:
|
||||
"""
|
||||
Validates audio metrics against hardware constraints and realistic ranges.
|
||||
"""
|
||||
|
||||
# Hardware constraints (from FR spec)
|
||||
RMS_MIN_DB = -40.0 # Noise floor
|
||||
RMS_MAX_DB = 80.0 # Clipping threshold
|
||||
|
||||
FREQ_MIN_HZ = 100 # Below = unreliable (FFT bin size ~43Hz)
|
||||
FREQ_MAX_HZ = 8000 # Nyquist @ 22.05kHz with safety margin
|
||||
|
||||
# Extended ranges for detection (not storage)
|
||||
FREQ_MIN_EXTENDED_HZ = 20
|
||||
FREQ_MAX_EXTENDED_HZ = 11000
|
||||
|
||||
@staticmethod
|
||||
def validate_rms(rms_db: float) -> ValidationResult:
|
||||
"""
|
||||
Validate RMS value.
|
||||
|
||||
Args:
|
||||
rms_db: RMS in dB
|
||||
|
||||
Returns:
|
||||
ValidationResult with valid flag and error message
|
||||
"""
|
||||
if not isinstance(rms_db, (int, float)):
|
||||
return ValidationResult(False, "RMS must be numeric")
|
||||
|
||||
if rms_db < AudioValidator.RMS_MIN_DB:
|
||||
return ValidationResult(
|
||||
False, f"RMS {rms_db:.1f}dB below minimum {AudioValidator.RMS_MIN_DB}dB"
|
||||
)
|
||||
|
||||
if rms_db > AudioValidator.RMS_MAX_DB:
|
||||
return ValidationResult(
|
||||
False,
|
||||
f"RMS {rms_db:.1f}dB exceeds maximum {AudioValidator.RMS_MAX_DB}dB",
|
||||
)
|
||||
|
||||
return ValidationResult(True)
|
||||
|
||||
@staticmethod
|
||||
def validate_frequency(freq_hz: int, strict: bool = True) -> ValidationResult:
|
||||
"""
|
||||
Validate frequency value.
|
||||
|
||||
Args:
|
||||
freq_hz: Frequency in Hz
|
||||
strict: If True, use tight range (100-8000Hz), else extended (20-11000Hz)
|
||||
|
||||
Returns:
|
||||
ValidationResult with valid flag and error message
|
||||
"""
|
||||
if not isinstance(freq_hz, int):
|
||||
return ValidationResult(False, "Frequency must be integer")
|
||||
|
||||
if strict:
|
||||
min_hz = AudioValidator.FREQ_MIN_HZ
|
||||
max_hz = AudioValidator.FREQ_MAX_HZ
|
||||
else:
|
||||
min_hz = AudioValidator.FREQ_MIN_EXTENDED_HZ
|
||||
max_hz = AudioValidator.FREQ_MAX_EXTENDED_HZ
|
||||
|
||||
if freq_hz < min_hz:
|
||||
return ValidationResult(
|
||||
False, f"Frequency {freq_hz}Hz below minimum {min_hz}Hz"
|
||||
)
|
||||
|
||||
if freq_hz > max_hz:
|
||||
return ValidationResult(
|
||||
False, f"Frequency {freq_hz}Hz exceeds maximum {max_hz}Hz"
|
||||
)
|
||||
|
||||
return ValidationResult(True)
|
||||
|
||||
@staticmethod
|
||||
def validate_packet(rms_db: float, freq_hz: int) -> ValidationResult:
|
||||
"""
|
||||
Validate complete audio packet.
|
||||
|
||||
Args:
|
||||
rms_db: RMS in dB
|
||||
freq_hz: Frequency in Hz
|
||||
|
||||
Returns:
|
||||
ValidationResult with valid flag and error message
|
||||
"""
|
||||
rms_result = AudioValidator.validate_rms(rms_db)
|
||||
if not rms_result.valid:
|
||||
return rms_result
|
||||
|
||||
freq_result = AudioValidator.validate_frequency(freq_hz, strict=True)
|
||||
if not freq_result.valid:
|
||||
return freq_result
|
||||
|
||||
return ValidationResult(True)
|
||||
Reference in New Issue
Block a user