Add types for distance and speed.

Not converting all at once so I can prove the concept. After that we'll
want to cover all the cases where an int distance or speed is a part of
the save game (I've done one of them here with `Flight.alt`) so further
cleanups don't break save compat.

https://github.com/Khopa/dcs_liberation/issues/558
This commit is contained in:
Dan Albert 2020-11-20 13:57:53 -08:00
parent 44bc2d769b
commit 113947b9f0
11 changed files with 269 additions and 157 deletions

View File

@ -1,7 +1,7 @@
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from game.utils import nm_to_meter, feet_to_meter from game.utils import Distance, feet, nm_to_meter, feet_to_meter
@dataclass(frozen=True) @dataclass(frozen=True)
@ -15,13 +15,13 @@ class Doctrine:
strike_max_range: int strike_max_range: int
sead_max_range: int sead_max_range: int
rendezvous_altitude: int rendezvous_altitude: Distance
hold_distance: int hold_distance: int
push_distance: int push_distance: int
join_distance: int join_distance: int
split_distance: int split_distance: int
ingress_egress_distance: int ingress_egress_distance: int
ingress_altitude: int ingress_altitude: Distance
egress_altitude: int egress_altitude: int
min_patrol_altitude: int min_patrol_altitude: int
@ -47,13 +47,13 @@ MODERN_DOCTRINE = Doctrine(
antiship=True, antiship=True,
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(25000), rendezvous_altitude=feet(25000),
hold_distance=nm_to_meter(15), hold_distance=nm_to_meter(15),
push_distance=nm_to_meter(20), push_distance=nm_to_meter(20),
join_distance=nm_to_meter(20), join_distance=nm_to_meter(20),
split_distance=nm_to_meter(20), split_distance=nm_to_meter(20),
ingress_egress_distance=nm_to_meter(45), ingress_egress_distance=nm_to_meter(45),
ingress_altitude=feet_to_meter(20000), ingress_altitude=feet(20000),
egress_altitude=feet_to_meter(20000), egress_altitude=feet_to_meter(20000),
min_patrol_altitude=feet_to_meter(15000), min_patrol_altitude=feet_to_meter(15000),
max_patrol_altitude=feet_to_meter(33000), max_patrol_altitude=feet_to_meter(33000),
@ -75,13 +75,13 @@ COLDWAR_DOCTRINE = Doctrine(
antiship=True, antiship=True,
strike_max_range=1500000, strike_max_range=1500000,
sead_max_range=1500000, sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(22000), rendezvous_altitude=feet(22000),
hold_distance=nm_to_meter(10), hold_distance=nm_to_meter(10),
push_distance=nm_to_meter(10), push_distance=nm_to_meter(10),
join_distance=nm_to_meter(10), join_distance=nm_to_meter(10),
split_distance=nm_to_meter(10), split_distance=nm_to_meter(10),
ingress_egress_distance=nm_to_meter(30), ingress_egress_distance=nm_to_meter(30),
ingress_altitude=feet_to_meter(18000), ingress_altitude=feet(18000),
egress_altitude=feet_to_meter(18000), egress_altitude=feet_to_meter(18000),
min_patrol_altitude=feet_to_meter(10000), min_patrol_altitude=feet_to_meter(10000),
max_patrol_altitude=feet_to_meter(24000), max_patrol_altitude=feet_to_meter(24000),
@ -107,9 +107,9 @@ WWII_DOCTRINE = Doctrine(
push_distance=nm_to_meter(5), push_distance=nm_to_meter(5),
join_distance=nm_to_meter(5), join_distance=nm_to_meter(5),
split_distance=nm_to_meter(5), split_distance=nm_to_meter(5),
rendezvous_altitude=feet_to_meter(10000), rendezvous_altitude=feet(10000),
ingress_egress_distance=nm_to_meter(7), ingress_egress_distance=nm_to_meter(7),
ingress_altitude=feet_to_meter(8000), ingress_altitude=feet(8000),
egress_altitude=feet_to_meter(8000), egress_altitude=feet_to_meter(8000),
min_patrol_altitude=feet_to_meter(4000), min_patrol_altitude=feet_to_meter(4000),
max_patrol_altitude=feet_to_meter(15000), max_patrol_altitude=feet_to_meter(15000),

View File

@ -1,65 +1,18 @@
def meter_to_feet(value_in_meter: float) -> int: from __future__ import annotations
"""Converts meters to feets
:arg value_in_meter Value in meters import math
""" from dataclasses import dataclass
return int(3.28084 * value_in_meter) from typing import Union
METERS_TO_FEET = 3.28084
FEET_TO_METERS = 1 / METERS_TO_FEET
NM_TO_METERS = 1852
METERS_TO_NM = 1 / NM_TO_METERS
def feet_to_meter(value_in_feet: float) -> int: KNOTS_TO_KPH = 1.852
"""Converts feets to meters KPH_TO_KNOTS = 1 / KNOTS_TO_KPH
MS_TO_KPH = 3.6
:arg value_in_feet Value in feets KPH_TO_MS = 1 / MS_TO_KPH
"""
return int(value_in_feet / 3.28084)
def meter_to_nm(value_in_meter: float) -> int:
"""Converts meters to nautic miles
:arg value_in_meter Value in meters
"""
return int(value_in_meter / 1852)
def nm_to_meter(value_in_nm: float) -> int:
"""Converts nautic miles to meters
:arg value_in_nm Value in nautic miles
"""
return int(value_in_nm * 1852)
def knots_to_kph(value_in_knots: float) -> int:
"""Converts Knots to Kilometer Per Hour
:arg value_in_knots Knots
"""
return int(value_in_knots * 1.852)
def mps_to_knots(value_in_mps: float) -> int:
"""Converts Meters Per Second To Knots
:arg value_in_mps Meters Per Second
"""
return int(value_in_mps * 1.943)
def mps_to_kph(speed: float) -> int:
"""Converts meters per second to kilometers per hour.
:arg speed Speed in m/s.
"""
return int(speed * 3.6)
def kph_to_mps(speed: float) -> int:
"""Converts kilometers per hour to meters per second.
:arg speed Speed in KPH.
"""
return int(speed / 3.6)
def heading_sum(h, a) -> int: def heading_sum(h, a) -> int:
@ -71,5 +24,171 @@ def heading_sum(h, a) -> int:
else: else:
return h return h
def opposite_heading(h): def opposite_heading(h):
return heading_sum(h, 180) return heading_sum(h, 180)
def meter_to_feet(value: float) -> int:
return int(meters(value).feet)
def feet_to_meter(value: float) -> int:
return int(feet(value).meters)
def meter_to_nm(value: float) -> int:
return int(meters(value).nautical_miles)
def nm_to_meter(value: float) -> int:
return int(nautical_miles(value).meters)
def knots_to_kph(value: float) -> int:
return int(knots(value).kph)
def kph_to_mps(value: float) -> int:
return int(kph(value).meters_per_second)
def mps_to_kph(value: float) -> int:
return int(mps(value).kph)
def mps_to_knots(value: float) -> int:
return int(mps(value).knots)
@dataclass(frozen=True, order=True)
class Distance:
distance_in_meters: float
@property
def feet(self) -> float:
return self.distance_in_meters * METERS_TO_FEET
@property
def meters(self) -> float:
return self.distance_in_meters
@property
def nautical_miles(self) -> float:
return self.distance_in_meters * METERS_TO_NM
@classmethod
def from_feet(cls, value: float) -> Distance:
return cls(value * FEET_TO_METERS)
@classmethod
def from_meters(cls, value: float) -> Distance:
return cls(value)
@classmethod
def from_nautical_miles(cls, value: float) -> Distance:
return cls(value * NM_TO_METERS)
def __mul__(self, other: Union[float, int]) -> Distance:
return Distance(self.meters * other)
def __truediv__(self, other: Union[float, int]) -> Distance:
return Distance(self.meters / other)
def __floordiv__(self, other: Union[float, int]) -> Distance:
return Distance(self.meters // other)
def feet(value: float) -> Distance:
return Distance.from_feet(value)
def meters(value: float) -> Distance:
return Distance.from_meters(value)
def nautical_miles(value: float) -> Distance:
return Distance.from_nautical_miles(value)
@dataclass(frozen=True, order=True)
class Speed:
speed_in_kph: float
@property
def knots(self) -> float:
return self.speed_in_kph * KPH_TO_KNOTS
@property
def kph(self) -> float:
return self.speed_in_kph
@property
def meters_per_second(self) -> float:
return self.speed_in_kph * KPH_TO_MS
def mach(self, altitude: Distance = meters(0)) -> float:
c_sound = mach(1, altitude)
return self.speed_in_kph / c_sound.kph
@classmethod
def from_knots(cls, value: float) -> Speed:
return cls(value * KNOTS_TO_KPH)
@classmethod
def from_kph(cls, value: float) -> Speed:
return cls(value)
@classmethod
def from_meters_per_second(cls, value: float) -> Speed:
return cls(value * MS_TO_KPH)
@classmethod
def from_mach(cls, value: float, altitude: Distance) -> Speed:
# https://www.grc.nasa.gov/WWW/K-12/airplane/atmos.html
if altitude <= feet(36152):
temperature_f = 59 - 0.00356 * altitude.feet
else:
# There's another formula for altitudes over 82k feet, but we better
# not be planning waypoints that high...
temperature_f = -70
temperature_k = (temperature_f + 459.67) * (5 / 9)
# https://www.engineeringtoolbox.com/specific-heat-ratio-d_602.html
# Dependent on temperature, but varies very little (+/-0.001)
# between -40F and 180F.
heat_capacity_ratio = 1.4
# https://www.grc.nasa.gov/WWW/K-12/airplane/sound.html
gas_constant = 286 # m^2/s^2/K
c_sound = math.sqrt(heat_capacity_ratio * gas_constant * temperature_k)
return mps(c_sound) * value
def __mul__(self, other: Union[float, int]) -> Speed:
return Speed(self.kph * other)
def __truediv__(self, other: Union[float, int]) -> Speed:
return Speed(self.kph / other)
def __floordiv__(self, other: Union[float, int]) -> Speed:
return Speed(self.kph // other)
def knots(value: float) -> Speed:
return Speed.from_knots(value)
def kph(value: float) -> Speed:
return Speed.from_kph(value)
def mps(value: float) -> Speed:
return Speed.from_meters_per_second(value)
def mach(value: float, altitude: Distance) -> Speed:
return Speed.from_mach(value, altitude)
SPEED_OF_SOUND_AT_SEA_LEVEL = knots(661.5)

View File

@ -85,7 +85,7 @@ from game.theater.controlpoint import (
) )
from game.theater.theatergroundobject import TheaterGroundObject from game.theater.theatergroundobject import TheaterGroundObject
from game.unitmap import UnitMap from game.unitmap import UnitMap
from game.utils import knots_to_kph, nm_to_meter from game.utils import Distance, Speed, knots_to_kph, kph, meters, nm_to_meter
from gen.airsupportgen import AirSupport from gen.airsupportgen import AirSupport
from gen.ato import AirTaskingOrder, Package from gen.ato import AirTaskingOrder, Package
from gen.callsigns import create_group_callsign_from_unit from gen.callsigns import create_group_callsign_from_unit
@ -110,12 +110,12 @@ from .naming import namegen
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
WARM_START_HELI_AIRSPEED = 120 WARM_START_HELI_AIRSPEED = kph(120)
WARM_START_HELI_ALT = 500 WARM_START_HELI_ALT = meters(500)
WARM_START_ALTITUDE = 3000 WARM_START_ALTITUDE = meters(3000)
WARM_START_AIRSPEED = 550 WARM_START_AIRSPEED = kph(550)
RTB_ALTITUDE = 800 RTB_ALTITUDE = meters(800)
RTB_DISTANCE = 5000 RTB_DISTANCE = 5000
HELI_ALT = 500 HELI_ALT = 500
@ -867,7 +867,9 @@ class AircraftConflictGenerator:
start_type=self._start_type(start_type), start_type=self._start_type(start_type),
group_size=count) group_size=count)
def _add_radio_waypoint(self, group: FlyingGroup, position, altitude: int, airspeed: int = 600): def _add_radio_waypoint(self, group: FlyingGroup, position,
altitude: Distance,
airspeed: int = 600) -> MovingPoint:
point = group.add_waypoint(position, altitude, airspeed) point = group.add_waypoint(position, altitude, airspeed)
point.alt_type = "RADIO" point.alt_type = "RADIO"
return point return point
@ -884,7 +886,8 @@ class AircraftConflictGenerator:
tod_location = position.point_from_heading(heading, RTB_DISTANCE) tod_location = position.point_from_heading(heading, RTB_DISTANCE)
self._add_radio_waypoint(group, tod_location, last_waypoint.alt) self._add_radio_waypoint(group, tod_location, last_waypoint.alt)
destination_waypoint = self._add_radio_waypoint(group, position, RTB_ALTITUDE) destination_waypoint = self._add_radio_waypoint(group, position,
RTB_ALTITUDE)
if isinstance(at, Airport): if isinstance(at, Airport):
group.land_at(at) group.land_at(at)
return destination_waypoint return destination_waypoint
@ -1380,7 +1383,8 @@ class PydcsWaypointBuilder:
def build(self) -> MovingPoint: def build(self) -> MovingPoint:
waypoint = self.group.add_waypoint( waypoint = self.group.add_waypoint(
Point(self.waypoint.x, self.waypoint.y), self.waypoint.alt, Point(self.waypoint.x, self.waypoint.y),
self.waypoint.alt.meters,
name=self.mission.string(self.waypoint.name)) name=self.mission.string(self.waypoint.name))
if self.waypoint.flyover: if self.waypoint.flyover:

View File

@ -10,6 +10,7 @@ from dcs.unittype import FlyingType
from game import db from game import db
from game.theater.controlpoint import ControlPoint, MissionTarget from game.theater.controlpoint import ControlPoint, MissionTarget
from game.utils import Distance, meters
if TYPE_CHECKING: if TYPE_CHECKING:
from gen.ato import Package from gen.ato import Package
@ -67,7 +68,7 @@ class FlightWaypointType(Enum):
class FlightWaypoint: class FlightWaypoint:
def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float, def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float,
alt: int = 0) -> None: alt: Distance = meters(0)) -> None:
"""Creates a flight waypoint. """Creates a flight waypoint.
Args: Args:

View File

@ -28,7 +28,7 @@ from game.theater import (
TheaterGroundObject, TheaterGroundObject,
) )
from game.theater.theatergroundobject import EwrGroundObject from game.theater.theatergroundobject import EwrGroundObject
from game.utils import nm_to_meter, meter_to_nm from game.utils import Distance, meters, nm_to_meter, meter_to_nm
from .closestairfields import ObjectiveDistanceCache from .closestairfields import ObjectiveDistanceCache
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
from .traveltime import GroundSpeed, TravelTime from .traveltime import GroundSpeed, TravelTime
@ -537,7 +537,8 @@ class StrikeFlightPlan(FormationFlightPlan):
def target_area_waypoint(self) -> FlightWaypoint: def target_area_waypoint(self) -> FlightWaypoint:
return FlightWaypoint(FlightWaypointType.TARGET_GROUP_LOC, return FlightWaypoint(FlightWaypointType.TARGET_GROUP_LOC,
self.package.target.position.x, self.package.target.position.x,
self.package.target.position.y, 0) self.package.target.position.y,
meters(0))
@property @property
def travel_time_to_target(self) -> timedelta: def travel_time_to_target(self) -> timedelta:
@ -863,10 +864,10 @@ class FlightPlanBuilder:
raise InvalidObjectiveLocation(flight.flight_type, location) raise InvalidObjectiveLocation(flight.flight_type, location)
start, end = self.racetrack_for_objective(location) start, end = self.racetrack_for_objective(location)
patrol_alt = random.randint( patrol_alt = meters(random.randint(
self.doctrine.min_patrol_altitude, self.doctrine.min_patrol_altitude,
self.doctrine.max_patrol_altitude self.doctrine.max_patrol_altitude
) ))
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine) builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
start, end = builder.race_track(start, end, patrol_alt) start, end = builder.race_track(start, end, patrol_alt)
@ -983,8 +984,8 @@ class FlightPlanBuilder:
""" """
location = self.package.target location = self.package.target
patrol_alt = random.randint(self.doctrine.min_patrol_altitude, patrol_alt = meters(random.randint(self.doctrine.min_patrol_altitude,
self.doctrine.max_patrol_altitude) self.doctrine.max_patrol_altitude))
# Create points # Create points
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine) builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)

View File

@ -8,7 +8,13 @@ from typing import Optional, TYPE_CHECKING
from dcs.mapping import Point from dcs.mapping import Point
from dcs.unittype import FlyingType from dcs.unittype import FlyingType
from game.utils import meter_to_nm from game.utils import (
Distance,
SPEED_OF_SOUND_AT_SEA_LEVEL,
Speed,
kph, mach, meter_to_nm,
meters,
)
from gen.flights.flight import Flight from gen.flights.flight import Flight
if TYPE_CHECKING: if TYPE_CHECKING:
@ -18,7 +24,7 @@ if TYPE_CHECKING:
class GroundSpeed: class GroundSpeed:
@classmethod @classmethod
def for_flight(cls, flight: Flight, altitude: int) -> int: def for_flight(cls, flight: Flight, altitude: Distance) -> int:
if not issubclass(flight.unit_type, FlyingType): if not issubclass(flight.unit_type, FlyingType):
raise TypeError("Flight has non-flying unit") raise TypeError("Flight has non-flying unit")
@ -27,56 +33,20 @@ class GroundSpeed:
# on fuel, but mission speed will be fast enough to keep the flight # on fuel, but mission speed will be fast enough to keep the flight
# safer. # safer.
c_sound_sea_level = 661.5 # DCS's max speed is in kph at 0 MSL.
max_speed = kph(flight.unit_type.max_speed)
# DCS's max speed is in kph at 0 MSL. Convert to knots. if max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL:
max_speed = flight.unit_type.max_speed * 0.539957
if max_speed > c_sound_sea_level:
# Aircraft is supersonic. Limit to mach 0.8 to conserve fuel and # Aircraft is supersonic. Limit to mach 0.8 to conserve fuel and
# account for heavily loaded jets. # account for heavily loaded jets.
return int(cls.from_mach(0.8, altitude)) return int(mach(0.8, altitude).knots)
# For subsonic aircraft, assume the aircraft can reasonably perform at # For subsonic aircraft, assume the aircraft can reasonably perform at
# 80% of its maximum, and that it can maintain the same mach at altitude # 80% of its maximum, and that it can maintain the same mach at altitude
# as it can at sea level. This probably isn't great assumption, but # as it can at sea level. This probably isn't great assumption, but
# might. be sufficient given the wiggle room. We can come up with # might. be sufficient given the wiggle room. We can come up with
# another heuristic if needed. # another heuristic if needed.
mach = max_speed * 0.8 / c_sound_sea_level cruise_mach = max_speed.mach() * 0.8
return int(cls.from_mach(mach, altitude)) # knots return int(mach(cruise_mach, altitude).knots)
@staticmethod
def from_mach(mach: float, altitude_m: int) -> float:
"""Returns the ground speed in knots for the given mach and altitude.
Args:
mach: The mach number to convert to ground speed.
altitude_m: The altitude in meters.
Returns:
The ground speed corresponding to the given altitude and mach number
in knots.
"""
# https://www.grc.nasa.gov/WWW/K-12/airplane/atmos.html
altitude_ft = altitude_m * 3.28084
if altitude_ft <= 36152:
temperature_f = 59 - 0.00356 * altitude_ft
else:
# There's another formula for altitudes over 82k feet, but we better
# not be planning waypoints that high...
temperature_f = -70
temperature_k = (temperature_f + 459.67) * (5 / 9)
# https://www.engineeringtoolbox.com/specific-heat-ratio-d_602.html
# Dependent on temperature, but varies very little (+/-0.001)
# between -40F and 180F.
heat_capacity_ratio = 1.4
# https://www.grc.nasa.gov/WWW/K-12/airplane/sound.html
gas_constant = 286 # m^2/s^2/K
c_sound = math.sqrt(heat_capacity_ratio * gas_constant * temperature_k)
# c_sound is in m/s, convert to knots.
return (c_sound * 1.944) * mach
class TravelTime: class TravelTime:

View File

@ -14,6 +14,7 @@ from game.theater import (
OffMapSpawn, OffMapSpawn,
TheaterGroundObject, TheaterGroundObject,
) )
from game.utils import Distance, meters
from game.weather import Conditions from game.weather import Conditions
from .flight import Flight, FlightWaypoint, FlightWaypointType from .flight import Flight, FlightWaypoint, FlightWaypointType
@ -53,7 +54,9 @@ class WaypointBuilder:
FlightWaypointType.NAV, FlightWaypointType.NAV,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.rendezvous_altitude meters(
500
) if self.is_helo else self.doctrine.rendezvous_altitude
) )
waypoint.name = "NAV" waypoint.name = "NAV"
waypoint.alt_type = "BARO" waypoint.alt_type = "BARO"
@ -64,7 +67,7 @@ class WaypointBuilder:
FlightWaypointType.TAKEOFF, FlightWaypointType.TAKEOFF,
position.x, position.x,
position.y, position.y,
0 meters(0)
) )
waypoint.name = "TAKEOFF" waypoint.name = "TAKEOFF"
waypoint.alt_type = "RADIO" waypoint.alt_type = "RADIO"
@ -84,7 +87,9 @@ class WaypointBuilder:
FlightWaypointType.NAV, FlightWaypointType.NAV,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.rendezvous_altitude meters(
500
) if self.is_helo else self.doctrine.rendezvous_altitude
) )
waypoint.name = "NAV" waypoint.name = "NAV"
waypoint.alt_type = "BARO" waypoint.alt_type = "BARO"
@ -95,7 +100,7 @@ class WaypointBuilder:
FlightWaypointType.LANDING_POINT, FlightWaypointType.LANDING_POINT,
position.x, position.x,
position.y, position.y,
0 meters(0)
) )
waypoint.name = "LANDING" waypoint.name = "LANDING"
waypoint.alt_type = "RADIO" waypoint.alt_type = "RADIO"
@ -116,12 +121,12 @@ class WaypointBuilder:
position = divert.position position = divert.position
if isinstance(divert, OffMapSpawn): if isinstance(divert, OffMapSpawn):
if self.is_helo: if self.is_helo:
altitude = 500 altitude = meters(500)
else: else:
altitude = self.doctrine.rendezvous_altitude altitude = self.doctrine.rendezvous_altitude
altitude_type = "BARO" altitude_type = "BARO"
else: else:
altitude = 0 altitude = meters(0)
altitude_type = "RADIO" altitude_type = "RADIO"
waypoint = FlightWaypoint( waypoint = FlightWaypoint(
@ -142,7 +147,9 @@ class WaypointBuilder:
FlightWaypointType.LOITER, FlightWaypointType.LOITER,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.rendezvous_altitude meters(
500
) if self.is_helo else self.doctrine.rendezvous_altitude
) )
waypoint.pretty_name = "Hold" waypoint.pretty_name = "Hold"
waypoint.description = "Wait until push time" waypoint.description = "Wait until push time"
@ -154,7 +161,9 @@ class WaypointBuilder:
FlightWaypointType.JOIN, FlightWaypointType.JOIN,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.ingress_altitude meters(
500
) if self.is_helo else self.doctrine.ingress_altitude
) )
waypoint.pretty_name = "Join" waypoint.pretty_name = "Join"
waypoint.description = "Rendezvous with package" waypoint.description = "Rendezvous with package"
@ -166,7 +175,9 @@ class WaypointBuilder:
FlightWaypointType.SPLIT, FlightWaypointType.SPLIT,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.ingress_altitude meters(
500
) if self.is_helo else self.doctrine.ingress_altitude
) )
waypoint.pretty_name = "Split" waypoint.pretty_name = "Split"
waypoint.description = "Depart from package" waypoint.description = "Depart from package"
@ -179,7 +190,9 @@ class WaypointBuilder:
ingress_type, ingress_type,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.ingress_altitude meters(
500
) if self.is_helo else self.doctrine.ingress_altitude
) )
waypoint.pretty_name = "INGRESS on " + objective.name waypoint.pretty_name = "INGRESS on " + objective.name
waypoint.description = "INGRESS on " + objective.name waypoint.description = "INGRESS on " + objective.name
@ -193,7 +206,9 @@ class WaypointBuilder:
FlightWaypointType.EGRESS, FlightWaypointType.EGRESS,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else self.doctrine.ingress_altitude meters(
500
) if self.is_helo else self.doctrine.ingress_altitude
) )
waypoint.pretty_name = "EGRESS from " + target.name waypoint.pretty_name = "EGRESS from " + target.name
waypoint.description = "EGRESS from " + target.name waypoint.description = "EGRESS from " + target.name
@ -218,7 +233,7 @@ class WaypointBuilder:
FlightWaypointType.TARGET_POINT, FlightWaypointType.TARGET_POINT,
target.target.position.x, target.target.position.x,
target.target.position.y, target.target.position.y,
0 meters(0)
) )
waypoint.description = description waypoint.description = description
waypoint.pretty_name = description waypoint.pretty_name = description
@ -249,7 +264,7 @@ class WaypointBuilder:
FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_GROUP_LOC,
location.position.x, location.position.x,
location.position.y, location.position.y,
0 meters(0)
) )
waypoint.description = name waypoint.description = name
waypoint.pretty_name = name waypoint.pretty_name = name
@ -274,7 +289,7 @@ class WaypointBuilder:
FlightWaypointType.CAS, FlightWaypointType.CAS,
position.x, position.x,
position.y, position.y,
500 if self.is_helo else 1000 meters(500) if self.is_helo else meters(1000)
) )
waypoint.alt_type = "RADIO" waypoint.alt_type = "RADIO"
waypoint.description = "Provide CAS" waypoint.description = "Provide CAS"
@ -283,12 +298,12 @@ class WaypointBuilder:
return waypoint return waypoint
@staticmethod @staticmethod
def race_track_start(position: Point, altitude: int) -> FlightWaypoint: def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint:
"""Creates a racetrack start waypoint. """Creates a racetrack start waypoint.
Args: Args:
position: Position of the waypoint. position: Position of the waypoint.
altitude: Altitude of the racetrack in meters. altitude: Altitude of the racetrack.
""" """
waypoint = FlightWaypoint( waypoint = FlightWaypoint(
FlightWaypointType.PATROL_TRACK, FlightWaypointType.PATROL_TRACK,
@ -302,12 +317,12 @@ class WaypointBuilder:
return waypoint return waypoint
@staticmethod @staticmethod
def race_track_end(position: Point, altitude: int) -> FlightWaypoint: def race_track_end(position: Point, altitude: Distance) -> FlightWaypoint:
"""Creates a racetrack end waypoint. """Creates a racetrack end waypoint.
Args: Args:
position: Position of the waypoint. position: Position of the waypoint.
altitude: Altitude of the racetrack in meters. altitude: Altitude of the racetrack.
""" """
waypoint = FlightWaypoint( waypoint = FlightWaypoint(
FlightWaypointType.PATROL, FlightWaypointType.PATROL,
@ -321,7 +336,7 @@ class WaypointBuilder:
return waypoint return waypoint
def race_track(self, start: Point, end: Point, def race_track(self, start: Point, end: Point,
altitude: int) -> Tuple[FlightWaypoint, FlightWaypoint]: altitude: Distance) -> Tuple[FlightWaypoint, FlightWaypoint]:
"""Creates two waypoint for a racetrack orbit. """Creates two waypoint for a racetrack orbit.
Args: Args:
@ -333,7 +348,7 @@ class WaypointBuilder:
self.race_track_end(end, altitude)) self.race_track_end(end, altitude))
@staticmethod @staticmethod
def sweep_start(position: Point, altitude: int) -> FlightWaypoint: def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint:
"""Creates a sweep start waypoint. """Creates a sweep start waypoint.
Args: Args:
@ -352,7 +367,7 @@ class WaypointBuilder:
return waypoint return waypoint
@staticmethod @staticmethod
def sweep_end(position: Point, altitude: int) -> FlightWaypoint: def sweep_end(position: Point, altitude: Distance) -> FlightWaypoint:
"""Creates a sweep end waypoint. """Creates a sweep end waypoint.
Args: Args:
@ -371,7 +386,7 @@ class WaypointBuilder:
return waypoint return waypoint
def sweep(self, start: Point, end: Point, def sweep(self, start: Point, end: Point,
altitude: int) -> Tuple[FlightWaypoint, FlightWaypoint]: altitude: Distance) -> Tuple[FlightWaypoint, FlightWaypoint]:
"""Creates two waypoint for a racetrack orbit. """Creates two waypoint for a racetrack orbit.
Args: Args:
@ -404,7 +419,9 @@ class WaypointBuilder:
FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_GROUP_LOC,
target.position.x, target.position.x,
target.position.y, target.position.y,
500 if self.is_helo else self.doctrine.ingress_altitude meters(
500
) if self.is_helo else self.doctrine.ingress_altitude
) )
waypoint.name = "TARGET" waypoint.name = "TARGET"
waypoint.description = "Escort the package" waypoint.description = "Escort the package"

View File

@ -153,7 +153,7 @@ class FlightPlanBuilder:
self.rows.append([ self.rows.append([
str(waypoint.number), str(waypoint.number),
waypoint.waypoint.pretty_name, waypoint.waypoint.pretty_name,
str(int(units.meters_to_feet(waypoint.waypoint.alt))), str(int(waypoint.waypoint.alt.feet)),
self._waypoint_distance(waypoint.waypoint), self._waypoint_distance(waypoint.waypoint),
self._ground_speed(waypoint.waypoint), self._ground_speed(waypoint.waypoint),
self._format_time(waypoint.waypoint.tot), self._format_time(waypoint.waypoint.tot),

View File

@ -441,7 +441,7 @@ class QLiberationMap(QGraphicsView):
waypoint: FlightWaypoint, position: Tuple[int, int], waypoint: FlightWaypoint, position: Tuple[int, int],
flight_plan: FlightPlan) -> None: flight_plan: FlightPlan) -> None:
altitude = meter_to_feet(waypoint.alt) altitude = int(waypoint.alt.feet)
altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL" altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL"
prefix = "TOT" prefix = "TOT"

View File

@ -12,7 +12,7 @@ class QFlightWaypointInfoBox(QGroupBox):
self.flight_wpt = FlightWaypoint(0,0,0) self.flight_wpt = FlightWaypoint(0,0,0)
self.x_position_label = QLabel(str(self.flight_wpt.x)) self.x_position_label = QLabel(str(self.flight_wpt.x))
self.y_position_label = QLabel(str(self.flight_wpt.y)) self.y_position_label = QLabel(str(self.flight_wpt.y))
self.alt_label = QLabel(str(self.flight_wpt.alt)) self.alt_label = QLabel(str(int(self.flight_wpt.alt.feet)))
self.name_label = QLabel(str(self.flight_wpt.name)) self.name_label = QLabel(str(self.flight_wpt.name))
self.desc_label = QLabel(str(self.flight_wpt.description)) self.desc_label = QLabel(str(self.flight_wpt.description))
self.init_ui() self.init_ui()
@ -60,7 +60,7 @@ class QFlightWaypointInfoBox(QGroupBox):
self.flight_wpt = FlightWaypoint(0,0,0) self.flight_wpt = FlightWaypoint(0,0,0)
self.x_position_label.setText(str(self.flight_wpt.x)) self.x_position_label.setText(str(self.flight_wpt.x))
self.y_position_label.setText(str(self.flight_wpt.y)) self.y_position_label.setText(str(self.flight_wpt.y))
self.alt_label.setText(str(self.flight_wpt.alt)) self.alt_label.setText(str(int(self.flight_wpt.alt.feet)))
self.name_label.setText(str(self.flight_wpt.name)) self.name_label.setText(str(self.flight_wpt.name))
self.desc_label.setText(str(self.flight_wpt.description)) self.desc_label.setText(str(self.flight_wpt.description))
self.setTitle(self.flight_wpt.name) self.setTitle(self.flight_wpt.name)

View File

@ -49,7 +49,7 @@ class QFlightWaypointList(QTableView):
self.model.setItem(row, 0, QWaypointItem(waypoint, row)) self.model.setItem(row, 0, QWaypointItem(waypoint, row))
altitude = meter_to_feet(waypoint.alt) altitude = int(waypoint.alt.feet)
altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL" altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL"
altitude_item = QStandardItem(f"{altitude} ft {altitude_type}") altitude_item = QStandardItem(f"{altitude} ft {altitude_type}")
altitude_item.setEditable(False) altitude_item.setEditable(False)