mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Merge remote-tracking branch 'khopa/develop' into helipads
# Conflicts: # changelog.md
This commit is contained in:
@@ -57,6 +57,7 @@ from dcs.task import (
|
||||
Transport,
|
||||
WeaponType,
|
||||
TargetType,
|
||||
Nothing,
|
||||
)
|
||||
from dcs.terrain.terrain import Airport, NoParkingSlotError
|
||||
from dcs.triggers import Event, TriggerOnce, TriggerRule
|
||||
@@ -92,7 +93,7 @@ from gen.flights.flight import (
|
||||
from gen.lasercoderegistry import LaserCodeRegistry
|
||||
from gen.radios import RadioFrequency, RadioRegistry
|
||||
from gen.runways import RunwayData
|
||||
from gen.tacan import TacanBand, TacanRegistry
|
||||
from gen.tacan import TacanBand, TacanRegistry, TacanUsage
|
||||
from .airsupport import AirSupport, AwacsInfo, TankerInfo
|
||||
from .callsigns import callsign_for_support_unit
|
||||
from .flights.flightplan import (
|
||||
@@ -437,7 +438,7 @@ class AircraftConflictGenerator:
|
||||
if isinstance(flight.flight_plan, RefuelingFlightPlan):
|
||||
callsign = callsign_for_support_unit(group)
|
||||
|
||||
tacan = self.tacan_registy.alloc_for_band(TacanBand.Y)
|
||||
tacan = self.tacan_registy.alloc_for_band(TacanBand.Y, TacanUsage.AirToAir)
|
||||
self.air_support.tankers.append(
|
||||
TankerInfo(
|
||||
group_name=str(group.name),
|
||||
@@ -656,41 +657,36 @@ class AircraftConflictGenerator:
|
||||
|
||||
for squadron in control_point.squadrons:
|
||||
try:
|
||||
self._spawn_unused_at(control_point, country, faction, squadron)
|
||||
self._spawn_unused_for(squadron, country, faction)
|
||||
except NoParkingSlotError:
|
||||
# If we run out of parking, stop spawning aircraft.
|
||||
return
|
||||
|
||||
def _spawn_unused_at(
|
||||
self,
|
||||
control_point: Airfield,
|
||||
country: Country,
|
||||
faction: Faction,
|
||||
squadron: Squadron,
|
||||
def _spawn_unused_for(
|
||||
self, squadron: Squadron, country: Country, faction: Faction
|
||||
) -> None:
|
||||
assert isinstance(squadron.location, Airfield)
|
||||
for _ in range(squadron.untasked_aircraft):
|
||||
# Creating a flight even those this isn't a fragged mission lets us
|
||||
# reuse the existing debriefing code.
|
||||
# TODO: Special flight type?
|
||||
flight = Flight(
|
||||
Package(control_point),
|
||||
Package(squadron.location),
|
||||
faction.country,
|
||||
squadron,
|
||||
1,
|
||||
FlightType.BARCAP,
|
||||
"Cold",
|
||||
departure=control_point,
|
||||
arrival=control_point,
|
||||
divert=None,
|
||||
)
|
||||
|
||||
group = self._generate_at_airport(
|
||||
name=namegen.next_aircraft_name(country, control_point.id, flight),
|
||||
name=namegen.next_aircraft_name(country, flight.departure.id, flight),
|
||||
side=country,
|
||||
unit_type=squadron.aircraft.dcs_unit_type,
|
||||
count=1,
|
||||
start_type="Cold",
|
||||
airport=control_point.airport,
|
||||
airport=squadron.location.airport,
|
||||
)
|
||||
|
||||
self._setup_livery(flight, group)
|
||||
@@ -1153,6 +1149,23 @@ class AircraftConflictGenerator:
|
||||
restrict_jettison=True,
|
||||
)
|
||||
|
||||
def configure_ferry(
|
||||
self,
|
||||
group: FlyingGroup[Any],
|
||||
package: Package,
|
||||
flight: Flight,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> None:
|
||||
group.task = Nothing.name
|
||||
self._setup_group(group, package, flight, dynamic_runways)
|
||||
self.configure_behavior(
|
||||
flight,
|
||||
group,
|
||||
react_on_threat=OptReactOnThreat.Values.EvadeFire,
|
||||
roe=OptROE.Values.WeaponHold,
|
||||
restrict_jettison=True,
|
||||
)
|
||||
|
||||
def configure_unknown_task(self, group: FlyingGroup[Any], flight: Flight) -> None:
|
||||
logging.error(f"Unhandled flight type: {flight.flight_type}")
|
||||
self.configure_behavior(flight, group)
|
||||
@@ -1197,6 +1210,8 @@ class AircraftConflictGenerator:
|
||||
self.configure_oca_strike(group, package, flight, dynamic_runways)
|
||||
elif flight_type == FlightType.TRANSPORT:
|
||||
self.configure_transport(group, package, flight, dynamic_runways)
|
||||
elif flight_type == FlightType.FERRY:
|
||||
self.configure_ferry(group, package, flight, dynamic_runways)
|
||||
else:
|
||||
self.configure_unknown_task(group, flight)
|
||||
|
||||
@@ -1783,6 +1798,8 @@ class LandingPointBuilder(PydcsWaypointBuilder):
|
||||
waypoint = super().build()
|
||||
waypoint.type = "Land"
|
||||
waypoint.action = PointAction.Landing
|
||||
if (control_point := self.waypoint.control_point) is not None:
|
||||
waypoint.airdrome_id = control_point.airdrome_id_for_landing
|
||||
return waypoint
|
||||
|
||||
|
||||
@@ -1792,6 +1809,8 @@ class CargoStopBuilder(PydcsWaypointBuilder):
|
||||
waypoint.type = "LandingReFuAr"
|
||||
waypoint.action = PointAction.LandingReFuAr
|
||||
waypoint.landing_refuel_rearm_time = 2 # Minutes.
|
||||
if (control_point := self.waypoint.control_point) is not None:
|
||||
waypoint.airdrome_id = control_point.airdrome_id_for_landing
|
||||
return waypoint
|
||||
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ from .conflictgen import Conflict
|
||||
from .flights.ai_flight_planner_db import AEWC_CAPABLE
|
||||
from .naming import namegen
|
||||
from .radios import RadioRegistry
|
||||
from .tacan import TacanBand, TacanRegistry
|
||||
from .tacan import TacanBand, TacanRegistry, TacanUsage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@@ -89,7 +89,9 @@ class AirSupportConflictGenerator:
|
||||
# TODO: Make loiter altitude a property of the unit type.
|
||||
alt, airspeed = self._get_tanker_params(tanker_unit_type.dcs_unit_type)
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
||||
tacan = self.tacan_registry.alloc_for_band(
|
||||
TacanBand.Y, TacanUsage.AirToAir
|
||||
)
|
||||
tanker_heading = Heading.from_degrees(
|
||||
self.conflict.red_cp.position.heading_between_point(
|
||||
self.conflict.blue_cp.position
|
||||
|
||||
10
gen/armor.py
10
gen/armor.py
@@ -143,9 +143,17 @@ class GroundConflictGenerator:
|
||||
# Add JTAC
|
||||
if self.game.blue.faction.has_jtac:
|
||||
n = "JTAC" + str(self.conflict.blue_cp.id) + str(self.conflict.red_cp.id)
|
||||
code: int = self.laser_code_registry.get_next_laser_code()
|
||||
code: int
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
|
||||
# If the option fc3LaserCode is enabled, force all JTAC
|
||||
# laser codes to 1113 to allow lasing for Su-25 Frogfoots and A-10A Warthogs.
|
||||
# Otherwise use 1688 for the first JTAC, 1687 for the second etc.
|
||||
if self.game.settings.plugins["plugins.jtacautolase.fc3LaserCode"]:
|
||||
code = 1113
|
||||
else:
|
||||
code = self.laser_code_registry.get_next_laser_code()
|
||||
|
||||
utype = self.game.blue.faction.jtac_unit
|
||||
if utype is None:
|
||||
utype = AircraftType.named("MQ-9 Reaper")
|
||||
|
||||
@@ -183,6 +183,7 @@ class Package:
|
||||
FlightType.TARCAP,
|
||||
FlightType.BARCAP,
|
||||
FlightType.AEWC,
|
||||
FlightType.FERRY,
|
||||
FlightType.REFUELING,
|
||||
FlightType.SWEEP,
|
||||
FlightType.ESCORT,
|
||||
|
||||
@@ -70,6 +70,7 @@ class FlightType(Enum):
|
||||
TRANSPORT = "Transport"
|
||||
SEAD_ESCORT = "SEAD Escort"
|
||||
REFUELING = "Refueling"
|
||||
FERRY = "Ferry"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
@@ -159,6 +160,7 @@ class FlightWaypoint:
|
||||
x: float,
|
||||
y: float,
|
||||
alt: Distance = meters(0),
|
||||
control_point: Optional[ControlPoint] = None,
|
||||
) -> None:
|
||||
"""Creates a flight waypoint.
|
||||
|
||||
@@ -168,11 +170,14 @@ class FlightWaypoint:
|
||||
y: Y coordinate of the waypoint.
|
||||
alt: Altitude of the waypoint. By default this is MSL, but it can be
|
||||
changed to AGL by setting alt_type to "RADIO"
|
||||
control_point: The control point to associate with this waypoint. Needed for
|
||||
landing points.
|
||||
"""
|
||||
self.waypoint_type = waypoint_type
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.alt = alt
|
||||
self.control_point = control_point
|
||||
self.alt_type = "BARO"
|
||||
self.name = ""
|
||||
# TODO: Merge with pretty_name.
|
||||
@@ -282,8 +287,6 @@ class Flight:
|
||||
count: int,
|
||||
flight_type: FlightType,
|
||||
start_type: str,
|
||||
departure: ControlPoint,
|
||||
arrival: ControlPoint,
|
||||
divert: Optional[ControlPoint],
|
||||
custom_name: Optional[str] = None,
|
||||
cargo: Optional[TransferOrder] = None,
|
||||
@@ -297,8 +300,8 @@ class Flight:
|
||||
self.roster = FlightRoster(self.squadron, initial_size=count)
|
||||
else:
|
||||
self.roster = roster
|
||||
self.departure = departure
|
||||
self.arrival = arrival
|
||||
self.departure = self.squadron.location
|
||||
self.arrival = self.squadron.arrival
|
||||
self.divert = divert
|
||||
self.flight_type = flight_type
|
||||
# TODO: Replace with FlightPlan.
|
||||
|
||||
@@ -37,10 +37,8 @@ from game.theater.theatergroundobject import (
|
||||
NavalGroundObject,
|
||||
BuildingGroundObject,
|
||||
)
|
||||
|
||||
from game.threatzones import ThreatZones
|
||||
from game.utils import Distance, Heading, Speed, feet, meters, nautical_miles, knots
|
||||
|
||||
from .closestairfields import ObjectiveDistanceCache
|
||||
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||
from .traveltime import GroundSpeed, TravelTime
|
||||
@@ -836,6 +834,39 @@ class AirliftFlightPlan(FlightPlan):
|
||||
return self.package.time_over_target
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FerryFlightPlan(FlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
nav_to_destination: list[FlightWaypoint]
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
yield self.takeoff
|
||||
yield from self.nav_to_destination
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
@property
|
||||
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||
return self.land
|
||||
|
||||
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||
# TOT planning isn't really useful for ferries. They're behind the front
|
||||
# lines so no need to wait for escorts or for other missions to complete.
|
||||
return None
|
||||
|
||||
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||
return None
|
||||
|
||||
@property
|
||||
def mission_departure_time(self) -> timedelta:
|
||||
return self.package.time_over_target
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CustomFlightPlan(FlightPlan):
|
||||
custom_waypoints: List[FlightWaypoint]
|
||||
@@ -958,6 +989,8 @@ class FlightPlanBuilder:
|
||||
return self.generate_transport(flight)
|
||||
elif task == FlightType.REFUELING:
|
||||
return self.generate_refueling_racetrack(flight)
|
||||
elif task == FlightType.FERRY:
|
||||
return self.generate_ferry(flight)
|
||||
raise PlanningError(f"{task} flight plan generation not implemented")
|
||||
|
||||
def regenerate_package_waypoints(self) -> None:
|
||||
@@ -1244,6 +1277,42 @@ class FlightPlanBuilder:
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def generate_ferry(self, flight: Flight) -> FerryFlightPlan:
|
||||
"""Generate a ferry flight at a given location.
|
||||
|
||||
Args:
|
||||
flight: The flight to generate the flight plan for.
|
||||
"""
|
||||
|
||||
if flight.departure == flight.arrival:
|
||||
raise PlanningError(
|
||||
f"Cannot plan ferry flight: departure and arrival are both "
|
||||
f"{flight.departure}"
|
||||
)
|
||||
|
||||
altitude_is_agl = flight.unit_type.dcs_unit_type.helicopter
|
||||
altitude = (
|
||||
feet(1500)
|
||||
if altitude_is_agl
|
||||
else flight.unit_type.preferred_patrol_altitude
|
||||
)
|
||||
|
||||
builder = WaypointBuilder(flight, self.coalition)
|
||||
return FerryFlightPlan(
|
||||
package=self.package,
|
||||
flight=flight,
|
||||
takeoff=builder.takeoff(flight.departure),
|
||||
nav_to_destination=builder.nav_path(
|
||||
flight.departure.position,
|
||||
flight.arrival.position,
|
||||
altitude,
|
||||
altitude_is_agl,
|
||||
),
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def cap_racetrack_for_objective(
|
||||
self, location: MissionTarget, barcap: bool
|
||||
) -> Tuple[Point, Point]:
|
||||
|
||||
@@ -110,7 +110,11 @@ class WaypointBuilder:
|
||||
waypoint.pretty_name = "Exit theater"
|
||||
else:
|
||||
waypoint = FlightWaypoint(
|
||||
FlightWaypointType.LANDING_POINT, position.x, position.y, meters(0)
|
||||
FlightWaypointType.LANDING_POINT,
|
||||
position.x,
|
||||
position.y,
|
||||
meters(0),
|
||||
control_point=arrival,
|
||||
)
|
||||
waypoint.name = "LANDING"
|
||||
waypoint.alt_type = "RADIO"
|
||||
@@ -139,7 +143,11 @@ class WaypointBuilder:
|
||||
altitude_type = "RADIO"
|
||||
|
||||
waypoint = FlightWaypoint(
|
||||
FlightWaypointType.DIVERT, position.x, position.y, altitude
|
||||
FlightWaypointType.DIVERT,
|
||||
position.x,
|
||||
position.y,
|
||||
altitude,
|
||||
control_point=divert,
|
||||
)
|
||||
waypoint.alt_type = altitude_type
|
||||
waypoint.name = "DIVERT"
|
||||
@@ -488,6 +496,7 @@ class WaypointBuilder:
|
||||
control_point.position.x,
|
||||
control_point.position.y,
|
||||
meters(0),
|
||||
control_point=control_point,
|
||||
)
|
||||
waypoint.alt_type = "RADIO"
|
||||
waypoint.name = "DROP OFF"
|
||||
|
||||
@@ -59,7 +59,7 @@ from game.unitmap import UnitMap
|
||||
from game.utils import Heading, feet, knots, mps
|
||||
from .radios import RadioFrequency, RadioRegistry
|
||||
from .runways import RunwayData
|
||||
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
||||
from .tacan import TacanBand, TacanChannel, TacanRegistry, TacanUsage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
@@ -378,7 +378,9 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator[GenericCarrierGroundO
|
||||
for unit in group.units[1:]:
|
||||
ship_group.add_unit(self.create_ship(unit, atc))
|
||||
|
||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.X)
|
||||
tacan = self.tacan_registry.alloc_for_band(
|
||||
TacanBand.X, TacanUsage.TransmitReceive
|
||||
)
|
||||
tacan_callsign = self.tacan_callsign()
|
||||
icls = next(self.icls_alloc)
|
||||
|
||||
|
||||
569
gen/naming.py
569
gen/naming.py
@@ -43,209 +43,406 @@ ALPHA_MILITARY = [
|
||||
]
|
||||
|
||||
ANIMALS: tuple[str, ...] = (
|
||||
"SHARK",
|
||||
"TORTOISE",
|
||||
"BAT",
|
||||
"PANGOLIN",
|
||||
"AARDVARK",
|
||||
"AARDWOLF",
|
||||
"MONKEY",
|
||||
"BUFFALO",
|
||||
"DOG",
|
||||
"BOBCAT",
|
||||
"LYNX",
|
||||
"PANTHER",
|
||||
"TIGER",
|
||||
"LION",
|
||||
"OWL",
|
||||
"BUTTERFLY",
|
||||
"BISON",
|
||||
"DUCK",
|
||||
"COBRA",
|
||||
"MAMBA",
|
||||
"DOLPHIN",
|
||||
"PHEASANT",
|
||||
"ADDER",
|
||||
"ALBACORE",
|
||||
"ALBATROSS",
|
||||
"ALLIGATOR",
|
||||
"ALPACA",
|
||||
"ANACONDA",
|
||||
"ANOLE",
|
||||
"ANTEATER",
|
||||
"ANTELOPE",
|
||||
"ANTLION",
|
||||
"ARAPAIMA",
|
||||
"ARCHERFISH",
|
||||
"ARGALI",
|
||||
"ARMADILLO",
|
||||
"RACOON",
|
||||
"ZEBRA",
|
||||
"ASP",
|
||||
"AUROCHS",
|
||||
"AXOLOTL",
|
||||
"BABIRUSA",
|
||||
"BABOON",
|
||||
"BADGER",
|
||||
"BANDICOOT",
|
||||
"BARRACUDA",
|
||||
"BARRAMUNDI",
|
||||
"BASILISK",
|
||||
"BASS",
|
||||
"BAT",
|
||||
"BEAR",
|
||||
"BEAVER",
|
||||
"BEETLE",
|
||||
"BELUGA",
|
||||
"BETTONG",
|
||||
"BINTURONG",
|
||||
"BISON",
|
||||
"BLOODHOUND",
|
||||
"BOA",
|
||||
"BOBCAT",
|
||||
"BONGO",
|
||||
"BONITO",
|
||||
"BUFFALO",
|
||||
"BULLDOG",
|
||||
"BULLFROG",
|
||||
"BULLSHARK",
|
||||
"BUMBLEBEE",
|
||||
"BUNNY",
|
||||
"BUTTERFLY",
|
||||
"CAIMAN",
|
||||
"CAMEL",
|
||||
"CANARY",
|
||||
"CAPYBARA",
|
||||
"CARACAL",
|
||||
"CARP",
|
||||
"CASTOR",
|
||||
"CAT",
|
||||
"CATERPILLAR",
|
||||
"CATFISH",
|
||||
"CENTIPEDE",
|
||||
"CHAMELEON",
|
||||
"CHEETAH",
|
||||
"CHICKEN",
|
||||
"CHIMAERA",
|
||||
"CICADA",
|
||||
"CICHLID",
|
||||
"CIVET",
|
||||
"COBIA",
|
||||
"COBRA",
|
||||
"COCKATOO",
|
||||
"COD",
|
||||
"COELACANTH",
|
||||
"COLT",
|
||||
"CONDOR",
|
||||
"COPPERHEAD",
|
||||
"CORAL",
|
||||
"CORGI",
|
||||
"COTTONMOUTH",
|
||||
"COUGAR",
|
||||
"COW",
|
||||
"COYOTE",
|
||||
"FOX",
|
||||
"LIGHTFOOT",
|
||||
"COTTONMOUTH",
|
||||
"TAURUS",
|
||||
"VIPER",
|
||||
"CASTOR",
|
||||
"GIRAFFE",
|
||||
"SNAKE",
|
||||
"MONSTER",
|
||||
"ALBATROSS",
|
||||
"HAWK",
|
||||
"DOVE",
|
||||
"MOCKINGBIRD",
|
||||
"GECKO",
|
||||
"ORYX",
|
||||
"GORILLA",
|
||||
"HARAMBE",
|
||||
"GOOSE",
|
||||
"MAVERICK",
|
||||
"HARE",
|
||||
"JACKAL",
|
||||
"LEOPARD",
|
||||
"CAT",
|
||||
"MUSK",
|
||||
"ORCA",
|
||||
"OCELOT",
|
||||
"BEAR",
|
||||
"PANDA",
|
||||
"GULL",
|
||||
"PENGUIN",
|
||||
"PYTHON",
|
||||
"RAVEN",
|
||||
"DEER",
|
||||
"MOOSE",
|
||||
"REINDEER",
|
||||
"SHEEP",
|
||||
"GAZELLE",
|
||||
"INSECT",
|
||||
"VULTURE",
|
||||
"WALLABY",
|
||||
"KANGAROO",
|
||||
"KOALA",
|
||||
"KIWI",
|
||||
"WHALE",
|
||||
"FISH",
|
||||
"RHINO",
|
||||
"HIPPO",
|
||||
"RAT",
|
||||
"WOODPECKER",
|
||||
"WORM",
|
||||
"BABOON",
|
||||
"YAK",
|
||||
"SCORPIO",
|
||||
"HORSE",
|
||||
"POODLE",
|
||||
"CENTIPEDE",
|
||||
"CHICKEN",
|
||||
"CHEETAH",
|
||||
"CHAMELEON",
|
||||
"CATFISH",
|
||||
"CATERPILLAR",
|
||||
"CARACAL",
|
||||
"CAMEL",
|
||||
"CAIMAN",
|
||||
"BARRACUDA",
|
||||
"BANDICOOT",
|
||||
"ALLIGATOR",
|
||||
"BONGO",
|
||||
"CORAL",
|
||||
"ELEPHANT",
|
||||
"ANTELOPE",
|
||||
"CRAB",
|
||||
"CRANE",
|
||||
"CRICKET",
|
||||
"CROCODILE",
|
||||
"CROW",
|
||||
"CUTTLEFISH",
|
||||
"DACHSHUND",
|
||||
"DEER",
|
||||
"DINGO",
|
||||
"DIREWOLF",
|
||||
"DODO",
|
||||
"FLAMINGO",
|
||||
"FERRET",
|
||||
"FALCON",
|
||||
"BULLDOG",
|
||||
"DOG",
|
||||
"DOLPHIN",
|
||||
"DONKEY",
|
||||
"IGUANA",
|
||||
"TAMARIN",
|
||||
"HARRIER",
|
||||
"GRIZZLY",
|
||||
"GREYHOUND",
|
||||
"GRASSHOPPER",
|
||||
"JAGUAR",
|
||||
"LADYBUG",
|
||||
"KOMODO",
|
||||
"DOVE",
|
||||
"DRACO",
|
||||
"DRAGON",
|
||||
"DRAGONFLY",
|
||||
"DUCK",
|
||||
"DUGONG",
|
||||
"EAGLE",
|
||||
"EARWIG",
|
||||
"ECHIDNA",
|
||||
"EEL",
|
||||
"ELEPHANT",
|
||||
"ELK",
|
||||
"EMU",
|
||||
"ERMINE",
|
||||
"FALCON",
|
||||
"FANGTOOTH",
|
||||
"FAWN",
|
||||
"FENNEC",
|
||||
"FERRET",
|
||||
"FINCH",
|
||||
"FIREFLY",
|
||||
"FISH",
|
||||
"FLAMINGO",
|
||||
"FLEA",
|
||||
"FLOUNDER",
|
||||
"FORGMOUTH",
|
||||
"FOX",
|
||||
"FRINGEHEAD",
|
||||
"FROG",
|
||||
"GAR",
|
||||
"GAZELLE",
|
||||
"GECKO",
|
||||
"GENET",
|
||||
"GERBIL",
|
||||
"GHARIAL",
|
||||
"GIBBON",
|
||||
"GIRAFFE",
|
||||
"GOOSE",
|
||||
"GOPHER",
|
||||
"GORILLA",
|
||||
"GOSHAWK",
|
||||
"GRASSHOPPER",
|
||||
"GREYHOUND",
|
||||
"GRIZZLY",
|
||||
"GROUPER",
|
||||
"GROUSE",
|
||||
"GRYPHON",
|
||||
"GUANACO",
|
||||
"GULL",
|
||||
"GUPPY",
|
||||
"HADDOCK",
|
||||
"HAGFISH",
|
||||
"HALIBUT",
|
||||
"HAMSTER",
|
||||
"HARAMBE",
|
||||
"HARE",
|
||||
"HARRIER",
|
||||
"HAWK",
|
||||
"HEDGEHOG",
|
||||
"HERMITCRAB",
|
||||
"HERON",
|
||||
"HERRING",
|
||||
"HIPPO",
|
||||
"HORNBILL",
|
||||
"HORNET",
|
||||
"HORSE",
|
||||
"HUNTSMAN",
|
||||
"HUSKY",
|
||||
"HYENA",
|
||||
"IBEX",
|
||||
"IBIS",
|
||||
"IGUANA",
|
||||
"IMPALA",
|
||||
"INSECT",
|
||||
"IRUKANDJI",
|
||||
"ISOPOD",
|
||||
"JACKAL",
|
||||
"JAGUAR",
|
||||
"JELLYFISH",
|
||||
"JERBOA",
|
||||
"KAKAPO",
|
||||
"KANGAROO",
|
||||
"KATYDID",
|
||||
"KEA",
|
||||
"KINGFISHER",
|
||||
"KITTEN",
|
||||
"KIWI",
|
||||
"KOALA",
|
||||
"KOMODO",
|
||||
"KRAIT",
|
||||
"LADYBUG",
|
||||
"LAMPREY",
|
||||
"LEMUR",
|
||||
"LEOPARD",
|
||||
"LIGHTFOOT",
|
||||
"LION",
|
||||
"LIONFISH",
|
||||
"LIZARD",
|
||||
"LLAMA",
|
||||
"LOACH",
|
||||
"LOBSTER",
|
||||
"OCTOPUS",
|
||||
"MANATEE",
|
||||
"MAGPIE",
|
||||
"MACAW",
|
||||
"OSTRICH",
|
||||
"OYSTER",
|
||||
"MOLE",
|
||||
"MULE",
|
||||
"MOTH",
|
||||
"MONGOOSE",
|
||||
"MOLLY",
|
||||
"MEERKAT",
|
||||
"MOUSE",
|
||||
"PEACOCK",
|
||||
"PIKE",
|
||||
"ROBIN",
|
||||
"RAGDOLL",
|
||||
"PLATYPUS",
|
||||
"PELICAN",
|
||||
"PARROT",
|
||||
"PORCUPINE",
|
||||
"PIRANHA",
|
||||
"PUMA",
|
||||
"PUG",
|
||||
"TAPIR",
|
||||
"TERMITE",
|
||||
"URCHIN",
|
||||
"SHRIMP",
|
||||
"TURKEY",
|
||||
"TOUCAN",
|
||||
"TETRA",
|
||||
"HUSKY",
|
||||
"STARFISH",
|
||||
"SWAN",
|
||||
"FROG",
|
||||
"SQUIRREL",
|
||||
"WALRUS",
|
||||
"WARTHOG",
|
||||
"CORGI",
|
||||
"WEASEL",
|
||||
"WOMBAT",
|
||||
"WOLVERINE",
|
||||
"MAMMOTH",
|
||||
"TOAD",
|
||||
"WOLF",
|
||||
"ZEBU",
|
||||
"SEAL",
|
||||
"SKATE",
|
||||
"JELLYFISH",
|
||||
"MOSQUITO",
|
||||
"LOCUST",
|
||||
"LORIKEET",
|
||||
"LUNGFISH",
|
||||
"LYNX",
|
||||
"MACAW",
|
||||
"MAGPIE",
|
||||
"MALLARD",
|
||||
"MAMBA",
|
||||
"MAMMOTH",
|
||||
"MANATEE",
|
||||
"MANDRILL",
|
||||
"MANTA",
|
||||
"MANTIS",
|
||||
"MARE",
|
||||
"MARLIN",
|
||||
"MARMOT",
|
||||
"MARTEN",
|
||||
"MASTIFF",
|
||||
"MASTODON",
|
||||
"MAVERICK",
|
||||
"MAYFLY",
|
||||
"MEERKAT",
|
||||
"MILLIPEDE",
|
||||
"MINK",
|
||||
"MOA",
|
||||
"MOCKINGBIRD",
|
||||
"MOLE",
|
||||
"MOLERAT",
|
||||
"MOLLY",
|
||||
"MONGOOSE",
|
||||
"MONKEY",
|
||||
"MONKFISH",
|
||||
"MONSTER",
|
||||
"MOOSE",
|
||||
"MORAY",
|
||||
"MOSQUITO",
|
||||
"MOTH",
|
||||
"MOUSE",
|
||||
"MUDSKIPPER",
|
||||
"MULE",
|
||||
"MUSK",
|
||||
"MYNA",
|
||||
"NARWHAL",
|
||||
"NAUTILUS",
|
||||
"NEWT",
|
||||
"NIGHTINGALE",
|
||||
"NUMBAT",
|
||||
"OCELOT",
|
||||
"OCTOPUS",
|
||||
"OKAPI",
|
||||
"OLM",
|
||||
"OPAH",
|
||||
"OPOSSUM",
|
||||
"ORCA",
|
||||
"ORYX",
|
||||
"OSPREY",
|
||||
"OSTRICH",
|
||||
"OTTER",
|
||||
"OWL",
|
||||
"OX",
|
||||
"OYSTER",
|
||||
"PADDLEFISH",
|
||||
"PADEMELON",
|
||||
"PANDA",
|
||||
"PANGOLIN",
|
||||
"PANTHER",
|
||||
"PARAKEET",
|
||||
"PARROT",
|
||||
"PEACOCK",
|
||||
"PELICAN",
|
||||
"PENGUIN",
|
||||
"PERCH",
|
||||
"PEREGRINE",
|
||||
"PETRAL",
|
||||
"PHEASANT",
|
||||
"PIG",
|
||||
"PIGEON",
|
||||
"PIGLET",
|
||||
"PIKE",
|
||||
"PIRANHA",
|
||||
"PLATYPUS",
|
||||
"POODLE",
|
||||
"PORCUPINE",
|
||||
"PORPOISE",
|
||||
"POSSUM",
|
||||
"POTOROO",
|
||||
"PRONGHORN",
|
||||
"PUFFERFISH",
|
||||
"PUFFIN",
|
||||
"PUG",
|
||||
"PUMA",
|
||||
"PYTHON",
|
||||
"QUAGGA",
|
||||
"QUAIL",
|
||||
"QUOKKA",
|
||||
"QUOLL",
|
||||
"RABBIT",
|
||||
"RACOON",
|
||||
"RAGDOLL",
|
||||
"RAT",
|
||||
"RATTLESNAKE",
|
||||
"RAVEN",
|
||||
"REINDEER",
|
||||
"RHINO",
|
||||
"ROACH",
|
||||
"ROBIN",
|
||||
"SABERTOOTH",
|
||||
"SAILFISH",
|
||||
"SALAMANDER",
|
||||
"SALMON",
|
||||
"SANDFLY",
|
||||
"SARDINE",
|
||||
"SAWFISH",
|
||||
"SCARAB",
|
||||
"SCORPION",
|
||||
"SEAHORSE",
|
||||
"SEAL",
|
||||
"SEALION",
|
||||
"SERVAL",
|
||||
"SHARK",
|
||||
"SHEEP",
|
||||
"SHOEBILL",
|
||||
"SHRIKE",
|
||||
"SHRIMP",
|
||||
"SIDEWINDER",
|
||||
"SILKWORM",
|
||||
"SKATE",
|
||||
"SKINK",
|
||||
"SKUNK",
|
||||
"SLOTH",
|
||||
"SLUG",
|
||||
"SNAIL",
|
||||
"HEDGEHOG",
|
||||
"PIGLET",
|
||||
"FENNEC",
|
||||
"BADGER",
|
||||
"ALPACA",
|
||||
"DINGO",
|
||||
"COLT",
|
||||
"SKUNK",
|
||||
"BUNNY",
|
||||
"IMPALA",
|
||||
"GUANACO",
|
||||
"CAPYBARA",
|
||||
"ELK",
|
||||
"MINK",
|
||||
"PRONGHORN",
|
||||
"CROW",
|
||||
"BUMBLEBEE",
|
||||
"FAWN",
|
||||
"OTTER",
|
||||
"SNAKE",
|
||||
"SNAPPER",
|
||||
"SNOOK",
|
||||
"SPARROW",
|
||||
"SPIDER",
|
||||
"SPRINGBOK",
|
||||
"SQUID",
|
||||
"SQUIRREL",
|
||||
"STAGHORN",
|
||||
"STARFISH",
|
||||
"STINGRAY",
|
||||
"STINKBUG",
|
||||
"STOUT",
|
||||
"STURGEON",
|
||||
"SUGARGLIDER",
|
||||
"SUNBEAR",
|
||||
"SWALLOW",
|
||||
"SWAN",
|
||||
"SWIFT",
|
||||
"SWORDFISH",
|
||||
"TAIPAN",
|
||||
"TAKAHE",
|
||||
"TAMARIN",
|
||||
"TANG",
|
||||
"TAPIR",
|
||||
"TARANTULA",
|
||||
"TARPON",
|
||||
"TARSIER",
|
||||
"TAURUS",
|
||||
"TERMITE",
|
||||
"TERRIER",
|
||||
"TETRA",
|
||||
"THRUSH",
|
||||
"THYLACINE",
|
||||
"TIGER",
|
||||
"TOAD",
|
||||
"TORTOISE",
|
||||
"TOUCAN",
|
||||
"TREADFIN",
|
||||
"TREVALLY",
|
||||
"TRIGGERFISH",
|
||||
"TROUT",
|
||||
"TUATARA",
|
||||
"TUNA",
|
||||
"TURKEY",
|
||||
"TURTLE",
|
||||
"URCHIN",
|
||||
"VIPER",
|
||||
"VULTURE",
|
||||
"WALLABY",
|
||||
"WALLAROO",
|
||||
"WALLEYE",
|
||||
"WALRUS",
|
||||
"WARTHOG",
|
||||
"WASP",
|
||||
"WATERBUCK",
|
||||
"JERBOA",
|
||||
"KITTEN",
|
||||
"ARGALI",
|
||||
"OX",
|
||||
"MARE",
|
||||
"FINCH",
|
||||
"BASILISK",
|
||||
"GOPHER",
|
||||
"HAMSTER",
|
||||
"CANARY",
|
||||
"WEASEL",
|
||||
"WEEVIL",
|
||||
"WEKA",
|
||||
"WHALE",
|
||||
"WILDCAT",
|
||||
"WILDEBEEST",
|
||||
"WOLF",
|
||||
"WOLFHOUND",
|
||||
"WOLVERINE",
|
||||
"WOMBAT",
|
||||
"WOODCHUCK",
|
||||
"ANACONDA",
|
||||
"WOODPECKER",
|
||||
"WORM",
|
||||
"WRASSE",
|
||||
"WYVERN",
|
||||
"YAK",
|
||||
"ZEBRA",
|
||||
"ZEBU",
|
||||
)
|
||||
|
||||
|
||||
|
||||
45
gen/tacan.py
45
gen/tacan.py
@@ -4,13 +4,37 @@ from enum import Enum
|
||||
from typing import Dict, Iterator, Set
|
||||
|
||||
|
||||
class TacanUsage(Enum):
|
||||
TransmitReceive = "transmit receive"
|
||||
AirToAir = "air to air"
|
||||
|
||||
|
||||
class TacanBand(Enum):
|
||||
X = "X"
|
||||
Y = "Y"
|
||||
|
||||
def range(self) -> Iterator["TacanChannel"]:
|
||||
"""Returns an iterator over the channels in this band."""
|
||||
return (TacanChannel(x, self) for x in range(1, 100))
|
||||
return (TacanChannel(x, self) for x in range(1, 126 + 1))
|
||||
|
||||
def valid_channels(self, usage: TacanUsage) -> Iterator["TacanChannel"]:
|
||||
for x in self.range():
|
||||
if x.number not in UNAVAILABLE[usage][self]:
|
||||
yield x
|
||||
|
||||
|
||||
# Avoid certain TACAN channels for various reasons
|
||||
# https://forums.eagle.ru/topic/276390-datalink-issue/
|
||||
UNAVAILABLE = {
|
||||
TacanUsage.TransmitReceive: {
|
||||
TacanBand.X: set(range(2, 30 + 1)) | set(range(47, 63 + 1)),
|
||||
TacanBand.Y: set(range(2, 30 + 1)) | set(range(64, 92 + 1)),
|
||||
},
|
||||
TacanUsage.AirToAir: {
|
||||
TacanBand.X: set(range(1, 36 + 1)) | set(range(64, 99 + 1)),
|
||||
TacanBand.Y: set(range(1, 36 + 1)) | set(range(64, 99 + 1)),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -41,25 +65,30 @@ class TacanRegistry:
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.allocated_channels: Set[TacanChannel] = set()
|
||||
self.band_allocators: Dict[TacanBand, Iterator[TacanChannel]] = {}
|
||||
self.allocators: Dict[TacanBand, Dict[TacanUsage, Iterator[TacanChannel]]] = {}
|
||||
|
||||
for band in TacanBand:
|
||||
self.band_allocators[band] = band.range()
|
||||
self.allocators[band] = {}
|
||||
for usage in TacanUsage:
|
||||
self.allocators[band][usage] = band.valid_channels(usage)
|
||||
|
||||
def alloc_for_band(self, band: TacanBand) -> TacanChannel:
|
||||
def alloc_for_band(
|
||||
self, band: TacanBand, intended_usage: TacanUsage
|
||||
) -> TacanChannel:
|
||||
"""Allocates a TACAN channel in the given band.
|
||||
|
||||
Args:
|
||||
band: The TACAN band to allocate a channel for.
|
||||
intended_usage: What the caller intends to use the tacan channel for.
|
||||
|
||||
Returns:
|
||||
A TACAN channel in the given band.
|
||||
|
||||
Raises:
|
||||
OutOfChannelsError: All channels compatible with the given radio are
|
||||
OutOfTacanChannelsError: All channels compatible with the given radio are
|
||||
already allocated.
|
||||
"""
|
||||
allocator = self.band_allocators[band]
|
||||
allocator = self.allocators[band][intended_usage]
|
||||
try:
|
||||
while (channel := next(allocator)) in self.allocated_channels:
|
||||
pass
|
||||
@@ -67,7 +96,7 @@ class TacanRegistry:
|
||||
except StopIteration:
|
||||
raise OutOfTacanChannelsError(band)
|
||||
|
||||
def reserve(self, channel: TacanChannel) -> None:
|
||||
def mark_unavailable(self, channel: TacanChannel) -> None:
|
||||
"""Reserves the given channel.
|
||||
|
||||
Reserving a channel ensures that it will not be allocated in the future.
|
||||
@@ -76,7 +105,7 @@ class TacanRegistry:
|
||||
channel: The channel to reserve.
|
||||
|
||||
Raises:
|
||||
ChannelInUseError: The given frequency is already in use.
|
||||
TacanChannelInUseError: The given channel is already in use.
|
||||
"""
|
||||
if channel in self.allocated_channels:
|
||||
raise TacanChannelInUseError(channel)
|
||||
|
||||
Reference in New Issue
Block a user