mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
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:
@@ -85,7 +85,7 @@ from game.theater.controlpoint import (
|
||||
)
|
||||
from game.theater.theatergroundobject import TheaterGroundObject
|
||||
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.ato import AirTaskingOrder, Package
|
||||
from gen.callsigns import create_group_callsign_from_unit
|
||||
@@ -110,12 +110,12 @@ from .naming import namegen
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
|
||||
WARM_START_HELI_AIRSPEED = 120
|
||||
WARM_START_HELI_ALT = 500
|
||||
WARM_START_ALTITUDE = 3000
|
||||
WARM_START_AIRSPEED = 550
|
||||
WARM_START_HELI_AIRSPEED = kph(120)
|
||||
WARM_START_HELI_ALT = meters(500)
|
||||
WARM_START_ALTITUDE = meters(3000)
|
||||
WARM_START_AIRSPEED = kph(550)
|
||||
|
||||
RTB_ALTITUDE = 800
|
||||
RTB_ALTITUDE = meters(800)
|
||||
RTB_DISTANCE = 5000
|
||||
HELI_ALT = 500
|
||||
|
||||
@@ -867,7 +867,9 @@ class AircraftConflictGenerator:
|
||||
start_type=self._start_type(start_type),
|
||||
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.alt_type = "RADIO"
|
||||
return point
|
||||
@@ -884,7 +886,8 @@ class AircraftConflictGenerator:
|
||||
tod_location = position.point_from_heading(heading, RTB_DISTANCE)
|
||||
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):
|
||||
group.land_at(at)
|
||||
return destination_waypoint
|
||||
@@ -1380,7 +1383,8 @@ class PydcsWaypointBuilder:
|
||||
|
||||
def build(self) -> MovingPoint:
|
||||
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))
|
||||
|
||||
if self.waypoint.flyover:
|
||||
|
||||
@@ -10,6 +10,7 @@ from dcs.unittype import FlyingType
|
||||
|
||||
from game import db
|
||||
from game.theater.controlpoint import ControlPoint, MissionTarget
|
||||
from game.utils import Distance, meters
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from gen.ato import Package
|
||||
@@ -67,7 +68,7 @@ class FlightWaypointType(Enum):
|
||||
class FlightWaypoint:
|
||||
|
||||
def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float,
|
||||
alt: int = 0) -> None:
|
||||
alt: Distance = meters(0)) -> None:
|
||||
"""Creates a flight waypoint.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -28,7 +28,7 @@ from game.theater import (
|
||||
TheaterGroundObject,
|
||||
)
|
||||
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 .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||
from .traveltime import GroundSpeed, TravelTime
|
||||
@@ -537,7 +537,8 @@ class StrikeFlightPlan(FormationFlightPlan):
|
||||
def target_area_waypoint(self) -> FlightWaypoint:
|
||||
return FlightWaypoint(FlightWaypointType.TARGET_GROUP_LOC,
|
||||
self.package.target.position.x,
|
||||
self.package.target.position.y, 0)
|
||||
self.package.target.position.y,
|
||||
meters(0))
|
||||
|
||||
@property
|
||||
def travel_time_to_target(self) -> timedelta:
|
||||
@@ -863,10 +864,10 @@ class FlightPlanBuilder:
|
||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||
|
||||
start, end = self.racetrack_for_objective(location)
|
||||
patrol_alt = random.randint(
|
||||
patrol_alt = meters(random.randint(
|
||||
self.doctrine.min_patrol_altitude,
|
||||
self.doctrine.max_patrol_altitude
|
||||
)
|
||||
))
|
||||
|
||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||
start, end = builder.race_track(start, end, patrol_alt)
|
||||
@@ -983,8 +984,8 @@ class FlightPlanBuilder:
|
||||
"""
|
||||
location = self.package.target
|
||||
|
||||
patrol_alt = random.randint(self.doctrine.min_patrol_altitude,
|
||||
self.doctrine.max_patrol_altitude)
|
||||
patrol_alt = meters(random.randint(self.doctrine.min_patrol_altitude,
|
||||
self.doctrine.max_patrol_altitude))
|
||||
|
||||
# Create points
|
||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||
|
||||
@@ -8,7 +8,13 @@ from typing import Optional, TYPE_CHECKING
|
||||
from dcs.mapping import Point
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -18,7 +24,7 @@ if TYPE_CHECKING:
|
||||
class GroundSpeed:
|
||||
|
||||
@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):
|
||||
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
|
||||
# safer.
|
||||
|
||||
c_sound_sea_level = 661.5
|
||||
|
||||
# DCS's max speed is in kph at 0 MSL. Convert to knots.
|
||||
max_speed = flight.unit_type.max_speed * 0.539957
|
||||
if max_speed > c_sound_sea_level:
|
||||
# DCS's max speed is in kph at 0 MSL.
|
||||
max_speed = kph(flight.unit_type.max_speed)
|
||||
if max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL:
|
||||
# Aircraft is supersonic. Limit to mach 0.8 to conserve fuel and
|
||||
# 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
|
||||
# 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
|
||||
# might. be sufficient given the wiggle room. We can come up with
|
||||
# another heuristic if needed.
|
||||
mach = max_speed * 0.8 / c_sound_sea_level
|
||||
return int(cls.from_mach(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
|
||||
cruise_mach = max_speed.mach() * 0.8
|
||||
return int(mach(cruise_mach, altitude).knots)
|
||||
|
||||
|
||||
class TravelTime:
|
||||
|
||||
@@ -14,6 +14,7 @@ from game.theater import (
|
||||
OffMapSpawn,
|
||||
TheaterGroundObject,
|
||||
)
|
||||
from game.utils import Distance, meters
|
||||
from game.weather import Conditions
|
||||
from .flight import Flight, FlightWaypoint, FlightWaypointType
|
||||
|
||||
@@ -53,7 +54,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.NAV,
|
||||
position.x,
|
||||
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.alt_type = "BARO"
|
||||
@@ -64,7 +67,7 @@ class WaypointBuilder:
|
||||
FlightWaypointType.TAKEOFF,
|
||||
position.x,
|
||||
position.y,
|
||||
0
|
||||
meters(0)
|
||||
)
|
||||
waypoint.name = "TAKEOFF"
|
||||
waypoint.alt_type = "RADIO"
|
||||
@@ -84,7 +87,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.NAV,
|
||||
position.x,
|
||||
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.alt_type = "BARO"
|
||||
@@ -95,7 +100,7 @@ class WaypointBuilder:
|
||||
FlightWaypointType.LANDING_POINT,
|
||||
position.x,
|
||||
position.y,
|
||||
0
|
||||
meters(0)
|
||||
)
|
||||
waypoint.name = "LANDING"
|
||||
waypoint.alt_type = "RADIO"
|
||||
@@ -116,12 +121,12 @@ class WaypointBuilder:
|
||||
position = divert.position
|
||||
if isinstance(divert, OffMapSpawn):
|
||||
if self.is_helo:
|
||||
altitude = 500
|
||||
altitude = meters(500)
|
||||
else:
|
||||
altitude = self.doctrine.rendezvous_altitude
|
||||
altitude_type = "BARO"
|
||||
else:
|
||||
altitude = 0
|
||||
altitude = meters(0)
|
||||
altitude_type = "RADIO"
|
||||
|
||||
waypoint = FlightWaypoint(
|
||||
@@ -142,7 +147,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.LOITER,
|
||||
position.x,
|
||||
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.description = "Wait until push time"
|
||||
@@ -154,7 +161,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.JOIN,
|
||||
position.x,
|
||||
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.description = "Rendezvous with package"
|
||||
@@ -166,7 +175,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.SPLIT,
|
||||
position.x,
|
||||
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.description = "Depart from package"
|
||||
@@ -179,7 +190,9 @@ class WaypointBuilder:
|
||||
ingress_type,
|
||||
position.x,
|
||||
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.description = "INGRESS on " + objective.name
|
||||
@@ -193,7 +206,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.EGRESS,
|
||||
position.x,
|
||||
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.description = "EGRESS from " + target.name
|
||||
@@ -218,7 +233,7 @@ class WaypointBuilder:
|
||||
FlightWaypointType.TARGET_POINT,
|
||||
target.target.position.x,
|
||||
target.target.position.y,
|
||||
0
|
||||
meters(0)
|
||||
)
|
||||
waypoint.description = description
|
||||
waypoint.pretty_name = description
|
||||
@@ -249,7 +264,7 @@ class WaypointBuilder:
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
location.position.x,
|
||||
location.position.y,
|
||||
0
|
||||
meters(0)
|
||||
)
|
||||
waypoint.description = name
|
||||
waypoint.pretty_name = name
|
||||
@@ -274,7 +289,7 @@ class WaypointBuilder:
|
||||
FlightWaypointType.CAS,
|
||||
position.x,
|
||||
position.y,
|
||||
500 if self.is_helo else 1000
|
||||
meters(500) if self.is_helo else meters(1000)
|
||||
)
|
||||
waypoint.alt_type = "RADIO"
|
||||
waypoint.description = "Provide CAS"
|
||||
@@ -283,12 +298,12 @@ class WaypointBuilder:
|
||||
return waypoint
|
||||
|
||||
@staticmethod
|
||||
def race_track_start(position: Point, altitude: int) -> FlightWaypoint:
|
||||
def race_track_start(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||
"""Creates a racetrack start waypoint.
|
||||
|
||||
Args:
|
||||
position: Position of the waypoint.
|
||||
altitude: Altitude of the racetrack in meters.
|
||||
altitude: Altitude of the racetrack.
|
||||
"""
|
||||
waypoint = FlightWaypoint(
|
||||
FlightWaypointType.PATROL_TRACK,
|
||||
@@ -302,12 +317,12 @@ class WaypointBuilder:
|
||||
return waypoint
|
||||
|
||||
@staticmethod
|
||||
def race_track_end(position: Point, altitude: int) -> FlightWaypoint:
|
||||
def race_track_end(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||
"""Creates a racetrack end waypoint.
|
||||
|
||||
Args:
|
||||
position: Position of the waypoint.
|
||||
altitude: Altitude of the racetrack in meters.
|
||||
altitude: Altitude of the racetrack.
|
||||
"""
|
||||
waypoint = FlightWaypoint(
|
||||
FlightWaypointType.PATROL,
|
||||
@@ -321,7 +336,7 @@ class WaypointBuilder:
|
||||
return waypoint
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
@@ -333,7 +348,7 @@ class WaypointBuilder:
|
||||
self.race_track_end(end, altitude))
|
||||
|
||||
@staticmethod
|
||||
def sweep_start(position: Point, altitude: int) -> FlightWaypoint:
|
||||
def sweep_start(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||
"""Creates a sweep start waypoint.
|
||||
|
||||
Args:
|
||||
@@ -352,7 +367,7 @@ class WaypointBuilder:
|
||||
return waypoint
|
||||
|
||||
@staticmethod
|
||||
def sweep_end(position: Point, altitude: int) -> FlightWaypoint:
|
||||
def sweep_end(position: Point, altitude: Distance) -> FlightWaypoint:
|
||||
"""Creates a sweep end waypoint.
|
||||
|
||||
Args:
|
||||
@@ -371,7 +386,7 @@ class WaypointBuilder:
|
||||
return waypoint
|
||||
|
||||
def sweep(self, start: Point, end: Point,
|
||||
altitude: int) -> Tuple[FlightWaypoint, FlightWaypoint]:
|
||||
altitude: Distance) -> Tuple[FlightWaypoint, FlightWaypoint]:
|
||||
"""Creates two waypoint for a racetrack orbit.
|
||||
|
||||
Args:
|
||||
@@ -404,7 +419,9 @@ class WaypointBuilder:
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
target.position.x,
|
||||
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.description = "Escort the package"
|
||||
|
||||
@@ -153,7 +153,7 @@ class FlightPlanBuilder:
|
||||
self.rows.append([
|
||||
str(waypoint.number),
|
||||
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._ground_speed(waypoint.waypoint),
|
||||
self._format_time(waypoint.waypoint.tot),
|
||||
|
||||
Reference in New Issue
Block a user