public static AprsMessage Parse(string rawData) { try { var match = Constants.Regexes.CallsignRegex.Match(rawData); var aprsMessage = new AprsMessage { RawData = rawData, ReceivedDate = DateTime.UtcNow, Callsign = match.Groups[1].Value }; rawData = match.Groups[2].Value; match = Regex.Match(rawData, @"^([^\:]*)\:(.*)$"); aprsMessage.StationRoute = new ReadOnlyCollection <string>(match.Groups[1].Value.Split(',')); rawData = match.Groups[2].Value; if (String.IsNullOrEmpty(rawData)) { return(null); } DataType dataType; if (!Constants.Maps.DataTypeMap.TryGetValue(Convert.ToByte(rawData[0]), out dataType)) { throw new ArgumentException("Unsupported data type in raw data", nameof(rawData)); } aprsMessage.DataType = dataType; switch (aprsMessage.DataType) { case DataType.CurrentMicE: Latitude latitude; short longitudeOffset; LongitudeHemisphere longitudeHemisphere; MicEMessageType micEMessageType; DecodeMicEDestinationAddress( aprsMessage.StationRoute[0], out latitude, out longitudeOffset, out longitudeHemisphere, out micEMessageType); aprsMessage.Latitude = latitude; aprsMessage.MicEMessageType = micEMessageType; var longitudeDegrees = (short)(rawData[1] - 28 + longitudeOffset); if (180 <= longitudeDegrees && longitudeDegrees <= 189) { longitudeDegrees -= 80; } else if (190 <= longitudeDegrees && longitudeDegrees <= 199) { longitudeDegrees -= 190; } aprsMessage.Longitude = new Longitude( longitudeDegrees, (short)((rawData[2] - 28) % 60), (rawData[3] - 28) * 0.6, longitudeHemisphere, latitude.Ambiguity); var speedCourseSharedByte = (rawData[5] - 28); aprsMessage.Speed = Speed.FromKnots(((rawData[4] - 28) * 10 + (int)Math.Floor(speedCourseSharedByte / 10.0)) % 800); aprsMessage.Direction = new Heading((short)(((speedCourseSharedByte % 10 * 100) + (rawData[6] - 28)) % 400), 0, 0); aprsMessage.SymbolTable = Constants.Maps.SymbolTableMap[rawData[8]]; aprsMessage.Symbol = (aprsMessage.SymbolTable == SymbolTable.Primary ? Constants.Maps.PrimarySymbolTableSymbolMap : Constants.Maps.SecondarySymbolTableSymbolMap)[rawData[7]]; if (rawData.Length > 12 && rawData[12] == '}') { aprsMessage.Altitude = Altitude.FromMetersAboveBaseline(ConvertFromBase91(rawData.Substring(9, 3))); } break; case DataType.PositionWithoutTimestampWithAprsMessaging: case DataType.PositionWithoutTimestampNoAprsMessaging: aprsMessage.Latitude = new Latitude(Convert.ToInt16(rawData.Substring(1, 2)), Convert.ToDouble(rawData.Substring(3, 5)), rawData[8] == 'N' ? LatitudeHemisphere.North : LatitudeHemisphere.South); aprsMessage.SymbolTable = Constants.Maps.SymbolTableMap[rawData[9]]; aprsMessage.Longitude = new Longitude(Convert.ToInt16(rawData.Substring(10, 3)), Convert.ToDouble(rawData.Substring(13, 5)), rawData[18] == 'E' ? LongitudeHemisphere.East : LongitudeHemisphere.West); aprsMessage.Symbol = (aprsMessage.SymbolTable == SymbolTable.Primary ? Constants.Maps.PrimarySymbolTableSymbolMap : Constants.Maps.SecondarySymbolTableSymbolMap)[rawData[19]]; rawData = rawData.Substring(20); if (Regex.IsMatch(rawData, @"^\d\d\d\\\d\d\d")) { aprsMessage.Direction = new Heading(Convert.ToInt16(rawData.Substring(0, 3)), 0, 0); aprsMessage.Speed = Speed.FromKnots(Convert.ToDouble(rawData.Substring(4, 3))); rawData = rawData.Substring(7); } if (Constants.Regexes.AltitudeRegex.IsMatch(rawData)) { aprsMessage.Altitude = Altitude.FromFeetAboveSeaLevel( Convert.ToInt32(Constants.Regexes.AltitudeRegex.Match(rawData).Groups[1].Value)); } // OGN FLAVORED STUFF if (Regex.IsMatch(rawData, @"([\+\-]\d\d\d)fpm")) { aprsMessage.ClimbRate = Convert.ToInt16(Regex.Match(rawData, @"([\+\-]\d\d\d)fpm").Groups[1].Value); } if (Regex.IsMatch(rawData, @"([\+\-]\d.\d)rot")) { aprsMessage.TurnRate = Convert.ToDouble(Regex.Match(rawData, @"([\+\-]\d.\d)rot").Groups[1].Value); } // ToDo: In case OGN flavored aprs is enabled add the precision enhancement, clibrate and turnrate. break; case DataType.PositionWithTimestampWithAprsMessaging: case DataType.PositionWithTimestampNoAprsMessaging: if (Regex.IsMatch(rawData.Substring(1), @"^(\d\d\d\d\d\d)h")) { rawData = rawData.Substring(8); } else { return(null); } aprsMessage.Latitude = new Latitude(Convert.ToInt16(rawData.Substring(0, 2)), Convert.ToDouble(rawData.Substring(2, 5)), rawData[7] == 'N' ? LatitudeHemisphere.North : LatitudeHemisphere.South); SymbolTable symbolTable; if (!Constants.Maps.SymbolTableMap.TryGetValue(rawData[8], out symbolTable)) { return(null); } aprsMessage.SymbolTable = symbolTable; aprsMessage.Longitude = new Longitude(Convert.ToInt16(rawData.Substring(9, 3)), Convert.ToDouble(rawData.Substring(12, 5)), rawData[17] == 'E' ? LongitudeHemisphere.East : LongitudeHemisphere.West); Symbol symbol; if ( !(aprsMessage.SymbolTable == SymbolTable.Primary ? Constants.Maps.PrimarySymbolTableSymbolMap : Constants.Maps.SecondarySymbolTableSymbolMap).TryGetValue(rawData[18], out symbol)) { return(null); } aprsMessage.Symbol = symbol; short direction; if (!Int16.TryParse(rawData.Substring(19, 3), out direction)) { return(null); } aprsMessage.Direction = new Heading(direction, 0, 0); aprsMessage.Speed = Speed.FromKnots(Convert.ToDouble(rawData.Substring(23, 3))); rawData = rawData.Substring(26); if (Constants.Regexes.AltitudeRegex.IsMatch(rawData)) { aprsMessage.Altitude = Altitude.FromFeetAboveSeaLevel( Convert.ToInt32(Constants.Regexes.AltitudeRegex.Match(rawData).Groups[1].Value)); } // OGN FLAVORED STUFF if (Regex.IsMatch(rawData, @"([\+\-]\d\d\d)fpm")) { aprsMessage.ClimbRate = Convert.ToInt16(Regex.Match(rawData, @"([\+\-]\d\d\d)fpm").Groups[1].Value); } if (Regex.IsMatch(rawData, @"([\+\-]\d.\d)rot")) { aprsMessage.TurnRate = Convert.ToDouble(Regex.Match(rawData, @"([\+\-]\d.\d)rot").Groups[1].Value); } break; } return(aprsMessage); } catch { return(null); } }
public PacketReceivedEventArgs(AprsMessage pi) { AprsMessage = pi; }