Make Radio dataclass store list of ranges

This commit is contained in:
Kangwook Lee 2021-08-04 23:44:48 +09:00 committed by Dan Albert
parent 8608b73009
commit c58ecd96f0

View File

@ -2,7 +2,7 @@
import itertools import itertools
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, Iterator, List, Set from typing import Dict, FrozenSet, Iterator, List, Reversible, Set, Tuple
@dataclass(frozen=True) @dataclass(frozen=True)
@ -45,14 +45,8 @@ def kHz(num: int) -> RadioFrequency:
@dataclass(frozen=True) @dataclass(frozen=True)
class Radio: class RadioRange:
"""A radio. """Defines the minimum (inclusive) and maximum (exclusive) range of the radio."""
Defines the minimum (inclusive) and maximum (exclusive) range of the radio.
"""
#: The name of the radio.
name: str
#: The minimum (inclusive) frequency tunable by this radio. #: The minimum (inclusive) frequency tunable by this radio.
minimum: RadioFrequency minimum: RadioFrequency
@ -63,19 +57,51 @@ class Radio:
#: The spacing between adjacent frequencies. #: The spacing between adjacent frequencies.
step: RadioFrequency step: RadioFrequency
def __str__(self) -> str: #: Specific frequencies to exclude. (e.g. Guard channels)
return self.name excludes: FrozenSet[RadioFrequency] = frozenset()
def range(self) -> Iterator[RadioFrequency]: def range(self) -> Iterator[RadioFrequency]:
"""Returns an iterator over the usable frequencies of this radio.""" """Returns an iterator over the usable frequencies of this radio."""
return ( return (
RadioFrequency(x) RadioFrequency(x)
for x in range(self.minimum.hertz, self.maximum.hertz, self.step.hertz) for x in range(self.minimum.hertz, self.maximum.hertz, self.step.hertz)
if RadioFrequency(x) not in self.excludes
) )
@property @property
def last_channel(self) -> RadioFrequency: def last_channel(self) -> RadioFrequency:
return RadioFrequency(self.maximum.hertz - self.step.hertz) return next(
RadioFrequency(x)
for x in reversed(
range(self.minimum.hertz, self.maximum.hertz, self.step.hertz)
)
if RadioFrequency(x) not in self.excludes
)
@dataclass(frozen=True)
class Radio:
"""A radio.
Defines ranges of usable frequencies of the radio.
"""
#: The name of the radio.
name: str
#: List of usable frequency range of this radio.
ranges: Tuple[RadioRange, ...]
def __str__(self) -> str:
return self.name
def range(self) -> Iterator[RadioFrequency]:
"""Returns an iterator over the usable frequencies of this radio."""
return itertools.chain.from_iterable(rng.range() for rng in self.ranges)
@property
def last_channel(self) -> RadioFrequency:
return self.ranges[-1].last_channel
class ChannelInUseError(RuntimeError): class ChannelInUseError(RuntimeError):
@ -88,53 +114,53 @@ class ChannelInUseError(RuntimeError):
# TODO: Figure out appropriate steps for each radio. These are just guesses. # TODO: Figure out appropriate steps for each radio. These are just guesses.
#: List of all known radios used by aircraft in the game. #: List of all known radios used by aircraft in the game.
RADIOS: List[Radio] = [ RADIOS: List[Radio] = [
Radio("AN/ARC-164", MHz(225), MHz(400), step=MHz(1)), Radio("AN/ARC-164", (RadioRange(MHz(225), MHz(400), step=MHz(1)),)),
Radio("AN/ARC-186(V) AM", MHz(116), MHz(152), step=MHz(1)), Radio("AN/ARC-186(V) AM", (RadioRange(MHz(116), MHz(152), step=MHz(1)),)),
Radio("AN/ARC-186(V) FM", MHz(30), MHz(76), step=MHz(1)), Radio("AN/ARC-186(V) FM", (RadioRange(MHz(30), MHz(76), step=MHz(1)),)),
# The AN/ARC-210 can also use [30, 88) and [108, 118), but the current # The AN/ARC-210 can also use [30, 88) and [108, 118), but the current
# implementation can't implement the gap and the radio can't transmit on the # implementation can't implement the gap and the radio can't transmit on the
# latter. There's still plenty of channels between 118 MHz and 400 MHz, so # latter. There's still plenty of channels between 118 MHz and 400 MHz, so
# not worth worrying about. # not worth worrying about.
Radio("AN/ARC-210", MHz(118), MHz(400), step=MHz(1)), Radio("AN/ARC-210", (RadioRange(MHz(118), MHz(400), step=MHz(1)),)),
Radio("AN/ARC-222", MHz(116), MHz(174), step=MHz(1)), Radio("AN/ARC-222", (RadioRange(MHz(116), MHz(174), step=MHz(1)),)),
Radio("SCR-522", MHz(100), MHz(156), step=MHz(1)), Radio("SCR-522", (RadioRange(MHz(100), MHz(156), step=MHz(1)),)),
Radio("A.R.I. 1063", MHz(100), MHz(156), step=MHz(1)), Radio("A.R.I. 1063", (RadioRange(MHz(100), MHz(156), step=MHz(1)),)),
Radio("BC-1206", kHz(200), kHz(400), step=kHz(10)), Radio("BC-1206", (RadioRange(kHz(200), kHz(400), step=kHz(10)),)),
# Note: The M2000C V/UHF can operate in both ranges, but has a gap between # Note: The M2000C V/UHF can operate in both ranges, but has a gap between
# 150 MHz and 225 MHz. We can't allocate in that gap, and the current # 150 MHz and 225 MHz. We can't allocate in that gap, and the current
# system doesn't model gaps, so just pretend it ends at 150 MHz for now. We # system doesn't model gaps, so just pretend it ends at 150 MHz for now. We
# can model gaps later if needed. # can model gaps later if needed.
Radio("TRT ERA 7000 V/UHF", MHz(118), MHz(150), step=MHz(1)), Radio("TRT ERA 7000 V/UHF", (RadioRange(MHz(118), MHz(150), step=MHz(1)),)),
Radio("TRT ERA 7200 UHF", MHz(225), MHz(400), step=MHz(1)), Radio("TRT ERA 7200 UHF", (RadioRange(MHz(225), MHz(400), step=MHz(1)),)),
# Tomcat radios # Tomcat radios
# # https://www.heatblur.se/F-14Manual/general.html#an-arc-159-uhf-1-radio # # https://www.heatblur.se/F-14Manual/general.html#an-arc-159-uhf-1-radio
Radio("AN/ARC-159", MHz(225), MHz(400), step=MHz(1)), Radio("AN/ARC-159", (RadioRange(MHz(225), MHz(400), step=MHz(1)),)),
# AN/ARC-182 can also operate from 30 MHz to 88 MHz, as well as from 225 MHz # AN/ARC-182 can also operate from 30 MHz to 88 MHz, as well as from 225 MHz
# to 400 MHz range, but we can't model gaps with the current implementation. # to 400 MHz range, but we can't model gaps with the current implementation.
# https://www.heatblur.se/F-14Manual/general.html#an-arc-182-v-uhf-2-radio # https://www.heatblur.se/F-14Manual/general.html#an-arc-182-v-uhf-2-radio
Radio("AN/ARC-182", MHz(108), MHz(174), step=MHz(1)), Radio("AN/ARC-182", (RadioRange(MHz(108), MHz(174), step=MHz(1)),)),
# Also capable of [103, 156) at 25 kHz intervals, but we can't do gaps. # Also capable of [103, 156) at 25 kHz intervals, but we can't do gaps.
Radio("FR 22", MHz(225), MHz(400), step=kHz(50)), Radio("FR 22", (RadioRange(MHz(225), MHz(400), step=kHz(50)),)),
# P-51 / P-47 Radio # P-51 / P-47 Radio
# 4 preset channels (A/B/C/D) # 4 preset channels (A/B/C/D)
Radio("SCR522", MHz(100), MHz(156), step=kHz(25)), Radio("SCR522", (RadioRange(MHz(100), MHz(156), step=kHz(25)),)),
Radio("R&S M3AR VHF", MHz(120), MHz(174), step=MHz(1)), Radio("R&S M3AR VHF", (RadioRange(MHz(120), MHz(174), step=MHz(1)),)),
Radio("R&S M3AR UHF", MHz(225), MHz(400), step=MHz(1)), Radio("R&S M3AR UHF", (RadioRange(MHz(225), MHz(400), step=MHz(1)),)),
# MiG-15bis # MiG-15bis
Radio("RSI-6K HF", MHz(3, 750), MHz(5), step=kHz(25)), Radio("RSI-6K HF", (RadioRange(MHz(3, 750), MHz(5), step=kHz(25)),)),
# MiG-19P # MiG-19P
Radio("RSIU-4V", MHz(100), MHz(150), step=MHz(1)), Radio("RSIU-4V", (RadioRange(MHz(100), MHz(150), step=MHz(1)),)),
# MiG-21bis # MiG-21bis
Radio("RSIU-5V", MHz(118), MHz(140), step=MHz(1)), Radio("RSIU-5V", (RadioRange(MHz(118), MHz(140), step=MHz(1)),)),
# Ka-50 # Ka-50
# Note: Also capable of 100MHz-150MHz, but we can't model gaps. # Note: Also capable of 100MHz-150MHz, but we can't model gaps.
Radio("R-800L1", MHz(220), MHz(400), step=kHz(25)), Radio("R-800L1", (RadioRange(MHz(220), MHz(400), step=kHz(25)),)),
Radio("R-828", MHz(20), MHz(60), step=kHz(25)), Radio("R-828", (RadioRange(MHz(20), MHz(60), step=kHz(25)),)),
# UH-1H # UH-1H
Radio("AN/ARC-51BX", MHz(225), MHz(400), step=kHz(50)), Radio("AN/ARC-51BX", (RadioRange(MHz(225), MHz(400), step=kHz(50)),)),
Radio("AN/ARC-131", MHz(30), MHz(76), step=kHz(50)), Radio("AN/ARC-131", (RadioRange(MHz(30), MHz(76), step=kHz(50)),)),
Radio("AN/ARC-134", MHz(116), MHz(150), step=kHz(25)), Radio("AN/ARC-134", (RadioRange(MHz(116), MHz(150), step=kHz(25)),)),
Radio("R&S Series 6000", MHz(100), MHz(156), step=kHz(25)), Radio("R&S Series 6000", (RadioRange(MHz(100), MHz(156), step=kHz(25)),)),
] ]
@ -175,7 +201,7 @@ class RadioRegistry:
# Not a real radio, but useful for allocating a channel usable for # Not a real radio, but useful for allocating a channel usable for
# inter-flight communications. # inter-flight communications.
BLUFOR_UHF = Radio("BLUFOR UHF", MHz(225), MHz(400), step=MHz(1)) BLUFOR_UHF = Radio("BLUFOR UHF", (RadioRange(MHz(225), MHz(400), step=MHz(1)),))
def __init__(self) -> None: def __init__(self) -> None:
self.allocated_channels: Set[RadioFrequency] = set() self.allocated_channels: Set[RadioFrequency] = set()