decoder

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package decoder converts input messages to an intermediate (Packet) form, and outputs equivalent golang structs.

The decoder package sits in the middle of the NMEA 2000 decoding pipeline. It receives raw frame data from an adapter layer and produces decoded Go structs representing specific PGN (Parameter Group Number) messages. The Packet type accumulates frame data and tracks fast-packet assembly state.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

func New

func New() *Decoder

func (*Decoder) Decode

func (ps *Decoder) Decode(pkt Packet)

func (*Decoder) SetOutput

func (ps *Decoder) SetOutput(sh Handler)

type Handler

type Handler interface {
	HandleStruct(pgn.Message)
}

type Packet

type Packet struct {
	// Info provides PGN number, source address, priority, destination, and timestamp.
	// This includes metadata like PGN number, source address, priority, destination, and timestamp.
	Info pgn.MessageInfo `json:"info"`

	// Data is (when complete) the data payload for a PGN, ready to decode.
	// For single-frame messages this is the raw 8-byte CAN frame data.
	// For fast-packet messages this is the reassembled payload from multiple frames,
	// with the sequence/frame header bytes stripped and the result trimmed to the
	// expected length declared in frame 0.
	Data []uint8 `json:"data"`

	// Fast (when complete) indicates if matching pgn variants are all fast or all slow.
	// This is determined by looking up the PGN in the generated PgnInfoLookup table.
	// A "fast" PGN carries more than 8 bytes and must be split across multiple CAN frames.
	// Note: PGN 130824 is a special case where some variants are fast and some are slow.
	Fast bool `json:"fast"`

	// SeqId (for fast packets) is the 3-bit sequence identifier (0-7) that connects
	// partial packets belonging to the same logical message. Multiple in-flight sequences
	// for the same PGN/source can coexist using different SeqIds.
	SeqId uint8 `json:"seqId"`

	// FrameNum (for fast packets) is the 5-bit frame number (0-31) indicating the position
	// of this partial packet within the complete multi-frame message. Frame 0 carries a
	// length byte; subsequent frames are continuation frames.
	FrameNum uint8 `json:"frameNum"`

	// Proprietary indicates if the PGN falls in the NMEA 2000 proprietary range.
	// Proprietary PGNs encode a manufacturer ID and industry code in their first two data
	// bytes, which is used to select the correct variant.
	Proprietary bool `json:"proprietary"`

	// Complete is true for single-frame messages and for fast-packet messages when all
	// frames in the sequence have been received and assembled. Only complete packets are
	// forwarded downstream for decoding.
	Complete bool `json:"complete"`

	// Manufacturer is the Manufacturer ID extracted from the first two bytes of proprietary
	// PGN data.
	Manufacturer pgn.ManufacturerCodeConst `json:"manufacturer"`

	// Candidates is the list of all possible PGN definitions that match this PGN number.
	// Multiple candidates exist when different manufacturers define their own proprietary
	// messages under the same PGN number (e.g., PGN 130820 has many vendor-specific variants).
	Candidates []*pgn.PgnInfo `json:"candidates"`

	// ParseErrors tracks errors encountered during packet processing. Errors accumulate
	// as validation fails or assembly issues arise. Decode errors are bundled into the
	// resulting UnknownPGN for debugging.
	ParseErrors []error `json:"parseErrors"`
}

Packet is the core data type used in the package. When complete a Packet contains the complete message (coallescing multiple fast packets if needed). It connects the encoded frame format used as the NMNEA 2000 wire format with the PGN payload metadata.

In our data flow it fits like this:

NMEA 2000/Canbus wire format
Gateway to Endpoint (various representations)
Endpoint to Adapter (various Message implementations)
Packet (Adapter intermediate format) <--- this type
Golang datatypes for each known PGN, or UnknownPGN (output from Adapter)

A Packet starts life when a CAN frame arrives. For single-frame PGNs, the Packet is immediately complete. For fast-packet (multi-frame) PGNs, multiple CAN frames are assembled into a single Packet via the sequence/MultiBuilder system before the Packet is marked Complete and ready for decoding.

func NewPacket

func NewPacket(info pgn.MessageInfo, data []byte) *Packet

NewPacket creates and returns a pointer to an initialized Packet from a MessageInfo and raw data bytes. It performs initial validation, determines if the PGN is proprietary, looks up candidate metadata from the PGN metadata table, and sets the Fast flag based on the first candidate's metadata.

Parameters:

  • info: MessageInfo containing PGN number, source, priority, destination, and timestamp.
  • data: Raw payload bytes from the CAN frame.

Returns a *Packet. If the PGN is unknown (not in metadata), ParseErrors will contain a "no data for pgn" error but the packet is still returned for downstream handling as an UnknownPGN.

func (*Packet) FilterCandidates

func (p *Packet) FilterCandidates()

FilterCandidates filters the candidate metadata to variants matching the payload.

func (*Packet) GetManCode

func (p *Packet) GetManCode()

GetManCode extracts and sets the Manufacturer code from the packet data. For proprietary PGNs, the first 11 bits of the data payload contain the manufacturer code (little-endian), and bits 13-15 contain the industry code. This method calls pgn.GetProprietaryInfo to perform the extraction and sets p.Manufacturer on success. If the extraction fails (e.g., insufficient data), the Manufacturer field is left at its zero value.

func (*Packet) GetSeqFrame

func (p *Packet) GetSeqFrame()

GetSeqFrame extracts the sequence ID and frame number from the first byte of the packet data. In NMEA 2000 fast-packet encoding, the first byte of each CAN frame encodes:

  • Bits 7-5 (upper 3 bits): Sequence ID (0-7), identifying which logical sequence this frame belongs to. Multiple concurrent sequences for the same PGN/source use different IDs.
  • Bits 4-0 (lower 5 bits): Frame number (0-31), indicating this frame's position in the multi-frame message. Frame 0 is the initial frame containing the total payload length.

Note: We can't always know if a packet is actually a fast-packet partial frame, so these extracted values are not always meaningful. They are only valid when the PGN is known to be a fast-packet type.

func (*Packet) UnknownPGN

func (p *Packet) UnknownPGN() *pgn.UnknownPGN

UnknownPGN creates a new instance of UnknownPGN from this packet. This is used as a fallback when no decoder can successfully parse the packet data, preserving the raw data and accumulated error information for debugging and logging.

func (*Packet) Valid

func (p *Packet) Valid() bool

Valid performs lightweight sanity checking on the packet's essential fields. It verifies that the PGN number is non-zero and that the data payload is non-empty. Any validation failures are recorded in ParseErrors.

Returns true if the packet passes all checks, false otherwise.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL