Skip to main content

NMEA 0183 Driver

Overview

An in-house Java implementation of NMEA 0183 v4.x, the standard ASCII protocol used in the marine / shipboard / GNSS domains. Based on the publicly available NMEA association spec, Wikipedia, and general manuals. No external libraries or copied GitHub code.

ItemValue
opc_typeNMEA
Implementation classplantpulse.driver.protocol.nmea.NMEADriver
Library(none — in-house implementation, NMEA 0183 v4)
readGood (based on the receive cache)
write❌ (isWriteSupported() = false, receive-only)
Default port10110 (NMEA over IP de-facto)
SecurityNone
Physical medium

The NMEA 0183 standard specifies RS-232/RS-422 serial (4800 / 38400 baud), but in actual operation it is common for a multiplexer/gateway to forward the data over TCP or UDP as NMEA over IP. This driver accepts the TCP variant.


Classes / structure

NMEADriver (BaseProtocolDriver)
├── Socket / BufferedReader (US-ASCII)
├── receiverThread — daemon, "NMEA-Receiver-<opc_id>"
├── lastByType — ConcurrentHashMap<sentenceType, parsed map>
├── readLine() — CR/LF 분리 + MAX_LINE_LEN(256) 가드
├── processLine(line) — '$' / checksum 검증 / talker 제거 / 필드 split
├── addAliases(type, fields) — RMC/GGA/GLL/VTG/GSA/GSV/HDT/HDM/MWV/DBT/DPT/MTW/VHW/ZDA/XDR
└── xorChecksum(payload) — '$' 와 '*' 사이 XOR

socket.setSoTimeout(0) — provided for serial emulation / low-speed equipment.


Wire format summary

$<talker><sentence>,f1,f2,...,fN*<CC><CR><LF>
예) $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A
  • 2-character talker ID (GP/GL/GN/GA/HC/II/AI ...) + 3-character sentence type.
  • A proprietary P prefix is used as the cache key as-is, without talker separation.
  • *<CC> checksum = XOR of every char between $ and * (2-digit uppercase hex).
  • Length limit is 82 characters (NMEA 0183 v4 standard); this implementation guards with MAX_LINE_LEN=256.

Cache / read format

Messages that pass processLine() are stored in lastByType under the sentence type (e.g. RMC) key:

parsed = {
"__raw__": "$GPRMC,...",
"__type__": "RMC",
"1": "123519", // utc
"2": "A", // status
...
// alias
"utc": "123519",
"lat": "4807.038",
...
}

read("RMC.7") → field 7 (speed). read("RMC.lat") → alias.


OPC registration options

If options.nmea.port is present, it overrides the OPC opc_agent_port.

No connect-timeout / read-timeout options — CONNECT_TIMEOUT_MS = 5000, soTimeout = 0.


Tag address format

NotationMeaning
RMC.7Field 7 of the RMC sentence
GGA.2Field 2 of GGA (latitude)
RMC.utc / RMC.lat / RMC.lon / RMC.speedalias
RMCRaw string (no field index / alias specified)

Supported sentences + aliases

Defined by addAliases():

typealias examples
RMCutc, status, lat, ns, lon, ew, speed, course, date, magvar, magvarew, mode
GGAutc, lat, ns, lon, ew, fix, sats, hdop, alt, altunit, geoidsep, sepunit, dgpsage, dgpsid
GLLlat, ns, lon, ew, utc, status, mode
VTGcogtrue, cogmag, speed, speedkn, speedkm, mode
GSAmode1, mode2, pdop, hdop, vdop
GSVtotalmsgs, msgnum, satsinview
HDT / HDMheading, t / m
MWVangle, ref, speed, units, status
DBT / DPTdepthft, depthm, depth, depthfa / depth, offset, max
MTWtemp, units
VHWheadingt, headingm, speed, speedkm
ZDAutc, day, month, year, tzh, tzm
XDRtype, value, units, id (first group only)

Undefined sentences can still be accessed by 1..N index.


Supported / not supported

  • ✅ Checksum verification ($~* XOR); lines are discarded on mismatch
  • ✅ Automatic talker ID removal (e.g. GPRMCRMC)
  • ✅ Aliases for the 16 most commonly used sentences
  • ✅ Proprietary $P... lines preserved as-is
  • ! encapsulation (AIVDM and other AIS) — currently only $ is accepted
  • ❌ Multi-message GSV accumulation
  • ❌ write / sentence transmission

Test coverage

test/java/plantpulse/driver/protocol/nmea/

ClassTest count
NMEADriverTest15
NMEASpecTest22

37 tests in total — checksum, talker separation, alias mapping, index access, length limit.


References

  • NMEA 0183 v4 standard (publicly available NMEA association items)
  • Code: src/plantpulse/driver/protocol/nmea/NMEADriver.java