from __future__ import annotations import csv from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: from app.core.antenna import AntennaPattern def _parse_float(value: str) -> float: return float(value.strip()) def load_pattern_file(path: str | Path) -> list[tuple[float, float, float]]: file_path = Path(path) if not file_path.exists(): raise FileNotFoundError(f"Antenna pattern file not found: {file_path}") samples: list[tuple[float, float, float]] = [] with file_path.open(encoding="utf-8", newline="") as handle: reader = csv.reader(handle) for row in reader: if not row or row[0].startswith("#"): continue if len(row) < 3: continue if row[0].lower() in {"azimuth_deg", "az", "azimuth"}: continue samples.append((_parse_float(row[0]), _parse_float(row[1]), _parse_float(row[2]))) if not samples: raise ValueError(f"No antenna pattern samples found in {file_path}") return samples def gain_from_file( pattern: AntennaPattern, az_deg: float, el_deg: float, ) -> float: if not pattern.pattern_file: raise ValueError("pattern_file is required for file patterns") samples = load_pattern_file(pattern.pattern_file) nearest = min( samples, key=lambda item: abs(item[0] - az_deg) + abs(item[1] - el_deg), ) return pattern.gain_dbi + nearest[2]