diff --git a/docs/en-us/MetarDecoder-Guide.md b/docs/en-us/MetarDecoder-Guide.md new file mode 100644 index 0000000..69ddbd3 --- /dev/null +++ b/docs/en-us/MetarDecoder-Guide.md @@ -0,0 +1,274 @@ +# Metar.Decoder - Usage Guide (EN-US) + +## Overview + +**Metar.Decoder** is a .NET library for parsing METAR (Meteorological Aerodrome Report) strings into structured, strongly-typed objects. It supports multiple .NET frameworks: `.NET Standard 2.0`, `.NET 8.0`, `.NET 10.0`, and `.NET Framework 4.8`. + +**Version:** 1.0.9 + +## Installation + +```bash +dotnet add package Metar.Decoder +``` + +Or via NuGet Package Manager: + +``` +Install-Package Metar.Decoder +``` + +## Quick Start + +```csharp +using Metar.Decoder; +using Metar.Decoder.Entity; + +var decoder = new MetarDecoder(); +var metar = decoder.Parse("METAR SBGR 031000Z 35006KT 9999 FEW040 SCT100 27/18 Q1020"); + +Console.WriteLine($"ICAO: {metar.ICAO}"); +Console.WriteLine($"Day: {metar.Day}"); +Console.WriteLine($"Time: {metar.Time}"); +Console.WriteLine($"Wind: {metar.SurfaceWind.MeanDirection.ActualValue} at {metar.SurfaceWind.MeanSpeed.ActualValue} {metar.SurfaceWind.MeanSpeed.ActualUnit}"); +Console.WriteLine($"Visibility: {metar.Visibility.PrevailingVisibility.ActualValue} {metar.Visibility.PrevailingVisibility.ActualUnit}"); +Console.WriteLine($"Temperature: {metar.AirTemperature.ActualValue} C"); +Console.WriteLine($"Dew Point: {metar.DewPointTemperature.ActualValue} C"); +Console.WriteLine($"Pressure: {metar.Pressure.ActualValue} {metar.Pressure.ActualUnit}"); +``` + +## Parsing Modes + +### Default Parsing +```csharp +var metar = decoder.Parse("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009"); +``` + +### Strict Parsing +Stops at the first decoding error: +```csharp +var metar = decoder.ParseStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009"); +``` + +### Non-Strict Parsing +Continues decoding even if errors are encountered: +```csharp +var metar = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT BADVALUE FEW020 17/10 Q1009"); +// metar.IsValid == false, but other fields are still decoded +``` + +### Static Parsing +```csharp +var metar = MetarDecoder.ParseWithMode("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009", isStrict: false); +``` + +## Decoded Fields + +### Report Type +```csharp +metar.Type // MetarType.METAR, MetarType.SPECI, MetarType.METAR_COR, MetarType.SPECI_COR +``` + +### ICAO Code +```csharp +metar.ICAO // "SBGR", "LFPO", "KJFK", etc. +``` + +### Date/Time +```csharp +metar.Day // Day of month (1-31) +metar.Time // "10:00 UTC" +metar.ObservationDateTime // DateTime object +``` + +### Report Status +```csharp +metar.Status // "AUTO", "NIL", or empty +``` + +### Surface Wind +```csharp +var wind = metar.SurfaceWind; +wind.MeanDirection.ActualValue // Direction in degrees (0-360) +wind.MeanSpeed.ActualValue // Speed value +wind.MeanSpeed.ActualUnit // Unit.Knot, Unit.MeterPerSecond, Unit.KilometerPerHour +wind.SpeedVariations?.ActualValue // Gust speed (if present) +wind.VariableDirection // true if VRB +wind.DirectionVariations // [min, max] direction variation values +``` + +### Visibility +```csharp +var vis = metar.Visibility; +vis.PrevailingVisibility.ActualValue // Main visibility value +vis.PrevailingVisibility.ActualUnit // Unit.Meter or Unit.StatuteMile +vis.MinimumVisibility?.ActualValue // Minimum visibility (if present) +vis.MinimumVisibilityDirection // "NW", "NE", "S", etc. +vis.NDV // No Directional Variation flag +metar.Cavok // CAVOK flag +``` + +### Runway Visual Range (RVR) +```csharp +foreach (var rvr in metar.RunwaysVisualRange) +{ + Console.WriteLine($"Runway: {rvr.Runway}"); + Console.WriteLine($"Range: {rvr.VisualRange?.ActualValue}"); + Console.WriteLine($"Variable: {rvr.Variable}"); + if (rvr.Variable) + { + Console.WriteLine($"Min: {rvr.VisualRangeInterval[0].ActualValue}"); + Console.WriteLine($"Max: {rvr.VisualRangeInterval[1].ActualValue}"); + } + Console.WriteLine($"Tendency: {rvr.PastTendency}"); // U (up), D (down), N (no change) +} +``` + +### Present Weather +```csharp +foreach (var pw in metar.PresentWeather) +{ + Console.WriteLine($"Intensity: {pw.IntensityProximity}"); // +, -, VC + Console.WriteLine($"Descriptor: {pw.Characteristics}"); // TS, FZ, SH, etc. + foreach (var type in pw.Types) + { + Console.WriteLine($"Phenomenon: {type}"); // RA, SN, FG, BR, etc. + } +} +``` + +### Clouds +```csharp +foreach (var cloud in metar.Clouds) +{ + Console.WriteLine($"Amount: {cloud.Amount}"); // FEW, SCT, BKN, OVC, VV + Console.WriteLine($"Height: {cloud.BaseHeight?.ActualValue} feet"); + Console.WriteLine($"Type: {cloud.Type}"); // CB, TCU, or NULL +} +``` + +### Temperature +```csharp +metar.AirTemperature.ActualValue // Air temperature in Celsius +metar.DewPointTemperature.ActualValue // Dew point in Celsius +``` + +### Pressure +```csharp +metar.Pressure.ActualValue // Pressure value +metar.Pressure.ActualUnit // Unit.HectoPascal (Q) or Unit.MercuryInch (A) +``` + +### Recent Weather +```csharp +var rw = metar.RecentWeather; +rw?.Characteristics // Weather descriptor +rw?.Types // Weather phenomena list +``` + +### Wind Shear +```csharp +metar.WindshearAllRunways // true if WS ALL RWY +metar.WindshearRunways // List of affected runways +``` + +### Trend Forecast (NOSIG/BECMG/TEMPO) +```csharp +metar.TrendType // "NOSIG", "BECMG", or "TEMPO" +metar.TrendForecast // Raw trend content +``` + +### Remarks (RMK) +```csharp +metar.Remark // Raw remarks content +metar.SeaLevelPressure // Parsed SLPnnn value (if present) +``` + +## Value Conversion + +The `Value` class supports unit conversion: + +```csharp +var speed = metar.SurfaceWind.MeanSpeed; +double knots = speed.GetConvertedValue(Value.Unit.Knot); +double mps = speed.GetConvertedValue(Value.Unit.MeterPerSecond); +double kph = speed.GetConvertedValue(Value.Unit.KilometerPerHour); +``` + +## Error Handling + +```csharp +var metar = decoder.ParseNotStrict("METAR LFPO 231027Z BAD_DATA"); + +if (!metar.IsValid) +{ + foreach (var ex in metar.DecodingExceptions) + { + Console.WriteLine($"Error in: {ex.ChunkDecoder.GetType().Name}"); + Console.WriteLine($"Message: {ex.Message}"); + Console.WriteLine($"Remaining: {ex.RemainingMetar}"); + } +} + +// Reset errors +metar.ResetDecodingExceptions(); +``` + +## Architecture + +The library follows Clean Architecture with SOLID principles: + +- **ChunkDecoder Pattern**: Chain of Responsibility - each decoder handles one METAR section +- **Interface Segregation**: `IMetarChunkDecoder` defines the contract +- **Single Responsibility**: Each decoder class handles exactly one METAR element +- **Open/Closed**: New decoders can be added without modifying existing ones + +### Decoder Chain Order +1. `ReportTypeChunkDecoder` - METAR/SPECI/COR +2. `IcaoChunkDecoder` - Airport ICAO code +3. `DatetimeChunkDecoder` - Day/time of observation +4. `ReportStatusChunkDecoder` - AUTO/NIL status +5. `SurfaceWindChunkDecoder` - Wind direction/speed/gusts +6. `VisibilityChunkDecoder` - CAVOK or visibility values +7. `RunwayVisualRangeChunkDecoder` - RVR for runways +8. `PresentWeatherChunkDecoder` - Current weather phenomena +9. `CloudChunkDecoder` - Cloud layers +10. `TemperatureChunkDecoder` - Air/dew point temperatures +11. `PressureChunkDecoder` - QNH/altimeter setting +12. `RecentWeatherChunkDecoder` - Recent weather (RE) +13. `WindShearChunkDecoder` - Wind shear information +14. `TrendChunkDecoder` - Trend forecast (NOSIG/BECMG/TEMPO) +15. `RemarkChunkDecoder` - Remarks section (RMK) + +## Supported METAR Elements + +| Element | Example | Supported | +|---------|---------|-----------| +| Report Type | METAR, SPECI, COR | Yes | +| ICAO Code | SBGR, KJFK | Yes | +| Date/Time | 231027Z | Yes | +| Status | AUTO, NIL | Yes | +| Surface Wind | 24004KT, VRB02MPS, 24015G25KT | Yes | +| Direction Variation | 180V270 | Yes | +| CAVOK | CAVOK | Yes | +| Visibility (ICAO) | 9999, 2500 | Yes | +| Visibility (US) | 3SM, 1 1/2SM | Yes | +| Minimum Visibility | 1000NW | Yes | +| NDV | 9999NDV | Yes | +| RVR | R32/0400, R06L/0200V0600U | Yes | +| Present Weather | +TSRA, -SN, FZFG, VCSH | Yes | +| Clouds | FEW020, SCT030CB, OVC005 | Yes | +| Vertical Visibility | VV003 | Yes | +| Clear Sky | SKC, NSC, NCD, CLR | Yes | +| Temperature | 17/10, M02/M05 | Yes | +| Pressure (QNH) | Q1009 | Yes | +| Pressure (Altimeter) | A2992 | Yes | +| Recent Weather | REFZRA | Yes | +| Wind Shear | WS R03, WS ALL RWY | Yes | +| Trend Forecast | NOSIG, BECMG, TEMPO | Yes | +| Remarks | RMK AO2 SLP013 | Yes | +| Sea Level Pressure | SLPnnn (in RMK) | Yes | + +## License + +MIT License diff --git a/docs/en-us/TafDecoder-Guide.md b/docs/en-us/TafDecoder-Guide.md new file mode 100644 index 0000000..77b2059 --- /dev/null +++ b/docs/en-us/TafDecoder-Guide.md @@ -0,0 +1,278 @@ +# Taf.Decoder - Usage Guide (EN-US) + +## Overview + +**Taf.Decoder** is a .NET library for parsing TAF (Terminal Aerodrome Forecast) strings into structured, strongly-typed objects. It supports multiple .NET frameworks: `.NET Standard 2.0`, `.NET 8.0`, `.NET 10.0`, and `.NET Framework 4.8`. + +**Version:** 1.0.7 + +## Installation + +```bash +dotnet add package Taf.Decoder +``` + +Or via NuGet Package Manager: + +``` +Install-Package Taf.Decoder +``` + +## Quick Start + +```csharp +using Taf.Decoder; +using Taf.Decoder.Entity; + +var decoder = new TafDecoder(); +var taf = decoder.Parse("TAF SBGR 031100Z 0312/0418 35008KT 9999 FEW040 SCT100 TX30/0318Z TN20/0409Z"); + +Console.WriteLine($"ICAO: {taf.Icao}"); +Console.WriteLine($"Day: {taf.Day}"); +Console.WriteLine($"Time: {taf.Time}"); +Console.WriteLine($"Forecast Period: {taf.ForecastPeriod.FromDay}/{taf.ForecastPeriod.FromHour} to {taf.ForecastPeriod.ToDay}/{taf.ForecastPeriod.ToHour}"); +Console.WriteLine($"Wind: {taf.SurfaceWind.MeanDirection.ActualValue} at {taf.SurfaceWind.MeanSpeed.ActualValue} {taf.SurfaceWind.MeanSpeed.ActualUnit}"); +Console.WriteLine($"Visibility: {taf.Visibility.ActualVisibility.ActualValue} {taf.Visibility.ActualVisibility.ActualUnit}"); +``` + +## Parsing Modes + +### Default Parsing +```csharp +var taf = decoder.Parse("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030"); +``` + +### Strict Parsing +Stops at the first decoding error: +```csharp +var taf = decoder.ParseStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030"); +``` + +### Non-Strict Parsing +Continues decoding even if errors are encountered: +```csharp +var taf = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 BADVALUE 9999 FEW030"); +``` + +### Static Parsing +```csharp +var taf = TafDecoder.ParseWithMode("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030", isStrict: false); +``` + +## Decoded Fields + +### Report Type +```csharp +taf.Type // TafType.TAF, TafType.TAFAMD, TafType.TAFCOR, TafType.RTD +``` + +### ICAO Code +```csharp +taf.Icao // "SBGR", "LFPO", "KJFK", etc. +``` + +### Date/Time +```csharp +taf.Day // Day of month (1-31) +taf.Time // "11:00 UTC" +taf.OriginDateTime // DateTime object +``` + +### Forecast Period +```csharp +var period = taf.ForecastPeriod; +period.FromDay // Start day +period.FromHour // Start hour +period.ToDay // End day +period.ToHour // End hour +period.IsValid // Validity check +``` + +### Surface Wind +```csharp +var wind = taf.SurfaceWind; +wind.MeanDirection.ActualValue // Direction in degrees (0-360) +wind.MeanSpeed.ActualValue // Speed value +wind.MeanSpeed.ActualUnit // Unit.Knot, Unit.MeterPerSecond, Unit.KilometerPerHour +wind.SpeedVariations?.ActualValue // Gust speed (if present) +wind.VariableDirection // true if VRB +wind.DirectionVariations // [min, max] direction variation values +``` + +### Visibility +```csharp +var vis = taf.Visibility; +vis.ActualVisibility.ActualValue // Visibility value +vis.ActualVisibility.ActualUnit // Unit.Meter or Unit.StatuteMile +vis.Greater // true if P (greater than) prefix +taf.Cavok // CAVOK flag +``` + +### Weather Phenomena +```csharp +foreach (var wp in taf.WeatherPhenomenons) +{ + Console.WriteLine($"Intensity: {wp.IntensityProximity}"); // +, -, VC + Console.WriteLine($"Descriptor: {wp.Descriptor}"); // TS, FZ, SH, etc. + foreach (var p in wp.Phenomena) + { + Console.WriteLine($"Phenomenon: {p}"); // RA, SN, FG, BR, etc. + } +} +``` + +### Clouds +```csharp +foreach (var cloud in taf.Clouds) +{ + Console.WriteLine($"Amount: {cloud.Amount}"); // FEW, SCT, BKN, OVC, VV + Console.WriteLine($"Height: {cloud.BaseHeight?.ActualValue} feet"); + Console.WriteLine($"Type: {cloud.Type}"); // CB, TCU, or NULL +} +``` + +### Temperature Forecast +```csharp +// Maximum temperature +var maxTemp = taf.MaximumTemperature; +if (maxTemp != null) +{ + Console.WriteLine($"Type: {maxTemp.Type}"); // "TX" + Console.WriteLine($"Value: {maxTemp.TemperatureValue.ActualValue} C"); + Console.WriteLine($"Day: {maxTemp.Day}, Hour: {maxTemp.Hour}"); +} + +// Minimum temperature +var minTemp = taf.MinimumTemperature; +if (minTemp != null) +{ + Console.WriteLine($"Type: {minTemp.Type}"); // "TN" + Console.WriteLine($"Value: {minTemp.TemperatureValue.ActualValue} C"); + Console.WriteLine($"Day: {minTemp.Day}, Hour: {minTemp.Hour}"); +} +``` + +## Weather Evolutions (TEMPO/BECMG/FM/PROB) + +TAF includes weather evolution sections that modify the base forecast. Evolutions are stored on each entity: + +```csharp +// Check wind evolutions +var windEvos = taf.SurfaceWind.Evolutions; +foreach (var evo in windEvos) +{ + Console.WriteLine($"Type: {evo.Type}"); // "TEMPO", "BECMG", "FM" + Console.WriteLine($"From: Day {evo.FromDay} {evo.FromTime}"); + Console.WriteLine($"To: Day {evo.ToDay} {evo.ToTime}"); + Console.WriteLine($"Probability: {evo.Probability}"); // "PROB30", "PROB40", or null + + var windEntity = evo.Entity as SurfaceWind; + if (windEntity != null) + { + Console.WriteLine($"New Wind: {windEntity.MeanDirection?.ActualValue} at {windEntity.MeanSpeed?.ActualValue}"); + } +} + +// Check visibility evolutions +var visEvos = taf.Visibility?.Evolutions; +// ... similar pattern + +// Check cloud evolutions +foreach (var cloud in taf.Clouds) +{ + var cloudEvos = cloud.Evolutions; + // ... similar pattern +} +``` + +### Evolution Types +- **TEMPO**: Temporary fluctuation expected to last less than 1 hour +- **BECMG**: Gradual change expected to occur during the specified period +- **FM**: From a specific time, conditions replace the previous forecast +- **PROB30/PROB40**: Probability of occurrence (30% or 40%) + +## Value Conversion + +The `Value` class supports unit conversion: + +```csharp +var speed = taf.SurfaceWind.MeanSpeed; +double knots = speed.GetConvertedValue(Value.Unit.Knot); +double mps = speed.GetConvertedValue(Value.Unit.MeterPerSecond); +double kph = speed.GetConvertedValue(Value.Unit.KilometerPerHour); +``` + +## Error Handling + +```csharp +var taf = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 BAD_DATA"); + +if (!taf.IsValid) +{ + foreach (var ex in taf.DecodingExceptions) + { + Console.WriteLine($"Error in: {ex.ChunkDecoder.GetType().Name}"); + Console.WriteLine($"Message: {ex.Message}"); + Console.WriteLine($"Remaining: {ex.RemainingTaf}"); + } +} + +// Reset errors +taf.ResetDecodingExceptions(); +``` + +## Architecture + +The library follows Clean Architecture with SOLID principles: + +- **ChunkDecoder Pattern**: Chain of Responsibility - each decoder handles one TAF section +- **Interface Segregation**: `ITafChunkDecoder` defines the contract +- **Single Responsibility**: Each decoder class handles exactly one TAF element +- **Open/Closed**: New decoders can be added without modifying existing ones + +### Main Decoder Chain +1. `ReportTypeChunkDecoder` - TAF/TAF AMD/TAF COR/RTD +2. `IcaoChunkDecoder` - Airport ICAO code +3. `DatetimeChunkDecoder` - Day/time of issue +4. `ForecastPeriodChunkDecoder` - Valid period (e.g., 0312/0418) +5. `SurfaceWindChunkDecoder` - Wind direction/speed/gusts +6. `VisibilityChunkDecoder` - CAVOK or visibility values +7. `WeatherChunkDecoder` - Weather phenomena +8. `CloudChunkDecoder` - Cloud layers +9. `TemperatureChunkDecoder` - TX/TN temperatures + +### Evolution Decoder +After the main chain, `EvolutionChunkDecoder` processes: +- `TEMPO` - Temporary changes +- `BECMG` - Becoming changes +- `FM` - From time changes +- `PROB30/PROB40` - Probability groups + +## Supported TAF Elements + +| Element | Example | Supported | +|---------|---------|-----------| +| Report Type | TAF, TAF AMD, TAF COR, RTD | Yes | +| ICAO Code | SBGR, KJFK | Yes | +| Date/Time | 231100Z | Yes | +| Forecast Period | 2312/2418 | Yes | +| Surface Wind | 24005KT, VRB03MPS, 24015G25KT | Yes | +| Direction Variation | 180V270 | Yes | +| CAVOK | CAVOK | Yes | +| Visibility (ICAO) | 9999, 2500 | Yes | +| Visibility (US) | 3SM, 1 1/2SM, P6SM | Yes | +| No Visibility Info | //// | Yes | +| Weather Phenomena | -RA, +TSRA, FZFG | Yes | +| Clouds | FEW020, SCT030CB, OVC005 | Yes | +| Vertical Visibility | VV003 | Yes | +| Clear Sky | SKC, NSC, NCD, CLR | Yes | +| Temperature Max | TX25/0318Z | Yes | +| Temperature Min | TNM03/0405Z | Yes | +| TEMPO | TEMPO 0312/0315 ... | Yes | +| BECMG | BECMG 0315/0317 ... | Yes | +| FM | FM031500 ... | Yes | +| PROB30/PROB40 | PROB40 TEMPO ... | Yes | + +## License + +MIT License diff --git a/docs/pt-br/MetarDecoder-Guia.md b/docs/pt-br/MetarDecoder-Guia.md new file mode 100644 index 0000000..83fe883 --- /dev/null +++ b/docs/pt-br/MetarDecoder-Guia.md @@ -0,0 +1,301 @@ +# Metar.Decoder - Guia de Uso (PT-BR) + +## Visao Geral + +**Metar.Decoder** e uma biblioteca .NET para decodificacao de strings METAR (Meteorological Aerodrome Report / Relatorio Meteorologico de Aerodromo) em objetos estruturados e fortemente tipados. Suporta multiplos frameworks: `.NET Standard 2.0`, `.NET 8.0`, `.NET 10.0` e `.NET Framework 4.8`. + +**Versao:** 1.0.9 + +## Instalacao + +```bash +dotnet add package Metar.Decoder +``` + +Ou pelo NuGet Package Manager: + +``` +Install-Package Metar.Decoder +``` + +## Inicio Rapido + +```csharp +using Metar.Decoder; +using Metar.Decoder.Entity; + +var decoder = new MetarDecoder(); +var metar = decoder.Parse("METAR SBGR 031000Z 35006KT 9999 FEW040 SCT100 27/18 Q1020"); + +Console.WriteLine($"ICAO: {metar.ICAO}"); +Console.WriteLine($"Dia: {metar.Day}"); +Console.WriteLine($"Hora: {metar.Time}"); +Console.WriteLine($"Vento: {metar.SurfaceWind.MeanDirection.ActualValue} graus a {metar.SurfaceWind.MeanSpeed.ActualValue} {metar.SurfaceWind.MeanSpeed.ActualUnit}"); +Console.WriteLine($"Visibilidade: {metar.Visibility.PrevailingVisibility.ActualValue} {metar.Visibility.PrevailingVisibility.ActualUnit}"); +Console.WriteLine($"Temperatura: {metar.AirTemperature.ActualValue} C"); +Console.WriteLine($"Ponto de Orvalho: {metar.DewPointTemperature.ActualValue} C"); +Console.WriteLine($"Pressao: {metar.Pressure.ActualValue} {metar.Pressure.ActualUnit}"); +``` + +## Modos de Decodificacao + +### Decodificacao Padrao +```csharp +var metar = decoder.Parse("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009"); +``` + +### Decodificacao Estrita (Strict) +Para na primeira falha de decodificacao: +```csharp +var metar = decoder.ParseStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009"); +``` + +### Decodificacao Nao-Estrita (Not Strict) +Continua a decodificacao mesmo encontrando erros: +```csharp +var metar = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT INVALIDO FEW020 17/10 Q1009"); +// metar.IsValid == false, mas os demais campos sao decodificados normalmente +``` + +### Decodificacao Estatica +```csharp +var metar = MetarDecoder.ParseWithMode("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009", isStrict: false); +``` + +## Campos Decodificados + +### Tipo de Relatorio +```csharp +metar.Type // MetarType.METAR, MetarType.SPECI, MetarType.METAR_COR, MetarType.SPECI_COR +``` + +### Codigo ICAO +```csharp +metar.ICAO // "SBGR", "LFPO", "KJFK", etc. +``` + +### Data/Hora +```csharp +metar.Day // Dia do mes (1-31) +metar.Time // "10:00 UTC" +metar.ObservationDateTime // Objeto DateTime +``` + +### Status do Relatorio +```csharp +metar.Status // "AUTO", "NIL", ou vazio +``` + +### Vento de Superficie +```csharp +var vento = metar.SurfaceWind; +vento.MeanDirection.ActualValue // Direcao em graus (0-360) +vento.MeanSpeed.ActualValue // Valor da velocidade +vento.MeanSpeed.ActualUnit // Unit.Knot, Unit.MeterPerSecond, Unit.KilometerPerHour +vento.SpeedVariations?.ActualValue // Rajada (se presente) +vento.VariableDirection // true se VRB (variavel) +vento.DirectionVariations // [min, max] variacao de direcao +``` + +### Visibilidade +```csharp +var vis = metar.Visibility; +vis.PrevailingVisibility.ActualValue // Valor da visibilidade predominante +vis.PrevailingVisibility.ActualUnit // Unit.Meter ou Unit.StatuteMile +vis.MinimumVisibility?.ActualValue // Visibilidade minima (se presente) +vis.MinimumVisibilityDirection // "NW", "NE", "S", etc. +vis.NDV // Sem Variacao Direcional +metar.Cavok // CAVOK (teto e visibilidade OK) +``` + +### Alcance Visual de Pista (RVR) +```csharp +foreach (var rvr in metar.RunwaysVisualRange) +{ + Console.WriteLine($"Pista: {rvr.Runway}"); + Console.WriteLine($"Alcance: {rvr.VisualRange?.ActualValue}"); + Console.WriteLine($"Variavel: {rvr.Variable}"); + if (rvr.Variable) + { + Console.WriteLine($"Min: {rvr.VisualRangeInterval[0].ActualValue}"); + Console.WriteLine($"Max: {rvr.VisualRangeInterval[1].ActualValue}"); + } + Console.WriteLine($"Tendencia: {rvr.PastTendency}"); // U (subindo), D (descendo), N (sem mudanca) +} +``` + +### Tempo Presente +```csharp +foreach (var tp in metar.PresentWeather) +{ + Console.WriteLine($"Intensidade: {tp.IntensityProximity}"); // +, -, VC + Console.WriteLine($"Descritor: {tp.Characteristics}"); // TS, FZ, SH, etc. + foreach (var tipo in tp.Types) + { + Console.WriteLine($"Fenomeno: {tipo}"); // RA (chuva), SN (neve), FG (nevoeiro), etc. + } +} +``` + +### Nuvens +```csharp +foreach (var nuvem in metar.Clouds) +{ + Console.WriteLine($"Quantidade: {nuvem.Amount}"); // FEW (poucas), SCT (esparsas), BKN (nublado), OVC (encoberto), VV (visib. vertical) + Console.WriteLine($"Altura: {nuvem.BaseHeight?.ActualValue} pes"); + Console.WriteLine($"Tipo: {nuvem.Type}"); // CB (cumulonimbus), TCU (cumulus torre), ou NULL +} +``` + +### Temperatura +```csharp +metar.AirTemperature.ActualValue // Temperatura do ar em Celsius +metar.DewPointTemperature.ActualValue // Ponto de orvalho em Celsius +``` + +### Pressao +```csharp +metar.Pressure.ActualValue // Valor da pressao +metar.Pressure.ActualUnit // Unit.HectoPascal (QNH) ou Unit.MercuryInch (altimetro) +``` + +### Tempo Recente +```csharp +var tr = metar.RecentWeather; +tr?.Characteristics // Descritor do tempo +tr?.Types // Lista de fenomenos +``` + +### Cisalhamento de Vento (Wind Shear) +```csharp +metar.WindshearAllRunways // true se WS ALL RWY (todas as pistas) +metar.WindshearRunways // Lista de pistas afetadas +``` + +### Tendencia (NOSIG/BECMG/TEMPO) +```csharp +metar.TrendType // "NOSIG" (sem mudanca significativa), "BECMG" (tornando-se), ou "TEMPO" (temporario) +metar.TrendForecast // Conteudo bruto da tendencia +``` + +### Observacoes (RMK) +```csharp +metar.Remark // Conteudo das observacoes +metar.SeaLevelPressure // Pressao ao nivel do mar (SLPnnn) +``` + +## Conversao de Valores + +A classe `Value` suporta conversao de unidades: + +```csharp +var velocidade = metar.SurfaceWind.MeanSpeed; +double nos = velocidade.GetConvertedValue(Value.Unit.Knot); +double mps = velocidade.GetConvertedValue(Value.Unit.MeterPerSecond); +double kph = velocidade.GetConvertedValue(Value.Unit.KilometerPerHour); +``` + +## Tratamento de Erros + +```csharp +var metar = decoder.ParseNotStrict("METAR LFPO 231027Z DADOS_INVALIDOS"); + +if (!metar.IsValid) +{ + foreach (var ex in metar.DecodingExceptions) + { + Console.WriteLine($"Erro no decodificador: {ex.ChunkDecoder.GetType().Name}"); + Console.WriteLine($"Mensagem: {ex.Message}"); + Console.WriteLine($"METAR restante: {ex.RemainingMetar}"); + } +} + +// Limpar erros +metar.ResetDecodingExceptions(); +``` + +## Arquitetura + +A biblioteca segue os principios de Arquitetura Limpa (Clean Architecture) e SOLID: + +- **Padrao ChunkDecoder**: Cadeia de Responsabilidade - cada decodificador trata uma secao do METAR +- **Segregacao de Interface**: `IMetarChunkDecoder` define o contrato +- **Responsabilidade Unica**: Cada classe decodificadora trata exatamente um elemento do METAR +- **Aberto/Fechado**: Novos decodificadores podem ser adicionados sem modificar os existentes + +### Ordem da Cadeia de Decodificacao +1. `ReportTypeChunkDecoder` - METAR/SPECI/COR +2. `IcaoChunkDecoder` - Codigo ICAO do aerodromo +3. `DatetimeChunkDecoder` - Dia/hora da observacao +4. `ReportStatusChunkDecoder` - Status AUTO/NIL +5. `SurfaceWindChunkDecoder` - Direcao/velocidade/rajadas do vento +6. `VisibilityChunkDecoder` - CAVOK ou valores de visibilidade +7. `RunwayVisualRangeChunkDecoder` - RVR das pistas +8. `PresentWeatherChunkDecoder` - Fenomenos meteorologicos atuais +9. `CloudChunkDecoder` - Camadas de nuvens +10. `TemperatureChunkDecoder` - Temperatura do ar/ponto de orvalho +11. `PressureChunkDecoder` - Ajuste de altimetro (QNH) +12. `RecentWeatherChunkDecoder` - Tempo recente (RE) +13. `WindShearChunkDecoder` - Cisalhamento de vento +14. `TrendChunkDecoder` - Tendencia (NOSIG/BECMG/TEMPO) +15. `RemarkChunkDecoder` - Secao de observacoes (RMK) + +## Elementos METAR Suportados + +| Elemento | Exemplo | Suportado | +|----------|---------|-----------| +| Tipo de Relatorio | METAR, SPECI, COR | Sim | +| Codigo ICAO | SBGR, KJFK | Sim | +| Data/Hora | 231027Z | Sim | +| Status | AUTO, NIL | Sim | +| Vento de Superficie | 24004KT, VRB02MPS, 24015G25KT | Sim | +| Variacao de Direcao | 180V270 | Sim | +| CAVOK | CAVOK | Sim | +| Visibilidade (ICAO) | 9999, 2500 | Sim | +| Visibilidade (EUA) | 3SM, 1 1/2SM | Sim | +| Visibilidade Minima | 1000NW | Sim | +| NDV | 9999NDV | Sim | +| RVR | R32/0400, R06L/0200V0600U | Sim | +| Tempo Presente | +TSRA, -SN, FZFG, VCSH | Sim | +| Nuvens | FEW020, SCT030CB, OVC005 | Sim | +| Visibilidade Vertical | VV003 | Sim | +| Ceu Claro | SKC, NSC, NCD, CLR | Sim | +| Temperatura | 17/10, M02/M05 | Sim | +| Pressao (QNH) | Q1009 | Sim | +| Pressao (Altimetro) | A2992 | Sim | +| Tempo Recente | REFZRA | Sim | +| Cisalhamento de Vento | WS R03, WS ALL RWY | Sim | +| Tendencia | NOSIG, BECMG, TEMPO | Sim | +| Observacoes | RMK AO2 SLP013 | Sim | +| Pressao ao Nivel do Mar | SLPnnn (no RMK) | Sim | + +## Glossario de Termos METAR + +| Codigo | Significado | +|--------|-------------| +| METAR | Relatorio Meteorologico de Aerodromo | +| SPECI | Relatorio Especial | +| CAVOK | Teto e Visibilidade OK (vis >10km, sem nuvens abaixo de 5000ft, sem tempo significativo) | +| NOSIG | Sem Mudanca Significativa | +| BECMG | Tornando-se (mudanca gradual esperada) | +| TEMPO | Temporario (flutuacao temporaria) | +| FEW | Poucas nuvens (1-2 oitavos) | +| SCT | Esparsas (3-4 oitavos) | +| BKN | Nublado (5-7 oitavos) | +| OVC | Encoberto (8 oitavos) | +| VV | Visibilidade Vertical | +| CB | Cumulonimbus | +| TCU | Cumulus em Torre | +| RA | Chuva | +| SN | Neve | +| FG | Nevoeiro | +| BR | Nevoa | +| TS | Trovoada | +| FZ | Congelante | +| SH | Pancadas | +| VRB | Variavel | +| RVR | Alcance Visual de Pista | + +## Licenca + +MIT License diff --git a/docs/pt-br/TafDecoder-Guia.md b/docs/pt-br/TafDecoder-Guia.md new file mode 100644 index 0000000..0beed51 --- /dev/null +++ b/docs/pt-br/TafDecoder-Guia.md @@ -0,0 +1,309 @@ +# Taf.Decoder - Guia de Uso (PT-BR) + +## Visao Geral + +**Taf.Decoder** e uma biblioteca .NET para decodificacao de strings TAF (Terminal Aerodrome Forecast / Previsao de Aerodromo Terminal) em objetos estruturados e fortemente tipados. Suporta multiplos frameworks: `.NET Standard 2.0`, `.NET 8.0`, `.NET 10.0` e `.NET Framework 4.8`. + +**Versao:** 1.0.7 + +## Instalacao + +```bash +dotnet add package Taf.Decoder +``` + +Ou pelo NuGet Package Manager: + +``` +Install-Package Taf.Decoder +``` + +## Inicio Rapido + +```csharp +using Taf.Decoder; +using Taf.Decoder.Entity; + +var decoder = new TafDecoder(); +var taf = decoder.Parse("TAF SBGR 031100Z 0312/0418 35008KT 9999 FEW040 SCT100 TX30/0318Z TN20/0409Z"); + +Console.WriteLine($"ICAO: {taf.Icao}"); +Console.WriteLine($"Dia: {taf.Day}"); +Console.WriteLine($"Hora: {taf.Time}"); +Console.WriteLine($"Periodo: {taf.ForecastPeriod.FromDay}/{taf.ForecastPeriod.FromHour} ate {taf.ForecastPeriod.ToDay}/{taf.ForecastPeriod.ToHour}"); +Console.WriteLine($"Vento: {taf.SurfaceWind.MeanDirection.ActualValue} graus a {taf.SurfaceWind.MeanSpeed.ActualValue} {taf.SurfaceWind.MeanSpeed.ActualUnit}"); +Console.WriteLine($"Visibilidade: {taf.Visibility.ActualVisibility.ActualValue} {taf.Visibility.ActualVisibility.ActualUnit}"); +``` + +## Modos de Decodificacao + +### Decodificacao Padrao +```csharp +var taf = decoder.Parse("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030"); +``` + +### Decodificacao Estrita (Strict) +Para na primeira falha de decodificacao: +```csharp +var taf = decoder.ParseStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030"); +``` + +### Decodificacao Nao-Estrita (Not Strict) +Continua a decodificacao mesmo encontrando erros: +```csharp +var taf = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 INVALIDO 9999 FEW030"); +``` + +### Decodificacao Estatica +```csharp +var taf = TafDecoder.ParseWithMode("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030", isStrict: false); +``` + +## Campos Decodificados + +### Tipo de Relatorio +```csharp +taf.Type // TafType.TAF, TafType.TAFAMD (emendado), TafType.TAFCOR (corrigido), TafType.RTD +``` + +### Codigo ICAO +```csharp +taf.Icao // "SBGR", "LFPO", "KJFK", etc. +``` + +### Data/Hora +```csharp +taf.Day // Dia do mes (1-31) +taf.Time // "11:00 UTC" +taf.OriginDateTime // Objeto DateTime +``` + +### Periodo de Previsao +```csharp +var periodo = taf.ForecastPeriod; +periodo.FromDay // Dia inicial +periodo.FromHour // Hora inicial +periodo.ToDay // Dia final +periodo.ToHour // Hora final +periodo.IsValid // Verificacao de validade +``` + +### Vento de Superficie +```csharp +var vento = taf.SurfaceWind; +vento.MeanDirection.ActualValue // Direcao em graus (0-360) +vento.MeanSpeed.ActualValue // Valor da velocidade +vento.MeanSpeed.ActualUnit // Unit.Knot, Unit.MeterPerSecond, Unit.KilometerPerHour +vento.SpeedVariations?.ActualValue // Rajada (se presente) +vento.VariableDirection // true se VRB (variavel) +vento.DirectionVariations // [min, max] variacao de direcao +``` + +### Visibilidade +```csharp +var vis = taf.Visibility; +vis.ActualVisibility.ActualValue // Valor da visibilidade +vis.ActualVisibility.ActualUnit // Unit.Meter ou Unit.StatuteMile +vis.Greater // true se prefixo P (maior que) +taf.Cavok // CAVOK (teto e visibilidade OK) +``` + +### Fenomenos Meteorologicos +```csharp +foreach (var fm in taf.WeatherPhenomenons) +{ + Console.WriteLine($"Intensidade: {fm.IntensityProximity}"); // +, -, VC + Console.WriteLine($"Descritor: {fm.Descriptor}"); // TS, FZ, SH, etc. + foreach (var p in fm.Phenomena) + { + Console.WriteLine($"Fenomeno: {p}"); // RA (chuva), SN (neve), FG (nevoeiro), etc. + } +} +``` + +### Nuvens +```csharp +foreach (var nuvem in taf.Clouds) +{ + Console.WriteLine($"Quantidade: {nuvem.Amount}"); // FEW, SCT, BKN, OVC, VV + Console.WriteLine($"Altura: {nuvem.BaseHeight?.ActualValue} pes"); + Console.WriteLine($"Tipo: {nuvem.Type}"); // CB, TCU, ou NULL +} +``` + +### Previsao de Temperatura +```csharp +// Temperatura maxima +var tempMax = taf.MaximumTemperature; +if (tempMax != null) +{ + Console.WriteLine($"Tipo: {tempMax.Type}"); // "TX" + Console.WriteLine($"Valor: {tempMax.TemperatureValue.ActualValue} C"); + Console.WriteLine($"Dia: {tempMax.Day}, Hora: {tempMax.Hour}"); +} + +// Temperatura minima +var tempMin = taf.MinimumTemperature; +if (tempMin != null) +{ + Console.WriteLine($"Tipo: {tempMin.Type}"); // "TN" + Console.WriteLine($"Valor: {tempMin.TemperatureValue.ActualValue} C"); + Console.WriteLine($"Dia: {tempMin.Day}, Hora: {tempMin.Hour}"); +} +``` + +## Evolucoes Meteorologicas (TEMPO/BECMG/FM/PROB) + +O TAF inclui secoes de evolucao meteorologica que modificam a previsao base. As evolucoes sao armazenadas em cada entidade: + +```csharp +// Verificar evolucoes de vento +var evoVento = taf.SurfaceWind.Evolutions; +foreach (var evo in evoVento) +{ + Console.WriteLine($"Tipo: {evo.Type}"); // "TEMPO", "BECMG", "FM" + Console.WriteLine($"De: Dia {evo.FromDay} {evo.FromTime}"); + Console.WriteLine($"Ate: Dia {evo.ToDay} {evo.ToTime}"); + Console.WriteLine($"Probabilidade: {evo.Probability}"); // "PROB30", "PROB40", ou null + + var ventoEvo = evo.Entity as SurfaceWind; + if (ventoEvo != null) + { + Console.WriteLine($"Novo Vento: {ventoEvo.MeanDirection?.ActualValue} a {ventoEvo.MeanSpeed?.ActualValue}"); + } +} + +// Verificar evolucoes de visibilidade +var evoVis = taf.Visibility?.Evolutions; +// ... mesmo padrao + +// Verificar evolucoes de nuvens +foreach (var nuvem in taf.Clouds) +{ + var evoNuvem = nuvem.Evolutions; + // ... mesmo padrao +} +``` + +### Tipos de Evolucao +- **TEMPO**: Flutuacao temporaria com duracao esperada inferior a 1 hora +- **BECMG**: Mudanca gradual esperada durante o periodo especificado +- **FM**: A partir de um horario especifico, as condicoes substituem a previsao anterior +- **PROB30/PROB40**: Probabilidade de ocorrencia (30% ou 40%) + +## Conversao de Valores + +A classe `Value` suporta conversao de unidades: + +```csharp +var velocidade = taf.SurfaceWind.MeanSpeed; +double nos = velocidade.GetConvertedValue(Value.Unit.Knot); +double mps = velocidade.GetConvertedValue(Value.Unit.MeterPerSecond); +double kph = velocidade.GetConvertedValue(Value.Unit.KilometerPerHour); +``` + +## Tratamento de Erros + +```csharp +var taf = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 DADOS_INVALIDOS"); + +if (!taf.IsValid) +{ + foreach (var ex in taf.DecodingExceptions) + { + Console.WriteLine($"Erro no decodificador: {ex.ChunkDecoder.GetType().Name}"); + Console.WriteLine($"Mensagem: {ex.Message}"); + Console.WriteLine($"TAF restante: {ex.RemainingTaf}"); + } +} + +// Limpar erros +taf.ResetDecodingExceptions(); +``` + +## Arquitetura + +A biblioteca segue os principios de Arquitetura Limpa (Clean Architecture) e SOLID: + +- **Padrao ChunkDecoder**: Cadeia de Responsabilidade - cada decodificador trata uma secao do TAF +- **Segregacao de Interface**: `ITafChunkDecoder` define o contrato +- **Responsabilidade Unica**: Cada classe decodificadora trata exatamente um elemento do TAF +- **Aberto/Fechado**: Novos decodificadores podem ser adicionados sem modificar os existentes + +### Cadeia Principal de Decodificacao +1. `ReportTypeChunkDecoder` - TAF/TAF AMD/TAF COR/RTD +2. `IcaoChunkDecoder` - Codigo ICAO do aerodromo +3. `DatetimeChunkDecoder` - Dia/hora de emissao +4. `ForecastPeriodChunkDecoder` - Periodo de validade (ex: 0312/0418) +5. `SurfaceWindChunkDecoder` - Direcao/velocidade/rajadas do vento +6. `VisibilityChunkDecoder` - CAVOK ou valores de visibilidade +7. `WeatherChunkDecoder` - Fenomenos meteorologicos +8. `CloudChunkDecoder` - Camadas de nuvens +9. `TemperatureChunkDecoder` - Temperaturas TX/TN + +### Decodificador de Evolucao +Apos a cadeia principal, `EvolutionChunkDecoder` processa: +- `TEMPO` - Mudancas temporarias +- `BECMG` - Mudancas graduais (tornando-se) +- `FM` - A partir de determinado horario +- `PROB30/PROB40` - Grupos de probabilidade + +## Elementos TAF Suportados + +| Elemento | Exemplo | Suportado | +|----------|---------|-----------| +| Tipo de Relatorio | TAF, TAF AMD, TAF COR, RTD | Sim | +| Codigo ICAO | SBGR, KJFK | Sim | +| Data/Hora | 231100Z | Sim | +| Periodo de Previsao | 2312/2418 | Sim | +| Vento de Superficie | 24005KT, VRB03MPS, 24015G25KT | Sim | +| Variacao de Direcao | 180V270 | Sim | +| CAVOK | CAVOK | Sim | +| Visibilidade (ICAO) | 9999, 2500 | Sim | +| Visibilidade (EUA) | 3SM, 1 1/2SM, P6SM | Sim | +| Sem Info Visibilidade | //// | Sim | +| Fenomenos Meteorologicos | -RA, +TSRA, FZFG | Sim | +| Nuvens | FEW020, SCT030CB, OVC005 | Sim | +| Visibilidade Vertical | VV003 | Sim | +| Ceu Claro | SKC, NSC, NCD, CLR | Sim | +| Temperatura Maxima | TX25/0318Z | Sim | +| Temperatura Minima | TNM03/0405Z | Sim | +| TEMPO | TEMPO 0312/0315 ... | Sim | +| BECMG | BECMG 0315/0317 ... | Sim | +| FM | FM031500 ... | Sim | +| PROB30/PROB40 | PROB40 TEMPO ... | Sim | + +## Glossario de Termos TAF + +| Codigo | Significado | +|--------|-------------| +| TAF | Previsao de Aerodromo Terminal | +| AMD | Emenda (forecast emendado) | +| COR | Correcao (forecast corrigido) | +| RTD | Retardado (forecast atrasado) | +| CAVOK | Teto e Visibilidade OK | +| TEMPO | Temporario (flutuacao temporaria) | +| BECMG | Tornando-se (mudanca gradual) | +| FM | A partir de (mudanca completa) | +| PROB | Probabilidade | +| FEW | Poucas nuvens (1-2 oitavos) | +| SCT | Esparsas (3-4 oitavos) | +| BKN | Nublado (5-7 oitavos) | +| OVC | Encoberto (8 oitavos) | +| VV | Visibilidade Vertical | +| CB | Cumulonimbus | +| TCU | Cumulus em Torre | +| TX | Temperatura Maxima | +| TN | Temperatura Minima | +| RA | Chuva | +| SN | Neve | +| FG | Nevoeiro | +| BR | Nevoa | +| TS | Trovoada | +| FZ | Congelante | +| SH | Pancadas | +| VRB | Variavel | + +## Licenca + +MIT License diff --git a/src/Metar.Decoder/ChunkDecoder/Abstract/IMetarChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/Abstract/IMetarChunkDecoder.cs index 7a32ee3..51c9f14 100644 --- a/src/Metar.Decoder/ChunkDecoder/Abstract/IMetarChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/Abstract/IMetarChunkDecoder.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public interface IMetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/Abstract/MetarChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/Abstract/MetarChunkDecoder.cs index f4cacef..ca39ad4 100644 --- a/src/Metar.Decoder/ChunkDecoder/Abstract/MetarChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/Abstract/MetarChunkDecoder.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public abstract class MetarChunkDecoder : IMetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/CloudChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/CloudChunkDecoder.cs index e132ed2..1a57abb 100644 --- a/src/Metar.Decoder/ChunkDecoder/CloudChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/CloudChunkDecoder.cs @@ -1,7 +1,7 @@ using Metar.Decoder.Entity; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class CloudChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs index f2ff3b5..f4953a8 100644 --- a/src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class DatetimeChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/IcaoChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/IcaoChunkDecoder.cs index 9c8930a..5021c18 100644 --- a/src/Metar.Decoder/ChunkDecoder/IcaoChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/IcaoChunkDecoder.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class IcaoChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/PresentWeatherChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/PresentWeatherChunkDecoder.cs index e20b090..1854552 100644 --- a/src/Metar.Decoder/ChunkDecoder/PresentWeatherChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/PresentWeatherChunkDecoder.cs @@ -1,7 +1,7 @@ using Metar.Decoder.Entity; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class PresentWeatherChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/PressureChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/PressureChunkDecoder.cs index 5fb693e..cca9b2a 100644 --- a/src/Metar.Decoder/ChunkDecoder/PressureChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/PressureChunkDecoder.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using static Metar.Decoder.Entity.Value; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { /// /// Chunk decoder for atmospheric pressure section. diff --git a/src/Metar.Decoder/ChunkDecoder/RecentWeatherChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/RecentWeatherChunkDecoder.cs index ebd4127..eda0293 100644 --- a/src/Metar.Decoder/ChunkDecoder/RecentWeatherChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/RecentWeatherChunkDecoder.cs @@ -1,7 +1,7 @@ using Metar.Decoder.Entity; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class RecentWeatherChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/RemarkChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/RemarkChunkDecoder.cs new file mode 100644 index 0000000..9732749 --- /dev/null +++ b/src/Metar.Decoder/ChunkDecoder/RemarkChunkDecoder.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; + +namespace Metar.Decoder.ChunkDecoder +{ + /// + /// Chunk decoder for METAR remarks section (RMK). + /// Per ICAO Annex 3, the remarks section contains supplementary information + /// including sea-level pressure (SLPnnn), hourly precipitation, etc. + /// + public sealed class RemarkChunkDecoder : MetarChunkDecoder + { + public const string RemarkParameterName = "Remark"; + public const string SeaLevelPressureParameterName = "SeaLevelPressure"; + + public override string GetRegex() + { + return @"^RMK (.*)"; + } + + public override Dictionary Parse(string remainingMetar, bool withCavok = false) + { + var consumed = Consume(remainingMetar); + var found = consumed.Value; + var newRemainingMetar = consumed.Key; + var result = new Dictionary(); + + if (found.Count > 1) + { + var remarkContent = found[1].Value.Trim(); + result.Add(RemarkParameterName, remarkContent); + + // Try to extract sea-level pressure (SLPnnn) + var slpMatch = System.Text.RegularExpressions.Regex.Match( + remarkContent, + @"SLP(\d{3})", + System.Text.RegularExpressions.RegexOptions.None, + System.TimeSpan.FromMilliseconds(500)); + if (slpMatch.Success) + { + var slpValue = int.Parse(slpMatch.Groups[1].Value); + double pressureHpa = slpValue >= 500 ? (900.0 + slpValue / 10.0) : (1000.0 + slpValue / 10.0); + result.Add(SeaLevelPressureParameterName, new Entity.Value(pressureHpa, Entity.Value.Unit.HectoPascal)); + } + } + + return GetResults(newRemainingMetar, result); + } + } +} diff --git a/src/Metar.Decoder/ChunkDecoder/ReportStatusChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/ReportStatusChunkDecoder.cs index f04fca5..a3d4208 100644 --- a/src/Metar.Decoder/ChunkDecoder/ReportStatusChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/ReportStatusChunkDecoder.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class ReportStatusChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs index ca0cc76..194cd7b 100644 --- a/src/Metar.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using static Metar.Decoder.Entity.DecodedMetar; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class ReportTypeChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/RunwayVisualRangeChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/RunwayVisualRangeChunkDecoder.cs index dd5f490..b3fd5c3 100644 --- a/src/Metar.Decoder/ChunkDecoder/RunwayVisualRangeChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/RunwayVisualRangeChunkDecoder.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using static Metar.Decoder.Entity.RunwayVisualRange; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class RunwayVisualRangeChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs index 0c376ea..dbd5344 100644 --- a/src/Metar.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs @@ -1,7 +1,7 @@ using Metar.Decoder.Entity; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class SurfaceWindChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs index dbc38ca..a5268c9 100644 --- a/src/Metar.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs @@ -1,7 +1,7 @@ using Metar.Decoder.Entity; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class TemperatureChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/TrendChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/TrendChunkDecoder.cs new file mode 100644 index 0000000..9867dd6 --- /dev/null +++ b/src/Metar.Decoder/ChunkDecoder/TrendChunkDecoder.cs @@ -0,0 +1,52 @@ +using Metar.Decoder.Entity; +using System.Collections.Generic; + +namespace Metar.Decoder.ChunkDecoder +{ + /// + /// Chunk decoder for METAR trend forecast section (NOSIG, BECMG, TEMPO). + /// Per ICAO Annex 3, METAR can include a trend forecast indicating expected changes. + /// + public sealed class TrendChunkDecoder : MetarChunkDecoder + { + public const string TrendForecastParameterName = "TrendForecast"; + public const string TrendTypeParameterName = "TrendType"; + + public override string GetRegex() + { + return @"^(NOSIG|BECMG\s+.*|TEMPO\s+.*)( )"; + } + + public override Dictionary Parse(string remainingMetar, bool withCavok = false) + { + var consumed = Consume(remainingMetar); + var found = consumed.Value; + var newRemainingMetar = consumed.Key; + var result = new Dictionary(); + + if (found.Count > 1) + { + var trendRaw = found[1].Value.Trim(); + string trendType; + + if (trendRaw.StartsWith("NOSIG")) + { + trendType = "NOSIG"; + } + else if (trendRaw.StartsWith("BECMG")) + { + trendType = "BECMG"; + } + else + { + trendType = "TEMPO"; + } + + result.Add(TrendTypeParameterName, trendType); + result.Add(TrendForecastParameterName, trendRaw); + } + + return GetResults(newRemainingMetar, result); + } + } +} diff --git a/src/Metar.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs index 1d5adcd..d6b9a32 100644 --- a/src/Metar.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class VisibilityChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/ChunkDecoder/WindShearChunkDecoder.cs b/src/Metar.Decoder/ChunkDecoder/WindShearChunkDecoder.cs index e1f12ad..dbd3da3 100644 --- a/src/Metar.Decoder/ChunkDecoder/WindShearChunkDecoder.cs +++ b/src/Metar.Decoder/ChunkDecoder/WindShearChunkDecoder.cs @@ -1,7 +1,7 @@ using Metar.Decoder.Entity; using System.Collections.Generic; -namespace Metar.Decoder.Chunkdecoder +namespace Metar.Decoder.ChunkDecoder { public sealed class WindShearChunkDecoder : MetarChunkDecoder { diff --git a/src/Metar.Decoder/Entity/DecodedMetar.cs b/src/Metar.Decoder/Entity/DecodedMetar.cs index 039fe02..ac0b2c7 100644 --- a/src/Metar.Decoder/Entity/DecodedMetar.cs +++ b/src/Metar.Decoder/Entity/DecodedMetar.cs @@ -150,7 +150,27 @@ public ReadOnlyCollection DecodingExceptions /// public List WindshearRunways { get; set; } - internal DecodedMetar(string rawMetar = "") + /// + /// Trend forecast type (NOSIG, BECMG, TEMPO) + /// + public string TrendType { get; set; } = string.Empty; + + /// + /// Trend forecast raw content + /// + public string TrendForecast { get; set; } = string.Empty; + + /// + /// Remarks section raw content (after RMK) + /// + public string Remark { get; set; } = string.Empty; + + /// + /// Sea-level pressure extracted from remarks (SLPnnn) + /// + public Value SeaLevelPressure { get; set; } + + public DecodedMetar(string rawMetar = "") { RawMetar = rawMetar; } diff --git a/src/Metar.Decoder/Exception/MetarChunkDecoderException.cs b/src/Metar.Decoder/Exception/MetarChunkDecoderException.cs index acd5f81..9f4fb86 100644 --- a/src/Metar.Decoder/Exception/MetarChunkDecoderException.cs +++ b/src/Metar.Decoder/Exception/MetarChunkDecoderException.cs @@ -1,4 +1,4 @@ -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using System; using System.Runtime.Serialization; using System.Security.Permissions; diff --git a/src/Metar.Decoder/Metar.Decoder.csproj b/src/Metar.Decoder/Metar.Decoder.csproj index d02a51e..4238839 100644 --- a/src/Metar.Decoder/Metar.Decoder.csproj +++ b/src/Metar.Decoder/Metar.Decoder.csproj @@ -1,7 +1,7 @@ netstandard2.0;net8.0;net10.0;net48 - 1.0.8 + 1.0.9 $(NoWarn);CS1591;SYSLIB0001;SYSLIB0002;SYSLIB0003 https://github.com/afonsoft/metar-decoder git diff --git a/src/Metar.Decoder/MetarDecoder.cs b/src/Metar.Decoder/MetarDecoder.cs index f690466..d394800 100644 --- a/src/Metar.Decoder/MetarDecoder.cs +++ b/src/Metar.Decoder/MetarDecoder.cs @@ -1,4 +1,4 @@ -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using System; using System.Collections.Generic; @@ -40,6 +40,8 @@ public MetarDecoder() new PressureChunkDecoder(), new RecentWeatherChunkDecoder(), new WindShearChunkDecoder(), + new TrendChunkDecoder(), + new RemarkChunkDecoder(), }); private bool _globalStrictParsing = false; diff --git a/src/Taf.Decoder/ChunkDecoder/Abstract/ITafChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/Abstract/ITafChunkDecoder.cs index a6cfdec..60cd53b 100644 --- a/src/Taf.Decoder/ChunkDecoder/Abstract/ITafChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/Abstract/ITafChunkDecoder.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public interface ITafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/Abstract/TafChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/Abstract/TafChunkDecoder.cs index b1b9e35..99d268a 100644 --- a/src/Taf.Decoder/ChunkDecoder/Abstract/TafChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/Abstract/TafChunkDecoder.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text.RegularExpressions; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public abstract class TafChunkDecoder : ITafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/CloudChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/CloudChunkDecoder.cs index 4e14127..9fc0031 100644 --- a/src/Taf.Decoder/ChunkDecoder/CloudChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/CloudChunkDecoder.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class CloudChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs index cb93602..747e226 100644 --- a/src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/DatetimeChunkDecoder.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class DatetimeChunkDecoder : TafChunkDecoder { @@ -31,7 +31,7 @@ public override Dictionary Parse(string remainingTaf, bool withC var hour = Convert.ToInt32(found[2].Value); var minute = Convert.ToInt32(found[3].Value); - if (!checkValidity(day, hour, minute)) + if (!CheckValidity(day, hour, minute)) { throw new TafChunkDecoderException(remainingTaf, newRemainingTaf, TafChunkDecoderException.Messages.InvalidDayHourMinuteRanges, this); } @@ -70,7 +70,7 @@ public override Dictionary Parse(string remainingTaf, bool withC /// /// /// - private bool checkValidity(int day, int hour, int minute) + private bool CheckValidity(int day, int hour, int minute) { // check value range if (day < 1 || day > 31) diff --git a/src/Taf.Decoder/ChunkDecoder/EvolutionChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/EvolutionChunkDecoder.cs index f7afacc..075f8d2 100644 --- a/src/Taf.Decoder/ChunkDecoder/EvolutionChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/EvolutionChunkDecoder.cs @@ -3,9 +3,9 @@ using System.Collections.ObjectModel; using System.Linq; using System.Text.RegularExpressions; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class EvolutionChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/ForecastPeriodChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/ForecastPeriodChunkDecoder.cs index 1967a00..cacb1f7 100644 --- a/src/Taf.Decoder/ChunkDecoder/ForecastPeriodChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/ForecastPeriodChunkDecoder.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public class ForecastPeriodChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/IcaoChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/IcaoChunkDecoder.cs index 7479b90..fecd876 100644 --- a/src/Taf.Decoder/ChunkDecoder/IcaoChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/IcaoChunkDecoder.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class IcaoChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs index 063d92e..73e6289 100644 --- a/src/Taf.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/ReportTypeChunkDecoder.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class ReportTypeChunkDecoder : TafChunkDecoder { @@ -21,13 +22,13 @@ public override Dictionary Parse(string remainingTaf, bool withC // handle the case where nothing has been found if (found.Count <= 1) { - result.Add(TypeParameterName, entity.DecodedTaf.TafType.NULL); + result.Add(TypeParameterName, DecodedTaf.TafType.NULL); } else { // retrieve found params // 'TAF' sometimes happens to be duplicated - result.Add(TypeParameterName, (entity.DecodedTaf.TafType)Enum.Parse(typeof(entity.DecodedTaf.TafType), found[1].Value.Replace("TAF TAF", "TAF").Replace(" ", string.Empty))); + result.Add(TypeParameterName, (DecodedTaf.TafType)Enum.Parse(typeof(DecodedTaf.TafType), found[1].Value.Replace("TAF TAF", "TAF").Replace(" ", string.Empty))); } return GetResults(newRemainingTaf, result); diff --git a/src/Taf.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs index 267bb62..557a388 100644 --- a/src/Taf.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/SurfaceWindChunkDecoder.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class SurfaceWindChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs index d0d4302..aef6ba6 100644 --- a/src/Taf.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/TemperatureChunkDecoder.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class TemperatureChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs index 4ff9a4c..45dd0ab 100644 --- a/src/Taf.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/VisibilityChunkDecoder.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class VisibilityChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/ChunkDecoder/WeatherChunkDecoder.cs b/src/Taf.Decoder/ChunkDecoder/WeatherChunkDecoder.cs index 404d4ed..6a55ba1 100644 --- a/src/Taf.Decoder/ChunkDecoder/WeatherChunkDecoder.cs +++ b/src/Taf.Decoder/ChunkDecoder/WeatherChunkDecoder.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder.chunkdecoder +namespace Taf.Decoder.ChunkDecoder { public sealed class WeatherChunkDecoder : TafChunkDecoder { diff --git a/src/Taf.Decoder/Entity/BaseEntity.cs b/src/Taf.Decoder/Entity/AbstractEntity.cs similarity index 91% rename from src/Taf.Decoder/Entity/BaseEntity.cs rename to src/Taf.Decoder/Entity/AbstractEntity.cs index 8eeaa64..3da6088 100644 --- a/src/Taf.Decoder/Entity/BaseEntity.cs +++ b/src/Taf.Decoder/Entity/AbstractEntity.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public class AbstractEntity { diff --git a/src/Taf.Decoder/Entity/CloudLayer.cs b/src/Taf.Decoder/Entity/CloudLayer.cs index 551e76a..40e9cd6 100644 --- a/src/Taf.Decoder/Entity/CloudLayer.cs +++ b/src/Taf.Decoder/Entity/CloudLayer.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public sealed class CloudLayer : AbstractEntity { diff --git a/src/Taf.Decoder/Entity/DecodedTaf.cs b/src/Taf.Decoder/Entity/DecodedTaf.cs index 9fed6b9..24f21c8 100644 --- a/src/Taf.Decoder/Entity/DecodedTaf.cs +++ b/src/Taf.Decoder/Entity/DecodedTaf.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public sealed class DecodedTaf : AbstractEntity { @@ -117,7 +117,7 @@ public ReadOnlyCollection DecodingExceptions /// public Temperature MaximumTemperature { get; set; } - internal DecodedTaf(string rawTaf = "") + public DecodedTaf(string rawTaf = "") { RawTaf = rawTaf; } diff --git a/src/Taf.Decoder/Entity/Evolution.cs b/src/Taf.Decoder/Entity/Evolution.cs index ccd84f3..81a9ce4 100644 --- a/src/Taf.Decoder/Entity/Evolution.cs +++ b/src/Taf.Decoder/Entity/Evolution.cs @@ -1,6 +1,6 @@ using System; -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public class Evolution : AbstractEntity, ICloneable { diff --git a/src/Taf.Decoder/Entity/ForecastPeriod.cs b/src/Taf.Decoder/Entity/ForecastPeriod.cs index ca9bf7a..33fe2d3 100644 --- a/src/Taf.Decoder/Entity/ForecastPeriod.cs +++ b/src/Taf.Decoder/Entity/ForecastPeriod.cs @@ -1,4 +1,4 @@ -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public sealed class ForecastPeriod { diff --git a/src/Taf.Decoder/Entity/SurfaceWind.cs b/src/Taf.Decoder/Entity/SurfaceWind.cs index db3f6d5..42a8ec3 100644 --- a/src/Taf.Decoder/Entity/SurfaceWind.cs +++ b/src/Taf.Decoder/Entity/SurfaceWind.cs @@ -1,4 +1,4 @@ -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public sealed class SurfaceWind : AbstractEntity { diff --git a/src/Taf.Decoder/Entity/Temperature.cs b/src/Taf.Decoder/Entity/Temperature.cs index bf978e2..41454b0 100644 --- a/src/Taf.Decoder/Entity/Temperature.cs +++ b/src/Taf.Decoder/Entity/Temperature.cs @@ -1,4 +1,4 @@ -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public class Temperature : AbstractEntity { diff --git a/src/Taf.Decoder/Entity/Value.cs b/src/Taf.Decoder/Entity/Value.cs index 2611d5a..654dc9f 100644 --- a/src/Taf.Decoder/Entity/Value.cs +++ b/src/Taf.Decoder/Entity/Value.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using System.Text.RegularExpressions; -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { [DebuggerDisplay("{ActualValue} {ActualUnit}")] public sealed class Value diff --git a/src/Taf.Decoder/Entity/Visibility.cs b/src/Taf.Decoder/Entity/Visibility.cs index fc3937e..1b34234 100644 --- a/src/Taf.Decoder/Entity/Visibility.cs +++ b/src/Taf.Decoder/Entity/Visibility.cs @@ -1,4 +1,4 @@ -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public sealed class Visibility : AbstractEntity { diff --git a/src/Taf.Decoder/Entity/WeatherPhenomenon.cs b/src/Taf.Decoder/Entity/WeatherPhenomenon.cs index a8e0ddb..01b352b 100644 --- a/src/Taf.Decoder/Entity/WeatherPhenomenon.cs +++ b/src/Taf.Decoder/Entity/WeatherPhenomenon.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Taf.Decoder.entity +namespace Taf.Decoder.Entity { public sealed class WeatherPhenomenon : AbstractEntity { diff --git a/src/Taf.Decoder/Exception/TafChunkDecoderException.cs b/src/Taf.Decoder/Exception/TafChunkDecoderException.cs index aea1852..93e5126 100644 --- a/src/Taf.Decoder/Exception/TafChunkDecoderException.cs +++ b/src/Taf.Decoder/Exception/TafChunkDecoderException.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; using System.Security.Permissions; -using Taf.Decoder.chunkdecoder; +using Taf.Decoder.ChunkDecoder; namespace Taf.Decoder { diff --git a/src/Taf.Decoder/Taf.Decoder.csproj b/src/Taf.Decoder/Taf.Decoder.csproj index 5d46017..afa3d91 100644 --- a/src/Taf.Decoder/Taf.Decoder.csproj +++ b/src/Taf.Decoder/Taf.Decoder.csproj @@ -1,7 +1,7 @@ netstandard2.0;net8.0;net10.0;net48 - 1.0.6 + 1.0.7 $(NoWarn);CS1591;SYSLIB0001;SYSLIB0002;SYSLIB0003 https://github.com/afonsoft/metar-decoder git diff --git a/src/Taf.Decoder/TafDecoder.cs b/src/Taf.Decoder/TafDecoder.cs index bbb67bf..7409080 100644 --- a/src/Taf.Decoder/TafDecoder.cs +++ b/src/Taf.Decoder/TafDecoder.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text.RegularExpressions; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; namespace Taf.Decoder { diff --git a/tests/Metar.Decoder.Tests/BasicTest.cs b/tests/Metar.Decoder.Tests/BasicTest.cs index bd1ce26..255197d 100644 --- a/tests/Metar.Decoder.Tests/BasicTest.cs +++ b/tests/Metar.Decoder.Tests/BasicTest.cs @@ -1,5 +1,5 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -7,7 +7,7 @@ using System.Collections.ObjectModel; using System.Linq; -namespace Metar.Decoder_tests +namespace Metar.Decoder.Tests { /// /// BasicTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs index 90606d3..f193f44 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs @@ -1,12 +1,12 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; using static Metar.Decoder.Entity.CloudLayer; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { /// /// CloudChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs index 82a5347..06037bc 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using NUnit.Framework; using NUnit.Framework.Legacy; using System; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("DatetimeChunkDecoder")] public class DatetimeChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs index bb10927..788f225 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using NUnit.Framework; using NUnit.Framework.Legacy; using System; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { /// /// IcaoChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/PresentWeatherChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/PresentWeatherChunkDecoderTest.cs index 0accae8..84cbc41 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/PresentWeatherChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/PresentWeatherChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("PresentWeatherChunkDecoder")] public class PresentWeatherChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/PressureChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/PressureChunkDecoderTest.cs index eaeb5f3..67a785f 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/PressureChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/PressureChunkDecoderTest.cs @@ -1,5 +1,5 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; @@ -7,7 +7,7 @@ using System.Collections.Generic; using static Metar.Decoder.Entity.Value; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("PressureChunkDecoder")] public class PressureChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/RecentWeatherChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/RecentWeatherChunkDecoderTest.cs index d71c6e9..60bb8f5 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/RecentWeatherChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/RecentWeatherChunkDecoderTest.cs @@ -1,12 +1,12 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("RecentWeatherChunkDecoder")] public class RecentWeatherChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/RemarkChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/RemarkChunkDecoderTest.cs new file mode 100644 index 0000000..bbf281d --- /dev/null +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/RemarkChunkDecoderTest.cs @@ -0,0 +1,57 @@ +using Metar.Decoder.ChunkDecoder; +using Metar.Decoder.Entity; +using NUnit.Framework; +using NUnit.Framework.Legacy; +using System.Collections.Generic; + +namespace Metar.Decoder.Tests.ChunkDecoder +{ + [TestFixture, Category("RemarkChunkDecoder")] + public class RemarkChunkDecoderTest + { + private RemarkChunkDecoder decoder; + + [SetUp] + public void Setup() + { + decoder = new RemarkChunkDecoder(); + } + + [Test] + public void TestParseRemark() + { + var result = decoder.Parse("RMK AO2 SLP013 T00720044 "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.IsTrue(decoded.ContainsKey(RemarkChunkDecoder.RemarkParameterName)); + ClassicAssert.IsTrue(((string)decoded[RemarkChunkDecoder.RemarkParameterName]).Contains("AO2")); + } + + [Test] + public void TestParseSeaLevelPressure() + { + var result = decoder.Parse("RMK AO2 SLP013 T00720044 "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.IsTrue(decoded.ContainsKey(RemarkChunkDecoder.SeaLevelPressureParameterName)); + var slp = (Value)decoded[RemarkChunkDecoder.SeaLevelPressureParameterName]; + ClassicAssert.AreEqual(1001.3, slp.ActualValue, 0.01); + ClassicAssert.AreEqual(Value.Unit.HectoPascal, slp.ActualUnit); + } + + [Test] + public void TestParseSeaLevelPressureHigh() + { + var result = decoder.Parse("RMK SLP982 "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + var slp = (Value)decoded[RemarkChunkDecoder.SeaLevelPressureParameterName]; + ClassicAssert.AreEqual(998.2, slp.ActualValue, 0.01); + } + + [Test] + public void TestParseNoRemark() + { + var result = decoder.Parse("SOMETHING "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.IsFalse(decoded.ContainsKey(RemarkChunkDecoder.RemarkParameterName)); + } + } +} diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/ReportStatusChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/ReportStatusChunkDecoderTest.cs index 5bab3ef..0b60b9c 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/ReportStatusChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/ReportStatusChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using NUnit.Framework; using NUnit.Framework.Legacy; using System; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("ReportStatusChunkDecoder")] public class ReportStatusChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs index 6afd7d2..1dd66d7 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs @@ -1,12 +1,12 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using NUnit.Framework; using NUnit.Framework.Legacy; using System; using System.Collections.Generic; using static Metar.Decoder.Entity.DecodedMetar; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("ReportTypeChunkDecoder")] public class ReportTypeChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/RunwayVisualRangeChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/RunwayVisualRangeChunkDecoderTest.cs index 6f940ed..6f6c749 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/RunwayVisualRangeChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/RunwayVisualRangeChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("RunwayVisualRangeChunkDecoder")] public class RunwayVisualRangeChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs index 4a7186c..61096d6 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("SurfaceWindChunkDecoder")] public class SurfaceWindChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs index db425fe..2d6203c 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("TemperatureChunkDecoder")] public class TemperatureChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/TrendChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/TrendChunkDecoderTest.cs new file mode 100644 index 0000000..c65a0ae --- /dev/null +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/TrendChunkDecoderTest.cs @@ -0,0 +1,52 @@ +using Metar.Decoder.ChunkDecoder; +using NUnit.Framework; +using NUnit.Framework.Legacy; +using System.Collections.Generic; + +namespace Metar.Decoder.Tests.ChunkDecoder +{ + [TestFixture, Category("TrendChunkDecoder")] + public class TrendChunkDecoderTest + { + private TrendChunkDecoder decoder; + + [SetUp] + public void Setup() + { + decoder = new TrendChunkDecoder(); + } + + [Test] + public void TestParseNosig() + { + var result = decoder.Parse("NOSIG "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.AreEqual("NOSIG", decoded[TrendChunkDecoder.TrendTypeParameterName]); + ClassicAssert.AreEqual("NOSIG", decoded[TrendChunkDecoder.TrendForecastParameterName]); + } + + [Test] + public void TestParseBecmg() + { + var result = decoder.Parse("BECMG 24010KT "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.AreEqual("BECMG", decoded[TrendChunkDecoder.TrendTypeParameterName]); + } + + [Test] + public void TestParseTempo() + { + var result = decoder.Parse("TEMPO 3000 BR "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.AreEqual("TEMPO", decoded[TrendChunkDecoder.TrendTypeParameterName]); + } + + [Test] + public void TestParseNoTrend() + { + var result = decoder.Parse("SOMETHING "); + var decoded = result[MetarDecoder.ResultKey] as Dictionary; + ClassicAssert.IsFalse(decoded.ContainsKey(TrendChunkDecoder.TrendTypeParameterName)); + } + } +} diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs index ff604db..8485f23 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs @@ -1,11 +1,11 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using Metar.Decoder.Entity; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("VisibilityChunkDecoder")] public class VisibilityChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/ChunkDecoder/WindShearChunkDecoderTest.cs b/tests/Metar.Decoder.Tests/ChunkDecoder/WindShearChunkDecoderTest.cs index e1edcd4..f28a252 100644 --- a/tests/Metar.Decoder.Tests/ChunkDecoder/WindShearChunkDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/ChunkDecoder/WindShearChunkDecoderTest.cs @@ -1,10 +1,10 @@ using Metar.Decoder; -using Metar.Decoder.Chunkdecoder; +using Metar.Decoder.ChunkDecoder; using NUnit.Framework; using NUnit.Framework.Legacy; using System.Collections.Generic; -namespace Metar.Decoder_tests.chunkdecoder +namespace Metar.Decoder.Tests.ChunkDecoder { [TestFixture, Category("WindShearChunkDecoder")] public class WindShearChunkDecoderTest diff --git a/tests/Metar.Decoder.Tests/Integration.cs b/tests/Metar.Decoder.Tests/Integration.cs index 0279aad..247da54 100644 --- a/tests/Metar.Decoder.Tests/Integration.cs +++ b/tests/Metar.Decoder.Tests/Integration.cs @@ -8,7 +8,7 @@ using static Metar.Decoder.Entity.CloudLayer; using static Metar.Decoder.Entity.DecodedMetar; -namespace Metar.Decoder_tests +namespace Metar.Decoder.Tests { /// /// Integration diff --git a/tests/Metar.Decoder.Tests/MetarChunkDecoderExceptionTest.cs b/tests/Metar.Decoder.Tests/MetarChunkDecoderExceptionTest.cs index 9b24c16..10cc5af 100644 --- a/tests/Metar.Decoder.Tests/MetarChunkDecoderExceptionTest.cs +++ b/tests/Metar.Decoder.Tests/MetarChunkDecoderExceptionTest.cs @@ -6,7 +6,7 @@ using System.Text.Json; using System.Xml.Serialization; -namespace Metar.Decoder_tests +namespace Metar.Decoder.Tests { /// /// MetarChunkDecoderExceptionTest diff --git a/tests/Metar.Decoder.Tests/MetarDecoderComprehensiveTest.cs b/tests/Metar.Decoder.Tests/MetarDecoderComprehensiveTest.cs new file mode 100644 index 0000000..ba8209e --- /dev/null +++ b/tests/Metar.Decoder.Tests/MetarDecoderComprehensiveTest.cs @@ -0,0 +1,403 @@ +using Metar.Decoder; +using Metar.Decoder.Entity; +using NUnit.Framework; +using NUnit.Framework.Legacy; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using static Metar.Decoder.Entity.CloudLayer; +using static Metar.Decoder.Entity.DecodedMetar; +using static Metar.Decoder.Entity.RunwayVisualRange; +using static Metar.Decoder.Entity.Value; + +namespace Metar.Decoder.Tests +{ + [TestFixture, Category("MetarDecoderComprehensiveTest")] + public class MetarDecoderComprehensiveTest + { + private MetarDecoder decoder; + + [SetUp] + public void Setup() + { + decoder = new MetarDecoder(); + } + + [Test] + public void TestParseMetarWithCavok() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT CAVOK 17/10 Q1009 "); + ClassicAssert.IsTrue(d.Cavok); + ClassicAssert.AreEqual(MetarType.METAR, d.Type); + ClassicAssert.AreEqual("LFPO", d.ICAO); + } + + [Test] + public void TestParseMetarWithVariableWind() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z VRB02KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.IsTrue(d.SurfaceWind.VariableDirection); + ClassicAssert.IsNull(d.SurfaceWind.MeanDirection); + ClassicAssert.AreEqual(2, d.SurfaceWind.MeanSpeed.ActualValue); + } + + [Test] + public void TestParseMetarWithGust() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24015G25KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(240, d.SurfaceWind.MeanDirection.ActualValue); + ClassicAssert.AreEqual(15, d.SurfaceWind.MeanSpeed.ActualValue); + ClassicAssert.AreEqual(25, d.SurfaceWind.SpeedVariations.ActualValue); + ClassicAssert.AreEqual(Unit.Knot, d.SurfaceWind.MeanSpeed.ActualUnit); + } + + [Test] + public void TestParseMetarWithDirectionVariation() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 180V270 9999 FEW020 17/10 Q1009 "); + ClassicAssert.IsNotNull(d.SurfaceWind.DirectionVariations); + ClassicAssert.AreEqual(180, d.SurfaceWind.DirectionVariations[0].ActualValue); + ClassicAssert.AreEqual(270, d.SurfaceWind.DirectionVariations[1].ActualValue); + } + + [Test] + public void TestParseMetarWithNilStatus() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z NIL "); + ClassicAssert.AreEqual("NIL", d.Status); + } + + [Test] + public void TestParseMetarWithMultipleClouds() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 SCT030 BKN060 17/10 Q1009 "); + ClassicAssert.AreEqual(3, d.Clouds.Count); + ClassicAssert.AreEqual(CloudAmount.FEW, d.Clouds[0].Amount); + ClassicAssert.AreEqual(2000, d.Clouds[0].BaseHeight.ActualValue); + ClassicAssert.AreEqual(CloudAmount.SCT, d.Clouds[1].Amount); + ClassicAssert.AreEqual(3000, d.Clouds[1].BaseHeight.ActualValue); + ClassicAssert.AreEqual(CloudAmount.BKN, d.Clouds[2].Amount); + ClassicAssert.AreEqual(6000, d.Clouds[2].BaseHeight.ActualValue); + } + + [Test] + public void TestParseMetarWithCB() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 SCT025CB 17/10 Q1009 "); + ClassicAssert.AreEqual(1, d.Clouds.Count); + ClassicAssert.AreEqual(CloudType.CB, d.Clouds[0].Type); + } + + [Test] + public void TestParseMetarWithTCU() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 BKN035TCU 17/10 Q1009 "); + ClassicAssert.AreEqual(CloudType.TCU, d.Clouds[0].Type); + } + + [Test] + public void TestParseMetarWithClearSky() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 SKC 17/10 Q1009 "); + ClassicAssert.AreEqual(0, d.Clouds.Count); + } + + [Test] + public void TestParseMetarWithNSC() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 NSC 17/10 Q1009 "); + ClassicAssert.AreEqual(0, d.Clouds.Count); + } + + [Test] + public void TestParseMetarWithVerticalVisibility() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 0500 VV003 17/10 Q1009 "); + ClassicAssert.AreEqual(1, d.Clouds.Count); + ClassicAssert.AreEqual(CloudAmount.VV, d.Clouds[0].Amount); + ClassicAssert.AreEqual(300, d.Clouds[0].BaseHeight.ActualValue); + } + + [Test] + public void TestParseMetarWithUSVisibility() + { + var d = decoder.ParseNotStrict("METAR KJFK 231027Z 24004KT 1 1/2SM FEW020 17/10 A2992 "); + ClassicAssert.AreEqual(1.5, d.Visibility.PrevailingVisibility.ActualValue); + ClassicAssert.AreEqual(Unit.StatuteMile, d.Visibility.PrevailingVisibility.ActualUnit); + } + + [Test] + public void TestParseMetarWithMercuryPressure() + { + var d = decoder.ParseNotStrict("METAR KJFK 231027Z 24004KT 9999 FEW020 17/10 A2992 "); + ClassicAssert.AreEqual(29.92, d.Pressure.ActualValue); + ClassicAssert.AreEqual(Unit.MercuryInch, d.Pressure.ActualUnit); + } + + [Test] + public void TestParseMetarWithNegativeTemperature() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 M02/M05 Q1009 "); + ClassicAssert.AreEqual(-2, d.AirTemperature.ActualValue); + ClassicAssert.AreEqual(-5, d.DewPointTemperature.ActualValue); + } + + [Test] + public void TestParseMetarWithWindShearAllRunways() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 WS ALL RWY "); + ClassicAssert.IsTrue(d.WindshearAllRunways.Value); + } + + [Test] + public void TestParseMetarWithMultipleRunwayVisualRange() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 0800 R06L/0600U R24/0400D FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(2, d.RunwaysVisualRange.Count); + ClassicAssert.AreEqual("06L", d.RunwaysVisualRange[0].Runway); + ClassicAssert.AreEqual(Tendency.U, d.RunwaysVisualRange[0].PastTendency); + ClassicAssert.AreEqual("24", d.RunwaysVisualRange[1].Runway); + ClassicAssert.AreEqual(Tendency.D, d.RunwaysVisualRange[1].PastTendency); + } + + [Test] + public void TestParseMetarWithVariableRVR() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 0800 R06L/0200V0600U FEW020 17/10 Q1009 "); + ClassicAssert.IsTrue(d.RunwaysVisualRange[0].Variable); + ClassicAssert.AreEqual(200, d.RunwaysVisualRange[0].VisualRangeInterval[0].ActualValue); + ClassicAssert.AreEqual(600, d.RunwaysVisualRange[0].VisualRangeInterval[1].ActualValue); + } + + [Test] + public void TestParseMetarWithMultipleWeatherPhenomena() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 2000 +TSRA FZDZ FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(2, d.PresentWeather.Count); + ClassicAssert.AreEqual("+", d.PresentWeather[0].IntensityProximity); + ClassicAssert.AreEqual("TS", d.PresentWeather[0].Characteristics); + } + + [Test] + public void TestParseMetarNotStrictWithErrors() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT INVALID_VISIBILITY FEW020 17/10 Q1009 "); + ClassicAssert.IsFalse(d.IsValid); + ClassicAssert.IsTrue(d.DecodingExceptions.Count > 0); + } + + [Test] + public void TestParseMetarStrictWithErrors() + { + var d = decoder.ParseStrict("METAR LFPO 231027Z 24004KT INVALID_VISIBILITY FEW020 17/10 Q1009 "); + ClassicAssert.IsFalse(d.IsValid); + } + + [Test] + public void TestParseSpeciMetar() + { + var d = decoder.ParseNotStrict("SPECI LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(MetarType.SPECI, d.Type); + } + + [Test] + public void TestParseSpeciCorMetar() + { + var d = decoder.ParseNotStrict("SPECI COR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(MetarType.SPECI_COR, d.Type); + } + + [Test] + public void TestParseMetarCor() + { + var d = decoder.ParseNotStrict("METAR COR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(MetarType.METAR_COR, d.Type); + } + + [Test] + public void TestParseMetarWithNDV() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999NDV FEW020 17/10 Q1009 "); + ClassicAssert.IsTrue(d.Visibility.NDV); + } + + [Test] + public void TestParseMetarWithMinimumVisibility() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 5000 2000NE FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(5000, d.Visibility.PrevailingVisibility.ActualValue); + ClassicAssert.AreEqual(2000, d.Visibility.MinimumVisibility.ActualValue); + ClassicAssert.AreEqual("NE", d.Visibility.MinimumVisibilityDirection); + } + + [Test] + public void TestParseMetarWithNoVisibilityInfo() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT //// FEW020 17/10 Q1009 "); + ClassicAssert.IsNull(d.Visibility); + ClassicAssert.IsFalse(d.Cavok); + } + + [Test] + public void TestParseMetarWithNosig() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 NOSIG "); + ClassicAssert.AreEqual("NOSIG", d.TrendType); + } + + [Test] + public void TestParseMetarWithRecentWeather() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 RERA "); + ClassicAssert.IsNotNull(d.RecentWeather); + ClassicAssert.AreEqual("RA", d.RecentWeather.Types[0]); + } + + [Test] + public void TestParseMetarWithKPH() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24010KPH 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(Unit.KilometerPerHour, d.SurfaceWind.MeanSpeed.ActualUnit); + } + + [Test] + public void TestParseMetarWithNoPressureValue() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q//// "); + ClassicAssert.IsNull(d.Pressure); + } + + [Test] + public void TestParseMetarMissingType() + { + var d = decoder.ParseNotStrict("LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(MetarType.NULL, d.Type); + ClassicAssert.AreEqual("LFPO", d.ICAO); + } + + [Test] + public void TestSetStrictParsing() + { + decoder.SetStrictParsing(true); + var d = decoder.Parse("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.IsTrue(d.IsValid); + } + + [Test] + public void TestDecodedMetarResetExceptions() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT INVALID_VIS FEW020 17/10 Q1009 "); + ClassicAssert.IsFalse(d.IsValid); + d.ResetDecodingExceptions(); + ClassicAssert.IsTrue(d.IsValid); + } + + [Test] + public void TestParseMetarWithRVRFeetUnit() + { + var d = decoder.ParseNotStrict("METAR KJFK 231027Z 24004KT 0800 R04R/2000FT FEW020 17/10 A2992 "); + if (d.RunwaysVisualRange.Count > 0) + { + ClassicAssert.AreEqual(Unit.Feet, d.RunwaysVisualRange[0].VisualRange.ActualUnit); + } + } + + [Test] + public void TestParseMetarWithOVCClouds() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 3000 OVC005 17/10 Q1009 "); + ClassicAssert.AreEqual(1, d.Clouds.Count); + ClassicAssert.AreEqual(CloudAmount.OVC, d.Clouds[0].Amount); + ClassicAssert.AreEqual(500, d.Clouds[0].BaseHeight.ActualValue); + } + + [Test] + public void TestParseMetarWithAutoStatus() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z AUTO 24004KT 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual("AUTO", d.Status); + } + + [Test] + public void TestParseMetarWithFractionalUSVisibility() + { + var d = decoder.ParseNotStrict("METAR KJFK 231027Z 24004KT 3/4SM FEW020 17/10 A2992 "); + ClassicAssert.AreEqual(0.75, d.Visibility.PrevailingVisibility.ActualValue, 0.001); + ClassicAssert.AreEqual(Unit.StatuteMile, d.Visibility.PrevailingVisibility.ActualUnit); + } + + [Test] + public void TestParseRealWorldMetar() + { + var d = decoder.ParseNotStrict("METAR SBGR 031000Z 35006KT 9999 FEW040 SCT100 27/18 Q1020 "); + ClassicAssert.IsTrue(d.IsValid); + ClassicAssert.AreEqual("SBGR", d.ICAO); + ClassicAssert.AreEqual(3, d.Day); + ClassicAssert.AreEqual("10:00 UTC", d.Time); + ClassicAssert.AreEqual(350, d.SurfaceWind.MeanDirection.ActualValue); + ClassicAssert.AreEqual(6, d.SurfaceWind.MeanSpeed.ActualValue); + ClassicAssert.AreEqual(9999, d.Visibility.PrevailingVisibility.ActualValue); + ClassicAssert.AreEqual(2, d.Clouds.Count); + ClassicAssert.AreEqual(27, d.AirTemperature.ActualValue); + ClassicAssert.AreEqual(18, d.DewPointTemperature.ActualValue); + ClassicAssert.AreEqual(1020, d.Pressure.ActualValue); + } + + [Test] + public void TestParseRealWorldMetarWithPhenomena() + { + var d = decoder.ParseNotStrict("METAR SBSP 031200Z 09005KT 4000 -RA BR BKN010 OVC020 18/17 Q1019 "); + ClassicAssert.IsTrue(d.IsValid); + ClassicAssert.AreEqual("SBSP", d.ICAO); + ClassicAssert.AreEqual(4000, d.Visibility.PrevailingVisibility.ActualValue); + ClassicAssert.IsTrue(d.PresentWeather.Count >= 1); + } + + [Test] + public void TestParseMetarWithWindshearMultipleRunways() + { + var d = decoder.ParseNotStrict("METAR LFPO 231027Z 24004KT 9999 FEW020 17/10 Q1009 WS R03 WS R21 "); + if (d.WindshearRunways != null) + { + ClassicAssert.IsTrue(d.WindshearRunways.Count >= 1); + } + } + + [Test] + public void TestValueConversion() + { + var v = new Value(10, Unit.MeterPerSecond); + var ktValue = v.GetConvertedValue(Unit.Knot); + ClassicAssert.IsTrue(ktValue > 0); + } + + [Test] + public void TestValueToString() + { + var v = new Value(15, Unit.Knot); + ClassicAssert.AreEqual("15 Knot", v.ToString()); + } + + [Test] + public void TestValueToInt() + { + ClassicAssert.AreEqual(10, Value.ToInt("10")); + ClassicAssert.AreEqual(-10, Value.ToInt("M10")); + ClassicAssert.IsNull(Value.ToInt("///")); + } + + [Test] + public void TestParseMetarWithMPS() + { + var d = decoder.ParseNotStrict("METAR UUEE 231027Z 24004MPS 9999 FEW020 17/10 Q1009 "); + ClassicAssert.AreEqual(Unit.MeterPerSecond, d.SurfaceWind.MeanSpeed.ActualUnit); + ClassicAssert.AreEqual(4, d.SurfaceWind.MeanSpeed.ActualValue); + } + + [Test] + public void TestParseMetarLowercaseInput() + { + var d = decoder.ParseNotStrict("metar lfpo 231027z 24004kt 9999 few020 17/10 q1009 "); + ClassicAssert.AreEqual("LFPO", d.ICAO); + } + } +} diff --git a/tests/Metar.Decoder.Tests/MetarDecoderTest.cs b/tests/Metar.Decoder.Tests/MetarDecoderTest.cs index 11c35a3..b2ea675 100644 --- a/tests/Metar.Decoder.Tests/MetarDecoderTest.cs +++ b/tests/Metar.Decoder.Tests/MetarDecoderTest.cs @@ -10,7 +10,7 @@ using static Metar.Decoder.Entity.RunwayVisualRange; using static Metar.Decoder.Entity.Value; -namespace Metar.Decoder_tests +namespace Metar.Decoder.Tests { /// /// MetarDecoderTest diff --git a/tests/Metar.Decoder.Tests/ValueTest.cs b/tests/Metar.Decoder.Tests/ValueTest.cs index 70a8d02..f206636 100644 --- a/tests/Metar.Decoder.Tests/ValueTest.cs +++ b/tests/Metar.Decoder.Tests/ValueTest.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace Metar.Decoder_tests +namespace Metar.Decoder.Tests { /// /// ValueTest diff --git a/tests/Taf.Decoder.Tests/BasicTest.cs b/tests/Taf.Decoder.Tests/BasicTest.cs index 1ba0281..7e04713 100644 --- a/tests/Taf.Decoder.Tests/BasicTest.cs +++ b/tests/Taf.Decoder.Tests/BasicTest.cs @@ -4,9 +4,9 @@ using System.Collections.ObjectModel; using System.Linq; using Taf.Decoder; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests +namespace Taf.Decoder.Tests { [TestFixture] public class BasicTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs index 9ed1d9c..ae7ba67 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/CloudChunkDecoderTest.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("CloudChunkDecoder")] public class CloudChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs index 8c9e272..cfe520b 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/DatetimeChunkDecoderTest.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; +using Taf.Decoder.ChunkDecoder; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("DatetimeChunkDecoder")] public class DatetimeChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/EvolutionChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/EvolutionChunkDecoderTest.cs index 24f21cb..1d1f747 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/EvolutionChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/EvolutionChunkDecoderTest.cs @@ -2,10 +2,10 @@ using NUnit.Framework.Legacy; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("EvolutionChunkDecoder")] public class EvolutionChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/ForecastPeriodChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/ForecastPeriodChunkDecoderTest.cs index 775070d..5908268 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/ForecastPeriodChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/ForecastPeriodChunkDecoderTest.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("ForecastPeriodChunkDecoder")] public class ForecastPeriodChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs index d780677..fcc2771 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/IcaoChunkDecoderTest.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; +using Taf.Decoder.ChunkDecoder; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("IcaoChunkDecoder")] public class IcaoChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs index 3164ad5..5826d5d 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/ReportTypeChunkDecoderTest.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; +using Taf.Decoder.ChunkDecoder; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("ReportTypeChunkDecoder")] public class ReportTypeChunkDecoderTest @@ -13,22 +13,22 @@ public class ReportTypeChunkDecoderTest private static readonly ReportTypeChunkDecoder chunkDecoder = new ReportTypeChunkDecoder(); [Test, TestCaseSource("ValidChunks")] - public void TestParse(Tuple chunk) + public void TestParse(Tuple chunk) { var decoded = chunkDecoder.Parse(chunk.Item1); ClassicAssert.AreEqual(chunk.Item2, (decoded[TafDecoder.ResultKey] as Dictionary)[ReportTypeChunkDecoder.TypeParameterName]); ClassicAssert.AreEqual(chunk.Item3, decoded[TafDecoder.RemainingTafKey]); } - public static List> ValidChunks => new List>() + public static List> ValidChunks => new List>() { - new Tuple("TAF LFPG", Taf.Decoder.entity.DecodedTaf.TafType.TAF, "LFPG"), - new Tuple("TAF TAF LFPG", Taf.Decoder.entity.DecodedTaf.TafType.TAF, "LFPG"), - new Tuple("TAF AMD LFPO", Taf.Decoder.entity.DecodedTaf.TafType.TAFAMD, "LFPO"), - new Tuple("TA LFPG", Taf.Decoder.entity.DecodedTaf.TafType.NULL, "TA LFPG"), - new Tuple("123 LFPO", Taf.Decoder.entity.DecodedTaf.TafType.NULL, "123 LFPO"), - new Tuple("TAF COR LFPO", Taf.Decoder.entity.DecodedTaf.TafType.TAFCOR, "LFPO"), - new Tuple("RTD EKEB", Taf.Decoder.entity.DecodedTaf.TafType.RTD, "EKEB"), + new Tuple("TAF LFPG", Taf.Decoder.Entity.DecodedTaf.TafType.TAF, "LFPG"), + new Tuple("TAF TAF LFPG", Taf.Decoder.Entity.DecodedTaf.TafType.TAF, "LFPG"), + new Tuple("TAF AMD LFPO", Taf.Decoder.Entity.DecodedTaf.TafType.TAFAMD, "LFPO"), + new Tuple("TA LFPG", Taf.Decoder.Entity.DecodedTaf.TafType.NULL, "TA LFPG"), + new Tuple("123 LFPO", Taf.Decoder.Entity.DecodedTaf.TafType.NULL, "123 LFPO"), + new Tuple("TAF COR LFPO", Taf.Decoder.Entity.DecodedTaf.TafType.TAFCOR, "LFPO"), + new Tuple("RTD EKEB", Taf.Decoder.Entity.DecodedTaf.TafType.RTD, "EKEB"), }; } } \ No newline at end of file diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs index 7a5c152..3265b29 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/SurfaceWindChunkDecoderTest.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; -using static Taf.Decoder.entity.Value; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; +using static Taf.Decoder.Entity.Value; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("SurfaceWindChunkDecoder")] public class SurfaceWindChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs index d13cf82..c4bd9a3 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/TemperatureChunkDecoderTest.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("TemperatureChunkDecoder")] public class TemperatureChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs index 32a117f..0449e64 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/VisibilityChunkDecoderTest.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("VisibilityChunkDecoder")] public class VisibilityChunkDecoderTest diff --git a/tests/Taf.Decoder.Tests/ChunkDecoder/WeatherPhenomenonDecoderTest.cs b/tests/Taf.Decoder.Tests/ChunkDecoder/WeatherPhenomenonDecoderTest.cs index f4c6a03..dfa3698 100644 --- a/tests/Taf.Decoder.Tests/ChunkDecoder/WeatherPhenomenonDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/ChunkDecoder/WeatherPhenomenonDecoderTest.cs @@ -3,10 +3,10 @@ using System; using System.Collections.Generic; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests.ChunkDecoder +namespace Taf.Decoder.Tests.ChunkDecoder { [TestFixture, Category("WeatherPhenomenon")] public class WeatherPhenomenonDecoderTest diff --git a/tests/Taf.Decoder.Tests/RtdIntegrationTest.cs b/tests/Taf.Decoder.Tests/RtdIntegrationTest.cs index 0afbbf9..967017d 100644 --- a/tests/Taf.Decoder.Tests/RtdIntegrationTest.cs +++ b/tests/Taf.Decoder.Tests/RtdIntegrationTest.cs @@ -1,6 +1,7 @@ using NUnit.Framework; using NUnit.Framework.Legacy; using Taf.Decoder; +using Taf.Decoder.Entity; namespace Taf.Decoder.Tests { @@ -18,7 +19,7 @@ public void TestParseRtdTaf() var result = decoder.Parse(rtdTaf); // Assert - Most important: RTD type is recognized - ClassicAssert.AreEqual(entity.DecodedTaf.TafType.RTD, result.Type); + ClassicAssert.AreEqual(DecodedTaf.TafType.RTD, result.Type); ClassicAssert.AreEqual("EKEB", result.Icao); ClassicAssert.AreEqual(19, result.Day); ClassicAssert.AreEqual(rtdTaf, result.RawTaf); @@ -41,7 +42,7 @@ public void TestParseRtdTafNotStrict() // Assert ClassicAssert.IsTrue(result.IsValid); - ClassicAssert.AreEqual(entity.DecodedTaf.TafType.RTD, result.Type); + ClassicAssert.AreEqual(DecodedTaf.TafType.RTD, result.Type); ClassicAssert.AreEqual("EKEB", result.Icao); } } diff --git a/tests/Taf.Decoder.Tests/TafDecoderComprehensiveTest.cs b/tests/Taf.Decoder.Tests/TafDecoderComprehensiveTest.cs new file mode 100644 index 0000000..9d47ca7 --- /dev/null +++ b/tests/Taf.Decoder.Tests/TafDecoderComprehensiveTest.cs @@ -0,0 +1,411 @@ +using NUnit.Framework; +using NUnit.Framework.Legacy; +using Taf.Decoder; +using Taf.Decoder.Entity; +using static Taf.Decoder.Entity.CloudLayer; +using static Taf.Decoder.Entity.DecodedTaf; +using static Taf.Decoder.Entity.Value; + +namespace Taf.Decoder.Tests +{ + [TestFixture, Category("TafDecoderComprehensiveTest")] + public class TafDecoderComprehensiveTest + { + private TafDecoder decoder; + + [SetUp] + public void Setup() + { + decoder = new TafDecoder(); + } + + [Test] + public void TestParseTafBasic() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.AreEqual(TafType.TAF, d.Type); + ClassicAssert.AreEqual("LFPO", d.Icao); + ClassicAssert.AreEqual(23, d.Day); + ClassicAssert.AreEqual("11:00 UTC", d.Time); + } + + [Test] + public void TestParseTafWithCavok() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT CAVOK "); + ClassicAssert.IsTrue(d.Cavok); + ClassicAssert.AreEqual("LFPO", d.Icao); + } + + [Test] + public void TestParseTafWithVariableWind() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 VRB03KT 9999 FEW030 "); + ClassicAssert.IsTrue(d.SurfaceWind.VariableDirection); + ClassicAssert.IsNull(d.SurfaceWind.MeanDirection); + ClassicAssert.AreEqual(3, d.SurfaceWind.MeanSpeed.ActualValue); + } + + [Test] + public void TestParseTafWithGust() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24015G25KT 9999 FEW030 "); + ClassicAssert.AreEqual(15, d.SurfaceWind.MeanSpeed.ActualValue); + ClassicAssert.AreEqual(25, d.SurfaceWind.SpeedVariations.ActualValue); + ClassicAssert.AreEqual(Unit.Knot, d.SurfaceWind.MeanSpeed.ActualUnit); + } + + [Test] + public void TestParseTafWithMultipleClouds() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW020 SCT030 BKN060 "); + ClassicAssert.AreEqual(3, d.Clouds.Count); + ClassicAssert.AreEqual(CloudAmount.FEW, d.Clouds[0].Amount); + ClassicAssert.AreEqual(2000, d.Clouds[0].BaseHeight.ActualValue); + ClassicAssert.AreEqual(CloudAmount.SCT, d.Clouds[1].Amount); + ClassicAssert.AreEqual(CloudAmount.BKN, d.Clouds[2].Amount); + } + + [Test] + public void TestParseTafWithCB() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 SCT025CB "); + ClassicAssert.AreEqual(1, d.Clouds.Count); + ClassicAssert.AreEqual(CloudType.CB, d.Clouds[0].Type); + } + + [Test] + public void TestParseTafWithTCU() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 BKN035TCU "); + ClassicAssert.AreEqual(CloudType.TCU, d.Clouds[0].Type); + } + + [Test] + public void TestParseTafWithNSC() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 NSC "); + ClassicAssert.AreEqual(0, d.Clouds.Count); + } + + [Test] + public void TestParseTafWithVerticalVisibility() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 0500 VV003 "); + ClassicAssert.AreEqual(1, d.Clouds.Count); + ClassicAssert.AreEqual(CloudAmount.VV, d.Clouds[0].Amount); + ClassicAssert.AreEqual(300, d.Clouds[0].BaseHeight.ActualValue); + } + + [Test] + public void TestParseTafWithWeatherPhenomena() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 3000 -RA BKN010 "); + ClassicAssert.IsTrue(d.WeatherPhenomenons.Count >= 1); + } + + [Test] + public void TestParseTafWithFZRA() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 2000 FZRA BKN010 "); + ClassicAssert.IsTrue(d.WeatherPhenomenons.Count >= 1); + } + + [Test] + public void TestParseTafWithUSVisibility() + { + var d = decoder.ParseNotStrict("TAF KJFK 231100Z 2312/2418 24005KT 3SM FEW030 "); + ClassicAssert.AreEqual(3, d.Visibility.ActualVisibility.ActualValue); + ClassicAssert.AreEqual(Unit.StatuteMile, d.Visibility.ActualVisibility.ActualUnit); + } + + [Test] + public void TestParseTafWithFractionalUSVisibility() + { + var d = decoder.ParseNotStrict("TAF KJFK 231100Z 2312/2418 24005KT 1 1/2SM FEW030 "); + ClassicAssert.AreEqual(1.5, d.Visibility.ActualVisibility.ActualValue, 0.01); + } + + [Test] + public void TestParseTafWithEvolutions() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 TEMPO 2312/2315 24010KT 3000 -RA BKN010 "); + // Evolutions are stored on each entity (SurfaceWind, Visibility, etc.), not on DecodedTaf directly + ClassicAssert.IsNotNull(d.SurfaceWind); + ClassicAssert.IsTrue(d.SurfaceWind.Evolutions.Count >= 1); + } + + [Test] + public void TestParseTafWithBECMG() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 BECMG 2315/2318 18010KT 9999 SCT020 "); + ClassicAssert.IsNotNull(d.SurfaceWind); + ClassicAssert.IsTrue(d.SurfaceWind.Evolutions.Count >= 1); + ClassicAssert.AreEqual("BECMG", d.SurfaceWind.Evolutions[0].Type); + } + + [Test] + public void TestParseTafWithFM() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 FM231500 18010KT 9999 SCT020 "); + ClassicAssert.IsNotNull(d.SurfaceWind); + ClassicAssert.IsTrue(d.SurfaceWind.Evolutions.Count >= 1); + ClassicAssert.AreEqual("FM", d.SurfaceWind.Evolutions[0].Type); + } + + [Test] + public void TestParseTafWithPROB() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 PROB40 TEMPO 2318/2320 24010KT 2000 TSRA BKN010 "); + // PROB evolutions are stored on the entities + ClassicAssert.IsNotNull(d.SurfaceWind); + } + + [Test] + public void TestParseTafWithTemperature() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 TX25/2314Z TN15/2405Z "); + if (d.MaximumTemperature != null) + { + ClassicAssert.AreEqual("TX", d.MaximumTemperature.Type); + } + if (d.MinimumTemperature != null) + { + ClassicAssert.AreEqual("TN", d.MinimumTemperature.Type); + } + } + + [Test] + public void TestParseTafWithMPS() + { + var d = decoder.ParseNotStrict("TAF UUEE 231100Z 2312/2418 24005MPS 9999 FEW030 "); + ClassicAssert.AreEqual(Unit.MeterPerSecond, d.SurfaceWind.MeanSpeed.ActualUnit); + } + + [Test] + public void TestParseTafWithKPH() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24010KPH 9999 FEW030 "); + ClassicAssert.AreEqual(Unit.KilometerPerHour, d.SurfaceWind.MeanSpeed.ActualUnit); + } + + [Test] + public void TestParseTafAMD() + { + var d = decoder.ParseNotStrict("TAF AMD LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.AreEqual(TafType.TAFAMD, d.Type); + } + + [Test] + public void TestParseTafCOR() + { + var d = decoder.ParseNotStrict("TAF COR LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.AreEqual(TafType.TAFCOR, d.Type); + } + + [Test] + public void TestParseTafStrictWithErrors() + { + var d = decoder.ParseStrict("TAF LFPO 231100Z 2312/2418 INVALID_WIND 9999 FEW030 "); + ClassicAssert.IsFalse(d.IsValid); + } + + [Test] + public void TestParseTafNotStrictWithErrors() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 INVALID_WIND 9999 FEW030 "); + ClassicAssert.IsFalse(d.IsValid); + ClassicAssert.IsTrue(d.DecodingExceptions.Count > 0); + } + + [Test] + public void TestParseTafMissingType() + { + var d = decoder.ParseNotStrict("LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.AreEqual(TafType.NULL, d.Type); + ClassicAssert.AreEqual("LFPO", d.Icao); + } + + [Test] + public void TestParseTafForecastPeriod() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.IsNotNull(d.ForecastPeriod); + ClassicAssert.AreEqual(23, d.ForecastPeriod.FromDay); + ClassicAssert.AreEqual(12, d.ForecastPeriod.FromHour); + ClassicAssert.AreEqual(24, d.ForecastPeriod.ToDay); + ClassicAssert.AreEqual(18, d.ForecastPeriod.ToHour); + } + + [Test] + public void TestParseTafWithOVC() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 3000 OVC005 "); + ClassicAssert.AreEqual(1, d.Clouds.Count); + ClassicAssert.AreEqual(CloudAmount.OVC, d.Clouds[0].Amount); + ClassicAssert.AreEqual(500, d.Clouds[0].BaseHeight.ActualValue); + } + + [Test] + public void TestParseTafResetExceptions() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 INVALID 9999 FEW030 "); + ClassicAssert.IsFalse(d.IsValid); + d.ResetDecodingExceptions(); + ClassicAssert.IsTrue(d.IsValid); + } + + [Test] + public void TestParseTafLowercaseInput() + { + var d = decoder.ParseNotStrict("taf lfpo 231100z 2312/2418 24005kt 9999 few030 "); + ClassicAssert.AreEqual("LFPO", d.Icao); + } + + [Test] + public void TestParseTafWithDirectionVariation() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 180V270 9999 FEW030 "); + if (d.SurfaceWind.DirectionVariations != null) + { + ClassicAssert.AreEqual(180, d.SurfaceWind.DirectionVariations[0].ActualValue); + ClassicAssert.AreEqual(270, d.SurfaceWind.DirectionVariations[1].ActualValue); + } + } + + [Test] + public void TestParseTafNoVisibilityInfo() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT //// FEW030 "); + ClassicAssert.IsNull(d.Visibility); + } + + [Test] + public void TestParseTafWithNegativeTemperature() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 TX02/2314Z TNM10/2405Z "); + if (d.MaximumTemperature != null) + { + ClassicAssert.AreEqual("TX", d.MaximumTemperature.Type); + } + if (d.MinimumTemperature != null) + { + ClassicAssert.AreEqual(-10, d.MinimumTemperature.TemperatureValue.ActualValue); + } + } + + [Test] + public void TestParseRealWorldTaf() + { + var d = decoder.ParseNotStrict("TAF SBGR 031100Z 0312/0418 35008KT 9999 FEW040 SCT100 TX30/0318Z TN20/0409Z "); + ClassicAssert.AreEqual("SBGR", d.Icao); + ClassicAssert.AreEqual(3, d.Day); + ClassicAssert.IsNotNull(d.SurfaceWind); + ClassicAssert.IsNotNull(d.Visibility); + ClassicAssert.IsTrue(d.Clouds.Count >= 1); + } + + [Test] + public void TestParseRealWorldTafWithEvolutions() + { + var d = decoder.ParseNotStrict("TAF SBSP 031200Z 0312/0418 09005KT 9999 FEW025 TEMPO 0312/0315 09010KT 4000 -RA BKN010 BECMG 0315/0317 18010KT 9999 SCT020 "); + ClassicAssert.AreEqual("SBSP", d.Icao); + // Evolutions are stored on each entity + ClassicAssert.IsNotNull(d.SurfaceWind); + ClassicAssert.IsTrue(d.SurfaceWind.Evolutions.Count >= 1); + } + + [Test] + public void TestParseTafWithMultipleEvolutions() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 TEMPO 2312/2315 24010KT 3000 -RA BKN010 BECMG 2315/2318 18010KT 9999 SCT020 FM231800 27015G25KT 9999 SCT025 "); + ClassicAssert.IsNotNull(d.SurfaceWind); + // Multiple evolutions are stored on the wind entity + ClassicAssert.IsTrue(d.SurfaceWind.Evolutions.Count >= 1); + } + + [Test] + public void TestSetStrictParsing() + { + decoder.SetStrictParsing(true); + var d = decoder.Parse("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.IsTrue(d.IsValid); + } + + [Test] + public void TestParseTafRTD() + { + var d = decoder.ParseNotStrict("RTD EKEB 190416Z 1905/1912 13006KT 0200 FZFG BKN001 "); + ClassicAssert.AreEqual(TafType.RTD, d.Type); + ClassicAssert.AreEqual("EKEB", d.Icao); + } + + [Test] + public void TestParseTafWithSKC() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 SKC "); + ClassicAssert.AreEqual(0, d.Clouds.Count); + } + + [Test] + public void TestParseTafWithCLR() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 CLR "); + ClassicAssert.AreEqual(0, d.Clouds.Count); + } + + [Test] + public void TestTafValueConversions() + { + var v = new Value(10, Unit.MeterPerSecond); + var ktValue = v.GetConvertedValue(Unit.Knot); + ClassicAssert.IsTrue(ktValue > 0); + } + + [Test] + public void TestTafValueToString() + { + var v = new Value(15, Unit.Knot); + ClassicAssert.AreEqual("15 Knot", v.ToString()); + } + + [Test] + public void TestTafValueToInt() + { + ClassicAssert.AreEqual(10, Value.ToInt("10")); + ClassicAssert.AreEqual(-10, Value.ToInt("M10")); + ClassicAssert.IsNull(Value.ToInt("///")); + } + + [Test] + public void TestParseTafWithWindDirectionVariation() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 200V280 9999 FEW030 "); + if (d.SurfaceWind.DirectionVariations != null) + { + ClassicAssert.AreEqual(2, d.SurfaceWind.DirectionVariations.Length); + } + } + + [Test] + public void TestParseTafWithPROB30TEMPO() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 PROB30 TEMPO 2318/2320 24010KT 2000 TSRA BKN010 "); + ClassicAssert.IsNotNull(d.SurfaceWind); + } + + [Test] + public void TestParseTafWithEndMarker() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030="); + ClassicAssert.AreEqual("LFPO", d.Icao); + } + + [Test] + public void TestParseTafWithExtraSpaces() + { + var d = decoder.ParseNotStrict("TAF LFPO 231100Z 2312/2418 24005KT 9999 FEW030 "); + ClassicAssert.AreEqual("LFPO", d.Icao); + } + } +} diff --git a/tests/Taf.Decoder.Tests/TafDecoderTest.cs b/tests/Taf.Decoder.Tests/TafDecoderTest.cs index 76e1ba0..7174f6a 100644 --- a/tests/Taf.Decoder.Tests/TafDecoderTest.cs +++ b/tests/Taf.Decoder.Tests/TafDecoderTest.cs @@ -4,11 +4,11 @@ using System.Collections.Generic; using System.Linq; using Taf.Decoder; -using Taf.Decoder.chunkdecoder; -using Taf.Decoder.entity; -using static Taf.Decoder.entity.DecodedTaf; +using Taf.Decoder.ChunkDecoder; +using Taf.Decoder.Entity; +using static Taf.Decoder.Entity.DecodedTaf; -namespace Taf.Decoder_tests +namespace Taf.Decoder.Tests { [TestFixture, Category("TafDecoder")] public class TafDecoderTest diff --git a/tests/Taf.Decoder.Tests/ValueTest.cs b/tests/Taf.Decoder.Tests/ValueTest.cs index e18df74..9f072a0 100644 --- a/tests/Taf.Decoder.Tests/ValueTest.cs +++ b/tests/Taf.Decoder.Tests/ValueTest.cs @@ -2,9 +2,9 @@ using NUnit.Framework.Legacy; using System; using System.Collections.Generic; -using Taf.Decoder.entity; +using Taf.Decoder.Entity; -namespace Taf.Decoder_tests +namespace Taf.Decoder.Tests { [TestFixture, Category("Entity")] public class ValueTest