mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Remove bingo estimates from FlightPlan.
This doesn't need to be a part of FlightPlan, and it's easier to test if it isn't. Move it out and add the tests. It's pretty misleading to allow this in the core of the flight plan code anything. This is an extremely unreliable estimate for most aircraft so it should be more clearly just for briefing purposes.
This commit is contained in:
parent
99eed33241
commit
cb9063b5be
@ -12,7 +12,6 @@ from abc import ABC, abstractmethod
|
||||
from collections.abc import Iterator
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
from functools import cached_property
|
||||
from typing import Any, Generic, TYPE_CHECKING, TypeGuard, TypeVar
|
||||
|
||||
from game.typeguard import self_type_guard
|
||||
@ -23,7 +22,6 @@ from ..starttype import StartType
|
||||
from ..traveltime import GroundSpeed
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.dcs.aircrafttype import FuelConsumption
|
||||
from game.theater import ControlPoint
|
||||
from ..flight import Flight
|
||||
from ..flightwaypoint import FlightWaypoint
|
||||
@ -145,39 +143,6 @@ class FlightPlan(ABC, Generic[LayoutT]):
|
||||
def tot(self) -> datetime:
|
||||
return self.package.time_over_target + self.tot_offset
|
||||
|
||||
@cached_property
|
||||
def bingo_fuel(self) -> int:
|
||||
"""Bingo fuel value for the FlightPlan"""
|
||||
if (fuel := self.flight.unit_type.fuel_consumption) is not None:
|
||||
return self._bingo_estimate(fuel)
|
||||
return self._legacy_bingo_estimate()
|
||||
|
||||
def _bingo_estimate(self, fuel: FuelConsumption) -> int:
|
||||
distance_to_arrival = self.max_distance_from(self.flight.arrival)
|
||||
fuel_consumed = fuel.cruise * distance_to_arrival.nautical_miles
|
||||
bingo = fuel_consumed + fuel.min_safe
|
||||
return math.ceil(bingo / 100) * 100
|
||||
|
||||
def _legacy_bingo_estimate(self) -> int:
|
||||
distance_to_arrival = self.max_distance_from(self.flight.arrival)
|
||||
|
||||
bingo = 1000.0 # Minimum Emergency Fuel
|
||||
bingo += 500 # Visual Traffic
|
||||
bingo += 15 * distance_to_arrival.nautical_miles
|
||||
|
||||
# TODO: Per aircraft tweaks.
|
||||
|
||||
if self.flight.divert is not None:
|
||||
max_divert_distance = self.max_distance_from(self.flight.divert)
|
||||
bingo += 10 * max_divert_distance.nautical_miles
|
||||
|
||||
return round(bingo / 100) * 100
|
||||
|
||||
@cached_property
|
||||
def joker_fuel(self) -> int:
|
||||
"""Joker fuel value for the FlightPlan"""
|
||||
return self.bingo_fuel + 1000
|
||||
|
||||
def max_distance_from(self, cp: ControlPoint) -> Distance:
|
||||
"""Returns the farthest waypoint of the flight plan from a ControlPoint.
|
||||
:arg cp The ControlPoint to measure distance from.
|
||||
|
||||
66
game/missiongenerator/aircraft/bingoestimator.py
Normal file
66
game/missiongenerator/aircraft/bingoestimator.py
Normal file
@ -0,0 +1,66 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from dcs import Point
|
||||
|
||||
from game.utils import Distance, meters
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato.flightwaypoint import FlightWaypoint
|
||||
from game.dcs.aircrafttype import FuelConsumption
|
||||
|
||||
|
||||
class BingoEstimator:
|
||||
"""Estimates bingo/joker fuel values for a flight plan.
|
||||
|
||||
The results returned by this class are bogus for most airframes. Only the few
|
||||
airframes which have fuel consumption data available can provide even moderately
|
||||
reliable estimates. **Do not use this for flight planning.** This should only be
|
||||
used in briefing context where it's okay to be wrong.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
fuel_consumption: FuelConsumption | None,
|
||||
arrival: Point,
|
||||
divert: Point | None,
|
||||
waypoints: list[FlightWaypoint],
|
||||
) -> None:
|
||||
self.fuel_consumption = fuel_consumption
|
||||
self.arrival = arrival
|
||||
self.divert = divert
|
||||
self.waypoints = waypoints
|
||||
|
||||
def estimate_bingo(self) -> int:
|
||||
"""Bingo fuel value for the FlightPlan"""
|
||||
if (fuel := self.fuel_consumption) is not None:
|
||||
return self._fuel_consumption_based_estimate(fuel)
|
||||
return self._legacy_bingo_estimate()
|
||||
|
||||
def estimate_joker(self) -> int:
|
||||
"""Joker fuel value for the FlightPlan"""
|
||||
return self.estimate_bingo() + 1000
|
||||
|
||||
def _fuel_consumption_based_estimate(self, fuel: FuelConsumption) -> int:
|
||||
distance_to_arrival = self._max_distance_from(self.arrival)
|
||||
fuel_consumed = fuel.cruise * distance_to_arrival.nautical_miles
|
||||
bingo = fuel_consumed + fuel.min_safe
|
||||
return math.ceil(bingo / 100) * 100
|
||||
|
||||
def _legacy_bingo_estimate(self) -> int:
|
||||
distance_to_arrival = self._max_distance_from(self.arrival)
|
||||
|
||||
bingo = 1000.0 # Minimum Emergency Fuel
|
||||
bingo += 500 # Visual Traffic
|
||||
bingo += 15 * distance_to_arrival.nautical_miles
|
||||
|
||||
if self.divert is not None:
|
||||
max_divert_distance = self._max_distance_from(self.divert)
|
||||
bingo += 10 * max_divert_distance.nautical_miles
|
||||
|
||||
return round(bingo / 100) * 100
|
||||
|
||||
def _max_distance_from(self, point: Point) -> Distance:
|
||||
return max(meters(point.distance_to_point(w.position)) for w in self.waypoints)
|
||||
@ -4,7 +4,7 @@ import logging
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional, TYPE_CHECKING
|
||||
|
||||
from dcs import Mission
|
||||
from dcs import Mission, Point
|
||||
from dcs.flyingunit import FlyingUnit
|
||||
from dcs.unit import Skill
|
||||
from dcs.unitgroup import FlyingGroup
|
||||
@ -21,6 +21,7 @@ from game.squadrons import Pilot
|
||||
from game.unitmap import UnitMap
|
||||
from .aircraftbehavior import AircraftBehavior
|
||||
from .aircraftpainter import AircraftPainter
|
||||
from .bingoestimator import BingoEstimator
|
||||
from .flightdata import FlightData
|
||||
from .waypoints import WaypointGenerator
|
||||
from ...ato.flightmember import FlightMember
|
||||
@ -102,6 +103,16 @@ class FlightGroupConfigurator:
|
||||
self.unit_map,
|
||||
).create_waypoints()
|
||||
|
||||
divert_position: Point | None = None
|
||||
if self.flight.divert is not None:
|
||||
divert_position = self.flight.divert.position
|
||||
bingo_estimator = BingoEstimator(
|
||||
self.flight.unit_type.fuel_consumption,
|
||||
self.flight.arrival.position,
|
||||
divert_position,
|
||||
self.flight.flight_plan.waypoints,
|
||||
)
|
||||
|
||||
return FlightData(
|
||||
package=self.flight.package,
|
||||
aircraft_type=self.flight.unit_type,
|
||||
@ -119,8 +130,8 @@ class FlightGroupConfigurator:
|
||||
divert=divert,
|
||||
waypoints=waypoints,
|
||||
intra_flight_channel=flight_channel,
|
||||
bingo_fuel=self.flight.flight_plan.bingo_fuel,
|
||||
joker_fuel=self.flight.flight_plan.joker_fuel,
|
||||
bingo_fuel=bingo_estimator.estimate_bingo(),
|
||||
joker_fuel=bingo_estimator.estimate_joker(),
|
||||
custom_name=self.flight.custom_name,
|
||||
laser_codes=laser_codes,
|
||||
)
|
||||
|
||||
51
tests/missiongenerator/aircraft/test_bingoestimator.py
Normal file
51
tests/missiongenerator/aircraft/test_bingoestimator.py
Normal file
@ -0,0 +1,51 @@
|
||||
import pytest
|
||||
from dcs import Point
|
||||
from dcs.terrain import Terrain, Caucasus
|
||||
|
||||
from game.ato import FlightWaypoint
|
||||
from game.ato.flightwaypointtype import FlightWaypointType
|
||||
from game.dcs.aircrafttype import FuelConsumption
|
||||
from game.missiongenerator.aircraft.bingoestimator import BingoEstimator
|
||||
from game.utils import nautical_miles
|
||||
|
||||
|
||||
@pytest.fixture(name="terrain")
|
||||
def terrain_fixture() -> Terrain:
|
||||
return Caucasus()
|
||||
|
||||
|
||||
@pytest.fixture(name="waypoints")
|
||||
def waypoints_fixture(terrain: Terrain) -> list[FlightWaypoint]:
|
||||
return [
|
||||
FlightWaypoint(
|
||||
"", FlightWaypointType.NAV, Point(0, nautical_miles(d).meters, terrain)
|
||||
)
|
||||
for d in range(101)
|
||||
]
|
||||
|
||||
|
||||
def test_legacy_bingo_estimator(
|
||||
waypoints: list[FlightWaypoint], terrain: Terrain
|
||||
) -> None:
|
||||
estimator = BingoEstimator(None, Point(0, 0, terrain), None, waypoints)
|
||||
assert estimator.estimate_bingo() == 3000
|
||||
assert estimator.estimate_joker() == estimator.estimate_bingo() + 1000
|
||||
estimator = BingoEstimator(
|
||||
None, Point(0, 0, terrain), Point(0, 5, terrain), waypoints
|
||||
)
|
||||
assert estimator.estimate_bingo() == 4000
|
||||
assert estimator.estimate_joker() == estimator.estimate_bingo() + 1000
|
||||
|
||||
|
||||
def test_fuel_consumption_based_bingo_estimator(
|
||||
waypoints: list[FlightWaypoint], terrain: Terrain
|
||||
) -> None:
|
||||
consumption = FuelConsumption(100, 50, 10, 25, 1000)
|
||||
estimator = BingoEstimator(consumption, Point(0, 0, terrain), None, waypoints)
|
||||
assert estimator.estimate_bingo() == 2000
|
||||
assert estimator.estimate_joker() == estimator.estimate_bingo() + 1000
|
||||
estimator = BingoEstimator(
|
||||
consumption, Point(0, 0, terrain), Point(0, 5, terrain), waypoints
|
||||
)
|
||||
assert estimator.estimate_bingo() == 2000
|
||||
assert estimator.estimate_joker() == estimator.estimate_bingo() + 1000
|
||||
Loading…
x
Reference in New Issue
Block a user