Improved location datasource to better handle multiple gps systems

This commit is contained in:
Morten Nielsen 2020-07-28 20:14:40 -07:00
parent 3c2b9f2851
commit 9a71ca4db6
4 changed files with 117 additions and 102 deletions

View file

@ -39,6 +39,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp.NetCore", "Sample
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
UnitTests\NmeaParser.Tests\NmeaParser.Tests.projitems*{73efb2ef-de40-46c4-9685-745a9815c0d2}*SharedItemsImports = 5
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

View file

@ -0,0 +1,114 @@
using Esri.ArcGISRuntime.Geometry;
using System;
using System.Threading.Tasks;
namespace SampleApp.WinDesktop
{
public class NmeaLocationDataSource : Esri.ArcGISRuntime.Location.LocationDataSource
{
private NmeaParser.NmeaDevice device;
private double m_Accuracy = 0;
private double m_altitude = double.NaN;
private double m_speed = 0;
private double m_course = 0;
private bool supportGNMessages; // If device detect GN* messages, ignore all other Talker ID
private bool supportGGaMessages; //If device support GGA, ignore RMC for location
public NmeaLocationDataSource(NmeaParser.NmeaDevice device)
{
this.device = device;
if(device != null)
device.MessageReceived += device_MessageReceived;
}
void device_MessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs e)
{
var message = e.Message;
ParseMessage(message);
}
public void ParseMessage(NmeaParser.Messages.NmeaMessage message)
{
bool isNewFix = false;
bool lostFix = false;
double lat = 0;
double lon = 0;
if (message.TalkerId == NmeaParser.Talker.GlobalNavigationSatelliteSystem)
supportGNMessages = true;
else if(supportGNMessages && message.TalkerId != NmeaParser.Talker.GlobalNavigationSatelliteSystem)
return; // If device supports combined GN* messages, ignore non-GN messages
if (message is NmeaParser.Messages.Garmin.Pgrme rme)
{
m_Accuracy = rme.HorizontalError;
}
else if(message is NmeaParser.Messages.Gst gst)
{
Gst = gst;
m_Accuracy = Math.Sqrt(Gst.SigmaLatitudeError * Gst.SigmaLatitudeError + Gst.SigmaLongitudeError * Gst.SigmaLongitudeError);
}
else if (message is NmeaParser.Messages.Rmc rmc)
{
Rmc = rmc;
if (Rmc.Active)
{
m_speed = double.IsNaN(Rmc.Speed) ? Rmc.Speed : 0;
if (!double.IsNaN(Rmc.Course))
m_course = Rmc.Course;
lat = Rmc.Latitude;
lon = Rmc.Longitude;
}
else
{
lostFix = true;
}
isNewFix = !supportGGaMessages;
}
else if (message is NmeaParser.Messages.Gga gga)
{
supportGGaMessages = true;
if (gga.Quality != NmeaParser.Messages.Gga.FixQuality.Invalid)
{
lat = Rmc.Latitude;
lon = Rmc.Longitude;
m_altitude = gga.Altitude + gga.GeoidalSeparation; //Convert to ellipsoidal height
}
if (gga.Quality != NmeaParser.Messages.Gga.FixQuality.Invalid || gga.Quality == NmeaParser.Messages.Gga.FixQuality.Estimated)
{
lostFix = true;
}
isNewFix = true;
}
else if (message is NmeaParser.Messages.Gsa gsa)
{
Gsa = gsa;
}
if (isNewFix)
{
base.UpdateLocation(new Esri.ArcGISRuntime.Location.Location(
!double.IsNaN(m_altitude) ? new MapPoint(lon, lat, m_altitude, wgs84_ellipsoidHeight) : new MapPoint(lon, lat, SpatialReferences.Wgs84),
m_Accuracy, m_speed, m_course, lostFix));
}
}
private static SpatialReference wgs84_ellipsoidHeight = SpatialReference.Create(4326, 115700);
protected override Task OnStartAsync()
{
if (device != null)
return this.device.OpenAsync();
else
return System.Threading.Tasks.Task<bool>.FromResult(true);
}
protected override Task OnStopAsync()
{
m_Accuracy = double.NaN;
if(this.device != null)
return this.device.CloseAsync();
else
return System.Threading.Tasks.Task<bool>.FromResult(true);
}
public NmeaParser.Messages.Gsa Gsa { get; private set; }
public NmeaParser.Messages.Gga Gga { get; private set; }
public NmeaParser.Messages.Rmc Rmc { get; private set; }
public NmeaParser.Messages.Gst Gst { get; private set; }
}
}

View file

@ -1,100 +0,0 @@
using Esri.ArcGISRuntime.Geometry;
using System;
using System.Threading.Tasks;
namespace SampleApp.WinDesktop
{
public class NmeaLocationProvider : Esri.ArcGISRuntime.Location.LocationDataSource
{
private NmeaParser.NmeaDevice device;
double m_Accuracy = 0;
double m_altitude = double.NaN;
double m_speed = 0;
double m_course = 0;
public NmeaLocationProvider(NmeaParser.NmeaDevice device)
{
this.device = device;
if(device != null)
device.MessageReceived += device_MessageReceived;
}
void device_MessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs e)
{
var message = e.Message;
ParseMessage(message);
}
public void ParseMessage(NmeaParser.Messages.NmeaMessage message)
{
bool isNewFix = false;
bool lostFix = false;
double lat = 0;
double lon = 0;
if (message is NmeaParser.Messages.Garmin.Pgrme)
{
m_Accuracy = ((NmeaParser.Messages.Garmin.Pgrme)message).HorizontalError;
}
else if(message is NmeaParser.Messages.Gst)
{
Gst = ((NmeaParser.Messages.Gst)message);
m_Accuracy = Math.Sqrt(Gst.SigmaLatitudeError * Gst.SigmaLatitudeError + Gst.SigmaLongitudeError * Gst.SigmaLongitudeError);
}
else if(message is NmeaParser.Messages.Gga)
{
Gga = ((NmeaParser.Messages.Gga)message);
isNewFix = Gga.Quality != NmeaParser.Messages.Gga.FixQuality.Invalid;
lostFix = !isNewFix;
m_altitude = Gga.Altitude;
lat = Gga.Latitude;
lon = Gga.Longitude;
}
else if (message is NmeaParser.Messages.Rmc)
{
Rmc = (NmeaParser.Messages.Rmc)message;
if (Rmc.Active)
{
isNewFix = true;
m_speed = Rmc.Speed;
m_course = Rmc.Course;
lat = Rmc.Latitude;
lon = Rmc.Longitude;
}
else lostFix = true;
}
else if (message is NmeaParser.Messages.Gsa)
{
Gsa = (NmeaParser.Messages.Gsa)message;
}
if (isNewFix)
{
base.UpdateLocation(new Esri.ArcGISRuntime.Location.Location(new MapPoint(lon, lat, m_altitude, SpatialReferences.Wgs84), m_Accuracy, m_speed, m_course, false));
}
else if (lostFix)
{
}
}
protected override Task OnStartAsync()
{
if (device != null)
return this.device.OpenAsync();
else
return System.Threading.Tasks.Task<bool>.FromResult(true);
}
protected override Task OnStopAsync()
{
m_Accuracy = double.NaN;
if(this.device != null)
return this.device.CloseAsync();
else
return System.Threading.Tasks.Task<bool>.FromResult(true);
}
public NmeaParser.Messages.Gsa Gsa { get; private set; }
public NmeaParser.Messages.Gga Gga { get; private set; }
public NmeaParser.Messages.Rmc Rmc { get; private set; }
public NmeaParser.Messages.Gst Gst { get; private set; }
}
}

View file

@ -28,7 +28,7 @@ namespace SampleApp.WinDesktop
}
mapView.LocationDisplay.InitialZoomScale = 5000;
mapView.LocationDisplay.AutoPanMode = Esri.ArcGISRuntime.UI.LocationDisplayAutoPanMode.Navigation;
mapView.LocationDisplay.AutoPanMode = Esri.ArcGISRuntime.UI.LocationDisplayAutoPanMode.Recenter;
}
public NmeaDevice NmeaDevice
@ -51,7 +51,7 @@ namespace SampleApp.WinDesktop
mapView.LocationDisplay.IsEnabled = false;
if (newDevice != null)
{
mapView.LocationDisplay.DataSource = new NmeaLocationProvider(newDevice);
mapView.LocationDisplay.DataSource = new NmeaLocationDataSource(newDevice);
mapView.LocationDisplay.IsEnabled = true;
}
}