Example #1
0
        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;

                Symbol symbol;

                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);

                    {
                        var symbolTableSelector = rawData[8];

                        if (symbolTableSelector != '/' &&
                            symbolTableSelector != '\\')
                        {
                            aprsMessage.SymbolOverlay = symbolTableSelector;
                            aprsMessage.SymbolTable   = Constants.Maps.SymbolTableMap['\\'];   //Take the secondary table
                        }
                        else
                        {
                            aprsMessage.SymbolTable = Constants.Maps.SymbolTableMap[symbolTableSelector];
                        }
                    }

                    if ((aprsMessage.SymbolTable == SymbolTable.Primary
                            ? Constants.Maps.PrimarySymbolTableSymbolMap
                            : Constants.Maps.SecondarySymbolTableSymbolMap).TryGetValue(rawData[7], out symbol))
                    {
                        aprsMessage.Symbol = symbol;
                    }
                    else
                    {
                        aprsMessage.Symbol = null;
                    }

                    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);

                    {
                        var symbolTableSelector = rawData[9];

                        if (symbolTableSelector != '/' &&
                            symbolTableSelector != '\\')
                        {
                            aprsMessage.SymbolOverlay = symbolTableSelector;
                            aprsMessage.SymbolTable   = Constants.Maps.SymbolTableMap['\\'];   //Take the secondary table
                        }
                        else
                        {
                            aprsMessage.SymbolTable = Constants.Maps.SymbolTableMap[symbolTableSelector];
                        }
                    }

                    aprsMessage.Longitude = new Longitude(Convert.ToInt16(rawData.Substring(10, 3)),
                                                          Convert.ToDouble(rawData.Substring(13, 5)),
                                                          rawData[18] == 'E' ? LongitudeHemisphere.East : LongitudeHemisphere.West);

                    if ((aprsMessage.SymbolTable == SymbolTable.Primary
                            ? Constants.Maps.PrimarySymbolTableSymbolMap
                            : Constants.Maps.SecondarySymbolTableSymbolMap).TryGetValue(rawData[19], out symbol))
                    {
                        aprsMessage.Symbol = symbol;
                    }
                    else
                    {
                        aprsMessage.Symbol = null;
                    }

                    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);

                    if ((aprsMessage.SymbolTable == SymbolTable.Primary
                            ? Constants.Maps.PrimarySymbolTableSymbolMap
                            : Constants.Maps.SecondarySymbolTableSymbolMap).TryGetValue(rawData[18], out symbol))
                    {
                        aprsMessage.Symbol = symbol;
                    }
                    else
                    {
                        aprsMessage.Symbol = null;
                    }

                    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);
            }
        }