Skip to content

PGN 129808 (DSC): FieldType DECIMAL not implemented — MMSI address fields decode as binary integers #451

Description

@fakehec

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions