diff --git a/game/utils.py b/game/utils.py index 291e098b..bdd849ce 100644 --- a/game/utils.py +++ b/game/utils.py @@ -16,6 +16,9 @@ KPH_TO_KNOTS = 1 / KNOTS_TO_KPH MS_TO_KPH = 3.6 KPH_TO_MS = 1 / MS_TO_KPH +INHG_TO_HPA = 33.86389 +INHG_TR_MMHG = 25.400002776728 + def heading_sum(h: int, a: int) -> int: h += a @@ -181,6 +184,27 @@ def mach(value: float, altitude: Distance) -> Speed: SPEED_OF_SOUND_AT_SEA_LEVEL = knots(661.5) +@dataclass(frozen=True, order=True) +class Pressure: + pressure_in_inches_hg: float + + @property + def inches_hg(self) -> float: + return self.pressure_in_inches_hg + + @property + def mm_hg(self) -> float: + return self.pressure_in_inches_hg * INHG_TR_MMHG + + @property + def hecto_pascals(self) -> float: + return self.pressure_in_inches_hg * INHG_TO_HPA + + +def inches_hg(value: float) -> Pressure: + return Pressure(value) + + def pairwise(iterable: Iterable[Any]) -> Iterable[tuple[Any, Any]]: """ itertools recipe diff --git a/game/weather.py b/game/weather.py index ae31fa7f..952335bd 100644 --- a/game/weather.py +++ b/game/weather.py @@ -5,13 +5,14 @@ import logging import random from dataclasses import dataclass, field from enum import Enum -from typing import Optional, TYPE_CHECKING +from typing import Optional, TYPE_CHECKING, Any from dcs.cloud_presets import Clouds as PydcsClouds from dcs.weather import CloudPreset, Weather as PydcsWeather, Wind +from game.savecompat import has_save_compat_for from game.settings import Settings -from game.utils import Distance, meters, interpolate +from game.utils import Distance, meters, interpolate, Pressure, inches_hg if TYPE_CHECKING: from game.theater import ConflictTheater @@ -27,11 +28,19 @@ class TimeOfDay(Enum): @dataclass(frozen=True) class AtmosphericConditions: - #: Pressure at sea level in inches of mercury. - qnh_inches_mercury: float + #: Pressure at sea level. + qnh: Pressure + #: Temperature at sea level in Celcius. temperature_celsius: float + @has_save_compat_for(5) + def __setstate__(self, state: dict[str, Any]) -> None: + if "qnh" not in state: + state["qnh"] = inches_hg(state["qnh_inches_mercury"]) + del state["qnh_inches_mercury"] + self.__dict__.update(state) + @dataclass(frozen=True) class WindConditions: @@ -111,7 +120,7 @@ class Weather: pressure += self.pressure_adjustment temperature += self.temperature_adjustment conditions = AtmosphericConditions( - qnh_inches_mercury=self.random_pressure(pressure), + qnh=self.random_pressure(pressure), temperature_celsius=self.random_temperature(temperature), ) return conditions @@ -162,14 +171,14 @@ class Weather: return random.randint(100, 400) @staticmethod - def random_pressure(average_pressure: float) -> float: + def random_pressure(average_pressure: float) -> Pressure: # "Safe" constants based roughly on ME and viper altimeter. # Units are inches of mercury. SAFE_MIN = 28.4 SAFE_MAX = 30.9 # Use normalvariate to get normal distribution, more realistic than uniform pressure = random.normalvariate(average_pressure, 0.1) - return max(SAFE_MIN, min(SAFE_MAX, pressure)) + return inches_hg(max(SAFE_MIN, min(SAFE_MAX, pressure))) @staticmethod def random_temperature(average_temperature: float) -> float: diff --git a/gen/environmentgen.py b/gen/environmentgen.py index 2bc9da84..84f5bd59 100644 --- a/gen/environmentgen.py +++ b/gen/environmentgen.py @@ -3,7 +3,6 @@ from typing import Optional from dcs.mission import Mission from game.weather import Clouds, Fog, Conditions, WindConditions, AtmosphericConditions -from .units import inches_hg_to_mm_hg class EnvironmentGenerator: @@ -12,7 +11,7 @@ class EnvironmentGenerator: self.conditions = conditions def set_atmospheric(self, atmospheric: AtmosphericConditions) -> None: - self.mission.weather.qnh = inches_hg_to_mm_hg(atmospheric.qnh_inches_mercury) + self.mission.weather.qnh = atmospheric.qnh.mm_hg self.mission.weather.season_temperature = atmospheric.temperature_celsius def set_clouds(self, clouds: Optional[Clouds]) -> None: diff --git a/gen/kneeboard.py b/gen/kneeboard.py index a9c1d1c2..20fb8ca1 100644 --- a/gen/kneeboard.py +++ b/gen/kneeboard.py @@ -47,7 +47,6 @@ from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator from .flights.flight import FlightWaypoint, FlightWaypointType, FlightType from .radios import RadioFrequency from .runways import RunwayData -from .units import inches_hg_to_mm_hg, inches_hg_to_hpa if TYPE_CHECKING: from game import Game @@ -308,13 +307,9 @@ class BriefingPage(KneeboardPage): writer.text(f"Bullseye: {self.bullseye.to_lat_lon(self.theater).format_dms()}") - qnh_in_hg = "{:.2f}".format(self.weather.atmospheric.qnh_inches_mercury) - qnh_mm_hg = "{:.1f}".format( - inches_hg_to_mm_hg(self.weather.atmospheric.qnh_inches_mercury) - ) - qnh_hpa = "{:.1f}".format( - inches_hg_to_hpa(self.weather.atmospheric.qnh_inches_mercury) - ) + qnh_in_hg = f"{self.weather.atmospheric.qnh.inches_hg:.2f}" + qnh_mm_hg = f"{self.weather.atmospheric.qnh.mm_hg:.1f}" + qnh_hpa = f"{self.weather.atmospheric.qnh.hecto_pascals:.1f}" writer.text( f"Temperature: {round(self.weather.atmospheric.temperature_celsius)} °C at sea level" ) diff --git a/gen/units.py b/gen/units.py deleted file mode 100644 index 9aec8348..00000000 --- a/gen/units.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Unit conversions.""" - - -def meters_to_feet(meters: float) -> float: - """Converts meters to feet.""" - return meters * 3.28084 - - -def inches_hg_to_mm_hg(inches_hg: float) -> float: - """Converts inches mercury to millimeters mercury.""" - return inches_hg * 25.400002776728 - - -def inches_hg_to_hpa(inches_hg: float) -> float: - """Converts inches mercury to hectopascal.""" - return inches_hg * 33.86389