NmeaParser

repository·main·Indexed 42 days ago

https://github.com/dotmorten/nmeaparser

A C# library for reading and parsing NMEA data message streams from GNSS and proprietary devices. It supports Windows, iOS, and Android, with features including automatic merging of multi-sentence messages, custom message extensibility, and support for various input devices such as System.IO.Stream, serial ports, and Bluetooth. The library is available via NuGet as SharpGIS.NmeaParser.

Tokens
8.9K
Snippets
23
Records
34
Agent score
54%

What's inside NmeaParser

  1. Overview of NmeaParser features

    main

    NmeaParser is a library designed for reading and parsing NMEA data message streams. It is compatible with Windows Universal, Windows Desktop/.NET, and .NET for iOS and Android.

    Supported Message Types

    • GNSS: BOD, GGA, GLL, GNS, GSA, GST, GSV, RMB, RMA, RMC, RTE, VTG, ZDA
    • Garmin Proprietary: PGRME, PGRMZ
    • Trimble Laser Range Finder: PTNLA, PTNLB
    • TruePulse Laser Range Finder: PLTIT

    Key Capabilities

    • Automatic Merging: Multi-sentence messages are automatically merged for easier consumption.
    • Extensibility: Supports custom NMEA messages.
    • Input Device Support:
      • System.IO.Stream (all platforms)
      • NMEA log file emulation (all platforms)
      • Serial Device (.NET Framework, .NET Core on Windows/Linux/Mac, and Windows Universal)
      • Bluetooth (Windows Universal and Android; .NET Core/.NET Framework can use Bluetooth via SerialPortDevice)
  2. Overview of NMEA Parser

    main

    NMEA Parser is a library designed for reading and parsing NMEA data message streams. It provides a unified way to connect to and listen for NMEA messages from various devices across multiple platforms, including:

    • Windows Universal
    • Windows Desktop / .NET
    • .NET iOS
    • .NET Android
  3. Understand platform-specific NMEA device implementations

    main

    While the core library reference is identical across all platforms, some platforms include additional NMEA device implementations to simplify usage.

    Key considerations:

    • Naming: Devices may share the same name across platforms but might have different underlying implementations.
    • Arguments: Platform-specific devices may require different arguments tailored to that environment's hardware or OS capabilities.

    For a detailed explanation of how these differences affect your implementation, refer to the "Getting Started" section in the project documentation.

  4. Use GnssMonitor for robust ArcGIS location data

    main

    NMEA messages often arrive in bursts. Instead of parsing individual messages manually, use the NmeaParser.Gnss.GnssMonitor class to aggregate multiple NMEA messages into a single, robust location event. This approach provides access to combined data from multiple satellite systems and more complete information (like altitude and accuracy).

    Key Advantages:

    • Aggregation: Combines fragmented NMEA messages into a single state.
    • Robustness: Provides HorizontalError, VerticalError, and FixTime.
    • Lifecycle Management: You can configure the provider to automatically start and stop the underlying NmeaDevice when the ArcGIS data source starts or stops.

    Implementation Details:

    • Use the GnssMonitor.LocationChanged and GnssMonitor.LocationLost events to trigger updates in your LocationDataSource.
    • When mapping GnssMonitor values to ArcGIS Location objects:
      • Velocity: Convert speed (typically knots) to meters per second (multiply by 0.51444444).
      • Position: Use m_gnssMonitor.Altitude with a WGS84 ellipsoid height if available, otherwise fallback to standard SpatialReferences.Wgs84.
      • Course: Since ArcGIS does not allow NaN for course, cache the lastCourse value to use as a fallback.
    // Example of initializing the robust data source
    public class NmeaLocationDataSource : Esri.ArcGISRuntime.Location.LocationDataSource
    {
        private readonly GnssMonitor m_gnssMonitor;
        private readonly bool m_startStopDevice;
    
        public NmeaLocationDataSource(NmeaParser.NmeaDevice device, bool startStopDevice = true) 
            : this(new GnssMonitor(device), startStopDevice)
        { 
        }
    
        public NmeaLocationDataSource(NmeaParser.Gnss.GnssMonitor monitor, bool startStopDevice = true)
        {
            this.m_gnssMonitor = monitor;
            this.m_startStopDevice = startStopDevice;
        }
    }
  5. Using NTRIP for DGPS accuracy improvement

    main
    You can improve GPS position accuracy by using data from a nearby NTRIP (Networked Transport of RTCM via Internet Protocol) server. If your hardware device (such as a Serial or Bluetooth GPS device) supports writing to it, you can stream NTRIP data directly to that device to enable Differential GPS (DGPS).
  6. Handle multi-sentence messages in v2.0+

    main

    Multi-sentence messages (such as GSV messages) are now automatically merged by the parser.

    Key changes to behavior:

    • You will no longer receive individual events for 'message 1 of N'.
    • An event is only triggered once all constituent messages have been combined.
    • Partially-received multi-messages that do not complete are discarded.
    • Event arguments previously used to manage manual message merging have been removed.
  7. Create a basic location provider for ArcGIS Runtime SDK

    main

    To display NMEA device data on an ArcGIS MapView, you can implement a custom LocationDataSource that listens to NmeaDevice.MessageReceived events. In a basic implementation, you typically look for NmeaParser.Messages.Rmc messages to extract latitude, longitude, speed, and course.

    Note: ArcGIS Runtime has a limitation where course cannot be NaN. You should handle this by defaulting to 0 if the NMEA message provides an invalid course value.

    Implementation Steps:

    1. Inherit from Esri.ArcGISRuntime.Location.LocationDataSource.
    2. Subscribe to the device.MessageReceived event in the constructor.
    3. In the event handler, check if the message is an active Rmc message.
    4. Call base.UpdateLocation(...) with a new Esri.ArcGISRuntime.Location.Location object.
    5. Override OnStartAsync to call device.OpenAsync() and OnStopAsync to call device.CloseAsync().
    NmeaParser.NmeaDevice device = new NmeaParser.NmeaFileDevice("NmeaSampleData.txt");
    mapView.LocationDisplay.DataSource = new NmeaLocationProvider(device);
    mapView.LocationDisplay.InitialZoomScale = 20000;
    mapView.LocationDisplay.IsEnabled = true;
  8. Enable serial communication capability in Windows Universal apps

    main

    To use the NMEA Parser with a serial device in a Windows 10 Universal (UWP) application, you must declare the serialcommunication capability in your app's manifest. Open package.appxmanifest in a text editor and add the following XML block within the <Capabilities> section:

    <DeviceCapability Name="serialcommunication" > 
      <Device Id="any"> 
        <Function Type="name:serialPort"/> 
      </Device> 
    </DeviceCapability> 
  9. Connect to a Bluetooth device using NmeaParser.BluetoothDevice

    main

    To interface with a Bluetooth device in a UWP environment, follow these steps:

    1. Pair the device: Ensure your Bluetooth device is already paired with your Windows device.
    2. Find the device: Use RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort) and DeviceInformation.FindAllAsync to locate available serial port services.
    3. Select the device: Filter the list of DeviceInformation objects (e.g., by name).
    4. Initialize the parser: Use RfcommDeviceService.FromIdAsync to get the service, then instantiate NmeaParser.BluetoothDevice passing the rfcommService.
    5. Handle messages: Subscribe to the MessageReceived event to process incoming NMEA data.
    6. Open the connection: Call await rangeFinder.OpenAsync() to start receiving data.
    // Get list of devices
    string serialDeviceType = RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort);
    var devices = await DeviceInformation.FindAllAsync(serialDeviceType);
    
    // Select device by name (e.g., TruePulse 360B Laser Range Finder)
    var TruePulse360B = devices.Where(t => t.Name.StartsWith("TP360B-")).FirstOrDefault();
    
    // Get service
    RfcommDeviceService rfcommService = await RfcommDeviceService.FromIdAsync(TruePulse360B.Id);
    if (rfcommService != null)
    {
    	var rangeFinder = new NmeaParser.BluetoothDevice(rfcommService);
    	rangeFinder.MessageReceived += device_NmeaMessageReceived;
    	await rangeFinder.OpenAsync();		
    }
    
    // ...
    
    private void device_NmeaMessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs args)
    {
       // called when a message is received
    }
  10. Create a Serial Port device in .NET Framework

    main

    To parse NMEA data from a physical serial port in a .NET Framework environment, wrap a System.IO.Ports.SerialPort instance within an NmeaParser.SerialPortDevice.

    Follow these steps:

    1. Initialize a System.IO.Ports.SerialPort with the correct portname (e.g., "COM3") and baudrate (e.g., 9600).
    2. Instantiate NmeaParser.SerialPortDevice by passing the serial port instance to its constructor.
    3. Subscribe to the MessageReceived event to handle incoming NMEA messages.
    4. Call OpenAsync() to begin listening for data.

    Incoming messages are handled via the OnNmeaMessageReceived event handler, which provides NmeaParser.NmeaDevice as the sender and NmeaParser.NmeaMessageReceivedEventArgs containing the message data.

    string portname = "COM3"; // Change to match the name of the port your device is connected to
    int baudrate = 9600; // Change to the baud rate your device communicates at (usually specified in the manual)
    var port = new System.IO.Ports.SerialPort(portname, baudrate);
    var device = new NmeaParser.SerialPortDevice(port);
    
    // Subscribe to the event
    device.MessageReceived += OnNmeaMessageReceived;
    
    // Start listening
    device.OpenAsync();
    
    // ...
    
    private void OnNmeaMessageReceived(NmeaParser.NmeaDevice sender, NmeaParser.NmeaMessageReceivedEventArgs args)
    {
       // called when a message is received
    }
  11. Implement an NTRIP client to stream data to NMEA devices

    main

    To use NTRIP, you need to connect to an NTRIP server, retrieve a source table to find available streams, and then subscribe to a specific stream's data. Once connected, you can listen for data events and forward the received byte arrays to your NMEA-compatible device.

    string hostname = "esricaster.esri.com"; // Replace with a server near you
    int port = 2101; // Port for the ntrip server
    var client = new Ntrip.Client(hostname, port);
    
    // 1. Get the source table from the server
    var table = client.GetSourceTable();
    
    // 2. Pick a stream (e.g., the first NtripStream found)
    var str = table.OfType<NtripStream>().First();
    
    // 3. Listen for data and forward it to the NMEA device
    client.DataReceived += (sender, ntripData) => {
       nmeaDevice.WriteAsync(ntripData, 0, ntripData.Length);   
    };
    
    // 4. Connect to the specific mountpoint
    client.Connect(str.Mountpoint);