跳到主要内容

NMEA 0183 驱动

概述

面向海洋 / 船舶 / GNSS 领域的标准 ASCII 协议 NMEA 0183 v4.x 的自研 Java 实现。基于 NMEA 协会公开 spec + 维基百科 + 通用手册编写。未使用外部库,也未复制 GitHub 代码。

项目
opc_typeNMEA
实现类plantpulse.driver.protocol.nmea.NMEADriver
(无 — 自研实现。NMEA 0183 v4)
read良好 (基于接收缓存)
write❌ (isWriteSupported() = false,仅接收)
默认端口10110 (NMEA over IP de-facto)
安全
原始介质

NMEA 0183 的标准介质为 RS-232/RS-422 串口 (4800 / 38400 baud),但在实际运行中通常由多路复用器/网关 通过 TCP 或 UDP 转发,即 NMEA over IP,本驱动接收其 TCP 变体。


类 / 结构

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) — 用于应对串口仿真 / 低速设备。


wire 格式摘要

$<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
  • Talker ID 2 个字符 (GP/GL/GN/GA/HC/II/AI ...) + sentence type 3 个字符。
  • Proprietary P 前缀不做 talker 分离,直接作为缓存键使用。
  • *<CC> checksum = $* 之间所有 char 的 XOR (2 位十六进制大写)。
  • 长度上限 82 字符 (NMEA 0183 v4 标准),本实现采用 MAX_LINE_LEN=256 保护。

缓存 / read 格式

通过 processLine() 校验的消息以 sentence type (例如 RMC) 为键存入 lastByType:

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

read("RMC.7") → 第 7 个字段(speed)。read("RMC.lat") → alias。


OPC 注册选项

若存在 options.nmea.port,则覆盖 OPC 的 opc_agent_port

connect-timeout / read-timeout 选项 — CONNECT_TIMEOUT_MS = 5000,soTimeout = 0。


标签地址格式

表示含义
RMC.7sentence RMC 的第 7 个字段
GGA.2GGA 的第 2 个字段 (纬度)
RMC.utc / RMC.lat / RMC.lon / RMC.speedalias
RMCraw 字符串 (未指定字段索引 / alias)

支持的 sentence + alias

addAliases() 定义:

typealias 示例
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 (仅第一组)

未定义的 sentence 同样可通过 1..N 索引访问。


支持 / 不支持

  • ✅ checksum 校验 ($~* XOR),不一致时丢弃该行
  • ✅ 自动去除 Talker ID (例如 GPRMCRMC)
  • ✅ 常用 16 种 sentence 的 alias
  • ✅ proprietary $P... 行原样保留
  • ! encapsulation (AIVDM 等 AIS) — 当前仅接收 $
  • ❌ multi-message GSV 累积
  • ❌ write / Sentence 发送

测试覆盖率

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

测试数
NMEADriverTest15
NMEASpecTest22

共 37 个测试 — checksum、talker 分离、alias 映射、索引访问、长度上限。


参考

  • NMEA 0183 v4 标准 (NMEA 协会公开条目)
  • 代码: src/plantpulse/driver/protocol/nmea/NMEADriver.java