pxdl

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package pxdl implements the Proximity-1 Space Data Link Protocol per CCSDS 211.0-B-6 (July 2020), Data Link Layer.

Proximity-1 is the short-range link protocol: orbiter to lander, orbiter to rover, spacecraft to spacecraft. It is what the Mars relay network runs on.

It differs from the long-haul data link protocols this library also ships — TM, TC, AOS, USLP — in ways that follow from the short range. Frames are small, at most 2048 octets. The header is five octets with no error control field, because the coding layer below handles that. And a single frame type carries both user data and the protocol's own supervisory traffic, distinguished by one bit.

U-frame:  header │ user data (packets, segments, or raw)
P-frame:  header │ supervisory PDUs (link control words, directives)

This package implements the Version-3 Transfer Frame and its data field constructions. The coding and synchronization layer, CCSDS 211.2-B-3, lives in pkg/pxsc.

Index

Constants

View Source
const (
	// MaxDataFieldSize is the largest Transfer Frame Data field.
	MaxDataFieldSize = 2043
	// MinFrameSize is the smallest legal frame: header only.
	MinFrameSize = HeaderSize
	// MaxFrameSize is the largest frame the 11-bit length field can describe.
	MaxFrameSize = 2048
)

Frame size bounds, per §3.2.1 and §3.2.2.10.2.

View Source
const (
	// SPDUFormatVariable marks a variable-length SPDU ('0').
	SPDUFormatVariable uint8 = 0
	// SPDUFormatFixed marks a 16-bit fixed-length SPDU ('1').
	SPDUFormatFixed uint8 = 1
)

SPDU format identifiers, per §3.2.4.2.

View Source
const (
	// FixedTypePLCW identifies a Proximity Link Control Word ('0').
	FixedTypePLCW uint8 = 0
	// FixedTypeReserved is reserved for future CCSDS specification ('1').
	FixedTypeReserved uint8 = 1
)

Fixed-length SPDU type identifiers, per table 3-5.

View Source
const DefaultMaxPacketSize = 64 << 10

DefaultMaxPacketSize bounds an accumulating packet when Reassembler leaves MaxPacketSize at zero: 64 KiB.

The standard sets no ceiling on a reassembled packet. Without one, a stream of "continuing" segments that never ends would grow without limit.

View Source
const FixedSPDUSize = 2

FixedSPDUSize is the width of a fixed-length SPDU in octets (§3.2.4.2.1).

View Source
const HeaderSize = 5

HeaderSize is the width of the Transfer Frame Header in octets (§3.2.1 a).

View Source
const MaxVariableSPDUData = 15

MaxVariableSPDUData is the largest variable-length SPDU data field, bounded by its 4-bit length field (§3.2.4.2.2).

View Source
const SegmentHeaderSize = 1

SegmentHeaderSize is the width of a segment header in octets (§3.2.3.3.1).

View Source
const Version = 2

Version is the Transfer Frame Version Number for a Version-3 frame: binary '10', per CCSDS 211.0-B-6 §3.2.2.2.2.

Variables

View Source
var (
	// ErrDataTooShort indicates the input is shorter than the fields it must contain.
	ErrDataTooShort = errors.New("data too short for the Proximity-1 field being read")

	// ErrInvalidVersion indicates a Transfer Frame Version Number other than
	// binary '10', which CCSDS 211.0-B-6 §3.2.2.2.2 requires for Version-3.
	ErrInvalidVersion = errors.New("invalid transfer frame version: Version-3 requires binary '10'")

	// ErrInvalidFrameLength indicates a frame length outside the 5 to 2048
	// octet range of §3.2.2.10.2.
	ErrInvalidFrameLength = errors.New("invalid frame length: must be 5 to 2048 octets")

	// ErrDataTooLarge indicates a data field beyond the 2043 octets §3.2.1 allows.
	ErrDataTooLarge = errors.New("data field exceeds the maximum of 2043 octets")

	// ErrInvalidSCID indicates a spacecraft identifier beyond the 10-bit field.
	ErrInvalidSCID = errors.New("invalid spacecraft ID: must fit 10 bits")

	// ErrInvalidPortID indicates a port identifier beyond the 3-bit field.
	ErrInvalidPortID = errors.New("invalid port ID: must fit 3 bits")

	// ErrInvalidPCID indicates a physical channel identifier beyond one bit.
	ErrInvalidPCID = errors.New("invalid physical channel ID: must be 0 or 1")

	// ErrInvalidDFCID indicates a Data Field Construction ID that is reserved,
	// or non-zero on a P-frame where §3.2.2.5.2 requires '00'.
	ErrInvalidDFCID = errors.New("invalid data field construction ID")

	// ErrNotUserFrame indicates a user-data operation on a P-frame.
	ErrNotUserFrame = errors.New("frame carries supervisory data, not user data")

	// ErrNotSupervisoryFrame indicates a supervisory operation on a U-frame.
	ErrNotSupervisoryFrame = errors.New("frame carries user data, not supervisory data")

	// ErrInvalidQoS indicates an SPDU on the Sequence Controlled service.
	// §3.2.4.1 allows SPDUs only on Expedited.
	ErrInvalidQoS = errors.New("supervisory PDUs may travel only on the Expedited service")

	// ErrInvalidSPDU indicates a malformed supervisory PDU.
	ErrInvalidSPDU = errors.New("invalid supervisory PDU")

	// ErrSPDUDataTooLarge indicates a variable-length SPDU data field beyond
	// the 15 octets its 4-bit length field can describe.
	ErrSPDUDataTooLarge = errors.New("supervisory PDU data field exceeds 15 octets")

	// ErrInvalidSegment indicates a segment header or reassembly failure.
	ErrInvalidSegment = errors.New("invalid packet segment")

	// ErrSegmentOutOfOrder indicates a continuing or last segment arriving
	// before a first segment for its routing ID (§3.2.3.3.5 b).
	ErrSegmentOutOfOrder = errors.New("segment arrived before the first segment of its packet")

	// ErrReassemblyTooLarge indicates an accumulating packet beyond the
	// configured maximum.
	ErrReassemblyTooLarge = errors.New("reassembled packet exceeds the maximum size")
)

Sentinel errors returned by the Proximity-1 data link codecs.

Functions

func EncodeSPDUs

func EncodeSPDUs(spdus []SPDU) ([]byte, error)

EncodeSPDUs serializes a run of supervisory PDUs into a P-frame data field.

Types

type DFCID

type DFCID uint8

DFCID is the Data Field Construction ID of §3.2.2.5, saying how a U-frame's data field is arranged. Table 3-1 gives the four values.

const (
	// DFCPackets means an integer number of unsegmented packets.
	DFCPackets DFCID = 0
	// DFCSegment means one complete or segmented packet, behind a segment header.
	DFCSegment DFCID = 1
	// DFCReserved is reserved for future CCSDS definition.
	DFCReserved DFCID = 2
	// DFCUserDefined means the content is defined by the mission.
	DFCUserDefined DFCID = 3
)

func (DFCID) String

func (d DFCID) String() string

String names the construction.

type FrameOption

type FrameOption func(*TransferFrame)

FrameOption configures a frame at construction.

func WithDFCID

func WithDFCID(d DFCID) FrameOption

WithDFCID sets the data field construction for a U-frame.

func WithPCID

func WithPCID(pcid uint8) FrameOption

WithPCID selects the physical channel.

func WithQoS

func WithQoS(q QoS) FrameOption

WithQoS selects the quality of service.

func WithSequenceNumber

func WithSequenceNumber(n uint8) FrameOption

WithSequenceNumber sets the frame sequence number.

func WithSourceSCID

func WithSourceSCID() FrameOption

WithSourceSCID marks the SCID field as naming the source spacecraft. The default is the destination.

type Header struct {
	// QoS selects the sequence-controlled or expedited service.
	QoS QoS
	// PDUType distinguishes a U-frame from a P-frame.
	PDUType PDUType
	// DFCID says how a U-frame's data field is arranged. §3.2.2.5.2 requires
	// zero on a P-frame.
	DFCID DFCID
	// SCID identifies the spacecraft, 10 bits.
	SCID uint16
	// PCID selects one of two physical channels, 1 bit.
	PCID uint8
	// PortID identifies the port, 3 bits.
	PortID uint8
	// SourceOrDest says whether SCID names the source or the destination.
	SourceOrDest SourceOrDest
	// FrameLength is the total frame length in octets. It travels as a count
	// one less than this value (§3.2.2.10.2).
	FrameLength uint16
	// FrameSequenceNumber counts frames per PCID and service (§3.2.2.11).
	FrameSequenceNumber uint8
}

Header is the Transfer Frame Header of §3.2.2, figure 3-3.

Five octets, ten fields:

Octet 0:  TFVN(2) | QoS(1) | PDU type(1) | DFC ID(2) | SCID[9:8](2)
Octet 1:  SCID[7:0](8)
Octet 2:  PCID(1) | Port ID(3) | Src/Dest(1) | Frame Length[10:8](3)
Octet 3:  Frame Length[7:0](8)
Octet 4:  Frame Sequence Number(8)

func (*Header) Decode

func (h *Header) Decode(data []byte) error

Decode parses a Transfer Frame Header from the front of data.

func (*Header) Encode

func (h *Header) Encode() ([]byte, error)

Encode serializes the header per figure 3-3.

func (*Header) Humanize

func (h *Header) Humanize() string

Humanize returns a human-readable summary.

func (*Header) Validate

func (h *Header) Validate() error

Validate checks the header against §3.2.2.

type ManagedParameters

type ManagedParameters struct {
	// LocalSpacecraftID is Local_Spacecraft_ID: the SCID of this node. Used
	// to validate a received frame whose Source-or-Destination Identifier
	// says the SCID names the destination (§3.2.2.9.3).
	LocalSpacecraftID uint16

	// RemoteSpacecraftID is Remote_Spacecraft_ID: the SCID of the node at
	// the far end of the link (§3.2.2.9.3).
	RemoteSpacecraftID uint16

	// SendMaximumFrameLength is Maximum_Frame_Length for the frames this
	// node transmits, in octets. Annex C keys the parameter to the frame
	// version in use; for Version-3 frames it can be at most 2048.
	SendMaximumFrameLength int

	// ReceiveMaximumFrameLength is Maximum_Frame_Length for the frames this
	// node accepts. The two directions of a Proximity-1 link often run at
	// very different data rates, so the negotiated maxima can differ too.
	ReceiveMaximumFrameLength int

	// MaximumPacketSize is Maximum_Packet_Size: the largest packet the
	// segmentation and reassembly process handles, in octets (§4.4.2.1).
	MaximumPacketSize int

	// SynchTimeout is Synch_Timeout: how long the receiving end waits
	// without frame synchronization before declaring the link lost.
	SynchTimeout time.Duration

	// PLCWRepeatInterval is PLCW_Repeat_Interval: how often COP-P repeats
	// the current PLCW when nothing has changed (annex C).
	PLCWRepeatInterval time.Duration
}

ManagedParameters holds the MIB entries relevant to this layer, named after the annex C parameters they represent.

The zero value is not useful; start from DefaultManagedParameters.

func DefaultManagedParameters

func DefaultManagedParameters() ManagedParameters

DefaultManagedParameters returns parameters with the Version-3 frame bounds and the package's reassembly default. Spacecraft IDs and timing have no meaningful defaults; they come from the mission.

func (*ManagedParameters) Validate

func (m *ManagedParameters) Validate() error

Validate checks the parameters against the Version-3 frame bounds.

type PDUType

type PDUType uint8

PDUType is the PDU Type ID of §3.2.2.4: whether the data field carries user data or the protocol's own supervisory traffic.

const (
	// UserData marks a U-frame, carrying user data.
	UserData PDUType = 0
	// SupervisoryData marks a P-frame, carrying SPDUs.
	SupervisoryData PDUType = 1
)

func (PDUType) String

func (p PDUType) String() string

String names the PDU type.

type PLCW

type PLCW struct {
	// RetransmitFlag says the receiver is missing frames and wants them again.
	RetransmitFlag bool
	// PCID names the physical channel this report covers.
	PCID uint8
	// ExpeditedFrameCounter counts frames received on the Expedited service,
	// 3 bits.
	ExpeditedFrameCounter uint8
	// ReportValue is V(R): the sequence number the receiver expects next
	// (§3.2.4.3.2.2.2).
	ReportValue uint8
}

PLCW is the Proximity Link Control Word, the Type F1 fixed-length SPDU of §3.2.4.3.2.

It is Proximity-1's acknowledgement: the receiver reports which frame it expects next, so the sender knows what got through. The same job COP-1's CLCW does for TC links.

Sixteen bits, described in figure 3-5 from bit 0:

format ID(1) │ type ID(1) │ retransmit(1) │ PCID(1) │ spare(1) │
expedited frame counter(3) │ report value(8)

This order is verified against CCSDS 211.0-B-6 §3.2.4.3.2.1.1, which lists the seven fields from bit 15 up — Report Value, Expedited Frame Counter, Reserved Spare, PCID, Retransmit Flag, SPDU Type Identifier, SPDU Format ID — and §3.2.4.3.2.2.1, which puts the Report Value in bits 8–15.

func DecodePLCW

func DecodePLCW(data []byte) (*PLCW, error)

DecodePLCW parses a Proximity Link Control Word.

func (*PLCW) Encode

func (p *PLCW) Encode() ([]byte, error)

Encode serializes the PLCW into two octets.

func (*PLCW) Humanize

func (p *PLCW) Humanize() string

Humanize returns a human-readable summary.

func (*PLCW) Validate

func (p *PLCW) Validate() error

Validate checks the PLCW's field widths.

type QoS

type QoS uint8

QoS is the Quality of Service Indicator of §3.2.2.3.

const (
	// SequenceControlled is the reliable service. COP-P checks the frame
	// sequence number of every frame on it.
	SequenceControlled QoS = 0
	// Expedited bypasses the sequence number check. Supervisory PDUs travel
	// only on this service (§3.2.4.1).
	Expedited QoS = 1
)

func (QoS) String

func (q QoS) String() string

String names the service.

type Reassembler

type Reassembler struct {
	// MaxPacketSize bounds one accumulating packet. Zero selects
	// DefaultMaxPacketSize.
	MaxPacketSize int
	// contains filtered or unexported fields
}

Reassembler rebuilds packets from the segments arriving on a link, per §3.2.3.3.3.

Segments of different packets interleave freely as long as they differ in PCID or Port ID, so the reassembler keeps one buffer per routing ID.

§3.2.3.3.4 is strict: only complete packets are delivered. A stream that starts mid-packet, or grows past the limit, is discarded rather than half-delivered.

A Reassembler is not safe for concurrent use.

func NewReassembler

func NewReassembler() *Reassembler

NewReassembler returns an empty reassembler.

func (*Reassembler) Accept

func (r *Reassembler) Accept(pcid, portID uint8, seg *Segment) ([]byte, error)

Accept folds one segment into the reassembler.

It returns a complete packet when this segment finishes one, and nil when more segments are still needed.

func (*Reassembler) AcceptFrame

func (r *Reassembler) AcceptFrame(f *TransferFrame) ([]byte, error)

AcceptFrame folds a U-frame's segment into the reassembler, taking the PCID and Port ID from the frame header.

func (*Reassembler) Pending

func (r *Reassembler) Pending() int

Pending returns how many partial packets are still accumulating.

func (*Reassembler) Reset

func (r *Reassembler) Reset()

Reset discards every partial packet.

type RoutingID

type RoutingID struct {
	PCID           uint8
	PortID         uint8
	PseudoPacketID uint8
}

RoutingID identifies the stream a segment belongs to, per §1.5.1.2: the physical channel, the port, and the pseudo packet ID together.

§3.2.3.3.2 c) requires all segments of one packet to travel with the same PCID and Port ID, which is what makes this triple sufficient.

func (RoutingID) String

func (r RoutingID) String() string

String renders the routing ID.

type SPDU

type SPDU struct {
	PLCW     *PLCW
	Variable *VariableSPDU
}

SPDU is one supervisory PDU of either shape. Exactly one field is set.

func DecodeSPDUs

func DecodeSPDUs(data []byte) ([]SPDU, error)

DecodeSPDUs parses the run of supervisory PDUs in a P-frame's data field.

§3.2.4.1: SPDUs are self-identifying and self-delimiting, so a decoder can walk them without being told how many there are.

func (*SPDU) Encode

func (s *SPDU) Encode() ([]byte, error)

Encode serializes whichever kind this is.

type Segment

type Segment struct {
	Header SegmentHeader
	Data   []byte
}

Segment is one segment data unit: a header and a slice of a packet.

func DecodeSegment

func DecodeSegment(data []byte) (*Segment, error)

DecodeSegment parses a segment data unit from a U-frame's data field.

func Segmentize

func Segmentize(packet []byte, pseudoPacketID uint8, maxSegmentData int) ([]*Segment, error)

Segmentize cuts a packet into segments whose data fields are at most maxSegmentData octets each, tagged with the given pseudo packet ID.

A packet that fits in one segment comes back as a single unsegmented one, which is what the '11' sequence flag is for.

func (*Segment) Encode

func (s *Segment) Encode() ([]byte, error)

Encode serializes the segment data unit.

type SegmentHeader

type SegmentHeader struct {
	SequenceFlags SequenceFlags
	// PseudoPacketID ties the segments of one packet together, 6 bits.
	PseudoPacketID uint8
}

SegmentHeader is the one-octet header of a segment data unit (§3.2.3.3.2).

bits 0-1: sequence flags
bits 2-7: pseudo packet identifier

func (*SegmentHeader) Decode

func (s *SegmentHeader) Decode(data []byte) error

Decode parses a segment header.

func (*SegmentHeader) Encode

func (s *SegmentHeader) Encode() ([]byte, error)

Encode serializes the segment header.

func (*SegmentHeader) Humanize

func (s *SegmentHeader) Humanize() string

Humanize returns a human-readable summary.

func (*SegmentHeader) Validate

func (s *SegmentHeader) Validate() error

Validate checks the header's field widths.

type SequenceFlags

type SequenceFlags uint8

SequenceFlags say where a segment sits relative to its packet, per table 3-4.

const (
	// SegmentContinuing is a middle segment ('00').
	SegmentContinuing SequenceFlags = 0
	// SegmentFirst starts a packet ('01').
	SegmentFirst SequenceFlags = 1
	// SegmentLast ends a packet ('10').
	SegmentLast SequenceFlags = 2
	// SegmentUnsegmented carries a whole packet ('11').
	SegmentUnsegmented SequenceFlags = 3
)

func (SequenceFlags) String

func (s SequenceFlags) String() string

String names the position.

type SourceOrDest

type SourceOrDest uint8

SourceOrDest is the Source-or-Destination Identifier of §3.2.2.9, saying whether the SCID names the sender or the receiver.

const (
	// SCIDIsSource means the SCID field names the source spacecraft ('0').
	SCIDIsSource SourceOrDest = 0
	// SCIDIsDestination means the SCID field names the destination
	// spacecraft ('1').
	SCIDIsDestination SourceOrDest = 1
)

Polarity per §3.2.2.9.2, table 3-2: '0' means the SCID field carries the SCID of the spacecraft sending the frame over this link (the MIB parameter Local_Spacecraft_ID), '1' means it carries the SCID of the spacecraft intended to receive it (Remote_Spacecraft_ID).

func (SourceOrDest) String

func (s SourceOrDest) String() string

String names the interpretation.

type TransferFrame

type TransferFrame struct {
	Header    Header
	DataField []byte
}

TransferFrame is a Version-3 Transfer Frame: a five-octet header and a data field of up to 2043 octets (§3.2.1).

There is no frame error control field. Proximity-1 leaves error detection to the coding layer below, CCSDS 211.2-B-3.

func DecodeTransferFrame

func DecodeTransferFrame(data []byte) (*TransferFrame, error)

DecodeTransferFrame parses a Version-3 Transfer Frame.

func NewSupervisoryFrame

func NewSupervisoryFrame(scid uint16, portID uint8, spdus []byte, opts ...FrameOption) (*TransferFrame, error)

NewSupervisoryFrame builds a P-frame carrying supervisory PDUs.

§3.2.4.1 restricts SPDUs to the Expedited service and §3.2.2.5.2 requires a zero DFC ID, so both are set here rather than left to the caller.

func NewTransferFrame

func NewTransferFrame(scid uint16, portID uint8, data []byte, opts ...FrameOption) (*TransferFrame, error)

NewTransferFrame builds a U-frame carrying user data.

func (*TransferFrame) Encode

func (f *TransferFrame) Encode() ([]byte, error)

Encode serializes the whole frame.

func (*TransferFrame) Humanize

func (f *TransferFrame) Humanize() string

Humanize returns a human-readable summary.

func (*TransferFrame) IsSupervisoryFrame

func (f *TransferFrame) IsSupervisoryFrame() bool

IsSupervisoryFrame reports whether this is a P-frame.

func (*TransferFrame) IsUserFrame

func (f *TransferFrame) IsUserFrame() bool

IsUserFrame reports whether this is a U-frame.

func (*TransferFrame) SPDUs

func (f *TransferFrame) SPDUs() ([]SPDU, error)

SPDUs parses the supervisory PDUs a P-frame carries.

func (*TransferFrame) Validate

func (f *TransferFrame) Validate() error

Validate checks the frame.

type VariableSPDU

type VariableSPDU struct {
	// TypeID selects the kind of directive or report, 3 bits. Annex B
	// specifies the types; this package carries the payload without
	// interpreting it.
	TypeID uint8
	// Data holds one or more directives or reports of that type.
	Data []byte
}

VariableSPDU is a variable-length supervisory PDU, per §3.2.4.2.2.

One octet of header — a zero format bit, a 3-bit type, and a 4-bit length — then up to 15 octets of directives or status reports, all of the same type.

Note the length field is the actual octet count, not a count-less-one. The standard calls that out explicitly, presumably because everything else in CCSDS goes the other way.

func DecodeVariableSPDU

func DecodeVariableSPDU(data []byte) (*VariableSPDU, int, error)

DecodeVariableSPDU parses a variable-length SPDU from the front of data, returning it and the octets consumed.

func (*VariableSPDU) Encode

func (s *VariableSPDU) Encode() ([]byte, error)

Encode serializes the variable-length SPDU.

func (*VariableSPDU) Humanize

func (s *VariableSPDU) Humanize() string

Humanize returns a human-readable summary.

func (*VariableSPDU) Validate

func (s *VariableSPDU) Validate() error

Validate checks the SPDU's field widths.

Jump to

Keyboard shortcuts

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