/// <summary> /// Decode a METAR/TAF message /// </summary> /// <param name="msg">The message</param> /// <returns>A filled MTData object</returns> public static MTData Decode(string msg) { var mdata = new MTData { RAW = msg }; // save reference if (string.IsNullOrWhiteSpace(msg)) { return(mdata); } string raw = msg.Replace("\n", "") + " "; // remove CRLF and we need a space at the end... raw = M_MsgTypeDecoder.Decode(raw, mdata); // defaults to METAR if not tagged with a Msg Type if (mdata.MsgType == MsgType.METAR) { DecodeMetar(raw, mdata); } else if (mdata.MsgType == MsgType.SPECI) { DecodeMetar(raw, mdata); } else if (mdata.MsgType == MsgType.TAF) { DecodeTaf(raw, mdata); } return(mdata); }
/// <summary> /// Decode a part into mData and return the rest /// </summary> /// <param name="raw">The raw METAR input string</param> /// <param name="mData">The MData record to fill in</param> /// <returns>The reminder of the input string, (raw minus the processed part)</returns> public static string Decode(string raw, MTData mData) { try { Match match = RE_metar.Match(raw); if (match.Success) { mData.MsgType = MsgType.METAR; if (match.Groups["opt"].Success && match.Groups["opt"].Value == "COR") { mData.MsgModifier = MsgModifier.COR; } return(match.Groups["rest"].Value.TrimStart( )); } match = RE_speci.Match(raw); if (match.Success) { mData.MsgType = MsgType.SPECI; if (match.Groups["opt"].Success && match.Groups["opt"].Value == "COR") { mData.MsgModifier = MsgModifier.COR; } return(match.Groups["rest"].Value.TrimStart( )); } match = RE_taf.Match(raw); if (match.Success) { mData.MsgType = MsgType.TAF; if (match.Groups["opt"].Success && match.Groups["opt"].Value == "AMD") { mData.MsgModifier = MsgModifier.AMD; } else if (match.Groups["opt"].Success && match.Groups["opt"].Value == "COR") { mData.MsgModifier = MsgModifier.COR; } else { mData.MsgModifier = MsgModifier.NONE; } return(match.Groups["rest"].Value.TrimStart( )); } } catch { ; // DEBUG STOP } return(raw); }
// Static Decoder /// <summary> /// Decode a METAR Message /// </summary> /// <param name="raw">The raw message starting with the StationID</param> /// <param name="mdata">The MData record to fill</param> private static void DecodeMetar(string raw, MTData mdata) { raw = M_StationDecoder.Decode(raw, mdata.Station); if (!mdata.Station.Valid) { return; // ERROR must have } raw = M_ObsTimeDecoder.Decode(raw, mdata.ObsTime); if (!mdata.ObsTime.Valid) { return; // ERROR must have } raw = M_ModifierDecoder.Decode(raw, mdata.Modifier); if (mdata.Modifier.IsNil) { // cancelled - forget the rest and return return; } raw = M_WindDecoder.Decode(raw, mdata.Wind); raw = M_WindDecoder.Decode(raw, mdata.Wind); // optional variable part, will capture if there raw = M_VisibilityDecoder.Decode(raw, mdata.Visibility); while (M_RunwayVRDecoder.IsMatch(raw))// optional multiple { raw = M_RunwayVRDecoder.Decode(raw, mdata.RunwayVRs); } ; while (M_WeatherDecoder.IsMatch(raw))// optional multiple { raw = M_WeatherDecoder.Decode(raw, mdata.Weather); } while (M_SkyConditionDecoder.IsMatch(raw))// optional multiple { raw = M_SkyConditionDecoder.Decode(raw, mdata.SkyConditions); } ; raw = M_TempDecoder.Decode(raw, mdata.Temperature); raw = M_PressureDecoder.Decode(raw, mdata.Altimeter); // RMKs // Eval Category mdata.FlightCategory = M_CategoryDecoder.Decode(mdata); }
/// <summary> /// Decode a part into mData and return the rest /// </summary> /// <param name="raw">The raw METAR input string</param> /// <param name="mData">The MData record to fill in</param> /// <returns>The reminder of the input string, (raw minus the processed part)</returns> public static string Decode(string raw, MTData mData) { try { Match match = RE_regular.Match(raw); if (match.Success) { mData.CancelFlag = (match.Groups["rest"].Value == "CNL"); return(match.Groups["rest"].Value.TrimStart( )); } } catch { ; // DEBUG STOP } return(raw); }
/// <summary> /// Decode a TAF Message /// </summary> /// <param name="raw">The raw message starting with the StationID</param> /// <param name="mdata">The MData record to fill</param> private static void DecodeTaf(string raw, MTData mdata) { raw = M_StationDecoder.Decode(raw, mdata.Station); if (!mdata.Station.Valid) { return; // ERROR must have } raw = M_ObsTimeDecoder.Decode(raw, mdata.ObsTime); if (!mdata.ObsTime.Valid) { return; // ERROR must have } raw = T_PeriodDecoder.Decode(raw, mdata.TafPeriod); if (!mdata.TafPeriod.Valid) { return; // ERROR must have } if (T_CancelDecoder.IsMatch(raw)) { // cancelled - collect the flag, forget the rest and return raw = T_CancelDecoder.Decode(raw, mdata); return; } // add the basic FC record here as we go and process further parts mdata.Forecasts.Add(new T_Forecast( ) { Valid = true, From = mdata.TafPeriod.From, To = mdata.TafPeriod.To, IsTimeSpan = true }); raw = M_WindDecoder.Decode(raw, mdata.Forecasts.First( ).Wind); raw = M_WindDecoder.Decode(raw, mdata.Forecasts.First( ).Wind); // optional variable part, will capture if there raw = M_VisibilityDecoder.Decode(raw, mdata.Forecasts.First( ).Visibility); while (M_WeatherDecoder.IsMatch(raw))// optional multiple { raw = M_WeatherDecoder.Decode(raw, mdata.Forecasts.First( ).Weather); } while (M_SkyConditionDecoder.IsMatch(raw))// optional multiple { raw = M_SkyConditionDecoder.Decode(raw, mdata.Forecasts.First( ).SkyConditions); } ; while (T_TempMinMaxDecoder.IsMatch(raw))// optional multiple { raw = T_TempMinMaxDecoder.Decode(raw, mdata.Forecasts.First( ).TempMinMax); } ; // Eval Category mdata.Forecasts.First( ).FlightCategory = M_CategoryDecoder.Decode(mdata.Forecasts.First( )); // We should get into the additional forecast records now.. while (T_ForecastDecoder.IsMatch(raw)) { raw = T_ForecastDecoder.Decode(raw, mdata.Forecasts); // collect the allowed forecast records raw = M_WindDecoder.Decode(raw, mdata.Forecasts.Last( ).Wind); raw = M_WindDecoder.Decode(raw, mdata.Forecasts.Last( ).Wind); // optional variable part, will capture if there raw = M_VisibilityDecoder.Decode(raw, mdata.Forecasts.Last( ).Visibility); while (M_WeatherDecoder.IsMatch(raw))// optional multiple { raw = M_WeatherDecoder.Decode(raw, mdata.Forecasts.Last( ).Weather); } while (M_SkyConditionDecoder.IsMatch(raw))// optional multiple { raw = M_SkyConditionDecoder.Decode(raw, mdata.Forecasts.Last( ).SkyConditions); } ; // Eval Category mdata.Forecasts.Last( ).FlightCategory = M_CategoryDecoder.Decode(mdata.Forecasts.Last( )); } }
/// <summary> /// Evaluate the FlightCategory /// </summary> /// <param name="mdata">An MData record</param> /// <returns>A FlightCategory record</returns> public static M_Category Decode(MTData mdata) { var ret = new M_Category(); // OFF_LIMIT Ceilings are less than 200 feet above ground level and/or visibility is less than 0.5 mile. bool trigger = false; trigger |= mdata.Visibility.Valid && mdata.Visibility.Distance_SM < 0.5; trigger |= mdata.SkyConditions.Count > 0 && mdata.SkyConditions[0].Height_ft < 200; if (trigger) { ret.FlightCategory = FLCat.LIFR; ret.Valid = true; return(ret); } // LIFR Ceilings are less than 500 feet above ground level and/or visibility is less than 1 mile. trigger = false; trigger |= mdata.Visibility.Valid && mdata.Visibility.Distance_SM < 1.0; trigger |= mdata.SkyConditions.Count > 0 && mdata.SkyConditions[0].Height_ft < 500; if (trigger) { ret.FlightCategory = FLCat.LIFR; ret.Valid = true; return(ret); } // IFR Ceilings 500 to less than 1,000 feet and/or visibility 1 to less than 3 miles. trigger = false; trigger |= mdata.Visibility.Valid && mdata.Visibility.Distance_SM >= 1.0 && mdata.Visibility.Distance_SM < 3.0; trigger |= mdata.SkyConditions.Count > 0 && mdata.SkyConditions[0].Height_ft >= 500 && mdata.SkyConditions[0].Height_ft < 1000; if (trigger) { ret.FlightCategory = FLCat.IFR; ret.Valid = true; return(ret); } // MVFR Ceilings 1,000 to 3,000 feet and/or visibility is 3-5 miles inclusive. trigger = false; trigger |= mdata.Visibility.Valid && mdata.Visibility.Distance_SM >= 3.0 && mdata.Visibility.Distance_SM <= 5.0; trigger |= mdata.SkyConditions.Count > 0 && mdata.SkyConditions[0].Height_ft >= 1000 && mdata.SkyConditions[0].Height_ft <= 3000; if (trigger) { ret.FlightCategory = FLCat.MVFR; ret.Valid = true; return(ret); } // VFR Ceiling greater than 3000 feet and visibility greater than 5 miles (includes sky clear). trigger = true; trigger &= mdata.Visibility.Valid && mdata.Visibility.Distance_SM > 5.0; trigger &= mdata.SkyConditions.Count > 0 && mdata.SkyConditions[0].Height_ft > 3000; trigger |= mdata.Visibility.CAVOK; // overrides if (trigger) { ret.FlightCategory = FLCat.VFR; ret.Valid = true; return(ret); } // missing data ret.FlightCategory = FLCat.UNKOWN; ret.Valid = true; return(ret); }