diff --git a/src/NmeaParser/GnssMessageProcessor.cs b/src/NmeaParser/GnssMessageProcessor.cs new file mode 100644 index 0000000..73eb29f --- /dev/null +++ b/src/NmeaParser/GnssMessageProcessor.cs @@ -0,0 +1,108 @@ +using NmeaParser.Nmea; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NmeaParser +{ + public class GnssMessageProcessor : MessageAggregator + { + + public GnssMessageProcessor(NmeaDevice device) : base(device) + { + } + + protected override void OnMessagesReceived(NmeaMessage[] messages) + { + base.OnMessagesReceived(messages); + // We prefer GNSS messages, so put thos first + var info = new PositionInformation(); + messages = messages.OrderBy(rmc => rmc.TalkerId == Talker.GlobalNavigationSatelliteSystem ? -100 : (int)rmc.TalkerId).ToArray(); + info.Longitude = double.NaN; + info.Latitude = double.NaN; + var RmcMessages = messages.OfType().Where(rmc => rmc.Active); + if (RmcMessages.Any()) + { + var rmc = RmcMessages.First(); + info.Latitude = rmc.Latitude; + info.Longitude = rmc.Longitude; + info.Speed = rmc.Speed; + info.TimeOfFix = rmc.FixTime; + } + var GsaMessages = messages.OfType().Where(gsa => gsa.Fix == Gsa.FixType.Fix3D); + if (!GsaMessages.Any()) + GsaMessages = messages.OfType().Where(gsa => gsa.Fix == Gsa.FixType.Fix2D); + if (GsaMessages.Any()) + { + var gsa = GsaMessages.First(); + info.Hdop = gsa.Hdop; + info.Pdop = gsa.Pdop; + info.Vdop = gsa.Vdop; + info.SatelliteIds = gsa.SatelliteIDs; + } + var GsvMessages = messages.OfType(); + if (GsvMessages.Any()) + { + Gsv gsv = GsvMessages.First(); + info.Satellites = gsv.SVs.ToArray(); + } + var GstMessages = messages.OfType(); + if (GstMessages.Any()) + { + Gst gst = GstMessages.First(); + info.HorizontalAccuracy = (gst.SemiMajorError + gst.SemiMinorError) / 2; + info.VerticalAccuracy = gst.SigmaHeightError; + if (!info.TimeOfFix.HasValue) + { + info.TimeOfFix = DateTime.UtcNow.Date.Add(gst.FixTime); + } + } + var GgaMessages = messages.OfType().Where(g=>g.Quality != Gga.FixQuality.Invalid); + if (GgaMessages.Any()) + { + Gga gga = GgaMessages.First(); + info.Latitude = gga.Latitude; + info.Longitude = gga.Longitude; + info.Altitude = gga.Altitude; + info.GeoidalSeparation = gga.GeoidalSeparation; + if (!info.TimeOfFix.HasValue) + { + info.TimeOfFix = DateTime.UtcNow.Date.Add(gga.FixTime); + } + } + var GnsMessages = messages.OfType(); + if (GnsMessages.Any()) + { + Gns gns = GnsMessages.First(); + if (!info.TimeOfFix.HasValue) + { + info.TimeOfFix = DateTime.UtcNow.Date.Add(gns.FixTime); + } + info.GeoidalSeparation = gns.GeoidalSeparation; + info.Latitude = gns.Latitude; + info.Longitude = gns.Longitude; + } + LocationUpdated?.Invoke(this, info); + } + + public event EventHandler LocationUpdated; + } + + public struct PositionInformation + { + public double? Longitude { get; internal set; } + public double? Latitude { get; internal set; } + public double? Altitude { get; internal set; } + public double? GeoidalSeparation { get; internal set; } + public double? Speed { get; internal set; } + public double? Pdop { get; internal set; } + public double? Hdop { get; internal set; } + public double? Vdop { get; internal set; } + public double? HorizontalAccuracy { get; internal set; } + public double? VerticalAccuracy { get; internal set; } + public DateTimeOffset? TimeOfFix { get; internal set; } + public int[] SatelliteIds { get; internal set; } + public SatelliteVehicle[] Satellites { get; internal set; } + } +} diff --git a/src/NmeaParser/MessageAggregator.cs b/src/NmeaParser/MessageAggregator.cs new file mode 100644 index 0000000..651d00b --- /dev/null +++ b/src/NmeaParser/MessageAggregator.cs @@ -0,0 +1,57 @@ +using NmeaParser.Nmea; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NmeaParser +{ + /// + /// Aggregates a group of messages, and raises an event once all messages has completed transmission + /// + public class MessageAggregator + { + private readonly Dictionary _messages = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// + public MessageAggregator(NmeaDevice device) + { + if (device == null) + throw new ArgumentNullException(nameof(device)); + Device = device; + Device.MessageReceived += Device_MessageReceived; + } + + private void Device_MessageReceived(object sender, NmeaMessageReceivedEventArgs e) + { + if(_messages.ContainsKey(e.Message.MessageType)) + { + var messages = _messages.Values.ToArray(); + _messages.Clear(); + OnMessagesReceived(messages); + MessagesReceived?.Invoke(this, messages); + } + _messages.Add(e.Message.MessageType, e.Message); + } + + /// + /// Called when a group of messages have been received. + /// + protected virtual void OnMessagesReceived(NmeaMessage[] messages) + { + } + + /// + /// Raised when a group of messages have been received. + /// + public event EventHandler MessagesReceived; + + /// + /// The device that's being listened to. + /// + public NmeaDevice Device { get; } + } +}