The nmea CLI simulates a sailing scenario as real NMEA data and decodes a
NMEA stream back to readings — the exact wire your chartplotter or nav software
already speaks. It’s how you drive and verify the 32°N displays against a
faithful synthetic source, and a standalone tool you can point OpenCPN at.
Two subcommands:
nmea sim— play a 32°N scenario as NMEA-0183 over TCP (and, with--n2k, NMEA-2000 battery/tank/engine/AIS).nmea decode— read a NMEA-0183 stream (TCP / file / stdin) and print the decoded readings, human-readable or as JSON.
It’s built on the pure, fully-tested serializer/decoder in packages/sim: a
ScenarioEngine ticks a physically-coherent world state, and the same codecs
that emit the wire also decode it.
Install & run
In the monorepo:
pnpm --filter @32n/nmea-sim start -- sim --scenario coastal-passage
pnpm --filter @32n/nmea-sim start -- decode --tcp 127.0.0.1:10110
Standalone (no clone) — the same model as the mss scanner, via npm or a
self-contained binary:
npx @32n/nmea-sim sim --scenario coastal-passage
nmea sim --stdout | nmea decode
nmea sim
nmea sim --scenario coastal-passage
Streams the scenario as NMEA-0183 on tcp://127.0.0.1:10110.
| Flag | Default | Description |
|---|---|---|
--scenario <name> | coastal-passage | Preset scenario to play |
--port <n> | 10110 | TCP port |
--host <addr> | 127.0.0.1 | Bind address (loopback only) |
--tick <ms> | 1000 | Real ms between engine ticks |
--time-scale <n> | 1 | Sim-time acceleration (>1 plays faster) |
--stdout / --file <path> | — | Also echo each frame to stdout / a file |
--loopback | off | Decode the 0183 instrument frame back onto the bus |
--n2k | off | Decode battery/tank/engine/AIS over NMEA-2000 onto the bus |
Scenarios: anchor-calm, coastal-passage, battery-deep-discharge,
anchor-dragging, passage-squall, motoring-overnight, alternator-fault,
engine-overheat, ais-collision.
Connect from OpenCPN: add a Network connection — type TCP, address
127.0.0.1, port 10110, receive NMEA 0183. The boat moves along the
scenario track with depth, wind, heading, and (for ais-collision) AIS targets
live on the chart.
nmea decode
nmea decode --tcp 127.0.0.1:10110 # a live 0183-over-TCP stream
nmea decode --file capture.nmea --json # a captured file, as JSON
cat capture.nmea | nmea decode # stdin
Decodes 0183 instrument sentences and AIS !AIVDM to compact readings
(pos … sog … cog …, ais <mmsi> …) or structured JSON. Decoding a live CAN
bus (can0) needs OS-level socketcan and is a separate tool; stream decode
(TCP / file / stdin) works today.
What it carries
NMEA-0183 (always): RMC/GGA/GLL position, VTG SOG/COG, HDT heading,
MWD/MWV wind (true + apparent), DBT/DPT depth, VHW water speed, and AIS
targets as !AIVDM. Every line is $<talker><type>,…*HH with an XOR checksum.
NMEA-2000 (with --loopback/--n2k): battery SoC/voltage (PGN 127506/127508),
fluid level (127505), engine rpm/coolant/oil (127488/127489), AIS position +
static (129038/129039/129794) — fast-packet where the PGN needs it.
A fidelity sweep proves the decoded bus reproduces the simulator’s scenarios — for every scenario, across its full duration — within each wire field’s resolution.