Documentation
¶
Overview ¶
Package pxsc implements the Proximity-1 Coding and Synchronization Sublayer per CCSDS 211.2-B-3 (October 2019).
This is the layer beneath pkg/pxdl. It wraps each transfer frame in a Proximity Link Transmission Unit (a sync marker, the frame, and a CRC-32) and fills the gaps between them with idle data so the receiver keeps bit lock.
PLTU: ASM (FAF320) │ transfer frame │ CRC-32
3 octets variable 4 octets
It plays the same role for Proximity-1 that pkg/tmsc plays for TM and pkg/tcsc for TC, and the shape of the API follows those two.
The convolutional code of clause 3.4.3 is fully supported: the encoder is in convolutional.go and a matching Viterbi decoder is in viterbi.go, taking hard decisions through Decode and the soft decisions clause 3.4.3.3 recommends through DecodeSoft. Deliberately absent are the LDPC code of clause 3.4.4, its Codeword Sync Marker, and the pseudo-randomizer of clause 3.4.5, which applies only when LDPC is used; the PICS declares all three.
Index ¶
- Constants
- Variables
- func AcquisitionSequence(n int) []byte
- func ComputeCRC32(data []byte) uint32
- func ConvolutionalEncode(data []byte) []byte
- func DefaultASM() []byte
- func FindASM(data []byte, start int) int
- func IdleData(n int) []byte
- func IdleSequence(n int) []byte
- func IsIdleData(data []byte) bool
- func TailSequence(n int) []byte
- func UnwrapPLTU(pltu []byte) ([]byte, error)
- func UnwrapPLTUWithLimit(pltu []byte, maxFrameLength int) ([]byte, error)
- func VerifyCRC32(codeword []byte) bool
- func ViterbiDecode(symbols []byte) ([]byte, error)
- func WrapPLTU(frame []byte) ([]byte, error)
- type ConvolutionalEncoder
- type PLTU
- type Synchronizer
- type ViterbiDecoder
Constants ¶
const ( // ConstraintLength is the number of input bits each output symbol depends // on, including the current one. ConstraintLength = 7 // CodeRateNumerator and CodeRateDenominator give the rate 1/2. CodeRateNumerator = 1 CodeRateDenominator = 2 )
Convolutional code parameters, per CCSDS 131.0-B as referenced by clause 3.4.3.1.
const ( // G1 is the first connection vector, 1111001 in binary. G1 uint8 = 0o171 // G2 is the second connection vector, 1011011 in binary. Its output is // inverted, per clause 3.4.3.1 note 1. G2 uint8 = 0o133 )
Connection vectors for the rate 1/2 code, in octal as CCSDS writes them: G1 = 171, G2 = 133.
const ASMSize = 3
ASMSize is the width of the sync marker in octets (clause 3.2.3.1).
const CRC32Polynomial uint32 = 0x00A00805
CRC32Polynomial is the generator of annex C, C1.3, in MSB-first form with the implicit X^32 term dropped.
const CRC32Size = 4
CRC32Size is the width of the attached CRC in octets (clause 3.2.2 c).
const DefaultMaxFrameLength = 2048
DefaultMaxFrameLength bounds a frame recovered from a PLTU when no limit is given. It matches the largest Version-3 Transfer Frame, per CCSDS 211.0-B-6 clause 3.2.2.10.2.
The coding sublayer itself sets no ceiling: Clause 3.2.2 note 1 says the maximum comes from the mission's Maximum_Frame_Length parameter. This is a safe default for a decoder that has not been told one.
const DefaultMinFrameLength = 5
DefaultMinFrameLength is the shortest Version-3 Transfer Frame: the header alone, per CCSDS 211.0-B-6 clause 3.2.2.10.2.
const IdlePatternSize = 4
IdlePatternSize is the width of one repetition in octets.
const PLTUOverhead = ASMSize + CRC32Size
PLTUOverhead is what a PLTU costs on top of the frame it carries.
Variables ¶
var ( // ErrDataTooShort indicates the input is shorter than the fields it must contain. ErrDataTooShort = errors.New("data too short for the PLTU field being read") // ErrInvalidASM indicates the Attached Sync Marker is not the FAF320 of // CCSDS 211.2-B-3 clause 3.2.3.2. ErrInvalidASM = errors.New("invalid attached sync marker: expected FAF320") // ErrCRCMismatch indicates the attached CRC-32 did not verify, so the // PLTU must be discarded per clause 3.6. ErrCRCMismatch = errors.New("CRC-32 mismatch: the PLTU is corrupt") // ErrFrameTooLarge indicates a transfer frame beyond the configured // maximum PLTU length. ErrFrameTooLarge = errors.New("transfer frame exceeds the maximum length") // ErrEmptyFrame indicates an attempt to wrap nothing in a PLTU. ErrEmptyFrame = errors.New("cannot build a PLTU around an empty transfer frame") // ErrInvalidLength indicates a symbol stream that is not a whole number of // coded bits, so it cannot be decoded. ErrInvalidLength = errors.New("symbol stream length is not a whole number of coded input bits") )
Sentinel errors returned by the Proximity-1 coding and synchronization codecs.
var ASM = [3]byte{0xFA, 0xF3, 0x20}
ASM is the Attached Synchronization Marker of CCSDS 211.2-B-3 clause 3.2.3.2: the 24-bit pattern FAF320.
Note it is three octets, not the four that TM and AOS use. A Proximity-1 link is short and re-acquires per PLTU, so the marker is cheaper.
var IdlePattern = [4]byte{0x35, 0x2E, 0xF8, 0x53}
IdlePattern is the PN sequence of clause 3.3.2.2: hexadecimal 352EF853, repeated as needed.
Functions ¶
func AcquisitionSequence ¶
AcquisitionSequence returns n octets for the acquisition sequence of clause 3.3.3.
It is the same pattern as any other idle data. The distinction is when it is sent, not what it contains: Clause 3.3.3.1 has the transmitter radiate carrier first, then this, so the receiver can reach a reliable symbol stream before real data starts.
The duration comes from the mission's Acquisition_Idle_Duration parameter, which is why the caller passes a length rather than this package choosing one.
func ComputeCRC32 ¶
ComputeCRC32 returns the Proximity-1 CRC-32 over data.
The register starts at zero and there is no final inversion, per annex C and its encoder note.
func ConvolutionalEncode ¶
ConvolutionalEncode encodes data with a fresh encoder. Use a ConvolutionalEncoder directly when the stream continues across calls.
func DefaultASM ¶
func DefaultASM() []byte
DefaultASM returns the sync marker as a slice, matching the shape of pkg/tmsc.DefaultASM.
func FindASM ¶
FindASM returns the offset of the first sync marker at or after start, or -1 when there is none.
func IdleData ¶
IdleData returns n octets of idle data.
Clause 3.3.2.4: whenever the end of the PN sequence is reached it repeats from the first bit, so the output is the pattern tiled to length.
func IdleSequence ¶
IdleSequence returns n octets to send while no PLTU is ready (clause 3.3.4).
func IsIdleData ¶
IsIdleData reports whether data is a run of the idle pattern starting at the beginning of the sequence.
A receiver uses this to tell filler from a PLTU that lost its sync marker.
func TailSequence ¶
TailSequence returns n octets for the tail sequence of clause 3.3.5, sent before the transmitter stops.
Its length comes from the mission's Tail_Idle_Duration parameter.
func UnwrapPLTU ¶
UnwrapPLTU recovers the transfer frame from a PLTU, checking the sync marker and the CRC-32.
Like pkg/tmsc.UnwrapCADU, this expects the marker at offset zero. Use a Synchronizer to find PLTUs in a byte stream.
func UnwrapPLTUWithLimit ¶
UnwrapPLTUWithLimit recovers the transfer frame, rejecting one longer than maxFrameLength octets.
func VerifyCRC32 ¶
VerifyCRC32 reports whether a block ending in its own CRC-32 checks out.
Annex C, C2.2: the syndrome of a correct codeword is zero, so running the CRC over the message and its appended check value must give zero.
func ViterbiDecode ¶
ViterbiDecode decodes a complete stream with a fresh decoder, flushing the traceback window at the end.
Use a ViterbiDecoder directly when the stream continues across calls.
Types ¶
type ConvolutionalEncoder ¶
type ConvolutionalEncoder struct {
// contains filtered or unexported fields
}
ConvolutionalEncoder holds the shift register between calls, so a stream can be encoded in pieces.
The register is not reset between Encode calls. That is deliberate: Clause 3.4.3.2 encodes everything transmitted as one continuous stream, PLTUs and idle data alike, so the encoder state carries across unit boundaries.
func NewConvolutionalEncoder ¶
func NewConvolutionalEncoder() *ConvolutionalEncoder
NewConvolutionalEncoder returns an encoder with a cleared register.
func (*ConvolutionalEncoder) Encode ¶
func (e *ConvolutionalEncoder) Encode(data []byte) []byte
Encode convolutionally encodes data, returning twice as many octets.
Bits are taken most significant first, matching the bit numbering convention of clause 1.6.2. Each input bit yields two symbols, packed the same way.
func (*ConvolutionalEncoder) EncodeBit ¶
func (e *ConvolutionalEncoder) EncodeBit(bit uint8) (c1, c2 uint8)
EncodeBit encodes one input bit and returns the two output symbols.
Clause 3.4.3.1 note 1: the output on the G2 path is inverted.
func (*ConvolutionalEncoder) Reset ¶
func (e *ConvolutionalEncoder) Reset()
Reset clears the shift register.
type PLTU ¶
type PLTU struct {
// Frame is the transfer frame the PLTU carried.
Frame []byte
// CRC is the attached check value.
CRC uint32
// Offset is where the PLTU started in the stream it came from.
Offset int
}
PLTU is a decoded Proximity Link Transmission Unit.
type Synchronizer ¶
type Synchronizer struct {
// MinFrameLength is the shortest transfer frame to consider. For
// Version-3 frames this is 5, the header size.
MinFrameLength int
// MaxFrameLength is the longest transfer frame to consider. Zero selects
// DefaultMaxFrameLength.
MaxFrameLength int
}
Synchronizer finds PLTUs in a byte stream, per CCSDS 211.2-B-3 clause 3.6.
A Proximity-1 stream is not a tidy sequence of units. PLTUs of different lengths are separated by runs of idle data, and the receiver has to hunt for each sync marker in turn. Worse, the marker is only 24 bits, so a random match happens roughly once every 16 million octets. The CRC is what separates a real PLTU from a coincidence.
That is why this scans rather than parses: find a marker, try the frame lengths that could follow, and accept the first one whose CRC verifies.
A Synchronizer is not safe for concurrent use.
func NewSynchronizer ¶
func NewSynchronizer() *Synchronizer
NewSynchronizer returns a synchronizer with the Version-3 frame bounds.
func (*Synchronizer) Scan ¶
func (s *Synchronizer) Scan(data []byte) []PLTU
Scan finds every PLTU in data.
It walks the stream looking for sync markers. At each one it tries frame lengths from the minimum upward and takes the first whose CRC-32 verifies, then resumes after that PLTU. A marker with no verifying length is skipped as a false match.
func (*Synchronizer) ScanFrames ¶
func (s *Synchronizer) ScanFrames(data []byte) [][]byte
ScanFrames finds every PLTU and returns just the transfer frames.
type ViterbiDecoder ¶
type ViterbiDecoder struct {
// contains filtered or unexported fields
}
ViterbiDecoder decodes a continuous convolutionally encoded stream.
It holds the trellis between calls, so a stream arriving in pieces decodes as one, which matters because clause 3.4.3.2 encodes everything transmitted as a single stream, PLTUs and idle data alike.
A ViterbiDecoder is not safe for concurrent use.
func NewViterbiDecoder ¶
func NewViterbiDecoder() *ViterbiDecoder
NewViterbiDecoder returns a decoder positioned at the start of a stream.
Only state zero has a finite metric, which pins the decoder to the encoder's cleared register: no path can start anywhere else.
func (*ViterbiDecoder) Decode ¶
func (d *ViterbiDecoder) Decode(symbols []byte) ([]byte, error)
Decode decodes convolutionally encoded symbols and returns the input bits they carry, packed most significant bit first.
symbols holds two coded bits per input bit, in the order Encode produced them, so the input must be an even number of octets: eight input bits become two octets of symbols.
func (*ViterbiDecoder) DecodeSoft ¶
func (d *ViterbiDecoder) DecodeSoft(symbols []int8) ([]byte, error)
DecodeSoft decodes from soft decisions, which clause 3.4.3.3 recommends over hard ones.
Each entry is the demodulator's confidence in one coded symbol: positive for a one, negative for a zero, and further from zero for more confident. Two entries per input bit, so the slice length must be even. The scale does not matter, only the sign and the relative magnitude; three-bit decisions in the range -4 to 3 are what clause 3.4.3.3 has in mind.
func (*ViterbiDecoder) Flush ¶
func (d *ViterbiDecoder) Flush() []byte
Flush returns the bits still held in the traceback window, ending the stream.
Decode only emits a bit once the survivor paths have had tracebackDepth steps to converge, so the last few decisions are still pending when the symbols run out. Flush forces them out along the best surviving path, where the usual convergence argument no longer applies. The final few bits are the least reliable in the stream.
func (*ViterbiDecoder) Reset ¶
func (d *ViterbiDecoder) Reset()
Reset returns the decoder to the start of a stream.