@signalk/nmea0183-utilities
v1.1.1
Published
Various utilities for transforming NMEA0183 units into SI units for use in SK.
Readme
@signalk/nmea0183-utilities
Small, strictly-typed TypeScript helpers for parsing NMEA 0183 sentences into
Signal K-flavoured SI units. Used under the hood by
@signalk/nmea0183-signalk and
available standalone for plugin authors.
Installation
npm install @signalk/nmea0183-utilitiesRequires Node >=22.
Quick tour
import utils from '@signalk/nmea0183-utilities'
// Or: const utils = require('@signalk/nmea0183-utilities')
// Or: import { valid, transform, coordinate } from '@signalk/nmea0183-utilities'
// 1. Validate a sentence and its *XX checksum
utils.valid('$GPBOD,045.,T,023.,M,DEST,START*01') // -> true
utils.valid('$GPBOD,045.,T,023.,M,DEST,START*AA') // -> false (bad checksum)
utils.valid('$GPBOD,045.,T,023.,M,DEST,START', false) // -> true (skip checksum)
// 2. Compute and append a *XX checksum
utils.appendChecksum('$GPBOD,045.,T,023.,M,DEST,START')
// -> '$GPBOD,045.,T,023.,M,DEST,START*01'
// 3. Convert between units (SI-first)
utils.transform(10, 'knots', 'ms') // -> 5.144... m/s
utils.transform(100, 'c', 'k') // -> 373.15 K
utils.transform(33, 'ft', 'm') // -> 10.058... m
// 4. Parse a latitude/longitude pair
utils.coordinate('5222.3277', 'N') // -> 52.372128... (decimal degrees)
utils.coordinate('00454.5824', 'W') // -> -4.909706... (W/S negative)
// 5. Sanity-check a position
utils.isValidPosition(52.37, -4.91) // -> true
utils.isValidPosition(NaN, 0) // -> false
// 6. Build an ISO-8601 timestamp from NMEA time (HHMMSS[.FFF]) + date (DDMMYY)
utils.timestamp('173456.75', '050426') // -> '2026-04-05T17:34:56.750Z'
utils.timestamp() // -> current UTC as ISO string
// 7. Decode a magnetic-variation field
utils.magneticVariation(3.5, 'E') // -> 3.5 (east positive)
utils.magneticVariation(3.5, 'W') // -> -3.5 (west negative)
// 8. Build the `source` object Signal K deltas expect
utils.source('GPGGA')
// -> { type: 'NMEA0183', label: 'signalk-parser-nmea0183', sentence: 'GPGGA' }API
All functions are named exports; a default export aggregates them so
import utils from '@signalk/nmea0183-utilities' works as well.
Validation
valid(sentence, validateChecksum?) => boolean
Returns true if sentence looks like a well-formed NMEA 0183 frame.
Requires a $ or ! prefix and, when validateChecksum is true (the
default), a matching *XX checksum suffix. Pass false to accept
prefix-only frames.
appendChecksum(sentence) => string
Returns sentence with a computed *XX suffix appended, uppercase hex,
zero-padded. Returns the input unchanged if it already contains *,
doesn't start with $/!, or is an unsupported shape.
Unit conversion
transform(value, from, to) => number
Converts value between the units below. value may be a number or a
numeric string (it's coerced via float). from and to are typed as
UnitFormat and are case-sensitive in TypeScript callers — pass an
unknown pair and it throws unsupported conversion: <from> -> <to>.
| Family | Units |
| ----------- | --------------------------- |
| Distance | km, nm, m, ft, fa |
| Speed | knots, kph, ms, mph |
| Angle | deg, rad |
| Temperature | c, k, f |
All combinations within each family are supported. Same-unit
transform(x, 'm', 'm') short-circuits.
RATIOS
The lookup table used internally — exported for downstream code that
needs the raw constants (e.g. RATIOS.NM_IN_KM === 1.852).
Coordinates & position
coordinate(value, pole) => number
Parses an NMEA coordinate (DDDMM.MMMM, DDMM.MMMM) to decimal degrees.
pole is a Pole ('N' | 'S' | 'E' | 'W', uppercase only per
IEC 61162-1). 'S' and 'W' flip the sign. Unknown pole letters throw
unsupported pole: <value>.
isValidPosition(latitude, longitude) => boolean
true iff both inputs are finite numbers within [-90, 90] /
[-180, 180]. Rejects NaN and ±Infinity.
magneticVariation(degrees, pole) => number
degrees is the magnitude; pole is a Pole. East is positive, West
is negative; 'N' | 'E' pass through, 'S' | 'W' negate. Unknown pole
letters throw.
Timestamp
timestamp(time?, date?) => string
Assembles an ISO-8601 UTC string from the two fields NMEA sentences carry.
timeisHHMMSSwith an optional fractional tail (HHMMSS.sss). The first three digits of the tail become milliseconds; extra digits are truncated, a missing or non-digit tail falls through to.000Z. When omitted, the current wall-clock time is used.dateisDDMMYY. 2-digit years follow IEC 61162-1:YY < 80→20YY,YY >= 80→19YY. When omitted, today's UTC date is used.
Sentence metadata
source(sentence?) => SignalKSource
Returns the source sub-object Signal K deltas expect:
{ type: 'NMEA0183', label: 'signalk-parser-nmea0183', sentence: <sentence> }sentence defaults to '' when the argument is falsy.
Numeric parsing
int(n) => number
parseInt(n, 10) that returns 0 instead of NaN when the input
doesn't parse. Accepts unknown so int(null), int(undefined),
int('abc'), int(NaN) all return 0.
float(n) => number
Same idea for parseFloat. Accepts numbers or numeric strings. Returns
0.0 on parse failure.
intOrNull(n) => number | null / floatOrNull(n) => number | null
Null-preserving variants of int / float. An NMEA 0183 null field
(IEC 61162-1 §7.2.3.4) signals "sensor working, value not available"
and must not be confused with a legitimate zero. intOrNull(''),
intOrNull(null), intOrNull('abc') all return null; intOrNull('0')
returns 0. Prefer these over int/float when the caller wants to
pass the not-available semantic through to Signal K (which treats null
the same way).
transformOrNull(value, from, to) => number | null
Null-short-circuiting unit conversion. Identical to transform for real
input; returns null when value is empty / null / undefined /
non-numeric. Unsupported unit pairs still throw — the null short-circuit
runs first, so a missing field with an unknown unit pair is still
null, not an error.
magneticVariationOrNull(degrees, pole) => number | null
Null-preserving magnetic variation. Returns null when degrees or pole
is missing or unparseable (including unknown / lowercase pole letters),
rather than throwing or silently returning 0. 0 with a valid pole
returns 0, not null.
zero(n) => string
Width-2 left-pad for integer date/time components. zero(2) === '02',
zero(42) === '42', zero(-5) === '-05'. Throws TypeError on
non-finite or non-integer input — feed it Math.trunc(n) if you have a
float.
TypeScript
Full types ship in the package. The useful ones:
import type {
UnitFormat, // 'km' | 'nm' | 'm' | 'ft' | 'fa' | 'knots' | ... | 'c' | 'k' | 'f'
Pole, // 'N' | 'S' | 'E' | 'W'
SignalKSource
} from '@signalk/nmea0183-utilities'Both named and default imports are supported; the default is a plain object whose keys are the named exports.
License
Apache-2.0. See LICENSE.
