public class NmeaParser { public struct Coordinate { public int Hours; public int Minutes; public double Seconds; } public enum FixStatus { NotSet, Obtained, //A Lost //V } public enum FixMode { Auto, //A Manual } public enum FixMethod { NotSet, Fix2D, Fix3D } public enum DifferentialGpsType { NotSet, SPS, DSPS, PPS, RTK } public class GpsSatellite { public int PRC { get; set; } public int Elevation { get; set; } public int Azimuth { get; set; } public int SNR { get; set; } public bool InUsed { get; set; } public bool InView { get; set; } public bool NotTracking { get; set; } } private static readonly CultureInfo NmeaCultureInfo = new CultureInfo("en-US"); private static readonly decimal KMpHPerKnot = decimal.Parse("1.852", NmeaCultureInfo); private Coordinate latitude; private Coordinate longitude; private decimal altitude = 0; private DateTime utcDateTime; private decimal velocity = 0; private decimal azimuth = 0; private FixStatus fixStatus; private DifferentialGpsType differentialGpsType; private FixMode fixMode; private FixMethod fixMethod; private int satellitesInView; private int satellitesInUsed; private readonly Dictionary<int, GpsSatellite> satellites; private decimal horizontalDilutionOfPrecision = 50; private decimal positionDilutionOfPrecision = 50; private decimal verticalDilutionOfPrecision = 50; public NmeaParser() { satellites = new Dictionary<int, GpsSatellite>(); } public bool Parse(string sentence) { string rawData = sentence; try { if (!IsValid(sentence)) { return false; } sentence = sentence.Substring(1, sentence.IndexOf('*') - 1); string[] Words = Getwords(sentence); switch (Words[0]) { case "GPRMC": return ParseGPRMC(Words); case "GPGGA": return ParseGPGGA(Words); case "GPGSA": return ParseGPGSA(Words); case "GPGSV": return ParseGPGSV(Words); default: return false; } } catch (Exception e) { Console.WriteLine(e.Message + rawData); return false; } } private bool IsValid(string sentence) { // GPS data can't be zero length if (sentence.Length == 0) { return false; } // first character must be a $ if (sentence[0] != '$') { return false; } // GPS data can't be longer than 82 character if (sentence.Length > 82) { return false; } try { string checksum = sentence.Substring(sentence.IndexOf('*') + 1); return Checksum(sentence, checksum); } catch (Exception e) { Console.WriteLine("Checksum failure. " + e.Message); return false; } } private bool Checksum(string sentence, string checksumStr) { int checksum = 0; int length = sentence.IndexOf('*') - 1; // go from first character upto last * for (int i = 1; i <= length; ++i) { checksum = checksum ^ Convert.ToByte(sentence[i]); } return (checksum.ToString("X2") == checksumStr); } // Divides a sentence into individual Words private static string[] Getwords(string sentence) { return sentence.Split(','); } private bool ParseGPRMC(string[] Words) { if (Words[1].Length > 0 & Words[9].Length > 0) { int UtcHours = Convert.ToInt32(Words[1].Substring(0, 2)); int UtcMinutes = Convert.ToInt32(Words[1].Substring(2, 2)); int UtcSeconds = Convert.ToInt32(Words[1].Substring(4, 2)); int UtcMilliseconds = 0; // Extract milliseconds if it is available if (Words[1].Length > 7) { UtcMilliseconds = Convert.ToInt32(Words[1].Substring(7)); } int UtcDay = Convert.ToInt32(Words[9].Substring(0, 2)); int UtcMonth = Convert.ToInt32(Words[9].Substring(2, 2)); // available for this century int UtcYear = Convert.ToInt32(Words[9].Substring(4, 2)) + 2000; utcDateTime = new DateTime(UtcYear, UtcMonth, UtcDay, UtcHours, UtcMinutes, UtcSeconds, UtcMilliseconds); } fixStatus = (Words[2][0] == 'A') ? FixStatus.Obtained : FixStatus.Lost; if (Words[3].Length > 0 & Words[4].Length == 1 & Words[5].Length > 0 & Words[6].Length == 1) { latitude.Hours = int.Parse(Words[3].Substring(0, 2)); latitude.Minutes = int.Parse(Words[3].Substring(2, 2)); latitude.Seconds = Math.Round(double.Parse(Words[3].Substring(5, 4)) * 6 / 1000.0, 3); if ("S" == Words[4]) { latitude.Hours = -latitude.Hours; } longitude.Hours = int.Parse(Words[5].Substring(0, 3)); longitude.Minutes = int.Parse(Words[5].Substring(3, 2)); longitude.Seconds = Math.Round(double.Parse(Words[5].Substring(6, 4)) * 6 / 1000.0, 3); if ("W" == Words[6]) { longitude.Hours = -longitude.Hours; } } if (Words[8].Length > 0) { azimuth = decimal.Parse(Words[8], NmeaCultureInfo); } if (Words[7].Length > 0) { velocity = decimal.Parse(Words[7], NmeaCultureInfo) * KMpHPerKnot; } return true; } private bool ParseGPGGA(string[] Words) { if (Words[6].Length > 0) { switch (Convert.ToInt32(Words[6])) { case 0: differentialGpsType = DifferentialGpsType.NotSet; break; case 1: differentialGpsType = DifferentialGpsType.SPS; break; case 2: differentialGpsType = DifferentialGpsType.DSPS; break; case 3: differentialGpsType = DifferentialGpsType.PPS; break; case 4: differentialGpsType = DifferentialGpsType.RTK; break; default: differentialGpsType = DifferentialGpsType.NotSet; break; } } if (Words[7].Length > 0) { satellitesInUsed = Convert.ToInt32(Words[7]); } if (Words[8].Length > 0) { horizontalDilutionOfPrecision = Convert.ToDecimal(Words[8]); } if (Words[9].Length > 0) { altitude = Convert.ToDecimal(Words[9]); } return true; } private bool ParseGPGSA(string[] Words) { if (Words[1].Length > 0) { fixMode = Words[1][0] == 'A' ? FixMode.Auto : FixMode.Manual; } if (Words[2].Length > 0) { switch (Convert.ToInt32(Words[2])) { case 1: fixMethod = FixMethod.NotSet; break; case 2: fixMethod = FixMethod.Fix2D; break; case 3: fixMethod = FixMethod.Fix3D; break; default: fixMethod = FixMethod.NotSet; break; } } foreach (GpsSatellite s in satellites.Values) { s.InUsed = false; } satellitesInUsed = 0; for (int i = 0; i < 12; ++i) { string id = Words[3 + i]; if (id.Length > 0) { int nId = Convert.ToInt32(id); if (!satellites.ContainsKey(nId)) { satellites[nId] = new GpsSatellite(); satellites[nId].PRC = nId; } satellites[nId].InUsed = true; ++satellitesInUsed; } } if (Words[15].Length > 0) { positionDilutionOfPrecision = Convert.ToDecimal(Words[15]); } if (Words[16].Length > 0) { horizontalDilutionOfPrecision = Convert.ToDecimal(Words[16]); } if (Words[17].Length > 0) { verticalDilutionOfPrecision = Convert.ToDecimal(Words[17]); } return true; } private bool ParseGPGSV(string[] Words) { int messageNumber = 0; if (Words[2].Length > 0) { messageNumber = Convert.ToInt32(Words[2]); } if (Words[3].Length > 0) { satellitesInView = Convert.ToInt32(Words[3]); } if (messageNumber == 0 || satellitesInView == 0) { return false; } for (int i = 1; i <= 4; ++i) { if ((Words.Length - 1) >= (i * 4 + 3)) { int nId = 0; if (Words[i * 4].Length > 0) { string id = Words[i * 4]; nId = Convert.ToInt32(id); if (!satellites.ContainsKey(nId)) { satellites[nId] = new GpsSatellite(); satellites[nId].PRC = nId; } satellites[nId].InView = true; } if (Words[i * 4 + 1].Length > 0) { satellites[nId].Elevation = Convert.ToInt32(Words[i * 4 + 1]); } if (Words[i * 4 + 2].Length > 0) { satellites[nId].Azimuth = Convert.ToInt32(Words[i * 4 + 2]); } if (Words[i * 4 + 3].Length > 0) { satellites[nId].SNR = Convert.ToInt32(Words[i * 4 + 3]); satellites[nId].NotTracking = false; } else { satellites[nId].NotTracking = true; } } } return true; } }