mirror of
https://github.com/dotMorten/NmeaParser.git
synced 2025-12-06 07:12:04 +01:00
Merge pull request #62 from dotMorten/MultiSentence
Multi sentence rewrite
This commit is contained in:
commit
ca02c614b9
9
.github/workflows/CIBuild.yml
vendored
9
.github/workflows/CIBuild.yml
vendored
|
|
@ -18,8 +18,13 @@ jobs:
|
|||
- name: Clone NmeaParser
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Setup Visual Studio Command Prompt
|
||||
uses: warrenbuckley/Setup-MSBuild@v1
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||
msbuild /restore /t:Build src/NmeaParser.sln /p:Configuration=Release /p:JavaSdkDirectory="$(JAVA_HOME_8_X64)"
|
||||
msbuild /restore /t:Build src/NmeaParser.sln /p:Configuration=Release
|
||||
|
||||
- name: Tests
|
||||
run: |
|
||||
dotnet test src/UnitTests/NmeaParser.Tests.NET/bin/Release/netcoreapp3.1/NmeaParser.Tests.Net.dll -v normal
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 16
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.0.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NmeaParser", "NmeaParser", "{1701F3BA-A09C-4706-A612-24FD9340FC18}"
|
||||
|
|
@ -34,18 +34,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NmeaParser.Tests.UWP", "Uni
|
|||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "NmeaParser.Tests", "UnitTests\NmeaParser.Tests\NmeaParser.Tests.shproj", "{979AE182-EB59-4181-9D45-3FD6E4817F11}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NmeaParser.Tests.NET45", "UnitTests\NmeaParser.Tests.NET45\NmeaParser.Tests.NET45.csproj", "{170EE734-37F0-425F-822B-B865348ECEC6}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{1ADC3666-1DDB-48C4-9811-1E58B6D09A7C} = {1ADC3666-1DDB-48C4-9811-1E58B6D09A7C}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{456E7573-3324-43CB-8BA0-8D9C300EEB50}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleApp.Droid", "SampleApp.Droid\SampleApp.Droid.csproj", "{48540D33-4349-42D2-9D49-144A7049565A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NmeaParser.Tests.Net", "UnitTests\NmeaParser.Tests.NET\NmeaParser.Tests.Net.csproj", "{73EFB2EF-DE40-46C4-9685-745A9815C0D2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
UnitTests\NmeaParser.Tests\NmeaParser.Tests.projitems*{170ee734-37f0-425f-822b-b865348ecec6}*SharedItemsImports = 4
|
||||
UnitTests\NmeaParser.Tests\NmeaParser.Tests.projitems*{92cad93b-6c3b-45a0-a723-be046de50fec}*SharedItemsImports = 4
|
||||
UnitTests\NmeaParser.Tests\NmeaParser.Tests.projitems*{979ae182-eb59-4181-9d45-3fd6e4817f11}*SharedItemsImports = 13
|
||||
EndGlobalSection
|
||||
|
|
@ -138,22 +134,6 @@ Global
|
|||
{92CAD93B-6C3B-45A0-A723-BE046DE50FEC}.Release|x86.ActiveCfg = Release|x86
|
||||
{92CAD93B-6C3B-45A0-A723-BE046DE50FEC}.Release|x86.Build.0 = Release|x86
|
||||
{92CAD93B-6C3B-45A0-A723-BE046DE50FEC}.Release|x86.Deploy.0 = Release|x86
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{48540D33-4349-42D2-9D49-144A7049565A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{48540D33-4349-42D2-9D49-144A7049565A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{48540D33-4349-42D2-9D49-144A7049565A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
|
|
@ -178,6 +158,22 @@ Global
|
|||
{48540D33-4349-42D2-9D49-144A7049565A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{48540D33-4349-42D2-9D49-144A7049565A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{48540D33-4349-42D2-9D49-144A7049565A}.Release|x86.Deploy.0 = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -191,9 +187,9 @@ Global
|
|||
{1ADC3666-1DDB-48C4-9811-1E58B6D09A7C} = {1701F3BA-A09C-4706-A612-24FD9340FC18}
|
||||
{92CAD93B-6C3B-45A0-A723-BE046DE50FEC} = {28B8E327-C504-4E08-B2CE-09D1CBB8B904}
|
||||
{979AE182-EB59-4181-9D45-3FD6E4817F11} = {28B8E327-C504-4E08-B2CE-09D1CBB8B904}
|
||||
{170EE734-37F0-425F-822B-B865348ECEC6} = {28B8E327-C504-4E08-B2CE-09D1CBB8B904}
|
||||
{456E7573-3324-43CB-8BA0-8D9C300EEB50} = {A4B9D59A-C8C6-4199-A7F3-F3AF0C748281}
|
||||
{48540D33-4349-42D2-9D49-144A7049565A} = {456E7573-3324-43CB-8BA0-8D9C300EEB50}
|
||||
{73EFB2EF-DE40-46C4-9685-745A9815C0D2} = {28B8E327-C504-4E08-B2CE-09D1CBB8B904}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {03788B53-C0BF-485B-AA19-A9EAB0E9AF7B}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NmeaParser
|
||||
{
|
||||
interface IMultiPartMessage : System.Collections.IEnumerable
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of messages of this type in this cycle
|
||||
/// </summary>
|
||||
int TotalMessages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Message number
|
||||
/// </summary>
|
||||
int MessageNumber { get; }
|
||||
}
|
||||
interface IMultiPartMessage<T> : IMultiPartMessage, IEnumerable<T>
|
||||
{
|
||||
}
|
||||
}
|
||||
42
src/NmeaParser/IMultiSentenceMessage.cs
Normal file
42
src/NmeaParser/IMultiSentenceMessage.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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>
|
||||
/// Gets a value indicating whether this message is fully loaded from all its sentences
|
||||
/// </summary>
|
||||
bool IsComplete { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ namespace NmeaParser.Nmea
|
|||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gsv")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
|
||||
[NmeaMessageType("--GSV")]
|
||||
public class Gsv : NmeaMessage, IMultiPartMessage<SatelliteVehicle>
|
||||
public class Gsv : NmeaMultiSentenceMessage, IEnumerable<SatelliteVehicle>
|
||||
{
|
||||
private readonly List<SatelliteVehicle> svs = new List<SatelliteVehicle>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Gsv"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -35,41 +37,43 @@ namespace NmeaParser.Nmea
|
|||
{
|
||||
if (message == null || message.Length < 3)
|
||||
throw new ArgumentException("Invalid GSV", "message");
|
||||
}
|
||||
|
||||
TotalMessages = int.Parse(message[0], CultureInfo.InvariantCulture);
|
||||
MessageNumber = int.Parse(message[1], CultureInfo.InvariantCulture);
|
||||
SVsInView = int.Parse(message[2], CultureInfo.InvariantCulture);
|
||||
/// <inheritdoc />
|
||||
protected override int MessageCountIndex => 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override int MessageNumberIndex => 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ParseSentences(Talker talkerType, string[] message)
|
||||
{
|
||||
var satellites = int.Parse(message[2], CultureInfo.InvariantCulture);
|
||||
|
||||
if (SVsInView == -1)
|
||||
SVsInView = satellites;
|
||||
else if ( satellites != SVsInView)
|
||||
return false; // Messages do not match
|
||||
|
||||
List<SatelliteVehicle> svs = new List<SatelliteVehicle>();
|
||||
for (int i = 3; i < message.Length - 3; i += 4)
|
||||
{
|
||||
if (message[i].Length == 0)
|
||||
continue;
|
||||
else
|
||||
svs.Add(new SatelliteVehicle(message, i));
|
||||
svs.Add(new SatelliteVehicle(talkerType, message, i));
|
||||
}
|
||||
this.SVs = svs.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total number of messages of this type in this cycle
|
||||
/// </summary>
|
||||
public int TotalMessages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Message number
|
||||
/// </summary>
|
||||
public int MessageNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of SVs in view
|
||||
/// </summary>
|
||||
public int SVsInView { get; }
|
||||
public int SVsInView { get; private set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Satellite vehicles in this message part.
|
||||
/// </summary>
|
||||
public IReadOnlyList<SatelliteVehicle> SVs { get; }
|
||||
public IReadOnlyList<SatelliteVehicle> SVs => svs.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
|
|
@ -95,7 +99,7 @@ namespace NmeaParser.Nmea
|
|||
/// </summary>
|
||||
public sealed class SatelliteVehicle
|
||||
{
|
||||
internal SatelliteVehicle(string[] message, int startIndex)
|
||||
internal SatelliteVehicle(Talker talker, string[] message, int startIndex)
|
||||
{
|
||||
PrnNumber = int.Parse(message[startIndex], CultureInfo.InvariantCulture);
|
||||
if (double.TryParse(message[startIndex + 1], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out double e))
|
||||
|
|
@ -105,19 +109,29 @@ namespace NmeaParser.Nmea
|
|||
int snr = -1;
|
||||
if (int.TryParse(message[startIndex + 3], out snr))
|
||||
SignalToNoiseRatio = snr;
|
||||
TalkerId = talker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the talker ID for this vehicle
|
||||
/// </summary>
|
||||
public Talker TalkerId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// SV PRN number
|
||||
/// </summary>
|
||||
public int PrnNumber { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Elevation in degrees, 90 maximum
|
||||
/// </summary>
|
||||
public double Elevation { get; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// Azimuth, degrees from true north, 000 to 359
|
||||
/// </summary>
|
||||
public double Azimuth { get; } = double.NaN;
|
||||
|
||||
/// <summary>
|
||||
/// Signal-to-Noise ratio, 0-99 dB (-1 when not tracking)
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -87,15 +87,16 @@ namespace NmeaParser.Nmea
|
|||
/// Parses the specified NMEA message.
|
||||
/// </summary>
|
||||
/// <param name="message">The NMEA message string.</param>
|
||||
/// <param name="previousSentence">The previously received message (only used if parsing multi-sentence messages)</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// Invalid nmea message: Missing starting character '$'
|
||||
/// or checksum failure
|
||||
/// </exception>
|
||||
public static NmeaMessage Parse(string message)
|
||||
public static NmeaMessage Parse(string message, IMultiSentenceMessage? previousSentence = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
throw new ArgumentNullException("message");
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
|
||||
int checksum = -1;
|
||||
if (message[0] != '$')
|
||||
|
|
@ -120,6 +121,13 @@ namespace NmeaParser.Nmea
|
|||
string[] parts = message.Split(new char[] { ',' });
|
||||
string MessageType = parts[0].Substring(1);
|
||||
string[] MessageParts = parts.Skip(1).ToArray();
|
||||
if(previousSentence is NmeaMessage pmsg && pmsg.MessageType.Substring(2) == MessageType.Substring(2))
|
||||
{
|
||||
if (previousSentence.TryAppend(MessageType, MessageParts))
|
||||
{
|
||||
return pmsg;
|
||||
}
|
||||
}
|
||||
if (messageTypes.ContainsKey(MessageType))
|
||||
{
|
||||
return (NmeaMessage)messageTypes[MessageType].Invoke(new object[] { MessageType, MessageParts });
|
||||
|
|
@ -147,7 +155,7 @@ namespace NmeaParser.Nmea
|
|||
/// <summary>
|
||||
/// Gets the talker ID for this message (
|
||||
/// </summary>
|
||||
public Talker TalkerId => TalkerHelper.GetTalker(MessageType);
|
||||
public virtual Talker TalkerId => TalkerHelper.GetTalker(MessageType);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this message type is proprietary
|
||||
|
|
@ -161,69 +169,68 @@ 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
|
||||
{
|
||||
get
|
||||
{
|
||||
int checksumTest = 0;
|
||||
for (int j = -1; j < MessageParts.Count; j++)
|
||||
{
|
||||
string message = j < 0 ? MessageType : MessageParts[j];
|
||||
if (j >= 0)
|
||||
checksumTest ^= 0x2C; //Comma separator
|
||||
for (int i = 0; i < message.Length; i++)
|
||||
{
|
||||
checksumTest ^= Convert.ToByte(message[i]);
|
||||
}
|
||||
}
|
||||
return Convert.ToByte(checksumTest);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the checksum value of the message.
|
||||
/// </summary>
|
||||
public byte Checksum => GetChecksum(MessageType, MessageParts);
|
||||
|
||||
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 byte GetChecksum(string messageType, IReadOnlyList<string> messageParts)
|
||||
{
|
||||
int checksumTest = 0;
|
||||
for (int j = -1; j < messageParts.Count; j++)
|
||||
{
|
||||
string message = j < 0 ? messageType : messageParts[j];
|
||||
if (j >= 0)
|
||||
checksumTest ^= 0x2C; //Comma separator
|
||||
for (int i = 0; i < message.Length; i++)
|
||||
{
|
||||
checksumTest ^= Convert.ToByte(message[i]);
|
||||
}
|
||||
}
|
||||
return Convert.ToByte(checksumTest);
|
||||
}
|
||||
|
||||
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 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 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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
116
src/NmeaParser/Nmea/NmeaMultiSentenceMessage.cs
Normal file
116
src/NmeaParser/Nmea/NmeaMultiSentenceMessage.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// *******************************************************************************
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// ******************************************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace NmeaParser.Nmea
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for easily creating message that are spread across multiple sentences
|
||||
/// </summary>
|
||||
public abstract class NmeaMultiSentenceMessage : NmeaMessage, IMultiSentenceMessage
|
||||
{
|
||||
private int lastMessageNumber = 0;
|
||||
private int totalMessages;
|
||||
private readonly int firstMessageNumber;
|
||||
private readonly List<string[]> messages = new List<string[]>();
|
||||
private readonly bool initialized;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of the <see cref="NmeaMultiSentenceMessage"/> class.
|
||||
/// </summary>
|
||||
/// <param name="messageType">Type</param>
|
||||
/// <param name="messageParts">Message values</param>
|
||||
protected NmeaMultiSentenceMessage(string messageType, string[] messageParts) : base(messageType, messageParts)
|
||||
{
|
||||
totalMessages = int.Parse(messageParts[MessageCountIndex], CultureInfo.InvariantCulture);
|
||||
firstMessageNumber = int.Parse(messageParts[MessageNumberIndex], CultureInfo.InvariantCulture);
|
||||
talkerId = base.TalkerId;
|
||||
if (!((IMultiSentenceMessage)this).TryAppend(messageType, messageParts))
|
||||
throw new ArgumentException("Failed to parse message");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index in the <see cref="NmeaMessage.MessageParts"/> where the total count of messages is listed.
|
||||
/// </summary>
|
||||
protected virtual int MessageCountIndex { get; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index in the <see cref="NmeaMessage.MessageParts"/> where the message number is listed.
|
||||
/// </summary>
|
||||
protected virtual int MessageNumberIndex { get; } = 1;
|
||||
|
||||
bool IMultiSentenceMessage.IsComplete => firstMessageNumber == 1 && lastMessageNumber == totalMessages;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||||
foreach (var msg in messages)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append("\r\n");
|
||||
sb.AppendFormat(CultureInfo.InvariantCulture, "${0},{1}*{2:X2}", MessageType, string.Join(",", msg), Checksum);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
bool IMultiSentenceMessage.TryAppend(string messageType, string[] message)
|
||||
{
|
||||
if (message == null || message.Length < Math.Max(MessageCountIndex, MessageNumberIndex))
|
||||
throw new ArgumentException("Invalid message", "message");
|
||||
|
||||
|
||||
int msgCount = int.Parse(message[0], CultureInfo.InvariantCulture);
|
||||
int msgNumber = int.Parse(message[1], CultureInfo.InvariantCulture);
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
if (firstMessageNumber != 1) //We can only append to message who has message number 1
|
||||
return false;
|
||||
if (msgCount != totalMessages || msgNumber != lastMessageNumber + 1)
|
||||
return false; // Messages do not match
|
||||
}
|
||||
|
||||
var talker = TalkerHelper.GetTalker(messageType);
|
||||
if (talkerId != talker)
|
||||
talkerId = Talker.Multiple;
|
||||
if (ParseSentences(talker, message))
|
||||
{
|
||||
lastMessageNumber = msgNumber;
|
||||
messages.Add(message);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the messages or any message being appended. False should be returned if it's a message being appended doesn't appear to match what has already been loded.
|
||||
/// </summary>
|
||||
/// <param name="talkerType"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <returns>True if the message could succesfully be appended.</returns>
|
||||
protected abstract bool ParseSentences(Talker talkerType, string[] message);
|
||||
|
||||
private Talker talkerId;
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Talker TalkerId => talkerId;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ namespace NmeaParser.Nmea
|
|||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gprte")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
|
||||
[NmeaMessageType("--RTE")]
|
||||
public sealed class Rte : NmeaMessage, IMultiPartMessage<string>
|
||||
public sealed class Rte : NmeaMultiSentenceMessage, IEnumerable<string>
|
||||
{
|
||||
private readonly List<string> _waypoints = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Waypoint tpe
|
||||
/// </summary>
|
||||
|
|
@ -58,23 +60,18 @@ namespace NmeaParser.Nmea
|
|||
{
|
||||
if (message == null || message.Length < 4)
|
||||
throw new ArgumentException("Invalid GPRTE", "message");
|
||||
|
||||
TotalMessages = int.Parse(message[0], CultureInfo.InvariantCulture);
|
||||
MessageNumber = int.Parse(message[1], CultureInfo.InvariantCulture);
|
||||
ListType = message[2] == "c" ? WaypointListType.CompleteWaypointsList : WaypointListType.RemainingWaypointsList;
|
||||
RouteId = message[3];
|
||||
Waypoints = message.Skip(4).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total number of messages of this type in this cycle
|
||||
/// </summary>
|
||||
public int TotalMessages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Message number
|
||||
/// </summary>
|
||||
public int MessageNumber { get; }
|
||||
/// <inheritdoc />
|
||||
protected override bool ParseSentences(Talker talker, string[] message)
|
||||
{
|
||||
if (MessageParts[2] != message[2] || MessageParts[3] != message[3])
|
||||
return false;
|
||||
_waypoints.AddRange(message.Skip(4));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the list.
|
||||
|
|
@ -89,7 +86,7 @@ namespace NmeaParser.Nmea
|
|||
/// <summary>
|
||||
/// Waypoints
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Waypoints { get; }
|
||||
public IReadOnlyList<string> Waypoints => _waypoints.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
|
|
|
|||
|
|
@ -114,10 +114,14 @@ namespace NmeaParser.Nmea
|
|||
/// </summary>
|
||||
public enum Talker
|
||||
{
|
||||
/// <summary>
|
||||
/// Multiple talker IDs sometimes seen in <see cref="IMultiSentenceMessage"/>
|
||||
/// </summary>
|
||||
Multiple = -2,
|
||||
/// <summary>
|
||||
/// Unrecognized Talker ID
|
||||
/// </summary>
|
||||
Unknown,
|
||||
Unknown = -1,
|
||||
/// <summary>Independent AIS Base Station</summary>
|
||||
IndependentAISBaseStation, // = AB
|
||||
/// <summary>Dependent AIS Base Station</summary>
|
||||
|
|
|
|||
|
|
@ -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; } }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -22,40 +22,40 @@ 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);
|
||||
MultiPartMessageCache.Clear();
|
||||
}
|
||||
m_cts = new CancellationTokenSource();
|
||||
m_stream = await OpenStreamAsync();
|
||||
StartParser(m_cts.Token);
|
||||
_lastMultiMessage = null;
|
||||
lock (m_lockObject)
|
||||
{
|
||||
IsOpen = true;
|
||||
|
|
@ -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,151 +116,134 @@ 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);
|
||||
MultiPartMessageCache.Clear();
|
||||
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;
|
||||
|
||||
[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);
|
||||
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)
|
||||
{
|
||||
if (msg == null)
|
||||
return;
|
||||
Nmea.NmeaMessage[]? messageParts = null;
|
||||
if (msg is IMultiPartMessage multi)
|
||||
{
|
||||
string messageType = msg.MessageType.Substring(2); //We don't care about the two first characters. Ie GPGSV, GLGSV, GAGSV etc are all part of the same multi-part message
|
||||
if (MultiPartMessageCache.ContainsKey(messageType))
|
||||
{
|
||||
var dic = MultiPartMessageCache[messageType];
|
||||
if (dic.ContainsKey(multi.MessageNumber - 1) && !dic.ContainsKey(multi.MessageNumber))
|
||||
{
|
||||
dic[multi.MessageNumber] = msg;
|
||||
}
|
||||
else //Something is out of order. Clear cache
|
||||
MultiPartMessageCache.Remove(messageType);
|
||||
}
|
||||
else if (multi.MessageNumber == 1)
|
||||
{
|
||||
MultiPartMessageCache[messageType] = new Dictionary<int, Nmea.NmeaMessage>(multi.TotalMessages);
|
||||
MultiPartMessageCache[messageType][1] = msg;
|
||||
}
|
||||
if (MultiPartMessageCache.ContainsKey(messageType))
|
||||
{
|
||||
var dic = MultiPartMessageCache[messageType];
|
||||
if (dic.Count == multi.TotalMessages) //We have a full list
|
||||
{
|
||||
MultiPartMessageCache.Remove(messageType);
|
||||
messageParts = dic.Values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageReceived?.Invoke(this, new NmeaMessageReceivedEventArgs(msg, messageParts));
|
||||
|
||||
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
|
||||
|
|
@ -272,51 +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, IReadOnlyList<Nmea.NmeaMessage>? messageParts)
|
||||
/// <summary>
|
||||
/// Event argument for the <see cref="NmeaDevice.MessageReceived" />
|
||||
/// </summary>
|
||||
public sealed class NmeaMessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message)
|
||||
{
|
||||
Message = message;
|
||||
MessageParts = messageParts;
|
||||
}
|
||||
Message = message;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the nmea message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The nmea message.
|
||||
/// </value>
|
||||
public Nmea.NmeaMessage Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is a multi part message.
|
||||
/// Gets the nmea message.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is multi part; otherwise, <c>false</c>.
|
||||
/// The nmea message.
|
||||
/// </value>
|
||||
public bool IsMultipart => Message is IMultiPartMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message parts if this is a multi-part message and all message parts has been received.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The message parts.
|
||||
/// </value>
|
||||
public IReadOnlyList<Nmea.NmeaMessage>? MessageParts { get; }
|
||||
}
|
||||
public Nmea.NmeaMessage Message { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 />
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ namespace SampleApp.Droid
|
|||
socket?.Close();
|
||||
socket?.Dispose();
|
||||
socket = null;
|
||||
listener.CloseAsync();
|
||||
_ = listener.CloseAsync();
|
||||
listener = null;
|
||||
startButton.Enabled = !(stopButton.Enabled = false);
|
||||
}
|
||||
|
|
|
|||
526
src/SampleApp.Droid/Resources/Resource.Designer.cs
generated
526
src/SampleApp.Droid/Resources/Resource.Designer.cs
generated
|
|
@ -31,31 +31,31 @@ namespace SampleApp.Droid
|
|||
public partial class Attribute
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f010007
|
||||
public const int font = 2130771975;
|
||||
// aapt resource value: 0x7F010000
|
||||
public const int font = 2130771968;
|
||||
|
||||
// aapt resource value: 0x7f010000
|
||||
public const int fontProviderAuthority = 2130771968;
|
||||
// aapt resource value: 0x7F010001
|
||||
public const int fontProviderAuthority = 2130771969;
|
||||
|
||||
// aapt resource value: 0x7f010003
|
||||
public const int fontProviderCerts = 2130771971;
|
||||
// aapt resource value: 0x7F010002
|
||||
public const int fontProviderCerts = 2130771970;
|
||||
|
||||
// aapt resource value: 0x7f010004
|
||||
public const int fontProviderFetchStrategy = 2130771972;
|
||||
// aapt resource value: 0x7F010003
|
||||
public const int fontProviderFetchStrategy = 2130771971;
|
||||
|
||||
// aapt resource value: 0x7f010005
|
||||
public const int fontProviderFetchTimeout = 2130771973;
|
||||
// aapt resource value: 0x7F010004
|
||||
public const int fontProviderFetchTimeout = 2130771972;
|
||||
|
||||
// aapt resource value: 0x7f010001
|
||||
public const int fontProviderPackage = 2130771969;
|
||||
// aapt resource value: 0x7F010005
|
||||
public const int fontProviderPackage = 2130771973;
|
||||
|
||||
// aapt resource value: 0x7f010002
|
||||
public const int fontProviderQuery = 2130771970;
|
||||
// aapt resource value: 0x7F010006
|
||||
public const int fontProviderQuery = 2130771974;
|
||||
|
||||
// aapt resource value: 0x7f010006
|
||||
public const int fontStyle = 2130771974;
|
||||
// aapt resource value: 0x7F010007
|
||||
public const int fontStyle = 2130771975;
|
||||
|
||||
// aapt resource value: 0x7f010008
|
||||
// aapt resource value: 0x7F010008
|
||||
public const int fontWeight = 2130771976;
|
||||
|
||||
static Attribute()
|
||||
|
|
@ -71,8 +71,8 @@ namespace SampleApp.Droid
|
|||
public partial class Boolean
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f080000
|
||||
public const int abc_action_bar_embed_tabs = 2131230720;
|
||||
// aapt resource value: 0x7F020000
|
||||
public const int abc_action_bar_embed_tabs = 2130837504;
|
||||
|
||||
static Boolean()
|
||||
{
|
||||
|
|
@ -87,26 +87,26 @@ namespace SampleApp.Droid
|
|||
public partial class Color
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f050003
|
||||
public const int notification_action_color_filter = 2131034115;
|
||||
// aapt resource value: 0x7F030000
|
||||
public const int notification_action_color_filter = 2130903040;
|
||||
|
||||
// aapt resource value: 0x7f050004
|
||||
public const int notification_icon_bg_color = 2131034116;
|
||||
// aapt resource value: 0x7F030001
|
||||
public const int notification_icon_bg_color = 2130903041;
|
||||
|
||||
// aapt resource value: 0x7f050000
|
||||
public const int notification_material_background_media_default_color = 2131034112;
|
||||
// aapt resource value: 0x7F030002
|
||||
public const int notification_material_background_media_default_color = 2130903042;
|
||||
|
||||
// aapt resource value: 0x7f050001
|
||||
public const int primary_text_default_material_dark = 2131034113;
|
||||
// aapt resource value: 0x7F030003
|
||||
public const int primary_text_default_material_dark = 2130903043;
|
||||
|
||||
// aapt resource value: 0x7f050005
|
||||
public const int ripple_material_light = 2131034117;
|
||||
// aapt resource value: 0x7F030004
|
||||
public const int ripple_material_light = 2130903044;
|
||||
|
||||
// aapt resource value: 0x7f050002
|
||||
public const int secondary_text_default_material_dark = 2131034114;
|
||||
// aapt resource value: 0x7F030005
|
||||
public const int secondary_text_default_material_dark = 2130903045;
|
||||
|
||||
// aapt resource value: 0x7f050006
|
||||
public const int secondary_text_default_material_light = 2131034118;
|
||||
// aapt resource value: 0x7F030006
|
||||
public const int secondary_text_default_material_light = 2130903046;
|
||||
|
||||
static Color()
|
||||
{
|
||||
|
|
@ -121,65 +121,65 @@ namespace SampleApp.Droid
|
|||
public partial class Dimension
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f090004
|
||||
public const int compat_button_inset_horizontal_material = 2131296260;
|
||||
// aapt resource value: 0x7F040000
|
||||
public const int compat_button_inset_horizontal_material = 2130968576;
|
||||
|
||||
// aapt resource value: 0x7f090005
|
||||
public const int compat_button_inset_vertical_material = 2131296261;
|
||||
// aapt resource value: 0x7F040001
|
||||
public const int compat_button_inset_vertical_material = 2130968577;
|
||||
|
||||
// aapt resource value: 0x7f090006
|
||||
public const int compat_button_padding_horizontal_material = 2131296262;
|
||||
// aapt resource value: 0x7F040002
|
||||
public const int compat_button_padding_horizontal_material = 2130968578;
|
||||
|
||||
// aapt resource value: 0x7f090007
|
||||
public const int compat_button_padding_vertical_material = 2131296263;
|
||||
// aapt resource value: 0x7F040003
|
||||
public const int compat_button_padding_vertical_material = 2130968579;
|
||||
|
||||
// aapt resource value: 0x7f090008
|
||||
public const int compat_control_corner_material = 2131296264;
|
||||
// aapt resource value: 0x7F040004
|
||||
public const int compat_control_corner_material = 2130968580;
|
||||
|
||||
// aapt resource value: 0x7f090009
|
||||
public const int notification_action_icon_size = 2131296265;
|
||||
// aapt resource value: 0x7F040005
|
||||
public const int notification_action_icon_size = 2130968581;
|
||||
|
||||
// aapt resource value: 0x7f09000a
|
||||
public const int notification_action_text_size = 2131296266;
|
||||
// aapt resource value: 0x7F040006
|
||||
public const int notification_action_text_size = 2130968582;
|
||||
|
||||
// aapt resource value: 0x7f09000b
|
||||
public const int notification_big_circle_margin = 2131296267;
|
||||
// aapt resource value: 0x7F040007
|
||||
public const int notification_big_circle_margin = 2130968583;
|
||||
|
||||
// aapt resource value: 0x7f090001
|
||||
public const int notification_content_margin_start = 2131296257;
|
||||
// aapt resource value: 0x7F040008
|
||||
public const int notification_content_margin_start = 2130968584;
|
||||
|
||||
// aapt resource value: 0x7f09000c
|
||||
public const int notification_large_icon_height = 2131296268;
|
||||
// aapt resource value: 0x7F040009
|
||||
public const int notification_large_icon_height = 2130968585;
|
||||
|
||||
// aapt resource value: 0x7f09000d
|
||||
public const int notification_large_icon_width = 2131296269;
|
||||
// aapt resource value: 0x7F04000A
|
||||
public const int notification_large_icon_width = 2130968586;
|
||||
|
||||
// aapt resource value: 0x7f090002
|
||||
public const int notification_main_column_padding_top = 2131296258;
|
||||
// aapt resource value: 0x7F04000B
|
||||
public const int notification_main_column_padding_top = 2130968587;
|
||||
|
||||
// aapt resource value: 0x7f090003
|
||||
public const int notification_media_narrow_margin = 2131296259;
|
||||
// aapt resource value: 0x7F04000C
|
||||
public const int notification_media_narrow_margin = 2130968588;
|
||||
|
||||
// aapt resource value: 0x7f09000e
|
||||
public const int notification_right_icon_size = 2131296270;
|
||||
// aapt resource value: 0x7F04000D
|
||||
public const int notification_right_icon_size = 2130968589;
|
||||
|
||||
// aapt resource value: 0x7f090000
|
||||
public const int notification_right_side_padding_top = 2131296256;
|
||||
// aapt resource value: 0x7F04000E
|
||||
public const int notification_right_side_padding_top = 2130968590;
|
||||
|
||||
// aapt resource value: 0x7f09000f
|
||||
public const int notification_small_icon_background_padding = 2131296271;
|
||||
// aapt resource value: 0x7F04000F
|
||||
public const int notification_small_icon_background_padding = 2130968591;
|
||||
|
||||
// aapt resource value: 0x7f090010
|
||||
public const int notification_small_icon_size_as_large = 2131296272;
|
||||
// aapt resource value: 0x7F040010
|
||||
public const int notification_small_icon_size_as_large = 2130968592;
|
||||
|
||||
// aapt resource value: 0x7f090011
|
||||
public const int notification_subtext_size = 2131296273;
|
||||
// aapt resource value: 0x7F040011
|
||||
public const int notification_subtext_size = 2130968593;
|
||||
|
||||
// aapt resource value: 0x7f090012
|
||||
public const int notification_top_pad = 2131296274;
|
||||
// aapt resource value: 0x7F040012
|
||||
public const int notification_top_pad = 2130968594;
|
||||
|
||||
// aapt resource value: 0x7f090013
|
||||
public const int notification_top_pad_large_text = 2131296275;
|
||||
// aapt resource value: 0x7F040013
|
||||
public const int notification_top_pad_large_text = 2130968595;
|
||||
|
||||
static Dimension()
|
||||
{
|
||||
|
|
@ -194,41 +194,41 @@ namespace SampleApp.Droid
|
|||
public partial class Drawable
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f020000
|
||||
public const int notification_action_background = 2130837504;
|
||||
// aapt resource value: 0x7F050000
|
||||
public const int notification_action_background = 2131034112;
|
||||
|
||||
// aapt resource value: 0x7f020001
|
||||
public const int notification_bg = 2130837505;
|
||||
// aapt resource value: 0x7F050001
|
||||
public const int notification_bg = 2131034113;
|
||||
|
||||
// aapt resource value: 0x7f020002
|
||||
public const int notification_bg_low = 2130837506;
|
||||
// aapt resource value: 0x7F050002
|
||||
public const int notification_bg_low = 2131034114;
|
||||
|
||||
// aapt resource value: 0x7f020003
|
||||
public const int notification_bg_low_normal = 2130837507;
|
||||
// aapt resource value: 0x7F050003
|
||||
public const int notification_bg_low_normal = 2131034115;
|
||||
|
||||
// aapt resource value: 0x7f020004
|
||||
public const int notification_bg_low_pressed = 2130837508;
|
||||
// aapt resource value: 0x7F050004
|
||||
public const int notification_bg_low_pressed = 2131034116;
|
||||
|
||||
// aapt resource value: 0x7f020005
|
||||
public const int notification_bg_normal = 2130837509;
|
||||
// aapt resource value: 0x7F050005
|
||||
public const int notification_bg_normal = 2131034117;
|
||||
|
||||
// aapt resource value: 0x7f020006
|
||||
public const int notification_bg_normal_pressed = 2130837510;
|
||||
// aapt resource value: 0x7F050006
|
||||
public const int notification_bg_normal_pressed = 2131034118;
|
||||
|
||||
// aapt resource value: 0x7f020007
|
||||
public const int notification_icon_background = 2130837511;
|
||||
// aapt resource value: 0x7F050007
|
||||
public const int notification_icon_background = 2131034119;
|
||||
|
||||
// aapt resource value: 0x7f02000a
|
||||
public const int notification_template_icon_bg = 2130837514;
|
||||
// aapt resource value: 0x7F050008
|
||||
public const int notification_template_icon_bg = 2131034120;
|
||||
|
||||
// aapt resource value: 0x7f02000b
|
||||
public const int notification_template_icon_low_bg = 2130837515;
|
||||
// aapt resource value: 0x7F050009
|
||||
public const int notification_template_icon_low_bg = 2131034121;
|
||||
|
||||
// aapt resource value: 0x7f020008
|
||||
public const int notification_tile_bg = 2130837512;
|
||||
// aapt resource value: 0x7F05000A
|
||||
public const int notification_tile_bg = 2131034122;
|
||||
|
||||
// aapt resource value: 0x7f020009
|
||||
public const int notify_panel_notification_icon_bg = 2130837513;
|
||||
// aapt resource value: 0x7F05000B
|
||||
public const int notify_panel_notification_icon_bg = 2131034123;
|
||||
|
||||
static Drawable()
|
||||
{
|
||||
|
|
@ -243,119 +243,119 @@ namespace SampleApp.Droid
|
|||
public partial class Id
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f0a0015
|
||||
public const int action0 = 2131361813;
|
||||
// aapt resource value: 0x7F060000
|
||||
public const int action0 = 2131099648;
|
||||
|
||||
// aapt resource value: 0x7f0a0012
|
||||
public const int action_container = 2131361810;
|
||||
// aapt resource value: 0x7F060005
|
||||
public const int actions = 2131099653;
|
||||
|
||||
// aapt resource value: 0x7f0a0019
|
||||
public const int action_divider = 2131361817;
|
||||
// aapt resource value: 0x7F060001
|
||||
public const int action_container = 2131099649;
|
||||
|
||||
// aapt resource value: 0x7f0a0013
|
||||
public const int action_image = 2131361811;
|
||||
// aapt resource value: 0x7F060002
|
||||
public const int action_divider = 2131099650;
|
||||
|
||||
// aapt resource value: 0x7f0a0014
|
||||
public const int action_text = 2131361812;
|
||||
// aapt resource value: 0x7F060003
|
||||
public const int action_image = 2131099651;
|
||||
|
||||
// aapt resource value: 0x7f0a0023
|
||||
public const int actions = 2131361827;
|
||||
// aapt resource value: 0x7F060004
|
||||
public const int action_text = 2131099652;
|
||||
|
||||
// aapt resource value: 0x7f0a000d
|
||||
public const int altitude = 2131361805;
|
||||
// aapt resource value: 0x7F060006
|
||||
public const int altitude = 2131099654;
|
||||
|
||||
// aapt resource value: 0x7f0a0006
|
||||
public const int async = 2131361798;
|
||||
// aapt resource value: 0x7F060007
|
||||
public const int async = 2131099655;
|
||||
|
||||
// aapt resource value: 0x7f0a0007
|
||||
public const int blocking = 2131361799;
|
||||
// aapt resource value: 0x7F060008
|
||||
public const int blocking = 2131099656;
|
||||
|
||||
// aapt resource value: 0x7f0a0016
|
||||
public const int cancel_action = 2131361814;
|
||||
// aapt resource value: 0x7F060009
|
||||
public const int cancel_action = 2131099657;
|
||||
|
||||
// aapt resource value: 0x7f0a001e
|
||||
public const int chronometer = 2131361822;
|
||||
// aapt resource value: 0x7F06000A
|
||||
public const int chronometer = 2131099658;
|
||||
|
||||
// aapt resource value: 0x7f0a000e
|
||||
public const int device_picker = 2131361806;
|
||||
// aapt resource value: 0x7F06000B
|
||||
public const int device_picker = 2131099659;
|
||||
|
||||
// aapt resource value: 0x7f0a0025
|
||||
public const int end_padder = 2131361829;
|
||||
// aapt resource value: 0x7F06000C
|
||||
public const int end_padder = 2131099660;
|
||||
|
||||
// aapt resource value: 0x7f0a0008
|
||||
public const int forever = 2131361800;
|
||||
// aapt resource value: 0x7F06000D
|
||||
public const int forever = 2131099661;
|
||||
|
||||
// aapt resource value: 0x7f0a0020
|
||||
public const int icon = 2131361824;
|
||||
// aapt resource value: 0x7F06000E
|
||||
public const int icon = 2131099662;
|
||||
|
||||
// aapt resource value: 0x7f0a0024
|
||||
public const int icon_group = 2131361828;
|
||||
// aapt resource value: 0x7F06000F
|
||||
public const int icon_group = 2131099663;
|
||||
|
||||
// aapt resource value: 0x7f0a001f
|
||||
public const int info = 2131361823;
|
||||
// aapt resource value: 0x7F060010
|
||||
public const int info = 2131099664;
|
||||
|
||||
// aapt resource value: 0x7f0a0009
|
||||
public const int italic = 2131361801;
|
||||
// aapt resource value: 0x7F060011
|
||||
public const int italic = 2131099665;
|
||||
|
||||
// aapt resource value: 0x7f0a000c
|
||||
public const int latitude = 2131361804;
|
||||
// aapt resource value: 0x7F060012
|
||||
public const int latitude = 2131099666;
|
||||
|
||||
// aapt resource value: 0x7f0a0000
|
||||
public const int line1 = 2131361792;
|
||||
// aapt resource value: 0x7F060013
|
||||
public const int line1 = 2131099667;
|
||||
|
||||
// aapt resource value: 0x7f0a0001
|
||||
public const int line3 = 2131361793;
|
||||
// aapt resource value: 0x7F060014
|
||||
public const int line3 = 2131099668;
|
||||
|
||||
// aapt resource value: 0x7f0a000b
|
||||
public const int longitude = 2131361803;
|
||||
// aapt resource value: 0x7F060015
|
||||
public const int longitude = 2131099669;
|
||||
|
||||
// aapt resource value: 0x7f0a0018
|
||||
public const int media_actions = 2131361816;
|
||||
// aapt resource value: 0x7F060016
|
||||
public const int media_actions = 2131099670;
|
||||
|
||||
// aapt resource value: 0x7f0a000a
|
||||
public const int normal = 2131361802;
|
||||
// aapt resource value: 0x7F060017
|
||||
public const int normal = 2131099671;
|
||||
|
||||
// aapt resource value: 0x7f0a0022
|
||||
public const int notification_background = 2131361826;
|
||||
// aapt resource value: 0x7F060018
|
||||
public const int notification_background = 2131099672;
|
||||
|
||||
// aapt resource value: 0x7f0a001b
|
||||
public const int notification_main_column = 2131361819;
|
||||
// aapt resource value: 0x7F060019
|
||||
public const int notification_main_column = 2131099673;
|
||||
|
||||
// aapt resource value: 0x7f0a001a
|
||||
public const int notification_main_column_container = 2131361818;
|
||||
// aapt resource value: 0x7F06001A
|
||||
public const int notification_main_column_container = 2131099674;
|
||||
|
||||
// aapt resource value: 0x7f0a0011
|
||||
public const int output = 2131361809;
|
||||
// aapt resource value: 0x7F06001B
|
||||
public const int output = 2131099675;
|
||||
|
||||
// aapt resource value: 0x7f0a0021
|
||||
public const int right_icon = 2131361825;
|
||||
// aapt resource value: 0x7F06001C
|
||||
public const int right_icon = 2131099676;
|
||||
|
||||
// aapt resource value: 0x7f0a001c
|
||||
public const int right_side = 2131361820;
|
||||
// aapt resource value: 0x7F06001D
|
||||
public const int right_side = 2131099677;
|
||||
|
||||
// aapt resource value: 0x7f0a000f
|
||||
public const int startButton = 2131361807;
|
||||
// aapt resource value: 0x7F06001E
|
||||
public const int startButton = 2131099678;
|
||||
|
||||
// aapt resource value: 0x7f0a0017
|
||||
public const int status_bar_latest_event_content = 2131361815;
|
||||
// aapt resource value: 0x7F06001F
|
||||
public const int status_bar_latest_event_content = 2131099679;
|
||||
|
||||
// aapt resource value: 0x7f0a0010
|
||||
public const int stopButton = 2131361808;
|
||||
// aapt resource value: 0x7F060020
|
||||
public const int stopButton = 2131099680;
|
||||
|
||||
// aapt resource value: 0x7f0a0002
|
||||
public const int tag_transition_group = 2131361794;
|
||||
// aapt resource value: 0x7F060021
|
||||
public const int tag_transition_group = 2131099681;
|
||||
|
||||
// aapt resource value: 0x7f0a0003
|
||||
public const int text = 2131361795;
|
||||
// aapt resource value: 0x7F060022
|
||||
public const int text = 2131099682;
|
||||
|
||||
// aapt resource value: 0x7f0a0004
|
||||
public const int text2 = 2131361796;
|
||||
// aapt resource value: 0x7F060023
|
||||
public const int text2 = 2131099683;
|
||||
|
||||
// aapt resource value: 0x7f0a001d
|
||||
public const int time = 2131361821;
|
||||
// aapt resource value: 0x7F060024
|
||||
public const int time = 2131099684;
|
||||
|
||||
// aapt resource value: 0x7f0a0005
|
||||
public const int title = 2131361797;
|
||||
// aapt resource value: 0x7F060025
|
||||
public const int title = 2131099685;
|
||||
|
||||
static Id()
|
||||
{
|
||||
|
|
@ -370,11 +370,11 @@ namespace SampleApp.Droid
|
|||
public partial class Integer
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f060000
|
||||
public const int cancel_button_image_alpha = 2131099648;
|
||||
// aapt resource value: 0x7F070000
|
||||
public const int cancel_button_image_alpha = 2131165184;
|
||||
|
||||
// aapt resource value: 0x7f060001
|
||||
public const int status_bar_notification_info_maxnum = 2131099649;
|
||||
// aapt resource value: 0x7F070001
|
||||
public const int status_bar_notification_info_maxnum = 2131165185;
|
||||
|
||||
static Integer()
|
||||
{
|
||||
|
|
@ -389,53 +389,53 @@ namespace SampleApp.Droid
|
|||
public partial class Layout
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f030000
|
||||
public const int Main = 2130903040;
|
||||
// aapt resource value: 0x7F080000
|
||||
public const int Main = 2131230720;
|
||||
|
||||
// aapt resource value: 0x7f030001
|
||||
public const int notification_action = 2130903041;
|
||||
// aapt resource value: 0x7F080001
|
||||
public const int notification_action = 2131230721;
|
||||
|
||||
// aapt resource value: 0x7f030002
|
||||
public const int notification_action_tombstone = 2130903042;
|
||||
// aapt resource value: 0x7F080002
|
||||
public const int notification_action_tombstone = 2131230722;
|
||||
|
||||
// aapt resource value: 0x7f030003
|
||||
public const int notification_media_action = 2130903043;
|
||||
// aapt resource value: 0x7F080003
|
||||
public const int notification_media_action = 2131230723;
|
||||
|
||||
// aapt resource value: 0x7f030004
|
||||
public const int notification_media_cancel_action = 2130903044;
|
||||
// aapt resource value: 0x7F080004
|
||||
public const int notification_media_cancel_action = 2131230724;
|
||||
|
||||
// aapt resource value: 0x7f030005
|
||||
public const int notification_template_big_media = 2130903045;
|
||||
// aapt resource value: 0x7F080005
|
||||
public const int notification_template_big_media = 2131230725;
|
||||
|
||||
// aapt resource value: 0x7f030006
|
||||
public const int notification_template_big_media_custom = 2130903046;
|
||||
// aapt resource value: 0x7F080006
|
||||
public const int notification_template_big_media_custom = 2131230726;
|
||||
|
||||
// aapt resource value: 0x7f030007
|
||||
public const int notification_template_big_media_narrow = 2130903047;
|
||||
// aapt resource value: 0x7F080007
|
||||
public const int notification_template_big_media_narrow = 2131230727;
|
||||
|
||||
// aapt resource value: 0x7f030008
|
||||
public const int notification_template_big_media_narrow_custom = 2130903048;
|
||||
// aapt resource value: 0x7F080008
|
||||
public const int notification_template_big_media_narrow_custom = 2131230728;
|
||||
|
||||
// aapt resource value: 0x7f030009
|
||||
public const int notification_template_custom_big = 2130903049;
|
||||
// aapt resource value: 0x7F080009
|
||||
public const int notification_template_custom_big = 2131230729;
|
||||
|
||||
// aapt resource value: 0x7f03000a
|
||||
public const int notification_template_icon_group = 2130903050;
|
||||
// aapt resource value: 0x7F08000A
|
||||
public const int notification_template_icon_group = 2131230730;
|
||||
|
||||
// aapt resource value: 0x7f03000b
|
||||
public const int notification_template_lines_media = 2130903051;
|
||||
// aapt resource value: 0x7F08000B
|
||||
public const int notification_template_lines_media = 2131230731;
|
||||
|
||||
// aapt resource value: 0x7f03000c
|
||||
public const int notification_template_media = 2130903052;
|
||||
// aapt resource value: 0x7F08000C
|
||||
public const int notification_template_media = 2131230732;
|
||||
|
||||
// aapt resource value: 0x7f03000d
|
||||
public const int notification_template_media_custom = 2130903053;
|
||||
// aapt resource value: 0x7F08000D
|
||||
public const int notification_template_media_custom = 2131230733;
|
||||
|
||||
// aapt resource value: 0x7f03000e
|
||||
public const int notification_template_part_chronometer = 2130903054;
|
||||
// aapt resource value: 0x7F08000E
|
||||
public const int notification_template_part_chronometer = 2131230734;
|
||||
|
||||
// aapt resource value: 0x7f03000f
|
||||
public const int notification_template_part_time = 2130903055;
|
||||
// aapt resource value: 0x7F08000F
|
||||
public const int notification_template_part_time = 2131230735;
|
||||
|
||||
static Layout()
|
||||
{
|
||||
|
|
@ -450,11 +450,11 @@ namespace SampleApp.Droid
|
|||
public partial class String
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f070001
|
||||
public const int app_name = 2131165185;
|
||||
// aapt resource value: 0x7F090000
|
||||
public const int app_name = 2131296256;
|
||||
|
||||
// aapt resource value: 0x7f070000
|
||||
public const int status_bar_notification_info_overflow = 2131165184;
|
||||
// aapt resource value: 0x7F090001
|
||||
public const int status_bar_notification_info_overflow = 2131296257;
|
||||
|
||||
static String()
|
||||
{
|
||||
|
|
@ -469,41 +469,41 @@ namespace SampleApp.Droid
|
|||
public partial class Style
|
||||
{
|
||||
|
||||
// aapt resource value: 0x7f040005
|
||||
public const int TextAppearance_Compat_Notification = 2130968581;
|
||||
// aapt resource value: 0x7F0A0000
|
||||
public const int TextAppearance_Compat_Notification = 2131361792;
|
||||
|
||||
// aapt resource value: 0x7f040006
|
||||
public const int TextAppearance_Compat_Notification_Info = 2130968582;
|
||||
// aapt resource value: 0x7F0A0001
|
||||
public const int TextAppearance_Compat_Notification_Info = 2131361793;
|
||||
|
||||
// aapt resource value: 0x7f040000
|
||||
public const int TextAppearance_Compat_Notification_Info_Media = 2130968576;
|
||||
// aapt resource value: 0x7F0A0002
|
||||
public const int TextAppearance_Compat_Notification_Info_Media = 2131361794;
|
||||
|
||||
// aapt resource value: 0x7f04000b
|
||||
public const int TextAppearance_Compat_Notification_Line2 = 2130968587;
|
||||
// aapt resource value: 0x7F0A0003
|
||||
public const int TextAppearance_Compat_Notification_Line2 = 2131361795;
|
||||
|
||||
// aapt resource value: 0x7f040004
|
||||
public const int TextAppearance_Compat_Notification_Line2_Media = 2130968580;
|
||||
// aapt resource value: 0x7F0A0004
|
||||
public const int TextAppearance_Compat_Notification_Line2_Media = 2131361796;
|
||||
|
||||
// aapt resource value: 0x7f040001
|
||||
public const int TextAppearance_Compat_Notification_Media = 2130968577;
|
||||
// aapt resource value: 0x7F0A0005
|
||||
public const int TextAppearance_Compat_Notification_Media = 2131361797;
|
||||
|
||||
// aapt resource value: 0x7f040007
|
||||
public const int TextAppearance_Compat_Notification_Time = 2130968583;
|
||||
// aapt resource value: 0x7F0A0006
|
||||
public const int TextAppearance_Compat_Notification_Time = 2131361798;
|
||||
|
||||
// aapt resource value: 0x7f040002
|
||||
public const int TextAppearance_Compat_Notification_Time_Media = 2130968578;
|
||||
// aapt resource value: 0x7F0A0007
|
||||
public const int TextAppearance_Compat_Notification_Time_Media = 2131361799;
|
||||
|
||||
// aapt resource value: 0x7f040008
|
||||
public const int TextAppearance_Compat_Notification_Title = 2130968584;
|
||||
// aapt resource value: 0x7F0A0008
|
||||
public const int TextAppearance_Compat_Notification_Title = 2131361800;
|
||||
|
||||
// aapt resource value: 0x7f040003
|
||||
public const int TextAppearance_Compat_Notification_Title_Media = 2130968579;
|
||||
// aapt resource value: 0x7F0A0009
|
||||
public const int TextAppearance_Compat_Notification_Title_Media = 2131361801;
|
||||
|
||||
// aapt resource value: 0x7f040009
|
||||
public const int Widget_Compat_NotificationActionContainer = 2130968585;
|
||||
// aapt resource value: 0x7F0A000A
|
||||
public const int Widget_Compat_NotificationActionContainer = 2131361802;
|
||||
|
||||
// aapt resource value: 0x7f04000a
|
||||
public const int Widget_Compat_NotificationActionText = 2130968586;
|
||||
// aapt resource value: 0x7F0A000B
|
||||
public const int Widget_Compat_NotificationActionText = 2131361803;
|
||||
|
||||
static Style()
|
||||
{
|
||||
|
|
@ -518,37 +518,21 @@ namespace SampleApp.Droid
|
|||
public partial class Styleable
|
||||
{
|
||||
|
||||
// aapt resource value: { 0x7F010001,0x7F010002,0x7F010003,0x7F010004,0x7F010005,0x7F010006 }
|
||||
public static int[] FontFamily = new int[] {
|
||||
2130771968,
|
||||
2130771969,
|
||||
2130771970,
|
||||
2130771971,
|
||||
2130771972,
|
||||
2130771973};
|
||||
|
||||
// aapt resource value: 0
|
||||
public const int FontFamily_fontProviderAuthority = 0;
|
||||
|
||||
// aapt resource value: 3
|
||||
public const int FontFamily_fontProviderCerts = 3;
|
||||
|
||||
// aapt resource value: 4
|
||||
public const int FontFamily_fontProviderFetchStrategy = 4;
|
||||
|
||||
// aapt resource value: 5
|
||||
public const int FontFamily_fontProviderFetchTimeout = 5;
|
||||
|
||||
// aapt resource value: 1
|
||||
public const int FontFamily_fontProviderPackage = 1;
|
||||
|
||||
// aapt resource value: 2
|
||||
public const int FontFamily_fontProviderQuery = 2;
|
||||
2130771973,
|
||||
2130771974};
|
||||
|
||||
// aapt resource value: { 0x1010532,0x1010533,0x101053F,0x7F010000,0x7F010007,0x7F010008 }
|
||||
public static int[] FontFamilyFont = new int[] {
|
||||
16844082,
|
||||
16844083,
|
||||
16844095,
|
||||
2130771974,
|
||||
2130771968,
|
||||
2130771975,
|
||||
2130771976};
|
||||
|
||||
|
|
@ -561,15 +545,33 @@ namespace SampleApp.Droid
|
|||
// aapt resource value: 1
|
||||
public const int FontFamilyFont_android_fontWeight = 1;
|
||||
|
||||
// aapt resource value: 4
|
||||
public const int FontFamilyFont_font = 4;
|
||||
|
||||
// aapt resource value: 3
|
||||
public const int FontFamilyFont_fontStyle = 3;
|
||||
public const int FontFamilyFont_font = 3;
|
||||
|
||||
// aapt resource value: 4
|
||||
public const int FontFamilyFont_fontStyle = 4;
|
||||
|
||||
// aapt resource value: 5
|
||||
public const int FontFamilyFont_fontWeight = 5;
|
||||
|
||||
// aapt resource value: 0
|
||||
public const int FontFamily_fontProviderAuthority = 0;
|
||||
|
||||
// aapt resource value: 1
|
||||
public const int FontFamily_fontProviderCerts = 1;
|
||||
|
||||
// aapt resource value: 2
|
||||
public const int FontFamily_fontProviderFetchStrategy = 2;
|
||||
|
||||
// aapt resource value: 3
|
||||
public const int FontFamily_fontProviderFetchTimeout = 3;
|
||||
|
||||
// aapt resource value: 4
|
||||
public const int FontFamily_fontProviderPackage = 4;
|
||||
|
||||
// aapt resource value: 5
|
||||
public const int FontFamily_fontProviderQuery = 5;
|
||||
|
||||
static Styleable()
|
||||
{
|
||||
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
|
||||
|
|
|
|||
|
|
@ -83,24 +83,21 @@ namespace SampleApp.WinDesktop
|
|||
((NmeaParser.SerialPortDevice)device).Port.BaudRate);
|
||||
}
|
||||
}
|
||||
Dictionary<string, List<NmeaParser.Nmea.Gsv>> gsvMessages = new Dictionary<string, List<NmeaParser.Nmea.Gsv>>();
|
||||
Dictionary<NmeaParser.Nmea.Talker, NmeaParser.Nmea.Gsv> gsvMessages = new Dictionary<NmeaParser.Nmea.Talker, NmeaParser.Nmea.Gsv>();
|
||||
|
||||
private void device_MessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs args)
|
||||
{
|
||||
Dispatcher.BeginInvoke((Action) delegate()
|
||||
{
|
||||
messages.Enqueue(args.Message.MessageType + ": " + args.Message.ToString());
|
||||
messages.Enqueue(args.Message.ToString());
|
||||
if (messages.Count > 100) messages.Dequeue(); //Keep message queue at 100
|
||||
output.Text = string.Join("\n", messages.ToArray());
|
||||
output.Select(output.Text.Length - 1, 0); //scroll to bottom
|
||||
|
||||
if(args.Message is NmeaParser.Nmea.Gsv gpgsv)
|
||||
if (args.Message is NmeaParser.Nmea.Gsv gpgsv)
|
||||
{
|
||||
if (args.IsMultipart && args.MessageParts != null)
|
||||
{
|
||||
gsvMessages[args.Message.MessageType] = args.MessageParts.OfType<NmeaParser.Nmea.Gsv>().ToList();
|
||||
satView.GsvMessages = gsvMessages.SelectMany(m=>m.Value);
|
||||
}
|
||||
gsvMessages[gpgsv.TalkerId] = gpgsv;
|
||||
satView.GsvMessages = gsvMessages.Values;
|
||||
}
|
||||
else if (args.Message is NmeaParser.Nmea.Rmc)
|
||||
gprmcView.Message = args.Message as NmeaParser.Nmea.Rmc;
|
||||
|
|
@ -112,19 +109,19 @@ namespace SampleApp.WinDesktop
|
|||
gpgllView.Message = args.Message as NmeaParser.Nmea.Gll;
|
||||
else if (args.Message is NmeaParser.Nmea.Garmin.Pgrme)
|
||||
pgrmeView.Message = args.Message as NmeaParser.Nmea.Garmin.Pgrme;
|
||||
else
|
||||
{
|
||||
var ctrl = MessagePanel.Children.OfType<UnknownMessageControl>().Where(c => c.Message.MessageType == args.Message.MessageType).FirstOrDefault();
|
||||
if(ctrl == null)
|
||||
{
|
||||
ctrl = new UnknownMessageControl()
|
||||
{
|
||||
Style = this.Resources["card"] as Style
|
||||
};
|
||||
MessagePanel.Children.Add(ctrl);
|
||||
}
|
||||
ctrl.Message = args.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
var ctrl = MessagePanel.Children.OfType<UnknownMessageControl>().Where(c => c.Message.MessageType == args.Message.MessageType).FirstOrDefault();
|
||||
if (ctrl == null)
|
||||
{
|
||||
ctrl = new UnknownMessageControl()
|
||||
{
|
||||
Style = this.Resources["card"] as Style
|
||||
};
|
||||
MessagePanel.Children.Add(ctrl);
|
||||
}
|
||||
ctrl.Message = args.Message;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
<Project Sdk="MSBuild.Sdk.Extras/2.0.54">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp3.1</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.0.0" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\NmeaParser\NmeaParser.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\NmeaSampleData.txt">
|
||||
<Link>NmeaSampleData.txt</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\..\TrimbleR2SampleData.txt">
|
||||
<Link>TrimbleR2SampleData.txt</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\NmeaParser.Tests\NmeaParser.Tests.projitems" Label="Shared" />
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{170EE734-37F0-425F-822B-B865348ECEC6}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>NmeaParser.Tests.NET45</RootNamespace>
|
||||
<AssemblyName>NmeaParser.Tests.NET45</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSTest.TestFramework">
|
||||
<Version>1.4.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestAdapter">
|
||||
<Version>1.4.0</Version>
|
||||
</PackageReference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\NmeaParser\NmeaParser.csproj">
|
||||
<Project>{1adc3666-1ddb-48c4-9811-1e58b6d09a7c}</Project>
|
||||
<Name>NmeaParser</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\NmeaSampleData.txt">
|
||||
<Link>NmeaSampleData.txt</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="..\..\TrimbleR2SampleData.txt">
|
||||
<Link>TrimbleR2SampleData.txt</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="..\NmeaParser.Tests\NmeaParser.Tests.projitems" Label="Shared" />
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("NmeaParser.Tests.NET45")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NmeaParser.Tests.NET45")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
[assembly: Guid("170ee734-37f0-425f-822b-b865348ecec6")]
|
||||
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
|
@ -8,46 +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.IsTrue(e.IsMultipart, "IsMultiPart");
|
||||
Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv));
|
||||
var msg = (NmeaParser.Nmea.Gsv)e.Message;
|
||||
if (msg.TotalMessages == msg.MessageNumber)
|
||||
{
|
||||
Assert.IsNotNull(e.MessageParts);
|
||||
Assert.AreEqual(e.MessageParts.Count, 3, "MessageParts.Length");
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
else
|
||||
Assert.IsNull(e.MessageParts);
|
||||
if (count > 3)
|
||||
Assert.Fail();
|
||||
}
|
||||
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")]
|
||||
|
|
@ -61,34 +57,18 @@ $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>();
|
||||
int count = 0;
|
||||
dev.MessageReceived += (s, e) =>
|
||||
{
|
||||
count++;
|
||||
try
|
||||
{
|
||||
Assert.IsTrue(e.IsMultipart, "IsMultiPart");
|
||||
Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv));
|
||||
Assert.AreEqual(Talker.Multiple, e.Message.TalkerId);
|
||||
var msg = (NmeaParser.Nmea.Gsv)e.Message;
|
||||
if (msg.TotalMessages == msg.MessageNumber)
|
||||
{
|
||||
Assert.IsNotNull(e.MessageParts);
|
||||
Assert.AreEqual(e.MessageParts.Count, 4, "MessageParts.Length");
|
||||
Assert.IsInstanceOfType(e.MessageParts[0], typeof(NmeaParser.Nmea.Gsv));
|
||||
Assert.IsInstanceOfType(e.MessageParts[1], typeof(NmeaParser.Nmea.Gsv));
|
||||
Assert.IsInstanceOfType(e.MessageParts[2], typeof(NmeaParser.Nmea.Gsv));
|
||||
Assert.IsInstanceOfType(e.MessageParts[3], typeof(NmeaParser.Nmea.Gsv));
|
||||
Assert.AreEqual(Talker.GlobalPositioningSystem, e.MessageParts[0].TalkerId);
|
||||
Assert.AreEqual(Talker.GlobalPositioningSystem, e.MessageParts[1].TalkerId);
|
||||
Assert.AreEqual(Talker.GlonassReceiver, e.MessageParts[2].TalkerId);
|
||||
Assert.AreEqual(Talker.GalileoPositioningSystem, e.MessageParts[3].TalkerId);
|
||||
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
else
|
||||
Assert.IsNull(e.MessageParts);
|
||||
if (count > 3)
|
||||
Assert.Fail();
|
||||
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)
|
||||
{
|
||||
|
|
@ -101,34 +81,23 @@ $GAGSV,4,4,14,19,82,349,40,1,44,220,40,4,24,314,38*5F";
|
|||
}
|
||||
|
||||
[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>();
|
||||
int count = 0;
|
||||
dev.MessageReceived += (s, e) =>
|
||||
{
|
||||
count++;
|
||||
try
|
||||
{
|
||||
Assert.IsTrue(e.IsMultipart, "IsMultiPart");
|
||||
Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv));
|
||||
var msg = e.Message as NmeaParser.Nmea.Gsv;
|
||||
Assert.IsNull(e.MessageParts);
|
||||
if (count > 6)
|
||||
tcs.SetResult(true);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
tcs.SetException(ex);
|
||||
}
|
||||
};
|
||||
await dev.OpenAsync();
|
||||
await tcs.Task;
|
||||
var _ = dev.CloseAsync();
|
||||
}
|
||||
}
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,13 @@ namespace NmeaParser.Tests
|
|||
public class NmeaMessages
|
||||
{
|
||||
[TestMethod]
|
||||
public async Task ParseNmeaFile()
|
||||
public
|
||||
#if NETFX_CORE
|
||||
async Task
|
||||
#else
|
||||
void
|
||||
#endif
|
||||
ParseNmeaFile()
|
||||
{
|
||||
#if NETFX_CORE
|
||||
var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///NmeaSampleData.txt"));
|
||||
|
|
@ -38,12 +44,13 @@ namespace NmeaParser.Tests
|
|||
#else
|
||||
System.IO.StreamReader reader = new System.IO.StreamReader("NmeaSampleData.txt");
|
||||
#endif
|
||||
NmeaMessage previousMessage = null;
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line.StartsWith("$"))
|
||||
{
|
||||
var msg = NmeaMessage.Parse(line);
|
||||
var msg = NmeaMessage.Parse(line, previousMessage as IMultiSentenceMessage);
|
||||
Assert.IsNotNull(msg);
|
||||
var idx = line.IndexOf('*');
|
||||
if (idx >= 0)
|
||||
|
|
@ -56,7 +63,13 @@ namespace NmeaParser.Tests
|
|||
}
|
||||
}
|
||||
[TestMethod]
|
||||
public async Task ParseTrimbleR2NmeaFile()
|
||||
public
|
||||
#if NETFX_CORE
|
||||
async Task
|
||||
#else
|
||||
void
|
||||
#endif
|
||||
ParseTrimbleR2NmeaFile()
|
||||
{
|
||||
#if NETFX_CORE
|
||||
var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///TrimbleR2SampleData.txt"));
|
||||
|
|
@ -64,12 +77,13 @@ namespace NmeaParser.Tests
|
|||
#else
|
||||
System.IO.StreamReader reader = new System.IO.StreamReader("TrimbleR2SampleData.txt");
|
||||
#endif
|
||||
NmeaMessage previousMessage = null;
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
if (line.StartsWith("$"))
|
||||
{
|
||||
var msg = NmeaMessage.Parse(line);
|
||||
var msg = NmeaMessage.Parse(line, previousMessage as IMultiSentenceMessage);
|
||||
Assert.IsNotNull(msg);
|
||||
var idx = line.IndexOf('*');
|
||||
if (idx >= 0)
|
||||
|
|
@ -302,8 +316,8 @@ namespace NmeaParser.Tests
|
|||
var msg = NmeaMessage.Parse(input);
|
||||
Assert.IsInstanceOfType(msg, typeof(Gsv));
|
||||
Gsv gsv = (Gsv)msg;
|
||||
Assert.AreEqual(3, gsv.TotalMessages);
|
||||
Assert.AreEqual(3, gsv.MessageNumber);
|
||||
Assert.IsInstanceOfType(msg, typeof(IMultiSentenceMessage));
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg).IsComplete);
|
||||
Assert.AreEqual(11, gsv.SVsInView);
|
||||
Assert.IsNotNull(gsv.SVs);
|
||||
Assert.AreEqual(3, gsv.SVs.Count);
|
||||
|
|
@ -336,13 +350,75 @@ namespace NmeaParser.Tests
|
|||
var msg = NmeaMessage.Parse(input);
|
||||
Assert.IsInstanceOfType(msg, typeof(Gsv));
|
||||
Gsv gsv = (Gsv)msg;
|
||||
Assert.AreEqual(1, gsv.TotalMessages);
|
||||
Assert.AreEqual(1, gsv.MessageNumber);
|
||||
Assert.IsTrue(((IMultiSentenceMessage)gsv).IsComplete);
|
||||
Assert.AreEqual(0, gsv.SVsInView);
|
||||
Assert.IsNotNull(gsv.SVs);
|
||||
Assert.AreEqual(0, gsv.SVs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGpgsv_Multi()
|
||||
{
|
||||
var input1 = "$GPGSV,3,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4C";
|
||||
var input2 = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F";
|
||||
var input3 = "$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74";
|
||||
var msg1 = NmeaMessage.Parse(input1);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg1).IsComplete);
|
||||
var msg2 = NmeaMessage.Parse(input2, msg1 as IMultiSentenceMessage);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg2).IsComplete);
|
||||
var msg3 = NmeaMessage.Parse(input3, msg2 as IMultiSentenceMessage);
|
||||
Assert.IsTrue(((IMultiSentenceMessage)msg3).IsComplete);
|
||||
Assert.IsInstanceOfType(msg1, typeof(Gsv));
|
||||
Assert.AreSame(msg1, msg2);
|
||||
Assert.AreSame(msg1, msg3);
|
||||
Gsv gsv = (Gsv)msg1;
|
||||
Assert.AreEqual(9, gsv.SVsInView);
|
||||
Assert.IsNotNull(gsv.SVs);
|
||||
Assert.AreEqual(9, gsv.SVs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestGpgsv_MultiMissing()
|
||||
{
|
||||
var input1 = "$GPGSV,2,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4D";
|
||||
var input2 = "$GPGSV,2,2,8,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F"; //Satellite count doesn't match, so append will fail
|
||||
var msg1 = NmeaMessage.Parse(input1);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg1).IsComplete);
|
||||
var msg2 = NmeaMessage.Parse(input2, msg1 as IMultiSentenceMessage);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg2).IsComplete);
|
||||
Assert.IsInstanceOfType(msg2, typeof(Gsv));
|
||||
Assert.AreNotSame(msg1, msg2);
|
||||
Gsv gsv1 = (Gsv)msg1;
|
||||
Assert.AreEqual(9, gsv1.SVsInView);
|
||||
Assert.IsNotNull(gsv1.SVs);
|
||||
Assert.AreEqual(4, gsv1.SVs.Count);
|
||||
Gsv gsv2 = (Gsv)msg2;
|
||||
Assert.AreEqual(8, gsv2.SVsInView);
|
||||
Assert.IsNotNull(gsv2.SVs);
|
||||
Assert.AreEqual(4, gsv2.SVs.Count);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void TestGpgsv_MultiNotMatching()
|
||||
{
|
||||
var input2 = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F";
|
||||
var input3 = "$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74";
|
||||
var msg2 = NmeaMessage.Parse(input2);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg2).IsComplete);
|
||||
var msg3 = NmeaMessage.Parse(input3, msg2 as IMultiSentenceMessage);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)msg3).IsComplete);
|
||||
Assert.IsInstanceOfType(msg2, typeof(Gsv));
|
||||
Assert.AreNotSame(msg2, msg3);
|
||||
Gsv gsv2 = (Gsv)msg2;
|
||||
Assert.AreEqual(9, gsv2.SVsInView);
|
||||
Assert.IsNotNull(gsv2.SVs);
|
||||
Assert.AreEqual(4, gsv2.SVs.Count);
|
||||
Gsv gsv3 = (Gsv)msg3;
|
||||
Assert.AreEqual(9, gsv3.SVsInView);
|
||||
Assert.IsNotNull(gsv3.SVs);
|
||||
Assert.AreEqual(1, gsv3.SVs.Count);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[WorkItem(53)]
|
||||
|
|
@ -352,8 +428,7 @@ namespace NmeaParser.Tests
|
|||
var msg = NmeaMessage.Parse(msgstr);
|
||||
Assert.IsInstanceOfType(msg, typeof(Gsv));
|
||||
Gsv gsv = (Gsv)msg;
|
||||
Assert.AreEqual(3, gsv.TotalMessages);
|
||||
Assert.AreEqual(1, gsv.MessageNumber);
|
||||
Assert.IsFalse(((IMultiSentenceMessage)gsv).IsComplete);
|
||||
Assert.AreEqual(12, gsv.SVsInView);
|
||||
Assert.IsNotNull(gsv.SVs);
|
||||
Assert.AreEqual(4, gsv.SVs.Count);
|
||||
|
|
@ -497,118 +572,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 gsv = (Rte)msg;
|
||||
Assert.AreEqual(2, gsv.TotalMessages);
|
||||
Assert.AreEqual(1, gsv.MessageNumber);
|
||||
Assert.AreEqual(Rte.WaypointListType.CompleteWaypointsList, gsv.ListType);
|
||||
Assert.AreEqual("0", gsv.RouteId);
|
||||
Assert.AreEqual("0", gsv.RouteId);
|
||||
Assert.AreEqual(9, gsv.Waypoints.Count);
|
||||
Assert.AreEqual("W3IWI", gsv.Waypoints[0]);
|
||||
Assert.AreEqual("32BKLD", gsv.Waypoints[4]);
|
||||
Assert.AreEqual("BW-198", gsv.Waypoints[8]);
|
||||
}
|
||||
[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]);
|
||||
}
|
||||
|
||||
[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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue