Fix whitespace inconsistencies

This commit is contained in:
Morten Nielsen 2020-01-15 23:49:06 -08:00
parent c335ba2f20
commit ad4faa6f4b
23 changed files with 1124 additions and 1124 deletions

View file

@ -22,11 +22,11 @@ using Android.Bluetooth;
namespace NmeaParser
{
/// <summary>
/// A Bluetooth NMEA device
/// </summary>
public class BluetoothDevice : NmeaDevice
{
/// <summary>
/// A Bluetooth NMEA device
/// </summary>
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
}
}
/// <summary>
/// Initializes a new instance of the <see cref="BluetoothDevice"/> class.
/// </summary>
/// <param name="device">The Android Bluetooth Device.</param>
public BluetoothDevice(Android.Bluetooth.BluetoothDevice device)
{
/// <summary>
/// Initializes a new instance of the <see cref="BluetoothDevice"/> class.
/// </summary>
/// <param name="device">The Android Bluetooth Device.</param>
public BluetoothDevice(Android.Bluetooth.BluetoothDevice device)
{
m_device = device ?? throw new ArgumentNullException(nameof(device));
}
}
/// <summary>
/// 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<Stream>(socket.InputStream);
}
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
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);
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
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);
}
/// <inheritdoc />
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

View file

@ -28,12 +28,12 @@ using Windows.Networking.Proximity;
namespace NmeaParser
{
/// <summary>
/// A Bluetooth NMEA device
/// </summary>
public class BluetoothDevice : NmeaDevice
{
private Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService? m_deviceService;
/// <summary>
/// A Bluetooth NMEA device
/// </summary>
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
/// <param name="service">The RF Comm Device service.</param>
/// <param name="disposeService">Whether this devicee should also dispose the RfcommDeviceService provided when this device disposes.</param>
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
/// </summary>
/// <returns></returns>
protected override async Task<System.IO.Stream> 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
/// <param name="stream">The stream.</param>
/// <returns></returns>
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);
}
/// <inheritdoc />
@ -174,6 +174,6 @@ namespace NmeaParser
m_semaphoreSlim.Release();
}
}
}
}
}
#endif

View file

@ -21,241 +21,241 @@ using System.Threading.Tasks;
namespace NmeaParser
{
/// <summary>
/// An abstract generic NMEA device that reads a stream at a decreased pace,
/// mostly used to emulate NMEA input from files and strings.
/// </summary>
public abstract class BufferedStreamDevice : NmeaDevice
{
private BufferedStream? m_stream;
private readonly int m_readSpeed;
/// <summary>
/// An abstract generic NMEA device that reads a stream at a decreased pace,
/// mostly used to emulate NMEA input from files and strings.
/// </summary>
public abstract class BufferedStreamDevice : NmeaDevice
{
private BufferedStream? m_stream;
private readonly int m_readSpeed;
/// <summary>
/// Initializes a new instance of the <see cref="BufferedStreamDevice"/> class.
/// </summary>
protected BufferedStreamDevice() : this(1000)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferedStreamDevice"/> class.
/// </summary>
/// <param name="readSpeed">The time to wait between each group of lines being read in milliseconds</param>
protected BufferedStreamDevice(int readSpeed)
{
m_readSpeed = readSpeed;
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferedStreamDevice"/> class.
/// </summary>
protected BufferedStreamDevice() : this(1000)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferedStreamDevice"/> class.
/// </summary>
/// <param name="readSpeed">The time to wait between each group of lines being read in milliseconds</param>
protected BufferedStreamDevice(int readSpeed)
{
m_readSpeed = readSpeed;
}
/// <summary>
/// Gets the stream to perform buffer reads on.
/// </summary>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected abstract Task<System.IO.Stream> GetStreamAsync();
/// <summary>
/// Gets the stream to perform buffer reads on.
/// </summary>
/// <returns></returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
protected abstract Task<System.IO.Stream> GetStreamAsync();
/// <summary>
/// Opens the stream asynchronous.
/// </summary>
/// <returns></returns>
protected sealed async override Task<System.IO.Stream> OpenStreamAsync()
{
var stream = await GetStreamAsync();
StreamReader sr = new StreamReader(stream);
m_stream = new BufferedStream(sr, m_readSpeed);
return m_stream;
}
/// <summary>
/// Opens the stream asynchronous.
/// </summary>
/// <returns></returns>
protected sealed async override Task<System.IO.Stream> OpenStreamAsync()
{
var stream = await GetStreamAsync();
StreamReader sr = new StreamReader(stream);
m_stream = new BufferedStream(sr, m_readSpeed);
return m_stream;
}
/// <summary>
/// Closes the stream asynchronous.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
m_stream?.Dispose();
return Task.FromResult(true);
}
/// <summary>
/// Closes the stream asynchronous.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
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;
/// <summary>
/// Initializes a new instance of the <see cref="BufferedStream"/> class.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="readSpeed">The read speed in milliseconds.</param>
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';
}
/// <summary>
/// Gets a value indicating whether this instance can read.
/// </summary>
/// <value>
/// <c>true</c> if this instance can read; otherwise, <c>false</c>.
/// </value>
public override bool CanRead { get { return true; } }
/// <summary>
/// Gets a value indicating whether this instance can seek.
/// </summary>
/// <value>
/// <c>true</c> if this instance can seek; otherwise, <c>false</c>.
/// </value>
public override bool CanSeek { get { return false; } }
/// <summary>
/// Gets a value indicating whether this instance can write.
/// </summary>
/// <value>
/// <c>true</c> if this instance can write; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite { get { return false; } }
/// <summary>
/// Flushes this instance.
/// </summary>
public override void Flush() { }
/// <summary>
/// Gets the length.
/// </summary>
/// <value>
/// The length.
/// </value>
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;
/// <summary>
/// Initializes a new instance of the <see cref="BufferedStream"/> class.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="readSpeed">The read speed in milliseconds.</param>
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';
}
/// <summary>
/// Gets a value indicating whether this instance can read.
/// </summary>
/// <value>
/// <c>true</c> if this instance can read; otherwise, <c>false</c>.
/// </value>
public override bool CanRead { get { return true; } }
/// <summary>
/// Gets a value indicating whether this instance can seek.
/// </summary>
/// <value>
/// <c>true</c> if this instance can seek; otherwise, <c>false</c>.
/// </value>
public override bool CanSeek { get { return false; } }
/// <summary>
/// Gets a value indicating whether this instance can write.
/// </summary>
/// <value>
/// <c>true</c> if this instance can write; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite { get { return false; } }
/// <summary>
/// Flushes this instance.
/// </summary>
public override void Flush() { }
/// <summary>
/// Gets the length.
/// </summary>
/// <value>
/// The length.
/// </value>
public override long Length { get { return m_sr.BaseStream.Length; } }
/// <summary>
/// Gets or sets the position.
/// </summary>
/// <value>
/// The position.
/// </value>
/// <exception cref="System.NotSupportedException"></exception>
public override long Position
{
get { return m_sr.BaseStream.Position; }
set
{
throw new NotSupportedException();
}
}
/// <summary>
/// Reads the specified buffer.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="count">The count.</param>
/// <returns></returns>
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;
}
}
}
/// <summary>
/// Gets or sets the position.
/// </summary>
/// <value>
/// The position.
/// </value>
/// <exception cref="System.NotSupportedException"></exception>
public override long Position
{
get { return m_sr.BaseStream.Position; }
set
{
throw new NotSupportedException();
}
}
/// <summary>
/// Reads the specified buffer.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="count">The count.</param>
/// <returns></returns>
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;
}
}
}
/// <summary>
/// Seeks the specified offset.
/// </summary>
/// <param name="offset">The offset.</param>
/// <param name="origin">The origin.</param>
/// <returns></returns>
/// <exception cref="System.NotSupportedException"></exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
/// <summary>
/// Seeks the specified offset.
/// </summary>
/// <param name="offset">The offset.</param>
/// <param name="origin">The origin.</param>
/// <returns></returns>
/// <exception cref="System.NotSupportedException"></exception>
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
/// <summary>
/// Sets the length.
/// </summary>
/// <param name="value">The value.</param>
/// <exception cref="System.NotSupportedException"></exception>
public override void SetLength(long value)
{
throw new NotSupportedException();
}
/// <summary>
/// Sets the length.
/// </summary>
/// <param name="value">The value.</param>
/// <exception cref="System.NotSupportedException"></exception>
public override void SetLength(long value)
{
throw new NotSupportedException();
}
/// <summary>
/// Writes the specified buffer to the device.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="count">The count.</param>
/// <exception cref="System.NotSupportedException"></exception>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
/// <summary>
/// Writes the specified buffer to the device.
/// </summary>
/// <param name="buffer">The buffer.</param>
/// <param name="offset">The offset.</param>
/// <param name="count">The count.</param>
/// <exception cref="System.NotSupportedException"></exception>
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
m_sr.Dispose();
m_timer.Dispose();
}
}
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
m_sr.Dispose();
m_timer.Dispose();
}
}
}
}

View file

@ -18,25 +18,25 @@ using System.Text;
namespace NmeaParser
{
/// <summary>
/// Interface used for NMEA messages that span multiple sentences
/// </summary>
public interface IMultiSentenceMessage
{
/// <summary>
/// Attempts to append one message to an existing one
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="messageType"></param>
/// <param name="values"></param>
/// <returns><c>True</c> is the message was successfully appended, <c>False</c> is the message couldn't be appended.</returns>
bool TryAppend(string messageType, string[] values);
/// <summary>
/// Interface used for NMEA messages that span multiple sentences
/// </summary>
public interface IMultiSentenceMessage
{
/// <summary>
/// Attempts to append one message to an existing one
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="messageType"></param>
/// <param name="values"></param>
/// <returns><c>True</c> is the message was successfully appended, <c>False</c> is the message couldn't be appended.</returns>
bool TryAppend(string messageType, string[] values);
/// <summary>
/// Gets a value indicating whether this message is fully loaded from all its sentences
/// </summary>
bool IsComplete { get; }
}
/// <summary>
/// Gets a value indicating whether this message is fully loaded from all its sentences
/// </summary>
bool IsComplete { get; }
}
}

View file

@ -20,9 +20,9 @@ namespace NmeaParser.Nmea.Garmin
/// Recommended Minimum
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pgrme")]
[NmeaMessageType("PGRME")]
public class Pgrme : NmeaMessage
{
[NmeaMessageType("PGRME")]
public class Pgrme : NmeaMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="Pgrme"/> class.
/// </summary>
@ -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];
}
/// <summary>
/// Estimated horizontal position error in meters (HPE)
/// </summary>
/// <remarks>Range: 0.0 to 999.9 meters</remarks>
public double HorizontalError{ get; }
/// <summary>
/// Estimated horizontal position error in meters (HPE)
/// </summary>
/// <remarks>Range: 0.0 to 999.9 meters</remarks>
public double HorizontalError{ get; }
/// <summary>
/// Horizontal Error unit ('M' for Meters)
/// </summary>
public string HorizontalErrorUnits{ get; }
/// <summary>
/// Horizontal Error unit ('M' for Meters)
/// </summary>
public string HorizontalErrorUnits{ get; }
/// <summary>
/// Estimated vertical position error in meters (VPE)
/// </summary>
/// <remarks>Range: 0.0 to 999.9 meters</remarks>
public double VerticalError{ get; }
/// <summary>
/// Estimated vertical position error in meters (VPE)
/// </summary>
/// <remarks>Range: 0.0 to 999.9 meters</remarks>
public double VerticalError{ get; }
/// <summary>
/// Vertical Error unit ('M' for Meters)
/// </summary>
public string VerticalErrorUnits{ get; }
/// <summary>
/// Vertical Error unit ('M' for Meters)
/// </summary>
public string VerticalErrorUnits{ get; }
/// <summary>
/// Overall spherical equivalent position error (EPE)
/// </summary>
/// <remarks>Range: 0.0 to 999.9 meters</remarks>
public double SphericalError{ get; }
/// <summary>
/// Overall spherical equivalent position error (EPE)
/// </summary>
/// <remarks>Range: 0.0 to 999.9 meters</remarks>
public double SphericalError{ get; }
/// <summary>
/// Spherical Error unit ('M' for Meters)
/// </summary>
public string SphericalErrorUnits{ get; }
}
/// <summary>
/// Spherical Error unit ('M' for Meters)
/// </summary>
public string SphericalErrorUnits{ get; }
}
}

View file

@ -23,97 +23,97 @@ namespace NmeaParser.Nmea
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gpgga")]
[NmeaMessageType("--GGA")]
public class Gga : NmeaMessage
{
{
/// <summary>
/// Initializes a new instance of the <see cref="Gga"/> class.
/// </summary>
/// <param name="type">The message type</param>
/// <param name="message">The NMEA message values.</param>
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;
}
/// <summary>
/// Time of day fix was taken
/// </summary>
public TimeSpan FixTime { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Time of day fix was taken
/// </summary>
public TimeSpan FixTime { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Fix Quality
/// </summary>
public Gga.FixQuality Quality { get; }
/// <summary>
/// Fix Quality
/// </summary>
public Gga.FixQuality Quality { get; }
/// <summary>
/// Number of satellites being tracked
/// </summary>
public int NumberOfSatellites { get; }
/// <summary>
/// Number of satellites being tracked
/// </summary>
public int NumberOfSatellites { get; }
/// <summary>
/// Horizontal Dilution of Precision
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hdop")]
public double Hdop { get; }
/// <summary>
/// Horizontal Dilution of Precision
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hdop")]
public double Hdop { get; }
/// <summary>
/// Altitude
/// </summary>
public double Altitude { get; }
/// <summary>
/// Altitude
/// </summary>
public double Altitude { get; }
/// <summary>
/// Altitude units ('M' for Meters)
/// </summary>
public string AltitudeUnits { get; }
/// <summary>
/// Height of geoid (mean sea level) above WGS84
/// </summary>
public double HeightOfGeoid { get; }
/// <summary>
/// Altitude units ('M' for Meters)
/// </summary>
public string AltitudeUnits { get; }
/// <summary>
/// Height of geoid (mean sea level) above WGS84
/// </summary>
public double HeightOfGeoid { get; }
/// <summary>
/// Altitude units ('M' for Meters)
/// </summary>
public string HeightOfGeoidUnits { get; }
/// <summary>
/// Altitude units ('M' for Meters)
/// </summary>
public string HeightOfGeoidUnits { get; }
/// <summary>
/// Time since last DGPS update
/// </summary>
public TimeSpan TimeSinceLastDgpsUpdate { get; }
/// <summary>
/// Time since last DGPS update
/// </summary>
public TimeSpan TimeSinceLastDgpsUpdate { get; }
/// <summary>
/// DGPS Station ID Number
/// </summary>
public int DgpsStationId { get; }
/// <summary>
/// DGPS Station ID Number
/// </summary>
public int DgpsStationId { get; }
/// <summary>
/// Fix quality

View file

@ -22,7 +22,7 @@ namespace NmeaParser.Nmea
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gll")]
[NmeaMessageType("--GLL")]
public class Gll : NmeaMessage
{
{
/// <summary>
/// Initializes a new instance of the <see cref="Gll"/> class.
/// </summary>
@ -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
}
}
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Time since last DGPS update
/// </summary>
public TimeSpan FixTime { get; }
/// <summary>
/// Time since last DGPS update
/// </summary>
public TimeSpan FixTime { get; }
/// <summary>
/// Gets a value indicating whether data is active.
/// </summary>
/// <value>
/// <c>true</c> if data is active; otherwise, <c>false</c>.
/// </value>
public bool DataActive { get; }
/// <summary>
/// Gets a value indicating whether data is active.
/// </summary>
/// <value>
/// <c>true</c> if data is active; otherwise, <c>false</c>.
/// </value>
public bool DataActive { get; }
/// <summary>
/// Positioning system Mode Indicator
@ -113,5 +113,5 @@ namespace NmeaParser.Nmea
/// </summary>
DataNotValid
}
}
}
}

View file

@ -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
}
/// <summary>
/// Initializes a new instance of the <see cref="Gns"/> class.
/// </summary>
/// Initializes a new instance of the <see cref="Gns"/> class.
/// </summary>
/// <param name="type">The message type</param>
/// <param name="message">The NMEA message values.</param>
public Gns(string type, string[] message) : base(type, message)
/// <param name="message">The NMEA message values.</param>
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
}
}
/// <summary>
/// Time of day fix was taken
/// </summary>
public TimeSpan FixTime { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Time of day fix was taken
/// </summary>
public TimeSpan FixTime { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// 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.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hdop")]
public double Hdop { get; }
public double Hdop { get; }
/// <summary>
/// Orthometric height in meters (MSL reference)
@ -222,11 +222,11 @@ namespace NmeaParser.Nmea
/// <summary>
/// Age of differential data - <see cref="TimeSpan.MaxValue"/> if talker ID is GN, additional GNS messages follow with GP and/or GL Age of differential data
/// </summary>
public TimeSpan TimeSinceLastDgpsUpdate { get; }
/// </summary>
public TimeSpan TimeSinceLastDgpsUpdate { get; }
/// <summary>
/// eference station ID1, range 0000-4095 - Null if talker ID is GN, additional GNS messages follow with GP and/or GL Reference station ID
/// <summary>
/// eference station ID1, range 0000-4095 - Null if talker ID is GN, additional GNS messages follow with GP and/or GL Reference station ID
/// </summary>
public string? DgpsStationId { get; }
@ -234,5 +234,5 @@ namespace NmeaParser.Nmea
/// Navigational status
/// </summary>
public NavigationalStatus Status { get; }
}
}
}

View file

@ -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];
}
/// <summary>
/// Gets the horizontal vector.
/// </summary>
public string HorizontalVector { get; }
/// <summary>
/// Gets the horizontal vector.
/// </summary>
public string HorizontalVector { get; }
/// <summary>
/// Gets the horizontal distance.
/// </summary>
public double HorizontalDistance { get; }
/// <summary>
/// Gets the horizontal distance.
/// </summary>
public double HorizontalDistance { get; }
/// <summary>
/// Gets the units of the <see cref="HorizontalDistance"/> value.
/// </summary>
public char HorizontalDistanceUnits { get; }
/// <summary>
/// Gets the units of the <see cref="HorizontalDistance"/> value.
/// </summary>
public char HorizontalDistanceUnits { get; }
/// <summary>
/// Gets the horizontal angle.
/// </summary>
public double HorizontalAngle { get; }
/// <summary>
/// Gets the horizontal angle.
/// </summary>
public double HorizontalAngle { get; }
/// <summary>
/// Gets the units of the <see cref="HorizontalAngle"/> value.
/// </summary>
public char HorizontalAngleUnits { get; }
/// <summary>
/// Gets the units of the <see cref="HorizontalAngle"/> value.
/// </summary>
public char HorizontalAngleUnits { get; }
/// <summary>
/// Gets the vertical angle.
/// </summary>
public double VerticalAngle { get; }
/// <summary>
/// Gets the vertical angle.
/// </summary>
public double VerticalAngle { get; }
/// <summary>
/// Gets the units of the <see cref="VerticalAngle"/> value.
/// </summary>
public char VerticalAngleUnits { get; }
/// <summary>
/// Gets the units of the <see cref="VerticalAngle"/> value.
/// </summary>
public char VerticalAngleUnits { get; }
/// <summary>
/// Gets the slope distance.
/// </summary>
public double SlopeDistance { get; }
/// <summary>
/// Gets the slope distance.
/// </summary>
public double SlopeDistance { get; }
/// <summary>
/// Gets the units of the <see cref="SlopeDistance"/> value.
/// </summary>
public char SlopeDistanceUnits { get; }
}
/// <summary>
/// Gets the units of the <see cref="SlopeDistance"/> value.
/// </summary>
public char SlopeDistanceUnits { get; }
}
}

View file

@ -18,8 +18,8 @@ namespace NmeaParser.Nmea.LaserRange.LaserTech
/// Laser Range
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pltit")]
[NmeaMessageType("PLTIT")]
public class Pltit : LaserRangeMessage
[NmeaMessageType("PLTIT")]
public class Pltit : LaserRangeMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="Pltit"/> class.

View file

@ -18,8 +18,8 @@ namespace NmeaParser.Nmea.LaserRange.Trimble
/// Burden finder
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ptnla")]
[NmeaMessageType("PTNLA")]
public class Ptnla : LaserRangeMessage
[NmeaMessageType("PTNLA")]
public class Ptnla : LaserRangeMessage
{
/// <summary>
/// Initializes a new instance of the <see cref="Ptnla"/> class.

View file

@ -24,21 +24,21 @@ namespace NmeaParser.Nmea
/// Nmea message attribute type used on concrete <see cref="NmeaMessage"/> implementations.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class NmeaMessageTypeAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="NmeaMessageTypeAttribute"/> class.
/// </summary>
/// <param name="nmeaType">The type.</param>
public NmeaMessageTypeAttribute(string nmeaType)
{
NmeaType = nmeaType;
}
/// <summary>
/// Gets or sets the NMEA message type.
/// </summary>
public string NmeaType { get; private set; }
}
public sealed class NmeaMessageTypeAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="NmeaMessageTypeAttribute"/> class.
/// </summary>
/// <param name="nmeaType">The type.</param>
public NmeaMessageTypeAttribute(string nmeaType)
{
NmeaType = nmeaType;
}
/// <summary>
/// Gets or sets the NMEA message type.
/// </summary>
public string NmeaType { get; private set; }
}
/// <summary>
/// NMEA Message base class.
@ -169,14 +169,14 @@ namespace NmeaParser.Nmea
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
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);
}
/// <summary>
/// Gets the checksum value of the message.
/// </summary>
public byte Checksum => GetChecksum(MessageType, MessageParts);
/// <summary>
/// Gets the checksum value of the message.
/// </summary>
public byte Checksum => GetChecksum(MessageType, MessageParts);
internal static byte GetChecksum(string messageType, IReadOnlyList<string> 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;
}
}
}

View file

@ -23,7 +23,7 @@ namespace NmeaParser.Nmea
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gprmc")]
[NmeaMessageType("--RMC")]
public class Rmc : NmeaMessage
{
{
/// <summary>
/// Initializes a new instance of the <see cref="Rmc"/> class.
/// </summary>
@ -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;
}
/// <summary>
/// Fix Time
/// </summary>
public DateTime FixTime { get; }
/// <summary>
/// Fix Time
/// </summary>
public DateTime FixTime { get; }
/// <summary>
/// Gets a value whether the device is active
/// </summary>
public bool Active { get; }
/// <summary>
/// Gets a value whether the device is active
/// </summary>
public bool Active { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Latitude
/// </summary>
public double Latitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Longitude
/// </summary>
public double Longitude { get; }
/// <summary>
/// Speed over the ground in knots
/// </summary>
public double Speed { get; }
/// <summary>
/// Speed over the ground in knots
/// </summary>
public double Speed { get; }
/// <summary>
/// Track angle in degrees True
/// </summary>
public double Course { get; }
/// <summary>
/// Track angle in degrees True
/// </summary>
public double Course { get; }
/// <summary>
/// Magnetic Variation
/// </summary>
public double MagneticVariation { get; }
}
/// <summary>
/// Magnetic Variation
/// </summary>
public double MagneticVariation { get; }
}
}

View file

@ -20,12 +20,12 @@ namespace NmeaParser.Nmea
/// Represents an unknown message type
/// </summary>
public class UnknownMessage : NmeaMessage
{
{
internal UnknownMessage(string type, string[] messageParts) : base(type, messageParts) { }
/// <summary>
/// Gets the nmea value array.
/// </summary>
public IReadOnlyList<string> Values { get { return base.MessageParts; } }
}
/// <summary>
/// Gets the nmea value array.
/// </summary>
public IReadOnlyList<string> Values { get { return base.MessageParts; } }
}
}

View file

@ -17,12 +17,12 @@ using System;
namespace NmeaParser.Nmea
{
/// <summary>
/// Course over ground and ground speed
/// </summary>
/// Course over ground and ground speed
/// </summary>
/// <remarks>
/// The actual course and speed relative to the ground.
/// </remarks>
[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
{

View file

@ -22,41 +22,41 @@ using System.Threading;
namespace NmeaParser
{
/// <summary>
/// A generic abstract NMEA device
/// </summary>
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;
/// <summary>
/// A generic abstract NMEA device
/// </summary>
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;
/// <summary>
/// Initializes a new instance of the <see cref="NmeaDevice"/> class.
/// </summary>
protected NmeaDevice()
{
}
{
}
/// <summary>
/// Opens the device connection.
/// </summary>
/// <returns></returns>
public async Task OpenAsync()
{
lock (m_lockObject)
{
if (IsOpen || m_isOpening) return;
/// Opens the device connection.
/// </summary>
/// <returns></returns>
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);
}
});
}
/// <summary>
/// Performs a read operation of the stream
@ -116,84 +116,84 @@ namespace NmeaParser
/// <returns></returns>
protected abstract Task<Stream> OpenStreamAsync();
/// <summary>
/// Closes the device.
/// </summary>
/// <returns></returns>
public async Task CloseAsync()
{
if (m_cts != null)
{
if (m_cts != null)
m_cts.Cancel();
m_cts = null;
}
/// <summary>
/// Closes the device.
/// </summary>
/// <returns></returns>
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;
}
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected abstract Task CloseStreamAsync(Stream stream);
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected abstract Task CloseStreamAsync(Stream stream);
private void OnData(byte[] data)
{
var nmea = System.Text.Encoding.UTF8.GetString(data, 0, data.Length);
List<string> lines = new List<string>();
lock (m_lockObject)
{
m_message += nmea;
private void OnData(byte[] data)
{
var nmea = System.Text.Encoding.UTF8.GetString(data, 0, data.Length);
List<string> lines = new List<string>();
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<string, Dictionary<int, Nmea.NmeaMessage>> MultiPartMessageCache = new Dictionary<string,Dictionary<int,Nmea.NmeaMessage>>();
//private readonly Dictionary<string, Dictionary<int, Nmea.NmeaMessage>> MultiPartMessageCache = new Dictionary<string,Dictionary<int,Nmea.NmeaMessage>>();
/// <summary>
/// Occurs when an NMEA message is received.
/// </summary>
public event EventHandler<NmeaMessageReceivedEventArgs>? MessageReceived;
/// <summary>
/// Occurs when an NMEA message is received.
/// </summary>
public event EventHandler<NmeaMessageReceivedEventArgs>? MessageReceived;
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
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;
}
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
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;
}
}
/// <summary>
/// Gets a value indicating whether this device is open.
/// </summary>
/// <value>
/// <c>true</c> if this instance is open; otherwise, <c>false</c>.
/// </value>
public bool IsOpen { get; private set; }
/// <summary>
/// Gets a value indicating whether this device is open.
/// </summary>
/// <value>
/// <c>true</c> if this instance is open; otherwise, <c>false</c>.
/// </value>
public bool IsOpen { get; private set; }
/// <summary>
/// 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 <see cref="CanWrite"/> property before calling this method.
/// </summary>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="length">The number of bytes to write.</param>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="length">The number of bytes to write.</param>
/// <returns>Task</returns>
/// <seealso cref="CanWrite"/>
public virtual Task WriteAsync(byte[] buffer, int offset, int length)
{
throw new NotSupportedException();
}
}
}
/// <summary>
/// Event argument for the <see cref="NmeaDevice.MessageReceived" />
/// </summary>
public sealed class NmeaMessageReceivedEventArgs : EventArgs
{
internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message)
/// <summary>
/// Event argument for the <see cref="NmeaDevice.MessageReceived" />
/// </summary>
public sealed class NmeaMessageReceivedEventArgs : EventArgs
{
internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message)
{
Message = message;
}
Message = message;
}
/// <summary>
/// Gets the nmea message.
/// </summary>
/// <value>
/// The nmea message.
/// </value>
public Nmea.NmeaMessage Message { get; }
}
/// <summary>
/// Gets the nmea message.
/// </summary>
/// <value>
/// The nmea message.
/// </value>
public Nmea.NmeaMessage Message { get; }
}
}

View file

@ -21,13 +21,13 @@ using System.Threading.Tasks;
namespace NmeaParser
{
/// <summary>
/// A file-based NMEA device reading from a NMEA log file.
/// </summary>
public class NmeaFileDevice : BufferedStreamDevice
{
/// <summary>
/// A file-based NMEA device reading from a NMEA log file.
/// </summary>
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 <see cref="NmeaFileDevice"/> class.
/// </summary>
/// <param name="fileName"></param>
public NmeaFileDevice(string fileName) : this(fileName, 1000)
public NmeaFileDevice(string fileName) : this(fileName, 1000)
{
}
@ -46,18 +46,18 @@ namespace NmeaParser
/// </summary>
/// <param name="storageFile"></param>
public NmeaFileDevice(Windows.Storage.IStorageFile storageFile) : this(storageFile, 1000)
{
}
{
}
#endif
/// <summary>
/// Initializes a new instance of the <see cref="NmeaFileDevice"/> class.
/// </summary>
/// <param name="fileName"></param>
/// <param name="readSpeed">The time to wait between each group of lines being read in milliseconds</param>
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
/// <summary>
@ -73,24 +73,24 @@ namespace NmeaParser
}
#endif
/// <summary>
/// Gets the name of the nmea file this device is using.
/// </summary>
public string FileName
{
get
/// <summary>
/// Gets the name of the nmea file this device is using.
/// </summary>
public string FileName
{
get
{
return m_filename;
}
}
}
/// <summary>
/// Gets the stream to perform buffer reads on.
/// </summary>
/// <returns></returns>
[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<Stream> GetStreamAsync()
{
{
#if NETFX_CORE
if (m_storageFile != null)
@ -102,5 +102,5 @@ namespace NmeaParser
return Task.FromResult<Stream>(System.IO.File.OpenRead(m_filename));
#endif
}
}
}
}

View file

@ -27,7 +27,7 @@
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="NmeaParser.UWP">
<!-- add directives for your library here -->
<!-- add directives for your library here -->
</Library>
</Directives>

View file

@ -22,22 +22,22 @@ using System.Threading.Tasks;
namespace NmeaParser
{
/// <summary>
/// A Serial Port NMEA device
/// </summary>
public class SerialPortDevice : NmeaDevice
{
/// <summary>
/// A Serial Port NMEA device
/// </summary>
public class SerialPortDevice : NmeaDevice
{
/// <summary>
/// Initializes a new instance of the <see cref="SerialPortDevice" /> class.
/// </summary>
/// <param name="port">The serial port.</param>
/// <exception cref="System.ArgumentNullException">port</exception>
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;
}
/// <summary>
/// Gets the active serial port.
@ -49,35 +49,35 @@ namespace NmeaParser
/// </summary>
/// <returns></returns>
protected override Task<System.IO.Stream> OpenStreamAsync()
{
{
if (!Port.IsOpen)
Port.Open();
return Task.FromResult<System.IO.Stream>(Port.BaseStream);
}
return Task.FromResult<System.IO.Stream>(Port.BaseStream);
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
if (Port.IsOpen)
Port.Close();
return Task.FromResult<object?>(null);
}
return Task.FromResult<object?>(null);
}
/// <summary>
/// Writes data to the serial port (useful for RTCM/dGPS scenarios)
/// </summary>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="count">The number of bytes to write.</param>
/// <summary>
/// Writes data to the serial port (useful for RTCM/dGPS scenarios)
/// </summary>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="count">The number of bytes to write.</param>
[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);
}
/// <inheritdoc />

View file

@ -24,67 +24,67 @@ using System.Runtime.InteropServices.WindowsRuntime;
namespace NmeaParser
{
/// <summary>
/// A Serial Port NMEA device
/// </summary>
public class SerialPortDevice : NmeaDevice
{
private SerialDevice m_port;
/// <summary>
/// A Serial Port NMEA device
/// </summary>
public class SerialPortDevice : NmeaDevice
{
private SerialDevice m_port;
/// <summary>
/// Initializes a new instance of the <see cref="SerialPortDevice" /> class.
/// </summary>
/// <param name="device">The serial port.</param>
/// <exception cref="System.ArgumentNullException">port</exception>
public SerialPortDevice(SerialDevice device)
{
if (device == null)
throw new ArgumentNullException("device");
m_port = device;
}
/// <summary>
/// Initializes a new instance of the <see cref="SerialPortDevice" /> class.
/// </summary>
/// <param name="device">The serial port.</param>
/// <exception cref="System.ArgumentNullException">port</exception>
public SerialPortDevice(SerialDevice device)
{
if (device == null)
throw new ArgumentNullException("device");
m_port = device;
}
/// <summary>
/// Gets the active serial port.
/// </summary>
public SerialDevice SerialDevice
{
get
{
return m_port;
}
}
/// <summary>
/// Gets the active serial port.
/// </summary>
public SerialDevice SerialDevice
{
get
{
return m_port;
}
}
/// <summary>
/// Creates the stream the NmeaDevice is working on top off.
/// </summary>
/// <returns></returns>
protected override Task<System.IO.Stream> OpenStreamAsync()
{
return Task.FromResult<System.IO.Stream>(m_port.InputStream.AsStreamForRead(0));
}
/// <summary>
/// Creates the stream the NmeaDevice is working on top off.
/// </summary>
/// <returns></returns>
protected override Task<System.IO.Stream> OpenStreamAsync()
{
return Task.FromResult<System.IO.Stream>(m_port.InputStream.AsStreamForRead(0));
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
return Task.CompletedTask;
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
return Task.CompletedTask;
}
/// <summary>
/// Writes data to the serial port (useful for RTCM/dGPS scenarios)
/// </summary>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="count">The number of bytes to write.</param>
/// <summary>
/// Writes data to the serial port (useful for RTCM/dGPS scenarios)
/// </summary>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="count">The number of bytes to write.</param>
[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);
}
/// <inheritdoc />
public override bool CanWrite => true;

View file

@ -20,51 +20,51 @@ using System.Threading.Tasks;
namespace NmeaParser
{
/// <summary>
/// Generic stream device
/// </summary>
/// <summary>
/// Generic stream device
/// </summary>
public class StreamDevice : NmeaDevice
{
private Stream m_stream;
private Stream m_stream;
/// <summary>
/// Initializes a new instance of the <see cref="StreamDevice"/> class.
/// </summary>
/// <param name="stream">The stream.</param>
public StreamDevice(Stream stream) : base()
{
m_stream = stream ?? throw new ArgumentNullException(nameof(stream));
}
/// <summary>
/// Initializes a new instance of the <see cref="StreamDevice"/> class.
/// </summary>
/// <param name="stream">The stream.</param>
public StreamDevice(Stream stream) : base()
{
m_stream = stream ?? throw new ArgumentNullException(nameof(stream));
}
/// <summary>
/// Opens the stream asynchronous.
/// </summary>
/// <returns></returns>
protected override Task<Stream> OpenStreamAsync()
{
return Task.FromResult(m_stream);
}
/// <summary>
/// Opens the stream asynchronous.
/// </summary>
/// <returns></returns>
protected override Task<Stream> OpenStreamAsync()
{
return Task.FromResult(m_stream);
}
/// <summary>
/// Closes the stream asynchronous.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
return Task.FromResult(true); //do nothing
}
/// <summary>
/// Closes the stream asynchronous.
/// </summary>
/// <param name="stream">The stream.</param>
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
return Task.FromResult(true); //do nothing
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (m_stream != null)
m_stream.Dispose();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (m_stream != null)
m_stream.Dispose();
}
/// <inheritdoc />
public override bool CanWrite => m_stream?.CanWrite == true;

View file

@ -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<bool> tcs = new TaskCompletionSource<bool>();
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<bool> tcs = new TaskCompletionSource<bool>();
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<bool> tcs = new TaskCompletionSource<bool>();
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<bool> tcs = new TaskCompletionSource<bool>();
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<bool> tcs = new TaskCompletionSource<bool>();
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");
}
}
}

View file

@ -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()