mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
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:
parent
9121cf7ecb
commit
30801dff9f
@ -29,7 +29,15 @@ from game.radio.channels import (
|
|||||||
ViggenRadioChannelAllocator,
|
ViggenRadioChannelAllocator,
|
||||||
NoOpChannelAllocator,
|
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:
|
if TYPE_CHECKING:
|
||||||
from gen.aircraft import FlightData
|
from gen.aircraft import FlightData
|
||||||
@ -182,7 +190,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def preferred_patrol_altitude(self) -> Distance:
|
def preferred_patrol_altitude(self) -> Distance:
|
||||||
if self.patrol_altitude:
|
if self.patrol_altitude is not None:
|
||||||
return self.patrol_altitude
|
return self.patrol_altitude
|
||||||
else:
|
else:
|
||||||
# Estimate based on max speed.
|
# Estimate based on max speed.
|
||||||
@ -208,6 +216,40 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
min(altitude_for_highest_speed, rounded_altitude),
|
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:
|
def alloc_flight_radio(self, radio_registry: RadioRegistry) -> RadioFrequency:
|
||||||
from gen.radios import ChannelInUseError, kHz
|
from gen.radios import ChannelInUseError, kHz
|
||||||
|
|
||||||
|
|||||||
@ -1775,17 +1775,11 @@ class RaceTrackBuilder(PydcsWaypointBuilder):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: Set orbit speeds for all race tracks and remove this special case.
|
orbit = OrbitAction(
|
||||||
if isinstance(flight_plan, RefuelingFlightPlan):
|
altitude=waypoint.alt,
|
||||||
orbit = OrbitAction(
|
pattern=OrbitAction.OrbitPattern.RaceTrack,
|
||||||
altitude=waypoint.alt,
|
speed=int(flight_plan.patrol_speed.kph),
|
||||||
pattern=OrbitAction.OrbitPattern.RaceTrack,
|
)
|
||||||
speed=int(flight_plan.patrol_speed.kph),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
orbit = OrbitAction(
|
|
||||||
altitude=waypoint.alt, pattern=OrbitAction.OrbitPattern.RaceTrack
|
|
||||||
)
|
|
||||||
|
|
||||||
racetrack = ControlledTask(orbit)
|
racetrack = ControlledTask(orbit)
|
||||||
self.set_waypoint_tot(waypoint, flight_plan.patrol_start_time)
|
self.set_waypoint_tot(waypoint, flight_plan.patrol_start_time)
|
||||||
|
|||||||
@ -141,8 +141,8 @@ class FlightWaypoint:
|
|||||||
waypoint_type: The waypoint type.
|
waypoint_type: The waypoint type.
|
||||||
x: X coordinate of the waypoint.
|
x: X coordinate of the waypoint.
|
||||||
y: Y coordinate of the waypoint.
|
y: Y coordinate of the waypoint.
|
||||||
alt: Altitude of the waypoint. By default this is AGL, but it can be
|
alt: Altitude of the waypoint. By default this is MSL, but it can be
|
||||||
changed to MSL by setting alt_type to "RADIO".
|
changed to AGL by setting alt_type to "RADIO"
|
||||||
"""
|
"""
|
||||||
self.waypoint_type = waypoint_type
|
self.waypoint_type = waypoint_type
|
||||||
self.x = x
|
self.x = x
|
||||||
|
|||||||
@ -411,6 +411,9 @@ class PatrollingFlightPlan(FlightPlan):
|
|||||||
#: Maximum time to remain on station.
|
#: Maximum time to remain on station.
|
||||||
patrol_duration: timedelta
|
patrol_duration: timedelta
|
||||||
|
|
||||||
|
#: Racetrack speed TAS.
|
||||||
|
patrol_speed: Speed
|
||||||
|
|
||||||
#: The engagement range of any Search Then Engage task, or the radius of a
|
#: 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
|
#: 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
|
#: this mission within this range of the flight's current position (or the
|
||||||
@ -779,9 +782,6 @@ class RefuelingFlightPlan(PatrollingFlightPlan):
|
|||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
bullseye: FlightWaypoint
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
#: Racetrack speed.
|
|
||||||
patrol_speed: Speed
|
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.takeoff
|
yield self.takeoff
|
||||||
yield from self.nav_to
|
yield from self.nav_to
|
||||||
@ -1124,6 +1124,11 @@ class FlightPlanBuilder:
|
|||||||
min(self.doctrine.max_patrol_altitude, randomized_alt),
|
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)
|
builder = WaypointBuilder(flight, self.coalition)
|
||||||
start, end = builder.race_track(start_pos, end_pos, patrol_alt)
|
start, end = builder.race_track(start_pos, end_pos, patrol_alt)
|
||||||
|
|
||||||
@ -1131,6 +1136,7 @@ class FlightPlanBuilder:
|
|||||||
package=self.package,
|
package=self.package,
|
||||||
flight=flight,
|
flight=flight,
|
||||||
patrol_duration=self.doctrine.cap_duration,
|
patrol_duration=self.doctrine.cap_duration,
|
||||||
|
patrol_speed=patrol_speed,
|
||||||
engagement_distance=self.doctrine.cap_engagement_range,
|
engagement_distance=self.doctrine.cap_engagement_range,
|
||||||
takeoff=builder.takeoff(flight.departure),
|
takeoff=builder.takeoff(flight.departure),
|
||||||
nav_to=builder.nav_path(
|
nav_to=builder.nav_path(
|
||||||
@ -1362,6 +1368,10 @@ class FlightPlanBuilder:
|
|||||||
self.doctrine.min_patrol_altitude,
|
self.doctrine.min_patrol_altitude,
|
||||||
min(self.doctrine.max_patrol_altitude, randomized_alt),
|
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
|
# Create points
|
||||||
builder = WaypointBuilder(flight, self.coalition)
|
builder = WaypointBuilder(flight, self.coalition)
|
||||||
@ -1383,6 +1393,7 @@ class FlightPlanBuilder:
|
|||||||
# requests an escort the CAP flight will remain on station for the
|
# requests an escort the CAP flight will remain on station for the
|
||||||
# duration of the escorted mission, or until it is winchester/bingo.
|
# duration of the escorted mission, or until it is winchester/bingo.
|
||||||
patrol_duration=self.doctrine.cap_duration,
|
patrol_duration=self.doctrine.cap_duration,
|
||||||
|
patrol_speed=patrol_speed,
|
||||||
engagement_distance=self.doctrine.cap_engagement_range,
|
engagement_distance=self.doctrine.cap_engagement_range,
|
||||||
takeoff=builder.takeoff(flight.departure),
|
takeoff=builder.takeoff(flight.departure),
|
||||||
nav_to=builder.nav_path(flight.departure.position, orbit0p, patrol_alt),
|
nav_to=builder.nav_path(flight.departure.position, orbit0p, patrol_alt),
|
||||||
@ -1546,16 +1557,33 @@ class FlightPlanBuilder:
|
|||||||
|
|
||||||
builder = WaypointBuilder(flight, self.coalition)
|
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(
|
return CasFlightPlan(
|
||||||
package=self.package,
|
package=self.package,
|
||||||
flight=flight,
|
flight=flight,
|
||||||
patrol_duration=self.doctrine.cas_duration,
|
patrol_duration=self.doctrine.cas_duration,
|
||||||
|
patrol_speed=patrol_speed,
|
||||||
takeoff=builder.takeoff(flight.departure),
|
takeoff=builder.takeoff(flight.departure),
|
||||||
nav_to=builder.nav_path(
|
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(
|
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(
|
patrol_start=builder.ingress(
|
||||||
FlightWaypointType.INGRESS_CAS, ingress, location
|
FlightWaypointType.INGRESS_CAS, ingress, location
|
||||||
@ -1608,6 +1636,7 @@ class FlightPlanBuilder:
|
|||||||
else:
|
else:
|
||||||
altitude = feet(21000)
|
altitude = feet(21000)
|
||||||
|
|
||||||
|
# TODO: Could use flight.unit_type.preferred_patrol_speed(altitude) instead.
|
||||||
if tanker_type.patrol_speed is not None:
|
if tanker_type.patrol_speed is not None:
|
||||||
speed = tanker_type.patrol_speed
|
speed = tanker_type.patrol_speed
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import logging
|
||||||
|
|
||||||
import random
|
import random
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -55,7 +56,7 @@ class WaypointBuilder:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def is_helo(self) -> bool:
|
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:
|
def takeoff(self, departure: ControlPoint) -> FlightWaypoint:
|
||||||
"""Create takeoff waypoint for the given arrival airfield or carrier.
|
"""Create takeoff waypoint for the given arrival airfield or carrier.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user