diff --git a/src/NmeaParser/BluetoothDevice.Android.cs b/src/NmeaParser/BluetoothDevice.Android.cs index 0f1d56a..12c893a 100644 --- a/src/NmeaParser/BluetoothDevice.Android.cs +++ b/src/NmeaParser/BluetoothDevice.Android.cs @@ -22,11 +22,11 @@ using Android.Bluetooth; namespace NmeaParser { - /// - /// A Bluetooth NMEA device - /// - public class BluetoothDevice : NmeaDevice - { + /// + /// A Bluetooth NMEA device + /// + public class BluetoothDevice : NmeaDevice + { private static Java.Util.UUID SERIAL_UUID = Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); private Android.Bluetooth.BluetoothDevice m_device; private BluetoothSocket? m_socket; @@ -45,14 +45,14 @@ namespace NmeaParser } } - /// - /// Initializes a new instance of the class. - /// - /// The Android Bluetooth Device. - public BluetoothDevice(Android.Bluetooth.BluetoothDevice device) - { + /// + /// Initializes a new instance of the class. + /// + /// The Android Bluetooth Device. + public BluetoothDevice(Android.Bluetooth.BluetoothDevice device) + { m_device = device ?? throw new ArgumentNullException(nameof(device)); - } + } /// /// Creates the stream the NmeaDevice is working on top off. @@ -66,24 +66,24 @@ namespace NmeaParser var d = adapter.GetRemoteDevice(m_device.Address); var socket = d.CreateRfcommSocketToServiceRecord(SERIAL_UUID); socket.Connect(); - m_socket = socket; + m_socket = socket; return Task.FromResult(socket.InputStream); - } + } - /// - /// Closes the stream the NmeaDevice is working on top off. - /// - /// The stream. - /// - protected override Task CloseStreamAsync(System.IO.Stream stream) - { - if (stream == null) - throw new ArgumentNullException("stream"); - stream.Dispose(); - m_socket?.Dispose(); - m_socket = null; - return Task.FromResult(true); - } + /// + /// Closes the stream the NmeaDevice is working on top off. + /// + /// The stream. + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) + { + if (stream == null) + throw new ArgumentNullException("stream"); + stream.Dispose(); + m_socket?.Dispose(); + m_socket = null; + return Task.FromResult(true); + } /// public override bool CanWrite => true; @@ -95,6 +95,6 @@ namespace NmeaParser throw new InvalidOperationException("Device not open"); return m_socket.OutputStream.WriteAsync(buffer, offset, length); } - } + } } #endif \ No newline at end of file diff --git a/src/NmeaParser/BluetoothDevice.UWP.cs b/src/NmeaParser/BluetoothDevice.UWP.cs index f4d6bff..e752eea 100644 --- a/src/NmeaParser/BluetoothDevice.UWP.cs +++ b/src/NmeaParser/BluetoothDevice.UWP.cs @@ -28,12 +28,12 @@ using Windows.Networking.Proximity; namespace NmeaParser { - /// - /// A Bluetooth NMEA device - /// - public class BluetoothDevice : NmeaDevice - { - private Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService? m_deviceService; + /// + /// A Bluetooth NMEA device + /// + public class BluetoothDevice : NmeaDevice + { + private Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService? m_deviceService; private Windows.Networking.Proximity.PeerInformation? m_devicePeer; private StreamSocket? m_socket; private bool m_disposeService; @@ -59,8 +59,8 @@ namespace NmeaParser /// The RF Comm Device service. /// Whether this devicee should also dispose the RfcommDeviceService provided when this device disposes. public BluetoothDevice(RfcommDeviceService service, bool disposeService = false) - { - m_deviceService = service ?? throw new ArgumentNullException(nameof(service)); + { + m_deviceService = service ?? throw new ArgumentNullException(nameof(service)); m_disposeService = disposeService; } @@ -88,8 +88,8 @@ namespace NmeaParser /// /// protected override async Task OpenStreamAsync() - { - var socket = new Windows.Networking.Sockets.StreamSocket(); + { + var socket = new Windows.Networking.Sockets.StreamSocket(); socket.Control.KeepAlive = true; if (m_devicePeer != null) { @@ -101,10 +101,10 @@ namespace NmeaParser } else throw new InvalidOperationException(); - m_socket = socket; + m_socket = socket; return new DummyStream(); //We're going to use WinRT buffers instead and will handle read/write, so no reason to return a real stream. This is mainly done to avoid locking issues reading and writing at the same time - } + } private class DummyStream : Stream { @@ -126,13 +126,13 @@ namespace NmeaParser /// The stream. /// protected override Task CloseStreamAsync(System.IO.Stream stream) - { + { if(m_socket == null) throw new InvalidOperationException("No connection to close"); - m_socket.Dispose(); - m_socket = null; - return Task.FromResult(true); - } + m_socket.Dispose(); + m_socket = null; + return Task.FromResult(true); + } /// @@ -174,6 +174,6 @@ namespace NmeaParser m_semaphoreSlim.Release(); } } - } + } } #endif \ No newline at end of file diff --git a/src/NmeaParser/BufferedStreamDevice.cs b/src/NmeaParser/BufferedStreamDevice.cs index 3cd218a..09decc0 100644 --- a/src/NmeaParser/BufferedStreamDevice.cs +++ b/src/NmeaParser/BufferedStreamDevice.cs @@ -21,241 +21,241 @@ using System.Threading.Tasks; namespace NmeaParser { - /// - /// An abstract generic NMEA device that reads a stream at a decreased pace, - /// mostly used to emulate NMEA input from files and strings. - /// - public abstract class BufferedStreamDevice : NmeaDevice - { - private BufferedStream? m_stream; - private readonly int m_readSpeed; + /// + /// An abstract generic NMEA device that reads a stream at a decreased pace, + /// mostly used to emulate NMEA input from files and strings. + /// + public abstract class BufferedStreamDevice : NmeaDevice + { + private BufferedStream? m_stream; + private readonly int m_readSpeed; - /// - /// Initializes a new instance of the class. - /// - protected BufferedStreamDevice() : this(1000) - { - } - /// - /// Initializes a new instance of the class. - /// - /// The time to wait between each group of lines being read in milliseconds - protected BufferedStreamDevice(int readSpeed) - { - m_readSpeed = readSpeed; - } + /// + /// Initializes a new instance of the class. + /// + protected BufferedStreamDevice() : this(1000) + { + } + /// + /// Initializes a new instance of the class. + /// + /// The time to wait between each group of lines being read in milliseconds + protected BufferedStreamDevice(int readSpeed) + { + m_readSpeed = readSpeed; + } - /// - /// Gets the stream to perform buffer reads on. - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] - protected abstract Task GetStreamAsync(); + /// + /// Gets the stream to perform buffer reads on. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] + protected abstract Task GetStreamAsync(); - /// - /// Opens the stream asynchronous. - /// - /// - protected sealed async override Task OpenStreamAsync() - { - var stream = await GetStreamAsync(); - StreamReader sr = new StreamReader(stream); - m_stream = new BufferedStream(sr, m_readSpeed); - return m_stream; - } + /// + /// Opens the stream asynchronous. + /// + /// + protected sealed async override Task OpenStreamAsync() + { + var stream = await GetStreamAsync(); + StreamReader sr = new StreamReader(stream); + m_stream = new BufferedStream(sr, m_readSpeed); + return m_stream; + } - /// - /// Closes the stream asynchronous. - /// - /// The stream. - /// - protected override Task CloseStreamAsync(System.IO.Stream stream) - { - m_stream?.Dispose(); - return Task.FromResult(true); - } + /// + /// Closes the stream asynchronous. + /// + /// The stream. + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) + { + m_stream?.Dispose(); + return Task.FromResult(true); + } - // stream that slowly populates a buffer from a StreamReader to simulate nmea messages coming - // in lastLineRead by lastLineRead at a steady stream - private class BufferedStream : Stream - { - private readonly StreamReader m_sr; - private byte[] m_buffer = new byte[0]; - private readonly System.Threading.Timer m_timer; - private readonly object lockObj = new object(); - private string? groupToken = null; - private string? lastLineRead = null; - /// - /// Initializes a new instance of the class. - /// - /// The stream. - /// The read speed in milliseconds. - public BufferedStream(StreamReader stream, int readSpeed) - { - m_sr = stream; - m_timer = new System.Threading.Timer(OnRead, null, 0, readSpeed); //read a group of lines every 'readSpeed' milliseconds - } - private void OnRead(object state) - { - if (lastLineRead != null) - AppendToBuffer(lastLineRead); - //Get the group token if we don't have one - while (groupToken == null && (lastLineRead == null || !lastLineRead.StartsWith("$", StringComparison.Ordinal))) - { - lastLineRead = ReadLine(); //seek forward to first nmea token - AppendToBuffer(lastLineRead); - } - if(groupToken == null && lastLineRead != null) - { - var values = lastLineRead.Trim().Split(new char[] { ',' }); - if (values.Length > 0) - groupToken = values[0]; - } - lastLineRead = ReadLine(); - while (!lastLineRead.StartsWith(groupToken, StringComparison.Ordinal)) //keep reading until messages start repeating again - { - AppendToBuffer(lastLineRead); - lastLineRead = ReadLine(); - } - } - private void AppendToBuffer(string line) - { - var bytes = Encoding.UTF8.GetBytes(line); - lock (lockObj) - { - byte[] newBuffer = new byte[m_buffer.Length + bytes.Length]; - m_buffer.CopyTo(newBuffer, 0); - bytes.CopyTo(newBuffer, m_buffer.Length); - m_buffer = newBuffer; - } - } - private string ReadLine() - { - if (m_sr.EndOfStream) - m_sr.BaseStream.Seek(0, SeekOrigin.Begin); //start over - return m_sr.ReadLine() + '\n'; - } - /// - /// Gets a value indicating whether this instance can read. - /// - /// - /// true if this instance can read; otherwise, false. - /// - public override bool CanRead { get { return true; } } - /// - /// Gets a value indicating whether this instance can seek. - /// - /// - /// true if this instance can seek; otherwise, false. - /// - public override bool CanSeek { get { return false; } } - /// - /// Gets a value indicating whether this instance can write. - /// - /// - /// true if this instance can write; otherwise, false. - /// - public override bool CanWrite { get { return false; } } - /// - /// Flushes this instance. - /// - public override void Flush() { } - /// - /// Gets the length. - /// - /// - /// The length. - /// - public override long Length { get { return m_sr.BaseStream.Length; } } + // stream that slowly populates a buffer from a StreamReader to simulate nmea messages coming + // in lastLineRead by lastLineRead at a steady stream + private class BufferedStream : Stream + { + private readonly StreamReader m_sr; + private byte[] m_buffer = new byte[0]; + private readonly System.Threading.Timer m_timer; + private readonly object lockObj = new object(); + private string? groupToken = null; + private string? lastLineRead = null; + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// The read speed in milliseconds. + public BufferedStream(StreamReader stream, int readSpeed) + { + m_sr = stream; + m_timer = new System.Threading.Timer(OnRead, null, 0, readSpeed); //read a group of lines every 'readSpeed' milliseconds + } + private void OnRead(object state) + { + if (lastLineRead != null) + AppendToBuffer(lastLineRead); + //Get the group token if we don't have one + while (groupToken == null && (lastLineRead == null || !lastLineRead.StartsWith("$", StringComparison.Ordinal))) + { + lastLineRead = ReadLine(); //seek forward to first nmea token + AppendToBuffer(lastLineRead); + } + if(groupToken == null && lastLineRead != null) + { + var values = lastLineRead.Trim().Split(new char[] { ',' }); + if (values.Length > 0) + groupToken = values[0]; + } + lastLineRead = ReadLine(); + while (!lastLineRead.StartsWith(groupToken, StringComparison.Ordinal)) //keep reading until messages start repeating again + { + AppendToBuffer(lastLineRead); + lastLineRead = ReadLine(); + } + } + private void AppendToBuffer(string line) + { + var bytes = Encoding.UTF8.GetBytes(line); + lock (lockObj) + { + byte[] newBuffer = new byte[m_buffer.Length + bytes.Length]; + m_buffer.CopyTo(newBuffer, 0); + bytes.CopyTo(newBuffer, m_buffer.Length); + m_buffer = newBuffer; + } + } + private string ReadLine() + { + if (m_sr.EndOfStream) + m_sr.BaseStream.Seek(0, SeekOrigin.Begin); //start over + return m_sr.ReadLine() + '\n'; + } + /// + /// Gets a value indicating whether this instance can read. + /// + /// + /// true if this instance can read; otherwise, false. + /// + public override bool CanRead { get { return true; } } + /// + /// Gets a value indicating whether this instance can seek. + /// + /// + /// true if this instance can seek; otherwise, false. + /// + public override bool CanSeek { get { return false; } } + /// + /// Gets a value indicating whether this instance can write. + /// + /// + /// true if this instance can write; otherwise, false. + /// + public override bool CanWrite { get { return false; } } + /// + /// Flushes this instance. + /// + public override void Flush() { } + /// + /// Gets the length. + /// + /// + /// The length. + /// + public override long Length { get { return m_sr.BaseStream.Length; } } - /// - /// Gets or sets the position. - /// - /// - /// The position. - /// - /// - public override long Position - { - get { return m_sr.BaseStream.Position; } - set - { - throw new NotSupportedException(); - } - } - /// - /// Reads the specified buffer. - /// - /// The buffer. - /// The offset. - /// The count. - /// - public override int Read(byte[] buffer, int offset, int count) - { - lock (lockObj) - { - if (this.m_buffer.Length <= count) - { - int length = this.m_buffer.Length; - this.m_buffer.CopyTo(buffer, 0); - this.m_buffer = new byte[0]; - return length; - } - else - { - Array.Copy(this.m_buffer, buffer, count); - byte[] newBuffer = new byte[this.m_buffer.Length - count]; - Array.Copy(this.m_buffer, count, newBuffer, 0, newBuffer.Length); - this.m_buffer = newBuffer; - return count; - } - } - } + /// + /// Gets or sets the position. + /// + /// + /// The position. + /// + /// + public override long Position + { + get { return m_sr.BaseStream.Position; } + set + { + throw new NotSupportedException(); + } + } + /// + /// Reads the specified buffer. + /// + /// The buffer. + /// The offset. + /// The count. + /// + public override int Read(byte[] buffer, int offset, int count) + { + lock (lockObj) + { + if (this.m_buffer.Length <= count) + { + int length = this.m_buffer.Length; + this.m_buffer.CopyTo(buffer, 0); + this.m_buffer = new byte[0]; + return length; + } + else + { + Array.Copy(this.m_buffer, buffer, count); + byte[] newBuffer = new byte[this.m_buffer.Length - count]; + Array.Copy(this.m_buffer, count, newBuffer, 0, newBuffer.Length); + this.m_buffer = newBuffer; + return count; + } + } + } - /// - /// Seeks the specified offset. - /// - /// The offset. - /// The origin. - /// - /// - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } + /// + /// Seeks the specified offset. + /// + /// The offset. + /// The origin. + /// + /// + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } - /// - /// Sets the length. - /// - /// The value. - /// - public override void SetLength(long value) - { - throw new NotSupportedException(); - } + /// + /// Sets the length. + /// + /// The value. + /// + public override void SetLength(long value) + { + throw new NotSupportedException(); + } - /// - /// Writes the specified buffer to the device. - /// - /// The buffer. - /// The offset. - /// The count. - /// - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } + /// + /// Writes the specified buffer to the device. + /// + /// The buffer. + /// The offset. + /// The count. + /// + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - m_sr.Dispose(); - m_timer.Dispose(); - } - } - } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + m_sr.Dispose(); + m_timer.Dispose(); + } + } + } } diff --git a/src/NmeaParser/IMultiSentenceMessage.cs b/src/NmeaParser/IMultiSentenceMessage.cs index 6948d04..4e31a19 100644 --- a/src/NmeaParser/IMultiSentenceMessage.cs +++ b/src/NmeaParser/IMultiSentenceMessage.cs @@ -18,25 +18,25 @@ using System.Text; namespace NmeaParser { - /// - /// Interface used for NMEA messages that span multiple sentences - /// - public interface IMultiSentenceMessage - { - /// - /// Attempts to append one message to an existing one - /// - /// - /// This method should return false if the message being appended isn't the next message in line, and various indicators show this is a different message than the previous one. It should also return false if you append to a message that didn't start with the first message. - /// - /// - /// - /// True is the message was successfully appended, False is the message couldn't be appended. - bool TryAppend(string messageType, string[] values); + /// + /// Interface used for NMEA messages that span multiple sentences + /// + public interface IMultiSentenceMessage + { + /// + /// Attempts to append one message to an existing one + /// + /// + /// This method should return false if the message being appended isn't the next message in line, and various indicators show this is a different message than the previous one. It should also return false if you append to a message that didn't start with the first message. + /// + /// + /// + /// True is the message was successfully appended, False is the message couldn't be appended. + bool TryAppend(string messageType, string[] values); - /// - /// Gets a value indicating whether this message is fully loaded from all its sentences - /// - bool IsComplete { get; } - } + /// + /// Gets a value indicating whether this message is fully loaded from all its sentences + /// + bool IsComplete { get; } + } } diff --git a/src/NmeaParser/Nmea/Garmin/PGRME.cs b/src/NmeaParser/Nmea/Garmin/PGRME.cs index 5be2e18..99cc1bd 100644 --- a/src/NmeaParser/Nmea/Garmin/PGRME.cs +++ b/src/NmeaParser/Nmea/Garmin/PGRME.cs @@ -20,9 +20,9 @@ namespace NmeaParser.Nmea.Garmin /// Recommended Minimum /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pgrme")] - [NmeaMessageType("PGRME")] - public class Pgrme : NmeaMessage - { + [NmeaMessageType("PGRME")] + public class Pgrme : NmeaMessage + { /// /// Initializes a new instance of the class. /// @@ -31,47 +31,47 @@ namespace NmeaParser.Nmea.Garmin public Pgrme(string type, string[] message) : base(type, message) { if (message == null || message.Length < 6) - throw new ArgumentException("Invalid PGRME", "message"); - - HorizontalError = NmeaMessage.StringToDouble(message[0]); - HorizontalErrorUnits = message[1]; - VerticalError = NmeaMessage.StringToDouble(message[2]); - VerticalErrorUnits = message[3]; - SphericalError = NmeaMessage.StringToDouble(message[4]); - SphericalErrorUnits = message[5]; - } + throw new ArgumentException("Invalid PGRME", "message"); + + HorizontalError = NmeaMessage.StringToDouble(message[0]); + HorizontalErrorUnits = message[1]; + VerticalError = NmeaMessage.StringToDouble(message[2]); + VerticalErrorUnits = message[3]; + SphericalError = NmeaMessage.StringToDouble(message[4]); + SphericalErrorUnits = message[5]; + } - /// - /// Estimated horizontal position error in meters (HPE) - /// - /// Range: 0.0 to 999.9 meters - public double HorizontalError{ get; } + /// + /// Estimated horizontal position error in meters (HPE) + /// + /// Range: 0.0 to 999.9 meters + public double HorizontalError{ get; } - /// - /// Horizontal Error unit ('M' for Meters) - /// - public string HorizontalErrorUnits{ get; } + /// + /// Horizontal Error unit ('M' for Meters) + /// + public string HorizontalErrorUnits{ get; } - /// - /// Estimated vertical position error in meters (VPE) - /// - /// Range: 0.0 to 999.9 meters - public double VerticalError{ get; } + /// + /// Estimated vertical position error in meters (VPE) + /// + /// Range: 0.0 to 999.9 meters + public double VerticalError{ get; } - /// - /// Vertical Error unit ('M' for Meters) - /// - public string VerticalErrorUnits{ get; } + /// + /// Vertical Error unit ('M' for Meters) + /// + public string VerticalErrorUnits{ get; } - /// - /// Overall spherical equivalent position error (EPE) - /// - /// Range: 0.0 to 999.9 meters - public double SphericalError{ get; } + /// + /// Overall spherical equivalent position error (EPE) + /// + /// Range: 0.0 to 999.9 meters + public double SphericalError{ get; } - /// - /// Spherical Error unit ('M' for Meters) - /// - public string SphericalErrorUnits{ get; } - } + /// + /// Spherical Error unit ('M' for Meters) + /// + public string SphericalErrorUnits{ get; } + } } diff --git a/src/NmeaParser/Nmea/Gga.cs b/src/NmeaParser/Nmea/Gga.cs index bdda051..1a1e441 100644 --- a/src/NmeaParser/Nmea/Gga.cs +++ b/src/NmeaParser/Nmea/Gga.cs @@ -23,97 +23,97 @@ namespace NmeaParser.Nmea [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gpgga")] [NmeaMessageType("--GGA")] public class Gga : NmeaMessage - { + { /// /// Initializes a new instance of the class. /// /// The message type /// The NMEA message values. public Gga(string type, string[] message) : base(type, message) - { - if (message == null || message.Length < 14) - throw new ArgumentException("Invalid GPGGA", "message"); - FixTime = StringToTimeSpan(message[0]); - Latitude = NmeaMessage.StringToLatitude(message[1], message[2]); - Longitude = NmeaMessage.StringToLongitude(message[3], message[4]); - Quality = (Gga.FixQuality)int.Parse(message[5], CultureInfo.InvariantCulture); - NumberOfSatellites = int.Parse(message[6], CultureInfo.InvariantCulture); - Hdop = NmeaMessage.StringToDouble(message[7]); - Altitude = NmeaMessage.StringToDouble(message[8]); - AltitudeUnits = message[9]; - HeightOfGeoid = NmeaMessage.StringToDouble(message[10]); - HeightOfGeoidUnits = message[11]; - var timeInSeconds = StringToDouble(message[12]); - if (!double.IsNaN(timeInSeconds)) - TimeSinceLastDgpsUpdate = TimeSpan.FromSeconds(timeInSeconds); - else - TimeSinceLastDgpsUpdate = TimeSpan.MaxValue; - if (message[13].Length > 0) - DgpsStationId = int.Parse(message[13], CultureInfo.InvariantCulture); - else - DgpsStationId = -1; - } + { + if (message == null || message.Length < 14) + throw new ArgumentException("Invalid GPGGA", "message"); + FixTime = StringToTimeSpan(message[0]); + Latitude = NmeaMessage.StringToLatitude(message[1], message[2]); + Longitude = NmeaMessage.StringToLongitude(message[3], message[4]); + Quality = (Gga.FixQuality)int.Parse(message[5], CultureInfo.InvariantCulture); + NumberOfSatellites = int.Parse(message[6], CultureInfo.InvariantCulture); + Hdop = NmeaMessage.StringToDouble(message[7]); + Altitude = NmeaMessage.StringToDouble(message[8]); + AltitudeUnits = message[9]; + HeightOfGeoid = NmeaMessage.StringToDouble(message[10]); + HeightOfGeoidUnits = message[11]; + var timeInSeconds = StringToDouble(message[12]); + if (!double.IsNaN(timeInSeconds)) + TimeSinceLastDgpsUpdate = TimeSpan.FromSeconds(timeInSeconds); + else + TimeSinceLastDgpsUpdate = TimeSpan.MaxValue; + if (message[13].Length > 0) + DgpsStationId = int.Parse(message[13], CultureInfo.InvariantCulture); + else + DgpsStationId = -1; + } - /// - /// Time of day fix was taken - /// - public TimeSpan FixTime { get; } - - /// - /// Latitude - /// - public double Latitude { get; } + /// + /// Time of day fix was taken + /// + public TimeSpan FixTime { get; } + + /// + /// Latitude + /// + public double Latitude { get; } - /// - /// Longitude - /// - public double Longitude { get; } + /// + /// Longitude + /// + public double Longitude { get; } - /// - /// Fix Quality - /// - public Gga.FixQuality Quality { get; } + /// + /// Fix Quality + /// + public Gga.FixQuality Quality { get; } - /// - /// Number of satellites being tracked - /// - public int NumberOfSatellites { get; } + /// + /// Number of satellites being tracked + /// + public int NumberOfSatellites { get; } - /// - /// Horizontal Dilution of Precision - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hdop")] - public double Hdop { get; } + /// + /// Horizontal Dilution of Precision + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hdop")] + public double Hdop { get; } - /// - /// Altitude - /// - public double Altitude { get; } + /// + /// Altitude + /// + public double Altitude { get; } - /// - /// Altitude units ('M' for Meters) - /// - public string AltitudeUnits { get; } - - /// - /// Height of geoid (mean sea level) above WGS84 - /// - public double HeightOfGeoid { get; } + /// + /// Altitude units ('M' for Meters) + /// + public string AltitudeUnits { get; } + + /// + /// Height of geoid (mean sea level) above WGS84 + /// + public double HeightOfGeoid { get; } - /// - /// Altitude units ('M' for Meters) - /// - public string HeightOfGeoidUnits { get; } + /// + /// Altitude units ('M' for Meters) + /// + public string HeightOfGeoidUnits { get; } - /// - /// Time since last DGPS update - /// - public TimeSpan TimeSinceLastDgpsUpdate { get; } + /// + /// Time since last DGPS update + /// + public TimeSpan TimeSinceLastDgpsUpdate { get; } - /// - /// DGPS Station ID Number - /// - public int DgpsStationId { get; } + /// + /// DGPS Station ID Number + /// + public int DgpsStationId { get; } /// /// Fix quality diff --git a/src/NmeaParser/Nmea/Gll.cs b/src/NmeaParser/Nmea/Gll.cs index 61966a8..60086f1 100644 --- a/src/NmeaParser/Nmea/Gll.cs +++ b/src/NmeaParser/Nmea/Gll.cs @@ -22,7 +22,7 @@ namespace NmeaParser.Nmea [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gll")] [NmeaMessageType("--GLL")] public class Gll : NmeaMessage - { + { /// /// Initializes a new instance of the class. /// @@ -31,14 +31,14 @@ namespace NmeaParser.Nmea public Gll(string type, string[] message) : base(type, message) { if (message == null || message.Length < 4) - throw new ArgumentException("Invalid GPGLL", "message"); - Latitude = NmeaMessage.StringToLatitude(message[0], message[1]); - Longitude = NmeaMessage.StringToLongitude(message[2], message[3]); - if (message.Length >= 5) //Some older GPS doesn't broadcast fix time - { - FixTime = StringToTimeSpan(message[4]); - } - DataActive = (message.Length < 6 || message[5] == "A"); + throw new ArgumentException("Invalid GPGLL", "message"); + Latitude = NmeaMessage.StringToLatitude(message[0], message[1]); + Longitude = NmeaMessage.StringToLongitude(message[2], message[3]); + if (message.Length >= 5) //Some older GPS doesn't broadcast fix time + { + FixTime = StringToTimeSpan(message[4]); + } + DataActive = (message.Length < 6 || message[5] == "A"); ModeIndicator = DataActive ? Mode.Autonomous : Mode.DataNotValid; if (message.Length > 6) { @@ -54,28 +54,28 @@ namespace NmeaParser.Nmea } } - /// - /// Latitude - /// - public double Latitude { get; } + /// + /// Latitude + /// + public double Latitude { get; } - /// - /// Longitude - /// - public double Longitude { get; } + /// + /// Longitude + /// + public double Longitude { get; } - /// - /// Time since last DGPS update - /// - public TimeSpan FixTime { get; } + /// + /// Time since last DGPS update + /// + public TimeSpan FixTime { get; } - /// - /// Gets a value indicating whether data is active. - /// - /// - /// true if data is active; otherwise, false. - /// - public bool DataActive { get; } + /// + /// Gets a value indicating whether data is active. + /// + /// + /// true if data is active; otherwise, false. + /// + public bool DataActive { get; } /// /// Positioning system Mode Indicator @@ -113,5 +113,5 @@ namespace NmeaParser.Nmea /// DataNotValid } - } + } } diff --git a/src/NmeaParser/Nmea/Gns.cs b/src/NmeaParser/Nmea/Gns.cs index db70972..3a5e31e 100644 --- a/src/NmeaParser/Nmea/Gns.cs +++ b/src/NmeaParser/Nmea/Gns.cs @@ -24,7 +24,7 @@ namespace NmeaParser.Nmea [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gns")] [NmeaMessageType("--GNS")] public class Gns : NmeaMessage - { + { /* * Example of GNS messages: * $GNGNS,014035.00,4332.69262,S,17235.48549,E,RR,13,0.9,25.63,11.24,,*70 //GLONASS @@ -116,11 +116,11 @@ namespace NmeaParser.Nmea } /// - /// Initializes a new instance of the class. - /// + /// Initializes a new instance of the class. + /// /// The message type - /// The NMEA message values. - public Gns(string type, string[] message) : base(type, message) + /// The NMEA message values. + public Gns(string type, string[] message) : base(type, message) { if (message == null || message.Length < 12) throw new ArgumentException("Invalid GNS", "message"); @@ -162,20 +162,20 @@ namespace NmeaParser.Nmea } } - /// - /// Time of day fix was taken - /// - public TimeSpan FixTime { get; } - - /// - /// Latitude - /// - public double Latitude { get; } + /// + /// Time of day fix was taken + /// + public TimeSpan FixTime { get; } + + /// + /// Latitude + /// + public double Latitude { get; } - /// - /// Longitude - /// - public double Longitude { get; } + /// + /// Longitude + /// + public double Longitude { get; } /// /// Mode indicator for GPS @@ -207,7 +207,7 @@ namespace NmeaParser.Nmea /// Horizontal Dilution of Precision (HDOP), calculated using all the satellites (GPS, GLONASS, and any future satellites) used in computing the solution reported in each GNS sentence. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hdop")] - public double Hdop { get; } + public double Hdop { get; } /// /// Orthometric height in meters (MSL reference) @@ -222,11 +222,11 @@ namespace NmeaParser.Nmea /// /// Age of differential data - if talker ID is GN, additional GNS messages follow with GP and/or GL Age of differential data - /// - public TimeSpan TimeSinceLastDgpsUpdate { get; } + /// + public TimeSpan TimeSinceLastDgpsUpdate { get; } - /// - /// eference station ID1, range 0000-4095 - Null if talker ID is GN, additional GNS messages follow with GP and/or GL Reference station ID + /// + /// eference station ID1, range 0000-4095 - Null if talker ID is GN, additional GNS messages follow with GP and/or GL Reference station ID /// public string? DgpsStationId { get; } @@ -234,5 +234,5 @@ namespace NmeaParser.Nmea /// Navigational status /// public NavigationalStatus Status { get; } - } + } } diff --git a/src/NmeaParser/Nmea/LaserRange/LaserRangeMessage.cs b/src/NmeaParser/Nmea/LaserRange/LaserRangeMessage.cs index d5a8de2..c95ab1c 100644 --- a/src/NmeaParser/Nmea/LaserRange/LaserRangeMessage.cs +++ b/src/NmeaParser/Nmea/LaserRange/LaserRangeMessage.cs @@ -30,62 +30,62 @@ namespace NmeaParser.Nmea.LaserRange protected LaserRangeMessage(string type, string[] message) : base(type, message) { if (message == null || message.Length < 9) - throw new ArgumentException("Invalid Laser Range Message", "message"); - - HorizontalVector = message[0]; - HorizontalDistance = double.Parse(message[1], CultureInfo.InvariantCulture); - HorizontalDistanceUnits = message[2][0]; - HorizontalAngle = double.Parse(message[3], CultureInfo.InvariantCulture); - HorizontalAngleUnits = message[4][0]; - VerticalAngle = double.Parse(message[5], CultureInfo.InvariantCulture); - VerticalAngleUnits = message[6][0]; - SlopeDistance = double.Parse(message[7], CultureInfo.InvariantCulture); - SlopeDistanceUnits = message[8][0]; - } + throw new ArgumentException("Invalid Laser Range Message", "message"); + + HorizontalVector = message[0]; + HorizontalDistance = double.Parse(message[1], CultureInfo.InvariantCulture); + HorizontalDistanceUnits = message[2][0]; + HorizontalAngle = double.Parse(message[3], CultureInfo.InvariantCulture); + HorizontalAngleUnits = message[4][0]; + VerticalAngle = double.Parse(message[5], CultureInfo.InvariantCulture); + VerticalAngleUnits = message[6][0]; + SlopeDistance = double.Parse(message[7], CultureInfo.InvariantCulture); + SlopeDistanceUnits = message[8][0]; + } - /// - /// Gets the horizontal vector. - /// - public string HorizontalVector { get; } + /// + /// Gets the horizontal vector. + /// + public string HorizontalVector { get; } - /// - /// Gets the horizontal distance. - /// - public double HorizontalDistance { get; } + /// + /// Gets the horizontal distance. + /// + public double HorizontalDistance { get; } - /// - /// Gets the units of the value. - /// - public char HorizontalDistanceUnits { get; } + /// + /// Gets the units of the value. + /// + public char HorizontalDistanceUnits { get; } - /// - /// Gets the horizontal angle. - /// - public double HorizontalAngle { get; } + /// + /// Gets the horizontal angle. + /// + public double HorizontalAngle { get; } - /// - /// Gets the units of the value. - /// - public char HorizontalAngleUnits { get; } + /// + /// Gets the units of the value. + /// + public char HorizontalAngleUnits { get; } - /// - /// Gets the vertical angle. - /// - public double VerticalAngle { get; } + /// + /// Gets the vertical angle. + /// + public double VerticalAngle { get; } - /// - /// Gets the units of the value. - /// - public char VerticalAngleUnits { get; } + /// + /// Gets the units of the value. + /// + public char VerticalAngleUnits { get; } - /// - /// Gets the slope distance. - /// - public double SlopeDistance { get; } + /// + /// Gets the slope distance. + /// + public double SlopeDistance { get; } - /// - /// Gets the units of the value. - /// - public char SlopeDistanceUnits { get; } - } + /// + /// Gets the units of the value. + /// + public char SlopeDistanceUnits { get; } + } } diff --git a/src/NmeaParser/Nmea/LaserRange/LaserTech/PLTIT.cs b/src/NmeaParser/Nmea/LaserRange/LaserTech/PLTIT.cs index e479bfb..7390d86 100644 --- a/src/NmeaParser/Nmea/LaserRange/LaserTech/PLTIT.cs +++ b/src/NmeaParser/Nmea/LaserRange/LaserTech/PLTIT.cs @@ -18,8 +18,8 @@ namespace NmeaParser.Nmea.LaserRange.LaserTech /// Laser Range /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pltit")] - [NmeaMessageType("PLTIT")] - public class Pltit : LaserRangeMessage + [NmeaMessageType("PLTIT")] + public class Pltit : LaserRangeMessage { /// /// Initializes a new instance of the class. diff --git a/src/NmeaParser/Nmea/LaserRange/Trimble/PTNLA.cs b/src/NmeaParser/Nmea/LaserRange/Trimble/PTNLA.cs index 453d8a8..17c31bb 100644 --- a/src/NmeaParser/Nmea/LaserRange/Trimble/PTNLA.cs +++ b/src/NmeaParser/Nmea/LaserRange/Trimble/PTNLA.cs @@ -18,8 +18,8 @@ namespace NmeaParser.Nmea.LaserRange.Trimble /// Burden finder /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ptnla")] - [NmeaMessageType("PTNLA")] - public class Ptnla : LaserRangeMessage + [NmeaMessageType("PTNLA")] + public class Ptnla : LaserRangeMessage { /// /// Initializes a new instance of the class. diff --git a/src/NmeaParser/Nmea/NmeaMessage.cs b/src/NmeaParser/Nmea/NmeaMessage.cs index 076b484..a773dec 100644 --- a/src/NmeaParser/Nmea/NmeaMessage.cs +++ b/src/NmeaParser/Nmea/NmeaMessage.cs @@ -24,21 +24,21 @@ namespace NmeaParser.Nmea /// Nmea message attribute type used on concrete implementations. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public sealed class NmeaMessageTypeAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// The type. - public NmeaMessageTypeAttribute(string nmeaType) - { - NmeaType = nmeaType; - } - /// - /// Gets or sets the NMEA message type. - /// - public string NmeaType { get; private set; } - } + public sealed class NmeaMessageTypeAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The type. + public NmeaMessageTypeAttribute(string nmeaType) + { + NmeaType = nmeaType; + } + /// + /// Gets or sets the NMEA message type. + /// + public string NmeaType { get; private set; } + } /// /// NMEA Message base class. @@ -169,14 +169,14 @@ namespace NmeaParser.Nmea /// A that represents this instance. /// public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "${0},{1}*{2:X2}", MessageType, string.Join(",", MessageParts), Checksum); - } + { + return string.Format(CultureInfo.InvariantCulture, "${0},{1}*{2:X2}", MessageType, string.Join(",", MessageParts), Checksum); + } - /// - /// Gets the checksum value of the message. - /// - public byte Checksum => GetChecksum(MessageType, MessageParts); + /// + /// Gets the checksum value of the message. + /// + public byte Checksum => GetChecksum(MessageType, MessageParts); internal static byte GetChecksum(string messageType, IReadOnlyList messageParts) { @@ -194,43 +194,43 @@ namespace NmeaParser.Nmea return Convert.ToByte(checksumTest); } - internal static double StringToLatitude(string value, string ns) - { - if (value == null || value.Length < 3) - return double.NaN; - double latitude = int.Parse(value.Substring(0, 2), CultureInfo.InvariantCulture) + double.Parse(value.Substring(2), CultureInfo.InvariantCulture) / 60; - if (ns == "S") - latitude *= -1; - return latitude; - } + internal static double StringToLatitude(string value, string ns) + { + if (value == null || value.Length < 3) + return double.NaN; + double latitude = int.Parse(value.Substring(0, 2), CultureInfo.InvariantCulture) + double.Parse(value.Substring(2), CultureInfo.InvariantCulture) / 60; + if (ns == "S") + latitude *= -1; + return latitude; + } - internal static double StringToLongitude(string value, string ew) - { - if (value == null || value.Length < 4) - return double.NaN; - double longitude = int.Parse(value.Substring(0, 3), CultureInfo.InvariantCulture) + double.Parse(value.Substring(3), CultureInfo.InvariantCulture) / 60; - if (ew == "W") - longitude *= -1; - return longitude; - } + internal static double StringToLongitude(string value, string ew) + { + if (value == null || value.Length < 4) + return double.NaN; + double longitude = int.Parse(value.Substring(0, 3), CultureInfo.InvariantCulture) + double.Parse(value.Substring(3), CultureInfo.InvariantCulture) / 60; + if (ew == "W") + longitude *= -1; + return longitude; + } - internal static double StringToDouble(string value) - { - if(value != null && double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out double result)) - { - return result; - } - return double.NaN; - } - internal static TimeSpan StringToTimeSpan(string value) - { - if (value != null && value.Length >= 6) - { - return new TimeSpan(int.Parse(value.Substring(0, 2), CultureInfo.InvariantCulture), - int.Parse(value.Substring(2, 2), CultureInfo.InvariantCulture), 0) - .Add(TimeSpan.FromSeconds(double.Parse(value.Substring(4), CultureInfo.InvariantCulture))); - } - return TimeSpan.Zero; - } - } + internal static double StringToDouble(string value) + { + if(value != null && double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out double result)) + { + return result; + } + return double.NaN; + } + internal static TimeSpan StringToTimeSpan(string value) + { + if (value != null && value.Length >= 6) + { + return new TimeSpan(int.Parse(value.Substring(0, 2), CultureInfo.InvariantCulture), + int.Parse(value.Substring(2, 2), CultureInfo.InvariantCulture), 0) + .Add(TimeSpan.FromSeconds(double.Parse(value.Substring(4), CultureInfo.InvariantCulture))); + } + return TimeSpan.Zero; + } + } } diff --git a/src/NmeaParser/Nmea/Rmc.cs b/src/NmeaParser/Nmea/Rmc.cs index 89af646..1cfdd1e 100644 --- a/src/NmeaParser/Nmea/Rmc.cs +++ b/src/NmeaParser/Nmea/Rmc.cs @@ -23,7 +23,7 @@ namespace NmeaParser.Nmea [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gprmc")] [NmeaMessageType("--RMC")] public class Rmc : NmeaMessage - { + { /// /// Initializes a new instance of the class. /// @@ -32,60 +32,60 @@ namespace NmeaParser.Nmea public Rmc(string type, string[] message) : base(type, message) { if (message == null || message.Length < 11) - throw new ArgumentException("Invalid GPRMC", "message"); - - if (message[8].Length == 6 && message[0].Length >= 6) - { - FixTime = new DateTime(int.Parse(message[8].Substring(4, 2), CultureInfo.InvariantCulture) + 2000, - int.Parse(message[8].Substring(2, 2), CultureInfo.InvariantCulture), - int.Parse(message[8].Substring(0, 2), CultureInfo.InvariantCulture), - int.Parse(message[0].Substring(0, 2), CultureInfo.InvariantCulture), - int.Parse(message[0].Substring(2, 2), CultureInfo.InvariantCulture), - 0, DateTimeKind.Utc).AddSeconds(double.Parse(message[0].Substring(4), CultureInfo.InvariantCulture)); - } - Active = (message[1] == "A"); - Latitude = NmeaMessage.StringToLatitude(message[2], message[3]); - Longitude = NmeaMessage.StringToLongitude(message[4], message[5]); - Speed = NmeaMessage.StringToDouble(message[6]); - Course = NmeaMessage.StringToDouble(message[7]); - MagneticVariation = NmeaMessage.StringToDouble(message[9]); - if (!double.IsNaN(MagneticVariation) && message[10] == "W") - MagneticVariation *= -1; - } + throw new ArgumentException("Invalid GPRMC", "message"); + + if (message[8].Length == 6 && message[0].Length >= 6) + { + FixTime = new DateTime(int.Parse(message[8].Substring(4, 2), CultureInfo.InvariantCulture) + 2000, + int.Parse(message[8].Substring(2, 2), CultureInfo.InvariantCulture), + int.Parse(message[8].Substring(0, 2), CultureInfo.InvariantCulture), + int.Parse(message[0].Substring(0, 2), CultureInfo.InvariantCulture), + int.Parse(message[0].Substring(2, 2), CultureInfo.InvariantCulture), + 0, DateTimeKind.Utc).AddSeconds(double.Parse(message[0].Substring(4), CultureInfo.InvariantCulture)); + } + Active = (message[1] == "A"); + Latitude = NmeaMessage.StringToLatitude(message[2], message[3]); + Longitude = NmeaMessage.StringToLongitude(message[4], message[5]); + Speed = NmeaMessage.StringToDouble(message[6]); + Course = NmeaMessage.StringToDouble(message[7]); + MagneticVariation = NmeaMessage.StringToDouble(message[9]); + if (!double.IsNaN(MagneticVariation) && message[10] == "W") + MagneticVariation *= -1; + } - /// - /// Fix Time - /// - public DateTime FixTime { get; } + /// + /// Fix Time + /// + public DateTime FixTime { get; } - /// - /// Gets a value whether the device is active - /// - public bool Active { get; } + /// + /// Gets a value whether the device is active + /// + public bool Active { get; } - /// - /// Latitude - /// - public double Latitude { get; } + /// + /// Latitude + /// + public double Latitude { get; } - /// - /// Longitude - /// - public double Longitude { get; } + /// + /// Longitude + /// + public double Longitude { get; } - /// - /// Speed over the ground in knots - /// - public double Speed { get; } + /// + /// Speed over the ground in knots + /// + public double Speed { get; } - /// - /// Track angle in degrees True - /// - public double Course { get; } + /// + /// Track angle in degrees True + /// + public double Course { get; } - /// - /// Magnetic Variation - /// - public double MagneticVariation { get; } - } + /// + /// Magnetic Variation + /// + public double MagneticVariation { get; } + } } diff --git a/src/NmeaParser/Nmea/UnknownMessage.cs b/src/NmeaParser/Nmea/UnknownMessage.cs index cba4f01..f5a0d86 100644 --- a/src/NmeaParser/Nmea/UnknownMessage.cs +++ b/src/NmeaParser/Nmea/UnknownMessage.cs @@ -20,12 +20,12 @@ namespace NmeaParser.Nmea /// Represents an unknown message type /// public class UnknownMessage : NmeaMessage - { + { internal UnknownMessage(string type, string[] messageParts) : base(type, messageParts) { } - /// - /// Gets the nmea value array. - /// - public IReadOnlyList Values { get { return base.MessageParts; } } - } + /// + /// Gets the nmea value array. + /// + public IReadOnlyList Values { get { return base.MessageParts; } } + } } diff --git a/src/NmeaParser/Nmea/Vtg.cs b/src/NmeaParser/Nmea/Vtg.cs index 5ffd659..3c84dbf 100644 --- a/src/NmeaParser/Nmea/Vtg.cs +++ b/src/NmeaParser/Nmea/Vtg.cs @@ -17,12 +17,12 @@ using System; namespace NmeaParser.Nmea { /// - /// Course over ground and ground speed - /// + /// Course over ground and ground speed + /// /// /// The actual course and speed relative to the ground. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "GPVTG")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "GPVTG")] [NmeaMessageType("--VTG")] public class Vtg : NmeaMessage { diff --git a/src/NmeaParser/NmeaDevice.cs b/src/NmeaParser/NmeaDevice.cs index b0b1ed4..8f20a95 100644 --- a/src/NmeaParser/NmeaDevice.cs +++ b/src/NmeaParser/NmeaDevice.cs @@ -22,41 +22,41 @@ using System.Threading; namespace NmeaParser { - /// - /// A generic abstract NMEA device - /// - public abstract class NmeaDevice : IDisposable - { - private readonly object m_lockObject = new object(); - private string m_message = ""; - private Stream? m_stream; - private CancellationTokenSource? m_cts; - private bool m_isOpening; + /// + /// A generic abstract NMEA device + /// + public abstract class NmeaDevice : IDisposable + { + private readonly object m_lockObject = new object(); + private string m_message = ""; + private Stream? m_stream; + private CancellationTokenSource? m_cts; + private bool m_isOpening; private Task? m_ParserTask; /// /// Initializes a new instance of the class. /// protected NmeaDevice() - { - } + { + } /// - /// Opens the device connection. - /// - /// - public async Task OpenAsync() - { - lock (m_lockObject) - { - if (IsOpen || m_isOpening) return; + /// Opens the device connection. + /// + /// + public async Task OpenAsync() + { + lock (m_lockObject) + { + if (IsOpen || m_isOpening) return; m_isOpening = true; - } - m_cts = new CancellationTokenSource(); - m_stream = await OpenStreamAsync(); - StartParser(m_cts.Token); - _lastMultiMessage = null; - lock (m_lockObject) + } + m_cts = new CancellationTokenSource(); + m_stream = await OpenStreamAsync(); + StartParser(m_cts.Token); + _lastMultiMessage = null; + lock (m_lockObject) { IsOpen = true; m_isOpening = false; @@ -64,30 +64,30 @@ namespace NmeaParser } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "_")] - private void StartParser(CancellationToken token) - { - System.Diagnostics.Debug.WriteLine("Starting parser..."); + private void StartParser(CancellationToken token) + { + System.Diagnostics.Debug.WriteLine("Starting parser..."); m_ParserTask = Task.Run(async () => - { - byte[] buffer = new byte[1024]; - while (!token.IsCancellationRequested) - { - int readCount = 0; - try - { - readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false); - } - catch { } - if (token.IsCancellationRequested) - break; - if (readCount > 0) - { - OnData(buffer.Take(readCount).ToArray()); - } - await Task.Delay(50); - } - }); - } + { + byte[] buffer = new byte[1024]; + while (!token.IsCancellationRequested) + { + int readCount = 0; + try + { + readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false); + } + catch { } + if (token.IsCancellationRequested) + break; + if (readCount > 0) + { + OnData(buffer.Take(readCount).ToArray()); + } + await Task.Delay(50); + } + }); + } /// /// Performs a read operation of the stream @@ -116,84 +116,84 @@ namespace NmeaParser /// protected abstract Task OpenStreamAsync(); - /// - /// Closes the device. - /// - /// - public async Task CloseAsync() - { - if (m_cts != null) - { - if (m_cts != null) - m_cts.Cancel(); - m_cts = null; - } + /// + /// Closes the device. + /// + /// + public async Task CloseAsync() + { + if (m_cts != null) + { + if (m_cts != null) + m_cts.Cancel(); + m_cts = null; + } if (m_ParserTask != null) await m_ParserTask; if (m_stream != null) await CloseStreamAsync(m_stream); - _lastMultiMessage = null; - m_stream = null; + _lastMultiMessage = null; + m_stream = null; lock (m_lockObject) { m_isOpening = false; IsOpen = false; } - } - /// - /// Closes the stream the NmeaDevice is working on top off. - /// - /// The stream. - /// - protected abstract Task CloseStreamAsync(Stream stream); + } + /// + /// Closes the stream the NmeaDevice is working on top off. + /// + /// The stream. + /// + protected abstract Task CloseStreamAsync(Stream stream); - private void OnData(byte[] data) - { - var nmea = System.Text.Encoding.UTF8.GetString(data, 0, data.Length); - List lines = new List(); - lock (m_lockObject) - { - m_message += nmea; + private void OnData(byte[] data) + { + var nmea = System.Text.Encoding.UTF8.GetString(data, 0, data.Length); + List lines = new List(); + lock (m_lockObject) + { + m_message += nmea; - var lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); - while (lineEnd > -1) - { - string line = m_message.Substring(0, lineEnd).Trim(); - m_message = m_message.Substring(lineEnd + 1); - if (!string.IsNullOrEmpty(line)) - lines.Add(line); - lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); - } - } - foreach(var line in lines) - ProcessMessage(line); - } + var lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); + while (lineEnd > -1) + { + string line = m_message.Substring(0, lineEnd).Trim(); + m_message = m_message.Substring(lineEnd + 1); + if (!string.IsNullOrEmpty(line)) + lines.Add(line); + lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); + } + } + foreach(var line in lines) + ProcessMessage(line); + } - private IMultiSentenceMessage? _lastMultiMessage; + private IMultiSentenceMessage? _lastMultiMessage; - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Must silently handle invalid/corrupt input")] - private void ProcessMessage(string p) - { - try - { - var msg = NmeaParser.Nmea.NmeaMessage.Parse(p, _lastMultiMessage); - if(msg is IMultiSentenceMessage multi) - { - if (!multi.IsComplete) - { - _lastMultiMessage = multi; //Keep it around until next time - return; - } - } - _lastMultiMessage = null; - if (msg != null) - OnMessageReceived(msg); - } - catch { } - } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Must silently handle invalid/corrupt input")] + private void ProcessMessage(string p) + { + try + { + var msg = NmeaParser.Nmea.NmeaMessage.Parse(p, _lastMultiMessage); + if(msg is IMultiSentenceMessage multi) + { + if (!multi.IsComplete) + { + _lastMultiMessage = multi; //Keep it around until next time + return; + } + } + _lastMultiMessage = null; + if (msg != null) + OnMessageReceived(msg); + } + catch { } + } - private void OnMessageReceived(Nmea.NmeaMessage msg) + private void OnMessageReceived(Nmea.NmeaMessage msg) { if (msg == null) return; @@ -201,49 +201,49 @@ namespace NmeaParser MessageReceived?.Invoke(this, new NmeaMessageReceivedEventArgs(msg)); } - //private readonly Dictionary> MultiPartMessageCache = new Dictionary>(); + //private readonly Dictionary> MultiPartMessageCache = new Dictionary>(); - /// - /// Occurs when an NMEA message is received. - /// - public event EventHandler? MessageReceived; + /// + /// Occurs when an NMEA message is received. + /// + public event EventHandler? MessageReceived; - /// - /// Releases unmanaged and - optionally - managed resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (m_stream != null) - { - if (m_cts != null) - { - m_cts.Cancel(); - m_cts = null; - } - CloseStreamAsync(m_stream); - if (disposing && m_stream != null) - m_stream.Dispose(); - m_stream = null; - } - } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (m_stream != null) + { + if (m_cts != null) + { + m_cts.Cancel(); + m_cts = null; + } + CloseStreamAsync(m_stream); + if (disposing && m_stream != null) + m_stream.Dispose(); + m_stream = null; + } + } - /// - /// Gets a value indicating whether this device is open. - /// - /// - /// true if this instance is open; otherwise, false. - /// - public bool IsOpen { get; private set; } + /// + /// Gets a value indicating whether this device is open. + /// + /// + /// true if this instance is open; otherwise, false. + /// + public bool IsOpen { get; private set; } /// /// Gets a value indicating whether this device supports writing @@ -255,34 +255,34 @@ namespace NmeaParser /// Writes to the device stream. Useful for transmitting RTCM corrections to the device /// Check the property before calling this method. /// - /// The byte array that contains the data to write to the port. - /// The zero-based byte offset in the buffer parameter at which to begin copying - /// bytes to the port. - /// The number of bytes to write. + /// The byte array that contains the data to write to the port. + /// The zero-based byte offset in the buffer parameter at which to begin copying + /// bytes to the port. + /// The number of bytes to write. /// Task /// public virtual Task WriteAsync(byte[] buffer, int offset, int length) { throw new NotSupportedException(); } - } + } - /// - /// Event argument for the - /// - public sealed class NmeaMessageReceivedEventArgs : EventArgs - { - internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message) + /// + /// Event argument for the + /// + public sealed class NmeaMessageReceivedEventArgs : EventArgs + { + internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message) { - Message = message; - } + Message = message; + } - /// - /// Gets the nmea message. - /// - /// - /// The nmea message. - /// - public Nmea.NmeaMessage Message { get; } - } + /// + /// Gets the nmea message. + /// + /// + /// The nmea message. + /// + public Nmea.NmeaMessage Message { get; } + } } diff --git a/src/NmeaParser/NmeaFileDevice.cs b/src/NmeaParser/NmeaFileDevice.cs index e7f7d13..48bdc88 100644 --- a/src/NmeaParser/NmeaFileDevice.cs +++ b/src/NmeaParser/NmeaFileDevice.cs @@ -21,13 +21,13 @@ using System.Threading.Tasks; namespace NmeaParser { - /// - /// A file-based NMEA device reading from a NMEA log file. - /// - public class NmeaFileDevice : BufferedStreamDevice - { + /// + /// A file-based NMEA device reading from a NMEA log file. + /// + public class NmeaFileDevice : BufferedStreamDevice + { #if NETFX_CORE - private Windows.Storage.IStorageFile? m_storageFile; + private Windows.Storage.IStorageFile? m_storageFile; #endif private string m_filename; @@ -36,7 +36,7 @@ namespace NmeaParser /// Initializes a new instance of the class. /// /// - public NmeaFileDevice(string fileName) : this(fileName, 1000) + public NmeaFileDevice(string fileName) : this(fileName, 1000) { } @@ -46,18 +46,18 @@ namespace NmeaParser /// /// public NmeaFileDevice(Windows.Storage.IStorageFile storageFile) : this(storageFile, 1000) - { - } + { + } #endif /// /// Initializes a new instance of the class. /// /// /// The time to wait between each group of lines being read in milliseconds - public NmeaFileDevice(string fileName, int readSpeed) : base(readSpeed) - { - m_filename = fileName; - } + public NmeaFileDevice(string fileName, int readSpeed) : base(readSpeed) + { + m_filename = fileName; + } #if NETFX_CORE /// @@ -73,24 +73,24 @@ namespace NmeaParser } #endif - /// - /// Gets the name of the nmea file this device is using. - /// - public string FileName - { - get + /// + /// Gets the name of the nmea file this device is using. + /// + public string FileName + { + get { return m_filename; } - } + } /// /// Gets the stream to perform buffer reads on. /// /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] protected override Task GetStreamAsync() - { + { #if NETFX_CORE if (m_storageFile != null) @@ -102,5 +102,5 @@ namespace NmeaParser return Task.FromResult(System.IO.File.OpenRead(m_filename)); #endif } - } + } } diff --git a/src/NmeaParser/Properties/NmeaParser.rd.xml b/src/NmeaParser/Properties/NmeaParser.rd.xml index 9c46111..0b133a3 100644 --- a/src/NmeaParser/Properties/NmeaParser.rd.xml +++ b/src/NmeaParser/Properties/NmeaParser.rd.xml @@ -27,7 +27,7 @@ - + diff --git a/src/NmeaParser/SerialPortDevice.Desktop.cs b/src/NmeaParser/SerialPortDevice.Desktop.cs index 7c84311..bad59e3 100644 --- a/src/NmeaParser/SerialPortDevice.Desktop.cs +++ b/src/NmeaParser/SerialPortDevice.Desktop.cs @@ -22,22 +22,22 @@ using System.Threading.Tasks; namespace NmeaParser { - /// - /// A Serial Port NMEA device - /// - public class SerialPortDevice : NmeaDevice - { + /// + /// A Serial Port NMEA device + /// + public class SerialPortDevice : NmeaDevice + { /// /// Initializes a new instance of the class. /// /// The serial port. /// port public SerialPortDevice(System.IO.Ports.SerialPort port) - { - if (port == null) - throw new ArgumentNullException("port"); - Port = port; - } + { + if (port == null) + throw new ArgumentNullException("port"); + Port = port; + } /// /// Gets the active serial port. @@ -49,35 +49,35 @@ namespace NmeaParser /// /// protected override Task OpenStreamAsync() - { + { if (!Port.IsOpen) Port.Open(); - return Task.FromResult(Port.BaseStream); - } + return Task.FromResult(Port.BaseStream); + } - /// - /// Closes the stream the NmeaDevice is working on top off. - /// - /// The stream. - /// - protected override Task CloseStreamAsync(System.IO.Stream stream) - { + /// + /// Closes the stream the NmeaDevice is working on top off. + /// + /// The stream. + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) + { if (Port.IsOpen) Port.Close(); - return Task.FromResult(null); - } + return Task.FromResult(null); + } - /// - /// Writes data to the serial port (useful for RTCM/dGPS scenarios) - /// - /// The byte array that contains the data to write to the port. - /// The zero-based byte offset in the buffer parameter at which to begin copying - /// bytes to the port. - /// The number of bytes to write. + /// + /// Writes data to the serial port (useful for RTCM/dGPS scenarios) + /// + /// The byte array that contains the data to write to the port. + /// The zero-based byte offset in the buffer parameter at which to begin copying + /// bytes to the port. + /// The number of bytes to write. [Obsolete("Use WriteAsync")] - public void Write(byte[] buffer, int offset, int count) - { - Port.Write(buffer, offset, count); + public void Write(byte[] buffer, int offset, int count) + { + Port.Write(buffer, offset, count); } /// diff --git a/src/NmeaParser/SerialPortDevice.UWP.cs b/src/NmeaParser/SerialPortDevice.UWP.cs index c30119e..9c572f5 100644 --- a/src/NmeaParser/SerialPortDevice.UWP.cs +++ b/src/NmeaParser/SerialPortDevice.UWP.cs @@ -24,67 +24,67 @@ using System.Runtime.InteropServices.WindowsRuntime; namespace NmeaParser { - /// - /// A Serial Port NMEA device - /// - public class SerialPortDevice : NmeaDevice - { - private SerialDevice m_port; + /// + /// A Serial Port NMEA device + /// + public class SerialPortDevice : NmeaDevice + { + private SerialDevice m_port; - /// - /// Initializes a new instance of the class. - /// - /// The serial port. - /// port - public SerialPortDevice(SerialDevice device) - { - if (device == null) - throw new ArgumentNullException("device"); - m_port = device; - } + /// + /// Initializes a new instance of the class. + /// + /// The serial port. + /// port + public SerialPortDevice(SerialDevice device) + { + if (device == null) + throw new ArgumentNullException("device"); + m_port = device; + } - /// - /// Gets the active serial port. - /// - public SerialDevice SerialDevice - { - get - { - return m_port; - } - } + /// + /// Gets the active serial port. + /// + public SerialDevice SerialDevice + { + get + { + return m_port; + } + } - /// - /// Creates the stream the NmeaDevice is working on top off. - /// - /// - protected override Task OpenStreamAsync() - { - return Task.FromResult(m_port.InputStream.AsStreamForRead(0)); - } + /// + /// Creates the stream the NmeaDevice is working on top off. + /// + /// + protected override Task OpenStreamAsync() + { + return Task.FromResult(m_port.InputStream.AsStreamForRead(0)); + } - /// - /// Closes the stream the NmeaDevice is working on top off. - /// - /// The stream. - /// - protected override Task CloseStreamAsync(System.IO.Stream stream) - { - return Task.CompletedTask; - } + /// + /// Closes the stream the NmeaDevice is working on top off. + /// + /// The stream. + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) + { + return Task.CompletedTask; + } - /// - /// Writes data to the serial port (useful for RTCM/dGPS scenarios) - /// - /// The byte array that contains the data to write to the port. - /// The zero-based byte offset in the buffer parameter at which to begin copying - /// bytes to the port. - /// The number of bytes to write. + /// + /// Writes data to the serial port (useful for RTCM/dGPS scenarios) + /// + /// The byte array that contains the data to write to the port. + /// The zero-based byte offset in the buffer parameter at which to begin copying + /// bytes to the port. + /// The number of bytes to write. [Obsolete("Use WriteAsync")] - public void Write(byte[] buffer, int offset, int count) - { - m_port.OutputStream.AsStreamForWrite().Write(buffer, offset, count); - } + public void Write(byte[] buffer, int offset, int count) + { + m_port.OutputStream.AsStreamForWrite().Write(buffer, offset, count); + } /// public override bool CanWrite => true; diff --git a/src/NmeaParser/StreamDevice.cs b/src/NmeaParser/StreamDevice.cs index f92cbef..e8cbafc 100644 --- a/src/NmeaParser/StreamDevice.cs +++ b/src/NmeaParser/StreamDevice.cs @@ -20,51 +20,51 @@ using System.Threading.Tasks; namespace NmeaParser { - /// - /// Generic stream device - /// + /// + /// Generic stream device + /// public class StreamDevice : NmeaDevice { - private Stream m_stream; + private Stream m_stream; - /// - /// Initializes a new instance of the class. - /// - /// The stream. - public StreamDevice(Stream stream) : base() - { - m_stream = stream ?? throw new ArgumentNullException(nameof(stream)); - } + /// + /// Initializes a new instance of the class. + /// + /// The stream. + public StreamDevice(Stream stream) : base() + { + m_stream = stream ?? throw new ArgumentNullException(nameof(stream)); + } - /// - /// Opens the stream asynchronous. - /// - /// - protected override Task OpenStreamAsync() - { - return Task.FromResult(m_stream); - } + /// + /// Opens the stream asynchronous. + /// + /// + protected override Task OpenStreamAsync() + { + return Task.FromResult(m_stream); + } - /// - /// Closes the stream asynchronous. - /// - /// The stream. - /// - protected override Task CloseStreamAsync(System.IO.Stream stream) - { - return Task.FromResult(true); //do nothing - } + /// + /// Closes the stream asynchronous. + /// + /// The stream. + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) + { + return Task.FromResult(true); //do nothing + } - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (m_stream != null) - m_stream.Dispose(); - } + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (m_stream != null) + m_stream.Dispose(); + } /// public override bool CanWrite => m_stream?.CanWrite == true; diff --git a/src/UnitTests/NmeaParser.Tests/DeviceTests.cs b/src/UnitTests/NmeaParser.Tests/DeviceTests.cs index 28be409..d20ea1b 100644 --- a/src/UnitTests/NmeaParser.Tests/DeviceTests.cs +++ b/src/UnitTests/NmeaParser.Tests/DeviceTests.cs @@ -8,42 +8,42 @@ using System.Threading.Tasks; namespace NmeaParser.Tests { - [TestClass] - public class DeviceTests - { - [TestMethod] - [TestCategory("Device")] + [TestClass] + public class DeviceTests + { + [TestMethod] + [TestCategory("Device")] [Timeout(2000)] - public async Task TestGpgsvGroupMessage() - { - var message = "$GPGSV,3,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4C\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; - NmeaDevice dev = new BufferedStringDevice(message); - TaskCompletionSource tcs = new TaskCompletionSource(); - int count = 0; - dev.MessageReceived += (s, e) => - { - count++; - try - { - Assert.IsInstanceOfType(e.Message, typeof(Gsv)); - var msg = (NmeaParser.Nmea.Gsv)e.Message; - Assert.IsTrue(((IMultiSentenceMessage)e.Message).IsComplete); - Assert.AreEqual(9, msg.SVsInView); - Assert.AreEqual(9, msg.SVs.Count); - - if (count > 1) - Assert.Fail(); - tcs.SetResult(true); - } - catch(System.Exception ex) - { - tcs.SetException(ex); - } - }; - await dev.OpenAsync(); - await tcs.Task; - var _ = dev.CloseAsync(); - } + public async Task TestGpgsvGroupMessage() + { + var message = "$GPGSV,3,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4C\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; + NmeaDevice dev = new BufferedStringDevice(message); + TaskCompletionSource tcs = new TaskCompletionSource(); + int count = 0; + dev.MessageReceived += (s, e) => + { + count++; + try + { + Assert.IsInstanceOfType(e.Message, typeof(Gsv)); + var msg = (NmeaParser.Nmea.Gsv)e.Message; + Assert.IsTrue(((IMultiSentenceMessage)e.Message).IsComplete); + Assert.AreEqual(9, msg.SVsInView); + Assert.AreEqual(9, msg.SVs.Count); + + if (count > 1) + Assert.Fail(); + tcs.SetResult(true); + } + catch(System.Exception ex) + { + tcs.SetException(ex); + } + }; + await dev.OpenAsync(); + await tcs.Task; + var _ = dev.CloseAsync(); + } [TestMethod] [TestCategory("Device")] @@ -57,47 +57,47 @@ $GLGSV,4,3,14,73,52,022,47,74,62,248,47,72,44,331,42,71,78,111,49*6A $GAGSV,4,4,14,19,82,349,40,1,44,220,40,4,24,314,38*5F"; NmeaDevice dev = new BufferedStringDevice(message); TaskCompletionSource tcs = new TaskCompletionSource(); - dev.MessageReceived += (s, e) => - { - try - { - Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); - Assert.AreEqual(Talker.Multiple, e.Message.TalkerId); - var msg = (NmeaParser.Nmea.Gsv)e.Message; - Assert.AreEqual(Talker.GlobalPositioningSystem, msg.SVs[0].TalkerId); - Assert.AreEqual(Talker.GlobalPositioningSystem, msg.SVs[4].TalkerId); - Assert.AreEqual(Talker.GlonassReceiver, msg.SVs[8].TalkerId); - Assert.AreEqual(Talker.GalileoPositioningSystem, msg.SVs[12].TalkerId); - tcs.SetResult(true); - } - catch (System.Exception ex) - { - tcs.SetException(ex); - } - }; + dev.MessageReceived += (s, e) => + { + try + { + Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); + Assert.AreEqual(Talker.Multiple, e.Message.TalkerId); + var msg = (NmeaParser.Nmea.Gsv)e.Message; + Assert.AreEqual(Talker.GlobalPositioningSystem, msg.SVs[0].TalkerId); + Assert.AreEqual(Talker.GlobalPositioningSystem, msg.SVs[4].TalkerId); + Assert.AreEqual(Talker.GlonassReceiver, msg.SVs[8].TalkerId); + Assert.AreEqual(Talker.GalileoPositioningSystem, msg.SVs[12].TalkerId); + tcs.SetResult(true); + } + catch (System.Exception ex) + { + tcs.SetException(ex); + } + }; await dev.OpenAsync(); await tcs.Task; var _ = dev.CloseAsync(); } [TestMethod] - [TestCategory("Device")] + [TestCategory("Device")] [Timeout(2000)] public async Task TestInvalidGpgsvGroupMessage() - { - var message = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4D\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; - NmeaDevice dev = new BufferedStringDevice(message); - TaskCompletionSource tcs = new TaskCompletionSource(); - bool messageRecieved = false; - dev.MessageReceived += (s, e) => - { - messageRecieved = true; - }; - await dev.OpenAsync(); - await Task.Delay(100); - var _ = dev.CloseAsync(); - if (messageRecieved) - Assert.Fail("Event shouldn't be raised for incomplete messages"); - } - } + { + var message = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4D\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; + NmeaDevice dev = new BufferedStringDevice(message); + TaskCompletionSource tcs = new TaskCompletionSource(); + bool messageRecieved = false; + dev.MessageReceived += (s, e) => + { + messageRecieved = true; + }; + await dev.OpenAsync(); + await Task.Delay(100); + var _ = dev.CloseAsync(); + if (messageRecieved) + Assert.Fail("Event shouldn't be raised for incomplete messages"); + } + } } diff --git a/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs b/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs index d4295e3..76831fa 100644 --- a/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs +++ b/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs @@ -570,117 +570,117 @@ namespace NmeaParser.Tests } [TestMethod] - public void TestGpgll_NoFixTime_OrActiveIndicator() - { - string input = "$GPGLL,3751.65,S,14507.36,E*77"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(Gll)); - Gll gll = (Gll)msg; - Assert.IsTrue(gll.DataActive); - Assert.AreEqual(-37.860833333333333333, gll.Latitude); - Assert.AreEqual(145.1226666666666666667, gll.Longitude); - Assert.AreEqual(TimeSpan.Zero, gll.FixTime); - } + public void TestGpgll_NoFixTime_OrActiveIndicator() + { + string input = "$GPGLL,3751.65,S,14507.36,E*77"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(Gll)); + Gll gll = (Gll)msg; + Assert.IsTrue(gll.DataActive); + Assert.AreEqual(-37.860833333333333333, gll.Latitude); + Assert.AreEqual(145.1226666666666666667, gll.Longitude); + Assert.AreEqual(TimeSpan.Zero, gll.FixTime); + } - [TestMethod] - public void TestGpbod_Empty() - { - string input = "$GPBOD,,T,,M,,*47"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(Bod)); - Bod bod = (Bod)msg; - Assert.AreEqual(double.NaN, bod.TrueBearing, "TrueBearing"); - Assert.AreEqual(double.NaN, bod.MagneticBearing, "MagneticBearing"); - Assert.IsNull(bod.OriginId, "OriginID"); - Assert.IsNull(bod.DestinationId, "DestinationID"); - } + [TestMethod] + public void TestGpbod_Empty() + { + string input = "$GPBOD,,T,,M,,*47"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(Bod)); + Bod bod = (Bod)msg; + Assert.AreEqual(double.NaN, bod.TrueBearing, "TrueBearing"); + Assert.AreEqual(double.NaN, bod.MagneticBearing, "MagneticBearing"); + Assert.IsNull(bod.OriginId, "OriginID"); + Assert.IsNull(bod.DestinationId, "DestinationID"); + } - [TestMethod] - public void TestGpbod_GoToMode() - { - string input = "$GPBOD,099.3,T,105.6,M,POINTB,*48"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(Bod)); - Bod bod = (Bod)msg; - Assert.AreEqual(99.3, bod.TrueBearing, "TrueBearing"); - Assert.AreEqual(105.6, bod.MagneticBearing, "MagneticBearing"); - Assert.AreEqual("POINTB", bod.DestinationId, "DestinationID"); - Assert.IsNull(bod.OriginId, "OriginID"); - } + [TestMethod] + public void TestGpbod_GoToMode() + { + string input = "$GPBOD,099.3,T,105.6,M,POINTB,*48"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(Bod)); + Bod bod = (Bod)msg; + Assert.AreEqual(99.3, bod.TrueBearing, "TrueBearing"); + Assert.AreEqual(105.6, bod.MagneticBearing, "MagneticBearing"); + Assert.AreEqual("POINTB", bod.DestinationId, "DestinationID"); + Assert.IsNull(bod.OriginId, "OriginID"); + } - [TestMethod] - public void TestGpbod() - { - string input = "$GPBOD,097.0,T,103.2,M,POINTB,POINTA*4A"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(Bod)); - Bod bod = (Bod)msg; - Assert.AreEqual(97d, bod.TrueBearing, "TrueBearing"); - Assert.AreEqual(103.2, bod.MagneticBearing, "MagneticBearing"); - Assert.AreEqual("POINTB", bod.DestinationId, "DestinationID"); - Assert.AreEqual("POINTA", bod.OriginId, "OriginID"); - } + [TestMethod] + public void TestGpbod() + { + string input = "$GPBOD,097.0,T,103.2,M,POINTB,POINTA*4A"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(Bod)); + Bod bod = (Bod)msg; + Assert.AreEqual(97d, bod.TrueBearing, "TrueBearing"); + Assert.AreEqual(103.2, bod.MagneticBearing, "MagneticBearing"); + Assert.AreEqual("POINTB", bod.DestinationId, "DestinationID"); + Assert.AreEqual("POINTA", bod.OriginId, "OriginID"); + } - [TestMethod] - public void TestPgrmz_Empty() - { - string input = "$PGRMZ,,,*7E"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(NmeaParser.Nmea.Gps.Garmin.Pgrmz)); - var rmz = (NmeaParser.Nmea.Gps.Garmin.Pgrmz)msg; - Assert.AreEqual(double.NaN, rmz.Altitude, "Altitude"); - Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.AltitudeUnit.Unknown, rmz.Unit, "Unit"); - Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.PositionFixType.Unknown, rmz.FixType, "FixDimension"); - } + [TestMethod] + public void TestPgrmz_Empty() + { + string input = "$PGRMZ,,,*7E"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(NmeaParser.Nmea.Gps.Garmin.Pgrmz)); + var rmz = (NmeaParser.Nmea.Gps.Garmin.Pgrmz)msg; + Assert.AreEqual(double.NaN, rmz.Altitude, "Altitude"); + Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.AltitudeUnit.Unknown, rmz.Unit, "Unit"); + Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.PositionFixType.Unknown, rmz.FixType, "FixDimension"); + } - [TestMethod] - public void TestPgrmz() - { - string input = "$PGRMZ,93,f,3*21"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(NmeaParser.Nmea.Gps.Garmin.Pgrmz)); - var rmz = (NmeaParser.Nmea.Gps.Garmin.Pgrmz)msg; - Assert.AreEqual(93d, rmz.Altitude, "Altitude"); - Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.AltitudeUnit.Feet, rmz.Unit, "Unit"); - Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.PositionFixType.Fix3D, rmz.FixType, "FixDimension"); - } + [TestMethod] + public void TestPgrmz() + { + string input = "$PGRMZ,93,f,3*21"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(NmeaParser.Nmea.Gps.Garmin.Pgrmz)); + var rmz = (NmeaParser.Nmea.Gps.Garmin.Pgrmz)msg; + Assert.AreEqual(93d, rmz.Altitude, "Altitude"); + Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.AltitudeUnit.Feet, rmz.Unit, "Unit"); + Assert.AreEqual(NmeaParser.Nmea.Gps.Garmin.Pgrmz.PositionFixType.Fix3D, rmz.FixType, "FixDimension"); + } - [TestMethod] - public void TestGprte() - { - string input = "$GPRTE,2,1,c,0,W3IWI,DRIVWY,32CEDR,32-29,32BKLD,32-I95,32-US1,BW-32,BW-198*69"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(Rte)); - Rte rte = (Rte)msg; + [TestMethod] + public void TestGprte() + { + string input = "$GPRTE,2,1,c,0,W3IWI,DRIVWY,32CEDR,32-29,32BKLD,32-I95,32-US1,BW-32,BW-198*69"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(Rte)); + Rte rte = (Rte)msg; Assert.IsFalse(((IMultiSentenceMessage)rte).IsComplete); Assert.AreEqual(Rte.WaypointListType.CompleteWaypointsList, rte.ListType); - Assert.AreEqual("0", rte.RouteId); - Assert.AreEqual("0", rte.RouteId); - Assert.AreEqual(9, rte.Waypoints.Count); - Assert.AreEqual("W3IWI", rte.Waypoints[0]); - Assert.AreEqual("32BKLD", rte.Waypoints[4]); - Assert.AreEqual("BW-198", rte.Waypoints[8]); - } + Assert.AreEqual("0", rte.RouteId); + Assert.AreEqual("0", rte.RouteId); + Assert.AreEqual(9, rte.Waypoints.Count); + Assert.AreEqual("W3IWI", rte.Waypoints[0]); + Assert.AreEqual("32BKLD", rte.Waypoints[4]); + Assert.AreEqual("BW-198", rte.Waypoints[8]); + } - [TestMethod] - public void TestGpgst() - { - string input = "$GPGST,172814.0,0.006,0.023,0.020,273.6,0.023,0.020,0.031*6A"; - var msg = NmeaMessage.Parse(input); - Assert.IsInstanceOfType(msg, typeof(Gst)); - Gst gst = (Gst)msg; - Assert.AreEqual(new TimeSpan(17, 28, 14), gst.FixTime); - Assert.AreEqual(0.006, gst.Rms); - Assert.AreEqual(0.023, gst.SemiMajorError); - Assert.AreEqual(0.02, gst.SemiMinorError); - Assert.AreEqual(273.6, gst.ErrorOrientation); - Assert.AreEqual(0.023, gst.SigmaLatitudeError); - Assert.AreEqual(0.020, gst.SigmaLongitudeError); - Assert.AreEqual(0.031, gst.SigmaHeightError); - } + [TestMethod] + public void TestGpgst() + { + string input = "$GPGST,172814.0,0.006,0.023,0.020,273.6,0.023,0.020,0.031*6A"; + var msg = NmeaMessage.Parse(input); + Assert.IsInstanceOfType(msg, typeof(Gst)); + Gst gst = (Gst)msg; + Assert.AreEqual(new TimeSpan(17, 28, 14), gst.FixTime); + Assert.AreEqual(0.006, gst.Rms); + Assert.AreEqual(0.023, gst.SemiMajorError); + Assert.AreEqual(0.02, gst.SemiMinorError); + Assert.AreEqual(273.6, gst.ErrorOrientation); + Assert.AreEqual(0.023, gst.SigmaLatitudeError); + Assert.AreEqual(0.020, gst.SigmaLongitudeError); + Assert.AreEqual(0.031, gst.SigmaHeightError); + } [TestMethod] public void TestGngst()