NMEA Ruby Gem (nmea_plus)
NMEA Plus is a Ruby gem for parsing and decoding "GPS" messages: NMEA, AIS, and any other similar formats of short messaging typically used by marine equipment. It provides convenient access (by name) to the fields of each message type, and a stream reader designed for use with Ruby Blocks.
Install
gem install nmea_plus
Example
In its most basic use, you can decode messages as follows:
require 'nmea_plus'
decoder = NMEAPlus::Decoder.new
= decoder.parse("$GPGLL,4916.45,N,12311.12,W,225444,A*00")
# message data -- specific to this message type
puts .latitude # prints 49.27416666666666666666
puts .longitude # prints -123.18533333333333333
puts .fix_time # prints <today's date> 22:54:44 <your local time zone offset>
puts .valid? # prints true
puts .faa_mode # prints nil
# metadata
puts .checksum_ok? # prints false -- because this checksum is made up
puts .original # prints "$GPGLL,4916.45,N,12311.12,W,225444,A*00"
puts .data_type # prints "GPGLL" -- what was specified in the message
puts .interpreted_data_type # prints "GLL" -- the actual container used
# metadata that applies to multipart messages (also works for single messages)
puts . # prints true
puts .all_checksums_ok? # prints false -- checksum is still made up
# safer way to do what we did above
if "GPGLL" == .data_type # Alternately, if "GLL" == message.interpreted_data_type
puts .latitude # prints 49.27416666666666666666
puts .longitude # prints -123.18533333333333333
puts .fix_time # prints <today's date> 22:54:44 <your local time zone offset>
puts .valid? # prints true
puts .faa_mode # prints nil
end
Of course, decoding in practice is more complex than that. Some messages can have multiple parts, and AIS messages have their own complicated payload. NMEAPlus provides a SourceDecoder object that operates on IO objects (anything with each_line
support) -- a File, SerialPort, etc. You can iterate over each message (literally each_message
), or receive only fully assembled multipart messages by iterating over each_complete_message
.
require 'nmea_plus'
input1 = "!AIVDM,2,1,0,A,58wt8Ui`g??r21`7S=:22058<v05Htp000000015>8OA;0sk,0*7B"
input2 = "!AIVDM,2,2,0,A,eQ8823mDm3kP00000000000,2*5D"
io_source = StringIO.new("#{input1}\n#{input2}") # source decoder works on any IO object
source_decoder = NMEAPlus::SourceDecoder.new(io_source)
source_decoder. do ||
puts # prints true -- the full message set has good checksums
puts # prints true -- taken care of by each_complete_message
if "AIVDM" == .data_type
puts .ais. # prints 5
puts .ais.repeat_indicator # prints 0
puts .ais.source_mmsi # prints 603916439
puts .ais.ais_version # prints 0
puts .ais.imo_number # prints 439303422
puts .ais.callsign.strip # prints "ZA83R"
puts .ais.name.strip # prints "ARCO AVON"
puts .ais.ship_cargo_type # prints 69
puts .ais.ship_dimension_to_bow # prints 113
puts .ais.ship_dimension_to_stern # prints 31
puts .ais.ship_dimension_to_port # prints 17
puts .ais.ship_dimension_to_starboard # prints 11
puts .ais.epfd_type # prints 0
puts .ais.eta # prints <this year>-03-23 19:45:00 <your local time zone offset>
puts .ais.static_draught # prints 13.2
puts .ais.destination.strip # prints "HOUSTON"
puts .ais.dte? # prints false
end
end
Design documents
This gem was coded to accept the standard NMEA messages defined in the unoffical spec found here: http://www.catb.org/gpsd/NMEA.txt
Because the message types are standard, if no override is found for a particular talker ID then the message will parse according to the command (the last 3 characters) of the data type. In other words, $GPGLL will use the general GLL message type. Currently, the following standard message types are supported:
AAM, ALM, APA, APB, BOD, BWC, BWR, BWW, DBK, DBS, DBT, DCN, DPT, DTM, FSI, GBS, GGA, GLC, GLL, GNS, GRS, GSA, GST, GSV, GTD, GXA, HDG, HDM, HDT, HFB, HSC, ITS, LCD, MSK, MSS, MTW, MWV, OLN, OSD, R00, RMA, RMB, RMC, ROT, RPM, RSA, RSD, RTE, SFI, STN, TDS, TFI, TPC, TPR, TPT, TRF, TTM, VBW, VDR, VHW, VLW, VPW, VTG, VWR, WCV, WNC, WPL, XDR, XTE, XTR, ZDA, ZFO, ZTG
Additionally, AIS message type definitions were implemented from the unofficial spec found here: http://catb.org/gpsd/AIVDM.html
Currently AIVDM messages types 1, 2, 3, 5, and 8 are implemented.
Support for proprietary messages is also possible. PASHR is included as proof-of-concept.
Disclaimer
This module was written from information scraped together on the web, not from testing on actual devices. Please don't entrust your life or the safety of your ship to this code without doing your own rigorous testing.
Author
This gem was written by Ian Katz ([email protected]) in 2015. It's released under the Apache 2.0 license.