• Stars
    star
    220
  • Rank 180,422 (Top 4 %)
  • Language
    Go
  • License
    MIT License
  • Created over 9 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A NMEA parser library in pure Go

go-nmea

CI Go Report Card Coverage Status GoDoc

This is a NMEA library for the Go programming language (Golang).

Features

  • Parse individual NMEA 0183 sentences
  • Support for sentences with NMEA 4.10 "TAG Blocks"
  • Register custom parser for unsupported sentence types
  • User-friendly MIT license

Installing

To install go-nmea use go get:

go get github.com/adrianmo/go-nmea

This will then make the github.com/adrianmo/go-nmea package available to you.

Staying up to date

To update go-nmea to the latest version, use go get -u github.com/adrianmo/go-nmea.

Supported sentences

Sentence with link is supported by this library. NMEA0183 sentences list is based on IEC 61162-1:2016 (Edition 5.0 2016-08) table of contents.

Sentence Description References
AAM Waypoint arrival alarm gpsd
ABK AIS addressed and binary broadcast acknowledgement
ABM AIS addressed binary and safety related message
ACA AIS channel assignment message
ACK Acknowledge alarm
ACN Alert command
ACS AIS channel management information source
AIR AIS interrogation request
AKD Acknowledge detail alarm condition
ALA Report detailed alarm condition
ALC Cyclic alert list
ALF Alert sentence
ALR Set alarm state
APB Heading/track controller (autopilot) sentence B gpsd
ARC Alert command refused
BBM AIS broadcast binary message
BEC Bearing and distance to waypoint, Dead reckoning 1
BOD Bearing origin to destination gpsd
BWC Bearing and distance to waypoint, Great circle gpsd
BWR Bearing and distance to waypoint, Rhumb line gpsd
BWW Bearing waypoint to waypoint gpsd
CUR Water current layer, Multi-layer water current data
DBK Depth Below Keel (obsolete, use DPT instead) gpsd
DBS Depth below transducer gpsd
DBT Depth below transducer gpsd
DDC Display dimming control
DOR Door status detection
DPT Depth gpsd
DSC Digital selective calling information
DSE Expanded digital selective calling
DTM Datum reference gpsd
EPV Command or report equipment property value
ETL Engine telegraph operation status
EVE General event message
FIR Fire detection
FSI Frequency set information
GBS GNSS satellite fault detection
GEN Generic binary information
GFA GNSS fix accuracy and integrity
GGA Global positioning system (GPS) fix data 1
GLL Geographic position, Latitude/longitude 1
GNS GNSS fix data gpsd
GRS GNSS range residuals
GSA GNSS DOP and active satellites 1
GST GNSS pseudorange noise statistics
GSV GNSS satellites in view 1
HBT Heartbeat supervision sentence
HCR Heading correction report
HDG Heading, deviation and variation gpsd
HDM Heading - Magnetic gpsd
HDT Heading true gpsd
HMR Heading monitor receive
HMS Heading monitor set
HRM heel angle, roll period and roll amplitude measurement device
HSC Heading steering command gpsd
HSS Hull stress surveillance systems
HTC Heading/track control command
HTD Heading /track control data
LR1 AIS long-range reply sentence 1
LR2 AIS long-range reply sentence 2
LR3 AIS long-range reply sentence 3
LRF AIS long-range function
LRI AIS long-range interrogation
MDA Meteorological Composite gpsd
MTA Air Temperature (obsolete, use XDR instead)
MOB Man over board notification
MSK MSK receiver interface
MSS MSK receiver signal status
MTW Water temperature gpsd
MWD Wind direction and speed 1
MWV Wind speed and angle gpsd
NAK Negative acknowledgement
NRM NAVTEX receiver mask
NRX NAVTEX received message
NSR Navigation status report
OSD Own ship data gpsd
POS Device position and ship dimensions report or configuration command
PRC Propulsion remote control status
RLM Return link message
RMA Recommended minimum specific LORAN-C data
RMB Recommended minimum navigation information gpsd
RMC Recommended minimum specific GNSS data 1
ROR Rudder order status
ROT Rate of turn gpsd
RRT Report route transfer
RPM Revolutions gpsd
RSA Rudder sensor angle gpsd
RSD Radar system data gpsd
RTE Routes 1
SFI Scanning frequency information
SMI SafetyNET Message, All Ships/NavArea
SM2 SafetyNET Message, Coastal Warning Area
SM3 SafetyNET Message, Circular Area address
SM4 SafetyNET Message, Rectangular Area Address
SMB IMO SafetyNET Message Body
SPW Security password sentence
SSD AIS ship static data
STN Multiple data ID
THS True heading and status 1
TLB Target label
TLL Target latitude and longitude gpsd
TRC Thruster control data
TRL AIS transmitter-non-functioning log
TRD Thruster response data
TTD Tracked target data
TTM Tracked target message gpsd
TUT Transmission of multi-language text
TXT Text transmission NMEA
UID User identification code transmission
VBW Dual ground/water speed gpsd
VDM AIS VHF data-link message gpsd
VDO AIS VHF data-link own-vessel report gpsd
VDR Set and drift gpsd
VER Version
VHW Water speed and heading 1
VLW Dual ground/water distance gpsd
VPW Speed measured parallel to wind gpsd
VSD AIS voyage static data
VTG Course over ground and ground speed 1
VWR Relative Wind Speed and Angle gpsd
VWT True Wind Speed and Angle
WAT Water level detection
WCV Waypoint closure velocity
WNC Distance waypoint to waypoint
WPL Waypoint location 1
XDR Transducer measurements gpsd
XTE Cross-track error, measured
XTR Cross-track error, dead reckoning
ZDA Time and date 1
ZDL Time and distance to variable point
ZFO UTC and time from origin waypoint
ZTG UTC and time to destination waypoint
Proprietary sentence type Description References
PNG Transfer NMEA2000 frame as NMEA0183 sentence (ShipModul MiniPlex-3) 1
PCDIN Transfer NMEA2000 frame as NMEA0183 sentence (SeaSmart.Net Protocol) 1
PGRME Estimated Position Error (Garmin proprietary sentence) 1
PHTRO Vessel pitch and roll (Xsens IMU/VRU/AHRS)
PMTK001 Acknowledgement of previously sent command/packet 1
PRDID Vessel pitch, roll and heading (Xsens IMU/VRU/AHRS)
PSKPDPT Depth of Water for multiple transducer installation
PSONCMS Quaternion, acceleration, rate of turn, magnetic field, sensor temperature (Xsens IMU/VRU/AHRS)

If you need to parse a message that contains an unsupported sentence type you can implement and register your own message parser and get yourself unblocked immediately. Check the example below to know how to implement and register a custom message parser. However, if you think your custom message parser could be beneficial to other users we encourage you to contribute back to the library by submitting a PR and get it included in the list of supported sentences.

Examples

Built-in message parsing

package main

import (
	"fmt"
	"log"
	"github.com/adrianmo/go-nmea"
)

func main() {
	sentence := "$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70"
	s, err := nmea.Parse(sentence)
	if err != nil {
		log.Fatal(err)
	}
	if s.DataType() == nmea.TypeRMC {
		m := s.(nmea.RMC)
		fmt.Printf("Raw sentence: %v\n", m)
		fmt.Printf("Time: %s\n", m.Time)
		fmt.Printf("Validity: %s\n", m.Validity)
		fmt.Printf("Latitude GPS: %s\n", nmea.FormatGPS(m.Latitude))
		fmt.Printf("Latitude DMS: %s\n", nmea.FormatDMS(m.Latitude))
		fmt.Printf("Longitude GPS: %s\n", nmea.FormatGPS(m.Longitude))
		fmt.Printf("Longitude DMS: %s\n", nmea.FormatDMS(m.Longitude))
		fmt.Printf("Speed: %f\n", m.Speed)
		fmt.Printf("Course: %f\n", m.Course)
		fmt.Printf("Date: %s\n", m.Date)
		fmt.Printf("Variation: %f\n", m.Variation)
	}
}

Output:

$ go run main/main.go

Raw sentence: $GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70
Time: 22:05:16.0000
Validity: A
Latitude GPS: 5133.8200
Latitude DMS: 51° 33' 49.200000"
Longitude GPS: 042.2400
Longitude DMS: 0° 42' 14.400000"
Speed: 173.800000
Course: 231.800000
Date: 13/06/94
Variation: -4.200000

Customize sentence parser

Parser logic can be customized by creating nmea.SentenceParser instance and by providing callback implementations.

p := nmea.SentenceParser{
    CustomParsers: nil,
    ParsePrefix:   nil,
    CheckCRC:      nil,
    OnTagBlock:    nil,
}
s, err := p.Parse("$GPRMC,220516,A,5133.82,N,00042.24,W,173.8,231.8,130694,004.2,W*70")

TAG Blocks

NMEA 4.10 TAG Block values can be accessed via the message's TagBlock struct:

package main

import (
	"fmt"
	"log"
	"time"
	"github.com/adrianmo/go-nmea"
)

func main() {
	sentence := "\\s:Satelite_1,c:1553390539*62\\!AIVDM,1,1,,A,13M@ah0025QdPDTCOl`K6`nV00Sv,0*52"
	s, err := nmea.Parse(sentence)
	if err != nil {
		log.Fatal(err)
	}
	parsed := s.(nmea.VDMVDO)
	fmt.Printf("TAG Block timestamp: %v\n", time.Unix(parsed.TagBlock.Time, 0))
	fmt.Printf("TAG Block source:    %v\n", parsed.TagBlock.Source)
}

Output (locale/time zone dependent):

$  go run main/main.go

TAG Block timestamp: 2019-03-24 14:22:19 +1300 NZDT
TAG Block source:    Satelite_1

Custom message parsing

If you need to parse a message not supported by the library you can implement your own message parsing. The following example implements a parser for the hypothetical XYZ NMEA sentence type.

package main

import (
	"fmt"

	"github.com/adrianmo/go-nmea"
)

// A type to hold the parsed record
type XYZType struct {
	nmea.BaseSentence
	Time    nmea.Time
	Counter int64
	Label   string
	Value   float64
}

func main() {
	// Do this once it will error if you register the same type multiple times
	err := nmea.RegisterParser("XYZ", func(s nmea.BaseSentence) (nmea.Sentence, error) {
		// This example uses the package builtin parsing helpers
		// you can implement your own parsing logic also
		p := nmea.NewParser(s)
		return XYZType{
			BaseSentence: s,
			Time:         p.Time(0, "time"),
			Label:        p.String(1, "label"),
			Counter:      p.Int64(2, "counter"),
			Value:        p.Float64(3, "value"),
		}, p.Err()
	})

	if err != nil {
		panic(err)
	}

	sentence := "$00XYZ,220516,A,23,5133.82,W*42"
	s, err := nmea.Parse(sentence)
	if err != nil {
		panic(err)
	}

	switch m := s.(type) {
	case XYZType:
		fmt.Printf("Raw sentence: %v\n", m)
		fmt.Printf("Time: %s\n", m.Time)
		fmt.Printf("Label: %s\n", m.Label)
		fmt.Printf("Counter: %d\n", m.Counter)
		fmt.Printf("Value: %f\n", m.Value)
	default:
		panic("Could not parse XYZ sentence")
	}
}

Output:

$ go run main/main.go

Raw sentence: $AAXYZ,220516,A,23,5133.82,W*42
Time: 22:05:16.0000
Label: A
Counter: 23
Value: 5133.820000

Message parsing with optional values

Some messages have optional fields. By default, omitted numeric values are set to 0. In situations where you need finer control to distinguish between an undefined value and an actual 0, you can register types overriding existing sentences, using nmea.Int64 and nmea.Float64 instead of int64 and float64. The matching parsing methods are (*Parser).NullInt64 and (*Parser).NullFloat64. Both nmea.Int64 and nmea.Float64 contains a numeric field Value which is defined only if the field Valid is true.

See below example for a modified VTG sentence parser:

package main

import (
	"fmt"

	"github.com/adrianmo/go-nmea"
)

// VTG represents track & speed data.
// http://aprs.gids.nl/nmea/#vtg
type VTG struct {
	nmea.BaseSentence
	TrueTrack        nmea.Float64
	MagneticTrack    nmea.Float64
	GroundSpeedKnots nmea.Float64
	GroundSpeedKPH   nmea.Float64
}

func main() {
	nmea.MustRegisterParser("VTG", func(s nmea.BaseSentence) (nmea.Sentence, error) {
		p := nmea.NewParser(s)
		return VTG{
			BaseSentence:     s,
			TrueTrack:        p.NullFloat64(0, "true track"),
			MagneticTrack:    p.NullFloat64(2, "magnetic track"),
			GroundSpeedKnots: p.NullFloat64(4, "ground speed (knots)"),
			GroundSpeedKPH:   p.NullFloat64(6, "ground speed (km/h)"),
		}, p.Err()
	})

	sentence := "$GPVTG,140.88,T,,M,8.04,N,14.89,K,D*05"
	s, err := nmea.Parse(sentence)
	if err != nil {
		panic(err)
	}

	m, ok := s.(VTG)
	if !ok {
		panic("Could not parse VTG sentence")
	}
	fmt.Printf("Raw sentence: %v\n", m)
	fmt.Printf("TrueTrack: %v\n", m.TrueTrack)
	fmt.Printf("MagneticTrack: %v\n", m.MagneticTrack)
	fmt.Printf("GroundSpeedKnots: %v\n", m.GroundSpeedKnots)
	fmt.Printf("GroundSpeedKPH: %v\n", m.GroundSpeedKPH)
}

Output:

$ go run main/main.go

Raw sentence: $GPVTG,140.88,T,,M,8.04,N,14.89,K,D*05
TrueTrack: {140.88 true}
MagneticTrack: {0 false}
GroundSpeedKnots: {8.04 true}
GroundSpeedKPH: {14.89 true}

Contributing

Please feel free to submit issues or fork the repository and send pull requests to update the library and fix bugs, implement support for new sentence types, refactor code, etc.

License

Check LICENSE.