Add project files.

This commit is contained in:
2023-07-11 10:54:27 -05:00
parent bffa409ea4
commit 8b111f95d0
24 changed files with 1130 additions and 0 deletions

66
BaseGpsClient.cs Normal file
View File

@@ -0,0 +1,66 @@
using System;
using GpsClient2.Model;
namespace GpsClient2 {
public abstract class BaseGpsClient {
#region Properties
public GpsType GpsType { get; }
public bool IsRunning { get; set; }
protected BaseGpsInfo GpsInfo { get; set; }
#endregion
#region Event handlers
public event EventHandler<GpsDataEventArgs> GpsCallbackEvent;
public event EventHandler<string> RawGpsCallbackEvent;
public event EventHandler<GpsStatus> GpsStatusEvent;
#endregion
#region Constructors
protected BaseGpsClient(GpsType gpsType, BaseGpsInfo gpsInfo) {
GpsType = gpsType;
GpsInfo = gpsInfo;
}
#endregion
#region Connect and Disconnect
public abstract bool Connect();
public abstract bool Disconnect();
#endregion
#region Events Triggers
protected virtual void OnGpsDataReceived(GpsDataEventArgs e) {
if (GpsInfo.CoordinateSystem == GpsCoordinateSystem.Lambert72) {
var x = 0.0d;
var y = 0.0d;
var h = 0.0d;
CoordinateConverterUtilities.GeoETRS89ToLambert72(e.Latitude, e.Longitude, 0, ref x, ref y, ref h);
e.CoordinateSystem = GpsCoordinateSystem.Lambert72;
e.Latitude = x;
e.Longitude = y;
}
GpsCallbackEvent?.Invoke(this, e);
}
protected virtual void OnRawGpsDataReceived(string e) {
RawGpsCallbackEvent?.Invoke(this, e);
}
protected virtual void OnGpsStatusChanged(GpsStatus e) {
GpsStatusEvent?.Invoke(this, e);
}
#endregion
}
}

89
ComPortGpsClient.cs Normal file
View File

@@ -0,0 +1,89 @@
using GpsClient2.Model;
using GpsClient2.NmeaMessages;
using GpsClient2.Exceptions;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GpsClient2 {
public class ComPortGpsClient : BaseGpsClient {
#region Private Properties
private readonly NmeaParser _parser = new NmeaParser();
private SerialPort _serialPort;
private DateTime? _previousReadTime;
#endregion
#region Constructors
public ComPortGpsClient(ComPortInfo connectionData) : base(GpsType.ComPort, connectionData) {
}
public ComPortGpsClient(BaseGpsInfo connectionData) : base(GpsType.ComPort, connectionData) {
}
#endregion
#region Connect and Disconnect
public override bool Connect() {
var data = (ComPortInfo)GpsInfo;
IsRunning = true;
OnGpsStatusChanged(GpsStatus.Connecting);
_serialPort = new SerialPort(data.ComPort, 9600, Parity.None, 8, StopBits.One);
// Attach a method to be called when there
// is data waiting in the port's buffer
_serialPort.DataReceived += port_DataReceived;
try {
// Begin communications
_serialPort.Open();
OnGpsStatusChanged(GpsStatus.Connected);
// Enter an application loop to keep this thread alive
while (_serialPort.IsOpen) {
Thread.Sleep(data.ReadFrequenty);
}
} catch {
Disconnect();
throw;
}
return true;
}
public override bool Disconnect() {
_serialPort.Close();
IsRunning = false;
OnGpsStatusChanged(GpsStatus.Disabled);
return true;
}
#endregion
#region Location Callbacks
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e) {
try {
var readString = _serialPort.ReadExisting();
OnRawGpsDataReceived(readString);
var result = _parser.Parse(readString);
if (typeof(GprmcMessage) != result.GetType()) return;
if (_previousReadTime != null && GpsInfo.ReadFrequenty != 0 && ((GprmcMessage)result).UpdateDate.Subtract(new TimeSpan(0, 0, 0, 0, GpsInfo.ReadFrequenty)) <= _previousReadTime) return;
OnGpsDataReceived(new GpsDataEventArgs((GprmcMessage)result));
} catch (UnknownTypeException ex) {
Console.WriteLine(ex.Message);
}
}
#endregion
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2 {
internal class CoordinateConverterUtilities {
#if WIN64
private const string DllImport = @"plugins/ETRS89_LAMBERT_UTM_64bits.dll";
#else
private const string DllImport = @"plugins/ETRS89_LAMBERT_UTM_32bits.dll";
#endif
#region Coordinate conversion functions using NGI DLL
//Import the dll with the functions to calculate lambert coordinates
[DllImport(DllImport, SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GeoETRS89ToLambert72(double Xi, double Yi, double Zi, ref double xo, ref double yo, ref double Ho);
[DllImport(DllImport, SetLastError = true, CharSet = CharSet.Auto)]
public static extern int Lambert72ToLambert08(double Xi, double Yi, double Zi, ref double xo, ref double yo, ref double Ho);
[DllImport(DllImport, SetLastError = true, CharSet = CharSet.Auto)]
public static extern int Lambert72ToGeoETRS89(double Xi, double Yi, double Zi, ref double xo, ref double yo, ref double Ho);
#endregion
}
}

78
Enums.cs Normal file
View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2 {
public enum CoordinateType {
Latitude,
Longitude
}
public enum GpsFixQuality {
Invalid = 0,
/// <summary>
/// GPS fix (SPS)
/// </summary>
GpsFix = 1,
/// <summary>
/// DGPS fix
/// </summary>
DgpsFix = 2,
/// <summary>
/// PPS fix
/// </summary>
PpsFix = 3,
/// <summary>
/// Real Time Kinematic
/// </summary>
Rtk = 4,
/// <summary>
/// Float RTK
/// </summary>
FloatRtk = 5,
/// <summary>
/// estimated (dead reckoning) (2.3 feature)
/// </summary>
Estimated = 6,
/// <summary>
/// Manual input mode
/// </summary>
ManualInput = 7,
/// <summary>
/// Simulation mode
/// </summary>
Simulation = 8
}
public enum SatelliteFixType {
NoFix,
TwoDFix,
ThreeDFix
}
public enum FileType {
Nmea,
Gpsd,
LatitudeLongitude,
}
public enum GpsCoordinateSystem {
Lambert72,
GeoEtrs89,
}
public enum GpsStatus {
Disabled,
Connecting,
Connected
}
public enum GpsType {
File,
ComPort,
Gpsd,
WindowsLocationApi
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.Exceptions {
public class UnknownTypeException : Exception {
public UnknownTypeException(Type type) : base($"Unknown Class Type: {type}") {
}
public UnknownTypeException(string type) : base($"Unknown Class Type: {type}") {
}
public UnknownTypeException() : base("Unknown Class Type") {
}
}
}

76
GpsClient2.csproj Normal file
View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" 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>{C62E7A96-F279-4390-A1AC-8FACDD9E8C14}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GpsClient2</RootNamespace>
<AssemblyName>GpsClient2</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</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>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Device" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BaseGpsClient.cs" />
<Compile Include="CoordinateConverterUtilities.cs" />
<Compile Include="Enums.cs" />
<Compile Include="Exceptions\UnknownTypeException.cs" />
<Compile Include="Model\BaseGpsInfo.cs" />
<Compile Include="Model\ComPortInfo.cs" />
<Compile Include="Model\WindowsLocationApiInfo.cs" />
<Compile Include="NmeaConstants.cs" />
<Compile Include="NmeaMessages\GpggaMessage.cs" />
<Compile Include="NmeaMessages\GpgsaMessage.cs" />
<Compile Include="NmeaMessages\GpvtgMessage.cs" />
<Compile Include="NmeaMessages\NmeaMessage.cs" />
<Compile Include="NmeaParser.cs" />
<Compile Include="StringExtensions.cs" />
<Compile Include="ComPortGpsClient.cs" />
<Compile Include="GpsDataEventArgs.cs" />
<Compile Include="Model\GpsLocation.cs" />
<Compile Include="NmeaMessages\GprmcMessage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindowsLocationApiGpsClient.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="plugins\ETRS89_LAMBERT_UTM_32bits.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="plugins\ETRS89_LAMBERT_UTM_64bits.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

25
GpsClient2.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33801.468
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GpsClient2", "GpsClient2.csproj", "{C62E7A96-F279-4390-A1AC-8FACDD9E8C14}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C62E7A96-F279-4390-A1AC-8FACDD9E8C14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C62E7A96-F279-4390-A1AC-8FACDD9E8C14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C62E7A96-F279-4390-A1AC-8FACDD9E8C14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C62E7A96-F279-4390-A1AC-8FACDD9E8C14}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {85C8CFC9-7CA5-4D17-B598-653F56226D85}
EndGlobalSection
EndGlobal

47
GpsDataEventArgs.cs Normal file
View File

@@ -0,0 +1,47 @@
using System;
using GpsClient2.Model;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Device.Location;
using GpsClient2.NmeaMessages;
namespace GpsClient2 {
public class GpsDataEventArgs : EventArgs {
public GpsCoordinateSystem CoordinateSystem { get; set; } = GpsCoordinateSystem.GeoEtrs89;
public double Latitude { get; set; }
public double Longitude { get; set; }
public double Speed { get; set; }
public GpsDataEventArgs(GpsLocation gpsLocation) {
Latitude = gpsLocation.Latitude;
Longitude = gpsLocation.Longitude;
Speed = gpsLocation.Speed;
}
public GpsDataEventArgs(GprmcMessage gpsLocation) {
Latitude = gpsLocation.Latitude;
Longitude = gpsLocation.Longitude;
Speed = gpsLocation.Speed;
}
public GpsDataEventArgs(GeoCoordinate gpsLocation) {
Latitude = gpsLocation.Latitude;
Longitude = gpsLocation.Longitude;
Speed = gpsLocation.Speed;
}
public GpsDataEventArgs(double latitude, double longitude, double speed = 0.0d) {
Latitude = latitude;
Longitude = longitude;
Speed = speed;
}
public override string ToString() {
return $"Latitude: {Latitude} - Longitude: {Longitude} - Speed: {Speed}";
}
}
}

13
Model/BaseGpsInfo.cs Normal file
View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.Model {
public abstract class BaseGpsInfo {
public GpsCoordinateSystem CoordinateSystem { get; set; } = GpsCoordinateSystem.GeoEtrs89;
public int ReadFrequenty { get; set; }
}
}

20
Model/ComPortInfo.cs Normal file
View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.Model {
public class ComPortInfo : BaseGpsInfo {
public string ComPort { get; set; } = "ComPort1";
public ComPortInfo() {
ReadFrequenty = 1000;
}
public ComPortInfo(string comPort, int readFrequenty = 1000) {
ComPort = comPort;
ReadFrequenty = readFrequenty;
}
}
}

45
Model/GpsLocation.cs Normal file
View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace GpsClient2.Model {
[DataContract]
public class GpsLocation {
[DataMember(Name = "tag")]
public string Tag { get; set; }
[DataMember(Name = "device")]
public string Device { get; set; }
[DataMember(Name = "mode")]
public int Mode { get; set; }
[DataMember(Name = "time")]
public DateTime Time { get; set; }
[DataMember(Name = "ept")]
public float Ept { get; set; }
[DataMember(Name = "lat")]
public double Latitude { get; set; }
[DataMember(Name = "lon")]
public double Longitude { get; set; }
[DataMember(Name = "track")]
public float Track { get; set; }
[DataMember(Name = "speed")]
public float SpeedKnots { get; set; }
public double Speed => SpeedKnots * 1.852;
public override string ToString() {
return $"Tag: {Tag} - Device: {Device} - Mode: {Mode} - Time: {Time} - Latitude: {Latitude} - Longitude: {Longitude} - Track: {Track} - Speed: {Speed}";
}
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.Model {
public class WindowsLocationApiInfo : BaseGpsInfo {
public int Timeout { get; set; } = 1000;
public WindowsLocationApiInfo() {
ReadFrequenty = 0;
}
}
}

36
NmeaConstants.cs Normal file
View File

@@ -0,0 +1,36 @@
using GpsClient2.Exceptions;
using GpsClient2.NmeaMessages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2 {
public static class NmeaConstants {
private static readonly Dictionary<string, Type> TypeDictionary = new Dictionary<string, Type>
{
{"GPGGA", typeof(GpggaMessage)},
{"GPRMC", typeof(GprmcMessage)},
{"GPVTG", typeof(GpvtgMessage)},
{"GPGSA", typeof(GpgsaMessage)}
};
/// <summary>
/// Returns the correct class type of the message.
/// </summary>
/// <param name="typeName">The type name given.</param>
/// <returns>The class type.</returns>
/// <exception cref="UnknownTypeException">Given if the type is unkown.</exception>
public static Type GetClassType(string typeName) {
Type result;
TypeDictionary.TryGetValue(typeName, out result);
if (result == null) {
throw new UnknownTypeException();
}
return result;
}
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.NmeaMessages {
public class GpggaMessage : NmeaMessage {
#region Description
// $GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47
// Where:
// GGA Global Positioning System Fix Data
// 123519 Fix taken at 12:35:19 UTC
// 4807.038,N Latitude 48 deg 07.038' N
// 01131.000,E Longitude 11 deg 31.000' E
// 1 Fix quality: 0 = invalid
// 1 = GPS fix(SPS)
// 2 = DGPS fix
// 3 = PPS fix
// 4 = Real Time Kinematic
// 5 = Float RTK
// 6 = estimated(dead reckoning) (2.3 feature)
// 7 = Manual input mode
// 8 = Simulation mode
// 08 Number of satellites being tracked
// 0.9 Horizontal dilution of position
// 545.4, M Altitude, Meters, above mean sea level
// 46.9, M Height of geoid(mean sea level) above WGS84
// ellipsoid
// (empty field) time in seconds since last DGPS update
// (empty field) DGPS station ID number
// *47 the checksum data, always begins with*
#endregion
#region Properties
/// <summary>
/// Fix taken
/// </summary>
public TimeSpan FixTime { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public GpsFixQuality FixQuality { get; set; }
public int NumberOfSatellites { get; set; }
/// <summary>
/// Horizontal dilution of position
/// </summary>
public float Hdop { get; set; }
/// <summary>
/// Altitude, Meters, above mean sea level
/// </summary>
public float Altitude { get; set; }
/// <summary>
/// Altitude units ('M' for Meters)
/// </summary>
public string AltitudeUnits { get; set; }
/// <summary>
/// Height of geoid (mean sea level) above WGS84
/// </summary>
public float HeightOfGeoId { get; set; }
public string HeightOfGeoIdUnits { get; set; }
/// <summary>
/// Time in seconds since last DGPS update
/// </summary>
public int TimeSpanSinceDgpsUpdate { get; set; }
/// <summary>
/// DGPS station ID number
/// </summary>
public int? DgpsStationId { get; set; }
#endregion
#region Message parsing
public override void Parse(string[] messageParts) {
if (messageParts == null || messageParts.Length < 14) {
throw new ArgumentException("Invalid GPGGA message");
}
FixTime = messageParts[1].ToTimeSpan();
Latitude = messageParts[2].ToCoordinates(messageParts[3], CoordinateType.Latitude);
Longitude = messageParts[4].ToCoordinates(messageParts[5], CoordinateType.Longitude);
FixQuality = (GpsFixQuality)Enum.Parse(typeof(GpsFixQuality), messageParts[6]);
NumberOfSatellites = messageParts[7].ToInteger();
Hdop = messageParts[8].ToFloat();
Altitude = messageParts[9].ToFloat();
AltitudeUnits = messageParts[10];
HeightOfGeoId = messageParts[11].ToFloat();
HeightOfGeoIdUnits = messageParts[12];
TimeSpanSinceDgpsUpdate = messageParts[13].ToInteger();
DgpsStationId = messageParts[14].ToInteger();
}
#endregion
public override string ToString() {
return $"Latitude {Latitude} - Longitude {Longitude} - Hoogte {Altitude}";
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.NmeaMessages {
public class GpgsaMessage : NmeaMessage {
#region Description
// $GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39
// Where:
// GSA Satellite status
// A Auto selection of 2D or 3D fix(M = manual)
// 3 3D fix - values include: 1 = no fix
// 2 = 2D fix
// 3 = 3D fix
// 04,05... PRNs of satellites used for fix(space for 12)
// 2.5 PDOP(dilution of precision)
// 1.3 Horizontal dilution of precision(HDOP)
// 2.1 Vertical dilution of precision(VDOP)
// *39 the checksum data, always begins with*
#endregion
#region Properties
/// <summary>
/// Auto selection of 2D or 3D fix(M = manual)
/// </summary>
public bool GpsStatusAuto { get; set; }
/// <summary>
/// 3D fix - values include: 1 = no fix
// 2 = 2D fix
// 3 = 3D fix
/// </summary>
public SatelliteFixType SatelliteFix { get; set; }
/// <summary>
/// PRNs of satellites used for fix(space for 12)
/// </summary>
public string Pnrs { get; set; }
/// <summary>
/// PDOP(dilution of precision)
/// </summary>
public float Pdop { get; set; }
/// <summary>
/// Horizontal dilution of precision(HDOP)
/// </summary>
public float Hdop { get; set; }
/// <summary>
/// Vertical dilution of precision(VDOP)
/// </summary>
public float Vdop { get; set; }
#endregion
#region Message parsing
public override void Parse(string[] messageParts) {
if (messageParts == null || messageParts.Length < 9) {
throw new ArgumentException("Invalid GPGSA message");
}
GpsStatusAuto = messageParts[1].ToBoolean("A");
SatelliteFix = (SatelliteFixType)Enum.Parse(typeof(SatelliteFixType), messageParts[2]);
for (var i = 0 + 3; i < 12 + 3; i++) {
Pnrs += $"{messageParts[i]},";
}
Pdop = messageParts[15].ToFloat();
Hdop = messageParts[16].ToFloat();
Vdop = messageParts[17].ToFloat();
}
#endregion
public override string ToString() {
return $"Status {GpsStatusAuto} - Satellite Fix Type {SatelliteFix}";
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.NmeaMessages {
public class GprmcMessage : NmeaMessage {
#region Description
// $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
//
// Where:
// RMC Recommended Minimum sentence C
// 123519 Fix taken at 12:35:19 UTC
// A Status A = active or V = Void.
// 4807.038, N Latitude 48 deg 07.038' N
// 01131.000,E Longitude 11 deg 31.000' E
// 022.4 Speed over the ground in knots
// 084.4 Track angle in degrees True
// 230394 Date - 23rd of March 1994
// 003.1,W Magnetic Variation
// *6A The checksum data, always begins with *
#endregion
#region Properties
public TimeSpan FixTime { get; set; }
/// <summary>
/// Status A = active or V = Void.
/// </summary>
public bool IsActive { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
/// <summary>
/// Speed over the ground in knots
/// </summary>
public float Speed { get; set; }
/// <summary>
/// Track angle in degrees True
/// </summary>
public float Course { get; set; }
/// <summary>
/// Date - 23rd of March 1994
/// </summary>
public DateTime UpdateDate { get; set; }
/// <summary>
/// Magnetic Variation
/// </summary>
public float MagneticVariation { get; set; }
/// <summary>
/// Magnetic Variation Unit
/// </summary>
public string MagneticVariationUnit { get; set; }
#endregion
#region Message parsing
public override void Parse(string[] messageParts) {
if (messageParts == null || messageParts.Length < 11) {
throw new ArgumentException("Invalid GPGGA message");
}
FixTime = messageParts[1].ToTimeSpan();
IsActive = messageParts[2].ToBoolean("A");
Latitude = messageParts[3].ToCoordinates(messageParts[4], CoordinateType.Latitude);
Longitude = messageParts[5].ToCoordinates(messageParts[6], CoordinateType.Longitude);
Speed = messageParts[7].ToFloat();
Course = messageParts[8].ToFloat();
UpdateDate = DateTime.ParseExact(messageParts[9], "ddMMyy", CultureInfo.InvariantCulture);
MagneticVariation = messageParts[10].ToFloat();
MagneticVariationUnit = messageParts[11];
}
#endregion
public override string ToString() {
return $"Latitude {Latitude} - Longitude {Longitude} - Speed {Speed}";
}
}
}

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.NmeaMessages {
public class GpvtgMessage : NmeaMessage {
#region Description
// $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48
// where:
// VTG Track made good and ground speed
// 054.7,T True track made good(degrees)
// 034.4,M Magnetic track made good
// 005.5,N Ground speed, knots
// 010.2,K Ground speed, Kilometers per hour
// *48 Checksum
#endregion
#region Properties
/// <summary>
/// True track made good(degrees)
/// </summary>
public float TrackDegrees { get; set; }
/// <summary>
/// Magnetic track made good
/// </summary>
public float MagneticTrack { get; set; }
/// <summary>
/// Ground speed, knots
/// </summary>
public float GroundSpeetKnots { get; set; }
/// <summary>
/// Ground speed, Kilometers per hour
/// </summary>
public float GroundSpeed { get; set; }
#endregion
#region Message parsing
public override void Parse(string[] messageParts) {
//$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K * 48
if (messageParts == null || messageParts.Length < 9) {
throw new ArgumentException("Invalid GPVTG message");
}
TrackDegrees = messageParts[1].ToFloat();
MagneticTrack = messageParts[3].ToFloat();
GroundSpeetKnots = messageParts[5].ToFloat();
GroundSpeed = messageParts[7].ToFloat();
}
#endregion
public override string ToString() {
return $"Speed {GroundSpeed} - Track level {TrackDegrees}";
}
}
}

View File

@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2.NmeaMessages {
public abstract class NmeaMessage {
public abstract void Parse(string[] messageParts);
}
}

27
NmeaParser.cs Normal file
View File

@@ -0,0 +1,27 @@
using GpsClient2.NmeaMessages;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2 {
public class NmeaParser {
/// <summary>
/// Parses a string to the NmeaMessage class.
/// </summary>
/// <param name="message">The nmea string that need to be parsed.</param>
/// <returns>Returns an NmeaMessage class. If it cannot parse it will return null.</returns>
public NmeaMessage Parse(string message) {
if (!message.StartsWith("$")) {
return null;
}
var messageParts = message.RemoveAfter("*").Split(',');
var classType = NmeaConstants.GetClassType(messageParts[0].TrimStart('$'));
var newInstance = (NmeaMessage)Activator.CreateInstance(classType);
newInstance.Parse(messageParts);
return newInstance;
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GpsClient2")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GpsClient2")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c62e7a96-f279-4390-a1ac-8facdd9e8c14")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

53
StringExtensions.cs Normal file
View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace GpsClient2 {
public static class StringExtensions {
private const string Pattern = @"(\d{2})(\d{2})(\d{2})[.\d{2}]?";
public static TimeSpan ToTimeSpan(this string inputString) {
var regexMatch = Regex.Match(inputString, Pattern);
return regexMatch.Groups.Count == 0 ? TimeSpan.Zero : new TimeSpan(0, int.Parse(regexMatch.Groups[1].Value), int.Parse(regexMatch.Groups[2].Value), int.Parse(regexMatch.Groups[3].Value));
}
public static double ToCoordinates(this string inputString, string cardinalDirection, CoordinateType coordinateType) {
if (string.IsNullOrEmpty(inputString)) return 0.0d;
var degreeCharacters = coordinateType == CoordinateType.Latitude ? 2 : 3;
var degree = inputString.Substring(0, degreeCharacters).ToDouble();
degree += inputString.Substring(degreeCharacters).ToDouble() / 60;
if (!double.IsNaN(degree) && (cardinalDirection == "S" || cardinalDirection == "W")) {
degree *= -1;
}
return degree;
}
public static int ToInteger(this string inputString) {
return !string.IsNullOrEmpty(inputString) ? int.Parse(inputString, CultureInfo.InvariantCulture) : 0;
}
public static double ToDouble(this string inputString) {
return !string.IsNullOrEmpty(inputString) ? double.Parse(inputString, CultureInfo.InvariantCulture) : double.NaN;
}
public static float ToFloat(this string inputString) {
return !string.IsNullOrEmpty(inputString) ? float.Parse(inputString, CultureInfo.InvariantCulture) : float.NaN;
}
public static bool ToBoolean(this string inputString, string validValue) {
return inputString == validValue;
}
public static string RemoveAfter(this string inputString, string charValue) {
var index = inputString.IndexOf(charValue, StringComparison.Ordinal);
return index > 0 ? inputString.Substring(0, index) : inputString;
}
}
}

View File

@@ -0,0 +1,85 @@
using GpsClient2.Model;
using System;
using System.Collections.Generic;
using System.Device.Location;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace GpsClient2 {
public class WindowsLocationApiGpsClient : BaseGpsClient {
#region Private Properties
private GeoCoordinateWatcher _watcher;
private DateTimeOffset? _previousReadTime;
#endregion
#region Constructors
public WindowsLocationApiGpsClient(WindowsLocationApiInfo connectionData) : base(GpsType.WindowsLocationApi, connectionData) {
}
public WindowsLocationApiGpsClient(BaseGpsInfo connectionData) : base(GpsType.WindowsLocationApi, connectionData) {
}
#endregion
#region Connect and Disconnect
public override bool Connect() {
var data = (WindowsLocationApiInfo)GpsInfo;
IsRunning = true;
OnGpsStatusChanged(GpsStatus.Connecting);
_watcher = new GeoCoordinateWatcher();
_watcher.PositionChanged += WatcherOnPositionChanged;
_watcher.StatusChanged += WatcherOnStatusChanged;
bool result;
try {
result = _watcher.TryStart(false, TimeSpan.FromMilliseconds(data.Timeout));
} catch {
Disconnect();
throw;
}
return result;
}
private void WatcherOnStatusChanged(object sender, GeoPositionStatusChangedEventArgs e) {
switch (e.Status) {
case GeoPositionStatus.Ready:
OnGpsStatusChanged(GpsStatus.Connected);
break;
case GeoPositionStatus.Initializing:
OnGpsStatusChanged(GpsStatus.Connecting);
break;
case GeoPositionStatus.NoData:
OnGpsStatusChanged(GpsStatus.Connecting);
break;
case GeoPositionStatus.Disabled:
OnGpsStatusChanged(GpsStatus.Disabled);
break;
}
}
private void WatcherOnPositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) {
if (_previousReadTime != null && GpsInfo.ReadFrequenty != 0 && e.Position.Timestamp.Subtract(new TimeSpan(0, 0, 0, 0, GpsInfo.ReadFrequenty)) <= _previousReadTime) return;
OnGpsDataReceived(new GpsDataEventArgs(e.Position.Location));
_previousReadTime = e.Position.Timestamp;
}
public override bool Disconnect() {
IsRunning = false;
_watcher.Stop();
_watcher.Dispose();
OnGpsStatusChanged(GpsStatus.Disabled);
return true;
}
#endregion
}
}

Binary file not shown.

Binary file not shown.