NMEA 0183 驱动
概述
面向海洋 / 船舶 / GNSS 领域的标准 ASCII 协议 NMEA 0183 v4.x 的自研 Java 实现。基于 NMEA 协会公开 spec + 维基百科 + 通用手册编写。未使用外部库,也未复制 GitHub 代码。
| 项目 | 值 |
|---|---|
opc_type | NMEA |
| 实现类 | 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.7 | sentence RMC 的第 7 个字段 |
GGA.2 | GGA 的第 2 个字段 (纬度) |
RMC.utc / RMC.lat / RMC.lon / RMC.speed | alias |
RMC | raw 字符串 (未指定字段索引 / alias) |
支持的 sentence + alias
由 addAliases() 定义:
| type | alias 示例 |
|---|---|
RMC | utc, status, lat, ns, lon, ew, speed, course, date, magvar, magvarew, mode |
GGA | utc, lat, ns, lon, ew, fix, sats, hdop, alt, altunit, geoidsep, sepunit, dgpsage, dgpsid |
GLL | lat, ns, lon, ew, utc, status, mode |
VTG | cogtrue, cogmag, speed, speedkn, speedkm, mode |
GSA | mode1, mode2, pdop, hdop, vdop |
GSV | totalmsgs, msgnum, satsinview |
HDT / HDM | heading, t / m |
MWV | angle, ref, speed, units, status |
DBT / DPT | depthft, depthm, depth, depthfa / depth, offset, max |
MTW | temp, units |
VHW | headingt, headingm, speed, speedkm |
ZDA | utc, day, month, year, tzh, tzm |
XDR | type, value, units, id (仅第一组) |
未定义的 sentence 同样可通过 1..N 索引访问。
支持 / 不支持
- ✅ checksum 校验 (
$~*XOR),不一致时丢弃该行 - ✅ 自动去除 Talker ID (例如
GPRMC→RMC) - ✅ 常用 16 种 sentence 的 alias
- ✅ proprietary
$P...行原样保留 - ❌
!encapsulation (AIVDM 等 AIS) — 当前仅接收$ - ❌ multi-message GSV 累积
- ❌ write / Sentence 发送
测试覆盖率
test/java/plantpulse/driver/protocol/nmea/
| 类 | 测试数 |
|---|---|
NMEADriverTest | 15 |
NMEASpecTest | 22 |
共 37 个测试 — checksum、talker 分离、alias 映射、索引访问、长度上限。
参考
- NMEA 0183 v4 标准 (NMEA 协会公开条目)
- 代码:
src/plantpulse/driver/protocol/nmea/NMEADriver.java