mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Make wind speed moddable.
These should probably be overridable per theater and per season, but even with that we'll want some defaults. https://github.com/dcs-liberation/dcs_liberation/issues/2862
This commit is contained in:
parent
8ed843a9cf
commit
0e139b8640
@ -140,6 +140,11 @@ Saves from 6.x are not compatible with 7.0.
|
|||||||
* **[Modding]** Add support for VSN F-4B and F-4C mod.
|
* **[Modding]** Add support for VSN F-4B and F-4C mod.
|
||||||
* **[Modding]** Aircraft task capabilities and preferred aircraft for each task are now moddable in the aircraft unit yaml files. Each aircraft has a weight per task. Higher weights are given higher preference.
|
* **[Modding]** Aircraft task capabilities and preferred aircraft for each task are now moddable in the aircraft unit yaml files. Each aircraft has a weight per task. Higher weights are given higher preference.
|
||||||
* **[Modding]** The `mission_types` field in squadron files has been removed. Squadron task capability is now determined by airframe, and the auto-assignable list has always been overridden by the campaign settings.
|
* **[Modding]** The `mission_types` field in squadron files has been removed. Squadron task capability is now determined by airframe, and the auto-assignable list has always been overridden by the campaign settings.
|
||||||
|
* **[Modding]** Wind speed generation inputs are now moddable. See https://dcs-liberation.rtfd.io/en/latest/modding/weather.html.
|
||||||
|
* **[New Game Wizard]** Choices for some options will be remembered for the next new game. Not all settings will be preserved, as many are campaign dependent.
|
||||||
|
* **[New Game Wizard]** Lua plugins can now be set while creating a new game.
|
||||||
|
* **[New Game Wizard]** Squadrons can be directly replaced with a preset during air wing configuration rather than needing to remove and create a new squadron.
|
||||||
|
* **[New Game Wizard]** Squadron liveries can now be selected during air wing configuration.
|
||||||
* **[Squadrons]** Squadron-specific mission capability lists no longer restrict players from assigning missions outside the squadron's preferences.
|
* **[Squadrons]** Squadron-specific mission capability lists no longer restrict players from assigning missions outside the squadron's preferences.
|
||||||
* **[New Game Wizard]** Squadrons can be directly replaced with a preset during air wing configuration rather than needing to remove and create a new squadron.
|
* **[New Game Wizard]** Squadrons can be directly replaced with a preset during air wing configuration rather than needing to remove and create a new squadron.
|
||||||
* **[UI]** The orientation of objects like SAMs, EWRs, garrisons, and ships can now be manually adjusted.
|
* **[UI]** The orientation of objects like SAMs, EWRs, garrisons, and ships can now be manually adjusted.
|
||||||
|
|||||||
@ -6,4 +6,5 @@ Modding guide
|
|||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
fuel-consumption-measurement.md
|
fuel-consumption-measurement.md
|
||||||
layouts.rst
|
layouts.rst
|
||||||
|
weather.rst
|
||||||
|
|||||||
76
docs/modding/weather.rst
Normal file
76
docs/modding/weather.rst
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#######
|
||||||
|
Weather
|
||||||
|
#######
|
||||||
|
|
||||||
|
Weather conditions in DCS Liberation are randomly generated at the start of each
|
||||||
|
turn. Some of the inputs to that generator (more to come) can be controlled via
|
||||||
|
the config files in ``resources/weather``.
|
||||||
|
|
||||||
|
**********
|
||||||
|
Archetypes
|
||||||
|
**********
|
||||||
|
|
||||||
|
A weather archetype defines the the conditions for a style of weather, such as
|
||||||
|
"clear", or "raining". There are currently four archetypes:
|
||||||
|
|
||||||
|
1. clear
|
||||||
|
2. cloudy
|
||||||
|
3. raining
|
||||||
|
4. thunderstorm
|
||||||
|
|
||||||
|
The odds of each archetype appearing in each season are defined in the theater
|
||||||
|
yaml files (``resources/theaters/*/info.yaml``).
|
||||||
|
|
||||||
|
.. literalinclude:: ../../resources/weather/archetypes/clear.yaml
|
||||||
|
:language: yaml
|
||||||
|
:linenos:
|
||||||
|
:caption: resources/weather/archetypes/clear.yaml
|
||||||
|
|
||||||
|
Wind speeds
|
||||||
|
===========
|
||||||
|
|
||||||
|
DCS missions define wind with a speed and heading at each of three altitudes:
|
||||||
|
|
||||||
|
1. MSL
|
||||||
|
2. 2000 meters
|
||||||
|
3. 8000 meters
|
||||||
|
|
||||||
|
Blending between each altitude band is done in a manner defined by DCS.
|
||||||
|
|
||||||
|
Liberation randomly generates a direction for the wind at MSL, and each other
|
||||||
|
altitude band will have wind within +/- 90 degrees of that heading.
|
||||||
|
|
||||||
|
Wind speeds can be modded by altering the ``speed`` dict in the archetype yaml.
|
||||||
|
The only random distribution currently supported is the Weibull distribution, so
|
||||||
|
all archetypes currently use:
|
||||||
|
|
||||||
|
.. code:: yaml
|
||||||
|
|
||||||
|
speed:
|
||||||
|
weibull:
|
||||||
|
...
|
||||||
|
|
||||||
|
The Weibull distribution has two parameters: a shape and a scale.
|
||||||
|
|
||||||
|
The scale is simplest to understand. 63.2% of all outcomes of the distribution
|
||||||
|
are below the scale parameter.
|
||||||
|
|
||||||
|
The shape controls where the peak of the distribution is. See the examples in
|
||||||
|
the links below for illustrations and guidelines, but generally speaking low
|
||||||
|
values (between 1 and 2.6) will cause low speeds to be more common, medium
|
||||||
|
values (around 3) will be fairly evenly distributed around the median, and high
|
||||||
|
values (greater than 3.7) will cause high speeds to be more common. As wind
|
||||||
|
speeds tend to be higher at higher altitudes and fairly slow close to the
|
||||||
|
ground, you typically want a low value for MSL, a medium value for 2000m, and a
|
||||||
|
high value for 8000m.
|
||||||
|
|
||||||
|
For examples, see https://statisticsbyjim.com/probability/weibull-distribution/.
|
||||||
|
To experiment with different inputs, use Wolfram Alpha, e.g.
|
||||||
|
https://www.wolframalpha.com/input?i=weibull+distribution+1.5+5.
|
||||||
|
|
||||||
|
When generating wind speeds, each subsequent altitude band will have the lower
|
||||||
|
band's speed added to its scale parameter. That is, for the example above, the
|
||||||
|
actual scale parameter of ``at_2000m`` will be ``20 + wind speed at MSL``, and
|
||||||
|
the scale parameter of ``at_8000m`` will be ``20 + wind speed at 2000m``. This
|
||||||
|
is to ensure that a generally windy day (high wind speed at MSL) will create
|
||||||
|
similarly high winds at higher altitudes and vice versa.
|
||||||
@ -79,7 +79,9 @@ class Conditions:
|
|||||||
season = determine_season(day)
|
season = determine_season(day)
|
||||||
logging.debug("Weather: Season {}".format(season))
|
logging.debug("Weather: Season {}".format(season))
|
||||||
weather_chances = seasonal_conditions.weather_type_chances[season]
|
weather_chances = seasonal_conditions.weather_type_chances[season]
|
||||||
chances = {
|
chances: dict[
|
||||||
|
type[ClearSkies] | type[Cloudy] | type[Raining] | type[Thunderstorm], float
|
||||||
|
] = {
|
||||||
Thunderstorm: weather_chances.thunderstorm,
|
Thunderstorm: weather_chances.thunderstorm,
|
||||||
Raining: weather_chances.raining,
|
Raining: weather_chances.raining,
|
||||||
Cloudy: weather_chances.cloudy,
|
Cloudy: weather_chances.cloudy,
|
||||||
|
|||||||
@ -4,25 +4,23 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
from dataclasses import dataclass
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
from typing import Optional, TYPE_CHECKING
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
from dcs.weather import Weather as PydcsWeather, Wind
|
from dcs.weather import Weather as PydcsWeather
|
||||||
|
|
||||||
from game.timeofday import TimeOfDay
|
from game.timeofday import TimeOfDay
|
||||||
from game.utils import (
|
from game.utils import (
|
||||||
Heading,
|
|
||||||
Pressure,
|
Pressure,
|
||||||
inches_hg,
|
inches_hg,
|
||||||
interpolate,
|
interpolate,
|
||||||
knots,
|
|
||||||
meters,
|
meters,
|
||||||
Speed,
|
|
||||||
)
|
)
|
||||||
from game.weather.atmosphericconditions import AtmosphericConditions
|
from game.weather.atmosphericconditions import AtmosphericConditions
|
||||||
from game.weather.clouds import Clouds
|
from game.weather.clouds import Clouds
|
||||||
from game.weather.fog import Fog
|
from game.weather.fog import Fog
|
||||||
|
from game.weather.weatherarchetype import WeatherArchetype, WeatherArchetypes
|
||||||
from game.weather.wind import WindConditions
|
from game.weather.wind import WindConditions
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -35,13 +33,7 @@ class NightMissions(Enum):
|
|||||||
OnlyNight = "nightmissions_onlynight"
|
OnlyNight = "nightmissions_onlynight"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
class Weather(ABC):
|
||||||
class WeibullWindSpeedParameters:
|
|
||||||
shape: float
|
|
||||||
scale: Speed
|
|
||||||
|
|
||||||
|
|
||||||
class Weather:
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
seasonal_conditions: SeasonalConditions,
|
seasonal_conditions: SeasonalConditions,
|
||||||
@ -114,6 +106,11 @@ class Weather:
|
|||||||
)
|
)
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def archetype(self) -> WeatherArchetype:
|
||||||
|
...
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pressure_adjustment(self) -> float:
|
def pressure_adjustment(self) -> float:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -138,47 +135,7 @@ class Weather:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def generate_wind(self) -> WindConditions:
|
def generate_wind(self) -> WindConditions:
|
||||||
raise NotImplementedError
|
return self.archetype.wind_parameters.speed.random_wind()
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def random_wind(
|
|
||||||
params_at_msl: WeibullWindSpeedParameters,
|
|
||||||
params_at_2000m: WeibullWindSpeedParameters,
|
|
||||||
params_at_8000m: WeibullWindSpeedParameters,
|
|
||||||
) -> WindConditions:
|
|
||||||
"""Generates random wind."""
|
|
||||||
wind_direction = Heading.random()
|
|
||||||
wind_direction_2000m = wind_direction + Heading.random(-90, 90)
|
|
||||||
wind_direction_8000m = wind_direction + Heading.random(-90, 90)
|
|
||||||
|
|
||||||
# The first parameter is the scale. 63.2% of all results will fall below that
|
|
||||||
# value.
|
|
||||||
# https://www.itl.nist.gov/div898/handbook/eda/section3/weibplot.htm
|
|
||||||
msl = random.weibullvariate(
|
|
||||||
params_at_msl.scale.meters_per_second, params_at_msl.shape
|
|
||||||
)
|
|
||||||
at_2000m = random.weibullvariate(
|
|
||||||
msl + params_at_2000m.scale.meters_per_second, params_at_2000m.shape
|
|
||||||
)
|
|
||||||
at_8000m = random.weibullvariate(
|
|
||||||
at_2000m + params_at_8000m.scale.meters_per_second, params_at_8000m.shape
|
|
||||||
)
|
|
||||||
|
|
||||||
# DCS is limited to 97 knots wind speed.
|
|
||||||
max_supported_wind_speed = knots(97).meters_per_second
|
|
||||||
|
|
||||||
return WindConditions(
|
|
||||||
# Always some wind to make the smoke move a bit.
|
|
||||||
at_0m=Wind(wind_direction.degrees, max(1.0, msl)),
|
|
||||||
at_2000m=Wind(
|
|
||||||
wind_direction_2000m.degrees,
|
|
||||||
min(max_supported_wind_speed, at_2000m),
|
|
||||||
),
|
|
||||||
at_8000m=Wind(
|
|
||||||
wind_direction_8000m.degrees,
|
|
||||||
min(max_supported_wind_speed, at_8000m),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def random_cloud_base() -> int:
|
def random_cloud_base() -> int:
|
||||||
@ -257,6 +214,10 @@ class Weather:
|
|||||||
|
|
||||||
|
|
||||||
class ClearSkies(Weather):
|
class ClearSkies(Weather):
|
||||||
|
@property
|
||||||
|
def archetype(self) -> WeatherArchetype:
|
||||||
|
return WeatherArchetypes.with_id("clear")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pressure_adjustment(self) -> float:
|
def pressure_adjustment(self) -> float:
|
||||||
return 0.22
|
return 0.22
|
||||||
@ -275,15 +236,12 @@ class ClearSkies(Weather):
|
|||||||
def generate_fog(self) -> Optional[Fog]:
|
def generate_fog(self) -> Optional[Fog]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def generate_wind(self) -> WindConditions:
|
|
||||||
return self.random_wind(
|
|
||||||
WeibullWindSpeedParameters(1.5, knots(5)),
|
|
||||||
WeibullWindSpeedParameters(3.5, knots(20)),
|
|
||||||
WeibullWindSpeedParameters(6.4, knots(20)),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Cloudy(Weather):
|
class Cloudy(Weather):
|
||||||
|
@property
|
||||||
|
def archetype(self) -> WeatherArchetype:
|
||||||
|
return WeatherArchetypes.with_id("cloudy")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pressure_adjustment(self) -> float:
|
def pressure_adjustment(self) -> float:
|
||||||
return 0.0
|
return 0.0
|
||||||
@ -303,15 +261,12 @@ class Cloudy(Weather):
|
|||||||
# DCS 2.7 says to not use fog with the cloud presets.
|
# DCS 2.7 says to not use fog with the cloud presets.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def generate_wind(self) -> WindConditions:
|
|
||||||
return self.random_wind(
|
|
||||||
WeibullWindSpeedParameters(1.6, knots(6.5)),
|
|
||||||
WeibullWindSpeedParameters(3.5, knots(22)),
|
|
||||||
WeibullWindSpeedParameters(6.4, knots(18)),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Raining(Weather):
|
class Raining(Weather):
|
||||||
|
@property
|
||||||
|
def archetype(self) -> WeatherArchetype:
|
||||||
|
return WeatherArchetypes.with_id("raining")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pressure_adjustment(self) -> float:
|
def pressure_adjustment(self) -> float:
|
||||||
return -0.22
|
return -0.22
|
||||||
@ -331,15 +286,12 @@ class Raining(Weather):
|
|||||||
# DCS 2.7 says to not use fog with the cloud presets.
|
# DCS 2.7 says to not use fog with the cloud presets.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def generate_wind(self) -> WindConditions:
|
|
||||||
return self.random_wind(
|
|
||||||
WeibullWindSpeedParameters(2.6, knots(8)),
|
|
||||||
WeibullWindSpeedParameters(4.2, knots(20)),
|
|
||||||
WeibullWindSpeedParameters(6.4, knots(20)),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Thunderstorm(Weather):
|
class Thunderstorm(Weather):
|
||||||
|
@property
|
||||||
|
def archetype(self) -> WeatherArchetype:
|
||||||
|
return WeatherArchetypes.with_id("thunderstorm")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pressure_adjustment(self) -> float:
|
def pressure_adjustment(self) -> float:
|
||||||
return 0.1
|
return 0.1
|
||||||
@ -359,10 +311,3 @@ class Thunderstorm(Weather):
|
|||||||
thickness=self.random_cloud_thickness(),
|
thickness=self.random_cloud_thickness(),
|
||||||
precipitation=PydcsWeather.Preceptions.Thunderstorm,
|
precipitation=PydcsWeather.Preceptions.Thunderstorm,
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_wind(self) -> WindConditions:
|
|
||||||
return self.random_wind(
|
|
||||||
WeibullWindSpeedParameters(6, knots(20)),
|
|
||||||
WeibullWindSpeedParameters(6.2, knots(20)),
|
|
||||||
WeibullWindSpeedParameters(6.4, knots(20)),
|
|
||||||
)
|
|
||||||
|
|||||||
58
game/weather/weatherarchetype.py
Normal file
58
game/weather/weatherarchetype.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from .windspeedgenerators import WindSpeedGenerator
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class WindParameters:
|
||||||
|
speed: WindSpeedGenerator
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data: dict[str, Any]) -> WindParameters:
|
||||||
|
return WindParameters(speed=WindSpeedGenerator.from_data(data["speed"]))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class WeatherArchetype:
|
||||||
|
id: str
|
||||||
|
wind_parameters: WindParameters
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data: dict[str, Any]) -> WeatherArchetype:
|
||||||
|
return WeatherArchetype(
|
||||||
|
id=data["id"], wind_parameters=WindParameters.from_data(data["wind"])
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_yaml(path: Path) -> WeatherArchetype:
|
||||||
|
with path.open(encoding="utf-8") as yaml_file:
|
||||||
|
data = yaml.safe_load(yaml_file)
|
||||||
|
return WeatherArchetype.from_data(data)
|
||||||
|
|
||||||
|
|
||||||
|
class WeatherArchetypes:
|
||||||
|
_by_id: dict[str, WeatherArchetype] | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def with_id(cls, ident: str) -> WeatherArchetype:
|
||||||
|
if cls._by_id is None:
|
||||||
|
cls._by_id = cls.load()
|
||||||
|
return cls._by_id[ident]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load() -> dict[str, WeatherArchetype]:
|
||||||
|
by_id = {}
|
||||||
|
for path in Path("resources/weather/archetypes").glob("*.yaml"):
|
||||||
|
archetype = WeatherArchetype.from_yaml(path)
|
||||||
|
if archetype.id in by_id:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Found duplicate weather archetype ID: {archetype.id}"
|
||||||
|
)
|
||||||
|
by_id[archetype.id] = archetype
|
||||||
|
return by_id
|
||||||
95
game/weather/windspeedgenerators.py
Normal file
95
game/weather/windspeedgenerators.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from dcs.weather import Wind
|
||||||
|
|
||||||
|
from game.utils import Speed, knots, Heading
|
||||||
|
from .wind import WindConditions
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class WeibullWindSpeedParameters:
|
||||||
|
shape: float
|
||||||
|
scale: Speed
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data: dict[str, Any]) -> WeibullWindSpeedParameters:
|
||||||
|
return WeibullWindSpeedParameters(
|
||||||
|
shape=data["shape"], scale=knots(data["scale_kts"])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WindSpeedGenerator(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def random_wind(self) -> WindConditions:
|
||||||
|
...
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data: dict[str, Any]) -> WindSpeedGenerator:
|
||||||
|
if len(data) != 1:
|
||||||
|
raise ValueError(
|
||||||
|
f"Wind speed dict has wrong number of keys ({len(data)}). Expected 1."
|
||||||
|
)
|
||||||
|
name = list(data.keys())[0]
|
||||||
|
match name:
|
||||||
|
case "weibull":
|
||||||
|
return WeibullWindSpeedGenerator.from_data(data["weibull"])
|
||||||
|
raise KeyError(f"Unknown wind speed generator type: {name}")
|
||||||
|
|
||||||
|
|
||||||
|
class WeibullWindSpeedGenerator(WindSpeedGenerator):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
at_msl: WeibullWindSpeedParameters,
|
||||||
|
at_2000m: WeibullWindSpeedParameters,
|
||||||
|
at_8000m: WeibullWindSpeedParameters,
|
||||||
|
) -> None:
|
||||||
|
self.at_msl = at_msl
|
||||||
|
self.at_2000m = at_2000m
|
||||||
|
self.at_8000m = at_8000m
|
||||||
|
|
||||||
|
def random_wind(self) -> WindConditions:
|
||||||
|
wind_direction = Heading.random()
|
||||||
|
wind_direction_2000m = wind_direction + Heading.random(-90, 90)
|
||||||
|
wind_direction_8000m = wind_direction + Heading.random(-90, 90)
|
||||||
|
|
||||||
|
# The first parameter is the scale. 63.2% of all results will fall below that
|
||||||
|
# value.
|
||||||
|
# https://www.itl.nist.gov/div898/handbook/eda/section3/weibplot.htm
|
||||||
|
msl = random.weibullvariate(
|
||||||
|
self.at_msl.scale.meters_per_second, self.at_msl.shape
|
||||||
|
)
|
||||||
|
at_2000m = random.weibullvariate(
|
||||||
|
msl + self.at_2000m.scale.meters_per_second, self.at_2000m.shape
|
||||||
|
)
|
||||||
|
at_8000m = random.weibullvariate(
|
||||||
|
at_2000m + self.at_8000m.scale.meters_per_second, self.at_8000m.shape
|
||||||
|
)
|
||||||
|
|
||||||
|
# DCS is limited to 97 knots wind speed.
|
||||||
|
max_supported_wind_speed = knots(97).meters_per_second
|
||||||
|
|
||||||
|
return WindConditions(
|
||||||
|
# Always some wind to make the smoke move a bit.
|
||||||
|
at_0m=Wind(wind_direction.degrees, max(1.0, msl)),
|
||||||
|
at_2000m=Wind(
|
||||||
|
wind_direction_2000m.degrees,
|
||||||
|
min(max_supported_wind_speed, at_2000m),
|
||||||
|
),
|
||||||
|
at_8000m=Wind(
|
||||||
|
wind_direction_8000m.degrees,
|
||||||
|
min(max_supported_wind_speed, at_8000m),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_data(data: dict[str, Any]) -> WindSpeedGenerator:
|
||||||
|
return WeibullWindSpeedGenerator(
|
||||||
|
at_msl=WeibullWindSpeedParameters.from_data(data["at_msl"]),
|
||||||
|
at_2000m=WeibullWindSpeedParameters.from_data(data["at_2000m"]),
|
||||||
|
at_8000m=WeibullWindSpeedParameters.from_data(data["at_8000m"]),
|
||||||
|
)
|
||||||
14
resources/weather/archetypes/clear.yaml
Normal file
14
resources/weather/archetypes/clear.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
id: clear
|
||||||
|
wind:
|
||||||
|
speed:
|
||||||
|
weibull:
|
||||||
|
at_msl:
|
||||||
|
shape: 1.5
|
||||||
|
scale_kts: 5
|
||||||
|
at_2000m:
|
||||||
|
shape: 3.5
|
||||||
|
scale_kts: 20
|
||||||
|
at_8000m:
|
||||||
|
shape: 6.4
|
||||||
|
scale_kts: 20
|
||||||
14
resources/weather/archetypes/cloudy.yaml
Normal file
14
resources/weather/archetypes/cloudy.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
id: cloudy
|
||||||
|
wind:
|
||||||
|
speed:
|
||||||
|
weibull:
|
||||||
|
at_msl:
|
||||||
|
shape: 1.6
|
||||||
|
scale_kts: 6.5
|
||||||
|
at_2000m:
|
||||||
|
shape: 3.5
|
||||||
|
scale_kts: 22
|
||||||
|
at_8000m:
|
||||||
|
shape: 6.4
|
||||||
|
scale_kts: 18
|
||||||
14
resources/weather/archetypes/raining.yaml
Normal file
14
resources/weather/archetypes/raining.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
id: raining
|
||||||
|
wind:
|
||||||
|
speed:
|
||||||
|
weibull:
|
||||||
|
at_msl:
|
||||||
|
shape: 2.6
|
||||||
|
scale_kts: 8
|
||||||
|
at_2000m:
|
||||||
|
shape: 4.2
|
||||||
|
scale_kts: 20
|
||||||
|
at_8000m:
|
||||||
|
shape: 6.4
|
||||||
|
scale_kts: 20
|
||||||
14
resources/weather/archetypes/thunderstorm.yaml
Normal file
14
resources/weather/archetypes/thunderstorm.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
id: thunderstorm
|
||||||
|
wind:
|
||||||
|
speed:
|
||||||
|
weibull:
|
||||||
|
at_msl:
|
||||||
|
shape: 6
|
||||||
|
scale_kts: 20
|
||||||
|
at_2000m:
|
||||||
|
shape: 6.2
|
||||||
|
scale_kts: 20
|
||||||
|
at_8000m:
|
||||||
|
shape: 6.4
|
||||||
|
scale_kts: 20
|
||||||
Loading…
x
Reference in New Issue
Block a user