NMEA 0183 드라이버
개요
해양 / 선박 / GNSS 분야 표준 ASCII 프로토콜 NMEA 0183 v4.x 의 자체 자바 구현. 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 로 forwarding 한 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자리 hex 대문자).- 길이 한도 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 를 override.
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