51 lines
1.5 KiB
Python
51 lines
1.5 KiB
Python
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]
|