Added a more advanced example with combined messages

This commit is contained in:
Morten Nielsen 2020-07-22 10:37:52 -07:00 committed by GitHub
parent 168b2d0040
commit adef7b83c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -53,4 +53,140 @@ namespace NmeaParser.ArcGIS
```
### Combining multiple NMEA messages into a single location event
NMEA often happens in a burst of messages, which could be combined to one larger location object with more information available.
By relying on the time stamp in most of the messages, we can combine them all to get better metadata about the location.
```
using System;
using System.Linq;
using System.Threading.Tasks;
using Esri.ArcGISRuntime.Geometry;
using Esri.ArcGISRuntime.Location;
using NmeaParser;
using NmeaParser.Messages;
namespace NmeaParser.ArcGIS
{
public class NmeaLocationProvider : LocationDataSource
{
private readonly NmeaParser.NmeaDevice device;
private Gga lastGga;
private Rmc lastRmc;
private Gsa lastGsa;
private Gst lastGst;
public NmeaLocationProvider(NmeaParser.NmeaDevice device)
{
this.device = device;
device.MessageReceived += NmeaMessageReceived;
}
private void NmeaMessageReceived(object sender, NmeaMessageReceivedEventArgs e)
{
var message = e.Message;
bool newFix = false;
if (message is Rmc rmc && rmc.Active)
{
lastRmc = rmc;
newFix = true;
}
else if (message is Gga gga)
{
lastGga = gga;
newFix = true;
}
else if (message is Gst gst)
{
lastGst = gst;
newFix = true;
}
else if (message is Gsa gsa)
{
lastGsa = gsa;
}
else
{
return;
}
// We require the timestamps to match to raise them together. Gsa doesn't have a time stamp so just using latest for that
TimeSpan? timeOfFixMax = MaxTime(lastRmc?.FixTime.TimeOfDay, lastGga?.FixTime, lastGst?.FixTime);
TimeSpan? timeOfFixMin = MinTime(lastRmc?.FixTime.TimeOfDay, lastGga?.FixTime, lastGst?.FixTime);
if (newFix && timeOfFixMax == timeOfFixMin)
{
var location = NmeaLocation.Create(timeOfFixMax.Value, lastRmc, lastGga, lastGsa, lastGst);
if (location != null)
base.UpdateLocation(location);
}
}
private static TimeSpan? MaxTime(params TimeSpan?[] timeSpans) => timeSpans.Where(t => t != null).Max();
private static TimeSpan? MinTime(params TimeSpan?[] timeSpans) => timeSpans.Where(t => t != null).Min();
protected override Task OnStartAsync() => device.OpenAsync();
protected override Task OnStopAsync() => device.CloseAsync();
}
/// <summary>
/// Custom location class with the additional NMEA information associated with it
/// </summary>
public class NmeaLocation : Location
{
private NmeaLocation(DateTimeOffset? timestamp, MapPoint position, double horizontalAccuracy, double verticalAccuracy, double velocity, double course, bool isLastKnown)
: base(timestamp, position, horizontalAccuracy, verticalAccuracy, velocity, course, isLastKnown)
{
}
public static NmeaLocation Create(TimeSpan timeOfFix, Rmc rmc, Gga gga, Gsa gsa, Gst gst)
{
MapPoint position = null;
double horizontalAccuracy = double.NaN;
double verticalAccuracy = double.NaN;
double velocity = 0;
double course = 0;
// Prefer GGA over RMC for location
if (gga != null && gga.FixTime == timeOfFix)
{
if (double.IsNaN(gga.Altitude))
position = new MapPoint(gga.Longitude, gga.Latitude, SpatialReferences.Wgs84);
else
{
// Vertical id 115700 == ellipsoid reference system. Gga is geoid, but we subtract GeoidalSeparation to simplify
// vertical transformations from the simpler/better known ellipsoidal model
position = new MapPoint(gga.Longitude, gga.Latitude, gga.Altitude - gga.GeoidalSeparation, SpatialReference.Create(4326, 115700));
}
}
if (rmc != null && rmc.FixTime.TimeOfDay == timeOfFix)
{
if (position == null)
{
position = new MapPoint(rmc.Longitude, rmc.Latitude, SpatialReferences.Wgs84);
}
velocity = double.IsNaN(rmc.Speed) ? 0 : rmc.Speed;
course = double.IsNaN(rmc.Course) ? 0 : rmc.Course;
}
if (gst != null && gst.FixTime == timeOfFix)
{
verticalAccuracy = gst.SigmaHeightError;
horizontalAccuracy = gst.SemiMajorError;
}
if (position == null)
return null;
var location = new NmeaLocation(DateTimeOffset.UtcNow.Date.Add(timeOfFix), position, horizontalAccuracy, verticalAccuracy, velocity, course, false);
location.Rmc = rmc;
location.Gga = gga;
location.Gsa = gsa;
location.Gst = gst?.FixTime == timeOfFix ? gst : null;
return location;
}
public Rmc Rmc { get; private set; }
public Gga Gga { get; private set; }
public Gsa Gsa { get; private set; }
public Gst Gst { get; private set; }
public int NumberOfSatellites => Gga?.NumberOfSatellites ?? -1;
public double Hdop => Gsa?.Hdop ?? Gga?.Hdop ?? double.NaN;
public double Pdop => Gsa?.Pdop ?? double.NaN;
public double Vdop => Gsa?.Vdop ?? double.NaN;
}
}
```
![Screenshot](https://user-images.githubusercontent.com/1378165/73328707-95990e80-420f-11ea-85a7-43149e29bd21.png)