From 7e817406feefd80269b7aaf2fab97718370500b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 12:37:27 -0300 Subject: [PATCH 01/17] TAF WindShear --- lib/src/models/metar/metar.dart | 16 ++-- .../models/metar/models/weather_trend.dart | 3 +- lib/src/models/models.dart | 1 + .../models/taf/models/change_forecast.dart | 1 + lib/src/models/taf/models/windshear.dart | 40 ++++++++++ lib/src/utils/metar_regexp.dart | 2 +- lib/src/utils/taf_regexp.dart | 6 ++ test/taf/changes_change_indicators_test.dart | 9 ++- test/taf/wind_test.dart | 73 +++++++++++++++++++ 9 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 lib/src/models/taf/models/windshear.dart diff --git a/lib/src/models/metar/metar.dart b/lib/src/models/metar/metar.dart index 7a6ca65..398c2fb 100644 --- a/lib/src/models/metar/metar.dart +++ b/lib/src/models/metar/metar.dart @@ -125,8 +125,8 @@ class Metar extends Report /// Get the recent weather data of the METAR. MetarRecentWeather get recentWeather => _recentWeather; - void _handleWindshear(String group) { - final match = MetarRegExp.WINDSHEAR.firstMatch(group); + void _handleWindshearRunway(String group) { + final match = MetarRegExp.WINDSHEAR_RUNWAY.firstMatch(group); final windshear = MetarWindshearRunway(group, match); _windshear.add(windshear); @@ -190,9 +190,9 @@ class Metar extends Report GroupHandler(MetarRegExp.TEMPERATURES, _handleTemperatures), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.RECENT_WEATHER, _handleRecentWeather), - GroupHandler(MetarRegExp.WINDSHEAR, _handleWindshear), - GroupHandler(MetarRegExp.WINDSHEAR, _handleWindshear), - GroupHandler(MetarRegExp.WINDSHEAR, _handleWindshear), + GroupHandler(MetarRegExp.WINDSHEAR_RUNWAY, _handleWindshearRunway), + GroupHandler(MetarRegExp.WINDSHEAR_RUNWAY, _handleWindshearRunway), + GroupHandler(MetarRegExp.WINDSHEAR_RUNWAY, _handleWindshearRunway), GroupHandler(MetarRegExp.SEA_STATE, _handleSeaState), GroupHandler(MetarRegExp.RUNWAY_STATE, _handleRunwayState), ]; @@ -242,9 +242,9 @@ class Metar extends Report space: 'left', ); - String trend = ''; - String remark = ''; - String body = ''; + var trend = ''; + var remark = ''; + var body = ''; for (final section in sections) { if (section.startsWith('TEMPO') || section.startsWith('BECMG') || diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index 1fb05b7..bbb5fcf 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -7,7 +7,8 @@ class Forecast extends Group MetarWindMixin, MetarPrevailingMixin, MetarWeatherMixin, - MetarCloudMixin { + MetarCloudMixin, + TafWindshearMixin { final _unparsedGroups = []; Forecast(String code) : super(code); diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index 90ad58e..6685631 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -46,4 +46,5 @@ part 'taf/models/change_indicator.dart'; part 'taf/models/missing.dart'; part 'taf/models/temperature.dart'; part 'taf/models/valid.dart'; +part 'taf/models/windshear.dart'; part 'taf/taf.dart'; diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 596a8e8..aa05e59 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -49,6 +49,7 @@ class ChangeForecast extends Forecast { GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), + GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; var sanitizedCode = sanitizeChangeIndicator(_code!); diff --git a/lib/src/models/taf/models/windshear.dart b/lib/src/models/taf/models/windshear.dart new file mode 100644 index 0000000..c6c1684 --- /dev/null +++ b/lib/src/models/taf/models/windshear.dart @@ -0,0 +1,40 @@ +part of models; + +/// Basic structure for valid time groups in change periods and forecasts. +class TafWindshear extends Group { + TafWindshear(String? code) : super(code); + + late int heightInFeet; + late MetarWind wind; + + /// Named constructor of the TafWindshear clas + /// + /// Args: + /// code (String?): the code of the group. + /// match (RegExpMatch?): the match of the regular expression. + TafWindshear.fromTaf(String? code, RegExpMatch? match) : super(code) { + if (match != null) { + heightInFeet = int.parse(match.namedGroup('height')!) * 100; + wind = MetarWind(null, match); + } + } + + @override + String toString() { + return 'wind-shear at $heightInFeet feet, ${wind.toString()}'; + } +} + +/// Mixin to add the valid period of forecast attribute and handler. +mixin TafWindshearMixin on StringAttributeMixin { + final List _windshears = []; + + void _handleWindshear(String group) { + final match = TafRegExp.WINDSHEAR.firstMatch(group); + final _windshear = TafWindshear.fromTaf(group, match); + _windshears.add(_windshear); + _concatenateString(_windshear); + } + + List get windshears => _windshears; +} diff --git a/lib/src/utils/metar_regexp.dart b/lib/src/utils/metar_regexp.dart index 09c0edf..e766ba2 100644 --- a/lib/src/utils/metar_regexp.dart +++ b/lib/src/utils/metar_regexp.dart @@ -64,7 +64,7 @@ class MetarRegExp { r'(?BR|FG|VA|DU|SA|HZ|PY)?' r'(?PO|SQ|FC|SS|DS)?$'); - static final RegExp WINDSHEAR = RegExp(r'^WS(?_ALL)?' + static final RegExp WINDSHEAR_RUNWAY = RegExp(r'^WS(?_ALL)?' r'_(RWY|R(?\d{2}[RCL]?))$'); static final RegExp SEA_STATE = RegExp(r'^W(?M)?' diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index b65a8cb..d9f38d6 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -35,4 +35,10 @@ class TafRegExp { r'(?[0-1]\d|2[0-3])' r'(?[0-5]\d)' r'|PROB[34]0(_TEMPO)?$'); + + static final RegExp WINDSHEAR = RegExp(r'^WS(?([0-9][0-9][0-9]))/' + r'(?([0-2][0-9]|3[0-6])0|///|VRB)' + r'P?(?\d{2,3}|//|///)' + r'(G(P)?(?\d{2,3}))?' + r'(?KT|MPS)$'); } diff --git a/test/taf/changes_change_indicators_test.dart b/test/taf/changes_change_indicators_test.dart index c8be196..1a2bdfc 100644 --- a/test/taf/changes_change_indicators_test.dart +++ b/test/taf/changes_change_indicators_test.dart @@ -23,23 +23,26 @@ void main() { 'BECMG 0708/0710 27007G13MPS', ]); + final leadingZero = (int value) => value < 10 ? '0$value' : '$value'; + final month = leadingZero(DateTime.now().month); + final change0 = changes[0]; expect(change0.code, 'TEMPO 0618/0706 1000 SHSN BLSN VV004'); expect(change0.changeIndicator.code, 'TEMPO 0618/0706'); expect(change0.changeIndicator.translation, - 'temporary from 2022-03-06 18:00:00 until 2022-03-07 06:00:00'); + 'temporary from 2022-$month-06 18:00:00 until 2022-$month-07 06:00:00'); final change1 = changes[1]; expect(change1.code, 'BECMG 0700/0702 26006G12MPS'); expect(change1.changeIndicator.code, 'BECMG 0700/0702'); expect(change1.changeIndicator.translation, - 'becoming from 2022-03-07 00:00:00 until 2022-03-07 02:00:00'); + 'becoming from 2022-$month-07 00:00:00 until 2022-$month-07 02:00:00'); final change2 = changes[2]; expect(change2.code, 'BECMG 0708/0710 27007G13MPS'); expect(change2.changeIndicator.code, 'BECMG 0708/0710'); expect(change2.changeIndicator.translation, - 'becoming from 2022-03-07 08:00:00 until 2022-03-07 10:00:00'); + 'becoming from 2022-$month-07 08:00:00 until 2022-$month-07 10:00:00'); expect( () => changes[3].code, diff --git a/test/taf/wind_test.dart b/test/taf/wind_test.dart index 958c30b..dfad332 100644 --- a/test/taf/wind_test.dart +++ b/test/taf/wind_test.dart @@ -1,3 +1,6 @@ +import 'dart:developer'; +import 'dart:math'; + import 'package:test/test.dart'; import 'package:metar_dart/metar_dart.dart'; @@ -26,4 +29,74 @@ void main() { expect(wind.gustInMiph, 20.13243645902753); expect(wind.toString(), 'WSW (240.0°) 7.8 kt gust of 17.5 kt'); }); + + test('Test TAF Wind Shear', () { + final code = ''' + KMEM 081503Z 0815/0912 20006KT P6SM SCT100 BKN250 + FM082100 21006KT P6SM VCSH SCT050 BKN200 + FM090000 18005KT P6SM -RA OVC015 + FM090200 18010KT 2SM -RA BR BKN008 OVC015 WS020/20045KT= + '''; + final taf = Taf(code); + final wind = taf.wind; + + expect(wind.code, '20006KT'); + expect(wind.cardinalDirection, 'SSW'); + expect(wind.directionInDegrees, 200.0); + expect(wind.directionInRadians, 3.490658503988659); + expect(wind.variable, false); + expect(wind.speedInKnot, 6.0); + expect(wind.speedInMps, closeTo(3.08667, 2e-4)); + expect(wind.speedInKph, 11.112); + expect(wind.gustInKnot, null); + expect(wind.gustInMps, null); + expect(wind.gustInMiph, null); + expect(wind.toString(), 'SSW (200.0°) 6.0 kt'); + + final forecasts = taf.changePeriods; + expect(forecasts.length, 3); + + final forecast0 = forecasts[0]; + expect(forecast0.code, 'FM082100 21006KT P6SM VCSH SCT050 BKN200'); + expect(forecast0.wind.code, '21006KT'); + expect(forecast0.wind.cardinalDirection, 'SSW'); + expect(forecast0.wind.directionInDegrees, 210); + expect(forecast0.wind.speedInKnot, 6.0); + expect(forecast0.prevailingVisibility.inSeaMiles, closeTo(6.0, 2e-10)); + expect(forecast0.weathers.first.description, 'showers'); + expect(forecast0.weathers.first.intensity, 'nearby'); // in the vicinity + expect(forecast0.weathers.first.toString(), 'nearby showers'); + expect(forecast0.clouds.first.cover, 'scattered'); + expect(forecast0.clouds.first.heightInFeet, closeTo(5000, 2e-10)); + expect(forecast0.clouds[1].cover, 'broken'); + expect(forecast0.clouds[1].heightInFeet, closeTo(20000, 2e-10)); + expect(forecast0.toString(), + 'from 2022-04-08 21:00:00 until 2022-04-08 23:00:00\n'); + + final forecast2 = forecasts[2]; + expect(forecast2.code, + 'FM090200 18010KT 2SM -RA BR BKN008 OVC015 WS020/20045KT'); + expect(forecast2.changeIndicator.valid.periodFrom.day, 9); + expect(forecast2.changeIndicator.valid.periodFrom.hour, 2); + expect(forecast2.changeIndicator.valid.periodFrom.minute, 0); + expect(forecast2.wind.code, '18010KT'); + expect(forecast2.wind.cardinalDirection, 'S'); + expect(forecast2.wind.directionInDegrees, 180); + expect(forecast2.wind.speedInKnot, 10.0); + + expect(forecast2.prevailingVisibility.inSeaMiles, closeTo(2.0, 2e-10)); + final weatherf2 = forecast2.weathers.first; + expect(weatherf2.precipitation, 'rain'); + expect(weatherf2.intensity, 'light'); + expect(forecast2.weathers[1].toString(), 'mist'); + + expect(forecast2.clouds[0].toString(), 'broken at 800.0 feet'); + expect(forecast2.clouds[1].toString(), 'overcast at 1500.0 feet'); + final windshear = forecast2.windshears.first; + expect(windshear.heightInFeet, 2000); + expect( + windshear.toString(), 'wind-shear at 2000 feet, SSW (200.0°) 45.0 kt'); + expect(forecast2.windshears.first.toString(), + 'wind-shear at 2000 feet, SSW (200.0°) 45.0 kt'); + }); } From 4a5a1de76f70696d331eef534f4e4a621721a808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 12:53:06 -0300 Subject: [PATCH 02/17] another test --- test/taf/general_tests.dart | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/taf/general_tests.dart diff --git a/test/taf/general_tests.dart b/test/taf/general_tests.dart new file mode 100644 index 0000000..dc58d38 --- /dev/null +++ b/test/taf/general_tests.dart @@ -0,0 +1,29 @@ +import 'dart:developer'; + +import 'package:metar_dart/metar_dart.dart'; +import 'package:test/test.dart'; + +void main() { + test('Test NWWW', (() { + final metar = Metar( + 'NWWW 111130Z AUTO 18005KT 150V240 9999 -RA SCT008/// BKN019/// BKN041/// ///TCU 23/22 Q1011 TEMPO SHRA'); + log(metar.toString()); + expect( + metar.toString(), + 'Name: LA TONTOUTA NLLE | Coordinates: 22.01S 166.13E | Elevation: 16 m MSL | Country: Samoa\n' + '2022-04-11 11:30:00\n' + 'automatic report\n' + 'S (180.0°) 5.0 kt\n' + 'from SSE (150.0°) to WSW (240.0°)\n' + '10.0 km\n' + 'light rain\n' + 'scattered at 800.0 feet\n' + 'broken at 1900.0 feet\n' + 'broken at 4100.0 feet\n' + 'undefined\n' + 'temperature: 23.0°C | dewpoint: 22.0°C\n' + '1011.0 hPa\n' + 'temporary from 2022-04-11 11:30:00 until 2022-04-11 13:30:00\n' + 'showers rain'); + })); +} From cb9c00fedebabf06827a588bc03aba223af14d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 12:59:08 -0300 Subject: [PATCH 03/17] Small changes --- lib/src/models/change_indicator.dart | 1 + lib/src/models/metar/metar.dart | 3 +++ lib/src/utils/translations.dart | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/src/models/change_indicator.dart b/lib/src/models/change_indicator.dart index e8820fc..539cb55 100644 --- a/lib/src/models/change_indicator.dart +++ b/lib/src/models/change_indicator.dart @@ -4,6 +4,7 @@ final CHANGE_TRANSLATIONS = { 'NOSIG': 'no significant changes', 'BECMG': 'becoming', 'TEMPO': 'temporary', + 'FCST': 'forecast', 'PROB30': 'probability 30%', 'PROB40': 'probability 40%', }; diff --git a/lib/src/models/metar/metar.dart b/lib/src/models/metar/metar.dart index 398c2fb..d2227fb 100644 --- a/lib/src/models/metar/metar.dart +++ b/lib/src/models/metar/metar.dart @@ -26,11 +26,14 @@ class Metar extends Report // Trend groups final MetarWeatherTrends _weatherTrends = MetarWeatherTrends(); + final DateTime? observedAt; + Metar( String code, { int? year, int? month, bool truncate = false, + this.observedAt, }) : super(code, truncate) { _handleSections(); diff --git a/lib/src/utils/translations.dart b/lib/src/utils/translations.dart index 7795428..154f7a1 100644 --- a/lib/src/utils/translations.dart +++ b/lib/src/utils/translations.dart @@ -49,7 +49,7 @@ class SKY_TRANSLATIONS { 'BC': 'patches of', 'DR': 'low drifting', 'BL': 'blowing', - 'SH': 'showers', + 'SH': 'showers of', 'TS': 'thunderstorm', 'FZ': 'freezing', }; From 71cab3d441d1a8b117f2131b1bea534bcee2d773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 13:08:25 -0300 Subject: [PATCH 04/17] upgrade http version --- lib/src/models/metar/models/time.dart | 1 + pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/src/models/metar/models/time.dart b/lib/src/models/metar/models/time.dart index 2846f37..2fb6e3c 100644 --- a/lib/src/models/metar/models/time.dart +++ b/lib/src/models/metar/models/time.dart @@ -17,5 +17,6 @@ class MetarTime extends Time with GroupMixin { } mixin MetarTimeMixin on Report { + // ignore: prefer_final_fields MetarTime _time = MetarTime(null, null); } diff --git a/pubspec.yaml b/pubspec.yaml index 04efd51..f0b986d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ environment: sdk: ">=2.12.0 <3.0.0" dependencies: - http: ^0.12.2 + http: ^0.13.4 tuple: ^2.0.0 dev_dependencies: From 3d0e9e03ddca51531707f0432f25a565b41586be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 15:08:54 -0300 Subject: [PATCH 05/17] Not found warnings --- lib/src/models/metar/metar.dart | 18 ++++++++++++++++-- lib/src/models/metar/models/weather_trend.dart | 8 ++++++-- lib/src/models/taf/models/change_forecast.dart | 7 +++++-- lib/src/models/taf/taf.dart | 14 ++++++++++++-- lib/src/utils/parser.dart | 13 ++++++++++++- test/taf/exaustive_test.dart | 12 ++++++++++++ 6 files changed, 63 insertions(+), 9 deletions(-) create mode 100644 test/taf/exaustive_test.dart diff --git a/lib/src/models/metar/metar.dart b/lib/src/models/metar/metar.dart index d2227fb..8c6f0dc 100644 --- a/lib/src/models/metar/metar.dart +++ b/lib/src/models/metar/metar.dart @@ -28,6 +28,8 @@ class Metar extends Report final DateTime? observedAt; + List warnings = []; + Metar( String code, { int? year, @@ -160,7 +162,13 @@ class Metar extends Report MetarRunwayState get runwayState => _runwayState; void _handleWeatherTrend(String code) { - final wt = ChangePeriod(code, _time.time); + final wt = ChangePeriod( + code, + _time.time, + onWarning: (warning) { + warnings.add(warning); + }, + ); _weatherTrends.add(wt); _concatenateString(wt); @@ -203,7 +211,13 @@ class Metar extends Report var sanitizedBody = sanitizeVisibility(body); sanitizedBody = sanitizeWindshear(sanitizedBody); - final unparsed = parseSection(handlers, sanitizedBody); + final unparsed = parseSection( + handlers, + sanitizedBody, + onWarning: (warning) { + warnings.add(warning); + }, + ); _unparsedGroups.addAll(unparsed); } diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index bbb5fcf..c493f48 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -21,7 +21,10 @@ class ChangePeriod extends Forecast { late final Time _time; late MetarTrendIndicator _changeIndicator; - ChangePeriod(String code, DateTime time) : super(code) { + final Null Function(String warning) onWarning; + + ChangePeriod(String code, DateTime time, {required this.onWarning}) + : super(code) { _time = Time(time: time); // Groups _changeIndicator = MetarTrendIndicator(null, null, _time.time); @@ -66,7 +69,8 @@ class ChangePeriod extends Forecast { ]; final sanitizedCode = sanitizeVisibility(_code!); - final unparsed = parseSection(handlers, sanitizedCode); + final unparsed = + parseSection(handlers, sanitizedCode, onWarning: onWarning); _unparsedGroups.addAll(unparsed); } } diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index aa05e59..196d48b 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -4,7 +4,9 @@ part of models; class ChangeForecast extends Forecast { late final Valid _valid; late TafChangeIndicator _changeIndicator; - ChangeForecast(String code, Valid valid) : super(code) { + final Null Function(String warning) onWarning; + ChangeForecast(String code, Valid valid, {required this.onWarning}) + : super(code) { // Initialize valid period of the forecasts _valid = valid; @@ -54,7 +56,8 @@ class ChangeForecast extends Forecast { var sanitizedCode = sanitizeChangeIndicator(_code!); sanitizedCode = sanitizeVisibility(sanitizedCode); - final unparsed = parseSection(handlers, sanitizedCode); + final unparsed = + parseSection(handlers, sanitizedCode, onWarning: onWarning); _unparsedGroups.addAll(unparsed); } } diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index f815f65..e6d75d6 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -20,6 +20,8 @@ class Taf extends Report final _maxTemperatures = TafTemperatureList(); final _minTemperatures = TafTemperatureList(); + List warnings = []; + // Change periods final _changePeriods = TafChangePeriods(); @@ -94,7 +96,13 @@ class Taf extends Report TafTemperatureList get minTemperatures => _minTemperatures; void _handleChangePeriod(String code) { - final cf = ChangeForecast(code, _valid); + final cf = ChangeForecast( + code, + _valid, + onWarning: (warning) { + warnings.add(warning); + }, + ); if (_changePeriods.length > 0) { if (cf.code!.startsWith('FM') || cf.code!.startsWith('BECMG')) { _changePeriods.last.changeIndicator @@ -134,7 +142,9 @@ class Taf extends Report GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), ]; - final unparsed = parseSection(handlers, _body); + final unparsed = parseSection(handlers, _body, onWarning: (warning) { + warnings.add(warning); + }); _unparsedGroups.addAll(unparsed); } diff --git a/lib/src/utils/parser.dart b/lib/src/utils/parser.dart index 07a3b72..3b65fb5 100644 --- a/lib/src/utils/parser.dart +++ b/lib/src/utils/parser.dart @@ -83,13 +83,19 @@ String sanitizeChangeIndicator(String code) { /// Returns: /// unparsed_groups (List): the not matched groups with anyone /// of the regular expresions stored in `handlers`. -List parseSection(List handlers, String section) { +List parseSection( + List handlers, + String section, { + required Null Function(String warning) onWarning, +}) { final unparsedGroups = []; var index = 0; section.split(' ').forEach((group) { unparsedGroups.add(group); + var found = false; + for (var i = index; i < handlers.length; i++) { var handler = handlers[i]; index++; @@ -97,9 +103,14 @@ List parseSection(List handlers, String section) { if (handler.regexp.hasMatch(group)) { handler.handler(group); unparsedGroups.remove(group); + found = true; break; } } + + if (!found) { + onWarning('TAF Group not found: $group'); + } }); return unparsedGroups; diff --git a/test/taf/exaustive_test.dart b/test/taf/exaustive_test.dart new file mode 100644 index 0000000..adb5997 --- /dev/null +++ b/test/taf/exaustive_test.dart @@ -0,0 +1,12 @@ +import 'package:metar_dart/metar_dart.dart'; +import 'package:test/test.dart'; + +void main() { + test('warnings test', () { + final code = + '''TAF MMPQ 221654Z 2218/2318 10015KT P6SM SCT020 FM230300 13010KT P6SM BKN010 TEMPO 2308/2312 3SM -RA BKN005 D2DS FM231500 12010KT 4SM HZ SCT010 333'''; + final taf = Taf(code); + expect(taf.warnings.first, 'TAF Group not found: D2DS'); + expect(taf.warnings[1], 'TAF Group not found: 333'); + }); +} From ff3ac91f815df62e8f8074c8ffa814378ee31fe0 Mon Sep 17 00:00:00 2001 From: lawrenceCA <90285757+lawrenceCA@users.noreply.github.com> Date: Tue, 12 Apr 2022 19:26:06 +0100 Subject: [PATCH 06/17] windshear tweak --- lib/src/models/taf/taf.dart | 4 +++- lib/src/utils/taf_regexp.dart | 2 +- test/taf/wind_test.dart | 15 ++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index f815f65..a052d1a 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -9,7 +9,8 @@ class Taf extends Report MetarWindMixin, MetarPrevailingMixin, MetarWeatherMixin, - MetarCloudMixin { + MetarCloudMixin, + TafWindshearMixin { late final String _body; final List _changesCodes = []; int? _year, _month; @@ -132,6 +133,7 @@ class Taf extends Report GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), + GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; final unparsed = parseSection(handlers, _body); diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index d9f38d6..ddcdec0 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -36,7 +36,7 @@ class TafRegExp { r'(?[0-5]\d)' r'|PROB[34]0(_TEMPO)?$'); - static final RegExp WINDSHEAR = RegExp(r'^WS(?([0-9][0-9][0-9]))/' + static final RegExp WINDSHEAR = RegExp(r'^WS(?\d{3})/' r'(?([0-2][0-9]|3[0-6])0|///|VRB)' r'P?(?\d{2,3}|//|///)' r'(G(P)?(?\d{2,3}))?' diff --git a/test/taf/wind_test.dart b/test/taf/wind_test.dart index dfad332..907d016 100644 --- a/test/taf/wind_test.dart +++ b/test/taf/wind_test.dart @@ -32,13 +32,14 @@ void main() { test('Test TAF Wind Shear', () { final code = ''' - KMEM 081503Z 0815/0912 20006KT P6SM SCT100 BKN250 + KMEM 081503Z 0815/0912 20006KT P6SM SCT100 BKN250 WS020/20045KT FM082100 21006KT P6SM VCSH SCT050 BKN200 FM090000 18005KT P6SM -RA OVC015 FM090200 18010KT 2SM -RA BR BKN008 OVC015 WS020/20045KT= '''; final taf = Taf(code); final wind = taf.wind; + final windshear = taf.windshears.first; expect(wind.code, '20006KT'); expect(wind.cardinalDirection, 'SSW'); @@ -52,6 +53,10 @@ void main() { expect(wind.gustInMps, null); expect(wind.gustInMiph, null); expect(wind.toString(), 'SSW (200.0°) 6.0 kt'); + expect(windshear.heightInFeet, 2000); + expect( + windshear.toString(), 'wind-shear at 2000 feet, SSW (200.0°) 45.0 kt'); + expect(windshear.wind.toString(), 'SSW (200.0°) 45.0 kt'); final forecasts = taf.changePeriods; expect(forecasts.length, 3); @@ -92,10 +97,10 @@ void main() { expect(forecast2.clouds[0].toString(), 'broken at 800.0 feet'); expect(forecast2.clouds[1].toString(), 'overcast at 1500.0 feet'); - final windshear = forecast2.windshears.first; - expect(windshear.heightInFeet, 2000); - expect( - windshear.toString(), 'wind-shear at 2000 feet, SSW (200.0°) 45.0 kt'); + final windshearF2 = forecast2.windshears.first; + expect(windshearF2.heightInFeet, 2000); + expect(windshearF2.toString(), + 'wind-shear at 2000 feet, SSW (200.0°) 45.0 kt'); expect(forecast2.windshears.first.toString(), 'wind-shear at 2000 feet, SSW (200.0°) 45.0 kt'); }); From 7b86799454985050198df9b8dc70bc76e3209e08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 16:29:17 -0300 Subject: [PATCH 07/17] Some TAF fixes and exhaustive testing --- .../models/taf/models/change_forecast.dart | 3 +- lib/src/utils/metar_regexp.dart | 2 +- lib/src/utils/taf_regexp.dart | 2 +- test/taf/exaustive_test.dart | 12 - test/taf/exhaustive_test.dart | 590 ++++++++++++++++++ 5 files changed, 594 insertions(+), 15 deletions(-) delete mode 100644 test/taf/exaustive_test.dart create mode 100644 test/taf/exhaustive_test.dart diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 196d48b..02724bd 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -47,6 +47,7 @@ class ChangeForecast extends Forecast { GroupHandler(MetarRegExp.WEATHER, _handleWeather), GroupHandler(MetarRegExp.WEATHER, _handleWeather), GroupHandler(MetarRegExp.WEATHER, _handleWeather), + GroupHandler(MetarRegExp.VISIBILITY, _handlePrevailing), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), @@ -64,7 +65,7 @@ class ChangeForecast extends Forecast { /// Basic structure for weather change periods in TAF. class TafChangePeriods extends GroupList { - TafChangePeriods() : super(8); + TafChangePeriods() : super(20); @override String toString() { diff --git a/lib/src/utils/metar_regexp.dart b/lib/src/utils/metar_regexp.dart index e766ba2..22f8ac1 100644 --- a/lib/src/utils/metar_regexp.dart +++ b/lib/src/utils/metar_regexp.dart @@ -45,7 +45,7 @@ class MetarRegExp { r'(?PO|SQ|FC|SS|DS|NSW|/))?)$'); static final RegExp CLOUD = - RegExp(r'^(?VV|CLR|SCK|SCK|NSC|NCD|BKN|SCT|FEW|OVC|///)' + RegExp(r'^(?VV|SKC|CLR|SCK|SCK|NSC|NCD|BKN|SCT|FEW|OVC|///)' r'(?\d{3}|///)?' r'(?TCU|CB|///)?$'); diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index d9f38d6..dc1bc07 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -6,7 +6,7 @@ class TafRegExp { static final RegExp NIL = RegExp(r'^NIL$'); static final RegExp VALID = RegExp(r'^(?0[1-9]|[12][0-9]|3[01])' - r'(?[0-1]\d|2[0-3])/' + r'(?[0-1]\d|2[0-4])/' r'(?0[1-9]|[12][0-9]|3[01])' r'(?[0-1]\d|2[0-4])$'); diff --git a/test/taf/exaustive_test.dart b/test/taf/exaustive_test.dart deleted file mode 100644 index adb5997..0000000 --- a/test/taf/exaustive_test.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:metar_dart/metar_dart.dart'; -import 'package:test/test.dart'; - -void main() { - test('warnings test', () { - final code = - '''TAF MMPQ 221654Z 2218/2318 10015KT P6SM SCT020 FM230300 13010KT P6SM BKN010 TEMPO 2308/2312 3SM -RA BKN005 D2DS FM231500 12010KT 4SM HZ SCT010 333'''; - final taf = Taf(code); - expect(taf.warnings.first, 'TAF Group not found: D2DS'); - expect(taf.warnings[1], 'TAF Group not found: 333'); - }); -} diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart new file mode 100644 index 0000000..edeccd3 --- /dev/null +++ b/test/taf/exhaustive_test.dart @@ -0,0 +1,590 @@ +import 'package:metar_dart/metar_dart.dart'; +import 'package:test/test.dart'; + +void main() { + test('warnings test', () { + final code = + '''TAF MMPQ 221654Z 2218/2318 10015KT P6SM SCT020 FM230300 13010KT P6SM BKN010 TEMPO 2308/2312 3SM -RA BKN005 D2DS FM231500 12010KT 4SM HZ SCT010 333'''; + final taf = Taf(code); + expect(taf.warnings.first, 'TAF Group not found: D2DS'); + expect(taf.warnings[1], 'TAF Group not found: 333'); + }); + + test('test faling case', (() { + final code = + 'KABI 121740Z 1218/1318 22017G25KT P6SM SCT250 FM130200 17010KT P6SM SKC FM131200 24013G20KT P6SM SKC'; + var taf = Taf(code); + expect(taf.warnings.length, 0); + + final code2 = + 'TAF GFLL 121800Z 1218/1324 24008KT 9999 BKN016 TEMPO 1224/1305 29006KT 7000 HZ BKN010 FEW027CB BECMG 1312/1314 27010KT 9999 SCT012'; + taf = Taf(code2); + expect(taf.warnings.length, 0); + + final code3 = + 'TAF GMMN 121700Z 1218/1324 32013KT 9999 BKN026 BECMG 1221/1223 20006KT 9999 BKN020 SCT100 PROB30 TEMPO 1306/1309 36006KT RA 5000 BKN020 BKN080'; + taf = Taf(code3); + expect(taf.warnings.length, 0); + expect(taf.changePeriods[1].prevailingVisibility.inMeters, 5000); + + // TODO: still failing + final code4 = + 'TAF KADW 121700Z 1217/1323 29010G15KT 9999 SCT020 BKN040 OVC085 QNH3004INS TEMPO 1217/1221 29015G25KT 510002 BECMG 1220/1221 VRB06KT 9999 SCT030 BKN050 QNH3003INS BECMG 1315/1316 20010G15KT 9999 FEW060 SCT150 BKN200 QNH2992INS BECMG 1320/1321 21020G30KT 9999 FEW050 FEW110 BKN160 510003 QNH2990INS TX24/1317Z TN13/1308Z'; + taf = Taf(code4); + expect(taf.warnings.length, 0); + })); + + test('exhaustive', (() { + final tafs = [ + 'TAF GABS 121730Z 1218/1324 13010G20KT 9999 TS SCT030 FEW033CB TEMPO 1218/1220 4000 RA BKN030 FEW033CB BECMG 1220/1222 NSW FEW033 PROB40 TEMPO 1314/1318 TS SCT030 FEW040CB', + 'TAF GAGO 121100Z 1212/1312 03010G20KT 8000 NSC PROB40 TEMPO 1215/1222 3000 BLDU', + 'TAF GBYD 121700Z 1218/1324 28010KT CAVOK', + 'TAF GCFV 121400Z 1215/1315 05016KT 9999 SCT035 TX24/1314Z TN17/1306Z BECMG 1218/1220 35012KT BECMG 1309/1311 05016KT', + 'TAF GCHI 121400Z 1215/1315 02017KT 9999 FEW035 TX22/1314Z TN18/1306Z', + 'TAF GCLA 121400Z 1215/1315 03012KT 9999 BKN030 TX21/1314Z TN16/1306Z TEMPO 1222/1309 33009KT', + 'TAF GCLP 121400Z 1215/1315 03025KT 9999 FEW035 TX23/1314Z TN17/1306Z PROB30 TEMPO 1215/1218 02022G32KT PROB30 TEMPO 1311/1315 01022G32KT', + 'TAF GCRR 121400Z 1215/1315 36014KT 9999 FEW040 TX25/1314Z TN15/1306Z PROB30 TEMPO 1312/1315 01016G28KT', + 'TAF GCTS 121400Z 1215/1315 10014KT 9999 BKN030 TX22/1314Z TN18/1306Z BECMG 1221/1223 03007KT BECMG 1309/1312 09015KT PROB30 TEMPO 1312/1315 18008KT', + 'TAF GCXO 121400Z 1215/1315 31015KT 9999 SCT014 TX17/1314Z TN12/1306Z PROB40 TEMPO 1215/1315 BKN014 PROB30 TEMPO 1304/1309 2000 DZ BCFG BKN005', + 'TAF GEML 121700Z 1218/1318 27005KT 9999 FEW015 BKN030 TX17/1314Z TN13/1306Z TEMPO 1300/1308 4000 RA BR BKN010 BECMG 1308/1310 27015KT TEMPO 1309/1318 27015G25KT', + 'TAF GFLL 121800Z 1218/1324 24008KT 9999 BKN016 TEMPO 1224/1305 29006KT 7000 HZ BKN010 FEW027CB BECMG 1312/1314 27010KT 9999 SCT012', + 'TAF GGOV 121700Z 1218/1318 VRB05KT 8000 NSC', + 'TAF GLRB 121100Z 1212/1312 22010KT 9999 BKN018 TEMPO 1215/1219 17008KT SCT022 TEMPO 1220/1224 VRB02KT 8000 FEW014', + 'TAF GMAD 121700Z 1218/1324 26014KT CAVOK BECMG 1220/1222 18008KT TEMPO 1312/1320 26014G26KT', + 'TAF GMFF 121700Z 1218/1324 30010G20KT 9999 BKN030 SCT100 PROB30 TEMPO 1218/1221 RA BECMG 1222/1224 BKN010 BKN020 TEMPO 1222/1308 3000 SHRA', + 'TAF GMFK 121700Z 1218/1324 36018KT CAVOK BECMG 1219/1221 27008KT', + 'TAF GMFO 121700Z 1218/1324 04006KT 9999 SCT020 SCT040 TEMPO 1222/1306 SHRA SCT020 BKN033 BECMG 1309/1311 30017KT 9999 SCT026 BKN040', + 'TAF COR GMME 121700Z 1218/1324 32010KT 6000 SCT016 BKN020 PROB30 TEMPO 1302/1306 4000 RA SCT013TCU BKN016 OVC057', + 'TAF GMMI 121700Z 1218/1324 28014KT 8000 FEW023 SCT200', + 'TAF GMMN 121700Z 1218/1324 32013KT 9999 BKN026 BECMG 1221/1223 20006KT 9999 BKN020 SCT100 PROB30 TEMPO 1306/1309 36006KT RA 5000 BKN020 BKN080', + 'TAF GMMW 121700Z 1218/1318 36006KT 9999 SCT015 SCT021 PROB40 TEMPO 1218/1220 3000 -RA BKN030', + 'TAF GMMX 121700Z 1218/1324 02006KT 8000 SCT020 SCT080 TEMPO 1315/1320 32015KT', + 'TAF GMMZ 121700Z 1218/1324 31015KT 9999 FEW030 BECMG 1223/1301 28003KT', + 'TAF GMTA 121700Z 1218/1324 36010KT 9999 FEW020 SCT030 PROB40 TEMPO 1218/1222 5000 RA', + 'TAF GMTT 121700Z 1218/1324 23008KT 9999 SCT020 TEMPO 1219/1302 3000 -SHRA BKN013 BECMG 1307/1309 CAVOK', + 'TAF GOGS 121700Z 1218/1318 32010KT CAVOK', + 'TAF GOOY 121700Z 1218/1324 34010KT 9999 SCT010', + 'TAF GOSS 121700Z 1218/1318 34010KT CAVOK', + 'TAF GQPP 121100Z 1212/1312 36020KT 7000 NSC', + 'TAF GVAC 121700Z 1218/1324 04015KT 9999 BKN014', + 'TAF GVBA 121100Z 1212/1318 03016KT 9999 BKN014', + 'TAF GVNP 121700Z 1218/1324 03017KT 9999 SCT017', + 'TAF GVSV 121700Z 1218/1324 05020KT 9999 SCT022 TEMPO 1300/1308 05010KT', + 'TAF HAAB 121530Z 1218/1324 34010KT 9999 SCT025 SCT090 BECMG 1218/1223 09008KT -RA BKN030', + 'TAF HADR 121530Z 1218/1324 18008KT 9999 SCT025 SCT090 BECMG 1218/1223 14006KT 9000 -RA BKN028', + 'TAF HBBA 121700Z 1218/1324 00000KT CAVOK BECMG 1302/1304 36003KT 9999 FEW030 SCT100 BECMG 1307/1309 18006KT BECMG 1310/1312 18010G20KT BECMG 1318/1320 00000KT CAVOK', + 'TAF HCMM 121100Z 1212/1318 18011KT 9999 FEW025 BECMG 1221/1300 17005KT BKN020 BECMG 1306/1309 19010KT SCT025', + 'TAF HEAR 121700Z 1218/1324 32008KT 9999 SCT025', + 'TAF HEAT 121700Z 1218/1324 35016KT CAVOK', + 'TAF HEAX 121700Z 1218/1324 33012KT 9999 SCT020', + 'TAF HEBL 121700Z 1218/1324 03010KT CAVOK', + 'TAF HECA 121700Z 1218/1324 36009KT CAVOK PROB30 TEMPO 1300/1306 01005KT 4000 BR NSC', + 'TAF HEGN 121700Z 1218/1324 34015G25KT CAVOK', + 'TAF HELX 121700Z 1218/1324 34010KT CAVOK', + 'TAF HEMM 121700Z 1218/1324 35015KT 9999 SCT020', + 'TAF HEPS 121700Z 1218/1324 34011KT 9999 SCT020', + 'TAF HESC 121700Z 1218/1324 30012KT CAVOK', + 'TAF HESH 121700Z 1218/1324 01012KT CAVOK', + 'TAF HESN 121700Z 1218/1324 36012KT CAVOK', + 'TAF HETR 121700Z 1218/1324 31025G35KT 6000 NSC', + 'TAF HKEL 121700Z 1218/1324 13010KT 9999 FEW023CB SCT024 BECMG 1220/1223 08005KT FEW020 BECMG 1308/1311 13010KT FEW025CB SCT026 TEMPO 1313/1317 -TSRA FEW021CB SCT022 BKN080 BECMG 1320/1323 08005KT NSW FEW020', + 'TAF HKJK 121700Z 1218/1324 12010KT 9999 SCT027 BECMG 1220/1223 04005K T FEW018 BECMG 1308/1311 08010KT FEW024CB SCT025 BKN080 PROB40 TEMPO 1313/1317 -SHRA FEW022CB SCT023 BKN080 BECMG 1320/1323 04005KT NSW FEW018', + 'TAF HKKI 121700Z 1218/1324 23010KT 9999 FEW028CB SCT090 BECMG 1220/1223 06005KT FEW021 BECMG 1308/1311 24010KT FEW028CB SCT090 TEMPO 1313/1317 -TSRA FEW021CB SCT022 BKN080 BECMG 1320/1323 06005KT NSW FEW021', + 'TAF HKML 121700Z 1218/1318 18010KT 9999 SCT024 TEMPO 1300/1304 00000KT -SHRA FEW019CB SCT020 BKN080 BECMG 1309/1312 18015KT SCT026', + 'TAF HKNW 121700Z 1218/1318 13009KT 9999 SCT028 BECMG 1220/1223 05005KT FEW019 BECMG 1308/1311 09010KT FEW026CB SCT027 BKN090 PROB40 TEMPO 1313/1317', + 'TAF HLGT 121700Z 1218/1318 10015KT 7000 NSC PROB40 TEMPO 1218/1318 4000 BLSA BECMG 1308/1310 14015KT', + 'TAF HLKF 121700Z 1218/1318 05012KT 8000 NSC', + 'TAF HLLB 121700Z 1218/1318 09012KT 8000 NSC', + 'TAF HLLQ 121700Z 1218/1318 12012KT 8000 NSC', + 'TAF HLLS 121700Z 1218/1318 10012G22KT 7000 NSC BECMG 1308/1310 16015G25KT PROB40 TEMPO 1300/1318 4000 BLSA', + 'TAF HLON 121700Z 1218/1318 11012G22KT 7000 NSC', + 'TAF HLTD 121700Z 1218/1318 10010G20KT 8000 NSC BECMG 1302/1304 VRB02KT', + 'TAF HRYR 121700Z 1218/1324 34003KT 9999 FEW026 FM 130800 17006KT -RA BKN023 BKN090 BECMG 1310/1314 SCT025 TEMPO 1315/1319 27010KT 5000 TSRA FEW020CB BKN023', + 'HSKA 121700Z 1218/1324 18008KT 9999 SCT140', + 'HSOB 121100Z 1212/1318 21008G22KT CAVOK TEMPO 1214/1218 9999 SCT040 SCT140', + 'HSSK 121700Z 1218/1324 27010G20KT CAVOK TEMPO 1220/1304 VRB03KT', + 'HTAR 121700Z 1218/1318 11010KT 9999 SCT032 BECMG 1221/1224 BKN026', + 'HTDA 121700Z 1218/1324 13005KT 9999 TS BKN013 FEW014CB TEMPO 1223/1306 TSRA BECMG 1315/1318 SCT016 FEW017CB', + 'HTDO 121700Z 1218/1318 09005KT 9999 FEW029 FEW031CB', + 'HTKA 120500Z 1206/1306 10010KT 9999 FEW020 FEW021CB BECMG 1216/1218 00000KT', + 'HTKJ 121700Z 1218/1318 12015KT 9999 FEW026CB SCT028 BECMG 1222/1224 BKN022 BECMG 1300/1302 09005KT', + 'HTMW 121700Z 1218/1318 12010KT 9999 FEW026 FEW027CB SCT090 BECMG 1304/1306 TS SCT020 FEW021CB BECMG 1312/1314 32010KT NSW FEW024 FEW024CB BKN090', + 'HTTB 121700Z 1218/1318 00000KT 9999 TS FEW020 FEW021CB BECMG 1221/1223 NSW', + 'HTZA 121700Z 1218/1318 16010KT 9999 SCT016 FEW018CB BKN070 BECMG 1300/1302 22010KT PROB30 TEMPO 1306/1310 5000 TSRA', + 'TAF HUEN 121700Z 1218/1324 VRB12KT 9999 FEW022 FEW024CB BECMG 1303/1309 8000 -TSRA SCT019 FEW021CB BKN100 TEMPO 1311/1313 9000 TS', + 'KABI 121740Z 1218/1318 22017G25KT P6SM SCT250 FM130200 17010KT P6SM SKC FM131200 24013G20KT P6SM SKC', + 'KABQ 121735Z 1218/1318 24028G38KT P6SM BKN090 TEMPO 1218/1221 26033G45KT 4SM BLDU FM122300 29022G32KT 6SM BLDU SCT090 FM130200 30012G22KT P6SM FEW070 FM130700 30011KT P6SM SKC FM131700 29014G22KT P6SM FEW180', + 'KACK 121720Z 1218/1318 30012G18KT P6SM SCT025 BKN035 OVC090 FM122200 26009KT P6SM FEW250 FM130300 30008KT P6SM SCT250 FM130900 27004KT P6SM FEW250 FM131300 19007KT P6SM FEW250 FM131700 18013KT P6SM BKN150', + 'KACT 121738Z 1218/1318 16018KT P6SM BKN025 FM121900 18019G29KT P6SM BKN035 FM122000 17020G30KT P6SM VCTS BKN045CB FM130500 15015G25KT P6SM BKN015', + 'KACY 121725Z 1218/1318 30011G19KT P6SM FEW070 FM122300 VRB03KT P6SM FEW250 FM130700 13004KT P6SM FEW010 BKN070 FM131400 16007KT P6SM BKN080', + 'TAF KADW 121700Z 1217/1323 29010G15KT 9999 SCT020 BKN040 OVC085 QNH3004INS TEMPO 1217/1221 29015G25KT 510002 BECMG 1220/1221 VRB06KT 9999 SCT030 BKN050 QNH3003INS BECMG 1315/1316 20010G15KT 9999 FEW060 SCT150 BKN200 QNH2992INS BECMG 1320/1321 21020G30KT 9999 FEW050 FEW110 BKN160 510003 QNH2990INS TX24/1317Z TN13/1308Z', + 'KAEX 121804Z 1218/1318 17015G23KT P6SM VCTS OVC025CB FM130000 15014G23KT P6SM VCTS BKN022CB OVC035 FM130200 16011G22KT 6SM BR VCTS SCT018CB OVC023 FM131200 16016G23KT P6SM VCTS OVC015CB PROB30 1312/1318 2SM TSRA OVC015CB', + 'KAGS 121735Z 1218/1318 24010G15KT P6SM SCT100 BKN250 FM130100 19005KT P6SM BKN250 FM131000 16004KT P6SM SCT010 FM131500 20011KT P6SM BKN250', + 'KALB 121720Z 1218/1318 28011G23KT P6SM FEW040 FM130000 28007KT P6SM FEW200 FM130600 VRB03KT P6SM SCT150 FM131200 16008KT P6SM OVC050', + 'KALI 121724Z 1218/1318 17015G24KT P6SM SCT250 FM122300 15017G25KT P6SM VCTS SCT035CB BKN250 FM130300 16014G20KT P6SM BKN035 TEMPO 1303/1306 OVC015 FM130600 17012KT 4SM BR OVC015 FM131500 20013G20KT P6SM OVC050', + 'KAMA 121723Z 1218/1318 24033G45KT P6SM SKC TEMPO 1219/1223 5SM BLDU SKC FM130000 26026G37KT P6SM SKC FM130600 35015KT P6SM BKN250 FM131500 32017G27KT P6SM SKC', + 'KANB 121720Z 1218/1318 20010KT P6SM BKN045 FM130900 17008KT P6SM OVC050 WS020/19030KT FM131000 17009KT P6SM OVC035 FM131100 16009KT P6SM OVC045 WS020/19035KT FM131200 16010G23KT P6SM OVC060 WS020/19040KT FM131500 19012G22KT P6SM BKN060 PROB30 1315/1318 4SM -SHRA BR OVC025', + 'KAND 121727Z 1218/1318 22009KT P6SM FEW110 FM130000 19004KT P6SM FEW250 FM130500 17003KT P6SM BKN250 FM131300 18006KT P6SM BKN180 FM131600 21009KT P6SM BKN120', + 'KAOO 121746Z 1218/1318 29005KT P6SM FEW035 FM130000 00000KT P6SM SCT250 FM131000 20008KT P6SM SCT050 OVC120', + 'TAF COR KARA 121805Z 1218/1318 17017G24KT P6SM VCTS BKN019CB OVC025 FM130300 16014G24KT P6SM VCTS OVC025CB FM131200 17014G21KT P6SM VCTS OVC015CB PROB30 1312/1318 2SM TSRA OVC015CB', + 'KART 121737Z 1218/1318 24012KT P6SM SKC FM122100 23011G18KT P6SM FEW250 FM130000 20009KT P6SM SCT250 FM131100 13012G20KT P6SM BKN060 FM131500 00000KT P6SM BKN025', + 'KATL 121746Z 1218/1324 21010G18KT P6SM FEW040 SCT090 FM130000 17007KT P6SM SCT100 BKN200 FM131500 19012G20KT P6SM SCT040 BKN100', + 'KAUG 121720Z 1218/1318 31009KT P6SM SCT070 FM130200 26003KT P6SM FEW250', + 'KAUS 121720Z 1218/1324 19014G24KT P6SM SCT020 SCT200 TEMPO 1222/1301 4SM TSRA BR OVC025CB FM130400 18013KT P6SM SCT009 OVC015 FM131500 24010G20KT P6SM FEW015 FM131900 31012G22KT P6SM SKC', + 'TAF AMD KBAB 121420Z 1214/1317 34006KT 9999 FEW200 QNH3012INS WND 310V030 BECMG 1215/1216 32009KT 9999 FEW060 QNH3015INS BECMG 1221/1222 24012KT 9999 SCT050 BKN080 620804 QNH3015INS BECMG 1302/1303 20008KT 9999 SCT070 QNH3007INS BECMG 1309/1310 16007KT 9999 SCT030 BKN070 620703 QNH3019INS TX15/1300Z TN01/1215Z', + 'TAF AMD KBAD 121755Z 1217/1318 14015G25KT 9999 OVC030 QNH2984INS BECMG 1302/1303 18020G25KT 9999 -SHRA VCTS OVC015CB QNH2975INS TEMPO 1304/1306 27030G45KT 0800 +TSRA OVC010CB BECMG 1309/1310 18015G25KT 9999 NSW OVC015 QNH2972INS TX28/1222Z TN22/1312Z', + 'KBDL 121720Z 1218/1324 30006KT P6SM FEW020 FEW070 FEW200 FM130300 00000KT P6SM SCT250 FM131300 17003KT P6SM BKN140 FM131500 18005KT P6SM BKN050 FM131700 19008KT P6SM BKN060', + 'KBDR 121720Z 1218/1318 29009KT P6SM FEW060 FEW250 FM121900 30012G19KT P6SM FEW060 FEW250 FM122200 30007KT P6SM FEW060 FM130100 VRB03KT P6SM FEW050 FM131400 15007KT P6SM BKN050', + 'KBED 121720Z 1218/1318 29006KT P6SM SKC FM122100 30010G19KT P6SM FEW250 FM130000 31006KT P6SM FEW250 FM130300 00000KT P6SM BKN250 FM131400 20007KT P6SM BKN150 FM131600 19007KT P6SM BKN100', + 'KBFI 121734Z 1218/1318 17013G22KT P6SM VCSH BKN060 BKN250 FM130400 17005KT P6SM BKN050 FM130700 11002KT P6SM BKN070', + 'KBFL 121720Z 1218/1318 36012KT P6SM SKC FM122000 33012G19KT P6SM SKC FM130900 33010KT P6SM SKC', + 'KBFM 121745Z 1218/1318 16013G20KT P6SM SCT030 BKN050 FM130500 15011KT P6SM OVC018 FM131500 14015G25KT P6SM BKN030', + 'KBGR 121740Z 1218/1318 32005KT P6SM BKN018 FM121900 31004KT P6SM VCSH BKN060 FM122100 30008KT P6SM SCT060 FM130400 VRB03KT P6SM SKC FM131700 21004KT P6SM BKN080', + 'KBHM 121720Z 1218/1318 20011G19KT P6SM BKN045 FM122100 18012G20KT P6SM SCT090 PROB30 1309/1310 4SM -SHRA BR OVC025 FM131000 17010G22KT P6SM OVC028 PROB30 1310/1311 4SM -SHRA BR OVC015 FM131100 18009KT P6SM BKN045 PROB30 1311/1312 4SM -SHRA BR OVC025 FM131300 18010G21KT P6SM BKN045 PROB30 1315/1318 4SM -SHRA BR OVC025', + 'TAF KBIF 121400Z 1214/1320 24012KT 9999 SKC 530054 QNH2983INS BECMG 1215/1216 25030G40KT 4800 BLDU SKC 530074 QNH2973INS TEMPO 1219/1301 24030G50KT 1600 BLDU FEW090 BECMG 1300/1301 24035G45KT 9999 NSW SCT090 530064 QNH2965INS BECMG 1301/1302 24020KT 9999 SCT100 530054 QNH2966INS BECMG 1307/1308 28015KT 9999 SKC 530054 QNH2982INS TX26/1222Z TN05/1313Z', + 'TAF KBIX 121800Z 1218/1400 14009KT 9999 SCT015 BKN025 QNH3001INS TEMPO 1218/1222 15012G22KT 8000 -SHRA VCTS OVC025CB BECMG 1221/1222 13012KT 9999 VCSH SCT015 BKN025 QNH3001INS BECMG 1305/1306 15012KT 8000 -SHRA VCTS BKN015 BKN025CB QNH2988INS TEMPO 1314/1315 15015G25KT 8000 -SHRA BKN025 OVC055 TX26/1318Z TN20/1306Z', + 'TAF AMD KBKF 121710Z 1217/1323 25015KT 9000 -SHRA BKN075 520023 QNH2937INS BECMG 1219/1220 VRB06KT 9999 NSW FEW095 510023 QNH2938INS BECMG 1221/1222 22012G18KT 9999 FEW110 520023 QNH2939INS BECMG 1222/1223 22020G25KT 9999 FEW120 530024 QNH2940INS BECMG 1300/1301 26020G25KT 9999 SKC 530103 QNH2948INS BECMG 1302/1303 29015KT 9999 FEW110 530053 QNH2956INS BECMG 1306/1307 33006KT 9999 SKC 510053 QNH2969INS BECMG 1313/1314 VRB06KT 9999 FEW095 510053 QNH2978INS BECMG 1316/1317 28015G20KT 9999 FEW120 520053 QNH2976INS BECMG 1318/1319 28015G20KT 9999 SCT100 530054 QNH2975INS BECMG 1320/1321 29020G30KT 9999 FEW085 530103 QNH2978INS TX13/1222Z TNM06/1313Z', + 'KBLI 121734Z 1218/1318 15012G23KT P6SM VCSH BKN040 OVC060 FM130600 10003KT P6SM OVC050 FM131000 02001KT P6SM OVC070', + 'TAF COR KBLV 121601Z 1216/1322 15009KT 9999 VCSH OVC005 QNH2980INS BECMG 1217/1219 17018G25KT 9999 NSW OVC020 510003 QNH2973INS BECMG 1220/1222 18012KT 9999 OVC020 QNH2960INS BECMG 1311/1313 18012KT 9999 VCTS OVC015CB QNH2945INS TEMPO 1318/1321 20030G45KT 3200 +TSRAGR BKN009CB OVC015 TX24/1223Z TN18/1311Z', + 'KBNA 121720Z 1218/1318 18012G22KT P6SM BKN025 FM122100 18012G22KT P6SM BKN035 FM130200 20012G18KT P6SM BKN250 WS020/20035KT FM131200 20020G27KT P6SM VCTS SCT025CB OVC050', + 'KBOI 121720Z 1218/1318 31018G28KT P6SM VCSH SCT025 BKN050 FM130300 32008KT P6SM VCSH SCT025 BKN060 TEMPO 1304/1307 4SM -SN BKN020 FM131000 14006KT P6SM VCSH BKN035 TEMPO 1310/1314 4SM -SN OVC020', + 'KBOS 121720Z 1218/1324 30010KT P6SM FEW080 BKN160 FM122100 29010G18KT P6SM FEW250 FM130100 31007KT P6SM SCT250 FM130800 25004KT P6SM FEW200 FM131400 19007KT P6SM BKN150 FM131800 19011KT P6SM BKN050', + 'KBPT 121803Z 1218/1318 17016G26KT P6SM VCTS SCT013CB OVC020 TEMPO 1218/1221 4SM TSRA BR BKN012CB FM130000 16017G27KT P6SM SCT012 BKN070 TEMPO 1300/1303 4SM TSRA BR BKN012CB FM131200 16015G22KT P6SM VCTS OVC015CB PROB30 1312/1318 2SM TSRA OVC015CB', + 'KBRO 121728Z 1218/1318 16020G30KT 6SM HZ SCT035 TEMPO 1218/1220 BKN026 FM130000 16014G24KT P6SM VCTS BKN018CB FM130600 16018KT P6SM BKN029 FM131600 18018G28KT P6SM SCT030', + 'KBTR 121727Z 1218/1318 17013G21KT P6SM BKN025 FM122000 17013G21KT P6SM VCTS SCT025CB OVC035 FM122200 16014G26KT P6SM SHRA VCTS SCT015CB OVC025 TEMPO 1300/1302 4SM +TSRA BR BKN020CB FM130700 16012G21KT P6SM SCT015 BKN080 FM131400 16013G22KT P6SM FEW010 SCT015 FM131700 17016G27KT P6SM -SHRA BKN025', + 'KBTV 121722Z 1218/1318 31010G20KT P6SM SCT050 FM122300 32008KT P6SM SKC FM131100 17005KT P6SM SCT120 FM131600 18008KT P6SM BKN060', + 'KBUF 121737Z 1218/1318 VRB03KT P6SM FEW045 FEW250 FM122100 24007KT P6SM FEW250 FM131000 16011G19KT P6SM BKN250 FM131200 19014G24KT P6SM BKN250 FM131600 21018G31KT P6SM BKN250', + 'KBUR 121744Z 1218/1318 33016G25KT P6SM SKC FM130500 33006KT P6SM SKC FM131100 VRB03KT P6SM SKC', + 'KBWI 121734Z 1218/1324 28010G18KT P6SM BKN050 FM122100 30007KT P6SM FEW050 SCT150 FM130300 11003KT P6SM BKN090 FM131400 17005KT P6SM FEW080 SCT130 FM131800 21009KT P6SM FEW110 SCT250', + 'KCAE 121735Z 1218/1318 22010G15KT P6SM SCT100 BKN250 FM130100 19005KT P6SM BKN250 FM131000 16004KT P6SM SCT010 FM131500 20011KT P6SM BKN250', + 'KCAR 121740Z 1218/1318 23003KT P6SM VCSH OVC010 FM122000 29007KT P6SM BKN020 FM122200 30007KT P6SM BKN050 FM122300 31007KT P6SM SCT060 FM130200 29005KT P6SM SKC WS020/32035KT FM130500 28006KT P6SM SCT250 FM131700 26005KT P6SM BKN120', + 'TAF KCBM 121700Z 1217/1323 18012G20KT 9999 BKN025 QNH3001INS BECMG 1217/1218 18012G20KT 9999 SCT035 QNH2990INS TEMPO 1220/1222 18015G25KT BECMG 1222/1223 18009KT 8000 -SHRA BKN025 QNH2985INS BECMG 1306/1307 18012KT 9999 NSW BKN012 QNH2985INS TEMPO 1310/1313 18010G15KT 8000 -TSRA BKN005CB BECMG 1314/1315 18012G20KT 9999 SCT015 BKN030 QNH2976INS BECMG 1322/1323 18015G25KT 6000 TSRA OVC025CB QNH2975INS TX27/1221Z TN19/1311Z', + 'KCDC 121738Z 1218/1318 36005KT P6SM SKC FM122200 31011G22KT P6SM SCT080 FM130500 VRB06KT P6SM FEW200 FM131500 25011KT P6SM FEW100 SCT180', + 'KCDS 121720Z 1218/1318 22020G31KT P6SM FEW250 FM122100 22025G35KT P6SM SKC TEMPO 1221/1301 3SM BLDU BKN030 FM130100 22020KT P6SM FEW250 FM130900 30015KT P6SM FEW250', + 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z', + 'KCHA 121720Z 1218/1318 18007KT P6SM VCSH BKN025 FM121930 18009KT P6SM SCT045 FM130000 18005KT P6SM BKN250 FM131000 19004KT P6SM VCSH SCT040 BKN100 FM131600 18012G22KT P6SM SCT040 BKN150', + 'KCHS 121739Z 1218/1318 22009KT P6SM FEW100 FM121930 19012G17KT P6SM FEW150 FM130100 19005KT P6SM FEW250 FM131530 21011G17KT P6SM FEW250', + 'KCLE 121752Z 1218/1324 04008KT P6SM FEW250 FM130100 12010KT P6SM BKN100 FM130600 17014KT P6SM OVC040 FM131100 19013G21KT P6SM BKN050 FM131700 20016G27KT P6SM -SHRA OVC050 FM132200 20019G30KT P6SM BKN050', + 'KCLL 121737Z 1218/1318 17013KT 6SM BR VCSH OVC015 FM122100 17017KT 6SM BR VCTS SCT025CB OVC035 FM130000 17017G27KT 6SM BR VCSH BKN090 FM130300 16013KT P6SM BKN020 FM131200 18014KT P6SM BKN015', + 'KCLT 121727Z 1218/1324 22012G21KT P6SM FEW100 BKN170 FM122300 22006KT P6SM FEW150 SCT230 FM130300 19005KT P6SM FEW250 FM131300 19007KT P6SM BKN210 FM131800 21011G18KT P6SM BKN150 FM132300 19009KT P6SM SCT120', + 'KCMH 121737Z 1218/1318 17008KT P6SM SCT025 FM122300 16007KT P6SM BKN028 FM130400 17009KT P6SM BKN090 FM130900 18010KT P6SM BKN090 WS020/22040KT FM131400 19012G21KT P6SM BKN090 FM131600 19015G27KT 6SM -SHRA BR OVC050', + 'KCNM 121720Z 1218/1318 25030G42KT P6SM SKC FM130300 24022G32KT P6SM SKC FM131200 31020G30KT P6SM SKC', + 'TAF KCOF 121800Z 1218/1324 12012G18KT 9999 FEW040 QNH3010INS BECMG 1314/1315 14015G20KT 9999 FEW035 SCT310 QNH3004INS TX28/1218Z TN19/1310Z', + 'KCOS 121720Z 1218/1318 31025G45KT P6SM VCSH BKN100 BKN200 FM122100 32020G35KT P6SM BKN200 FM130100 32020G30KT P6SM BKN200 FM130400 32015KT P6SM BKN200 FM130700 34007KT P6SM BKN160 FM131600 29015G25KT P6SM BKN160', + 'KCOT 121724Z 1218/1318 17010G17KT P6SM BKN250 TEMPO 1306/1309 BKN015 FM130900 14010KT P6SM BKN015 WS020/18040KT FM131600 30010G18KT P6SM SKC', + 'KCOU 121748Z 1218/1318 16016G29KT P6SM BKN020 FM130900 19014G24KT P6SM VCTS BKN035CB FM131300 21010G18KT P6SM SHRA VCTS BKN020CB FM131400 28008KT P6SM SHRA VCTS BKN015CB', + 'KCPR 121728Z 1218/1318 25020G32KT P6SM VCSH SCT030 BKN060 FM121900 30018G30KT P6SM VCSH SCT025 BKN040 FM122300 02015G25KT P6SM VCSH SCT020 BKN030 FM130800 04008KT P6SM SCT030 BKN050 FM131500 26013G22KT P6SM SCT040 BKN060', + 'KCRP 121724Z 1218/1318 17022G31KT P6SM BKN250 TEMPO 1218/1222 BKN015 FM122300 15023G32KT P6SM VCTS SCT030CB BKN250 FM130200 15020G29KT P6SM OVC015 FM130800 16016G24KT 5SM BR SCT007 OVC015 FM131400 18015G22KT P6SM OVC030', + 'KCTB 121746Z 1218/1318 01022G32KT 1 1/2SM -SN OVC015 FM122000 01022G30KT P6SM OVC025 TEMPO 1220/1222 4SM -SN OVC015 FM130200 36016KT P6SM OVC015 PROB30 1303/1309 4SM -SN BR OVC009 FM130900 35014KT P6SM OVC025 PROB30 1315/1318 4SM -SN BR OVC009', + 'KCVG 121737Z 1218/1324 16012KT P6SM BKN018 FM130200 17010KT P6SM BKN040 FM130600 19011KT P6SM VCSH BKN090 WS020/21040KT FM131200 19012KT P6SM OVC070 FM131400 18013G21KT 6SM -SHRA BR BKN050 FM132300 18013KT P6SM BKN060', + 'TAF KCVS 121800Z 1218/1324 24030G40KT 8000 BLDU SKC 530009 QNH2961INS TEMPO 1218/1223 24035G45KT BECMG 1301/1302 25020G30KT 9000 BLDU FEW150 520009 QNH2950INS BECMG 1302/1303 26012G20KT 9999 NSW SKC QNH2954INS BECMG 1311/1312 32010G15KT 9999 SKC QNH2972INS BECMG 1315/1316 32015G25KT 9999 SKC QNH2988INS BECMG 1317/1318 29020G35KT 9000 BLDU SKC 520009 QNH2989INS BECMG 1323/1324 28015G30KT 9000 BLDU SKC 520009 QNH2983INS TX25/1221Z TN06/1313Z', + 'KCXO 121737Z 1218/1318 16010KT P6SM VCSH SCT018 BKN025 OVC035 FM122100 17014G24KT P6SM VCTS SCT025CB OVC035 FM130200 16011KT P6SM OVC013 FM130800 16017KT 5SM BR BKN015 FM131200 17014KT P6SM OVC015', + 'KCYS 121720Z 1218/1318 30030G45KT P6SM SCT100 FM130100 32020G30KT P6SM VCSH SCT060 BKN100 FM130800 34013KT P6SM SCT060 BKN080 FM131700 30020G30KT P6SM SCT070', + 'KDAL 121738Z 1218/1318 17017G28KT P6SM -RA BKN021 FM122000 17021G31KT P6SM VCTS BKN040CB TEMPO 1223/1301 22025G35KT 3SM TSRA OVC035CB FM130200 16019G29KT P6SM SCT040 FM130800 19020G30KT P6SM BKN015 FM131300 23018G28KT P6SM BKN035 FM131700 27020G30KT P6SM SKC', + 'KDAY 121737Z 1218/1318 16010KT P6SM BKN025 FM130300 17011G19KT P6SM BKN021 FM130600 18013KT P6SM BKN029 WS020/21040KT FM131200 19013KT P6SM OVC120 FM131500 19017G26KT 6SM -SHRA BR BKN035', + 'KDCA 121734Z 1218/1324 33010G17KT P6SM SCT050 BKN110 FM122200 28004KT P6SM FEW050 SCT150 FM130300 11004KT P6SM SCT060 FM131500 20007KT P6SM FEW080 SCT250 FM131800 21012G19KT P6SM FEW150 SCT250', + 'KDEN 121720Z 1218/1324 29014G22KT P6SM VCSH FEW045 BKN090 BKN220 FM122000 29020G30KT P6SM SCT060 BKN090 FM130000 30015G25KT P6SM SCT080 FM130700 32011KT P6SM SCT050 BKN080 FM131500 30018G30KT P6SM FEW090 SCT140', + 'KDET 121721Z 1218/1318 13009KT P6SM SKC FM122200 13010KT P6SM FEW050 SCT150 FM130000 13011KT P6SM BKN045 FM130300 15012KT P6SM SCT018 BKN025 WS020/18040KT FM130600 17010KT P6SM OVC010 WS020/20040KT FM131200 20014KT P6SM OVC017', + 'KDFW 121738Z 1218/1324 17017G28KT P6SM -RA BKN021 BKN050 FM122000 17021G31KT P6SM VCTS BKN040CB TEMPO 1223/1301 22025G35KT 3SM TSRA OVC035CB FM130200 16019G29KT P6SM SCT040 FM130800 19020G30KT P6SM BKN015 FM131300 23018G28KT P6SM BKN035 FM131700 27020G30KT P6SM SKC FM132100 31020G30KT P6SM SKC', + 'KDHN 121745Z 1218/1318 18010G16KT P6SM SCT030 BKN040 FM130100 19006KT P6SM BKN250 FM130700 14005KT P6SM SCT015 BKN150 FM130900 14005KT 6SM BR BKN015 BKN250 FM131300 16006KT P6SM FEW020 BKN250 FM131500 16013G18KT P6SM OVC250', + 'KDHT 121723Z 1218/1318 24038G50KT 5SM BLDU FEW250 FM130100 27023G32KT P6SM SCT250 FM130500 36015KT P6SM BKN250 FM131500 32018G26KT P6SM SKC', + 'TAF AMD KDLF 121546Z 1215/1317 12012G18KT 9999 SCT012 BKN015 BKN150 QNH2972INS BECMG 1216/1217 15009KT 9999 SCT020 SCT180 QNH2966INS BECMG 1217/1218 21012G18KT 9999 SKC QNH2948INS BECMG 1302/1303 15008KT 9999 SKC QNH2953INS BECMG 1311/1312 33015G25KT 9999 SKC QNH2953INS BECMG 1315/1316 35020G30KT 9999 SKC QNH2950INS TX35/1221Z TN18/1312Z', + 'KDLH 121737Z 1218/1318 10010G20KT P6SM SCT050 OVC070 FM121900 08013G23KT P6SM VCSH SCT025 OVC060 FM122200 08015G27KT 3SM -SHRA BR BKN015 OVC040 FM130300 08020G33KT 2SM -SHRA BR OVC003 WS020/10040KT TEMPO 1304/1306 1SM -TSRA BR BKN015CB FM131000 08019G32KT 2SM BR VCSH OVC002 FM131200 09010G18KT 1 1/2SM BR OVC003', + 'TAF AMD KDMA 121455Z 1214/1320 21015G26KT 9999 SKC 510009 QNH2980INS BECMG 1216/1217 28020G26KT 9999 SCT060 510009 QNH2976INS TEMPO 1220/1301 31025G30KT 510009 BECMG 1301/1302 31010KT 9999 SKC QNH2979INS BECMG 1309/1310 13009KT 9999 SKC QNH2999INS TX25/1222Z TN39/1312Z', + 'TAF KDOV 121600Z 1216/1322 28012KT 9999 FEW050 QNH3007INS WND VRB06KT AFT 1220 TEMPO 1216/1217 28012G20KT BECMG 1303/1304 16006KT 9999 BKN040 QNH3004INS BECMG 1317/1318 20006KT 9999 BKN100 QNH2994INS TX22/1218Z TN12/1310Z', + 'KDPA 121736Z 1218/1318 13012G20KT P6SM BKN250 FM121900 15013G20KT P6SM BKN040 BKN150 FM122200 16017G26KT P6SM SCT010 OVC020 FM130100 18019G28KT P6SM OVC010 WS015/20050KT FM130400 18018G28KT P6SM BKN014 WS020/20045KT FM131200 19015G23KT P6SM BKN020 PROB30 1312/1318 4SM TSRA BR OVC015CB', + 'KDRO 121816Z 1218/1318 27015G20KT P6SM VCSH SCT025 BKN040 OVC050 TEMPO 1218/1220 -SHRASN BKN025 FM122200 27020G35KT P6SM FEW040 SCT060 FM130200 31010G18KT P6SM FEW100 FM130800 28008KT P6SM FEW070 FEW100 FM131600 27014G24KT P6SM FEW100', + 'KDRT 121720Z 1218/1318 15012KT P6SM FEW200 FM121830 22012G22KT P6SM SKC FM130100 21009KT P6SM SKC FM130400 12009KT P6SM SKC FM131500 33015G25KT P6SM SKC', + 'KDSM 121745Z 1218/1318 14020G35KT P6SM VCSH OVC017 FM122100 16018G31KT P6SM BKN010 FM130400 18021G32KT 6SM SHRA VCTS BKN030CB WS020/19055KT TEMPO 1305/1308 VRB35G45KT 3SM -TSRA BKN025CB FM130900 27016KT P6SM BKN050 FM131200 30011G22KT P6SM BKN070', + 'KDTW 121721Z 1218/1324 13009KT P6SM SKC FM122200 13010KT P6SM FEW050 SCT150 FM130000 13011KT P6SM BKN045 FM130300 15012KT P6SM SCT018 BKN025 WS020/18040KT FM130600 17010KT P6SM OVC010 WS020/20040KT FM131200 20014KT P6SM OVC017', + 'TAF AMD KDYS 121402Z 1214/1318 18015G25KT 9999 FEW190 530053 QNH2970INS BECMG 1215/1216 22015G25KT 9999 FEW190 530053 QNH2949INS BECMG 1300/1301 23012KT 9999 SKC 510052 QNH2950INS BECMG 1314/1315 27020G30KT 9999 SKC 530103 QNH2970INS TX34/1221Z TN18/1214Z', + 'KECG 121720Z 1218/1318 26012KT P6SM SCT120 FM122300 17006KT P6SM SCT100 FM131100 21008KT P6SM SKC', + 'TAF AMD KEDW 121536Z 1215/1319 26015G25KT 9999 SKC QNH3002INS BECMG 1220/1221 31025G35KT 9999 SKC 520009 QNH2998INS BECMG 1301/1302 29015KT 9999 SKC QNH2999INS BECMG 1310/1311 24009KT 9999 SKC QNH3007INS TX14/1221Z TN04/1313Z', + 'TAF AMD KEFD 121440Z 1214/1318 15015G20KT 9999 BKN017 510023 QNH2985INS BECMG 1216/1217 15015G20KT 9999 OVC015 510023 QNH2981INS BECMG 1220/1221 16010G15KT 8000 -SHRA BKN012 510023 QNH2979INS TEMPO 1220/1300 17020G35KT 6000 -TSRA BKN009CB OVC016 BECMG 1300/1301 16012KT 8000 BR OVC008 WS016/18040KT 510203 QNH2970INS BECMG 1313/1314 17012KT 9999 NSW BKN015 WS016/18040KT 510203 QNH2971INS BECMG 1315/1316 18020G25KT 9999 BKN020 510013 QNH2974INS BECMG 1316/1317 19015G20KT 9000 -SHRA SCT020 510013 QNH2973INS TX27/1222Z TN21/1312Z', + 'KEKN 121723Z 1218/1318 23005KT P6SM SCT022 BKN035 FM131300 19004KT P6SM BKN060 FM131600 22008G17KT P6SM BKN090', + 'KELD 121741Z 1218/1318 18013G18KT P6SM VCTS SCT030 OVC035CB TEMPO 1220/1224 4SM -TSRA BR OVC025CB FM130000 16014G20KT P6SM VCTS OVC035CB FM130300 17014KT P6SM VCTS OVC025CB FM130600 17015KT P6SM VCTS OVC015CB', + 'KELP 121740Z 1218/1318 25030G40KT 5SM BLDU SKC FM122000 25035G45KT 3SM BLDU SKC FM130000 26030G45KT 6SM HZ FEW250 FM130900 29023G37KT P6SM FEW250 FM131400 29017G26KT P6SM SKC', + 'TAF KEND 121800Z 1218/1324 19030G40KT 9999 SKC QNH2942INS BECMG 1223/1224 17020G30KT 9999 SCT250 QNH2937INS BECMG 1306/1307 20020G30KT 9000 -TSRA OVC030CB QNH2942INS BECMG 1309/1310 25015KT 9999 NSW BKN070 QNH2958INS BECMG 1311/1312 36015G20KT 9999 FEW090 BKN130 QNH2968INS BECMG 1318/1319 35015G20KT 9999 SKC QNH2980INS TX30/1220Z TN09/1312Z', + 'KENV 121738Z 1218/1318 31016G30KT P6SM SCT070 FM130500 30011KT P6SM BKN100 FM130900 VRB05KT P6SM SCT100 FM131500 32011G24KT P6SM SCT200', + 'KEWN 121744Z 1218/1318 23010G17KT P6SM FEW120 FM122200 20010G15KT P6SM FEW120 FM130300 21007KT P6SM FEW120 FM130800 22006KT P6SM SCT030 FM131400 22008G14KT P6SM SCT120', + 'KEWR 121720Z 1218/1324 33012G19KT P6SM FEW060 FEW250 FM122200 31010KT P6SM FEW060 FM130100 30006KT P6SM SKC FM130500 VRB03KT P6SM FEW050 FM131400 15006KT P6SM BKN050 FM131800 18010KT P6SM FEW025 BKN050', + 'KEYW 121730Z 1218/1318 10014G22KT P6SM SCT045', + 'TAF KFAF 121800Z 1218/1324 25008KT 9999 SCT100 QNH3003INS BECMG 1223/1224 VRB06KT 9999 BKN060 QNH3001INS BECMG 1313/1314 23012G20KT 9999 SCT080 QNH2993INS TX25/1219Z TN17/1309Z', + 'KFAT 121720Z 1218/1318 31014G21KT P6SM SKC FM130600 31011KT P6SM SKC FM131200 32007KT P6SM SKC', + 'TAF KFCS 121300Z 1213/1319 23015KT 9999 SKC 570055 QNH2931INS BECMG 1214/1215 23020G25KT 9999 SKC 570105 QNH2931INS BECMG 1215/1216 23040G50KT 9999 SKC 570009 QNH2932INS BECMG 1217/1218 25020G30KT 9999 SCT065 570009 QNH2934INS BECMG 1218/1219 27018KT 9999 SKC 560053 QNH2936INS BECMG 1219/1220 24030G40KT 9999 FEW100 570009 QNH2938INS BECMG 1222/1223 24020G30KT 9999 FEW100 570009 QNH2943INS BECMG 1223/1224 25020G25KT 9999 SKC 530103 QNH2947INS BECMG 1300/1301 26015KT 9999 FEW095 520052 QNH2950INS BECMG 1306/1307 VRB06KT 9999 FEW095 510023 QNH2965INS BECMG 1317/1318 28010G15KT 9999 FEW100 510043 QNH2976INS TX15/1220Z TNM02/1309Z', + 'TAF KFFO 121100Z 1211/1317 VRB06KT 9999 BKN045 OVC080 QNH3003INS BECMG 1215/1216 14012KT 9999 BKN020 BKN030 QNH2997INS BECMG 1221/1222 18012KT 9999 SCT007 BKN015 510033 QNH2993INS BECMG 1302/1303 18012KT 9999 BKN007 OVC012 520202 QNH2984INS BECMG 1314/1315 17012KT 8000 -RA BR OVC007 510053 QNH2982INS TX20/1219Z TN10/1211Z', + 'TAF KFHU 121400Z 1214/1320 22015G24KT 9999 SKC 530007 QNH2987INS TEMPO 1214/1217 23022G35KT BECMG 1216/1217 23020G35KT 9999 SKC 530007 QNH2985INS TEMPO 1220/1223 25024G40KT 560005 530502 BECMG 1301/1302 27014G20KT 9999 SKC 520005 QNH2988INS BECMG 1306/1307 26009KT 9999 SKC QNH2998INS BECMG 1317/1318 30015G22KT 9999 SKC 520007 QNH3005INS TX21/1220Z TN03/1311Z', + 'KFLL 121720Z 1218/1324 11015G23KT P6SM SCT035 SCT050 FM122300 10012KT P6SM SCT040 SCT250 FM131400 12015G23KT P6SM SCT035 BKN100', + 'KFLO 121733Z 1218/1318 23009G14KT P6SM FEW150 FM122300 20005KT P6SM FEW200 FM131400 21010KT P6SM BKN150', + 'KFMH 121720Z 1218/1318 32009KT P6SM FEW030 OVC060 FM122100 26006KT P6SM FEW250 FM130400 32004KT P6SM SCT250 FM131000 23003KT P6SM SCT150 FM131300 19007KT P6SM SCT200 FM131700 19011G18KT P6SM BKN050', + 'KFMN 121735Z 1218/1318 29018G28KT P6SM FEW025 BKN050 TEMPO 1218/1219 5SM -SHRASN BKN020 FM121900 29022G32KT P6SM BKN070 FM130100 30009KT P6SM SCT080 FM131600 28016G26KT P6SM FEW070', + 'KFMY 121720Z 1218/1318 13012G18KT P6SM SCT050 FM130100 10006KT P6SM SCT250 FM131400 14012G20KT P6SM SCT040 SCT250', + 'KFOD 121745Z 1218/1318 14021G35KT P6SM VCSH BKN023 FM130000 15019G30KT 5SM -TSRA BR BKN050CB WS020/18045KT FM130400 25017G27KT 3SM -TSRA BR VCTS BKN025CB WS020/21050KT FM130700 25017G26KT P6SM BKN030 FM130900 28015G24KT P6SM BKN070', + 'KFOE 121724Z 1218/1318 18024G38KT P6SM SCT040 FM130400 23014G42KT 6SM TSRA BR OVC025CB FM130600 33014G24KT P6SM BKN070', + 'TAF AMD KFRI 121615Z 1216/1319 19030G40KT 9999 SCT030 530004 QNH2926INS BECMG 1223/1224 18010G15KT 9999 FEW085 530004 QNH2923INS BECMG 1303/1304 28010G15KT 4800 SHRA SCT020 BKN050 QNH2934INS TEMPO 1303/1306 21020G50KT 3200 TSRA BKN015CB OVC040 BECMG 1306/1307 34015KT 8000 -SHRA BKN040 510003 520503 QNH2951INS BECMG 1308/1309 34015KT 9999 NSW SCT065 510033 510553 QNH2955INS BECMG 1313/1314 34020G25KT 9999 SCT075 510043 QNH2967INS TX31/1221Z TN05/1311Z', + 'KFSM 121727Z 1218/1318 16012G18KT P6SM BKN040 FM122300 16012G18KT P6SM VCTS BKN040CB FM130200 13010KT P6SM BKN040 WS020/20045KT FM131200 19012G20KT P6SM BKN025 FM131700 25015G25KT P6SM VCTS BKN025CB', + 'TAF AMD KFTK 121503Z 1215/1317 09008KT 9999 SCT003 OVC009 QNH2995INS BECMG 1216/1217 12008KT 9999 VCSH SCT005 OVC012 QNH2988INS BECMG 1218/1219 16010KT 8000 -SHRA SCT015 OVC020 QNH2985INS BECMG 1311/1312 16010G15KT 6000 -SHRA VCTS BKN015CB OVC030 QNH2986INS TX20/1220Z TN16/1312Z', + 'KFTW 121738Z 1218/1318 17017G28KT P6SM -RA BKN021 FM122000 17021G31KT P6SM VCTS BKN040CB TEMPO 1223/1301 22025G35KT 3SM TSRA OVC035CB FM130200 16019G29KT P6SM SCT040 FM130800 19020G30KT P6SM BKN015 FM131300 23018G28KT P6SM BKN035 FM131700 27020G30KT P6SM SKC', + 'KFXE 121720Z 1218/1318 12015G22KT P6SM SCT035 SCT050 FM122300 10011KT P6SM SCT040 SCT250 FM131400 12012G22KT P6SM SCT035 BKN100', + 'KFYV 121727Z 1218/1318 18018G30KT P6SM BKN035 TEMPO 1218/1221 BKN025 FM130900 19015G25KT P6SM BKN025 FM131400 20015KT P6SM VCTS BKN025CB TEMPO 1315/1317 3SM TSRA BKN015CB FM131700 30015KT P6SM BKN025 TEMPO 1317/1318 3SM TSRA BKN015CB', + 'KGCK 121745Z 1218/1318 22033G48KT 6SM BLDU SKC FM122000 22035G51KT 4SM BLDU FEW250 FM122200 25031G44KT P6SM SCT250 FM130100 26020G30KT P6SM BKN150 FM130300 34024G34KT P6SM BKN100 FM131700 31019KT P6SM SCT120', + 'KGEG 121742Z 1218/1318 07013G19KT P6SM BKN050 FM130000 06011KT P6SM BKN050 FM130500 06006KT P6SM BKN060 FM131500 05013G19KT P6SM SCT060', + 'TAF KGFA 121200Z 1212/1318 03018KT 3000 -SN OVC015 620158 530054 QNH2972INS TEMPO 1212/1220 0800 SN OVC007 620109 BECMG 1220/1221 01015G20KT 4800 -SN OVC020 620209 520042 QNH2990INS TEMPO 1221/1300 2400 -SN OVC007 620079 BECMG 1300/1301 33009KT 4800 -SN OVC020 620209 510053 QNH2997INS BECMG 1314/1315 34009KT 8000 -SN OVC040 620409 510052 QNH3002INS TXM07/1219Z TNM08/1311Z', + 'KGFK 121726Z 1218/1318 04016KT P6SM OVC060 FM130000 06023G32KT 2SM -RASN OVC006 FM130400 06019G28KT 2SM RA BR OVC006 FM131300 13017G25KT 2SM -SN BR OVC005 FM131700 17016KT 4SM -RASN BLSN BR OVC008', + 'KGGG 121741Z 1218/1318 16018G24KT P6SM VCTS BKN020 OVC030CB FM122000 18014G22KT P6SM VCTS OVC035CB FM130600 17013KT P6SM VCTS OVC015CB FM130900 18014G20KT P6SM VCTS BKN015CB FM131200 19014G21KT 6SM -SHRA VCTS OVC015CB FM131600 21015G22KT 5SM -TSRA OVC015CB', + 'KGLS 121737Z 1218/1318 16017G35KT 3SM -SHRA FEW008 BKN014 OVC100 FM122200 16020G32KT P6SM VCTS OVC025CB FM130100 16019G29KT P6SM BKN011 FM130300 16016G25KT P6SM BKN008', + 'KGNV 121736Z 1218/1318 16007KT P6SM SKC FM130200 VRB03KT P6SM SCT250 FM131500 16010KT P6SM SCT250', + 'KGRB 121737Z 1218/1318 14013G22KT P6SM OVC120 FM122200 12015G26KT P6SM -SHRA OVC060 WS020/14045KT FM130000 11013G24KT P6SM -SHRA OVC035 WS020/13040KT FM130200 11012G24KT 5SM -SHRA OVC018 WS020/15040KT FM130400 14011G19KT 2SM -SHRA BR OVC007 WS020/20040KT TEMPO 1308/1310 4SM TSRA BR OVC007CB FM131000 19010G18KT 4SM BR SCT007 OVC012 WS020/22040KT TEMPO 1316/1318 4SM TSRA BKN015CB', + 'TAF KGRF 121300Z 1213/1319 15010KT 9999 VCSH BKN010 OVC020 610109 QNH2986INS BECMG 1213/1214 15012KT 9000 -SHRA BKN015 OVC025 610159 QNH2989INS BECMG 1217/1218 15010G16KT 9000 -SHRA SCT015 OVC035 640359 QNH2992INS BECMG 1220/1221 17010G20KT 9000 -SHRA BKN040 OVC050 640408 510002 QNH2994INS BECMG 1223/1224 15010KT 9999 NSW BKN050 620506 QNH2994INS BECMG 1306/1307 10006KT 9999 SCT075 BKN100 QNH2996INS BECMG 1315/1316 VRB03KT 9999 VCSH BKN045 OVC060 610456 QNH3001INS TX07/1223Z TN01/1312Z', + 'TAF KGRK 121600Z 1216/1322 17015G25KT 9999 BKN015 510052 QNH2970INS BECMG 1217/1218 18020G25KT 9000 -SHRA BKN030 510052 QNH2969INS TEMPO 1220/1221 21025G30KT 9000 TSRA BKN014CB OVC021 TEMPO 1222/1301 21030G40KT 8000 +TSRAGR BKN014CB BECMG 1302/1303 19015G20KT 9999 NSW SCT030 510052 QNH2962INS BECMG 1305/1306 16012KT 9999 BKN005 QNH2965INS BECMG 1309/1310 20009KT 9999 OVC013 QNH2969INS BECMG 1315/1316 28015G25KT 9999 SKC 520062 QNH2972INS TX32/1219Z TN21/1312Z', + 'KGRR 121725Z 1218/1318 12012G22KT P6SM BKN150 FM130200 12012KT P6SM VCSH OVC025 WS020/18040KT FM130600 16012G16KT P6SM OVC015 WS020/21040KT FM131200 19015G21KT P6SM VCSH OVC025', + 'TAF KGSB 121200Z 1212/1318 23012KT 9999 FEW060 BKN110 QNH3002INS TEMPO 1216/1220 25015G25KT 8000 -SHRA BKN040 BECMG 1304/1305 21009KT 9999 FEW060 FEW120 SCT200 QNH3001INS TX28/1221Z TN15/1311Z', + 'TAF KGTB 121700Z 1217/1323 30009KT 9999 SKC 510043 QNH3002INS BECMG 1221/1222 26010G15KT 9999 SKC 510053 QNH3005INS BECMG 1303/1304 VRB06KT 9999 FEW160 510053 QNH2999INS BECMG 1314/1315 13006KT 9000 -DZ BKN018 OVC025 510043 QNH2998INS BECMG 1315/1316 13006KT 9000 -RA BKN011 OVC018 530054 QNH2988INS BECMG 1322/1323 VRB06KT 8000 -SHRA BKN017 OVC024 530054 QNH2979INS TX15/1219Z TN05/1306Z', + 'KGTF 121746Z 1218/1318 02010G20KT 2SM -SN OVC015 TEMPO 1218/1222 1/2SM SN OVC010 FM130000 36014KT 2SM -SN BKN015 FM131100 33009KT 3SM -SN OVC008 FM131600 34010KT 5SM -SN SCT015 BKN025', + 'TAF KGUS 121300Z 1213/1319 11006KT 9999 FEW200 QNH2998INS BECMG 1215/1216 13012G18KT 9999 FEW040 SCT200 QNH2994INS BECMG 1217/1218 16015G20KT 9999 FEW025 SCT040 BKN055 QNH2986INS BECMG 1221/1222 16015G20KT 9999 OVC025 QNH2984INS BECMG 1300/1301 15012KT 9999 OVC020 QNH2970INS BECMG 1310/1311 19012KT 9999 OVC015 QNH2964INS BECMG 1314/1315 17015KT 8000 -RA BKN007 OVC014 QNH2961INS TX19/1219Z TN09/1213Z', + 'KGWO 121735Z 1218/1318 20019G28KT P6SM SCT040 SCT050 FM122000 18017G25KT P6SM OVC060 FM130000 17016KT 6SM -TSRA BKN030CB OVC070 WS020/18040KT FM130400 16016G26KT 6SM -SHRA BKN020 OVC045 FM130800 18014G26KT P6SM VCTS BKN015CB OVC030 WS020/19045KT FM131200 18017KT P6SM OVC015', + 'KHIB 121737Z 1218/1318 10008KT P6SM SCT050 OVC080 FM121900 09009G18KT P6SM VCSH SCT015 OVC060 FM122000 08012G21KT 5SM -SHRA BR OVC015 FM122200 08013G22KT 3SM -SHRA BR OVC008 FM130100 08015G25KT 3SM -SHRA BR OVC006 WS020/10040KT FM131100 09012G23KT 5SM BR OVC005', + 'TAF AMD KHIF 121609Z 1216/1319 24012G18KT 9999 BKN040 BKN070 620409 QNH2974INS TEMPO 1216/1223 27015G25KT 4800 -SHSN BKN020 BKN050 610209 BECMG 1222/1223 30009KT 9999 SCT030 BKN040 610409 QNH2978INS BECMG 1314/1315 34012KT 4800 -SHSN BKN010 OVC030 610109 QNH2994INS TX01/1221Z TNM02/1216Z', + 'KHKY 121727Z 1218/1318 22007KT P6SM VCSH BKN070 OVC100 TEMPO 1218/1222 5SM TSRA SCT030CB OVC070 FM130000 20004KT P6SM SCT240 FM130700 18003KT P6SM SCT230 FM131400 20006KT P6SM BKN210 FM131700 21010G18KT P6SM BKN120', + 'KHLN 121746Z 1218/1318 35018G28KT P6SM OVC025 TEMPO 1218/1222 4SM -SN OVC025 FM130200 34010G18KT P6SM OVC025 PROB30 1303/1306 4SM -SN BR OVC015 FM131200 VRB06KT P6SM SCT025 OVC050', + 'TAF COR KHMN 121711Z 1217/1323 22025G35KT 9000 BLDU FEW100 FEW250 510004 QNH2960INS TEMPO 1219/1301 24030G45KT 6000 BLDU 530004 BECMG 1304/1305 27015G25KT 9999 NSW FEW100 QNH2978INS BECMG 1310/1311 30012KT 9999 SKC QNH2996INS BECMG 1319/1320 27015G25KT 9999 SKC QNH2990INS TX29/1222Z TN05/1313Z', + 'KHOB 121720Z 1218/1318 24030G43KT P6SM SKC FM130200 23020G30KT P6SM SKC FM131000 29017G25KT P6SM SKC', + 'KHON 121739Z 1218/1318 09013G24KT P6SM OVC025 FM122100 09012KT P6SM OVC012 FM130000 34013KT P6SM BKN007 OVC015 FM130200 30017G28KT P6SM BKN015 OVC150 FM130800 27018G29KT P6SM SCT025', + 'TAF AMD KHOP 121800Z 1218/1322 19009KT 8000 -DZ BKN007 OVC025 QNH2994INS BECMG 1218/1219 20009KT 9999 NSW BKN018 OVC025 QNH2989INS BECMG 1300/1301 19015KT 9999 BKN030 QNH2983INS BECMG 1308/1309 19015KT 9000 -SHRA OVC020 QNH2981INS TEMPO 1309/1314 19025G35KT 8000 -TSRA OVC010CB BECMG 1317/1318 18012KT 9999 NSW BKN018 OVC026 520053 QNH2983INS TX24/1222Z TN16/1311Z', + 'KHOU 121737Z 1218/1318 17016G24KT 6SM BR VCSH SCT015 OVC025 FM122100 16016G25KT P6SM VCTS SCT015CB OVC040 FM130100 17013G018KT P6SM OVC015', + 'KHPN 121720Z 1218/1318 31011G18KT P6SM SCT060 FM122100 31009KT P6SM FEW060 FM130100 VRB03KT P6SM FEW050 FM131500 14007KT P6SM BKN050 AMD NOT SKED', + 'KHRL 121728Z 1218/1318 17021G30KT P6SM SCT028 TEMPO 1218/1220 BKN026 FM130100 16014G24KT P6SM VCTS BKN020CB FM130600 16017KT P6SM BKN030 FM131600 19017G25KT P6SM SCT022', + 'KHRO 121736Z 1218/1318 20008G17KT P6SM BKN035 FM130000 16020G27KT P6SM VCTS OVC050CB FM130700 18013G27KT P6SM BKN040 PROB30 1307/1309 4SM TSRA BR OVC028CB FM131400 18012G24KT 5SM BR BKN010 PROB30 1314/1318 2SM TSRA BR OVC008CB', + 'TAF KHRT 121800Z 1218/1324 14015G20KT 9999 BKN030 BKN120 QNH3012INS BECMG 1223/1224 15012KT 9999 SCT030 BKN080 QNH3012INS BECMG 1303/1304 VRB06KT 8000 BR SCT015 BKN150 QNH3006INS BECMG 1305/1306 VRB06KT 6000 BR BKN015 BKN150 QNH3004INS BECMG 1313/1314 13012KT 9999 NSW BKN030 BKN120 QNH3005INS BECMG 1316/1317 15015G25KT 9999 SCT030 BKN120 QNH3006INS TX25/1222Z TN20/1309Z', + 'TAF AMD KHST 121145Z 1211/1317 08009KT 9999 BKN040 QNH3010INS BECMG 1214/1215 10012G20KT 9999 SCT035 BKN060 QNH3008INS BECMG 1223/1224 09009KT 9999 FEW035 SCT060 QNH3004INS BECMG 1314/1315 11012G20KT 9999 SCT030 QNH3006INS TX27/1219Z TN22/1211Z', + 'TAF AMD KHUA 121730Z 1217/1318 24010G15KT 9999 BKN025 QNH2995INS BECMG 1219/1220 18012G18KT 9999 SCT040 QNH2995INS BECMG 1222/1223 18009KT 9999 SCT040 510034 QNH2990INS BECMG 1310/1311 18012G20KT 9999 OVC010 540034 QNH2991INS BECMG 1313/1314 17020G35KT 9999 SCT020 510002 520202 QNH2993INS TX26/1221Z TN16/1312Z', + 'KHUF 121720Z 1218/1318 16011G20KT P6SM BKN010 FM130500 18016G26KT P6SM BKN090 WS020/19045KT FM130900 19015G27KT P6SM VCTS OVC050CB WS020/21045KT FM131100 18015G26KT P6SM SHRA VCTS OVC030CB', + 'KHUL 121740Z 1218/1318 25003KT P6SM VCSH OVC017 FM121900 29009KT P6SM BKN027 FM122100 29010KT P6SM BKN045 FM122300 30010KT P6SM SCT070 FM130300 30005KT P6SM SKC WS020/33035KT FM130400 29006KT P6SM FEW250 FM131700 28003KT P6SM BKN120', + 'KHVR 121746Z 1218/1318 01016G25KT P6SM OVC025 TEMPO 1218/1219 1SM -SN BR OVC015 FM130000 36015KT P6SM VCSH OVC050 PROB30 1303/1305 4SM -SN BR OVC015 FM130500 36011KT P6SM OVC015 PROB30 1305/1311 4SM -SN BR OVC009 FM131600 33013KT P6SM SCT015 OVC025 PROB30 1316/1318 4SM -SN BR BKN015', + 'TAF KIAB 121800Z 1218/1324 21025G40KT 9999 FEW030 520005 QNH2930INS TEMPO 1219/1224 21035G50KT BECMG 1223/1224 21025G35KT 9999 SCT030 520053 QNH2930INS BECMG 1304/1305 19025G35KT 6000 -SHRA VCTS SCT010 OVC020CB 520053 QNH2940INS TEMPO 1305/1307 22040G50KT 1600 +TSRAGR OVC010CB BECMG 1306/1307 34025G35KT 9999 NSW SCT025 BKN090 BKN200 550259 QNH2945INS BECMG 1314/1315 35025G35KT 9000 -SHRA FEW030 BKN050 OVC090 640509 550509 QNH2950INS BECMG 1318/1319 35020G30KT 9999 NSW BKN090 620905 520009 QNH2980INS BECMG 1321/1322 32009KT 9999 BKN120 QNH2982INS TX31/1221Z TN07/1313Z', + 'KIAD 121734Z 1218/1324 30010G18KT P6SM BKN050 FM122300 01003KT P6SM FEW040 SCT110 FM130600 14004KT P6SM FEW070 SCT110 FM131500 19007KT P6SM FEW080 SCT250 FM131800 20010G18KT P6SM FEW110 SCT250', + 'KIAG 121737Z 1218/1318 VRB05KT P6SM FEW045 FEW250 FM130900 15009KT P6SM BKN045 FM131000 16011G19KT P6SM BKN250 FM131200 19015G26KT P6SM BKN070 FM131700 22020G34KT P6SM BKN080', + 'KIAH 121737Z 1218/1324 15014G22KT 6SM BR VCSH BKN020 OVC035 FM122100 16015G25KT 6SM BR VCTS SCT015CB OVC040 FM130000 17010G16KT P6SM OVC020 FM131200 17012G20KT 6SM BR BKN015 FM132200 22011KT P6SM BKN040', + 'KICT 121732Z 1218/1318 19030G43KT P6SM FEW090 FM130100 17027G38KT P6SM SCT200 FM130400 23015G27KT P6SM VCTS BKN100CB FM130800 36018G28KT P6SM FEW180 FM131200 35009KT P6SM FEW100', + 'KILG 121725Z 1218/1318 32010G17KT P6SM FEW060 FM122300 VRB03KT P6SM FEW250 FM130500 VRB03KT P6SM BKN060 FM131400 16007KT P6SM BKN070', + 'KILM 121733Z 1218/1318 22011G17KT P6SM FEW200 FM130000 21008KT P6SM FEW200 FM130600 21004KT P6SM FEW150 FM131400 21010G17KT P6SM SCT150', + 'KIND 121720Z 1218/1324 15010G20KT P6SM BKN019 FM122200 16011KT P6SM BKN009 FM130600 18014G24KT P6SM BKN120 WS020/20045KT FM131100 18014G24KT 5SM SHRA OVC040 FM132300 18017G26KT 4SM TSRA BR OVC035CB', + 'KINK 121720Z 1218/1318 26023G33KT P6SM SKC FM130300 27018G25KT P6SM SKC', + 'KINL 121737Z 1218/1318 07005KT P6SM OVC090 FM122200 07009KT P6SM VCSH SCT015 OVC045 FM122300 07010KT 2SM -SHRASN BR OVC015 FM130100 06014G23KT 2SM -SHRASN BR OVC005 FM130500 07015G25KT 2SM -SHRA BR OVC005 WS020/09040KT FM131300 08013G24KT 2SM -SHRA BR OVC004', + 'TAF AMD KINS 121518Z 1215/1319 30012KT 9999 SKC QNH2988INS BECMG 1217/1218 30012G20KT 9999 FEW070 510009 QNH2992INS WND 260V330 TEMPO 1222/1300 30015G26KT 520009 BECMG 1301/1302 30010G15KT 9999 FEW150 QNH2987INS BECMG 1303/1304 VRB06KT 9999 FEW100 QNH2988INS BECMG 1316/1317 27009KT 9999 SKC QNH2992INS TX14/1223Z TN03/1313Z', + 'KINT 121740Z 1218/1318 22007KT P6SM SCT080 BKN110 FM130000 20003KT P6SM SCT070 SCT150 FM131400 21009G15KT P6SM SCT040 BKN150', + 'KIPL 121720Z 1218/1318 31015G24KT P6SM SKC FM130300 27012KT P6SM SKC FM130500 27006KT P6SM SKC', + 'KIPT 121746Z 1218/1318 28007KT P6SM SKC FM130000 00000KT P6SM BKN250 FM131400 14007KT P6SM SCT100 BKN250', + 'KISP 121720Z 1218/1318 32012G18KT P6SM FEW060 FEW250 FM122200 29011KT P6SM FEW060 FM130100 VRB03KT P6SM FEW050 FM131300 16007KT P6SM BKN050 FM131700 17010KT P6SM BKN050', + 'KJAN 121735Z 1218/1318 18023G30KT P6SM BKN040 FM122300 17015G23KT 6SM -TSRA BKN035CB OVC060 FM130400 14016G27KT 6SM SHRA BKN025 OVC045 WS020/19045KT FM130900 17014G25KT P6SM SCT022 OVC026 FM131000 16018G24KT P6SM OVC015 FM131700 16022G29KT P6SM BKN050', + 'KJAX 121736Z 1218/1318 15010KT P6SM FEW030 SCT140 FM130200 15006KT P6SM SCT250 FM130700 VRB03KT P6SM BKN250 FM131500 17012KT P6SM FEW030 BKN250', + 'KJBR 121745Z 1218/1318 19018G26KT P6SM SCT025 BKN040 FM130300 17017G24KT P6SM VCSH OVC019 FM130500 18017G24KT P6SM VCTS OVC025CB WS020/20050KT FM130900 20019G26KT P6SM -SHRA BKN025 FM131300 20020G28KT P6SM VCSH SCT025 BKN040', + 'KJFK 121720Z 1218/1324 33013G20KT P6SM FEW060 FM122200 28010KT P6SM FEW060 FM130100 28007KT P6SM SKC FM130500 VRB04KT P6SM FEW050 FM131400 15008KT P6SM BKN050 FM131800 17013KT P6SM FEW025 BKN050', + 'KLAN 121725Z 1218/1318 13006KT P6SM SCT250 FM122000 12012G22KT P6SM BKN200 FM130300 13013KT P6SM VCSH OVC025 WS020/18040KT FM130600 17015KT P6SM OVC015 WS020/21040KT FM131100 20010G21KT P6SM VCSH BKN025', + 'KLAS 121733Z 1218/1324 34015G27KT P6SM SKC FM130300 VRB06KT P6SM SKC FM131700 08005KT P6SM SKC', + 'KLAX 121744Z 1218/1324 33024G31KT P6SM FEW020 FEW050 FM122100 29025G35KT P6SM SKC FM130400 28015G25KT P6SM SKC FM130800 32010KT P6SM SKC FM131300 04006KT P6SM SKC FM132000 26012KT P6SM SKC', + 'KLBB 121720Z 1218/1318 24025G35KT 5SM BLDU SKC FM122100 23030G40KT 5SM BLDU SKC TEMPO 1221/1301 3SM BLDU BKN030 BKN250 FM130100 23020KT 5SM BLDU SKC WS010/24050KT TEMPO 1301/1304 3SM BLDU BKN030 FM130900 29015KT P6SM SKC', + 'KLCH 121802Z 1218/1318 17018G27KT P6SM VCTS SCT015CB OVC025 TEMPO 1219/1221 4SM TSRA BR BKN014CB FM122200 17018G30KT 5SM -SHRA BR VCTS OVC015CB TEMPO 1222/1302 4SM TSRA BR BKN016CB FM131200 16015G22KT P6SM VCTS OVC015CB PROB30 1312/1318 2SM TSRA OVC015CB', + 'KLCK 121737Z 1218/1318 17008KT P6SM SCT025 FM122300 16007KT P6SM BKN028 FM130400 17009KT P6SM BKN090 FM130900 18010KT P6SM BKN090 WS020/22040KT FM131400 19012G21KT P6SM BKN090 FM131600 19015G27KT 6SM -SHRA BR OVC050', + 'TAF KLFI 121700Z 1217/1323 25009KT 9999 FEW040 BKN050 QNH3009INS BECMG 1300/1301 VRB06KT 9999 FEW060 SCT090 QNH3005INS BECMG 1305/1306 VRB06KT 9999 FEW040 BKN060 OVC080 QNH3007INS BECMG 1318/1319 21011KT 9999 FEW080 QNH3002INS TX25/1221Z TN16/1308Z', + 'KLFK 121741Z 1218/1318 16011KT P6SM OVC030 FM122000 17012G18KT P6SM VCTS OVC035CB FM130200 17011KT P6SM VCTS BKN015CB FM130700 18010KT P6SM VCTS OVC010CB FM131700 19011KT 6SM -SHRA VCTS BKN008 OVC015CB', + 'KLFT 121804Z 1218/1318 17017G24KT P6SM VCTS BKN019CB OVC025 FM130300 16014G24KT P6SM VCTS OVC025CB FM131200 17014G21KT P6SM VCTS OVC015CB PROB30 1312/1318 2SM TSRA OVC015CB', + 'KLGA 121720Z 1218/1324 32012G19KT P6SM FEW060 FM122200 31010KT P6SM FEW060 FM130100 28007KT P6SM SKC FM130500 VRB04KT P6SM FEW050 FM131400 14007KT P6SM BKN050 FM131800 17012KT P6SM FEW025 BKN050', + 'KLGB 121744Z 1218/1318 33019G26KT P6SM SKC FM122100 29020G30KT P6SM SKC FM130200 29012G20KT P6SM SKC FM130700 34006KT P6SM SKC FM131200 VRB03KT P6SM SKC', + 'KLIT 121736Z 1218/1318 22014G24KT P6SM BKN035 FM121930 17017G25KT P6SM OVC050 TEMPO 1302/1306 2SM TSRA BR OVC015CB FM130600 18017G24KT 3SM TSRA BR OVC025CB FM131100 19012G22KT 6SM BR OVC020 PROB30 1311/1317 4SM TSRA BR OVC023CB FM131700 20015G26KT 6SM -SHRA BR VCTS BKN010CB', + 'KLNK 121720Z 1218/1318 16020G26KT P6SM SCT250 TEMPO 1218/1219 BKN025 FM130000 23019G30KT P6SM SCT250 TEMPO 1300/1304 4SM +TSRA BR BKN010CB FM130400 33016G24KT P6SM BKN120', + 'KLRD 121724Z 1218/1318 16012KT P6SM BKN250 FM122200 15010KT P6SM SCT250 FM130300 17016G23KT P6SM SCT050 SCT250 TEMPO 1309/1313 BKN025 FM131500 28012G20KT P6SM SKC', + 'TAF AMD KLRF 121726Z 1217/1321 18012G20KT 9999 BKN040 BKN080 QNH2985INS BECMG 1218/1219 18020G25KT 9999 SCT040 SCT090 QNH2976INS BECMG 1220/1221 18012KT 9999 BKN028 WS020/18035KT QNH2974INS BECMG 1222/1223 18012KT 9999 VCSH OVC021 WS020/18040KT QNH2973INS BECMG 1223/1224 18012KT 9000 -SHRA SCT016 OVC020 WS020/18040KT QNH2968INS BECMG 1301/1302 18012KT 6000 -TSRA BKN004 BKN013 OVC022CB QNH2965INS TEMPO 1302/1306 19020G40KT 2400 +TSRAGR OVC004CB BECMG 1308/1309 18012G18KT 9999 VCSH OVC003 QNH2963INS BECMG 1310/1311 18012KT 9999 NSW OVC005 QNH2963INS BECMG 1315/1316 18012G18KT 9999 VCTS OVC009CB QNH2967INS BECMG 1316/1317 18012G25KT 8000 -TSRA OVC012CB QNH2958INS BECMG 1319/1320 18035G50KT 0800 +TSRAGR OVC007CB QNH2957INS TX29/1220Z TN22/1311Z', + 'TAF KLSF 121300Z 1213/1319 VRB05KT 9999 SKC QNH3015INS BECMG 1216/1217 20008KT 9999 FEW060 SCT120 510053 QNH3005INS TEMPO 1217/1222 21011G20KT BECMG 1305/1306 18004KT 6000 BR BKN120 510102 QNH3005INS TEMPO 1308/1312 1600 BR SCT000 SCT120 BECMG 1314/1315 16010KT 9999 NSW SCT020 BKN120 510003 QNH3000INS BECMG 1316/1317 17013G25KT 9999 FEW035 BKN120 510003 QNH2995INS TX29/1220Z TN12/1312Z', + 'TAF AMD KLTS 121759Z 1217/1323 20020G30KT 9999 FEW240 510003 QNH2948INS BECMG 1218/1219 20025G35KT 9999 SCT240 510003 QNH2935INS BECMG 1223/1224 19015KT 9999 SKC 510012 QNH2936INS BECMG 1302/1303 19012KT 9999 SKC WS015/21035KT QNH2939INS BECMG 1308/1309 34015G22KT 9999 SKC 510102 QNH2944INS BECMG 1310/1311 34025G35KT 9999 SCT150 510003 QNH2950INS BECMG 1314/1315 34012G22KT 9999 SKC 510052 QNH2966INS BECMG 1318/1319 29015G25KT 9999 SKC 510052 QNH2972INS TEMPO 1321/1323 29025G35KT 510003 TX35/1221Z TN20/1312Z/', + 'TAF AMD KLUF 121240Z 1212/1318 22012G18KT 9999 FEW070 QNH2973INS BECMG 1216/1217 25010G15KT 9999 FEW070 QNH2979INS BECMG 1221/1222 31015G27KT 9999 SKC 520007 QNH2974INS BECMG 1304/1305 VRB06KT 9999 SKC QNH2987INS TX23/1222Z TN13/1213Z', + 'KLUK 121737Z 1218/1318 17010KT P6SM BKN023 FM130200 17007KT P6SM BKN040 FM130600 18008KT P6SM BKN090 WS020/21040KT FM131200 19008KT P6SM OVC070 FM131500 19012G20KT 6SM -SHRA BR BKN050', + 'KMAF 121720Z 1218/1318 25023G33KT P6SM SKC FM130300 26015G22KT P6SM SKC FM131400 31020G28KT P6SM SKC', + 'KMCC 121739Z 1218/1318 32005KT P6SM SKC FM130000 22007KT P6SM SCT070 FM130900 15007KT P6SM OVC200', + 'TAF KMCF 121700Z 1217/1323 13009KT 9999 FEW060 QNH3009INS BECMG 1220/1221 VRB06KT 9999 FEW060 QNH3004INS BECMG 1309/1310 12010KT 9999 FEW040 QNH3004INS BECMG 1316/1317 14015G22KT 9999 FEW050 QNH2998INS TX29/1220Z TN17/1310Z', + 'KMCI 121740Z 1218/1318 17018G33KT P6SM BKN028 FM121900 18020G34KT P6SM SCT070 FM130600 20020G33KT P6SM VCTS SCT050CB FM130700 26015G26KT 6SM -TSRA BKN028CB FM130900 33013G24KT P6SM BKN035', + 'KMCN 121746Z 1218/1318 20009KT P6SM FEW040 FM130000 18005KT P6SM SCT100 BKN200 FM131500 18013G22KT P6SM SCT040 BKN100', + 'KMCO 121725Z 1218/1324 13012G17KT P6SM SCT040 SCT200 FM130100 12006KT P6SM FEW040 FM131500 14011KT P6SM SCT040 SCT250', + 'KMDT 121746Z 1218/1318 30007KT P6SM SCT050 FM130000 12003KT P6SM BKN250 FM131100 15007KT P6SM SCT100 BKN250', + 'KMDW 121750Z 1218/1324 13015G24KT P6SM SCT250 FM122200 16017G26KT P6SM SCT010 OVC025 FM130200 18019G28KT P6SM OVC010 WS015/20050KT FM130700 20018G27KT P6SM BKN025 OVC040 WS015/21050KT FM131200 19015G24KT P6SM OVC015 PROB30 1313/1317 6SM SHRA FM132100 20014G22KT 5SM SHRA BR BKN040 PROB30 1321/1324 2SM TSRA BR OVC015CB', + 'KMEM 121729Z 1218/1324 19015G25KT P6SM SCT025 BKN040 FM130200 16019G26KT P6SM VCSH OVC060 FM130500 18019G28KT P6SM -SHRA VCTS OVC025CB TEMPO 1307/1310 3SM -TSRA FM131100 20020G25KT P6SM VCSH OVC025 FM131500 20018G24KT P6SM VCSH SCT025 OVC040', + 'KMER 121720Z 1218/1318 32012G20KT P6SM SKC FM130600 VRB06KT P6SM SKC', + 'KMFE 121728Z 1218/1318 16018G25KT 6SM HZ BR SCT025 TEMPO 1218/1220 BKN026 FM130000 15013G23KT P6SM VCTS BKN030CB FM130600 15016KT P6SM BKN030', + 'TAF KMGE 121300Z 1213/1319 19007KT 9999 BKN080 BKN180 QNH3010INS BECMG 1214/1215 20010G15KT 9999 SCT060 SCT120 BKN200 QNH3005INS BECMG 1222/1223 17006KT 9999 SCT120 BKN200 QNH3003INS BECMG 1314/1315 17010G15KT 9999 BKN200 QNH3005INS BECMG 1316/1317 17015G25KT 9999 BKN200 QNH3001INS TX27/1221Z TN15/1311Z', + 'KMHR 121739Z 1218/1318 25006KT P6SM SKC FM130000 21007KT P6SM SCT070 FM130900 14005KT P6SM OVC200', + 'KMIA 121720Z 1218/1324 11015KT P6SM SCT035 SCT050 FM122300 10012KT P6SM SCT040 SCT250 FM131400 12012KT P6SM SCT035 BKN100', + 'TAF AMD KMIB 121446Z 1214/1315 VRB06KT 9999 OVC100 QNH2985INS BECMG 1214/1215 05012G18KT 3200 -SN BKN005 OVC030 620059 QNH2983INS BECMG 1216/1217 06015G25KT 0800 SN BKN005 OVC030 620059 510003 QNH2977INS BECMG 1223/1224 03025G35KT 0400 +SN BKN005 OVC010 620059 510003 QNH2975INS BECMG 1303/1304 03025G35KT 0200 +SN BKN005 OVC010 620059 510003 QNH2964INS TXM02/1214Z TNM05/1309Z LAST NO AMDS AFT 1215 NEXT 1509', + 'KMIV 121725Z 1218/1318 31010G18KT P6SM FEW070 FM122300 VRB03KT P6SM FEW250 FM130600 VRB03KT P6SM BKN060 FM131300 16006KT P6SM BKN080', + 'KMKE 121745Z 1218/1324 14013KT P6SM SCT080 OVC150 FM130000 13015G26KT P6SM OVC035 FM130200 15015G23KT P6SM OVC013 WS020/17045KT FM130700 18013KT P6SM OVC021 WS020/21045KT FM131100 19011G19KT P6SM OVC024', + 'KMKL 121729Z 1218/1318 20016G22KT P6SM BKN035 FM130600 18016G24KT P6SM VCSH OVC060 FM130900 18017G25KT P6SM -SHRA VCTS OVC025CB FM131200 20017G24KT P6SM VCSH SCT025 OVC040', + 'KMLB 121725Z 1218/1318 12014G20KT P6SM SCT040 FM130100 13007KT P6SM SCT035 FM131500 14012KT P6SM SCT035 SCT250', + 'KMLC 121727Z 1218/1318 18012G25KT P6SM BKN040 TEMPO 1218/1221 BKN025 FM122100 18012G25KT P6SM VCSH BKN040 FM130800 17014G22KT P6SM BKN025 FM131300 21012G18KT P6SM VCTS BKN025CB FM131600 31012G20KT P6SM BKN025', + 'KMLU 121741Z 1218/1318 18012G18KT P6SM VCTS BKN025 OVC040CB TEMPO 1218/1222 4SM -TSRA BR OVC025CB FM122200 16014G20KT P6SM VCTS OVC035CB FM130300 17014KT P6SM VCTS OVC025CB FM130600 17015KT P6SM VCTS OVC015CB', + 'KMOB 121745Z 1218/1318 17013G20KT P6SM SCT030 BKN040 FM130500 14011KT P6SM OVC014 FM131500 17016G25KT P6SM BKN030', + 'KMOD 121739Z 1218/1318 31013G20KT P6SM SKC FM130500 31008KT P6SM SCT250 FM131200 VRB04KT P6SM OVC200', + 'KMOT 121734Z 1218/1318 03019G28KT 1 1/2SM -SN BLSN SCT008 OVC018 TEMPO 1218/1221 03024G36KT 3/4SM -SN BLSN BKN009 OVC015 FM122100 02024G36KT 1/2SM SN BLSN OVC009 FM130100 02022G33KT 1/4SM +SN BLSN OVC008 FM131000 35025G36KT 1/2SM SN BLSN SCT009 OVC011', + 'KMPV 121722Z 1218/1318 28006KT P6SM BKN035 OVC070 FM122000 31015G23KT P6SM SKC FM130000 VRB04KT P6SM SKC FM131600 17007KT P6SM BKN080', + 'KMSN 121745Z 1218/1318 13013G21KT P6SM SCT060 OVC120 FM122300 14016G26KT P6SM OVC028 FM130400 16017G27KT P6SM OVC008 WS020/19045KT FM130600 18018G28KT P6SM OVC011 WS020/20050KT FM131100 19012G23KT P6SM -SHRA SCT012 OVC025 FM131600 21012G21KT P6SM SCT012 BKN025', + 'KMSP 121738Z 1218/1324 11020G32KT P6SM OVC100 FM121900 11017G30KT 5SM -TSRA OVC050CB FM122100 09014G25KT P6SM OVC035 FM130100 09013G23KT 2SM TSRA BR OVC008CB FM130500 07009KT P6SM -SHRA OVC007 FM130900 21010KT P6SM BKN015 FM131500 27012G22KT P6SM BKN040', + 'KMSS 121722Z 1218/1318 27012G20KT P6SM SKC FM122300 27007KT P6SM SKC FM131000 VRB03KT P6SM BKN120', + 'KMSY 121727Z 1218/1324 17015G24KT P6SM SCT050 OVC110 FM130200 16011KT P6SM VCTS SCT025CB BKN100 FM130300 15013G21KT P6SM VCSH SCT025 OVC100 FM130700 14015G24KT P6SM OVC025 FM131400 17016G25KT P6SM SCT020 BKN035 FM131700 17019G29KT P6SM -SHRA SCT035', + 'TAF KMTC 121700Z 1217/1323 14009KT 9999 SKC QNH2993INS BECMG 1307/1308 12009KT 9999 BKN030 OVC040 510053 QNH2988INS BECMG 1316/1317 18012G18KT 8000 -SHRA OVC010 520053 QNH2973INS BECMG 1319/1320 17015KT 9000 -RA OVC020 530054 QNH2964INS TX18/1317Z TN09/1303Z', + 'TAF KMUI 121100Z 1211/1317 VRB06KT 9999 BKN035 OVC080 QNH3000INS TEMPO 1211/1215 9000 -SHRA BR BKN025 BECMG 1214/1215 32006KT 9999 BKN030 QNH3004INS BECMG 1217/1218 30006KT 9999 SCT040 BKN060 QNH3006INS BECMG 1300/1301 16009KT 9000 BR BKN040 OVC100 QNH3006INS BECMG 1306/1307 17006KT 9999 NSW SCT010 BKN015 OVC025 QNH3000INS TX20/1222Z TN08/1211Z', + 'TAF KMUO 121800Z 1218/1324 32020G30KT 9999 VCSH SCT030 BKN070 610703 510009 QNH2987INS TEMPO 1218/1221 31025G35KT 520009 BECMG 1301/1302 28009KT 9999 NSW SCT040 QNH2992INS BECMG 1305/1306 VRB06KT 9999 BKN040 BKN150 620406 QNH2996INS BECMG 1315/1316 31009KT 9999 SCT050 BKN100 QNH3000INS TEMPO 1320/1323 BKN050 BKN100 620505 TX06/1221Z TNM05/1312Z', + 'KMWH 121739Z 1218/1318 09013KT P6SM VCSH BKN035 OVC050 FM130000 07009KT P6SM BKN050 FM130600 07009KT P6SM SCT020 OVC050 PROB30 1307/1311 4SM -SN BKN020 OVC040 FM131100 02015G23KT P6SM SCT025 OVC060', + 'TAF AMD KMXF 121300Z 1213/1317 VRB06KT 9999 OVC020 QNH3006INS BECMG 1215/1216 17012KT 9999 OVC020 QNH3012INS BECMG 1216/1217 19010G15KT 9999 BKN025 QNH3010INS BECMG 1217/1218 20010G15KT 9999 SCT025 QNH3001INS BECMG 1221/1222 17012G18KT 9999 BKN045 QNH3000INS BECMG 1300/1301 VRB06KT 9999 SKC 510033 QNH3000INS BECMG 1310/1311 14006KT 8000 BR FEW010 510033 QNH2998INS BECMG 1315/1316 17020G25KT 9999 NSW SCT015 QNH3000INS TX26/1220Z TN14/1311Z', + 'KMYR 121733Z 1218/1318 19012G17KT P6SM FEW200 FM130000 20007KT P6SM FEW250 FM131500 19010G15KT P6SM SCT150', + 'TAF KNBC 1215/1315 22010KT 9999 FEW120 QNH3017INS BECMG 1217/1219 18010G20KT 9999 FEW120 QNH3010INS FM122300 17010KT 9999 SCT020 QNH3007INS T27/1217Z T17/1309Z', + 'TAF COR KNBG 1215/1315 17013G23KT 8000 BR VCSH SCT020CB BKN035 BKN100 OVC250 QNH2998INS TEMPO 1215/1218 VRB25G35KT 3200 TSRA BR BKN018CB OVC030 BECMG 1218/1220 18017G25KT 9999 VCTS SCT025CB BKN040 BKN100 OVC250 QNH2993INS TEMPO 1218/1300 VRB25G35KT 3200 TSRA BR BKN015CB OVC030 BECMG 1300/1302 16010G19KT 9999 NSW SCT020 BKN035 OVC250 QNH2996INS BECMG 1306/1308 17007KT 3200 BR OVC007 QNH2985INS BECMG 1313/1315 17018G30KT 9999 BKN025 BKN120 OVC250 QNH2996INS T27/1218Z T21/1312Z COR 1545 FN20039', + 'TAF KNCA 1215/1315 21015G23KT 9999 BKN100 QNH3012INS TEMPO 1220/1300 21018G25KT FM130100 21012G19KT 9999 SKC QNH3014INS T28/1219Z T19/1311Z', + 'TAF KNFL 1215/1315 30010KT 9999 FEW050 620309 QNH2992INS FM122000 31012KT 9999 VCSH SCT050 SCT090 620409 QNH2989INS TEMPO 1222/1304 32015G25KT FM130500 VRB06KT 9999 SCT060 BKN100 620409 QNH2993INS T10/1223Z TM03/1314Z FS30155', + 'TAF KNGU 1215/1315 26008G15KT 9999 VCSH FEW060 BKN100 QNH3006INS TEMPO 1215/1217 9000 -SHRA BR BKN060 BKN100 BECMG 1220/1222 24007KT 9999 FEW100 BKN260 QNH3006INS FM130100 VRB05KT 9999 BKN060 BKN260 QNH3006INS FM130900 22008KT 9999 FEW060 SCT260 QNH3007INS T26/1221Z T17/1309Z FN20002', + 'TAF KNHK 1215/1315 25011KT 9999 VCSH FEW045 SCT100 BKN1500 QNH3004INS TEMPO 1215/1218 9000 -SHRA BKN040 OVC100 BECMG 1218/1220 25010KT 9999 NSW BKN090 BKN250 QNH3003INS BECMG 1300/1302 16006KT 9999 BKN090 OVC250 QNH3003INS T24/1220Z T15/1310Z FN00235', + 'TAF KNID 1215/1315 VRB05KT 9999 SKC 530909 531809 QNH2991INS BECMG 1220/1222 21010G20KT 9999 FEW250 530909 531809 QNH2983INS WND 180V250 TEMPO 1223/1304 23015G25KT SCT250 BECMG 1304/1306 25009KT 9999 FEW250 500000 QNH2988INS WND 220V290 T17/1223Z T01/1313Z', + 'TAF KNIP 1215/1315 19008KT 9999 FEW030 SCT180 QNH3014INS FM121900 13013KT 9999 FEW045 SCT250 QNH3011INS FM130100 15006KT 9999 SCT250 QNH3008INS FM131300 16010KT 9999 FEW025 SCT250 QNH3003INS T28/1220Z T19/1312Z FN20019', + 'TAF COR KNJK 1215/1315 27015G25KT 9999 FEW070 520009 QNH2990INS TEMPO 1215/1218 31015KT BECMG 1217/1218 33012KT 9999 FEW070 520009 QNH2984INS BECMG 1300/1301 27015G25KT 9999 FEW070 530009 QNH2984INS TEMPO 1303/1305 27012KT FM130530 26009KT 9999 SKC 520009 QNH2994INS T23/1223Z T09/1313Z COR 1631 FS30130', + 'TAF KNKT 1215/1315 24012G24KT 9999 SCT120 522009 522907 QNH3009INS FM122300 20009KT 9999 FEW100 500000 QNH3011INS T26/1218Z T15/1309Z', + 'TAF KNKX 1215/1315 14005KT 9999 SCT050 520005 540503 520808 QNH2995INS TEMPO 1215/1221 28015G25KT FM130530 18010KT 9999 SKC 500000 QNH3005INS FM130930 VRB06KT 9999 SKC QNH3007INS T18/1220Z T07/1312Z', + 'TAF KNLC 1215/1315 32012KT 9999 SKC 530009 530907 QNH3013INS BECMG 1215/1216 33018G26KT 9999 SKC 530009 530907 QNH3003INS TEMPO 1216/1219 34025G35KT FM130000 34016KT 9999 SKC 500000 QNH3001INS TEMPO 1306/1312 34017G26KT FM131400 34010KT 9999 FEW140 QNH3016INS T18/1223Z T03/1314Z FS30067', + 'TAF KNMM 1215/1315 13008KT 9999 SCT008 BKN015 BKN035 QNH3005INS BECMG 1215/1217 17010G18KT 9999 SCT012 BKN030 BKN120 QNH2998INS BECMG 1221/1223 16012G20KT 9999 VCSH SCT010 BKN120 621309 QNH2988INS TEMPO 1223/1305 VRB12G25KT 4800 -TSRA BR SCT012 BKN025CB BKN100 FM130500 16010KT 8000 BR BKN008 OVC015 600000 QNH2987INS BECMG 1310/1312 19010G18KT 8000 VCTS -RA SCT008 BKN015CB BKN045 QNH2985INS AUTOMATED SENSOR METWATCH 1305 TIL 1311 T27/1221Z T16/1311Z FN20082', + 'TAF KNPA 1215/1315 14012KT 9999 SCT035 SCT120 BKN250 QNH3003INS TEMPO 1219/1223 14012G18KT FM130000 14008KT 9999 FEW035 BKN250 QNH2998INS FM130800 14008KT 9999 SCT020 BKN150 BKN250 QNH2996INS TEMPO 1308/1314 BKN020 T26/1212Z T19/1320Z FN20033', + 'TAF AMD KNQI 1216/1315 16018G28KT 9999 SCT025 BKN100 BKN250 621608 510005 QNH2968INS TEMPO 1216/1219 20015G24KT 9000 -SHRA BR BKN025 OVC100 FM122100 14018G26KT 9999 BKN030 BKN200 600000 510005 QNH2962INS TEMPO 1301/1306 SCT008 BKN015 OVC030 FM130600 15012KT 4800 BR BKN008 OVC015 WS015/17035KT 510103 QNH2972INS T32/1220Z T23/1311Z AMD 1627 FN20081', + 'TAF KNQX 1215/1315 11014G22KT 9999 SCT035 QNH3002INS T29/1219Z T26/1311Z FN20027', + 'TAF AMD KNSE 1216/1315 17016G25KT 9999 SCT035 BKN050 BKN250 QNH3003INS TEMPO 1216/1219 BKN035 FM122300 15010KT 9999 SCT025 BKN150 BKN250 QNH3004INS FM130500 15008KT 9999 BKN015 BKN150 QNH2999INS TEMPO 1307/1313 VRB03KT 6000 BR BKN003 OVC015 FM131300 15010KT 8000 BR BKN020 BKN250 QNH3002INS T27/1218Z T17/1311Z AMD 1631 FN20071', + 'TAF KNTD 1215/1315 VRB05KT 9999 FEW045 QNH3000INS FM121700 27009KT 9999 FEW050 QNH3003INS BECMG 1218/1220 29018G25KT 9999 SKC 510009 QNH3000INS TEMPO 1222/1304 31011G17KT FM130400 35008KT 9999 SKC 500000 QNH3004INS FM130700 VRB06KT 9999 SKC QNH3007INS T18/1221Z T06/1314Z FS30029', + 'TAF KNTU 1215/1315 25010KT 9999 SCT050 BKN110 OVC200 QNH3006INS FM122200 22006KT 9999 FEW080 SCT100 QNH3006INS FM130600 18006KT 9999 BKN060 OVC100 QNH3010INS T26/1219Z T15/1210Z FN20051', + 'TAF KNUW 1215/1315 15008G20KT 9999 VCSH FEW030 SCT060 BKN120 BKN240 630409 631305 QNH2988INS FM121800 16018G32KT 9999 VCSH FEW025 SCT055 BKN110 BKN240 630409 631305 QNH2992INS FM130300 15008G18KT 9999 VCSH FEW030 SCT060 BKN120 BKN240 630409 631305 QNH3000INS TEMPO 1304/1310 16008G15KT 9999 -RA FEW015 BKN025 OVC050 T04/1215Z T10/1223Z FS30028', + 'TAF KNXP 1215/1315 31021G30KT 9999 SKC 520009 520907 QNH2994INS TEMPO 1304/1308 27009KT T19/1223Z T07/1313Z', + 'TAF KNYG 1215/1315 15003KT 9999 SCT065 QNH3008INS TEMPO 1216/1220 VRB06G15KT BECMG 1312/1314 VRB06KT 9999 SKC QNH3010INS T23/1219Z T13/1311Z', + 'TAF COR KNZY 1215/1315 31008KT 9999 SCT035 BKN045 520109 QNH2992INS FM121700 31014KT 9999 FEW035 SCT045 520109 QNH2995INS TEMPO 1220/1302 31018G25KT FM130300 31011KT 9999 SCT025 500000 QNH2997INS FM130700 34004KT 9999 SCT020 QNH3000INS T17/1221Z T11/1314Z COR 1715 FS30139', + 'KOAK 121740Z 1218/1324 30013G19KT P6SM SKC FM121900 29018G25KT P6SM FEW040 FM130400 30014KT P6SM FEW030 FEW250 FM130800 28008KT P6SM FEW030 SCT250 FM131300 VRB04KT P6SM BKN250 FM131800 28014KT P6SM SCT120 BKN200', + 'KOKC 121720Z 1218/1318 19023G38KT P6SM SCT040 BKN250 FM130300 17021G32KT P6SM FEW250 WS020/19050KT FM130800 21019G30KT P6SM FEW180 FM131000 35018G30KT P6SM SCT050', + 'KOLS 121742Z 1218/1318 23018G29KT P6SM SCT090 FM121900 23018G31KT P6SM SCT090 FM130100 26018G29KT P6SM SKC FM130300 28011G19KT P6SM SKC FM130700 VRB05KT P6SM SKC', + 'KOMA 121720Z 1218/1318 16017G25KT P6SM OVC008 FM121900 18019G27KT P6SM SCT250 TEMPO 1300/1304 4SM +TSRA BR BKN010CB FM130400 25017G25KT P6SM BKN250 FM130500 31015G26KT P6SM BKN120', + 'KONT 121730Z 1218/1324 25005KT P6SM FEW025 FEW050 FM121900 26012G20KT P6SM SKC FM130400 VRB05KT P6SM SKC FM132100 25011G17KT P6SM SKC', + 'KOPF 121720Z 1218/1318 11015KT P6SM SCT035 SCT050 FM122300 10012KT P6SM SCT040 SCT250 FM131400 12012KT P6SM SCT035 BKN100', + 'KORD 121736Z 1218/1324 10012G22KT P6SM BKN150 FM121900 13012G20KT P6SM SCT040 BKN150 FM122200 16017G26KT P6SM SCT010 OVC025 FM130100 18019G28KT P6SM OVC010 WS015/20050KT FM130700 20018G27KT P6SM BKN025 OVC040 WS015/21050KT FM131200 19015G24KT P6SM OVC015 PROB30 1313/1317 6SM SHRA FM132100 20014G22KT 5SM SHRA BR BKN040 PROB30 1321/1324 2SM TSRA BR OVC015CB', + 'KORF 121720Z 1218/1318 25008KT P6SM SCT110 FM122300 20005KT P6SM BKN150 FM131100 22012KT P6SM SCT150', + 'KPAE 121734Z 1218/1318 16015G25KT P6SM VCSH SCT020 OVC060 FM130300 18007KT P6SM OVC040 FM130900 13003KT P6SM OVC070', + 'TAF AMD KPAM 121430Z 1214/1317 17012G18KT 9999 SCT030 BKN150 QNH3011INS TEMPO 1214/1218 BKN030 BECMG 1220/1221 17009KT 9999 FEW030 SCT150 QNH3008INS BECMG 1310/1311 13006KT 6000 BR BKN020 QNH3004INS TEMPO 1311/1314 0800 FG BKN005 BECMG 1313/1314 16009KT 9999 NSW BKN030 OVC150 QNH3006INS TX24/1219Z TN16/1311Z', + 'KPBF 121736Z 1218/1318 16012KT P6SM OVC050 FM130000 16012G22KT 6SM -SHRA BR BKN030 FM130600 18012G21KT 5SM BR OVC015 PROB30 1306/1312 2SM TSRA BR OVC001CB FM131700 20016G28KT 6SM -SHRA BR VCTS BKN015CB', + 'KPBG 121722Z 1218/1318 29011G22KT P6SM FEW050 FM122300 31008KT P6SM SKC FM131500 15007KT P6SM BKN070', + 'KPBI 121720Z 1218/1318 11015G23KT P6SM SCT035 SCT050 FM122300 11012KT P6SM SCT040 SCT250 FM131400 12012G22KT P6SM SCT035 BKN100', + 'KPDX 121824Z 1218/1318 22010G19KT P6SM -SHRA BKN035 BKN050 TEMPO 1218/1221 -SHRA SCT008 BKN011 BKN025 FM130300 19007KT P6SM -SHRA BKN030 BKN050 FM131000 18004KT P6SM -SHRASN BKN025 OVC040 FM131200 VRB03KT 4SM -SHSNRA BR BKN017 BKN025', + 'KPHF 121720Z 1218/1318 26010KT P6SM SCT120 FM130100 17003KT P6SM BKN100 FM131300 23012KT P6SM SCT200', + 'KPHL 121725Z 1218/1324 32010G18KT P6SM FEW050 FM122300 VRB03KT P6SM FEW250 FM130500 VRB02KT P6SM BKN080 FM130800 VRB02KT P6SM BKN050 FM131400 16006KT P6SM BKN060 FM131700 20009KT P6SM FEW050 BKN120', + 'KPHX 121720Z 1218/1324 27017G26KT P6SM SKC FM122200 27022G31KT P6SM SKC FM130300 28015KT P6SM SKC FM130700 28007KT P6SM SKC FM131000 09006KT P6SM SKC FM131800 24005KT P6SM SKC FM132100 28008KT P6SM SKC', + 'KPIE 121720Z 1218/1318 13011G20KT P6SM FEW250 FM122000 21010KT P6SM FEW060 FEW250 FM122200 30009KT P6SM FEW250 FM130300 11008KT P6SM SCT250 FM131400 15011G20KT P6SM SCT250', + 'KPIT 121720Z 1218/1324 16005KT P6SM SCT040 FM130900 18006KT P6SM SCT100 WS020/23040KT FM131600 21011G20KT P6SM BKN090 FM132000 20012G26KT 5SM -SHRA OVC035', + 'KPMD 121744Z 1218/1318 28010KT P6SM SKC FM130000 28020G30KT P6SM SKC FM130800 26012G25KT P6SM SKC', + 'KPNC 121720Z 1218/1318 18022G35KT P6SM BKN200 FM130400 19021G33KT P6SM FEW250 WS020/20055KT PROB30 1306/1308 4SM +TSRA BR BKN250CB FM130800 36017G27KT P6SM SCT070 WS020/27035KT PROB30 1308/1312 4SM TSRA BR BKN070CB', + 'KPNE 121725Z 1218/1318 32011KT P6SM FEW050 FM130000 VRB03KT P6SM FEW200 FM130600 VRB03KT P6SM BKN070 FM131400 17006KT P6SM BKN060', + 'KPNS 121745Z 1218/1318 13014G20KT P6SM SCT030 BKN040 FM130600 15012KT P6SM OVC014 FM131600 15016G25KT P6SM BKN030', + 'TAF AMD KPOE 121655Z 1216/1316 17012G18KT 9999 OVC015 510073 QNH2987INS BECMG 1219/1220 17010G20KT 9000 -SHRA BKN015 QNH2985INS TEMPO 1220/1224 18015G35KT 9000 -TSRA BKN012CB OVC040 BECMG 1306/1307 17012KT 9999 NSW OVC020 510052 QNH2975INS BECMG 1315/1316 18025G35KT 9999 BKN020 510063 QNH2975INS TX24/1219Z TN19/1312Z', + 'KPQI 121740Z 1218/1318 24004KT P6SM VCSH OVC011 FM122000 29008KT P6SM BKN025 FM122200 30008KT P6SM BKN050 FM122300 30007KT P6SM SCT060 FM130200 29004KT P6SM SKC WS020/32035KT FM130500 29006KT P6SM SCT250 FM131700 27004KT P6SM BKN120', + 'KPRC 121720Z 1218/1318 29014G21KT P6SM SCT050 FM130300 30012G19KT P6SM SKC FM130600 29007KT P6SM SKC FM131700 24008G16KT P6SM SKC', + 'KPSP 121730Z 1218/1318 33015G25KT P6SM SKC FM130000 33012G20KT P6SM SKC FM130700 33005KT P6SM SKC', + 'KPUB 121825Z 1218/1318 31025G40KT P6SM FEW100 BKN200 FM130200 31020G30KT P6SM FEW100 BKN200 FM130500 32015G25KT P6SM BKN200 FM130800 27012KT P6SM BKN180', + 'KPVD 121720Z 1218/1318 31008KT P6SM FEW007 SCT080 BKN100 FM130400 30004KT P6SM SCT250 FM130700 00000KT P6SM FEW250 FM131400 17008KT P6SM BKN140', + 'KPWM 121720Z 1218/1318 30008KT P6SM FEW090 FM130200 27004KT P6SM FEW250', + 'TAF AMD KRCA 121740Z 1217/1317 36012G18KT 9999 OVC005 610155 QNH2933INS BECMG 1220/1221 32030G40KT 9999 NSW BKN015 620155 QNH2950INS BECMG 1305/1306 32015G25KT 9999 BKN020 QNH2968INS BECMG 1314/1315 32025G40KT 9999 BKN080 QNH2970INS TX04/1220Z TNM09/1311Z', + 'TAF KRDR 121800Z 1218/1324 04015G25KT 4800 -SN OVC020 610207 620909 QNH2968INS TEMPO 1218/1220 04015G30KT 3200 -RASN OVC010 640109 651008 TEMPO 1220/1301 04020G35KT 1600 -SN OVC010 640109 651008 510005 BECMG 1300/1301 03015G25KT 3200 -SN OVC007 610079 621008 QNH2949INS TEMPO 1301/1304 04020G35KT 0800 SN BLSN OVC004 640049 650959 510005 BECMG 1315/1316 08012G20KT 4800 -RASN BLSN OVC006 610069 621005 QNH2935INS BECMG 1317/1318 19015G25KT 3200 -SN BLSN OVC010 610106 QNH2936INS TX02/1316Z TNM03/1310Z', + 'KRDU 121740Z 1218/1318 23009KT P6SM SCT060 BKN110 FM130000 20005KT P6SM SCT070 SCT150 FM131500 22012G18KT P6SM SCT040 BKN150', + 'KRIC 121720Z 1218/1318 26009KT P6SM SCT120 FM130100 17003KT P6SM BKN100 FM131000 21008KT P6SM SKC FM131500 22013G21KT P6SM SCT250', + 'TAF KRIV 121212Z 1212/1318 33009KT 9999 VCSH BKN015 520009 QNH2988INS BECMG 1213/1214 33010G15KT 9999 NSW SCT015 520009 QNH2989INS TEMPO 1214/1219 34015G25KT BECMG 1219/1220 33009KT 9999 SKC 510006 QNH2993INS TEMPO 1222/1302 32012G20KT BECMG 1304/1305 16006KT 9999 SKC QNH3003INS TX19/1222Z TN00/1312Z', + 'KRME 121734Z 1218/1318 28013G19KT P6SM FEW040 FM130200 00000KT P6SM SCT250 FM131100 11008G13KT P6SM BKN040 WS020/19030KT FM131600 10008G13KT P6SM BKN035', + 'TAF KRND 121611Z 1216/1322 16012G25KT 9999 SCT025 SCT110 SCT200 QNH2966INS TEMPO 1219/1300 9000 -SHRA VCTS BKN030CB BKN150 BECMG 1306/1307 15009KT 8000 -SHRA BR SCT009 BKN018 QNH2962INS BECMG 1311/1312 18006KT 8000 -DZ BR BKN007 OVC011 QNH2968INS BECMG 1315/1316 20009KT 8000 BR SCT009 OVC016 QNH2977INS BECMG 1316/1317 26010G15KT 9999 NSW SCT016 QNH2980INS BECMG 1317/1318 32012G22KT 9999 SKC QNH2973INS TX33/1220Z TN21/1312Z LAST NO AMDS AFT 1300 NEXT 1310', + 'KRNO 121739Z 1218/1318 30007KT P6SM SCT050 FM122100 28012G22KT P6SM SCT060 FM130900 27008KT P6SM SCT100 BKN180', + 'KROC 121737Z 1218/1318 33008KT P6SM FEW250 FM121900 29007KT P6SM FEW250 FM122300 24005KT P6SM FEW250 FM131000 16011G19KT P6SM BKN250 FM131200 18014G26KT P6SM BKN250', + 'KROW 121735Z 1218/1318 21020G30KT P6SM FEW200 FM121900 23028G40KT P6SM FEW100 TEMPO 1221/1301 23034G46KT 3SM BLDU FM130200 25018G30KT P6SM SKC FM130800 29014G24KT P6SM SKC FM131600 31020G32KT P6SM SKC', + 'KRSW 121720Z 1218/1318 13013G20KT P6SM SCT050 FM130100 10007KT P6SM SCT250 FM131400 14013G20KT P6SM SCT040 SCT250', + 'KSAC 121739Z 1218/1318 33005KT P6SM SKC FM130000 25007KT P6SM SCT070 FM130900 17003KT P6SM OVC200', + 'KSAF 121735Z 1218/1318 24025G35KT P6SM BKN070 TEMPO 1218/1221 24030G42KT 4SM BLDU FM122200 28024G34KT 6SM BLDU BKN070 FM130200 31011KT P6SM SCT070 FM131600 27015G25KT P6SM FEW180', + 'KSAN 121730Z 1218/1324 28011KT P6SM SCT035 BKN050 FM122100 29013G23KT P6SM SCT040 FM130200 30011KT P6SM SKC FM130500 VRB04KT P6SM SKC FM131900 29011KT P6SM SKC', + 'KSAT 121720Z 1218/1324 15013G23KT P6SM FEW050 FEW250 TEMPO 1221/1224 4SM TSRA BR OVC025CB FM130600 15012KT P6SM SCT009 OVC015 FM131500 28010G20KT P6SM FEW015', + 'KSAV 121739Z 1218/1318 16005KT P6SM FEW150 FM121900 16012G18KT P6SM FEW150 FM130300 17007KT P6SM FEW250 FM131530 18011G17KT P6SM FEW250', + 'KSAW 121720Z 1218/1318 VRB04KT P6SM BKN250 FM122200 12007KT P6SM VCSH OVC120 FM130200 08009G19KT 4SM -SHRA BR OVC045 WS020/12035KT FM130400 09011G20KT 3SM -SHRA BR OVC018 WS020/13040KT FM130600 09012G20KT 3SM SHRA BR OVC005 WS020/14045KT FM131200 09006KT 1 1/2SM -SHRA BR OVC002', + 'KSBY 121728Z 1218/1318 27013G21KT P6SM SCT120 FM122300 10003KT P6SM SKC FM130500 13003KT P6SM BKN090 FM131300 22008KT P6SM SCT200', + 'KSCK 121739Z 1218/1318 30011G17KT P6SM SKC FM130000 27014G22KT P6SM FEW070 FM130500 27008KT P6SM SCT250 FM131200 VRB05KT P6SM OVC200', + 'KSEA 121734Z 1218/1324 18012G22KT P6SM VCSH BKN060 BKN250 FM130400 18007KT P6SM BKN050 FM131000 11005KT P6SM BKN070', + 'KSFF 121744Z 1218/1318 06012G20KT P6SM BKN050 FM130300 06009KT P6SM BKN060 FM131500 07012G20KT P6SM SCT060', + 'KSFO 121740Z 1218/1324 30013KT P6SM FEW035 FM122200 30020G30KT P6SM FEW040 FM130500 28012G20KT P6SM FEW015 FEW250 FM130900 28010KT P6SM FEW015 SCT250 FM131500 30006KT P6SM FEW015 BKN250 FM131900 28015KT P6SM SCT120 BKN200', + 'KSHV 121741Z 1218/1318 17015G22KT P6SM VCTS SCT025 BKN035 OVC050CB TEMPO 1218/1222 4SM -TSRA BR OVC025CB FM122200 16014G20KT P6SM VCTS OVC035CB FM130300 17014KT 4SM TSRA BR OVC025CB FM130600 18015KT P6SM VCTS OVC015CB', + 'KSJC 121740Z 1218/1318 34013G21KT P6SM FEW040 FM130600 31010KT P6SM FEW250 FM131100 VRB04KT P6SM SCT250 FM131700 30009KT P6SM SCT200', + 'KSJT 121740Z 1218/1318 22015G23KT P6SM SCT250 FM130100 22008KT P6SM SKC FM131100 26014G20KT P6SM SKC', + 'TAF AMD KSKA 121451Z 1214/1317 04012G18KT 9999 BKN035 OVC045 620355 QNH2974INS TEMPO 1214/1216 9000 -SN BKN015 OVC030 620153 BECMG 1216/1217 05012G22KT 9999 BKN060 620606 QNH2975INS BECMG 1223/1224 06015G25KT 9999 BKN060 620604 QNH2991INS BECMG 1303/1304 06009KT 9999 BKN060 620604 QNH2992INS BECMG 1315/1316 03015G25KT 9999 SCT070 QNH2995INS TX04/1221Z TNM02/1214Z', + 'TAF KSKF 121800Z 1218/1324 15015G25KT 9999 FEW020 SCT060 QNH2968INS BECMG 1220/1221 17015G25KT 9999 VCTS BKN020CB BKN060 QNH2963INS TEMPO 1221/1224 VRB20G30KT 4800 -TSRA OVC020CB BECMG 1301/1302 17012KT 9999 NSW FEW020 SCT065 QNH2960INS BECMG 1302/1303 15009KT 4800 BR BKN002 OVC010 QNH2965INS BECMG 1314/1315 30012KT 9999 NSW BKN015 BKN030 QNH2971INS BECMG 1317/1318 31014KT 9999 SKC QNH2970INS TX34/1221Z TN21/1308Z', + 'KSLC 121749Z 1218/1324 26012G25KT P6SM VCSH SCT060 BKN090 TEMPO 1220/1224 5SM -SN BR BKN025 FM130100 30009KT P6SM SCT070 BKN100 FM130300 14006KT P6SM BKN060 OVC090 FM131800 27010G22KT P6SM BKN070', + 'KSMF 121739Z 1218/1318 32007KT P6SM SKC FM130000 21009KT P6SM SCT070 FM130900 17006KT P6SM OVC200', + 'KSNA 121730Z 1218/1318 34006KT P6SM FEW030 FM121900 32011G18KT P6SM FEW030 FM130100 30007KT P6SM SKC FM130400 VRB04KT P6SM SKC', + 'KSPS 121720Z 1218/1318 19019G30KT P6SM FEW040 BKN250 FM130600 18019G29KT P6SM FEW250 WS020/20045KT FM131200 35015G26KT P6SM SCT200', + 'TAF KSSC 121500Z 1215/1321 22010G16KT 9999 FEW100 SCT250 QNH3010INS BECMG 1221/1222 19007KT 9999 FEW150 BKN220 QNH3011INS BECMG 1315/1316 19012G20KT 9999 BKN045 BKN200 QNH3004INS TX29/1220Z TN15/1311Z', + 'KSTL 121748Z 1218/1324 15013G21KT P6SM BKN005 FM122000 17012G24KT P6SM BKN015 FM130800 18015G23KT P6SM BKN025 FM131300 19009KT P6SM VCTS BKN015CB FM131700 21010G23KT P6SM SHRA VCTS OVC020CB', + 'KSUX 121739Z 1218/1318 15019G30KT P6SM OVC008 FM122000 16018G29KT P6SM SCT015 BKN200 FM130000 22016G25KT 6SM SHRA VCTS SCT022CB BKN150 TEMPO 1300/1302 VRB25G45KT 2SM TSRA OVC020CB FM130200 29023G34KT P6SM SCT025 BKN100', + 'TAF AMD KSVN 121720Z 1217/1318 17012KT 9999 FEW060 SCT150 QNH3016INS BECMG 1220/1221 18009KT 9999 SKC QNH3016INS BECMG 1302/1303 16006KT 9999 SKC 510033 QNH3015INS BECMG 1313/1314 18010G20KT 9999 FEW030 SCT200 QNH3017INS TX28/1218Z TN16/1312Z', + 'KSWF 121720Z 1218/1324 32009KT P6SM SCT060 FM121900 32012G19KT P6SM SCT060 FM122200 31009KT P6SM FEW060 FM130100 VRB03KT P6SM FEW050 FM131400 17007KT P6SM BKN050 FM131800 20009KT P6SM FEW025 BKN050 AMD NOT SKED', + 'KSYR 121734Z 1218/1318 28012G21KT P6SM FEW250 FM122300 26005KT P6SM SCT250 FM131100 12005KT P6SM BKN060 WS020/20030KT FM131500 16006G13KT P6SM BKN050', + 'TAF KSZL 121100Z 1211/1317 12012KT 9999 OVC007 QNH2966INS TEMPO 1211/1215 6000 BR OVC005 BECMG 1214/1215 19015G30KT 9999 BKN018 520003 QNH2960INS BECMG 1217/1218 20025G40KT 9999 BKN025 530003 QNH2949INS BECMG 1305/1306 22015G25KT 8000 RA OVC020 QNH2942INS BECMG 1308/1309 22015G25KT 4800 TSRA OVC018CB QNH2943INS TX27/1220Z TN11/1211Z', + 'KTCC 121735Z 1218/1318 23035G48KT P6SM SKC TEMPO 1221/1301 4SM BLDU FEW120 FM130100 25022G34KT P6SM FEW120 FM130300 28012G22KT P6SM SKC FM130700 30010KT P6SM SKC FM131600 29025G35KT P6SM FEW100', + 'TAF AMD KTCM 121501Z 1215/1319 20012KT 9999 BKN020 OVC065 620205 QNH2988INS TEMPO 1215/1218 BKN008 OVC030 BECMG 1217/1218 21012G20KT 8000 -SHRA SCT020 BKN040 610408 QNH2996INS BECMG 1302/1303 19008KT 9999 NSW SCT030 BKN050 QNH2994INS TX10/1223Z TN03/1215Z', + 'KTCS 121740Z 1218/1318 22025G35KT P6SM SKC FM122000 24030G45KT 6SM HZ FEW250 FM130600 27015G25KT P6SM SKC FM131500 26011KT P6SM SKC', + 'KTEB 121720Z 1218/1324 32011G17KT P6SM FEW060 FEW250 FM122200 31009KT P6SM FEW060 FM130200 VRB04KT P6SM FEW050 FM131500 15007KT P6SM BKN050 FM131900 18010KT P6SM FEW025 BKN050', + 'TAF AMD KTIK 121749Z 1217/1323 18020G30KT 9999 SCT035 510022 QNH2947INS TEMPO 1218/1224 19025G35KT BECMG 1302/1303 16015KT 9999 FEW015 SCT025 QNH2946INS BECMG 1308/1309 24018KT 9000 -RA BKN015 OVC025 QNH2945INS TEMPO 1310/1314 35020G30KT 8000 -SHRA OVC005 510013 BECMG 1314/1315 01012G18KT 9000 BR BKN009 OVC015 510013 QNH2957INS BECMG 1321/1322 36010G15KT 9999 NSW FEW020 SCT070 BKN100 QNH2974INS TX27/1222Z TN16/1314Z', + 'KTLH 121745Z 1218/1318 16009KT P6SM SCT025 BKN035 FM122200 17009KT P6SM SKC FM130800 11003KT 5SM BR SCT018 FM131300 12004KT P6SM FEW020 FM131500 14010KT P6SM OVC250', + 'KTMB 121720Z 1218/1318 11015KT P6SM SCT035 SCT050 FM122300 10011KT P6SM SCT040 SCT250 FM131400 12012KT P6SM SCT035 BKN100', + 'KTPA 121720Z 1218/1324 15010G18KT P6SM FEW250 FM122000 24009KT P6SM FEW060 FEW250 FM130000 33007KT P6SM FEW060 SCT250 FM130200 10005KT P6SM SCT250 FM131500 14010KT P6SM FEW030 SCT250 FM132000 22008KT P6SM SCT040 SCT250', + 'KTTN 121725Z 1218/1318 32009KT P6SM FEW050 FM130000 VRB03KT P6SM FEW250 FM130700 VRB03KT P6SM BKN060 FM131600 16006KT P6SM BKN050', + 'KTUL 121727Z 1218/1318 17022G34KT P6SM SCT040 SCT250 FM131000 18015G25KT P6SM VCTS BKN025CB TEMPO 1311/1313 3SM TSRA OVC025CB FM131300 35015KT P6SM OVC040 TEMPO 1313/1315 3SM TSRA BKN025CB', + 'KTUS 121742Z 1218/1318 27015G25KT P6SM SCT100 FM122100 28020G33KT P6SM SCT100 FM130400 29013G23KT P6SM SKC FM130500 28009KT P6SM SKC FM131000 17005KT P6SM SKC', + 'KTXK 121741Z 1218/1318 18013G20KT P6SM VCTS BKN025 OVC035CB FM122200 16014G20KT P6SM VCTS OVC035CB FM130300 17014KT 4SM TSRA BR OVC025CB FM130600 18015KT P6SM VCTS OVC015CB', + 'KTYR 121741Z 1218/1318 17014G22KT P6SM VCTS OVC020CB FM122000 18014G22KT P6SM VCTS OVC035CB FM130600 17013KT P6SM VCTS OVC015CB FM130900 18014G21KT P6SM VCTS BKN015CB FM131200 19014G22KT 6SM -SHRA VCTS OVC015CB FM131500 21015G23KT 5SM -TSRA OVC015CB', + 'KTYS 121720Z 1218/1318 20006KT P6SM -SHRA OVC025 FM121900 23009KT P6SM VCSH BKN035 TEMPO 1219/1220 BKN025 FM122200 23008KT P6SM SCT040 FM130000 21006KT P6SM BKN250 FM131100 18006KT P6SM VCSH SCT040 BKN100 FM131600 22015G25KT P6SM SCT040 BKN140', + 'TAF KVAD 121600Z 1216/1322 18010G15KT 9999 SKC QNH3012INS BECMG 1221/1222 18006KT 9999 SKC QNH3012INS BECMG 1305/1306 14006KT 9999 SCT180 QNH3011INS BECMG 1313/1314 16010G15KT 9999 SCT180 QNH3003INS TX29/1220Z TN17/1306Z', + 'TAF KVBG 121400Z 1214/1320 01009KT 9999 SKC QNH3011INS BECMG 1217/1218 34015G25KT 9999 SKC 520006 QNH3012INS BECMG 1222/1223 34022G32KT 9999 SKC 520006 QNH3006INS TEMPO 1223/1304 34028G38KT 530008 BECMG 1303/1304 36015KT 9999 FEW005 FEW250 QNH3000INS BECMG 1318/1319 34015G25KT 9999 FEW250 520006 QNH3004INS TX13/1220Z TN03/1314Z', + 'TAF KVPS 121700Z 1217/1323 13015KT 9999 BKN014 BKN045 BKN120 QNH3014INS TEMPO 1217/1219 SCT014 BKN045 BECMG 1218/1219 14013G18KT 9999 SCT015 BKN045 QNH3007INS WND 15014KT AFT 1222 BECMG 1302/1303 14012KT 8000 BR SCT005 BKN018 BKN180 QNH3004INS BECMG 1307/1308 13007KT 4800 BR SCT005 BKN012 BKN180 QNH3002INS BECMG 1311/1312 14013KT 6000 BR BKN005 OVC045 QNH3005INS BECMG 1313/1314 12012KT 9999 NSW FEW005 BKN035 QNH3004INS BECMG 1314/1315 13015KT 9999 BKN026 BKN035 QNH3000INS BECMG 1317/1318 14018G28KT 9999 SCT030 BKN200 QNH2996INS TX26/1218Z TN18/1311Z', + 'KVRB 121725Z 1218/1318 12014G22KT P6SM SCT040 FM130100 13009KT P6SM SCT035 FM131500 14013KT P6SM SCT035 SCT250', + 'TAF KWRB 121600Z 1216/1322 20010G15KT 9999 FEW150 SCT200 QNH3018INS BECMG 1303/1304 VRB06KT 9999 FEW200 QNH3006INS BECMG 1314/1315 17010G20KT 9999 SCT100 BKN180 QNH2998INS TX27/1220Z TN14/1308Z', + 'TAF KWRI 121800Z 1218/1324 30010G18KT 9999 FEW060 QNH3000INS BECMG 1222/1223 27006KT 9999 FEW250 QNH3007INS BECMG 1304/1305 VRB03KT 9999 BKN050 QNH3005INS BECMG 1313/1314 15010G15KT 9999 BKN060 QNH2987INS BECMG 1321/1322 20010KT 9999 FEW060 SCT120 QNH2986INS TX24/1318Z TN11/1310Z', + 'KYIP 121721Z 1218/1318 13009KT P6SM SKC FM122200 13010KT P6SM FEW050 SCT150 FM130000 13011KT P6SM BKN045 FM130300 15012KT P6SM SCT018 BKN025 WS020/18040KT FM130600 17010KT P6SM OVC010 WS020/20040KT FM131200 20014KT P6SM OVC017', + 'KYNG 121745Z 1218/1318 19004KT P6SM FEW250 FM130400 16008KT P6SM BKN050 FM131100 18010G18KT P6SM BKN010 FM131700 20015G27KT P6SM -SHRA OVC080', + 'TAF LATI 121700Z 1218/1318 VRB05KT CAVOK TX22/1312Z TN04/1303Z', + 'TAF LBBG 121700Z 1218/1318 06008KT 9999 SCT045 BECMG 1218/1220 27008KT BECMG 1307/1309 12012KT', + 'TAF LBGO 121700Z 1218/1318 30012KT 9999 SCT050 BECMG 1218/1220 VRB04KT BECMG 1308/1310 30010KT', + 'TAF LBPD 121700Z 1218/1318 28012KT 9999 SCT060 BECMG 1218/1220 VRB04KT BECMG 1307/1309 30010KT', + 'TAF LBSF 121700Z 1218/1318 30012KT CAVOK BECMG 1218/1220 VRB04KT TEMPO 1303/1306 5000 BR SCT010', + 'TAF LBWN 121700Z 1218/1318 29008KT 9999 SCT045 BECMG 1306/1308 35014KT', + 'TAF LCLK 121730Z 1218/1318 30009KT 9999 FEW025 BECMG 1307/1311 06013KT', + 'TAF LCPH 121730Z 1218/1318 30010KT 9999 FEW025 BECMG 1219/1221 36008KT BECMG 1306/1309 16013KT', + 'TAF LCRA 121615Z 1218/1312 05005KT 9999 FEW040 PROB40 1218/1301 31005KT', + 'TAF LDDU 121725Z 1218/1318 29005KT CAVOK TX17/1312Z TN08/1303Z BECMG 1220/1222 02008KT BECMG 1310/1312 26008KT PROB40 TEMPO 1312/1317 29010KT', + 'TAF LDOS 121725Z 1218/1318 11006KT CAVOK TX18/1314Z TN04/1305Z PROB40 TEMPO 1220/1304 11010KT', + 'TAF LDPL 121725Z 1218/1318 10005KT CAVOK TX18/1313Z TN05/1304Z BECMG 1310/1312 28008KT BECMG 1316/1318 30005KT', + 'TAF LDRI 121725Z 1218/1318 VRB02KT CAVOK TX19/1313Z TN07/1304Z PROB30 TEMPO 1310/1316 30007KT', + 'TAF LDSP 121725Z 1218/1318 33005KT CAVOK TX19/1312Z TN07/1303Z BECMG 1310/1312 23009KT BECMG 1316/1318 VRB02KT', + 'TAF LDZA 121725Z 1218/1318 11005KT CAVOK TX21/1314Z TN03/1305Z', + 'TAF LDZD 121725Z 1218/1318 VRB02KT CAVOK TX20/1313Z TN02/1303Z PROB30 TEMPO 1302/1306 4000 BR MIFG BECMG 1309/1311 28008KT BECMG 1316/1318 30005KT', + 'TAF LEAB 121700Z 1218/1318 VRB05KT 9999 FEW040 TX17/1316Z TN07/1306Z TEMPO 1218/1301 RA PROB40 TEMPO 1218/1301 4000 SHRA SCT030TCU PROB30 TEMPO 1218/1301 VRB15G25KT TSRA SCT035CB PROB40 TEMPO 1300/1308 BKN012', + 'TAF LEAL 121700Z 1218/1318 VRB04KT 9999 SCT030 TX18/1312Z TN10/1306Z TEMPO 1218/1224 4000 RA SHRA FEW020TCU PROB30 TEMPO 1218/1223 3000 TSRA FEW020CB TEMPO 1312/1318 3000 RA SHRA FEW020TCU', + 'TAF LEAM 121700Z 1218/1318 20012KT 9999 FEW015 TX19/1315Z TN12/1306Z PROB30 TEMPO 1218/1221 22015G25KT BECMG 1221/1223 34005KT BECMG 1310/1312 20010KT PROB30 TEMPO 1311/1318 4000 SHRA FEW030CB', + 'TAF LEAS 121400Z 1215/1315 20006KT 9999 FEW030 SCT045 TX16/1312Z TN12/1306Z PROB30 TEMPO 1215/1217 05010KT PROB40 TEMPO 1215/1221 36010KT 3000 RA SHRA SCT020TCU PROB30 TEMPO 1215/1221 VRB10KT TS SCT020CB TEMPO 1311/1315 31010KT BKN010', + 'TAF LEBA 121400Z 1215/1315 23004KT 9999 FEW030 TX19/1215Z TN08/1306Z PROB40 TEMPO 1215/1220 4000 SHRA FEW030CB PROB30 TEMPO 1215/1220 2000 TSRA SCT020CB TEMPO 1302/1311 2000 BR BKN006 PROB40 TEMPO 1303/1310 0300 FG VV001', + 'TAF LEBB 121700Z 1218/1318 VRB05KT 9999 FEW050 TX17/1314Z TN11/1306Z PROB30 TEMPO 1218/1220 36010KT BECMG 1306/1308 31010KT PROB40 TEMPO 1218/1224 3000 RA SHRA SCT020TCU PROB30 TEMPO 1218/1221 TS SCT020CB PROB30 TEMPO 1308/1312 3000 RA BKN010 FEW025TCU', + 'TAF LEBG 121400Z 1215/1315 VRB05KT 9999 FEW010 BKN035 TX16/1315Z TN03/1306Z PROB40 TEMPO 1215/1220 19010KT RA DZRA PROB40 TEMPO 1216/1220 4000 SHRA BKN012 SCT020TCU PROB30 TEMPO 1216/1220 VRB15G25KT 2000 TSRA BKN018CB PROB40 TEMPO 1222/1315 2500 BR BCFG BKN006 PROB30 TEMPO 1303/1310 0800 FG VV002', + 'TAF LEBL 121700Z 1218/1318 06015KT 9999 BKN012 TX15/1312Z TN13/1306Z PROB40 TEMPO 1218/1224 BKN006 TEMPO 1223/1312 5000 RA PROB40 TEMPO 1308/1313 BKN006', + 'TAF LEBZ 121400Z 1215/1315 25008KT 9999 FEW012 SCT030 TX18/1215Z TN07/1306Z PROB30 TEMPO 1215/1218 4000 SHRA FEW030CB PROB40 TEMPO 1305/1310 2000 BR BKN006 PROB30 TEMPO 1305/1309 0500 FG VV001', + 'TAF LECO 121400Z 1215/1315 22009KT 9999 SCT020 BKN045 TX17/1313Z TN11/1302Z PROB40 TEMPO 1215/1303 20015G25KT PROB30 TEMPO 1215/1224 3000 RA SHRA FEW030TCU PROB40 TEMPO 1300/1309 3000 RA SHRA SCT030TCU PROB30 TEMPO 1300/1307 3000 -RA BCFG BKN010 BECMG 1305/1308 30010KT', + 'TAF LEGE 121700Z 1218/1318 01009KT 9999 BKN016 TX19/1313Z TN12/1306Z PROB40 TEMPO 1218/1304 4000 RA BKN010 PROB30 TEMPO 1222/1306 0400 BCFG VV002 TEMPO 1304/1318 3000 RA BR BKN008', + 'TAF LEGR 121700Z 1218/1318 VRB02KT 9999 SCT040 TX19/1315Z TN06/1306Z PROB30 TEMPO 1218/1221 29010KT 4000 SHRA FEW030CB PROB30 TEMPO 1302/1310 4000 RA BR BKN006', + 'TAF LEGT 121700Z 1218/1318 VRB05KT 9999 SCT025 BKN045 TX20/1316Z TN06/1306Z TEMPO 1218/1222 23012KT RA PROB40 TEMPO 1218/1221 4000 SHRA BKN014 SCT030TCU PROB40 TEMPO 1218/1220 VRB15G25KT 3000 TSRA SCT030CB PROB40 TEMPO 1300/1308 4000 BR', + 'TAF LEHC 121400Z 1215/1315 12010KT 9999 BKN020 TX15/1314Z TN09/1306Z TEMPO 1215/1224 4000 RA SHRA BKN014 FEW035TCU PROB30 TEMPO 1218/1224 3000 TSRA FEW030CB TEMPO 1300/1312 4000 RA BKN014', + 'TAF LEIB 121400Z 1215/1315 VRB04KT 9999 FEW020 TX17/1312Z TN12/1306Z TEMPO 1215/1218 27010KT TEMPO 1217/1306 4000 RA SHRA FEW020TCU PROB40 TEMPO 1218/1224 3000 TSRA FEW020CB', + 'TAF LEJR 121400Z 1215/1315 23008KT 9999 FEW030 TX18/1215Z TN10/1306Z PROB30 TEMPO 1215/1218 4000 SHRA FEW030CB PROB40 TEMPO 1222/1309 4000 BR BKN008 PROB30 TEMPO 1305/1309 0300 FG VV001', + 'TAF LELC 121700Z 1218/1318 20010KT 9999 FEW010 SCT030 TX19/1313Z TN11/1306Z BECMG 1222/1224 35006KT PROB40 TEMPO 1300/1309 4000 RA BR BKN010 PROB30 TEMPO 1311/1318 4000 SHRA FEW030CB', + 'TAF LELL 121400Z 1215/1315 06010KT 9999 BKN025 TX19/1313Z TN12/1306Z PROB40 TEMPO 1215/1222 4000 DZ BKN014 TEMPO 1222/1312 4000 RA BKN014 BECMG 1218/1221 VRB04KT BECMG 1305/1307 06012KT', + 'TAF LELN 121400Z 1215/1315 VRB05KT 9999 BKN020 TX16/1315Z TN02/1306Z PROB40 TEMPO 1215/1221 18010KT RA PROB40 TEMPO 1215/1220 4000 SHRA BKN012 SCT020TCU PROB40 TEMPO 1215/1219 VRB15G30KT 3000 TSRA SCT035CB PROB40 TEMPO 1303/1308 1500 BR BKN010 PROB30 TEMPO 1304/1307 0800 FG VV002', + 'TAF LEMD 121700Z 1218/1324 VRB05KT 9999 SCT020 BKN040 TX20/1316Z TN05/1306Z TEMPO 1218/1221 23012KT RA PROB40 TEMPO 1218/1221 4000 SHRA BKN014 SCT030TCU PROB40 TEMPO 1218/1220 VRB15G25KT 3000 TSRA SCT030CB PROB30 TEMPO 1300/1308 4000 BR BECMG 1310/1312 03010KT', + 'TAF LEMG 121700Z 1218/1318 29008KT 9999 FEW020 SCT040 TX20/1312Z TN12/1306Z', + 'TAF AMD LEMH 121725Z 1217/1315 VRB04KT 9999 BKN010 TX17/1312Z TN11/1306Z TEMPO 1222/1306 4000 BR TEMPO 1301/1306 31010KT BECMG 1307/1310 07015G25KT', + 'TAF LEPA 121700Z 1218/1318 VRB04KT 7000 FEW025 TX17/1312Z TN13/1306Z TEMPO 1218/1224 30007KT TEMPO 1222/1318 4000 SHRA FEW030TCU BECMG 1306/1308 06009KT', + 'TAF COR LEPP 121553Z 1215/1315 12009KT 9999 FEW020 SCT040 TX15/1312Z TN07/1304Z PROB40 TEMPO 1215/1218 18010KT PROB30 TEMPO 1215/1224 3000 SHRA TS SCT020TCU SCT030CB PROB40 TEMPO 1300/1308 3000 -DZ BR BCFG BKN010 PROB30 TEMPO 1300/1307 0800 FG BKN002 TEMPO 1308/1315 36010KT', + 'TAF AMD LERS 121659Z 1216/1315 05010KT 9999 BKN025 TX17/1312Z TN12/1306Z TEMPO 1217/1222 VRB04KT TEMPO 1300/1315 3000 RA SHRA BKN014 FEW020TCU', + 'TAF AMD LESA 121541Z 1215/1315 VRB05KT 9999 BKN020 TX19/1315Z TN01/1306Z PROB40 TEMPO 1215/1221 30010KT RA PROB40 TEMPO 1215/1220 SHRA SCT030TCU PROB30 TEMPO 1215/1219 VRB15G25KT 3000 TSRA SCT035CB PROB40 TEMPO 1300/1308 2500 BR BCFG BKN010 PROB30 TEMPO 1301/1307 0800 FG VV002 BECMG 1311/1313 05010KT', + 'TAF LESO 121700Z 1218/1318 18009KT 9999 FEW050 TX17/1316Z TN12/1306Z PROB40 TEMPO 1218/1224 3000 RA SHRA SCT020TCU PROB30 TEMPO 1218/1224 TS FEW030CB BECMG 1306/1308 26010KT TEMPO 1308/1312 BKN010 PROB40 TEMPO 1308/1312 3000 -RA BR BKN005', + 'TAF LEST 121700Z 1218/1318 23009KT 9000 SCT010 BKN025 TX15/1313Z TN10/1306Z TEMPO 1218/1310 3000 RA SHRA BKN010 SCT030TCU TEMPO 1218/1308 1500 RA BR BKN005 TEMPO 1218/1307 0800 BCFG BKN002 PROB40 TEMPO 1222/1306 0350 FG VV001 TEMPO 1311/1318 34010KT', + 'TAF LETO 121700Z 1218/1318 VRB05KT 9999 SCT020 BKN040 TX20/1316Z TN04/1306Z TEMPO 1218/1221 23012KT RA PROB40 TEMPO 1218/1221 4000 SHRA BKN014 SCT030TCU PROB40 TEMPO 1218/1220 VRB15G25KT 3000 TSRA SCT030CB PROB30 TEMPO 1300/1308 4000 BR BECMG 1310/1312 03010KT', + 'TAF LEVC 121700Z 1218/1318 07008KT 9999 FEW010 BKN020 TX18/1312Z TN12/1307Z PROB40 TEMPO 1222/1306 4000 RA BR BKN008 TEMPO 1311/1318 3000 RA SHRA FEW020TCU BECMG 1219/1221 VRB04KT', + 'TAF LEVD 121400Z 1215/1315 VRB05KT 9999 SCT020 BKN030 TX16/1315Z TN03/1306Z TEMPO 1215/1221 25010KT RA PROB40 TEMPO 1215/1220 4000 SHRA BKN012 SCT020TCU PROB40 TEMPO 1215/1219 VRB15G25KT 3000 TSRA SCT035CB PROB40 TEMPO 1303/1311 2500 BR BCFG BKN010 PROB40 TEMPO 1301/1310 0800 FG VV002', + 'TAF LEVT 121700Z 1218/1318 20009KT 9999 FEW030 SCT045 TX14/1316Z TN05/1306Z PROB40 TEMPO 1218/1224 3000 RA SHRA BKN010 SCT035TCU PROB30 TEMPO 1218/1224 TS FEW030CB PROB40 TEMPO 1300/1307 3000 BR BCFG TEMPO 1308/1318 33010KT PROB30 TEMPO 1308/1312 RA FEW025TCU', + 'TAF LEVX 121700Z 1218/1318 26009KT 9000 SCT015 BKN030 TX16/1316Z TN10/1304Z PROB40 TEMPO 1218/1310 3000 RA SHRA BKN010 SCT030TCU TEMPO 1218/1308 1500 -RA BCFG BKN005 PROB40 TEMPO 1218/1306 0350 FG VV001 PROB40 TEMPO 1315/1318 32010KT', + 'TAF LEXJ 121400Z 1215/1315 22009KT 9999 FEW050 TX17/1314Z TN13/1306Z TEMPO 1215/1219 03012KT PROB30 TEMPO 1215/1221 3000 RA SHRA TS SCT030TCU SCT040CB PROB40 TEMPO 1306/1312 28010KT TEMPO 1311/1315 04010KT PROB30 TEMPO 1305/1312 3000 RA', + 'TAF LEZG 121700Z 1218/1318 12011KT 9999 FEW020 TX16/1314Z TN12/1306Z PROB40 TEMPO 1218/1302 4000 SHRA FEW030TCU PROB40 TEMPO 1219/1224 3000 TSRA FEW040CB BECMG 1303/1306 31010KT', + 'TAF LEZL 121400Z 1215/1315 24006KT 9999 FEW015 SCT030 TX19/1215Z TN08/1306Z PROB40 TEMPO 1215/1220 4000 SHRA FEW030CB PROB30 TEMPO 1215/1217 2000 TSRA SCT030CB PROB40 TEMPO 1303/1309 3000 BR BKN006 PROB30 TEMPO 1304/1309 0500 FG VV001', + 'TAF LFAQ 121400Z 1215/1224 21010KT CAVOK TEMPO 1216/1222 SCT060CB', + 'TAF LFAT 121400Z 1215/1224 23007KT CAVOK PROB40 TEMPO 1217/1222 3000 RA BKN012 BKN060CB', + 'TAF LFBA 121400Z 1215/1224 12007KT CAVOK TEMPO 1215/1224 4000 SHRA BKN035TCU PROB30 TEMPO 1215/1221 4000 TSRA BKN030CB TEMPO 1219/1222 15015G25KT', + 'TAF LFBC 121400Z 1215/1315 13008KT CAVOK PROB40 TEMPO 1215/1220 4000 -SHRA BKN030TCU PROB30 TEMPO 1215/1216 -TSRA SCT014 BKN025CB PROB40 TEMPO 1301/1308 BKN007 TEMPO 1312/1315 4000 -SHRA BKN025TCU', + 'TAF LFBD 121700Z 1218/1324 13010KT CAVOK TX20/1314Z TN11/1306Z PROB30 TEMPO 1218/1221 3000 TSRA BKN020CB BECMG 1302/1304 BKN010 BECMG 1308/1310 06005KT BKN015 PROB30 TEMPO 1312/1316 4000 -SHRA BKN020TCU PROB30 TEMPO 1321/1324 -SHRA BKN080TCU', + 'TAF LFBE 121100Z 1212/1312 20010KT CAVOK TEMPO 1212/1215 4000 -SHRA SCT010 BKN050TCU BECMG 1215/1217 12005KT TEMPO 1217/1224 -SHRA BKN060TCU PROB30 TEMPO 1218/1221 4000 TSRA BKN050CB BECMG 1302/1304 BKN020', + 'TAF LFBG 121400Z 1215/1315 16011KT CAVOK TEMPO 1216/1218 3000 SHRA SCT025CB BECMG 1301/1303 BKN014', + 'TAF LFBH 121700Z 1218/1318 15005KT CAVOK BECMG 1219/1221 VRB03KT PROB40 1223/1308 3000 BR NSC PROB30 1300/1307 0400 FG VV/// BECMG 1308/1310 31005KT BKN015 BECMG 1311/1313 SCT020 PROB30 TEMPO 1313/1316 4000 -SHRA BKN025TCU', + 'TAF LFBI 121700Z 1218/1318 20005KT CAVOK TEMPO 1218/1221 -SHRA BKN060TCU PROB40 TEMPO 1300/1311 4000 -RA BKN020 SCT060TCU PROB30 TEMPO 1308/1311 3000 RA BKN010 PROB30 TEMPO 1312/1316 4000 -SHRA BKN030TCU', + 'TAF LFBL 121700Z 1218/1318 14007KT CAVOK PROB30 TEMPO 1218/1220 VRB03KT PROB40 TEMPO 1218/1307 -RA BKN040 BECMG 1300/1302 BKN030 BECMG 1304/1306 VRB03KT BECMG 1314/1316 CAVOK', + 'TAF LFBO 121700Z 1218/1324 13017G27KT 9999 BKN020 BECMG 1218/1220 13012KT TEMPO 1221/1309 BKN012 PROB40 TEMPO 1309/1318 -RA BKN040 BECMG 1313/1315 CAVOK BECMG 1316/1318 VRB03KT BECMG 1322/1324 4000 BR', + 'TAF LFBP 121700Z 1218/1318 09008KT CAVOK PROB30 TEMPO 1218/1302 4000 SHRA BKN050TCU BECMG 1302/1304 VRB03KT BECMG 1309/1311 BKN025 TEMPO 1311/1318 3000 SHRA BKN025TCU', + 'TAF LFBT 121700Z 1218/1318 08004KT CAVOK PROB40 TEMPO 1218/1301 -SHRA BKN030TCU FEW035CB PROB30 TEMPO 1218/1221 TSRA BKN030CB BECMG 1223/1301 18012KT PROB40 TEMPO 1301/1303 18015G25KT BECMG 1304/1306 27005KT BECMG 1310/1312 SCT025 BKN060 TEMPO 1312/1318 4000 SHRA BKN015TCU BECMG 1313/1315 09005KT', + 'TAF LFBU 121700Z 1218/1318 15005KT CAVOK TEMPO 1218/1221 4000 SHRA BKN040TCU PROB30 TEMPO 1218/1221 3000 TSRA BKN030CB BECMG 1300/1302 BKN015 PROB30 TEMPO 1302/1308 4000 -RA BKN010 TEMPO 1312/1316 4000 SHRA BKN020TCU', + 'TAF LFBZ 121700Z 1218/1318 11005KT CAVOK PROB40 TEMPO 1218/1224 4000 SHRA BKN040TCU PROB30 TEMPO 1218/1220 TSRA BKN040CB BECMG 1300/1302 VRB03KT BECMG 1308/1310 27010KT BECMG 1310/1312 BKN005 PROB30 TEMPO 1315/1318 3000 -RA BKN003', + 'TAF LFCK 121700Z 1218/1303 13028G38KT CAVOK PROB40 TEMPO 1218/1301 13035G45KT PROB30 TEMPO 1219/1303 BKN008', + 'TAF LFCR 121700Z 1218/1318 13018G28KT CAVOK PROB30 TEMPO 1221/1224 BKN012 BECMG 1223/1301 14010KT PROB30 TEMPO 1302/1306 BKN030 BECMG 1303/1305 10005KT BECMG 1308/1310 17005KT', + 'TAF AMD LFGJ 121700Z 1217/1224 20012KT CAVOK', + 'TAF LFJL 121700Z 1218/1318 16008KT CAVOK BECMG 1311/1314 32006KT', + 'TAF AMD LFKB 121608Z 1216/1315 14015G25KT 9999 BKN020 TX19/1313Z TN08/1304Z TEMPO 1216/1219 14020G30KT SCT015 BKN020 TEMPO 1219/1307 SCT010 BKN015 BECMG 1220/1222 15012KT BECMG 1302/1304 16004KT BECMG 1307/1309 14012KT SCT018 BECMG 1310/1312 CAVOK', + 'TAF LFKC 121400Z 1215/1315 35008KT CAVOK TX22/1313Z TN08/1304Z BECMG 1215/1217 18006KT BECMG 1307/1309 36008KT', + 'TAF LFKF 121400Z 1215/1315 06015G25KT 9999 BKN020 TEMPO 1215/1218 06020G30KT BECMG 1304/1306 FEW020 TEMPO 1306/1315 06015G28KT', + 'TAF LFKJ 121400Z 1215/1315 21010KT CAVOK BECMG 1215/1217 34006KT BECMG 1220/1222 05005KT BECMG 1308/1310 22010KT', + 'TAF LFKS 121400Z 1215/1315 15012KT 9999 SCT020 BKN040 BECMG 1216/1218 23006KT', + 'TAF LFLB 121400Z 1215/1315 VRB03KT CAVOK', + 'TAF LFLC 121700Z 1218/1318 17010KT CAVOK BECMG 1314/1316 02012KT', + 'TAF LFLL 121700Z 1218/1324 15008KT CAVOK BECMG 1311/1314 36010KT', + 'TAF LFLN 121700Z 1218/1303 VRB04KT CAVOK', + 'TAF LFLP 121400Z 1215/1315 VRB03KT CAVOK', + 'TAF LFLS 121400Z 1215/1315 24010KT CAVOK BECMG 1220/1222 VRB03KT', + 'TAF LFLW 121400Z 1215/1224 16015KT CAVOK TEMPO 1215/1217 17015G30KT BECMG 1215/1217 17008KT', + 'TAF LFLX 121700Z 1218/1324 VRB05KT CAVOK TEMPO 1218/1314 -RA PROB40 TEMPO 1221/1308 4000 RA BKN030 SCT040TCU TEMPO 1322/1324 -RA BKN020', + 'TAF LFLY 121700Z 1218/1318 15008KT CAVOK BECMG 1312/1315 36010KT', + 'TAF AMD LFMD 121655Z 1216/1315 VRB05KT 9999 FEW023 BKN260 TEMPO 1216/1313 09025G35KT BECMG 1313/1315 04010KT', + 'TAF LFMI 121400Z 1215/1315 19010KT CAVOK TX20/1215Z TN11/1304Z TEMPO 1218/1222 10010KT BECMG 1221/1222 VRB02KT TEMPO 1313/1315 20012KT', + 'TAF AMD LFMK 121717Z 1217/1315 08017G27KT 9999 BKN012 TEMPO 1217/1222 09020G33KT TEMPO 1217/1306 OVC007 BECMG 1303/1305 09012KT BECMG 1309/1311 BKN020 BECMG 1311/1313 13005KT SCT025', + 'TAF LFML 121400Z 1215/1321 10008KT CAVOK TX22/1215Z TN11/1304Z TEMPO 1312/1316 21010KT', + 'TAF LFMN 121400Z 1215/1321 09024KT 9999 FEW023 BKN260 TEMPO 1215/1314 09025G35KT BECMG 1314/1316 04010KT', + 'TAF AMD LFMP 121739Z 1217/1315 10010KT 9999 BKN015 TEMPO 1217/1310 2000 BR BKN004 BKN010 BECMG 1301/1303 05005KT BECMG 1310/1312 35005KT BECMG 1313/1315 30006KT FEW015 BKN060', + 'TAF AMD LFMT 121817Z 1218/1315 14012KT CAVOK TEMPO 1218/1219 15023KT BECMG 1223/1302 02005KT BECMG 1309/1312 20005KT', + 'TAF LFMU 121400Z 1215/1315 11012KT 9999 SCT016 BKN050 TEMPO 1215/1217 12015G25KT TEMPO 1216/1307 BKN016 BKN040 BECMG 1305/1307 05005KT BECMG 1307/1309 SCT028 BECMG 1311/1313 CAVOK', + 'TAF LFMV 121400Z 1215/1315 VRB02KT CAVOK TEMPO 1215/1218 18010KT TEMPO 1314/1315 21012KT', + 'TAF LFOA 121400Z 1215/1315 17010KT CAVOK TX22/1215Z TN11/1306Z BECMG 1215/1218 30005KT TEMPO 1303/1312 -RA BKN014', + 'TAF LFOB 121700Z 1218/1318 22008KT CAVOK TEMPO 1218/1222 RA SCT050CB BECMG 1302/1305 2000 BR TEMPO 1305/1309 RA BKN012 BKN050TCU BECMG 1309/1311 9999 NSW BKN025', + 'TAF LFOE 121400Z 1215/1315 19012KT CAVOK TEMPO 1215/1218 -RA PROB30 TEMPO 1215/1216 SCT050TCU BECMG 1222/1223 VRB03KT 3000 BR PROB40 TEMPO 1301/1307 0400 FG VV/// BECMG 1306/1308 BKN004 BECMG 1309/1311 9999 NSW BKN010 PROB40 TEMPO 1312/1315 4000 -SHRA BKN020TCU', + 'TAF LFOH 121100Z 1212/1312 20012KT CAVOK TEMPO 1212/1218 3000 RA BKN012 BKN060CB BECMG 1218/1221 VRB03KT PROB40 1222/1308 0300 FG VV/// PROB30 TEMPO 1310/1312 4000 SHRA BKN020TCU', + 'TAF LFOJ 121400Z 1215/1315 20010KT CAVOK TX20/1215Z TN10/1304Z BECMG 1222/1224 VRB02KT PROB30 TEMPO 1303/1311 4900 -RA SCT010', + 'TAF LFOK 121700Z 1218/1318 VRB02KT CAVOK TEMPO 1312/1318 SCT040CB', + 'TAF LFOP 121700Z 1218/1318 20010KT CAVOK TEMPO 1218/1220 4000 SHRA BKN060CB BECMG 1220/1223 VRB02KT BECMG 1223/1302 1500 BR OVC006 TEMPO 1302/1308 0200 FG VV/// BECMG 1308/1311 9999 NSW BKN025', + 'TAF LFOT 121700Z 1218/1318 17005KT CAVOK TEMPO 1218/1302 -RA SCT060TCU TEMPO 1302/1312 4000 -RA BKN012 PROB40 TEMPO 1302/1308 2500 RA BKN007 SCT040TCU BECMG 1312/1314 SCT030', + 'TAF LFPB 121700Z 1218/1318 24010KT CAVOK BECMG 1300/1302 VRB03KT TEMPO 1306/1310 -RA BKN050 PROB40 TEMPO 1311/1315 -SHRA SCT018TCU', + 'TAF LFPG 121700Z 1218/1324 24010KT CAVOK TX18/1314Z TN12/1306Z BECMG 1300/1302 VRB03KT TEMPO 1306/1310 -RA BKN050 PROB40 TEMPO 1311/1315 -SHRA SCT018TCU', + 'TAF LFPN 121700Z 1218/1303 20008KT CAVOK TEMPO 1218/1220 -RA SCT060TCU BECMG 1222/1224 VRB03KT TEMPO 1301/1303 -RA', + 'TAF LFPO 121700Z 1218/1324 25010KT CAVOK BECMG 1222/1224 VRB02KT BECMG 1301/1304 -RA TEMPO 1304/1311 4000 RA BKN016TCU OVC060 BECMG 1313/1316 34004KT CAVOK', + 'TAF LFPV 121400Z 1215/1315 20008KT CAVOK TX19/1215Z TN11/1304Z TEMPO 1306/1309 4500 -RA TEMPO 1309/1315 4500 -SHRA BKN030TCU', + 'TAF LFQQ 121700Z 1218/1324 20006KT CAVOK TEMPO 1218/1220 SCT060CB PROB30 1304/1307 4000 BR TEMPO 1316/1318 34010KT SCT030TCU', + 'TAF LFRB 121700Z 1218/1324 19013KT 9999 SCT020 BKN030 TEMPO 1218/1221 BKN012 TEMPO 1221/1308 3000 BR BKN003 PROB40 TEMPO 1300/1307 0500 FG VV/// BECMG 1302/1304 VRB02KT TEMPO 1309/1311 RA BKN012 BECMG 1316/1319 3000 BR BKN005', + 'TAF LFRC 121400Z 1215/1224 20010KT 9999 BKN020 OVC050 TEMPO 1215/1216 RA BKN012 PROB40 TEMPO 1216/1220 4000 SHRA BKN025TCU TEMPO 1222/1224 4000 BR', + 'TAF LFRD 121700Z 1218/1318 20005KT 9999 BKN030 BECMG 1223/1301 3000 BR BECMG 1301/1303 0500 FG VV/// TEMPO 1303/1308 0300 FG VV/// BECMG 1308/1310 6000 NSW BKN010 BECMG 1310/1312 SCT025 PROB30 TEMPO 1315/1318 0800 FG BKN003', + 'TAF LFRG 121400Z 1215/1224 20008KT 9999 BKN030 TEMPO 1215/1219 4000 SHRA BKN010 BKN035CB TEMPO 1219/1221 SHRA BKN012 BECMG 1222/1224 4000 BR', + 'TAF LFRH 121700Z 1218/1318 15005KT 9999 BKN040 BECMG 1220/1223 2000 BR BKN002 TEMPO 1303/1306 0800 FG VV/// BECMG 1306/1309 9999 NSW SCT020 TEMPO 1312/1315 -SHRA SCT015CB', + 'TAF LFRI 121700Z 1218/1318 17008KT 9999 SCT020 BKN040 BECMG 1218/1220 VRB02KT TEMPO 1304/1308 3000 BR TEMPO 1306/1309 BKN004 TEMPO 1314/1318 4000 -SHRA SCT025CB', + 'TAF LFRK 121400Z 1215/1224 19008KT 9999 BKN030 TEMPO 1215/1217 4000 SHRA BKN012 BKN025CB BECMG 1220/1222 3000 BR TEMPO 1222/1224 0700 FG VV///', + 'TAF LFRM 121700Z 1218/1318 17007KT CAVOK TEMPO 1218/1220 -SHRA SCT030CB BECMG 1300/1302 2000 BR BKN003 PROB30 TEMPO 1300/1306 0600 FG VV/// BECMG 1306/1309 9999 NSW SCT010 BKN020 TEMPO 1310/1318 4000 -SHRA BKN018CB', + 'TAF LFRN 121700Z 1218/1318 16005KT 9999 SCT030 BECMG 1221/1223 2000 BR BECMG 1223/1301 0500 FG VV/// TEMPO 1301/1308 0300 FG VV/// BECMG 1308/1310 6000 NSW BKN008 BECMG 1310/1312 SCT030', + 'TAF LFRQ 121700Z 1218/1318 17010KT 9999 SCT016 BKN030 BECMG 1218/1220 16006KT TEMPO 1223/1309 3000 BR BKN003 PROB40 TEMPO 1300/1306 0400 FG VV///', + 'TAF LFRS 121700Z 1218/1324 14005KT 9999 SCT030 TEMPO 1300/1309 3000 BR BKN003 PROB40 TEMPO 1300/1306 0300 FG VV/// BECMG 1308/1311 NSW SCT020 TEMPO 1312/1318 4000 -SHRA SCT025CB', + 'TAF LFRZ 121700Z 1218/1318 21006KT 9999 SCT030 BECMG 1300/1302 2000 BR BKN002 TEMPO 1302/1307 0300 FG VV/// BECMG 1308/1311 9999 NSW SCT020', + 'TAF LFSB 121700Z 1218/1318 15003KT CAVOK PROB30 TEMPO 1312/1318 34010KT FEW050TCU', + ]; + + var c = 0; + var e = 0; + var a = 0; + for (final taf in tafs) { + try { + final tafO = Taf(taf); + if (tafO.warnings.isNotEmpty) { + c++; + } else { + a++; + } + } catch (er) { + e++; + } + } + expect('$a/$c/$e', 0); + })); +} From 0274c76f0115eff4c435152ab189686cad523918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 12 Apr 2022 16:30:46 -0300 Subject: [PATCH 08/17] . --- test/taf/exhaustive_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart index edeccd3..c28adc2 100644 --- a/test/taf/exhaustive_test.dart +++ b/test/taf/exhaustive_test.dart @@ -88,7 +88,7 @@ void main() { 'TAF HESN 121700Z 1218/1324 36012KT CAVOK', 'TAF HETR 121700Z 1218/1324 31025G35KT 6000 NSC', 'TAF HKEL 121700Z 1218/1324 13010KT 9999 FEW023CB SCT024 BECMG 1220/1223 08005KT FEW020 BECMG 1308/1311 13010KT FEW025CB SCT026 TEMPO 1313/1317 -TSRA FEW021CB SCT022 BKN080 BECMG 1320/1323 08005KT NSW FEW020', - 'TAF HKJK 121700Z 1218/1324 12010KT 9999 SCT027 BECMG 1220/1223 04005K T FEW018 BECMG 1308/1311 08010KT FEW024CB SCT025 BKN080 PROB40 TEMPO 1313/1317 -SHRA FEW022CB SCT023 BKN080 BECMG 1320/1323 04005KT NSW FEW018', + 'TAF HKJK 121700Z 1218/1324 12010KT 9999 SCT027 BECMG 1220/1223 04005KT FEW018 BECMG 1308/1311 08010KT FEW024CB SCT025 BKN080 PROB40 TEMPO 1313/1317 -SHRA FEW022CB SCT023 BKN080 BECMG 1320/1323 04005KT NSW FEW018', 'TAF HKKI 121700Z 1218/1324 23010KT 9999 FEW028CB SCT090 BECMG 1220/1223 06005KT FEW021 BECMG 1308/1311 24010KT FEW028CB SCT090 TEMPO 1313/1317 -TSRA FEW021CB SCT022 BKN080 BECMG 1320/1323 06005KT NSW FEW021', 'TAF HKML 121700Z 1218/1318 18010KT 9999 SCT024 TEMPO 1300/1304 00000KT -SHRA FEW019CB SCT020 BKN080 BECMG 1309/1312 18015KT SCT026', 'TAF HKNW 121700Z 1218/1318 13009KT 9999 SCT028 BECMG 1220/1223 05005KT FEW019 BECMG 1308/1311 09010KT FEW026CB SCT027 BKN090 PROB40 TEMPO 1313/1317', From 4901ac348c89536509c564012ab63cb7519c713c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Wed, 13 Apr 2022 09:46:46 -0300 Subject: [PATCH 09/17] Taf parser working for 87% of the taf examples --- lib/src/models/metar/metar.dart | 26 +---- lib/src/models/metar/models/pressure.dart | 13 +++ .../models/metar/models/weather_trend.dart | 5 +- lib/src/models/models.dart | 1 + .../models/taf/models/change_forecast.dart | 10 ++ lib/src/models/taf/models/temperature.dart | 24 +++++ lib/src/models/taf/models/turbulence.dart | 98 +++++++++++++++++++ lib/src/models/taf/taf.dart | 38 +++---- lib/src/models/temperature.dart | 14 +++ lib/src/utils/taf_regexp.dart | 3 + test/taf/exhaustive_test.dart | 15 ++- 11 files changed, 189 insertions(+), 58 deletions(-) create mode 100644 lib/src/models/taf/models/turbulence.dart diff --git a/lib/src/models/metar/metar.dart b/lib/src/models/metar/metar.dart index 8c6f0dc..855b169 100644 --- a/lib/src/models/metar/metar.dart +++ b/lib/src/models/metar/metar.dart @@ -8,7 +8,9 @@ class Metar extends Report MetarWindMixin, MetarPrevailingMixin, MetarWeatherMixin, - MetarCloudMixin { + MetarCloudMixin, + PressureMixin, + MetarTemperatureMixin { late final int? _year, _month; // Body groups @@ -16,8 +18,6 @@ class Metar extends Report MetarMinimumVisibility _minimumVisibility = MetarMinimumVisibility(null, null); final _runwayRanges = GroupList(3); - MetarTemperatures _temperatures = MetarTemperatures(null, null); - MetarPressure _pressure = MetarPressure(null, null); MetarRecentWeather _recentWeather = MetarRecentWeather(null, null); final _windshear = MetarWindshearList(); MetarSeaState _seaState = MetarSeaState(null, null); @@ -100,26 +100,6 @@ class Metar extends Report /// Get the runway ranges data of the METAR if provided. GroupList get runwayRanges => _runwayRanges; - void _handleTemperatures(String group) { - final match = MetarRegExp.TEMPERATURES.firstMatch(group); - _temperatures = MetarTemperatures(group, match); - - _concatenateString(_temperatures); - } - - /// Get the temperatures data of the METAR. - MetarTemperatures get temperatures => _temperatures; - - void _handlePressure(String group) { - final match = MetarRegExp.PRESSURE.firstMatch(group); - _pressure = MetarPressure(group, match); - - _concatenateString(_pressure); - } - - /// Get the pressure of the METAR. - MetarPressure get pressure => _pressure; - void _handleRecentWeather(String group) { final match = MetarRegExp.RECENT_WEATHER.firstMatch(group); _recentWeather = MetarRecentWeather(group, match); diff --git a/lib/src/models/metar/models/pressure.dart b/lib/src/models/metar/models/pressure.dart index 9ceb036..211ebee 100644 --- a/lib/src/models/metar/models/pressure.dart +++ b/lib/src/models/metar/models/pressure.dart @@ -27,3 +27,16 @@ class MetarPressure extends Pressure with GroupMixin { } } } + +mixin PressureMixin on StringAttributeMixin { + late MetarPressure _pressure = MetarPressure(null, null); + + void _handlePressure(String group) { + final match = MetarRegExp.PRESSURE.firstMatch(group); + _pressure = MetarPressure(group, match); + + _concatenateString(_pressure); + } + + MetarPressure get pressure => _pressure; +} diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index c493f48..b9419e1 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -8,7 +8,10 @@ class Forecast extends Group MetarPrevailingMixin, MetarWeatherMixin, MetarCloudMixin, - TafWindshearMixin { + TafWindshearMixin, + TafTurbulenceMixin, + PressureMixin, + TafTemperatureMixin { final _unparsedGroups = []; Forecast(String code) : super(code); diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index 6685631..71159d2 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -47,4 +47,5 @@ part 'taf/models/missing.dart'; part 'taf/models/temperature.dart'; part 'taf/models/valid.dart'; part 'taf/models/windshear.dart'; +part 'taf/models/turbulence.dart'; part 'taf/taf.dart'; diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 02724bd..394fb7a 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -52,6 +52,16 @@ class ChangeForecast extends Forecast { GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), + GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _valid.periodFrom.time)), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _valid.periodFrom.time)), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _valid.periodFrom.time)), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _valid.periodFrom.time)), GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; diff --git a/lib/src/models/taf/models/temperature.dart b/lib/src/models/taf/models/temperature.dart index 8a07ce3..7626564 100644 --- a/lib/src/models/taf/models/temperature.dart +++ b/lib/src/models/taf/models/temperature.dart @@ -51,3 +51,27 @@ class TafTemperature extends Temperature with GroupMixin { class TafTemperatureList extends GroupList { TafTemperatureList() : super(2); } + +mixin TafTemperatureMixin on StringAttributeMixin { + final _maxTemperatures = TafTemperatureList(); + final _minTemperatures = TafTemperatureList(); + + void _handleTemperature(String group, {required DateTime time}) { + final match = TafRegExp.TEMPERATURE.firstMatch(group); + final temperature = TafTemperature(group, match, time); + + if (match!.namedGroup('type') == 'X') { + _maxTemperatures.add(temperature); + } else { + _minTemperatures.add(temperature); + } + + _concatenateString(temperature); + } + + /// Get the maximum temperature expected to happen. + TafTemperatureList get maxTemperatures => _maxTemperatures; + + /// Get the minimum temperature expected to happen. + TafTemperatureList get minTemperatures => _minTemperatures; +} diff --git a/lib/src/models/taf/models/turbulence.dart b/lib/src/models/taf/models/turbulence.dart new file mode 100644 index 0000000..7df8a66 --- /dev/null +++ b/lib/src/models/taf/models/turbulence.dart @@ -0,0 +1,98 @@ +part of models; + +/// Basic structure for valid time groups in change periods and forecasts. +class TafTurbulence extends Group { + TafTurbulence(String? code) : super(code); + + late int layerBaseInFeet; + late int layerBaseInMeters; + late int thicknessLevel; + late int thicknessInFeet; + late int thicknessInMeters; + + late String? intensity, weatherCondition, frequency; + + /// Named constructor of the TafTurbulence clas + /// + /// Args: + /// code (String?): the code of the group. + /// match (RegExpMatch?): the match of the regular expression. + TafTurbulence.fromTaf(String? code, RegExpMatch? match) : super(code) { + if (match != null) { + layerBaseInFeet = int.parse(match.namedGroup('base')!) * 100; + layerBaseInMeters = (layerBaseInFeet * Conversions.FT_TO_M).round(); + final turbType = int.parse(match.namedGroup('turbType')!); + final thicknessLevel = int.parse(match.namedGroup('thickness')!); + thicknessInFeet = thicknessLevel * 1000; + thicknessInMeters = (thicknessInFeet * Conversions.FT_TO_M).round(); + + intensity = getIntensity(turbType); + weatherCondition = getWeatherCondition(turbType); + frequency = getFrequency(turbType); + } + } + + @override + String toString() { + return '$intensity turbulence between ${layerBaseInFeet == 0 ? 'surface' : '$layerBaseInFeet ft'} and ${layerBaseInFeet + thicknessInFeet} ft'; + } + + String? getIntensity(int turbType) { + return [ + 'None', + 'Light', + 'Moderate', + 'Moderate', + 'Moderate', + 'Moderate', + 'Severe', + 'Severe', + 'Severe', + 'Severe' + ][turbType]; + } + + String? getWeatherCondition(int turbType) { + return [ + null, + null, + 'Clear', + 'Clear', + 'Cloud', + 'Cloud', + 'Clear', + 'Clear', + 'Cloud', + 'Cloud' + ][turbType]; + } + + String? getFrequency(int turbType) { + return [ + null, + null, + 'Occasional', + 'Frequent', + 'Occasional', + 'Frequent', + 'Occasional', + 'Frequent', + 'Occasional', + 'Frequent' + ][turbType]; + } +} + +/// Mixin to add the valid period of forecast attribute and handler. +mixin TafTurbulenceMixin on StringAttributeMixin { + final List _turbulences = []; + + void _handleTurbulence(String group) { + final match = TafRegExp.TURBULENCE.firstMatch(group); + final turb = TafTurbulence.fromTaf(group, match); + _turbulences.add(turb); + _concatenateString(turb); + } + + List get turbulences => _turbulences; +} diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index 3630693..6e1b418 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -10,7 +10,9 @@ class Taf extends Report MetarPrevailingMixin, MetarWeatherMixin, MetarCloudMixin, - TafWindshearMixin { + TafWindshearMixin, + PressureMixin, + TafTemperatureMixin { late final String _body; final List _changesCodes = []; int? _year, _month; @@ -18,8 +20,6 @@ class Taf extends Report // Body groups Missing _missing = Missing(null); Cancelled _cancelled = Cancelled(null); - final _maxTemperatures = TafTemperatureList(); - final _minTemperatures = TafTemperatureList(); List warnings = []; @@ -77,25 +77,6 @@ class Taf extends Report /// Get the cancelled group data of the TAF. Cancelled get cancelled => _cancelled; - void _handleTemperature(String group) { - final match = TafRegExp.TEMPERATURE.firstMatch(group); - final temperature = TafTemperature(group, match, _time.time); - - if (match!.namedGroup('type') == 'X') { - _maxTemperatures.add(temperature); - } else { - _minTemperatures.add(temperature); - } - - _concatenateString(temperature); - } - - /// Get the maximum temperature expected to happen. - TafTemperatureList get maxTemperatures => _maxTemperatures; - - /// Get the minimum temperature expected to happen. - TafTemperatureList get minTemperatures => _minTemperatures; - void _handleChangePeriod(String code) { final cf = ChangeForecast( code, @@ -137,10 +118,15 @@ class Taf extends Report GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), - GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), - GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), - GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), - GroupHandler(TafRegExp.TEMPERATURE, _handleTemperature), + GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _time.time)), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _time.time)), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _time.time)), + GroupHandler(TafRegExp.TEMPERATURE, + (e) => _handleTemperature(e, time: _time.time)), GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; diff --git a/lib/src/models/temperature.dart b/lib/src/models/temperature.dart index 1182fe5..7e803bc 100644 --- a/lib/src/models/temperature.dart +++ b/lib/src/models/temperature.dart @@ -51,3 +51,17 @@ class Temperature extends Numeric { double? get inRankine => converted(conversionFunction: Conversions.celsiusToRankine); } + +mixin MetarTemperatureMixin on StringAttributeMixin { + late MetarTemperatures _temperatures = MetarTemperatures(null, null); + + void _handleTemperatures(String group) { + final match = MetarRegExp.TEMPERATURES.firstMatch(group); + _temperatures = MetarTemperatures(group, match); + + _concatenateString(_temperatures); + } + + /// Get the temperatures data of the METAR. + MetarTemperatures get temperatures => _temperatures; +} diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index 1f6b879..d59744b 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -41,4 +41,7 @@ class TafRegExp { r'P?(?\d{2,3}|//|///)' r'(G(P)?(?\d{2,3}))?' r'(?KT|MPS)$'); + + static final RegExp TURBULENCE = + RegExp(r'^(5(?[0-9])(?\d\d\d)(?\d))'); } diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart index c28adc2..50d47bc 100644 --- a/test/taf/exhaustive_test.dart +++ b/test/taf/exhaustive_test.dart @@ -27,7 +27,6 @@ void main() { expect(taf.warnings.length, 0); expect(taf.changePeriods[1].prevailingVisibility.inMeters, 5000); - // TODO: still failing final code4 = 'TAF KADW 121700Z 1217/1323 29010G15KT 9999 SCT020 BKN040 OVC085 QNH3004INS TEMPO 1217/1221 29015G25KT 510002 BECMG 1220/1221 VRB06KT 9999 SCT030 BKN050 QNH3003INS BECMG 1315/1316 20010G15KT 9999 FEW060 SCT150 BKN200 QNH2992INS BECMG 1320/1321 21020G30KT 9999 FEW050 FEW110 BKN160 510003 QNH2990INS TX24/1317Z TN13/1308Z'; taf = Taf(code4); @@ -570,21 +569,21 @@ void main() { 'TAF LFSB 121700Z 1218/1318 15003KT CAVOK PROB30 TEMPO 1312/1318 34010KT FEW050TCU', ]; - var c = 0; - var e = 0; - var a = 0; + var warnings = 0; + var errors = 0; + var correct = 0; for (final taf in tafs) { try { final tafO = Taf(taf); if (tafO.warnings.isNotEmpty) { - c++; + warnings++; } else { - a++; + correct++; } } catch (er) { - e++; + errors++; } } - expect('$a/$c/$e', 0); + expect('$correct/$warnings/$errors', '${tafs.length}/0/0'); })); } From 98c7010c13dce898b1a3b8f5807b8e2ca0bb1c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Wed, 13 Apr 2022 12:18:36 -0300 Subject: [PATCH 10/17] 92% progress --- lib/src/models/metar/metar.dart | 20 ++--- lib/src/models/metar/models/weather.dart | 4 +- .../models/metar/models/weather_trend.dart | 3 +- .../models/metar/models/wind_variation.dart | 13 +++ lib/src/models/models.dart | 1 + .../models/taf/models/change_forecast.dart | 5 ++ lib/src/models/taf/models/icing.dart | 81 +++++++++++++++++++ lib/src/models/taf/taf.dart | 15 +++- lib/src/utils/metar_regexp.dart | 4 +- lib/src/utils/parser.dart | 12 ++- lib/src/utils/taf_regexp.dart | 4 +- test/taf/exhaustive_test.dart | 40 +++++++-- 12 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 lib/src/models/taf/models/icing.dart diff --git a/lib/src/models/metar/metar.dart b/lib/src/models/metar/metar.dart index 855b169..edecb49 100644 --- a/lib/src/models/metar/metar.dart +++ b/lib/src/models/metar/metar.dart @@ -10,11 +10,11 @@ class Metar extends Report MetarWeatherMixin, MetarCloudMixin, PressureMixin, - MetarTemperatureMixin { + MetarTemperatureMixin, + MetarWindVariationMixin { late final int? _year, _month; - // Body groups - MetarWindVariation _windVariation = MetarWindVariation(null, null); + // Body group MetarMinimumVisibility _minimumVisibility = MetarMinimumVisibility(null, null); final _runwayRanges = GroupList(3); @@ -69,16 +69,6 @@ class Metar extends Report /// Get the time of the METAR. MetarTime get time => _time; - void _handleWindVariation(String group) { - final match = MetarRegExp.WIND_VARIATION.firstMatch(group); - _windVariation = MetarWindVariation(group, match); - - _concatenateString(_windVariation); - } - - /// Get the wind variation directions of the METAR. - MetarWindVariation get windVariation => _windVariation; - void _handleMinimumVisibility(String group) { final match = MetarRegExp.VISIBILITY.firstMatch(group); _minimumVisibility = MetarMinimumVisibility(group, match); @@ -259,3 +249,7 @@ class Metar extends Report _sections.add(remark); } } + +String sanitizeWindToken(String code) { + return code.replaceAll(' WND ', ' WND_'); +} diff --git a/lib/src/models/metar/models/weather.dart b/lib/src/models/metar/models/weather.dart index 429176e..11672cf 100644 --- a/lib/src/models/metar/models/weather.dart +++ b/lib/src/models/metar/models/weather.dart @@ -63,7 +63,9 @@ class MetarWeather extends Group { if (match != null) { _intensity = INTENSITY[match.namedGroup('int')]; _description = DESCRIPTION[match.namedGroup('desc')]; - _precipitation = PRECIPITATION[match.namedGroup('prec')]; + final prec2 = PRECIPITATION[match.namedGroup('prec2')]; + _precipitation = + '${PRECIPITATION[match.namedGroup('prec')]}${prec2 == null ? '' : ' $prec2'}'; _obscuration = OBSCURATION[match.namedGroup('obsc')]; _other = OTHER[match.namedGroup('other')]; } diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index b9419e1..9a66c8f 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -11,7 +11,8 @@ class Forecast extends Group TafWindshearMixin, TafTurbulenceMixin, PressureMixin, - TafTemperatureMixin { + TafTemperatureMixin, + TafIcingMixin { final _unparsedGroups = []; Forecast(String code) : super(code); diff --git a/lib/src/models/metar/models/wind_variation.dart b/lib/src/models/metar/models/wind_variation.dart index dfc6d9a..4f65be6 100644 --- a/lib/src/models/metar/models/wind_variation.dart +++ b/lib/src/models/metar/models/wind_variation.dart @@ -53,3 +53,16 @@ class MetarWindVariation extends Group { double? get toInGradians => _to.converted(conversionDouble: Conversions.DEGREES_TO_GRADIANS); } + +mixin MetarWindVariationMixin on StringAttributeMixin { + MetarWindVariation _windVariation = MetarWindVariation(null, null); + void _handleWindVariation(String group) { + final match = MetarRegExp.WIND_VARIATION.firstMatch(group); + _windVariation = MetarWindVariation(group, match); + + _concatenateString(_windVariation); + } + + /// Get the wind variation directions of the METAR. + MetarWindVariation get windVariation => _windVariation; +} diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index 71159d2..6d05789 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -48,4 +48,5 @@ part 'taf/models/temperature.dart'; part 'taf/models/valid.dart'; part 'taf/models/windshear.dart'; part 'taf/models/turbulence.dart'; +part 'taf/models/icing.dart'; part 'taf/taf.dart'; diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 394fb7a..1f80c50 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -9,6 +9,7 @@ class ChangeForecast extends Forecast { : super(code) { // Initialize valid period of the forecasts _valid = valid; + _code = code; // Groups _changeIndicator = TafChangeIndicator(null, null, valid); @@ -53,7 +54,10 @@ class ChangeForecast extends Forecast { GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(TafRegExp.ICING, _handleIcing), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(TafRegExp.WIND, _handleWind), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _valid.periodFrom.time)), GroupHandler(TafRegExp.TEMPERATURE, @@ -67,6 +71,7 @@ class ChangeForecast extends Forecast { var sanitizedCode = sanitizeChangeIndicator(_code!); sanitizedCode = sanitizeVisibility(sanitizedCode); + sanitizedCode = sanitizeWindToken(sanitizedCode); final unparsed = parseSection(handlers, sanitizedCode, onWarning: onWarning); _unparsedGroups.addAll(unparsed); diff --git a/lib/src/models/taf/models/icing.dart b/lib/src/models/taf/models/icing.dart new file mode 100644 index 0000000..f15ce59 --- /dev/null +++ b/lib/src/models/taf/models/icing.dart @@ -0,0 +1,81 @@ +part of models; + +class TafIcing extends Group { + TafIcing(String? code) : super(code); + + late int layerBaseInFeet; + late int layerBaseInMeters; + late int thicknessLevel; + late int thicknessInFeet; + late int thicknessInMeters; + + late String? intensity, location, frequency; + + /// Named constructor of the TafIcing clas + /// + /// Args: + /// code (String?): the code of the group. + /// match (RegExpMatch?): the match of the regular expression. + TafIcing.fromTaf(String? code, RegExpMatch? match) : super(code) { + if (match != null) { + layerBaseInFeet = int.parse(match.namedGroup('base')!) * 100; + layerBaseInMeters = (layerBaseInFeet * Conversions.FT_TO_M).round(); + final turbType = int.parse(match.namedGroup('icingType')!); + final thicknessLevel = int.parse(match.namedGroup('thickness')!); + thicknessInFeet = thicknessLevel * 1000; + thicknessInMeters = (thicknessInFeet * Conversions.FT_TO_M).round(); + + intensity = getIntensity(turbType); + location = getLocation(turbType); + } + } + + @override + String toString() { + return '$intensity icing between ${layerBaseInFeet == 0 ? 'surface' : '$layerBaseInFeet ft'} and ${layerBaseInFeet + thicknessInFeet} ft${location != null ? ' ($location)' : ''}'; + } + + String? getIntensity(int turbType) { + return [ + 'None', + 'Light', + 'Light', + 'Light', + 'Moderate', + 'Moderate', + 'Moderate', + 'Severe', + 'Severe', + 'Severe' + ][turbType]; + } + + String? getLocation(int turbType) { + return [ + 'None', + null, + 'In cloud', + 'In precipitation', + null, + 'In cloud', + 'In precipitation', + null, + 'In cloud', + 'In precipitation', + ][turbType]; + } +} + +/// Mixin to add the valid period of forecast attribute and handler. +mixin TafIcingMixin on StringAttributeMixin { + final List _icings = []; + + void _handleIcing(String group) { + final match = TafRegExp.ICING.firstMatch(group); + final icing = TafIcing.fromTaf(group, match); + _icings.add(icing); + _concatenateString(icing); + } + + List get icings => _icings; +} diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index 6e1b418..a8454a7 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -12,7 +12,10 @@ class Taf extends Report MetarCloudMixin, TafWindshearMixin, PressureMixin, - TafTemperatureMixin { + TafTemperatureMixin, + MetarWindVariationMixin, + TafTurbulenceMixin, + TafIcingMixin { late final String _body; final List _changesCodes = []; int? _year, _month; @@ -78,6 +81,7 @@ class Taf extends Report Cancelled get cancelled => _cancelled; void _handleChangePeriod(String code) { + code = sanitizeWindToken(code); final cf = ChangeForecast( code, _valid, @@ -118,7 +122,11 @@ class Taf extends Report GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), + GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(TafRegExp.ICING, _handleIcing), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(MetarRegExp.WIND_VARIATION, _handleWindVariation), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _time.time)), GroupHandler(TafRegExp.TEMPERATURE, @@ -130,7 +138,9 @@ class Taf extends Report GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; - final unparsed = parseSection(handlers, _body, onWarning: (warning) { + final body = sanitizeWindToken(_body); + + final unparsed = parseSection(handlers, body, onWarning: (warning) { warnings.add(warning); }); _unparsedGroups.addAll(unparsed); @@ -161,6 +171,7 @@ class Taf extends Report if (sanitizedCode.startsWith('TAF')) { sanitizedCode = sanitizedCode.replaceFirst('TAF ', 'TAF_'); } + final sections = splitSentence( sanitizedCode, keywords, diff --git a/lib/src/utils/metar_regexp.dart b/lib/src/utils/metar_regexp.dart index 22f8ac1..8135033 100644 --- a/lib/src/utils/metar_regexp.dart +++ b/lib/src/utils/metar_regexp.dart @@ -20,7 +20,7 @@ class MetarRegExp { r'(?KT|MPS)$'); static final RegExp WIND_VARIATION = - RegExp(r'^(?(0[1-9]|[12][0-9]|3[0-6])0)' + RegExp(r'^(WND_)?(?(0[1-9]|[12][0-9]|3[0-6])0)' r'V(?(0[1-9]|[12][0-9]|3[0-6])0)$'); static final RegExp VISIBILITY = RegExp(r'^(?\d{4}|//\//)' @@ -40,7 +40,7 @@ class MetarRegExp { static final RegExp WEATHER = RegExp(r'^((?(-|\+|VC))?' r'(?MI|PR|BC|DR|BL|SH|TS|FZ)?' - r'((?DZ|RA|SN|SG|IC|PL|GR|GS|UP)|' + r'((?DZ|RA|SN|SG|IC|PL|GR|GS|UP)(?DZ|RA|SN|SG|IC|PL|GR|GS|UP)?|' r'(?BR|FG|FU|VA|DU|SA|HZ|PY)|' r'(?PO|SQ|FC|SS|DS|NSW|/))?)$'); diff --git a/lib/src/utils/parser.dart b/lib/src/utils/parser.dart index 3b65fb5..c3deaab 100644 --- a/lib/src/utils/parser.dart +++ b/lib/src/utils/parser.dart @@ -59,7 +59,7 @@ String sanitizeWindshear(String code) { /// Returns: /// String: the sanitized report or section. String sanitizeChangeIndicator(String code) { - final regex = RegExp(r'PROB(?[34]0)\sTEMPO'); + var regex = RegExp(r'PROB(?[34]0)\sTEMPO'); for (var i = 0; i < 5; i++) { if (regex.hasMatch(code)) { final match = regex.firstMatch(code); @@ -70,6 +70,16 @@ String sanitizeChangeIndicator(String code) { } } + regex = RegExp(r' FM (?0[1-9]|[12][0-9]|3[01])'); + for (var i = 0; i < 5; i++) { + if (regex.hasMatch(code)) { + final match = regex.firstMatch(code); + code = code.replaceFirst(regex, ' FM${match!.namedGroup("day")}'); + } else { + break; + } + } + return code; } diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index d59744b..55c5da8 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -12,7 +12,7 @@ class TafRegExp { static final RegExp CANCELLED = RegExp(r'^CNL$'); - static final RegExp WIND = RegExp(r'^(?([0-2][0-9]|3[0-6])0|VRB)' + static final RegExp WIND = RegExp(r'^(WND_)?(?([0-2][0-9]|3[0-6])0|VRB)' r'P?(?\d{2,3})' r'(G(P)?(?\d{2,3}))?' r'(?KT|MPS)$'); @@ -44,4 +44,6 @@ class TafRegExp { static final RegExp TURBULENCE = RegExp(r'^(5(?[0-9])(?\d\d\d)(?\d))'); + static final RegExp ICING = + RegExp(r'^(6(?[0-9])(?\d\d\d)(?\d))'); } diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart index 50d47bc..dad4ef8 100644 --- a/test/taf/exhaustive_test.dart +++ b/test/taf/exhaustive_test.dart @@ -31,6 +31,36 @@ void main() { 'TAF KADW 121700Z 1217/1323 29010G15KT 9999 SCT020 BKN040 OVC085 QNH3004INS TEMPO 1217/1221 29015G25KT 510002 BECMG 1220/1221 VRB06KT 9999 SCT030 BKN050 QNH3003INS BECMG 1315/1316 20010G15KT 9999 FEW060 SCT150 BKN200 QNH2992INS BECMG 1320/1321 21020G30KT 9999 FEW050 FEW110 BKN160 510003 QNH2990INS TX24/1317Z TN13/1308Z'; taf = Taf(code4); expect(taf.warnings.length, 0); + + final code5 = + 'TAF AMD KBAB 121420Z 1214/1317 34006KT 9999 FEW200 QNH3012INS WND 310V030 BECMG 1215/1216 32009KT 9999 FEW060 QNH3015INS BECMG 1221/1222 24012KT 9999 SCT050 BKN080 620804 QNH3015INS BECMG 1302/1303 20008KT 9999 SCT070 QNH3007INS BECMG 1309/1310 16007KT 9999 SCT030 BKN070 620703 QNH3019INS TX15/1300Z TN01/1215Z'; + taf = Taf(code5); + expect(taf.warnings.length, 0); + + final code6 = + 'TAF KBIF 121400Z 1214/1320 24012KT 9999 SKC 530054 QNH2983INS BECMG 1215/1216 25030G40KT 4800 BLDU SKC 530074 QNH2973INS TEMPO 1219/1301 24030G50KT 1600 BLDU FEW090 BECMG 1300/1301 24035G45KT 9999 NSW SCT090 530064 QNH2965INS BECMG 1301/1302 24020KT 9999 SCT100 530054 QNH2966INS BECMG 1307/1308 28015KT 9999 SKC 530054 QNH2982INS TX26/1222Z TN05/1313Z'; + taf = Taf(code6); + expect(taf.warnings.length, 0); + + final code7 = + 'TAF HRYR 121700Z 1218/1324 34003KT 9999 FEW026 FM 130800 17006KT -RA BKN023 BKN090 BECMG 1310/1314 SCT025 TEMPO 1315/1319 27010KT 5000 TSRA FEW020CB BKN023'; + taf = Taf(code7); + expect(taf.warnings.length, 0); + + final code8 = + 'TAF COR KBLV 121601Z 1216/1322 15009KT 9999 VCSH OVC005 QNH2980INS BECMG 1217/1219 17018G25KT 9999 NSW OVC020 510003 QNH2973INS BECMG 1220/1222 18012KT 9999 OVC020 QNH2960INS BECMG 1311/1313 18012KT 9999 VCTS OVC015CB QNH2945INS TEMPO 1318/1321 20030G45KT 3200 +TSRAGR BKN009CB OVC015 TX24/1223Z TN18/1311Z'; + taf = Taf(code8); + expect(taf.warnings.length, 0); + + // final code9 = + // 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z'; + // taf = Taf(code9); + // expect(taf.warnings.length, 0); + + final code10 = + 'KCTB 121746Z 1218/1318 01022G32KT 1 1/2SM -SN OVC015 FM122000 01022G30KT P6SM OVC025 TEMPO 1220/1222 4SM -SN OVC015 FM130200 36016KT P6SM OVC015 PROB30 1303/1309 4SM -SN BR OVC009 FM130900 35014KT P6SM OVC025 PROB30 1315/1318 4SM -SN BR OVC009'; + taf = Taf(code10); + expect(taf.warnings.length, 0); })); test('exhaustive', (() { @@ -160,7 +190,6 @@ void main() { 'TAF KCBM 121700Z 1217/1323 18012G20KT 9999 BKN025 QNH3001INS BECMG 1217/1218 18012G20KT 9999 SCT035 QNH2990INS TEMPO 1220/1222 18015G25KT BECMG 1222/1223 18009KT 8000 -SHRA BKN025 QNH2985INS BECMG 1306/1307 18012KT 9999 NSW BKN012 QNH2985INS TEMPO 1310/1313 18010G15KT 8000 -TSRA BKN005CB BECMG 1314/1315 18012G20KT 9999 SCT015 BKN030 QNH2976INS BECMG 1322/1323 18015G25KT 6000 TSRA OVC025CB QNH2975INS TX27/1221Z TN19/1311Z', 'KCDC 121738Z 1218/1318 36005KT P6SM SKC FM122200 31011G22KT P6SM SCT080 FM130500 VRB06KT P6SM FEW200 FM131500 25011KT P6SM FEW100 SCT180', 'KCDS 121720Z 1218/1318 22020G31KT P6SM FEW250 FM122100 22025G35KT P6SM SKC TEMPO 1221/1301 3SM BLDU BKN030 FM130100 22020KT P6SM FEW250 FM130900 30015KT P6SM FEW250', - 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z', 'KCHA 121720Z 1218/1318 18007KT P6SM VCSH BKN025 FM121930 18009KT P6SM SCT045 FM130000 18005KT P6SM BKN250 FM131000 19004KT P6SM VCSH SCT040 BKN100 FM131600 18012G22KT P6SM SCT040 BKN150', 'KCHS 121739Z 1218/1318 22009KT P6SM FEW100 FM121930 19012G17KT P6SM FEW150 FM130100 19005KT P6SM FEW250 FM131530 21011G17KT P6SM FEW250', 'KCLE 121752Z 1218/1324 04008KT P6SM FEW250 FM130100 12010KT P6SM BKN100 FM130600 17014KT P6SM OVC040 FM131100 19013G21KT P6SM BKN050 FM131700 20016G27KT P6SM -SHRA OVC050 FM132200 20019G30KT P6SM BKN050', @@ -190,7 +219,6 @@ void main() { 'TAF AMD KDLF 121546Z 1215/1317 12012G18KT 9999 SCT012 BKN015 BKN150 QNH2972INS BECMG 1216/1217 15009KT 9999 SCT020 SCT180 QNH2966INS BECMG 1217/1218 21012G18KT 9999 SKC QNH2948INS BECMG 1302/1303 15008KT 9999 SKC QNH2953INS BECMG 1311/1312 33015G25KT 9999 SKC QNH2953INS BECMG 1315/1316 35020G30KT 9999 SKC QNH2950INS TX35/1221Z TN18/1312Z', 'KDLH 121737Z 1218/1318 10010G20KT P6SM SCT050 OVC070 FM121900 08013G23KT P6SM VCSH SCT025 OVC060 FM122200 08015G27KT 3SM -SHRA BR BKN015 OVC040 FM130300 08020G33KT 2SM -SHRA BR OVC003 WS020/10040KT TEMPO 1304/1306 1SM -TSRA BR BKN015CB FM131000 08019G32KT 2SM BR VCSH OVC002 FM131200 09010G18KT 1 1/2SM BR OVC003', 'TAF AMD KDMA 121455Z 1214/1320 21015G26KT 9999 SKC 510009 QNH2980INS BECMG 1216/1217 28020G26KT 9999 SCT060 510009 QNH2976INS TEMPO 1220/1301 31025G30KT 510009 BECMG 1301/1302 31010KT 9999 SKC QNH2979INS BECMG 1309/1310 13009KT 9999 SKC QNH2999INS TX25/1222Z TN39/1312Z', - 'TAF KDOV 121600Z 1216/1322 28012KT 9999 FEW050 QNH3007INS WND VRB06KT AFT 1220 TEMPO 1216/1217 28012G20KT BECMG 1303/1304 16006KT 9999 BKN040 QNH3004INS BECMG 1317/1318 20006KT 9999 BKN100 QNH2994INS TX22/1218Z TN12/1310Z', 'KDPA 121736Z 1218/1318 13012G20KT P6SM BKN250 FM121900 15013G20KT P6SM BKN040 BKN150 FM122200 16017G26KT P6SM SCT010 OVC020 FM130100 18019G28KT P6SM OVC010 WS015/20050KT FM130400 18018G28KT P6SM BKN014 WS020/20045KT FM131200 19015G23KT P6SM BKN020 PROB30 1312/1318 4SM TSRA BR OVC015CB', 'KDRO 121816Z 1218/1318 27015G20KT P6SM VCSH SCT025 BKN040 OVC050 TEMPO 1218/1220 -SHRASN BKN025 FM122200 27020G35KT P6SM FEW040 SCT060 FM130200 31010G18KT P6SM FEW100 FM130800 28008KT P6SM FEW070 FEW100 FM131600 27014G24KT P6SM FEW100', 'KDRT 121720Z 1218/1318 15012KT P6SM FEW200 FM121830 22012G22KT P6SM SKC FM130100 21009KT P6SM SKC FM130400 12009KT P6SM SKC FM131500 33015G25KT P6SM SKC', @@ -312,7 +340,6 @@ void main() { 'TAF KMGE 121300Z 1213/1319 19007KT 9999 BKN080 BKN180 QNH3010INS BECMG 1214/1215 20010G15KT 9999 SCT060 SCT120 BKN200 QNH3005INS BECMG 1222/1223 17006KT 9999 SCT120 BKN200 QNH3003INS BECMG 1314/1315 17010G15KT 9999 BKN200 QNH3005INS BECMG 1316/1317 17015G25KT 9999 BKN200 QNH3001INS TX27/1221Z TN15/1311Z', 'KMHR 121739Z 1218/1318 25006KT P6SM SKC FM130000 21007KT P6SM SCT070 FM130900 14005KT P6SM OVC200', 'KMIA 121720Z 1218/1324 11015KT P6SM SCT035 SCT050 FM122300 10012KT P6SM SCT040 SCT250 FM131400 12012KT P6SM SCT035 BKN100', - 'TAF AMD KMIB 121446Z 1214/1315 VRB06KT 9999 OVC100 QNH2985INS BECMG 1214/1215 05012G18KT 3200 -SN BKN005 OVC030 620059 QNH2983INS BECMG 1216/1217 06015G25KT 0800 SN BKN005 OVC030 620059 510003 QNH2977INS BECMG 1223/1224 03025G35KT 0400 +SN BKN005 OVC010 620059 510003 QNH2975INS BECMG 1303/1304 03025G35KT 0200 +SN BKN005 OVC010 620059 510003 QNH2964INS TXM02/1214Z TNM05/1309Z LAST NO AMDS AFT 1215 NEXT 1509', 'KMIV 121725Z 1218/1318 31010G18KT P6SM FEW070 FM122300 VRB03KT P6SM FEW250 FM130600 VRB03KT P6SM BKN060 FM131300 16006KT P6SM BKN080', 'KMKE 121745Z 1218/1324 14013KT P6SM SCT080 OVC150 FM130000 13015G26KT P6SM OVC035 FM130200 15015G23KT P6SM OVC013 WS020/17045KT FM130700 18013KT P6SM OVC021 WS020/21045KT FM131100 19011G19KT P6SM OVC024', 'KMKL 121729Z 1218/1318 20016G22KT P6SM BKN035 FM130600 18016G24KT P6SM VCSH OVC060 FM130900 18017G25KT P6SM -SHRA VCTS OVC025CB FM131200 20017G24KT P6SM VCSH SCT025 OVC040', @@ -392,7 +419,6 @@ void main() { 'KRIC 121720Z 1218/1318 26009KT P6SM SCT120 FM130100 17003KT P6SM BKN100 FM131000 21008KT P6SM SKC FM131500 22013G21KT P6SM SCT250', 'TAF KRIV 121212Z 1212/1318 33009KT 9999 VCSH BKN015 520009 QNH2988INS BECMG 1213/1214 33010G15KT 9999 NSW SCT015 520009 QNH2989INS TEMPO 1214/1219 34015G25KT BECMG 1219/1220 33009KT 9999 SKC 510006 QNH2993INS TEMPO 1222/1302 32012G20KT BECMG 1304/1305 16006KT 9999 SKC QNH3003INS TX19/1222Z TN00/1312Z', 'KRME 121734Z 1218/1318 28013G19KT P6SM FEW040 FM130200 00000KT P6SM SCT250 FM131100 11008G13KT P6SM BKN040 WS020/19030KT FM131600 10008G13KT P6SM BKN035', - 'TAF KRND 121611Z 1216/1322 16012G25KT 9999 SCT025 SCT110 SCT200 QNH2966INS TEMPO 1219/1300 9000 -SHRA VCTS BKN030CB BKN150 BECMG 1306/1307 15009KT 8000 -SHRA BR SCT009 BKN018 QNH2962INS BECMG 1311/1312 18006KT 8000 -DZ BR BKN007 OVC011 QNH2968INS BECMG 1315/1316 20009KT 8000 BR SCT009 OVC016 QNH2977INS BECMG 1316/1317 26010G15KT 9999 NSW SCT016 QNH2980INS BECMG 1317/1318 32012G22KT 9999 SKC QNH2973INS TX33/1220Z TN21/1312Z LAST NO AMDS AFT 1300 NEXT 1310', 'KRNO 121739Z 1218/1318 30007KT P6SM SCT050 FM122100 28012G22KT P6SM SCT060 FM130900 27008KT P6SM SCT100 BKN180', 'KROC 121737Z 1218/1318 33008KT P6SM FEW250 FM121900 29007KT P6SM FEW250 FM122300 24005KT P6SM FEW250 FM131000 16011G19KT P6SM BKN250 FM131200 18014G26KT P6SM BKN250', 'KROW 121735Z 1218/1318 21020G30KT P6SM FEW200 FM121900 23028G40KT P6SM FEW100 TEMPO 1221/1301 23034G46KT 3SM BLDU FM130200 25018G30KT P6SM SKC FM130800 29014G24KT P6SM SKC FM131600 31020G32KT P6SM SKC', @@ -440,7 +466,6 @@ void main() { 'KTYS 121720Z 1218/1318 20006KT P6SM -SHRA OVC025 FM121900 23009KT P6SM VCSH BKN035 TEMPO 1219/1220 BKN025 FM122200 23008KT P6SM SCT040 FM130000 21006KT P6SM BKN250 FM131100 18006KT P6SM VCSH SCT040 BKN100 FM131600 22015G25KT P6SM SCT040 BKN140', 'TAF KVAD 121600Z 1216/1322 18010G15KT 9999 SKC QNH3012INS BECMG 1221/1222 18006KT 9999 SKC QNH3012INS BECMG 1305/1306 14006KT 9999 SCT180 QNH3011INS BECMG 1313/1314 16010G15KT 9999 SCT180 QNH3003INS TX29/1220Z TN17/1306Z', 'TAF KVBG 121400Z 1214/1320 01009KT 9999 SKC QNH3011INS BECMG 1217/1218 34015G25KT 9999 SKC 520006 QNH3012INS BECMG 1222/1223 34022G32KT 9999 SKC 520006 QNH3006INS TEMPO 1223/1304 34028G38KT 530008 BECMG 1303/1304 36015KT 9999 FEW005 FEW250 QNH3000INS BECMG 1318/1319 34015G25KT 9999 FEW250 520006 QNH3004INS TX13/1220Z TN03/1314Z', - 'TAF KVPS 121700Z 1217/1323 13015KT 9999 BKN014 BKN045 BKN120 QNH3014INS TEMPO 1217/1219 SCT014 BKN045 BECMG 1218/1219 14013G18KT 9999 SCT015 BKN045 QNH3007INS WND 15014KT AFT 1222 BECMG 1302/1303 14012KT 8000 BR SCT005 BKN018 BKN180 QNH3004INS BECMG 1307/1308 13007KT 4800 BR SCT005 BKN012 BKN180 QNH3002INS BECMG 1311/1312 14013KT 6000 BR BKN005 OVC045 QNH3005INS BECMG 1313/1314 12012KT 9999 NSW FEW005 BKN035 QNH3004INS BECMG 1314/1315 13015KT 9999 BKN026 BKN035 QNH3000INS BECMG 1317/1318 14018G28KT 9999 SCT030 BKN200 QNH2996INS TX26/1218Z TN18/1311Z', 'KVRB 121725Z 1218/1318 12014G22KT P6SM SCT040 FM130100 13009KT P6SM SCT035 FM131500 14013KT P6SM SCT035 SCT250', 'TAF KWRB 121600Z 1216/1322 20010G15KT 9999 FEW150 SCT200 QNH3018INS BECMG 1303/1304 VRB06KT 9999 FEW200 QNH3006INS BECMG 1314/1315 17010G20KT 9999 SCT100 BKN180 QNH2998INS TX27/1220Z TN14/1308Z', 'TAF KWRI 121800Z 1218/1324 30010G18KT 9999 FEW060 QNH3000INS BECMG 1222/1223 27006KT 9999 FEW250 QNH3007INS BECMG 1304/1305 VRB03KT 9999 BKN050 QNH3005INS BECMG 1313/1314 15010G15KT 9999 BKN060 QNH2987INS BECMG 1321/1322 20010KT 9999 FEW060 SCT120 QNH2986INS TX24/1318Z TN11/1310Z', @@ -567,6 +592,11 @@ void main() { 'TAF LFRS 121700Z 1218/1324 14005KT 9999 SCT030 TEMPO 1300/1309 3000 BR BKN003 PROB40 TEMPO 1300/1306 0300 FG VV/// BECMG 1308/1311 NSW SCT020 TEMPO 1312/1318 4000 -SHRA SCT025CB', 'TAF LFRZ 121700Z 1218/1318 21006KT 9999 SCT030 BECMG 1300/1302 2000 BR BKN002 TEMPO 1302/1307 0300 FG VV/// BECMG 1308/1311 9999 NSW SCT020', 'TAF LFSB 121700Z 1218/1318 15003KT CAVOK PROB30 TEMPO 1312/1318 34010KT FEW050TCU', + 'TAF KRND 121611Z 1216/1322 16012G25KT 9999 SCT025 SCT110 SCT200 QNH2966INS TEMPO 1219/1300 9000 -SHRA VCTS BKN030CB BKN150 BECMG 1306/1307 15009KT 8000 -SHRA BR SCT009 BKN018 QNH2962INS BECMG 1311/1312 18006KT 8000 -DZ BR BKN007 OVC011 QNH2968INS BECMG 1315/1316 20009KT 8000 BR SCT009 OVC016 QNH2977INS BECMG 1316/1317 26010G15KT 9999 NSW SCT016 QNH2980INS BECMG 1317/1318 32012G22KT 9999 SKC QNH2973INS TX33/1220Z TN21/1312Z LAST NO AMDS AFT 1300 NEXT 1310', + 'TAF AMD KMIB 121446Z 1214/1315 VRB06KT 9999 OVC100 QNH2985INS BECMG 1214/1215 05012G18KT 3200 -SN BKN005 OVC030 620059 QNH2983INS BECMG 1216/1217 06015G25KT 0800 SN BKN005 OVC030 620059 510003 QNH2977INS BECMG 1223/1224 03025G35KT 0400 +SN BKN005 OVC010 620059 510003 QNH2975INS BECMG 1303/1304 03025G35KT 0200 +SN BKN005 OVC010 620059 510003 QNH2964INS TXM02/1214Z TNM05/1309Z LAST NO AMDS AFT 1215 NEXT 1509', + 'TAF KDOV 121600Z 1216/1322 28012KT 9999 FEW050 QNH3007INS WND VRB06KT AFT 1220 TEMPO 1216/1217 28012G20KT BECMG 1303/1304 16006KT 9999 BKN040 QNH3004INS BECMG 1317/1318 20006KT 9999 BKN100 QNH2994INS TX22/1218Z TN12/1310Z', + 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z', + 'TAF KVPS 121700Z 1217/1323 13015KT 9999 BKN014 BKN045 BKN120 QNH3014INS TEMPO 1217/1219 SCT014 BKN045 BECMG 1218/1219 14013G18KT 9999 SCT015 BKN045 QNH3007INS WND 15014KT AFT 1222 BECMG 1302/1303 14012KT 8000 BR SCT005 BKN018 BKN180 QNH3004INS BECMG 1307/1308 13007KT 4800 BR SCT005 BKN012 BKN180 QNH3002INS BECMG 1311/1312 14013KT 6000 BR BKN005 OVC045 QNH3005INS BECMG 1313/1314 12012KT 9999 NSW FEW005 BKN035 QNH3004INS BECMG 1314/1315 13015KT 9999 BKN026 BKN035 QNH3000INS BECMG 1317/1318 14018G28KT 9999 SCT030 BKN200 QNH2996INS TX26/1218Z TN18/1311Z', ]; var warnings = 0; From 84bb73a3fa72e675547de0a6ebb390b5dcee8f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Wed, 13 Apr 2022 13:45:26 -0300 Subject: [PATCH 11/17] ammendments and sanitise visibility --- .../models/metar/models/weather_trend.dart | 3 +- lib/src/models/models.dart | 1 + lib/src/models/taf/models/ammendments.dart | 85 +++++++++++++++++++ .../models/taf/models/change_forecast.dart | 3 + lib/src/models/taf/taf.dart | 3 +- lib/src/utils/taf_regexp.dart | 3 + test/taf/exhaustive_test.dart | 8 +- 7 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 lib/src/models/taf/models/ammendments.dart diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index 9a66c8f..cef8216 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -12,7 +12,8 @@ class Forecast extends Group TafTurbulenceMixin, PressureMixin, TafTemperatureMixin, - TafIcingMixin { + TafIcingMixin, + TafAmmendmentsMixin { final _unparsedGroups = []; Forecast(String code) : super(code); diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index 6d05789..f2a4c5f 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -49,4 +49,5 @@ part 'taf/models/valid.dart'; part 'taf/models/windshear.dart'; part 'taf/models/turbulence.dart'; part 'taf/models/icing.dart'; +part 'taf/models/ammendments.dart'; part 'taf/taf.dart'; diff --git a/lib/src/models/taf/models/ammendments.dart b/lib/src/models/taf/models/ammendments.dart new file mode 100644 index 0000000..2515839 --- /dev/null +++ b/lib/src/models/taf/models/ammendments.dart @@ -0,0 +1,85 @@ +part of models; + +/// Basic structure for valid time groups in change periods and forecasts. +class TafAmmendments extends Group { + TafAmmendments(String? code) : super(code); + + DateTime? after, next; + + /// Named constructor of the TafAmmendments clas + /// + /// Args: + /// code (String?): the code of the group. + /// match (RegExpMatch?): the match of the regular expression. + TafAmmendments.fromTaf(String? code, RegExpMatch? match) : super(code) { + if (match != null) { + final afterd = match.namedGroup('afterd') ?? match.namedGroup('afterd2'); + final afterh = match.namedGroup('afterh') ?? match.namedGroup('afterh2'); + final nextd = match.namedGroup('nextd'); + final nexth = match.namedGroup('nextd'); + + after = DateTime( + DateTime.now().year, + DateTime.now().month, + int.parse(afterd!), + int.parse(afterh!), + 0, + ); + + if (nextd != null && nexth != null) { + next = DateTime( + DateTime.now().year, + DateTime.now().month, + int.parse(nextd), + int.parse(nexth), + 0, + ); + } + } + } + + @override + String toString() { + if (next != null) { + return 'No ammendments between ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)} and ${next!.hour}:00 UTC on the ${next!.day}${daySuffix(next!.day)}'; + } + return 'No ammendments after ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)}'; + } + + String daySuffix(int day) { + switch (day) { + case 1: + case 21: + case 31: + return 'st'; + case 2: + case 22: + return 'nd'; + case 3: + case 23: + return 'rd'; + default: + return 'th'; + } + } +} + +/// Mixin to add the valid period of forecast attribute and handler. +mixin TafAmmendmentsMixin on StringAttributeMixin { + late final TafAmmendments _ammendment; + + void _handleAmmendment(String group) { + final match = TafRegExp.AMMENDMENTS.firstMatch(group); + _ammendment = TafAmmendments.fromTaf(group, match); + _concatenateString(_ammendment); + } + + TafAmmendments get ammendment => _ammendment; +} + +String sanitizeAmmendments(String code) { + return code + .replaceAll(' LAST NO AMDS AFT ', ' LAST_NO_AMDS_AFT_') + .replaceAll(' AFT ', ' AFT_') + .replaceAll(' NEXT ', '_NEXT_'); +} diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 1f80c50..b2ceba6 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -53,11 +53,13 @@ class ChangeForecast extends Forecast { GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(MetarRegExp.CLOUD, _handleCloud), + GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), GroupHandler(TafRegExp.ICING, _handleIcing), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(TafRegExp.WIND, _handleWind), + GroupHandler(TafRegExp.AMMENDMENTS, _handleAmmendment), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _valid.periodFrom.time)), GroupHandler(TafRegExp.TEMPERATURE, @@ -72,6 +74,7 @@ class ChangeForecast extends Forecast { var sanitizedCode = sanitizeChangeIndicator(_code!); sanitizedCode = sanitizeVisibility(sanitizedCode); sanitizedCode = sanitizeWindToken(sanitizedCode); + sanitizedCode = sanitizeAmmendments(sanitizedCode); final unparsed = parseSection(handlers, sanitizedCode, onWarning: onWarning); _unparsedGroups.addAll(unparsed); diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index a8454a7..250f69a 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -138,7 +138,8 @@ class Taf extends Report GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; - final body = sanitizeWindToken(_body); + var body = sanitizeWindToken(_body); + body = sanitizeVisibility(body); final unparsed = parseSection(handlers, body, onWarning: (warning) { warnings.add(warning); diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index 55c5da8..0fb444e 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -46,4 +46,7 @@ class TafRegExp { RegExp(r'^(5(?[0-9])(?\d\d\d)(?\d))'); static final RegExp ICING = RegExp(r'^(6(?[0-9])(?\d\d\d)(?\d))'); + + static final RegExp AMMENDMENTS = RegExp( + r'^(LAST_NO_AMDS_AFT_(?\d{2})(?\d{2})_NEXT(?\d{2})(?\d{2}))|AFT_(?\d{2})(?\d{2})'); } diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart index dad4ef8..a61c5f6 100644 --- a/test/taf/exhaustive_test.dart +++ b/test/taf/exhaustive_test.dart @@ -52,10 +52,10 @@ void main() { taf = Taf(code8); expect(taf.warnings.length, 0); - // final code9 = - // 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z'; - // taf = Taf(code9); - // expect(taf.warnings.length, 0); + final code9 = + 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z'; + taf = Taf(code9); + expect(taf.warnings.length, 0); final code10 = 'KCTB 121746Z 1218/1318 01022G32KT 1 1/2SM -SN OVC015 FM122000 01022G30KT P6SM OVC025 TEMPO 1220/1222 4SM -SN OVC015 FM130200 36016KT P6SM OVC015 PROB30 1303/1309 4SM -SN BR OVC009 FM130900 35014KT P6SM OVC025 PROB30 1315/1318 4SM -SN BR OVC009'; From d48bdd3cff25ddd3b9bcc3ca68903e3f7888e0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Wed, 13 Apr 2022 14:00:40 -0300 Subject: [PATCH 12/17] Amendments tweaks --- .../models/metar/models/weather_trend.dart | 2 +- lib/src/models/models.dart | 2 +- .../{ammendments.dart => amendments.dart} | 40 ++++++++++------ .../models/taf/models/change_forecast.dart | 8 +++- lib/src/models/taf/taf.dart | 4 ++ lib/src/utils/taf_regexp.dart | 2 +- test/taf/exhaustive_test.dart | 48 +------------------ 7 files changed, 39 insertions(+), 67 deletions(-) rename lib/src/models/taf/models/{ammendments.dart => amendments.dart} (55%) diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index cef8216..197ece5 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -13,7 +13,7 @@ class Forecast extends Group PressureMixin, TafTemperatureMixin, TafIcingMixin, - TafAmmendmentsMixin { + TafAmendmentsMixin { final _unparsedGroups = []; Forecast(String code) : super(code); diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index f2a4c5f..1291aa3 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -49,5 +49,5 @@ part 'taf/models/valid.dart'; part 'taf/models/windshear.dart'; part 'taf/models/turbulence.dart'; part 'taf/models/icing.dart'; -part 'taf/models/ammendments.dart'; +part 'taf/models/amendments.dart'; part 'taf/taf.dart'; diff --git a/lib/src/models/taf/models/ammendments.dart b/lib/src/models/taf/models/amendments.dart similarity index 55% rename from lib/src/models/taf/models/ammendments.dart rename to lib/src/models/taf/models/amendments.dart index 2515839..78176b3 100644 --- a/lib/src/models/taf/models/ammendments.dart +++ b/lib/src/models/taf/models/amendments.dart @@ -1,23 +1,29 @@ part of models; /// Basic structure for valid time groups in change periods and forecasts. -class TafAmmendments extends Group { - TafAmmendments(String? code) : super(code); +class TafAmendments extends Group { + TafAmendments(String? code) : super(code); DateTime? after, next; + bool nextNotSked = false; - /// Named constructor of the TafAmmendments clas + /// Named constructor of the TafAmendments clas /// /// Args: /// code (String?): the code of the group. /// match (RegExpMatch?): the match of the regular expression. - TafAmmendments.fromTaf(String? code, RegExpMatch? match) : super(code) { + TafAmendments.fromTaf(String? code, RegExpMatch? match) : super(code) { if (match != null) { final afterd = match.namedGroup('afterd') ?? match.namedGroup('afterd2'); final afterh = match.namedGroup('afterh') ?? match.namedGroup('afterh2'); final nextd = match.namedGroup('nextd'); final nexth = match.namedGroup('nextd'); + if (match.input == 'AMD_NOT_SKED' || (afterd == null && nexth == null)) { + nextNotSked = true; + return; + } + after = DateTime( DateTime.now().year, DateTime.now().month, @@ -40,10 +46,13 @@ class TafAmmendments extends Group { @override String toString() { + if (nextNotSked) { + return 'AMD_NOT_SKED'; + } if (next != null) { - return 'No ammendments between ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)} and ${next!.hour}:00 UTC on the ${next!.day}${daySuffix(next!.day)}'; + return 'No amendments between ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)} and ${next!.hour}:00 UTC on the ${next!.day}${daySuffix(next!.day)}'; } - return 'No ammendments after ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)}'; + return 'No amendments after ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)}'; } String daySuffix(int day) { @@ -65,21 +74,22 @@ class TafAmmendments extends Group { } /// Mixin to add the valid period of forecast attribute and handler. -mixin TafAmmendmentsMixin on StringAttributeMixin { - late final TafAmmendments _ammendment; +mixin TafAmendmentsMixin on StringAttributeMixin { + late final TafAmendments _amendment; - void _handleAmmendment(String group) { - final match = TafRegExp.AMMENDMENTS.firstMatch(group); - _ammendment = TafAmmendments.fromTaf(group, match); - _concatenateString(_ammendment); + void _handleAmendment(String group) { + final match = TafRegExp.AMENDMENTS.firstMatch(group); + _amendment = TafAmendments.fromTaf(group, match); + _concatenateString(_amendment); } - TafAmmendments get ammendment => _ammendment; + TafAmendments get ammendment => _amendment; } -String sanitizeAmmendments(String code) { +String sanitizeAmendments(String code) { return code .replaceAll(' LAST NO AMDS AFT ', ' LAST_NO_AMDS_AFT_') .replaceAll(' AFT ', ' AFT_') - .replaceAll(' NEXT ', '_NEXT_'); + .replaceAll(' NEXT ', '_NEXT_') + .replaceAll(' AMD NOT SKED', ' AMD_NOT_SKED'); } diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index b2ceba6..2e9d023 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -56,10 +56,14 @@ class ChangeForecast extends Forecast { GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), GroupHandler(TafRegExp.ICING, _handleIcing), + GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(TafRegExp.ICING, _handleIcing), + GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(TafRegExp.ICING, _handleIcing), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(TafRegExp.WIND, _handleWind), - GroupHandler(TafRegExp.AMMENDMENTS, _handleAmmendment), + GroupHandler(TafRegExp.AMENDMENTS, _handleAmendment), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _valid.periodFrom.time)), GroupHandler(TafRegExp.TEMPERATURE, @@ -74,7 +78,7 @@ class ChangeForecast extends Forecast { var sanitizedCode = sanitizeChangeIndicator(_code!); sanitizedCode = sanitizeVisibility(sanitizedCode); sanitizedCode = sanitizeWindToken(sanitizedCode); - sanitizedCode = sanitizeAmmendments(sanitizedCode); + sanitizedCode = sanitizeAmendments(sanitizedCode); final unparsed = parseSection(handlers, sanitizedCode, onWarning: onWarning); _unparsedGroups.addAll(unparsed); diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index 250f69a..4906dbc 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -124,6 +124,10 @@ class Taf extends Report GroupHandler(MetarRegExp.CLOUD, _handleCloud), GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), GroupHandler(TafRegExp.ICING, _handleIcing), + GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(TafRegExp.ICING, _handleIcing), + GroupHandler(TafRegExp.TURBULENCE, _handleTurbulence), + GroupHandler(TafRegExp.ICING, _handleIcing), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.WIND_VARIATION, _handleWindVariation), diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index 0fb444e..772acc7 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -48,5 +48,5 @@ class TafRegExp { RegExp(r'^(6(?[0-9])(?\d\d\d)(?\d))'); static final RegExp AMMENDMENTS = RegExp( - r'^(LAST_NO_AMDS_AFT_(?\d{2})(?\d{2})_NEXT(?\d{2})(?\d{2}))|AFT_(?\d{2})(?\d{2})'); + r'^(LAST_NO_AMDS_AFT_(?\d{2})(?\d{2})_NEXT(?\d{2})(?\d{2}))|AFT_(?\d{2})(?\d{2})|AMD_NOT_SKED'); } diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart index a61c5f6..924f68f 100644 --- a/test/taf/exhaustive_test.dart +++ b/test/taf/exhaustive_test.dart @@ -12,55 +12,9 @@ void main() { test('test faling case', (() { final code = - 'KABI 121740Z 1218/1318 22017G25KT P6SM SCT250 FM130200 17010KT P6SM SKC FM131200 24013G20KT P6SM SKC'; + 'KHPN 121720Z 1218/1318 31011G18KT P6SM SCT060 FM122100 31009KT P6SM FEW060 FM130100 VRB03KT P6SM FEW050 FM131500 14007KT P6SM BKN050 AMD NOT SKED'; var taf = Taf(code); expect(taf.warnings.length, 0); - - final code2 = - 'TAF GFLL 121800Z 1218/1324 24008KT 9999 BKN016 TEMPO 1224/1305 29006KT 7000 HZ BKN010 FEW027CB BECMG 1312/1314 27010KT 9999 SCT012'; - taf = Taf(code2); - expect(taf.warnings.length, 0); - - final code3 = - 'TAF GMMN 121700Z 1218/1324 32013KT 9999 BKN026 BECMG 1221/1223 20006KT 9999 BKN020 SCT100 PROB30 TEMPO 1306/1309 36006KT RA 5000 BKN020 BKN080'; - taf = Taf(code3); - expect(taf.warnings.length, 0); - expect(taf.changePeriods[1].prevailingVisibility.inMeters, 5000); - - final code4 = - 'TAF KADW 121700Z 1217/1323 29010G15KT 9999 SCT020 BKN040 OVC085 QNH3004INS TEMPO 1217/1221 29015G25KT 510002 BECMG 1220/1221 VRB06KT 9999 SCT030 BKN050 QNH3003INS BECMG 1315/1316 20010G15KT 9999 FEW060 SCT150 BKN200 QNH2992INS BECMG 1320/1321 21020G30KT 9999 FEW050 FEW110 BKN160 510003 QNH2990INS TX24/1317Z TN13/1308Z'; - taf = Taf(code4); - expect(taf.warnings.length, 0); - - final code5 = - 'TAF AMD KBAB 121420Z 1214/1317 34006KT 9999 FEW200 QNH3012INS WND 310V030 BECMG 1215/1216 32009KT 9999 FEW060 QNH3015INS BECMG 1221/1222 24012KT 9999 SCT050 BKN080 620804 QNH3015INS BECMG 1302/1303 20008KT 9999 SCT070 QNH3007INS BECMG 1309/1310 16007KT 9999 SCT030 BKN070 620703 QNH3019INS TX15/1300Z TN01/1215Z'; - taf = Taf(code5); - expect(taf.warnings.length, 0); - - final code6 = - 'TAF KBIF 121400Z 1214/1320 24012KT 9999 SKC 530054 QNH2983INS BECMG 1215/1216 25030G40KT 4800 BLDU SKC 530074 QNH2973INS TEMPO 1219/1301 24030G50KT 1600 BLDU FEW090 BECMG 1300/1301 24035G45KT 9999 NSW SCT090 530064 QNH2965INS BECMG 1301/1302 24020KT 9999 SCT100 530054 QNH2966INS BECMG 1307/1308 28015KT 9999 SKC 530054 QNH2982INS TX26/1222Z TN05/1313Z'; - taf = Taf(code6); - expect(taf.warnings.length, 0); - - final code7 = - 'TAF HRYR 121700Z 1218/1324 34003KT 9999 FEW026 FM 130800 17006KT -RA BKN023 BKN090 BECMG 1310/1314 SCT025 TEMPO 1315/1319 27010KT 5000 TSRA FEW020CB BKN023'; - taf = Taf(code7); - expect(taf.warnings.length, 0); - - final code8 = - 'TAF COR KBLV 121601Z 1216/1322 15009KT 9999 VCSH OVC005 QNH2980INS BECMG 1217/1219 17018G25KT 9999 NSW OVC020 510003 QNH2973INS BECMG 1220/1222 18012KT 9999 OVC020 QNH2960INS BECMG 1311/1313 18012KT 9999 VCTS OVC015CB QNH2945INS TEMPO 1318/1321 20030G45KT 3200 +TSRAGR BKN009CB OVC015 TX24/1223Z TN18/1311Z'; - taf = Taf(code8); - expect(taf.warnings.length, 0); - - final code9 = - 'TAF KCEF 121800Z 1218/1324 31012KT 9999 SCT050 SCT200 510009 QNH2996INS TEMPO 1220/1223 34012G22KT 520004 510405 BECMG 1222/1223 31009KT 9999 FEW080 BKN200 QNH3001INS WND VRB06KT AFT 1302 BECMG 1314/1315 20010G15KT 9999 BKN030 510006 QNH2985INS BECMG 1321/1322 20009KT 9999 SCT020 WS020/24035KT 510204 QNH2982INS TX21/1220Z TN03/1311Z'; - taf = Taf(code9); - expect(taf.warnings.length, 0); - - final code10 = - 'KCTB 121746Z 1218/1318 01022G32KT 1 1/2SM -SN OVC015 FM122000 01022G30KT P6SM OVC025 TEMPO 1220/1222 4SM -SN OVC015 FM130200 36016KT P6SM OVC015 PROB30 1303/1309 4SM -SN BR OVC009 FM130900 35014KT P6SM OVC025 PROB30 1315/1318 4SM -SN BR OVC009'; - taf = Taf(code10); - expect(taf.warnings.length, 0); })); test('exhaustive', (() { From fab282d87010bb4eb8abcd80ed3a292aa81b1e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Thu, 14 Apr 2022 11:40:19 -0300 Subject: [PATCH 13/17] Parsing all the 532 examples --- lib/src/models/metar/metar.dart | 18 +-- .../models/metar/models/weather_trend.dart | 11 +- lib/src/models/models.dart | 1 + lib/src/models/taf/models/amendments.dart | 17 --- .../taf/models/automated_sensor_metwatch.dart | 24 ++++ .../models/taf/models/change_forecast.dart | 9 +- lib/src/models/taf/models/temperature.dart | 8 +- lib/src/models/taf/taf.dart | 115 +++++++++++++++--- lib/src/utils/metar_regexp.dart | 1 + lib/src/utils/parser.dart | 13 +- lib/src/utils/taf_regexp.dart | 17 ++- test/taf/exhaustive_test.dart | 17 +-- 12 files changed, 159 insertions(+), 92 deletions(-) create mode 100644 lib/src/models/taf/models/automated_sensor_metwatch.dart diff --git a/lib/src/models/metar/metar.dart b/lib/src/models/metar/metar.dart index edecb49..45ffb56 100644 --- a/lib/src/models/metar/metar.dart +++ b/lib/src/models/metar/metar.dart @@ -28,8 +28,6 @@ class Metar extends Report final DateTime? observedAt; - List warnings = []; - Metar( String code, { int? year, @@ -132,13 +130,7 @@ class Metar extends Report MetarRunwayState get runwayState => _runwayState; void _handleWeatherTrend(String code) { - final wt = ChangePeriod( - code, - _time.time, - onWarning: (warning) { - warnings.add(warning); - }, - ); + final wt = ChangePeriod(code, _time.time); _weatherTrends.add(wt); _concatenateString(wt); @@ -181,13 +173,7 @@ class Metar extends Report var sanitizedBody = sanitizeVisibility(body); sanitizedBody = sanitizeWindshear(sanitizedBody); - final unparsed = parseSection( - handlers, - sanitizedBody, - onWarning: (warning) { - warnings.add(warning); - }, - ); + final unparsed = parseSection(handlers, sanitizedBody); _unparsedGroups.addAll(unparsed); } diff --git a/lib/src/models/metar/models/weather_trend.dart b/lib/src/models/metar/models/weather_trend.dart index 197ece5..de7ac76 100644 --- a/lib/src/models/metar/models/weather_trend.dart +++ b/lib/src/models/metar/models/weather_trend.dart @@ -13,7 +13,8 @@ class Forecast extends Group PressureMixin, TafTemperatureMixin, TafIcingMixin, - TafAmendmentsMixin { + TafAmendmentsMixin, + MetarWindVariationMixin { final _unparsedGroups = []; Forecast(String code) : super(code); @@ -26,10 +27,7 @@ class ChangePeriod extends Forecast { late final Time _time; late MetarTrendIndicator _changeIndicator; - final Null Function(String warning) onWarning; - - ChangePeriod(String code, DateTime time, {required this.onWarning}) - : super(code) { + ChangePeriod(String code, DateTime time) : super(code) { _time = Time(time: time); // Groups _changeIndicator = MetarTrendIndicator(null, null, _time.time); @@ -74,8 +72,7 @@ class ChangePeriod extends Forecast { ]; final sanitizedCode = sanitizeVisibility(_code!); - final unparsed = - parseSection(handlers, sanitizedCode, onWarning: onWarning); + final unparsed = parseSection(handlers, sanitizedCode); _unparsedGroups.addAll(unparsed); } } diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart index 1291aa3..b74cf2a 100644 --- a/lib/src/models/models.dart +++ b/lib/src/models/models.dart @@ -50,4 +50,5 @@ part 'taf/models/windshear.dart'; part 'taf/models/turbulence.dart'; part 'taf/models/icing.dart'; part 'taf/models/amendments.dart'; +part 'taf/models/automated_sensor_metwatch.dart'; part 'taf/taf.dart'; diff --git a/lib/src/models/taf/models/amendments.dart b/lib/src/models/taf/models/amendments.dart index 78176b3..9e008dc 100644 --- a/lib/src/models/taf/models/amendments.dart +++ b/lib/src/models/taf/models/amendments.dart @@ -54,23 +54,6 @@ class TafAmendments extends Group { } return 'No amendments after ${after!.hour}:00 UTC on the ${after!.day}${daySuffix(after!.day)}'; } - - String daySuffix(int day) { - switch (day) { - case 1: - case 21: - case 31: - return 'st'; - case 2: - case 22: - return 'nd'; - case 3: - case 23: - return 'rd'; - default: - return 'th'; - } - } } /// Mixin to add the valid period of forecast attribute and handler. diff --git a/lib/src/models/taf/models/automated_sensor_metwatch.dart b/lib/src/models/taf/models/automated_sensor_metwatch.dart new file mode 100644 index 0000000..5d635b6 --- /dev/null +++ b/lib/src/models/taf/models/automated_sensor_metwatch.dart @@ -0,0 +1,24 @@ +part of models; + +/// Basic structure for missing TAF. +class AutomatedSensorMetwatch extends Modifier { + DateTime? from, to; + + AutomatedSensorMetwatch(String? code, RegExpMatch match) : super(null) { + _code = code; + + final fd = match.namedGroup('df')!; + final fh = match.namedGroup('hf')!; + final td = match.namedGroup('dt')!; + final th = match.namedGroup('ht')!; + + final n = DateTime.now().toUtc(); + from = DateTime(n.year, n.month, int.parse(fd), int.parse(fh), 0, 0); + to = DateTime(n.year, n.month, int.parse(td), int.parse(th), 0, 0); + } + + @override + String toString() { + return 'Automated Sensor Metwatch from ${from!.hour}:00 UTC on the ${from!.day}${daySuffix(from!.day)} to ${to!.hour}:00 UTC on the ${to!.day}${daySuffix(to!.day)}'; + } +} diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 2e9d023..7155214 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -4,9 +4,7 @@ part of models; class ChangeForecast extends Forecast { late final Valid _valid; late TafChangeIndicator _changeIndicator; - final Null Function(String warning) onWarning; - ChangeForecast(String code, Valid valid, {required this.onWarning}) - : super(code) { + ChangeForecast(String code, Valid valid) : super(code) { // Initialize valid period of the forecasts _valid = valid; _code = code; @@ -63,6 +61,7 @@ class ChangeForecast extends Forecast { GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(TafRegExp.WIND, _handleWind), + GroupHandler(MetarRegExp.WIND_VARIATION, _handleWindVariation), GroupHandler(TafRegExp.AMENDMENTS, _handleAmendment), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _valid.periodFrom.time)), @@ -72,6 +71,7 @@ class ChangeForecast extends Forecast { (e) => _handleTemperature(e, time: _valid.periodFrom.time)), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _valid.periodFrom.time)), + GroupHandler(TafRegExp.AMENDMENTS, _handleAmendment), GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), ]; @@ -79,8 +79,7 @@ class ChangeForecast extends Forecast { sanitizedCode = sanitizeVisibility(sanitizedCode); sanitizedCode = sanitizeWindToken(sanitizedCode); sanitizedCode = sanitizeAmendments(sanitizedCode); - final unparsed = - parseSection(handlers, sanitizedCode, onWarning: onWarning); + final unparsed = parseSection(handlers, sanitizedCode); _unparsedGroups.addAll(unparsed); } } diff --git a/lib/src/models/taf/models/temperature.dart b/lib/src/models/taf/models/temperature.dart index 7626564..abdeddf 100644 --- a/lib/src/models/taf/models/temperature.dart +++ b/lib/src/models/taf/models/temperature.dart @@ -55,6 +55,7 @@ class TafTemperatureList extends GroupList { mixin TafTemperatureMixin on StringAttributeMixin { final _maxTemperatures = TafTemperatureList(); final _minTemperatures = TafTemperatureList(); + final _temperatures = TafTemperatureList(); void _handleTemperature(String group, {required DateTime time}) { final match = TafRegExp.TEMPERATURE.firstMatch(group); @@ -62,8 +63,10 @@ mixin TafTemperatureMixin on StringAttributeMixin { if (match!.namedGroup('type') == 'X') { _maxTemperatures.add(temperature); - } else { + } else if (match.namedGroup('type') == 'N') { _minTemperatures.add(temperature); + } else { + _temperatures.add(temperature); } _concatenateString(temperature); @@ -74,4 +77,7 @@ mixin TafTemperatureMixin on StringAttributeMixin { /// Get the minimum temperature expected to happen. TafTemperatureList get minTemperatures => _minTemperatures; + + // Get the temperatures expected to happen. + TafTemperatureList get temperatures => _temperatures; } diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index 4906dbc..b9094e2 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -15,7 +15,8 @@ class Taf extends Report TafTemperatureMixin, MetarWindVariationMixin, TafTurbulenceMixin, - TafIcingMixin { + TafIcingMixin, + TafAmendmentsMixin { late final String _body; final List _changesCodes = []; int? _year, _month; @@ -23,14 +24,14 @@ class Taf extends Report // Body groups Missing _missing = Missing(null); Cancelled _cancelled = Cancelled(null); - - List warnings = []; + DateTime? _correctionTime, _amendedTime; + AutomatedSensorMetwatch? _automatedSensorMetwatch; // Change periods final _changePeriods = TafChangePeriods(); Taf(String code, {int? year, int? month, bool truncate = false}) - : super(code, truncate, type: 'TAF') { + : super(removeUnnecessaryTokens(code), truncate, type: 'TAF') { _year = year; _month = month; @@ -77,18 +78,46 @@ class Taf extends Report _concatenateString(_cancelled); } + /// Get correction time of the TAF + DateTime? get correctionTime => _correctionTime; + DateTime? get amendedTime => _amendedTime; + void _handleCorrection(String group) { + final match = TafRegExp.CORRECTION.firstMatch(group); + + if (match != null) { + final mod = match.namedGroup('mod'); + final h = int.tryParse(match.namedGroup('h') ?? ''); + final m = int.tryParse(match.namedGroup('m') ?? ''); + final n = DateTime.now().toUtc(); + if (h == null || m == null) return; + if (mod == 'COR') { + _correctionTime = DateTime(n.year, n.month, n.day, h, m); + _concatenateString('Corrected forecast at $_correctionTime\n'); + } else if (mod == 'AMD') { + _amendedTime = DateTime(n.year, n.month, n.day, h, m); + _concatenateString('Amended forecast at $_correctionTime\n'); + } + } + } + + /// Get Automated sensor metwatch times + AutomatedSensorMetwatch? get automatedSensorMetwatch => + _automatedSensorMetwatch; + void _handleAutomatedSensorMetwatch(String group) { + final match = TafRegExp.AUTOMATED_SENSOR_METWATCH.firstMatch(group); + + if (match != null) { + _automatedSensorMetwatch = AutomatedSensorMetwatch(group, match); + _concatenateString(_automatedSensorMetwatch!); + } + } + /// Get the cancelled group data of the TAF. Cancelled get cancelled => _cancelled; void _handleChangePeriod(String code) { code = sanitizeWindToken(code); - final cf = ChangeForecast( - code, - _valid, - onWarning: (warning) { - warnings.add(warning); - }, - ); + final cf = ChangeForecast(code, _valid); if (_changePeriods.length > 0) { if (cf.code!.startsWith('FM') || cf.code!.startsWith('BECMG')) { _changePeriods.last.changeIndicator @@ -130,6 +159,7 @@ class Taf extends Report GroupHandler(TafRegExp.ICING, _handleIcing), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), GroupHandler(MetarRegExp.PRESSURE, _handlePressure), + GroupHandler(TafRegExp.WIND, _handleWind), GroupHandler(MetarRegExp.WIND_VARIATION, _handleWindVariation), GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _time.time)), @@ -140,14 +170,17 @@ class Taf extends Report GroupHandler(TafRegExp.TEMPERATURE, (e) => _handleTemperature(e, time: _time.time)), GroupHandler(TafRegExp.WINDSHEAR, _handleWindshear), + GroupHandler(TafRegExp.CORRECTION, _handleCorrection), + GroupHandler( + TafRegExp.AUTOMATED_SENSOR_METWATCH, _handleAutomatedSensorMetwatch), + GroupHandler(TafRegExp.AMENDMENTS, _handleAmendment), ]; var body = sanitizeWindToken(_body); body = sanitizeVisibility(body); + body = sanitizeAmendments(body); - final unparsed = parseSection(handlers, body, onWarning: (warning) { - warnings.add(warning); - }); + final unparsed = parseSection(handlers, body); _unparsedGroups.addAll(unparsed); } @@ -185,10 +218,14 @@ class Taf extends Report ); sections[0] = sections[0].replaceFirst('TAF_', 'TAF '); - _body = sections[0]; + var b = ''; if (sections.length > 1) { _changesCodes.addAll(sections.sublist(1)); + + // Extract final info from the change periods and append it to the body + b = _extractInfoFromChangePeriods(b, _changesCodes); } + _body = '${sections[0]}$b'; _sections.add(_body); _sections.add(_changesCodes @@ -196,4 +233,52 @@ class Taf extends Report .toList() .join(' ')); } + + static String removeUnnecessaryTokens(String code) { + return code.replaceAll(RegExp(r' (FN|FS)\d{5}'), ''); + } + + String _extractInfoFromChangePeriods(String b, List changesCodes) { + final l = _changesCodes.length - 1; + if (_changesCodes[l].contains(TafRegExp.CORRECTION_UNSANITIZED)) { + final lastFor = _changesCodes[l]; + final match = TafRegExp.CORRECTION_UNSANITIZED.firstMatch(lastFor)!; + b += + ' ${match.namedGroup('mod')}_${match.namedGroup('h')}${match.namedGroup('m')}'; + _changesCodes[l] = lastFor + .replaceFirst(TafRegExp.CORRECTION_UNSANITIZED, '') + .trimRight(); + } + + if (_changesCodes[l] + .contains(TafRegExp.AUTOMATED_SENSOR_METWATCH_UNSANITIZED)) { + final lastFor = _changesCodes[l]; + final match = + TafRegExp.AUTOMATED_SENSOR_METWATCH_UNSANITIZED.firstMatch(lastFor)!; + b += + ' AUTOMATED_SENSOR_METWATCH_${match.namedGroup('f')}_TIL_${match.namedGroup('t')}'; + _changesCodes[l] = lastFor + .replaceFirst(TafRegExp.AUTOMATED_SENSOR_METWATCH_UNSANITIZED, '') + .trimRight(); + } + + return b; + } +} + +String daySuffix(int day) { + switch (day) { + case 1: + case 21: + case 31: + return 'st'; + case 2: + case 22: + return 'nd'; + case 3: + case 23: + return 'rd'; + default: + return 'th'; + } } diff --git a/lib/src/utils/metar_regexp.dart b/lib/src/utils/metar_regexp.dart index 8135033..b80a11a 100644 --- a/lib/src/utils/metar_regexp.dart +++ b/lib/src/utils/metar_regexp.dart @@ -47,6 +47,7 @@ class MetarRegExp { static final RegExp CLOUD = RegExp(r'^(?VV|SKC|CLR|SCK|SCK|NSC|NCD|BKN|SCT|FEW|OVC|///)' r'(?\d{3}|///)?' + r'([0-9])?' r'(?TCU|CB|///)?$'); static final RegExp TEMPERATURES = RegExp(r'^(?M|-)?' diff --git a/lib/src/utils/parser.dart b/lib/src/utils/parser.dart index c3deaab..c90381a 100644 --- a/lib/src/utils/parser.dart +++ b/lib/src/utils/parser.dart @@ -93,19 +93,13 @@ String sanitizeChangeIndicator(String code) { /// Returns: /// unparsed_groups (List): the not matched groups with anyone /// of the regular expresions stored in `handlers`. -List parseSection( - List handlers, - String section, { - required Null Function(String warning) onWarning, -}) { +List parseSection(List handlers, String section) { final unparsedGroups = []; var index = 0; section.split(' ').forEach((group) { unparsedGroups.add(group); - var found = false; - for (var i = index; i < handlers.length; i++) { var handler = handlers[i]; index++; @@ -113,14 +107,9 @@ List parseSection( if (handler.regexp.hasMatch(group)) { handler.handler(group); unparsedGroups.remove(group); - found = true; break; } } - - if (!found) { - onWarning('TAF Group not found: $group'); - } }); return unparsedGroups; diff --git a/lib/src/utils/taf_regexp.dart b/lib/src/utils/taf_regexp.dart index 772acc7..00f89cd 100644 --- a/lib/src/utils/taf_regexp.dart +++ b/lib/src/utils/taf_regexp.dart @@ -24,7 +24,7 @@ class TafRegExp { r'(?SM|KM|M|U)|' r'(?CAVOK)$'); - static final RegExp TEMPERATURE = RegExp(r'T(?N|X)' + static final RegExp TEMPERATURE = RegExp(r'T(?N|X)?' r'(?M)?' r'(?\d{2})/' r'(?0[1-9]|[12][0-9]|3[01])' @@ -47,6 +47,17 @@ class TafRegExp { static final RegExp ICING = RegExp(r'^(6(?[0-9])(?\d\d\d)(?\d))'); - static final RegExp AMMENDMENTS = RegExp( - r'^(LAST_NO_AMDS_AFT_(?\d{2})(?\d{2})_NEXT(?\d{2})(?\d{2}))|AFT_(?\d{2})(?\d{2})|AMD_NOT_SKED'); + static final RegExp AMENDMENTS = RegExp( + r'(LAST_NO_AMDS_AFT_(?\d{2})(?\d{2})_NEXT_(?\d{2})(?\d{2}))|AFT_(?\d{2})(?\d{2})|AMD_NOT_SKED'); + + static final RegExp CORRECTION = + RegExp(r'^(?COR|AMD)_(?\d{2})(?\d{2})( |$)'); + static final RegExp CORRECTION_UNSANITIZED = + RegExp(r'(?COR|AMD) (?\d{2})(?\d{2})( |$)'); + + static final RegExp AUTOMATED_SENSOR_METWATCH = RegExp( + r'^AUTOMATED_SENSOR_METWATCH_(?\d{2})(?\d{2})_TIL_(?
\d{2})(?\d{2})'); + + static final RegExp AUTOMATED_SENSOR_METWATCH_UNSANITIZED = + RegExp(r' AUTOMATED SENSOR METWATCH (?\d{4}) TIL (?\d{4})'); } diff --git a/test/taf/exhaustive_test.dart b/test/taf/exhaustive_test.dart index 924f68f..7625d2d 100644 --- a/test/taf/exhaustive_test.dart +++ b/test/taf/exhaustive_test.dart @@ -2,21 +2,6 @@ import 'package:metar_dart/metar_dart.dart'; import 'package:test/test.dart'; void main() { - test('warnings test', () { - final code = - '''TAF MMPQ 221654Z 2218/2318 10015KT P6SM SCT020 FM230300 13010KT P6SM BKN010 TEMPO 2308/2312 3SM -RA BKN005 D2DS FM231500 12010KT 4SM HZ SCT010 333'''; - final taf = Taf(code); - expect(taf.warnings.first, 'TAF Group not found: D2DS'); - expect(taf.warnings[1], 'TAF Group not found: 333'); - }); - - test('test faling case', (() { - final code = - 'KHPN 121720Z 1218/1318 31011G18KT P6SM SCT060 FM122100 31009KT P6SM FEW060 FM130100 VRB03KT P6SM FEW050 FM131500 14007KT P6SM BKN050 AMD NOT SKED'; - var taf = Taf(code); - expect(taf.warnings.length, 0); - })); - test('exhaustive', (() { final tafs = [ 'TAF GABS 121730Z 1218/1324 13010G20KT 9999 TS SCT030 FEW033CB TEMPO 1218/1220 4000 RA BKN030 FEW033CB BECMG 1220/1222 NSW FEW033 PROB40 TEMPO 1314/1318 TS SCT030 FEW040CB', @@ -559,7 +544,7 @@ void main() { for (final taf in tafs) { try { final tafO = Taf(taf); - if (tafO.warnings.isNotEmpty) { + if (tafO.unparsedGroups.isNotEmpty) { warnings++; } else { correct++; From c9e49832d788cf29bfe5886dc9942621fda42fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Thu, 14 Apr 2022 11:57:54 -0300 Subject: [PATCH 14/17] a few more fixes --- lib/src/models/metar/models/weather.dart | 7 +++++-- lib/src/models/taf/models/change_forecast.dart | 1 - lib/src/models/taf/taf.dart | 1 - lib/src/utils/metar_regexp.dart | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/src/models/metar/models/weather.dart b/lib/src/models/metar/models/weather.dart index 11672cf..44e6d28 100644 --- a/lib/src/models/metar/models/weather.dart +++ b/lib/src/models/metar/models/weather.dart @@ -63,9 +63,12 @@ class MetarWeather extends Group { if (match != null) { _intensity = INTENSITY[match.namedGroup('int')]; _description = DESCRIPTION[match.namedGroup('desc')]; + final prec = match.namedGroup('prec'); final prec2 = PRECIPITATION[match.namedGroup('prec2')]; - _precipitation = - '${PRECIPITATION[match.namedGroup('prec')]}${prec2 == null ? '' : ' $prec2'}'; + if (prec != null || prec2 != null) { + _precipitation = + '${PRECIPITATION[prec]}${prec2 == null ? '' : ' $prec2'}'; + } _obscuration = OBSCURATION[match.namedGroup('obsc')]; _other = OTHER[match.namedGroup('other')]; } diff --git a/lib/src/models/taf/models/change_forecast.dart b/lib/src/models/taf/models/change_forecast.dart index 7155214..1ac4c6d 100644 --- a/lib/src/models/taf/models/change_forecast.dart +++ b/lib/src/models/taf/models/change_forecast.dart @@ -7,7 +7,6 @@ class ChangeForecast extends Forecast { ChangeForecast(String code, Valid valid) : super(code) { // Initialize valid period of the forecasts _valid = valid; - _code = code; // Groups _changeIndicator = TafChangeIndicator(null, null, valid); diff --git a/lib/src/models/taf/taf.dart b/lib/src/models/taf/taf.dart index b9094e2..94535f1 100644 --- a/lib/src/models/taf/taf.dart +++ b/lib/src/models/taf/taf.dart @@ -116,7 +116,6 @@ class Taf extends Report Cancelled get cancelled => _cancelled; void _handleChangePeriod(String code) { - code = sanitizeWindToken(code); final cf = ChangeForecast(code, _valid); if (_changePeriods.length > 0) { if (cf.code!.startsWith('FM') || cf.code!.startsWith('BECMG')) { diff --git a/lib/src/utils/metar_regexp.dart b/lib/src/utils/metar_regexp.dart index b80a11a..a287b18 100644 --- a/lib/src/utils/metar_regexp.dart +++ b/lib/src/utils/metar_regexp.dart @@ -23,12 +23,12 @@ class MetarRegExp { RegExp(r'^(WND_)?(?(0[1-9]|[12][0-9]|3[0-6])0)' r'V(?(0[1-9]|[12][0-9]|3[0-6])0)$'); - static final RegExp VISIBILITY = RegExp(r'^(?\d{4}|//\//)' + static final RegExp VISIBILITY = RegExp(r'^((?\d{4}|//\//)' r'(?[NSEW]([EW])?)?|' r'(M|P)?(?\d{1,2})?_?' r'(?\d/\d)?' r'(?SM|KM|M|U)|' - r'(?CAVOK)$'); + r'(?CAVOK))$'); static final RegExp RUNWAY_RANGE = RegExp(r'^R(?\d{2}[RLC]?)/' r'(?[MP])?' From cb29629a554b1ba03633ea5c9537a90c07fd8405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Thu, 14 Apr 2022 13:39:06 -0300 Subject: [PATCH 15/17] small fix --- lib/src/models/metar/models/weather.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/models/metar/models/weather.dart b/lib/src/models/metar/models/weather.dart index 44e6d28..14124ce 100644 --- a/lib/src/models/metar/models/weather.dart +++ b/lib/src/models/metar/models/weather.dart @@ -64,10 +64,10 @@ class MetarWeather extends Group { _intensity = INTENSITY[match.namedGroup('int')]; _description = DESCRIPTION[match.namedGroup('desc')]; final prec = match.namedGroup('prec'); - final prec2 = PRECIPITATION[match.namedGroup('prec2')]; + final prec2 = match.namedGroup('prec2'); if (prec != null || prec2 != null) { _precipitation = - '${PRECIPITATION[prec]}${prec2 == null ? '' : ' $prec2'}'; + '${PRECIPITATION[prec]}${prec2 == null ? '' : ' ${PRECIPITATION[prec2]}'}'; } _obscuration = OBSCURATION[match.namedGroup('obsc')]; _other = OTHER[match.namedGroup('other')]; From 47db348b35223fd47cd2c353dd02e30c4a98d571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Tue, 24 May 2022 09:45:20 -0300 Subject: [PATCH 16/17] Undefined clouds accept "//" . --- lib/src/models/clouds.dart | 1 + lib/src/utils/metar_regexp.dart | 2 +- test/metar/temperatures_test.dart | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/src/models/clouds.dart b/lib/src/models/clouds.dart index fac3a0e..c032fb5 100644 --- a/lib/src/models/clouds.dart +++ b/lib/src/models/clouds.dart @@ -10,6 +10,7 @@ Map SKY_COVER = { 'BKN': 'broken', 'OVC': 'overcast', '///': 'undefined', + '//': 'undefined', 'VV': 'indefinite ceiling', }; diff --git a/lib/src/utils/metar_regexp.dart b/lib/src/utils/metar_regexp.dart index a287b18..f625860 100644 --- a/lib/src/utils/metar_regexp.dart +++ b/lib/src/utils/metar_regexp.dart @@ -45,7 +45,7 @@ class MetarRegExp { r'(?PO|SQ|FC|SS|DS|NSW|/))?)$'); static final RegExp CLOUD = - RegExp(r'^(?VV|SKC|CLR|SCK|SCK|NSC|NCD|BKN|SCT|FEW|OVC|///)' + RegExp(r'^(?VV|SKC|CLR|SCK|SCK|NSC|NCD|BKN|SCT|FEW|OVC|///|//)' r'(?\d{3}|///)?' r'([0-9])?' r'(?TCU|CB|///)?$'); diff --git a/test/metar/temperatures_test.dart b/test/metar/temperatures_test.dart index b07cd0c..4b4f4bc 100644 --- a/test/metar/temperatures_test.dart +++ b/test/metar/temperatures_test.dart @@ -57,4 +57,16 @@ void main() { expect(temperatures.dewpointInRankine, null); expect(temperatures.toString(), ''); }); + + test('Test example of temperatures in METAR', () { + final code = 'FAVV 240800Z AUTO 21005KT //// // ////// 15/07 Q1029'; + final metar = Metar(code); + final temperatures = metar.temperatures; + final pressure = metar.pressure; + + expect(temperatures.code, '15/07'); + expect(temperatures.temperatureInCelsius, 15.0); + expect(temperatures.dewpointInCelsius, 7.0); + expect(pressure.inHPa, 1029.0); + }); } From 5a3720c63741b7a25c362c7d0b67eeb8c5446486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Assis=20Neves?= Date: Mon, 14 Aug 2023 19:58:50 -0300 Subject: [PATCH 17/17] Tweak Taf toString() --- .DS_Store | Bin 0 -> 6148 bytes lib/src/models/models.dart | 1 + lib/src/models/report.dart | 2 +- lib/src/models/time.dart | 2 +- pubspec.yaml | 1 + 5 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4f057a4da68cf9ba8e28da89c30388485c521152 GIT binary patch literal 6148 zcmeHKF>4z!7=5xY#3T^hsezVoTZhsTcguEhoS}1mKpWR4G1#0*ozkthgiIkF3J&=J znKNYV*0Do6bPV|g9SVVT?E56;EctxUp_EYc8R^~W`=syPyOU%AP|f{L1E>S2u?R*B ztY(<_r4+1UTOJdY9%F=87$N1aUx*G3L4lyaeN#Z@?gE~pgKg|_?&kBizth-ylMaSL zxbkxCQCZv|ZME9{v`v5QeDTTJb>+)?d2Hv8V~4wLeoZ4H&mHtIa=GE*FIk6jpUdZF z>1=ecqWL)f35QvJH!q%@b7;)p<;5=c(e|=q?xYyua`^D=#k(t`$<#Q5bi$-O%HxAr z_{^bA9zCwH9gj=%_QLnNJQ^RrU;HpUoc=C{*%4m;jaA7%k1pQxdl_Iz4qcA}Dqo8c zE{~@_9X-5pTrTBN9v{7Lp-L9LsrRyUIVKP5aq}M@AAPd#Blk|;L(%Kydr+6>k2p`d zcsunSR@YBmpPTdZ`1meo-q(HFS$;2XbSZiNwsFrEt1TJ~bx