def _make_wind(weather_object): if not weather_object.metar_data.wind_speed: LOGGER.warning( 'no wind speed data found in METAR, using triangular randomized wind speed ' '(low=0, high=25, mode= 7) [knots]') weather_object.wind_speed = WindSpeed(random.triangular(0, 25, mode=7), 'kt') # nosec else: weather_object.wind_speed = WindSpeed( weather_object.metar_data.wind_speed.value, weather_object.metar_units.wind_speed) if not weather_object.metar_data.wind_direction: LOGGER.warning( 'no wind direction data found in METAR, using random wind direction' '(between 0 and 359) [degrees]') weather_object.wind_direction = WindDirection(random.randint( 0, 359)) # nosec else: if weather_object.metar_data.wind_direction.repr == 'VRB': weather_object.wind_direction_is_variable = True weather_object.wind_direction = WindDirection( weather_object.metar_data.wind_direction.value) weather_object.wind_direction_range = [ WindDirection(wind_dir.value) for wind_dir in weather_object.metar_data.wind_variable_direction ] if weather_object.metar_data.wind_gust: weather_object.wind_gust = WindSpeed( weather_object.metar_data.wind_gust.value, weather_object.metar_units.wind_speed) else: weather_object.wind_gust = WindSpeed(0)
def _make_clouds(weather_object) -> typing.Tuple[int, int, int]: cloud_density = 0 cloud_base = 300 cloud_thickness = 200 _layer_in_use = None for _layer in weather_object.cloud_layers: _coverage = random.randint(*CLOUD_METAR_TO_DCS[_layer.type]) # nosec if _coverage > cloud_density: cloud_density = _coverage _layer_in_use = _layer if _layer_in_use: LOGGER.debug('using cloud layer: %s', _layer_in_use.repr) if not _layer_in_use.altitude: LOGGER.warning( 'no cloud altitude data found in METAR, using random number between 5 and 35 thousand feet' ) cloud_base = random.randint(5, 25) * 1000 # nosec else: if not weather_object.metar_units.altitude == 'ft': raise ValueError(weather_object.metar_units.altitude) cloud_base = CloudBase(_layer_in_use.altitude * 100, 'ft').value('m') cloud_thickness = int(cloud_density / 10 * 2000) else: LOGGER.debug('no clouds') cloud_base = DCSWeather.normalize_cloud_base(cloud_base) cloud_thickness = DCSWeather.normalize_cloud_thickness(cloud_thickness) return cloud_base, cloud_density, cloud_thickness
def _make_altimeter(weather_object): if not weather_object.metar_data.altimeter: LOGGER.warning( 'no altimeter data found in METAR, using triangular randomized pressure ' '(low=720, high=790, mode= 760) [mmHg]') weather_object.altimeter = Pressure( int(random.triangular(720, 790, mode=760))) # nosec else: weather_object.altimeter = Pressure( weather_object.metar_data.altimeter.value, weather_object.metar_units.altimeter)
def _make_dew_point(weather_object): if weather_object.metar_data.dewpoint: weather_object.dew_point = Temperature( weather_object.metar_data.dewpoint.value, weather_object.metar_units.temperature) else: LOGGER.warning( 'no dew point data found in METAR, creating dummy dew point from temperature' ) weather_object.dew_point = weather_object.temperature.make_dummy_dew_point( )
def normalize_turbulence(value: float) -> int: """ Enforces DCS constraints for turbulence value """ if value < 0: LOGGER.warning('turbulence value is too low, normalizing to 0') return 0 if value > 60: LOGGER.warning('temperature value is too high, normalizing to 60') return 60 return int(round(value, 0))
def _make_temperature(weather_object): if weather_object.metar_data.temperature: weather_object.temperature = Temperature( weather_object.metar_data.temperature.value, weather_object.metar_units.temperature) else: LOGGER.warning( 'no temperature value found in METAR, using triangular randomized temperature ' '(low=-10, high=40, mode= 18) [degrees Celsius]') weather_object.temperature = Temperature( round(int(random.triangular(-10, 40, mode=18)), 0), 'c') # nosec
def normalize_wind_speed(value: float, name: typing.Optional[str] = 'wind speed') -> int: """ Enforces DCS constraints for wind speed value """ if value < 0: LOGGER.warning('%s is too low, normalizing to 0 m/s', name) return 0 if value > 50: LOGGER.warning('%s is too high, normalizing to 50 m/s', name) return 50 return int(value)
def normalize_cloud_base(value: float) -> int: """ Enforces DCS constraints for cloud base value """ if value < 300: LOGGER.warning( 'cloud base value is too low, normalizing to 300 meters') return 300 if value > 5000: LOGGER.warning( 'cloud base value is too high, normalizing to 5000 meters') return 5000 return int(value)
def normalize_fog_visibility(value: float) -> int: """ Enforces DCS constraints for fog visibility value """ if value < 0: LOGGER.warning( 'fog visibility value is too low, normalizing to 0 meters') return 0 if value > 6000: LOGGER.warning( 'fog visibility value is too high, normalizing to 6000 meters') return 6000 return int(value)
def normalize_dust_density(value: float) -> int: """ Enforces DCS constraints for dust density value """ if value < 300: LOGGER.warning( 'dust density value is too low, normalizing to 300 meters') return 300 if value > 3000: LOGGER.warning( 'dust density value is too high, normalizing to 3000 meters') return 3000 return int(value)
def normalize_temperature(value: float) -> int: """ Enforces DCS constraints for temperature value """ if value < -20: LOGGER.warning( 'temperature value is too low, normalizing to -20° Celsius') return -20 if value > 40: LOGGER.warning( 'temperature value is too high, normalizing to 40° Celsius') return 40 return int(value)
def normalize_altimeter(value: float) -> int: """ Enforces DCS constraints for altimeter value """ if value < 720: LOGGER.warning( 'altimeter value is too low, normalizing to 720 mmHg') return 720 if value > 790: LOGGER.warning( 'altimeter value is too high, normalizing to 790 mmHg') return 790 return int(value)
def _make_dust(weather_object) -> typing.Tuple[bool, int]: dust_enabled = False dust_density = 3000 for _dust_indicator in {'DU', 'DS', 'PO', 'SS'}: if any(_dust_indicator in other for other in weather_object.metar_data.other): if weather_object.visibility.value() > 5000: LOGGER.debug('there is dust in the area but visibility is over 5000m, not adding dust') break else: LOGGER.warning('there is dust in the area, visibility will be severely restricted') dust_enabled = True dust_density = DCSWeather.normalize_dust_density(weather_object.visibility.value()) break return dust_enabled, dust_density
def normalize_cloud_thickness(value: float) -> int: """ Enforces DCS constraints for cloud thickness value """ if value < 200: LOGGER.warning( 'cloud thickness value is too low, normalizing to 200 meters') return 200 if value > 2000: LOGGER.warning( 'cloud thickness value is too high, normalizing to 2000 meters' ) return 2000 return int(value)
def _make_visibility(weather_object): if not weather_object.metar_data.visibility: LOGGER.warning( 'no visibility data found in METAR, using triangular randomized visibility ' '(low=2000, high=20000, mode= 15000) [meters]') weather_object.visibility = Visibility( min((round(int(random.triangular(2000, 20000, mode=15000)), -2), 9999))) # nosec else: if weather_object.metar_data.visibility.repr == 'P6': weather_object.visibility = Visibility(9999, 'm') elif weather_object.metar_data.visibility.repr == 'M1/4': weather_object.visibility = Visibility(400, 'm') else: weather_object.visibility = Visibility( weather_object.metar_data.visibility.value, weather_object.metar_units.visibility)
def _as_str(self, spoken: bool) -> str: intro = self._make_str_intro(spoken=spoken) wind = self._wind_as_str(spoken=spoken) visibility = self._visibility_as_str(spoken=spoken) temperature = self._temperature_as_str(spoken=spoken) dew_point = self._dew_point_as_str(spoken=spoken) altimeter = self._altimeter_as_str(spoken=spoken) other = self._others_as_str() clouds = self._clouds_as_str(spoken=spoken) try: remarks = self._remarks_as_str(spoken=spoken) or '' except (IndexError, ValueError): LOGGER.warning('failed to parse remarks: %s', self.remarks) remarks = '' _result = [intro, wind, visibility, temperature, dew_point, altimeter, other, clouds, remarks] _result = [x for x in _result if x != ''] result = ' '.join(_result) return result.replace(' ', ' ')
def _parse_cloud_layer_as_str(self, cloud, ret: list, spoken: bool): if cloud.altitude is None: LOGGER.warning('no altitude given, skipping cloud layer: %s', cloud.repr) return cloud_str = static.CLOUD_TRANSLATIONS[cloud.type] if cloud.modifier: try: cloud_str += f' ({static.CLOUD_TRANSLATIONS[cloud.modifier]})' except KeyError: LOGGER.warning('unknown cloud modifier: %s', cloud.modifier) altitude_as_str = str(cloud.altitude) while len(altitude_as_str) != 3: altitude_as_str = '0' + altitude_as_str if spoken: cloud_alt = [] _cloud_alt_in_thousand_feet_str = int(cloud.altitude / 10) if _cloud_alt_in_thousand_feet_str > 0: cloud_alt.append( utils.num_to_words(_cloud_alt_in_thousand_feet_str, group=0) + ' thousand') clouds_altitude_hundreds = altitude_as_str[2] if clouds_altitude_hundreds != '0': cloud_alt.append( utils.num_to_words(clouds_altitude_hundreds, group=0)) cloud_alt.append('hundred') cloud_alt.append('feet') ret.append(cloud_str.format(' '.join(cloud_alt))) else: cloud_base = Altitude(cloud.altitude * 100, self.metar_units.altitude) ret.append( cloud_str.format(cloud_base.to_string(unit='ft', spoken=spoken)))
def _make_precipitations(weather_object, temperature, cloud_density) -> typing.Tuple[int, int, int]: precipitation_code = 0 for phenomenon in WEATHER_PHENOMENONS: for indicator in phenomenon.indicators: if any(indicator in other for other in weather_object.metar_data.other): precipitation_code = phenomenon.precipitation_code LOGGER.warning('%s reported in the area', phenomenon.name) if phenomenon.min_temp is not None and temperature < phenomenon.min_temp: LOGGER.warning('forcing temperature to %s due to %s', phenomenon.min_temp, phenomenon.name) temperature = phenomenon.min_temp if phenomenon.max_temp is not None and temperature > phenomenon.max_temp: LOGGER.warning('forcing temperature to %s due to %s', phenomenon.max_temp, phenomenon.name) temperature = phenomenon.max_temp if cloud_density < phenomenon.min_cloud_density: LOGGER.warning('forcing cloud density to %s due to %s', phenomenon.min_cloud_density, phenomenon.name) cloud_density = phenomenon.min_cloud_density break return precipitation_code, temperature, cloud_density