Use more sensible patrol speeds for CAP, and fix is_helo (#1492)

* Use more sensible patrol speeds for CAP, and fix is_helo
This commit is contained in:
Magnus Wolffelt 2021-08-03 12:22:55 +02:00 committed by GitHub
parent 9121cf7ecb
commit 30801dff9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 21 deletions

View File

@ -29,7 +29,15 @@ from game.radio.channels import (
ViggenRadioChannelAllocator,
NoOpChannelAllocator,
)
from game.utils import Distance, Speed, feet, kph, knots, nautical_miles
from game.utils import (
Distance,
SPEED_OF_SOUND_AT_SEA_LEVEL,
Speed,
feet,
kph,
knots,
nautical_miles,
)
if TYPE_CHECKING:
from gen.aircraft import FlightData
@ -182,7 +190,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
@property
def preferred_patrol_altitude(self) -> Distance:
if self.patrol_altitude:
if self.patrol_altitude is not None:
return self.patrol_altitude
else:
# Estimate based on max speed.
@ -208,6 +216,40 @@ class AircraftType(UnitType[Type[FlyingType]]):
min(altitude_for_highest_speed, rounded_altitude),
)
def preferred_patrol_speed(self, altitude: Distance) -> Speed:
"""Preferred true airspeed when patrolling"""
if self.patrol_speed is not None:
return self.patrol_speed
else:
# Estimate based on max speed.
max_speed = self.max_speed
if max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL * 1.6:
# Fast airplanes, should manage pretty high patrol speed
return (
Speed.from_mach(0.85, altitude)
if altitude.feet > 20000
else Speed.from_mach(0.7, altitude)
)
elif max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL * 1.2:
# Medium-fast like F/A-18C
return (
Speed.from_mach(0.8, altitude)
if altitude.feet > 20000
else Speed.from_mach(0.65, altitude)
)
elif max_speed > SPEED_OF_SOUND_AT_SEA_LEVEL * 0.7:
# Semi-fast like airliners or similar
return (
Speed.from_mach(0.5, altitude)
if altitude.feet > 20000
else Speed.from_mach(0.4, altitude)
)
else:
# Slow like warbirds or helicopters
# Use whichever is slowest - mach 0.35 or 70% of max speed
logging.debug(f"{self.name} max_speed * 0.7 is {max_speed * 0.7}")
return min(Speed.from_mach(0.35, altitude), max_speed * 0.7)
def alloc_flight_radio(self, radio_registry: RadioRegistry) -> RadioFrequency:
from gen.radios import ChannelInUseError, kHz

View File

@ -1775,17 +1775,11 @@ class RaceTrackBuilder(PydcsWaypointBuilder):
)
)
# TODO: Set orbit speeds for all race tracks and remove this special case.
if isinstance(flight_plan, RefuelingFlightPlan):
orbit = OrbitAction(
altitude=waypoint.alt,
pattern=OrbitAction.OrbitPattern.RaceTrack,
speed=int(flight_plan.patrol_speed.kph),
)
else:
orbit = OrbitAction(
altitude=waypoint.alt, pattern=OrbitAction.OrbitPattern.RaceTrack
)
orbit = OrbitAction(
altitude=waypoint.alt,
pattern=OrbitAction.OrbitPattern.RaceTrack,
speed=int(flight_plan.patrol_speed.kph),
)
racetrack = ControlledTask(orbit)
self.set_waypoint_tot(waypoint, flight_plan.patrol_start_time)

View File

@ -141,8 +141,8 @@ class FlightWaypoint:
waypoint_type: The waypoint type.
x: X coordinate of the waypoint.
y: Y coordinate of the waypoint.
alt: Altitude of the waypoint. By default this is AGL, but it can be
changed to MSL by setting alt_type to "RADIO".
alt: Altitude of the waypoint. By default this is MSL, but it can be
changed to AGL by setting alt_type to "RADIO"
"""
self.waypoint_type = waypoint_type
self.x = x

View File

@ -411,6 +411,9 @@ class PatrollingFlightPlan(FlightPlan):
#: Maximum time to remain on station.
patrol_duration: timedelta
#: Racetrack speed TAS.
patrol_speed: Speed
#: The engagement range of any Search Then Engage task, or the radius of a
#: Search Then Engage in Zone task. Any enemies of the appropriate type for
#: this mission within this range of the flight's current position (or the
@ -779,9 +782,6 @@ class RefuelingFlightPlan(PatrollingFlightPlan):
divert: Optional[FlightWaypoint]
bullseye: FlightWaypoint
#: Racetrack speed.
patrol_speed: Speed
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.takeoff
yield from self.nav_to
@ -1124,6 +1124,11 @@ class FlightPlanBuilder:
min(self.doctrine.max_patrol_altitude, randomized_alt),
)
patrol_speed = flight.unit_type.preferred_patrol_speed(patrol_alt)
logging.debug(
f"BARCAP patrol speed for {flight.unit_type.name} at {patrol_alt.feet}ft: {patrol_speed.knots} KTAS"
)
builder = WaypointBuilder(flight, self.coalition)
start, end = builder.race_track(start_pos, end_pos, patrol_alt)
@ -1131,6 +1136,7 @@ class FlightPlanBuilder:
package=self.package,
flight=flight,
patrol_duration=self.doctrine.cap_duration,
patrol_speed=patrol_speed,
engagement_distance=self.doctrine.cap_engagement_range,
takeoff=builder.takeoff(flight.departure),
nav_to=builder.nav_path(
@ -1362,6 +1368,10 @@ class FlightPlanBuilder:
self.doctrine.min_patrol_altitude,
min(self.doctrine.max_patrol_altitude, randomized_alt),
)
patrol_speed = flight.unit_type.preferred_patrol_speed(patrol_alt)
logging.debug(
f"TARCAP patrol speed for {flight.unit_type.name} at {patrol_alt.feet}ft: {patrol_speed.knots} KTAS"
)
# Create points
builder = WaypointBuilder(flight, self.coalition)
@ -1383,6 +1393,7 @@ class FlightPlanBuilder:
# requests an escort the CAP flight will remain on station for the
# duration of the escorted mission, or until it is winchester/bingo.
patrol_duration=self.doctrine.cap_duration,
patrol_speed=patrol_speed,
engagement_distance=self.doctrine.cap_engagement_range,
takeoff=builder.takeoff(flight.departure),
nav_to=builder.nav_path(flight.departure.position, orbit0p, patrol_alt),
@ -1546,16 +1557,33 @@ class FlightPlanBuilder:
builder = WaypointBuilder(flight, self.coalition)
# 2021-08-02: patrol_speed will currently have no effect because
# CAS doesn't use OrbitAction. But all PatrollingFlightPlan are expected
# to have patrol_speed
is_helo = flight.unit_type.dcs_unit_type.helicopter
ingress_egress_altitude = (
self.doctrine.ingress_altitude if not is_helo else meters(50)
)
patrol_speed = flight.unit_type.preferred_patrol_speed(ingress_egress_altitude)
use_agl_ingress_egress = is_helo
return CasFlightPlan(
package=self.package,
flight=flight,
patrol_duration=self.doctrine.cas_duration,
patrol_speed=patrol_speed,
takeoff=builder.takeoff(flight.departure),
nav_to=builder.nav_path(
flight.departure.position, ingress, self.doctrine.ingress_altitude
flight.departure.position,
ingress,
ingress_egress_altitude,
use_agl_ingress_egress,
),
nav_from=builder.nav_path(
egress, flight.arrival.position, self.doctrine.ingress_altitude
egress,
flight.arrival.position,
ingress_egress_altitude,
use_agl_ingress_egress,
),
patrol_start=builder.ingress(
FlightWaypointType.INGRESS_CAS, ingress, location
@ -1608,6 +1636,7 @@ class FlightPlanBuilder:
else:
altitude = feet(21000)
# TODO: Could use flight.unit_type.preferred_patrol_speed(altitude) instead.
if tanker_type.patrol_speed is not None:
speed = tanker_type.patrol_speed
else:

View File

@ -1,4 +1,5 @@
from __future__ import annotations
import logging
import random
from dataclasses import dataclass
@ -55,7 +56,7 @@ class WaypointBuilder:
@property
def is_helo(self) -> bool:
return getattr(self.flight.unit_type, "helicopter", False)
return self.flight.unit_type.dcs_unit_type.helicopter
def takeoff(self, departure: ControlPoint) -> FlightWaypoint:
"""Create takeoff waypoint for the given arrival airfield or carrier.