mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Add fighter sweep tasks.
Fighter sweeps arrive at the target ahead of the rest of the package (currently a fixed 5 minute lead) to clear out enemy fighters and then RTB. Fixes https://github.com/Khopa/dcs_liberation/issues/348
This commit is contained in:
parent
e60166dc89
commit
d369ce8847
@ -1,3 +1,8 @@
|
|||||||
|
# 2.3.0
|
||||||
|
|
||||||
|
# Features/Improvements
|
||||||
|
* **[Flight Planner]** Added fighter sweep missions.
|
||||||
|
|
||||||
# 2.2.1
|
# 2.2.1
|
||||||
|
|
||||||
# Features/Improvements
|
# Features/Improvements
|
||||||
|
|||||||
@ -36,6 +36,8 @@ class Doctrine:
|
|||||||
|
|
||||||
cas_duration: timedelta
|
cas_duration: timedelta
|
||||||
|
|
||||||
|
sweep_distance: int
|
||||||
|
|
||||||
|
|
||||||
MODERN_DOCTRINE = Doctrine(
|
MODERN_DOCTRINE = Doctrine(
|
||||||
cap=True,
|
cap=True,
|
||||||
@ -62,6 +64,7 @@ MODERN_DOCTRINE = Doctrine(
|
|||||||
cap_min_distance_from_cp=nm_to_meter(10),
|
cap_min_distance_from_cp=nm_to_meter(10),
|
||||||
cap_max_distance_from_cp=nm_to_meter(40),
|
cap_max_distance_from_cp=nm_to_meter(40),
|
||||||
cas_duration=timedelta(minutes=30),
|
cas_duration=timedelta(minutes=30),
|
||||||
|
sweep_distance=nm_to_meter(60),
|
||||||
)
|
)
|
||||||
|
|
||||||
COLDWAR_DOCTRINE = Doctrine(
|
COLDWAR_DOCTRINE = Doctrine(
|
||||||
@ -89,6 +92,7 @@ COLDWAR_DOCTRINE = Doctrine(
|
|||||||
cap_min_distance_from_cp=nm_to_meter(8),
|
cap_min_distance_from_cp=nm_to_meter(8),
|
||||||
cap_max_distance_from_cp=nm_to_meter(25),
|
cap_max_distance_from_cp=nm_to_meter(25),
|
||||||
cas_duration=timedelta(minutes=30),
|
cas_duration=timedelta(minutes=30),
|
||||||
|
sweep_distance=nm_to_meter(40),
|
||||||
)
|
)
|
||||||
|
|
||||||
WWII_DOCTRINE = Doctrine(
|
WWII_DOCTRINE = Doctrine(
|
||||||
@ -116,4 +120,5 @@ WWII_DOCTRINE = Doctrine(
|
|||||||
cap_min_distance_from_cp=nm_to_meter(0),
|
cap_min_distance_from_cp=nm_to_meter(0),
|
||||||
cap_max_distance_from_cp=nm_to_meter(5),
|
cap_max_distance_from_cp=nm_to_meter(5),
|
||||||
cas_duration=timedelta(minutes=30),
|
cas_duration=timedelta(minutes=30),
|
||||||
|
sweep_distance=nm_to_meter(10),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import random
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Dict, List, Optional, Type, Union, TYPE_CHECKING
|
from typing import Dict, List, Optional, TYPE_CHECKING, Type, Union
|
||||||
|
|
||||||
from dcs import helicopters
|
from dcs import helicopters
|
||||||
from dcs.action import AITaskPush, ActivateGroup
|
from dcs.action import AITaskPush, ActivateGroup
|
||||||
@ -13,10 +13,12 @@ from dcs.condition import CoalitionHasAirdrome, TimeAfter
|
|||||||
from dcs.country import Country
|
from dcs.country import Country
|
||||||
from dcs.flyingunit import FlyingUnit
|
from dcs.flyingunit import FlyingUnit
|
||||||
from dcs.helicopters import UH_1H, helicopter_map
|
from dcs.helicopters import UH_1H, helicopter_map
|
||||||
|
from dcs.mapping import Point
|
||||||
from dcs.mission import Mission, StartType
|
from dcs.mission import Mission, StartType
|
||||||
from dcs.planes import (
|
from dcs.planes import (
|
||||||
AJS37,
|
AJS37,
|
||||||
B_17G,
|
B_17G,
|
||||||
|
B_52H,
|
||||||
Bf_109K_4,
|
Bf_109K_4,
|
||||||
FW_190A8,
|
FW_190A8,
|
||||||
FW_190D9,
|
FW_190D9,
|
||||||
@ -31,7 +33,8 @@ from dcs.planes import (
|
|||||||
P_51D_30_NA,
|
P_51D_30_NA,
|
||||||
SpitfireLFMkIX,
|
SpitfireLFMkIX,
|
||||||
SpitfireLFMkIXCW,
|
SpitfireLFMkIXCW,
|
||||||
Su_33, A_20G, Tu_22M3, B_52H,
|
Su_33,
|
||||||
|
Tu_22M3,
|
||||||
)
|
)
|
||||||
from dcs.point import MovingPoint, PointAction
|
from dcs.point import MovingPoint, PointAction
|
||||||
from dcs.task import (
|
from dcs.task import (
|
||||||
@ -49,10 +52,8 @@ from dcs.task import (
|
|||||||
OptRTBOnBingoFuel,
|
OptRTBOnBingoFuel,
|
||||||
OptRTBOnOutOfAmmo,
|
OptRTBOnOutOfAmmo,
|
||||||
OptReactOnThreat,
|
OptReactOnThreat,
|
||||||
OptRestrictAfterburner,
|
|
||||||
OptRestrictJettison,
|
OptRestrictJettison,
|
||||||
OrbitAction,
|
OrbitAction,
|
||||||
PinpointStrike,
|
|
||||||
SEAD,
|
SEAD,
|
||||||
StartCommand,
|
StartCommand,
|
||||||
Targets,
|
Targets,
|
||||||
@ -71,6 +72,7 @@ from game.utils import 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
|
||||||
|
from gen.conflictgen import FRONTLINE_LENGTH
|
||||||
from gen.flights.flight import (
|
from gen.flights.flight import (
|
||||||
Flight,
|
Flight,
|
||||||
FlightType,
|
FlightType,
|
||||||
@ -79,15 +81,14 @@ from gen.flights.flight import (
|
|||||||
)
|
)
|
||||||
from gen.radios import MHz, Radio, RadioFrequency, RadioRegistry, get_radio
|
from gen.radios import MHz, Radio, RadioFrequency, RadioRegistry, get_radio
|
||||||
from gen.runways import RunwayData
|
from gen.runways import RunwayData
|
||||||
from gen.conflictgen import FRONTLINE_LENGTH
|
|
||||||
from dcs.mapping import Point
|
|
||||||
from theater import TheaterGroundObject
|
from theater import TheaterGroundObject
|
||||||
from theater.controlpoint import ControlPoint, ControlPointType
|
from theater.controlpoint import ControlPoint, ControlPointType
|
||||||
from .conflictgen import Conflict
|
from .conflictgen import Conflict
|
||||||
from .flights.flightplan import (
|
from .flights.flightplan import (
|
||||||
CasFlightPlan,
|
CasFlightPlan,
|
||||||
FormationFlightPlan,
|
LoiterFlightPlan,
|
||||||
PatrollingFlightPlan,
|
PatrollingFlightPlan,
|
||||||
|
SweepFlightPlan,
|
||||||
)
|
)
|
||||||
from .flights.traveltime import TotEstimator
|
from .flights.traveltime import TotEstimator
|
||||||
from .naming import namegen
|
from .naming import namegen
|
||||||
@ -1035,9 +1036,6 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
self.configure_behavior(group, rtb_winchester=ammo_type)
|
self.configure_behavior(group, rtb_winchester=ammo_type)
|
||||||
|
|
||||||
group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(50),
|
|
||||||
targets=[Targets.All.Air]))
|
|
||||||
|
|
||||||
def configure_cas(self, group: FlyingGroup, package: Package,
|
def configure_cas(self, group: FlyingGroup, package: Package,
|
||||||
flight: Flight,
|
flight: Flight,
|
||||||
dynamic_runways: Dict[str, RunwayData]) -> None:
|
dynamic_runways: Dict[str, RunwayData]) -> None:
|
||||||
@ -1118,7 +1116,7 @@ class AircraftConflictGenerator:
|
|||||||
dynamic_runways: Dict[str, RunwayData]) -> None:
|
dynamic_runways: Dict[str, RunwayData]) -> None:
|
||||||
flight_type = flight.flight_type
|
flight_type = flight.flight_type
|
||||||
if flight_type in [FlightType.BARCAP, FlightType.TARCAP,
|
if flight_type in [FlightType.BARCAP, FlightType.TARCAP,
|
||||||
FlightType.INTERCEPTION]:
|
FlightType.INTERCEPTION, FlightType.SWEEP]:
|
||||||
self.configure_cap(group, package, flight, dynamic_runways)
|
self.configure_cap(group, package, flight, dynamic_runways)
|
||||||
elif flight_type in [FlightType.CAS, FlightType.BAI]:
|
elif flight_type in [FlightType.CAS, FlightType.BAI]:
|
||||||
self.configure_cas(group, package, flight, dynamic_runways)
|
self.configure_cas(group, package, flight, dynamic_runways)
|
||||||
@ -1278,6 +1276,7 @@ class PydcsWaypointBuilder:
|
|||||||
FlightWaypointType.LANDING_POINT: LandingPointBuilder,
|
FlightWaypointType.LANDING_POINT: LandingPointBuilder,
|
||||||
FlightWaypointType.LOITER: HoldPointBuilder,
|
FlightWaypointType.LOITER: HoldPointBuilder,
|
||||||
FlightWaypointType.PATROL_TRACK: RaceTrackBuilder,
|
FlightWaypointType.PATROL_TRACK: RaceTrackBuilder,
|
||||||
|
FlightWaypointType.INGRESS_SWEEP: SweepIngressBuilder,
|
||||||
}
|
}
|
||||||
builder = builders.get(waypoint.waypoint_type, DefaultWaypointBuilder)
|
builder = builders.get(waypoint.waypoint_type, DefaultWaypointBuilder)
|
||||||
return builder(waypoint, group, package, flight, mission)
|
return builder(waypoint, group, package, flight, mission)
|
||||||
@ -1314,7 +1313,7 @@ class HoldPointBuilder(PydcsWaypointBuilder):
|
|||||||
altitude=waypoint.alt,
|
altitude=waypoint.alt,
|
||||||
pattern=OrbitAction.OrbitPattern.Circle
|
pattern=OrbitAction.OrbitPattern.Circle
|
||||||
))
|
))
|
||||||
if not isinstance(self.flight.flight_plan, FormationFlightPlan):
|
if not isinstance(self.flight.flight_plan, LoiterFlightPlan):
|
||||||
flight_plan_type = self.flight.flight_plan.__class__.__name__
|
flight_plan_type = self.flight.flight_plan.__class__.__name__
|
||||||
logging.error(
|
logging.error(
|
||||||
f"Cannot configure hold for for {self.flight} because "
|
f"Cannot configure hold for for {self.flight} because "
|
||||||
@ -1458,6 +1457,23 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
|
|||||||
return waypoint
|
return waypoint
|
||||||
|
|
||||||
|
|
||||||
|
class SweepIngressBuilder(PydcsWaypointBuilder):
|
||||||
|
def build(self) -> MovingPoint:
|
||||||
|
waypoint = super().build()
|
||||||
|
|
||||||
|
if not isinstance(self.flight.flight_plan, SweepFlightPlan):
|
||||||
|
flight_plan_type = self.flight.flight_plan.__class__.__name__
|
||||||
|
logging.error(
|
||||||
|
f"Cannot create sweep for {self.flight} because "
|
||||||
|
f"{flight_plan_type} is not a sweep flight plan.")
|
||||||
|
return waypoint
|
||||||
|
|
||||||
|
waypoint.tasks.append(EngageTargets(max_distance=nm_to_meter(50),
|
||||||
|
targets=[Targets.All.Air]))
|
||||||
|
|
||||||
|
return waypoint
|
||||||
|
|
||||||
|
|
||||||
class JoinPointBuilder(PydcsWaypointBuilder):
|
class JoinPointBuilder(PydcsWaypointBuilder):
|
||||||
def build(self) -> MovingPoint:
|
def build(self) -> MovingPoint:
|
||||||
waypoint = super().build()
|
waypoint = super().build()
|
||||||
@ -1532,4 +1548,14 @@ class RaceTrackBuilder(PydcsWaypointBuilder):
|
|||||||
racetrack.stop_after_time(
|
racetrack.stop_after_time(
|
||||||
int(self.flight.flight_plan.patrol_end_time.total_seconds()))
|
int(self.flight.flight_plan.patrol_end_time.total_seconds()))
|
||||||
waypoint.add_task(racetrack)
|
waypoint.add_task(racetrack)
|
||||||
|
|
||||||
|
# TODO: Move the properties of this task into the flight plan?
|
||||||
|
# CAP is the only current user of this so it's not a big deal, but might
|
||||||
|
# be good to make this usable for things like BAI when we add that
|
||||||
|
# later.
|
||||||
|
cap_types = {FlightType.BARCAP, FlightType.TARCAP}
|
||||||
|
if self.flight.flight_type in cap_types:
|
||||||
|
waypoint.tasks.append(EngageTargets(max_distance=nm_to_meter(50),
|
||||||
|
targets=[Targets.All.Air]))
|
||||||
|
|
||||||
return waypoint
|
return waypoint
|
||||||
|
|||||||
@ -159,6 +159,7 @@ class Package:
|
|||||||
FlightType.TARCAP,
|
FlightType.TARCAP,
|
||||||
FlightType.CAP,
|
FlightType.CAP,
|
||||||
FlightType.BARCAP,
|
FlightType.BARCAP,
|
||||||
|
FlightType.SWEEP,
|
||||||
FlightType.EWAR,
|
FlightType.EWAR,
|
||||||
FlightType.ESCORT,
|
FlightType.ESCORT,
|
||||||
]
|
]
|
||||||
|
|||||||
@ -38,6 +38,8 @@ class FlightType(Enum):
|
|||||||
RECON = 15
|
RECON = 15
|
||||||
EWAR = 16
|
EWAR = 16
|
||||||
|
|
||||||
|
SWEEP = 17
|
||||||
|
|
||||||
|
|
||||||
class FlightWaypointType(Enum):
|
class FlightWaypointType(Enum):
|
||||||
TAKEOFF = 0 # Take off point
|
TAKEOFF = 0 # Take off point
|
||||||
@ -61,6 +63,7 @@ class FlightWaypointType(Enum):
|
|||||||
LOITER = 18
|
LOITER = 18
|
||||||
INGRESS_ESCORT = 19
|
INGRESS_ESCORT = 19
|
||||||
INGRESS_DEAD = 20
|
INGRESS_DEAD = 20
|
||||||
|
INGRESS_SWEEP = 21
|
||||||
|
|
||||||
|
|
||||||
class FlightWaypoint:
|
class FlightWaypoint:
|
||||||
|
|||||||
@ -105,6 +105,15 @@ class FlightPlan:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tot_offset(self) -> timedelta:
|
||||||
|
"""This flight's offset from the package's TOT.
|
||||||
|
|
||||||
|
Positive values represent later TOTs. An offset of -2 minutes is used
|
||||||
|
for a flight that has a TOT 2 minutes before the rest of the package.
|
||||||
|
"""
|
||||||
|
return timedelta()
|
||||||
|
|
||||||
# Not cached because changes to the package might alter the formation speed.
|
# Not cached because changes to the package might alter the formation speed.
|
||||||
@property
|
@property
|
||||||
def travel_time_to_target(self) -> Optional[timedelta]:
|
def travel_time_to_target(self) -> Optional[timedelta]:
|
||||||
@ -147,8 +156,33 @@ class FlightPlan:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class FormationFlightPlan(FlightPlan):
|
class LoiterFlightPlan(FlightPlan):
|
||||||
hold: FlightWaypoint
|
hold: FlightWaypoint
|
||||||
|
|
||||||
|
@property
|
||||||
|
def waypoints(self) -> List[FlightWaypoint]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def push_time(self) -> timedelta:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def depart_time_for_waypoint(
|
||||||
|
self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||||
|
if waypoint == self.hold:
|
||||||
|
return self.push_time
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FormationFlightPlan(LoiterFlightPlan):
|
||||||
join: FlightWaypoint
|
join: FlightWaypoint
|
||||||
split: FlightWaypoint
|
split: FlightWaypoint
|
||||||
|
|
||||||
@ -215,12 +249,6 @@ class FormationFlightPlan(FlightPlan):
|
|||||||
return self.split_time
|
return self.split_time
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def depart_time_for_waypoint(
|
|
||||||
self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
|
||||||
if waypoint == self.hold:
|
|
||||||
return self.push_time
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def push_time(self) -> timedelta:
|
def push_time(self) -> timedelta:
|
||||||
return self.join_time - TravelTime.between_points(
|
return self.join_time - TravelTime.between_points(
|
||||||
@ -461,6 +489,64 @@ class StrikeFlightPlan(FormationFlightPlan):
|
|||||||
return super().tot_for_waypoint(waypoint)
|
return super().tot_for_waypoint(waypoint)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SweepFlightPlan(LoiterFlightPlan):
|
||||||
|
takeoff: FlightWaypoint
|
||||||
|
sweep_start: FlightWaypoint
|
||||||
|
sweep_end: FlightWaypoint
|
||||||
|
land: FlightWaypoint
|
||||||
|
lead_time: timedelta
|
||||||
|
|
||||||
|
@property
|
||||||
|
def waypoints(self) -> List[FlightWaypoint]:
|
||||||
|
return [
|
||||||
|
self.takeoff,
|
||||||
|
self.hold,
|
||||||
|
self.sweep_start,
|
||||||
|
self.sweep_end,
|
||||||
|
self.land,
|
||||||
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||||
|
return self.sweep_end
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tot_offset(self) -> timedelta:
|
||||||
|
return -self.lead_time
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sweep_start_time(self) -> timedelta:
|
||||||
|
travel_time = self.travel_time_between_waypoints(
|
||||||
|
self.sweep_start, self.sweep_end)
|
||||||
|
return self.sweep_end_time - travel_time
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sweep_end_time(self) -> timedelta:
|
||||||
|
return self.package.time_over_target + self.tot_offset
|
||||||
|
|
||||||
|
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||||
|
if waypoint == self.sweep_start:
|
||||||
|
return self.sweep_start_time
|
||||||
|
if waypoint == self.sweep_end:
|
||||||
|
return self.sweep_end_time
|
||||||
|
return None
|
||||||
|
|
||||||
|
def depart_time_for_waypoint(
|
||||||
|
self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||||
|
if waypoint == self.hold:
|
||||||
|
return self.push_time
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def push_time(self) -> timedelta:
|
||||||
|
return self.sweep_end_time - TravelTime.between_points(
|
||||||
|
self.hold.position,
|
||||||
|
self.sweep_end.position,
|
||||||
|
GroundSpeed.for_flight(self.flight, self.hold.alt)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class CustomFlightPlan(FlightPlan):
|
class CustomFlightPlan(FlightPlan):
|
||||||
custom_waypoints: List[FlightWaypoint]
|
custom_waypoints: List[FlightWaypoint]
|
||||||
@ -546,6 +632,8 @@ class FlightPlanBuilder:
|
|||||||
return self.generate_sead(flight, custom_targets)
|
return self.generate_sead(flight, custom_targets)
|
||||||
elif task == FlightType.STRIKE:
|
elif task == FlightType.STRIKE:
|
||||||
return self.generate_strike(flight)
|
return self.generate_strike(flight)
|
||||||
|
elif task == FlightType.SWEEP:
|
||||||
|
return self.generate_sweep(flight)
|
||||||
elif task == FlightType.TARCAP:
|
elif task == FlightType.TARCAP:
|
||||||
return self.generate_frontline_cap(flight)
|
return self.generate_frontline_cap(flight)
|
||||||
elif task == FlightType.TROOP_TRANSPORT:
|
elif task == FlightType.TROOP_TRANSPORT:
|
||||||
@ -671,6 +759,35 @@ class FlightPlanBuilder:
|
|||||||
land=land
|
land=land
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def generate_sweep(self, flight: Flight) -> SweepFlightPlan:
|
||||||
|
"""Generate a BARCAP flight at a given location.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
flight: The flight to generate the flight plan for.
|
||||||
|
"""
|
||||||
|
target = self.package.target.position
|
||||||
|
|
||||||
|
heading = self._heading_to_package_airfield(target)
|
||||||
|
start = target.point_from_heading(heading,
|
||||||
|
-self.doctrine.sweep_distance)
|
||||||
|
|
||||||
|
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||||
|
descent, land = builder.rtb(flight.from_cp)
|
||||||
|
|
||||||
|
start, end = builder.sweep(start, target,
|
||||||
|
self.doctrine.ingress_altitude)
|
||||||
|
|
||||||
|
return SweepFlightPlan(
|
||||||
|
package=self.package,
|
||||||
|
flight=flight,
|
||||||
|
lead_time=timedelta(minutes=5),
|
||||||
|
takeoff=builder.takeoff(flight.from_cp),
|
||||||
|
hold=builder.hold(self._hold_point(flight)),
|
||||||
|
sweep_start=start,
|
||||||
|
sweep_end=end,
|
||||||
|
land=land
|
||||||
|
)
|
||||||
|
|
||||||
def generate_frontline_cap(self, flight: Flight) -> FrontLineCapFlightPlan:
|
def generate_frontline_cap(self, flight: Flight) -> FrontLineCapFlightPlan:
|
||||||
"""Generate a CAP flight plan for the given front line.
|
"""Generate a CAP flight plan for the given front line.
|
||||||
|
|
||||||
|
|||||||
@ -128,7 +128,11 @@ class TotEstimator:
|
|||||||
f"time for {flight} will be immediate.")
|
f"time for {flight} will be immediate.")
|
||||||
return timedelta()
|
return timedelta()
|
||||||
else:
|
else:
|
||||||
tot = self.package.time_over_target
|
tot_waypoint = flight.flight_plan.tot_waypoint
|
||||||
|
if tot_waypoint is None:
|
||||||
|
tot = self.package.time_over_target
|
||||||
|
else:
|
||||||
|
tot = flight.flight_plan.tot_for_waypoint(tot_waypoint)
|
||||||
return tot - travel_time - self.HOLD_TIME
|
return tot - travel_time - self.HOLD_TIME
|
||||||
|
|
||||||
def earliest_tot(self) -> timedelta:
|
def earliest_tot(self) -> timedelta:
|
||||||
@ -165,9 +169,13 @@ class TotEstimator:
|
|||||||
# Return 0 so this flight's travel time does not affect the rest
|
# Return 0 so this flight's travel time does not affect the rest
|
||||||
# of the package.
|
# of the package.
|
||||||
return timedelta()
|
return timedelta()
|
||||||
|
# Account for TOT offsets for the flight plan. An offset of -2 minutes
|
||||||
|
# means the flight's TOT is 2 minutes ahead of the package's so it needs
|
||||||
|
# an extra two minutes.
|
||||||
|
offset = -flight.flight_plan.tot_offset
|
||||||
startup = self.estimate_startup(flight)
|
startup = self.estimate_startup(flight)
|
||||||
ground_ops = self.estimate_ground_ops(flight)
|
ground_ops = self.estimate_ground_ops(flight)
|
||||||
return startup + ground_ops + time_to_target
|
return startup + ground_ops + time_to_target + offset
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def estimate_startup(flight: Flight) -> timedelta:
|
def estimate_startup(flight: Flight) -> timedelta:
|
||||||
|
|||||||
@ -326,6 +326,56 @@ class WaypointBuilder:
|
|||||||
return (self.race_track_start(start, altitude),
|
return (self.race_track_start(start, altitude),
|
||||||
self.race_track_end(end, altitude))
|
self.race_track_end(end, altitude))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sweep_start(position: Point, altitude: int) -> FlightWaypoint:
|
||||||
|
"""Creates a sweep start waypoint.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
position: Position of the waypoint.
|
||||||
|
altitude: Altitude of the sweep in meters.
|
||||||
|
"""
|
||||||
|
waypoint = FlightWaypoint(
|
||||||
|
FlightWaypointType.INGRESS_SWEEP,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude
|
||||||
|
)
|
||||||
|
waypoint.name = "SWEEP START"
|
||||||
|
waypoint.description = "Proceed to the target and engage enemy aircraft"
|
||||||
|
waypoint.pretty_name = "Sweep start"
|
||||||
|
return waypoint
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sweep_end(position: Point, altitude: int) -> FlightWaypoint:
|
||||||
|
"""Creates a sweep end waypoint.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
position: Position of the waypoint.
|
||||||
|
altitude: Altitude of the sweep in meters.
|
||||||
|
"""
|
||||||
|
waypoint = FlightWaypoint(
|
||||||
|
FlightWaypointType.EGRESS,
|
||||||
|
position.x,
|
||||||
|
position.y,
|
||||||
|
altitude
|
||||||
|
)
|
||||||
|
waypoint.name = "SWEEP END"
|
||||||
|
waypoint.description = "End of sweep"
|
||||||
|
waypoint.pretty_name = "Sweep end"
|
||||||
|
return waypoint
|
||||||
|
|
||||||
|
def sweep(self, start: Point, end: Point,
|
||||||
|
altitude: int) -> Tuple[FlightWaypoint, FlightWaypoint]:
|
||||||
|
"""Creates two waypoint for a racetrack orbit.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start: The beginning of the sweep.
|
||||||
|
end: The end of the sweep.
|
||||||
|
altitude: The sweep altitude.
|
||||||
|
"""
|
||||||
|
return (self.sweep_start(start, altitude),
|
||||||
|
self.sweep_end(end, altitude))
|
||||||
|
|
||||||
def rtb(self,
|
def rtb(self,
|
||||||
arrival: ControlPoint) -> Tuple[FlightWaypoint, FlightWaypoint]:
|
arrival: ControlPoint) -> Tuple[FlightWaypoint, FlightWaypoint]:
|
||||||
"""Creates descent ant landing waypoints for the given control point.
|
"""Creates descent ant landing waypoints for the given control point.
|
||||||
|
|||||||
@ -21,6 +21,7 @@ class QFlightTypeComboBox(QComboBox):
|
|||||||
FlightType.ESCORT,
|
FlightType.ESCORT,
|
||||||
FlightType.SEAD,
|
FlightType.SEAD,
|
||||||
FlightType.DEAD,
|
FlightType.DEAD,
|
||||||
|
FlightType.SWEEP,
|
||||||
# TODO: FlightType.ELINT,
|
# TODO: FlightType.ELINT,
|
||||||
# TODO: FlightType.EWAR,
|
# TODO: FlightType.EWAR,
|
||||||
# TODO: FlightType.RECON,
|
# TODO: FlightType.RECON,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user