From eb31a0f03836079f1595a92a27ff3624036ecbe9 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 15 May 2023 22:21:34 -0700 Subject: [PATCH] Rework wind speed Weibull inputs, tune. The previous method of using a uniform scalar of the MSL wind speed for higher altitudes didn't offer enough control. In particular, the shape needs to be quite different to skew low, mid, high. This patch reworks that system so the parameters of each distribution are configured per-altitude level. To keep some continuity between altitudes (on a windy day, all levels should have higher wind speeds on average), the wind speed of the lower altitude will be added to the scale value of the higher altitude. Since it wasn't practical to approximate the previous behavior with the new system, this also handles the tuning of each. The low altitude speeds remain mostly unchanged (typically around 5 knots expect for thunderstorms), but the average speeds for other altitudes went up to more closely match the previous intent but without the massive overshoot. At 2000m wind speeds are typically in the 20-25 knot range now, and 8000m 30-50 knots. https://www.quora.com/What-is-the-average-wind-speed-at-different-altitudes has some of the source data, and Quora is the most authoritative source there is. It claims that cruise altitude winds can get "as high as 150 knots", but doesn't claim anything about the average. I had a surprisingly difficult time finding good data for cruise altitude air speeds for non-jet stream paths (though many of our maps are in jet streams), so I just eyeballed it from https://turbli.com/wind-during-flights/. https://github.com/dcs-liberation/dcs_liberation/issues/2861 Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2863. --- game/weather.py | 51 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/game/weather.py b/game/weather.py index c11027c7..a023f503 100644 --- a/game/weather.py +++ b/game/weather.py @@ -49,6 +49,12 @@ class WindConditions: at_8000m: Wind +@dataclass(frozen=True) +class WeibullWindSpeedParameters: + shape: float + scale: Speed + + @dataclass(frozen=True) class Clouds: base: int @@ -181,10 +187,9 @@ class Weather: @staticmethod def random_wind( - weibull_shape: float, - weibull_scale_speed: Speed, - at_2000m_factor_range: tuple[float, float], - at_8000m_factor_range: tuple[float, float], + params_at_msl: WeibullWindSpeedParameters, + params_at_2000m: WeibullWindSpeedParameters, + params_at_8000m: WeibullWindSpeedParameters, ) -> WindConditions: """Generates random wind.""" wind_direction = Heading.random() @@ -195,10 +200,14 @@ class Weather: # value. # https://www.itl.nist.gov/div898/handbook/eda/section3/weibplot.htm msl = random.weibullvariate( - weibull_scale_speed.meters_per_second, weibull_shape + 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 ) - at_2000m_factor = random.uniform(*at_2000m_factor_range) - at_8000m_factor = random.uniform(*at_8000m_factor_range) # DCS is limited to 97 knots wind speed. max_supported_wind_speed = knots(97).meters_per_second @@ -208,11 +217,11 @@ class Weather: at_0m=Wind(wind_direction.degrees, max(1.0, msl)), at_2000m=Wind( wind_direction_2000m.degrees, - min(max_supported_wind_speed, msl * at_2000m_factor), + min(max_supported_wind_speed, at_2000m), ), at_8000m=Wind( wind_direction_8000m.degrees, - min(max_supported_wind_speed, msl * at_8000m_factor), + min(max_supported_wind_speed, at_8000m), ), ) @@ -312,7 +321,11 @@ class ClearSkies(Weather): return None def generate_wind(self) -> WindConditions: - return self.random_wind(1.5, knots(5), (1, 1.8), (1.5, 2.5)) + return self.random_wind( + WeibullWindSpeedParameters(1.5, knots(5)), + WeibullWindSpeedParameters(3.5, knots(20)), + WeibullWindSpeedParameters(6.4, knots(20)), + ) class Cloudy(Weather): @@ -336,7 +349,11 @@ class Cloudy(Weather): return None def generate_wind(self) -> WindConditions: - return self.random_wind(1.6, knots(6.5), (1, 1.8), (1.5, 2.5)) + return self.random_wind( + WeibullWindSpeedParameters(1.6, knots(6.5)), + WeibullWindSpeedParameters(3.5, knots(22)), + WeibullWindSpeedParameters(6.4, knots(18)), + ) class Raining(Weather): @@ -360,7 +377,11 @@ class Raining(Weather): return None def generate_wind(self) -> WindConditions: - return self.random_wind(2.6, knots(12), (1, 1.7), (2.2, 2.8)) + return self.random_wind( + WeibullWindSpeedParameters(2.6, knots(8)), + WeibullWindSpeedParameters(4.2, knots(20)), + WeibullWindSpeedParameters(6.4, knots(20)), + ) class Thunderstorm(Weather): @@ -385,7 +406,11 @@ class Thunderstorm(Weather): ) def generate_wind(self) -> WindConditions: - return self.random_wind(6, knots(20), (1, 2), (2.5, 3.5)) + return self.random_wind( + WeibullWindSpeedParameters(6, knots(20)), + WeibullWindSpeedParameters(6.2, knots(20)), + WeibullWindSpeedParameters(6.4, knots(20)), + ) @dataclass