Dan Albert a9348154af Alter the beacon format to be keyed by ID.
ID based lookup will be used for finding ILS and TACAN beacons from the
pydcs data.
2022-09-27 18:24:49 -07:00

98 lines
2.8 KiB
Python

import json
from collections.abc import Iterator
from dataclasses import dataclass
from enum import IntEnum, auto
from pathlib import Path
from typing import Optional
from game.radio.radios import RadioFrequency
from game.radio.tacan import TacanBand, TacanChannel
from game.theater import ConflictTheater
BEACONS_RESOURCE_PATH = Path("resources/dcs/beacons")
class BeaconType(IntEnum):
BEACON_TYPE_NULL = auto()
BEACON_TYPE_VOR = auto()
BEACON_TYPE_DME = auto()
BEACON_TYPE_VOR_DME = auto()
BEACON_TYPE_TACAN = auto()
BEACON_TYPE_VORTAC = auto()
BEACON_TYPE_RSBN = auto()
BEACON_TYPE_BROADCAST_STATION = auto()
BEACON_TYPE_HOMER = auto()
BEACON_TYPE_AIRPORT_HOMER = auto()
BEACON_TYPE_AIRPORT_HOMER_WITH_MARKER = auto()
BEACON_TYPE_ILS_FAR_HOMER = auto()
BEACON_TYPE_ILS_NEAR_HOMER = auto()
BEACON_TYPE_ILS_LOCALIZER = auto()
BEACON_TYPE_ILS_GLIDESLOPE = auto()
BEACON_TYPE_PRMG_LOCALIZER = auto()
BEACON_TYPE_PRMG_GLIDESLOPE = auto()
BEACON_TYPE_ICLS_LOCALIZER = auto()
BEACON_TYPE_ICLS_GLIDESLOPE = auto()
BEACON_TYPE_NAUTICAL_HOMER = auto()
@dataclass(frozen=True)
class Beacon:
name: str
callsign: str
beacon_type: BeaconType
hertz: int
channel: Optional[int]
@property
def frequency(self) -> RadioFrequency:
return RadioFrequency(self.hertz)
@property
def is_tacan(self) -> bool:
return self.beacon_type in (
BeaconType.BEACON_TYPE_VORTAC,
BeaconType.BEACON_TYPE_TACAN,
)
@property
def tacan_channel(self) -> TacanChannel:
assert self.is_tacan
assert self.channel is not None
return TacanChannel(self.channel, TacanBand.X)
class Beacons:
_by_terrain: dict[str, dict[str, Beacon]] = {}
@classmethod
def _load_for_theater_if_needed(cls, theater: ConflictTheater) -> None:
if theater.terrain.name in cls._by_terrain:
return
beacons_file = BEACONS_RESOURCE_PATH / f"{theater.terrain.name.lower()}.json"
if not beacons_file.exists():
raise RuntimeError(f"Beacon file {beacons_file.resolve()} is missing")
beacons = {}
for bid, beacon in json.loads(beacons_file.read_text()).items():
beacons[bid] = Beacon(**beacon)
cls._by_terrain[theater.terrain.name] = beacons
@classmethod
def _dict_for_theater(cls, theater: ConflictTheater) -> dict[str, Beacon]:
cls._load_for_theater_if_needed(theater)
return cls._by_terrain[theater.terrain.name]
@classmethod
def iter_theater(cls, theater: ConflictTheater) -> Iterator[Beacon]:
yield from cls._dict_for_theater(theater).values()
@classmethod
def with_id(cls, beacon_id: str, theater: ConflictTheater) -> Beacon:
return cls._dict_for_theater(theater)[beacon_id]