Summary
FieldType: "DECIMAL" is not implemented in readValue() (fromPgn.js). Fields of this type are decoded as plain little-endian binary integers instead of packed decimal digits, producing wrong values.
The most visible impact is PGN 129808 (DSC Call Information): its dscMessageAddress and mmsiOfShipInDistress fields are DECIMAL (40-bit / 5 bytes). Consumers such as signalk-dsc therefore log incorrect MMSIs for every DSC call.
Tested with canboatjs 3.20.0.
Root cause
In the canboat spec, DECIMAL means each byte holds two decimal digits (00–99). But in readValue() there is no branch for it — a 40-bit DECIMAL field falls through to the generic path (bs.readBits(bitLength, ...)), i.e. it is read as a 40-bit little-endian integer.
Example (illustrative MMSI 366123456):
- On the wire, the 10-symbol DSC address is packed decimal → bytes
24 3D 17 2D 3C (= decimal pairs 36 61 23 45 60 → "3661234560", i.e. MMSI + trailing fill digit).
- Correct decode (per spec, and matching what a Raymarine MFD shows):
"3661234560" → MMSI 366123456.
- canboatjs actual: reads those bytes as a little-endian integer → a completely different number.
DECIMAL is currently used only by PGN 129808 (two fields), so the change is very low blast-radius.
Proposed fix
Add a DECIMAL branch near the top of readValue() (right after the VARIABLE check). Each byte is emitted as two decimal digits; the field is returned as a digit string to preserve leading zeros (e.g. coast-station identities 00MIDxxxx); all-0xFF (or any byte > 99) → null (N/A):
else if (field.FieldType === 'DECIMAL') {
const nbytes = Math.floor((bitLength === undefined ? field.BitLength : bitLength) / 8);
let s = '';
let allFF = true;
for (let i = 0; i < nbytes; i++) {
const b = bs.readUint8();
if (b !== 0xff) allFF = false;
if (b > 99) allFF = true; // not a valid decimal pair -> treat field as N/A
s += String(b).padStart(2, '0');
}
return allFF ? [null, undefined] : [s, undefined];
}
This advances the bitstream by exactly the same number of bits as the current binary read, so downstream field alignment is unchanged.
Related
Downstream consumers that read the DSC MMSI (e.g. signalk-dsc) may then need to map the 10-symbol DSC address to the 9-digit MMSI (drop the trailing fill digit), but that is out of scope for canboatjs itself.
Summary
FieldType: "DECIMAL"is not implemented inreadValue()(fromPgn.js). Fields of this type are decoded as plain little-endian binary integers instead of packed decimal digits, producing wrong values.The most visible impact is PGN 129808 (DSC Call Information): its
dscMessageAddressandmmsiOfShipInDistressfields areDECIMAL(40-bit / 5 bytes). Consumers such assignalk-dsctherefore log incorrect MMSIs for every DSC call.Tested with canboatjs 3.20.0.
Root cause
In the canboat spec,
DECIMALmeans each byte holds two decimal digits (00–99). But inreadValue()there is no branch for it — a 40-bitDECIMALfield falls through to the generic path (bs.readBits(bitLength, ...)), i.e. it is read as a 40-bit little-endian integer.Example (illustrative MMSI
366123456):24 3D 17 2D 3C(= decimal pairs36 61 23 45 60→"3661234560", i.e. MMSI + trailing fill digit)."3661234560"→ MMSI366123456.DECIMALis currently used only by PGN 129808 (two fields), so the change is very low blast-radius.Proposed fix
Add a
DECIMALbranch near the top ofreadValue()(right after theVARIABLEcheck). Each byte is emitted as two decimal digits; the field is returned as a digit string to preserve leading zeros (e.g. coast-station identities00MIDxxxx); all-0xFF(or any byte > 99) →null(N/A):This advances the bitstream by exactly the same number of bits as the current binary read, so downstream field alignment is unchanged.
Related
Downstream consumers that read the DSC MMSI (e.g.
signalk-dsc) may then need to map the 10-symbol DSC address to the 9-digit MMSI (drop the trailing fill digit), but that is out of scope for canboatjs itself.