Documentation
¶
Overview ¶
Package ocsc implements the data-conditioning chain of the CCSDS optical communications coding and synchronization standard, CCSDS 142.0-B-1 (August 2019).
This is deep-space laser communication in the High Photon Efficiency regime. The full standard specifies SCPPM (serially concatenated convolutional coding with pulse-position modulation) and everything that surrounds it. This package implements the deterministic front half of that chain, the part that is pure bit manipulation:
transfer frames -> attach sync marker (clause 3.3) ASM 1ACFFC1D -> slice into blocks (clause 3.4) k bits, zero-filled -> pseudo-randomize (clause 3.5) g(D) = D^8+D^7+D^5+D^3+1 -> attach CRC-32 (clause 3.6) h(X) = X^32+X^29+X^18+X^14+X^3+1 -> attach termination bits (clause 3.7) two zeros -> SCPPM encoder input block
What follows (the SCPPM encoder proper, the channel interleaver, the codeword sync marker, the slot mapper) is coupled to the modulation and is not here. Neither is iterative SCPPM decoding: that is a research-grade job and does not belong in a wire-format library. What is here on the receive side is everything after the decoder: Recover synchronizes frames on their sync markers (clause 3.14.1) and delivers each with the quality indicator of Clause 3.14.2 and the sequence indicator of clause 3.15.
Condition is the batch form of the send side; Conditioner is the streaming form the NOTE under clause 3.2 permits, carrying partial blocks between pushes and zero-filling only at explicit closure.
Everything is bits, not octets ¶
The block lengths of table 3-1 are 5006, 7526 and 10046 binary digits. None is a multiple of eight. So this package works in BitString throughout, and converting to octets is something you do at the very end, if at all.
Index ¶
- Constants
- Variables
- func ComputeCRC32(block *BitString) uint32
- func DefaultASM() []byte
- func PNBit(i int) uint8
- func PNSequence(n int) []uint8
- func StripASM(smtf *BitString) ([]byte, error)
- type BitString
- func AttachASM(frame []byte) (*BitString, error)
- func AttachCRC(block *BitString) *BitString
- func AttachTermination(block *BitString) *BitString
- func BitStringFromBits(data []byte, length int) *BitString
- func BitStringFromBytes(data []byte) *BitString
- func Condition(frames [][]byte, rate CodeRate) ([]*BitString, error)
- func Derandomize(block *BitString) *BitString
- func NewBitString(length int) *BitString
- func Randomize(block *BitString) *BitString
- func Slice(smtfs *BitString, rate CodeRate) ([]*BitString, error)
- func StripTermination(block *BitString) (*BitString, error)
- func Unslice(blocks []*BitString) *BitString
- func VerifyCRC(block *BitString) (*BitString, bool)
- func (b *BitString) Append(v uint8)
- func (b *BitString) AppendBits(other *BitString, count int)
- func (b *BitString) AppendBytes(data []byte)
- func (b *BitString) Bit(i int) uint8
- func (b *BitString) Bytes() []byte
- func (b *BitString) Equal(other *BitString) bool
- func (b *BitString) Len() int
- func (b *BitString) SetBit(i int, v uint8)
- func (b *BitString) Slice(start, end int) *BitString
- func (b *BitString) XorBit(i int, v uint8)
- type CodeRate
- type Conditioner
- type RecoveredFrame
Constants ¶
const ( // KOneThird is the information block size at code rate 1/3. KOneThird = 5006 // KOneHalf is the information block size at code rate 1/2. KOneHalf = 7526 // KTwoThirds is the information block size at code rate 2/3. KTwoThirds = 10046 // CRCBits is the width of the attached CRC (clause 3.6.1.1). CRCBits = 32 // TerminationBits is how many zeros clause 3.7 appends. TerminationBits = 2 // MaxFrameLength is the upper bound on the transfer frame length managed // parameter, in octets (clause 5.2, table 5-1: "Integer (max 65536)"). MaxFrameLength = 65536 )
Information block sizes in binary digits, per table 3-1.
k is the block the slicer produces; k-hat is what the SCPPM encoder receives, after 32 CRC digits and 2 termination digits are added. The arithmetic is worth seeing: 5006 + 32 + 2 = 5040, and likewise for the others.
const ASMBits = 32
ASMBits is the width of the sync marker in binary digits (clause 3.3.1).
const CRC32Polynomial uint32 = 0x20044009
CRC32Polynomial is the generator of clause 3.6.2.2, MSB-first, with the implicit X^32 term dropped.
const PNPeriod = 255
PNPeriod is how many digits pass before the sequence repeats (clause 3.5.3.1).
Variables ¶
var ( // ErrDataTooShort indicates the input is shorter than the fields it must contain. ErrDataTooShort = errors.New("data too short for the optical field being read") // ErrInvalidASM indicates the Attached Sync Marker is not the 1ACFFC1D of // CCSDS 142.0-B-1 clause 3.3.2. ErrInvalidASM = errors.New("invalid attached sync marker: expected 1ACFFC1D") // ErrInvalidCodeRate indicates a code rate outside the three of table 3-1. ErrInvalidCodeRate = errors.New("invalid code rate: must be 1/3, 1/2, or 2/3") // ErrCRCMismatch indicates a code block whose attached CRC-32 did not verify. ErrCRCMismatch = errors.New("CRC-32 mismatch: the code block is corrupt") // ErrInvalidBlockLength indicates a code block that is not the k-hat // length its code rate requires. ErrInvalidBlockLength = errors.New("code block is not the length its code rate requires") // ErrInvalidTermination indicates a code block whose two termination bits // are not zero, contrary to clause 3.7. ErrInvalidTermination = errors.New("termination bits are not zero") // ErrEmptyFrame indicates an attempt to mark an empty transfer frame. ErrEmptyFrame = errors.New("cannot attach a sync marker to an empty transfer frame") // ErrFrameTooLong indicates a transfer frame longer than the 65536-octet // bound of the managed parameters (CCSDS 142.0-B-1 clause 5.2, table 5-1). ErrFrameTooLong = errors.New("transfer frame exceeds the 65536-octet managed-parameter bound") // ErrConditionerClosed indicates use of a Conditioner after Close: Clause 3.4.2.1.1 // permits zero fill only at transmission closure, so a closed stream is over. ErrConditionerClosed = errors.New("the conditioner is closed: transmission closure has been declared") )
Sentinel errors returned by the optical coding and synchronization codecs.
var ASM = [4]byte{0x1A, 0xCF, 0xFC, 0x1D}
ASM is the Attached Synchronization Marker of clause 3.3.2: the 32-bit sequence 1ACFFC1D.
It is the same marker TM uses for a CADU, which is convenient: a ground system that already hunts for 1ACFFC1D needs no new pattern.
Functions ¶
func ComputeCRC32 ¶
ComputeCRC32 returns the optical CRC-32 over a bit string.
The register starts at all ones, matching the Σ X^(k+j) term of clause 3.6.2.2. Because a block length need not be a multiple of eight, this walks bits rather than octets whenever the tail is partial.
func PNSequence ¶
PNSequence returns the first n digits of the pseudo-random sequence, one bit per entry.
Types ¶
type BitString ¶
type BitString struct {
// contains filtered or unexported fields
}
BitString is a run of binary digits with a length that need not be a multiple of eight.
func AttachASM ¶
AttachASM builds a Sync-Marked Transfer Frame, per clause 3.3.1: the marker followed by the transfer frame.
The frame must fit the transfer frame length managed parameter: at most 65536 octets (clause 5.2, table 5-1).
func AttachCRC ¶
AttachCRC appends the 32 check digits to a block, per clause 3.6.1.1, returning a block of k + 32 digits.
func AttachTermination ¶
AttachTermination appends the two zero digits of clause 3.7, producing an SCPPM encoder input block of k-hat digits.
func BitStringFromBits ¶
BitStringFromBits wraps octets as a bit string of the given bit length. Bits past length in the final octet are cleared.
func BitStringFromBytes ¶
BitStringFromBytes wraps octets as a bit string of exactly 8*len(data) bits.
func Condition ¶
Condition runs the full send-side chain over a run of transfer frames, returning SCPPM encoder input blocks.
Each frame gets a sync marker (clause 3.3), the marked frames are sliced into k-digit blocks with zero fill (clause 3.4), each block is randomized (clause 3.5), gets a CRC (clause 3.6), and gets two termination digits (clause 3.7).
Condition is a batch call: it treats its input as a complete transmission, so the call itself is the transmission closure of clause 3.4.2.1.1 and the final block is zero-filled. Two Condition calls are two transmissions, not one. To condition one transmission across several calls, carrying partial blocks between them, use a Conditioner.
What comes back is what the SCPPM encoder would take as input. This package stops there.
func Derandomize ¶
Derandomize reverses Randomize. It is the same operation, named for what the receiver is doing.
func NewBitString ¶
NewBitString returns an all-zero bit string of the given length.
func Randomize ¶
Randomize XORs a block with the pseudo-random sequence, per clause 3.5.1.1.
Clause 3.5.3.1: the sequence begins at the first digit of each block, so every block is randomized independently. The operation is its own inverse.
func Slice ¶
Slice cuts a stream of Sync-Marked Transfer Frames into information blocks of k binary digits, per CCSDS 142.0-B-1 clause 3.4.
Clause 3.4.2.1.1: at transmission closure the output is zero-filled with the minimum number of zeros needed to make its length a multiple of k. So the final block is always full, padded if it has to be.
Slice is a batch call: the end of its input is treated as transmission closure, so the fill lands in this call's final block. To slice one transmission across several calls, use a Conditioner, which carries the partial block between calls and fills only at explicit Close.
Note the frames are treated as one continuous bit stream, not as separate units: a block can straddle a frame boundary. Figure 3-3 shows exactly that. The ASM on each frame is what lets the receiver find the boundaries again.
func StripTermination ¶
StripTermination removes the two termination digits, checking they are zero.
func Unslice ¶
Unslice concatenates information blocks back into one bit stream.
The zero fill the slicer added is still there: only the receiver's frame synchronization, hunting for the ASM, can tell padding from data. This returns the bits as they are.
func VerifyCRC ¶
VerifyCRC reports whether a block ending in its own 32 check digits is intact, and returns the block without them.
func (*BitString) AppendBits ¶
AppendBits adds the leading count bits of other.
func (*BitString) AppendBytes ¶
AppendBytes adds every bit of data.
type CodeRate ¶
type CodeRate uint8
CodeRate selects the SCPPM code rate, a managed parameter per clause 3.4.1.
func (CodeRate) EncoderInputSize ¶
EncoderInputSize returns k-hat, the SCPPM encoder input block length in binary digits: k plus the CRC and termination digits (table 3-1, clause 3.7).
func (CodeRate) InformationBlockSize ¶
InformationBlockSize returns k, the slicer's output length in binary digits, for a code rate (table 3-1).
type Conditioner ¶
type Conditioner struct {
// contains filtered or unexported fields
}
Conditioner is the streaming form of Condition.
The NOTE under clause 3.2 says encoding may be performed in a streaming fashion: not all transfer frames of a session need be available when encoding begins, and their total number need not be known a priori. A Conditioner does exactly that. Each Push marks the frames it is given (clause 3.3), appends them to the carried bit stream, and returns every information block that is now complete, conditioned (clause 3.5-clause 3.7). Bits short of a full block stay buffered for the next Push, no fill is inserted mid-stream, because Clause 3.4.2.1.1 permits zero fill only at transmission closure.
Closure is explicit: Close zero-fills whatever remains into a final block and ends the stream. After Close, the Conditioner refuses further use.
A Conditioner is not safe for concurrent use.
func NewConditioner ¶
func NewConditioner(rate CodeRate) (*Conditioner, error)
NewConditioner returns a Conditioner for one transmission at the given rate.
func (*Conditioner) Close ¶
func (c *Conditioner) Close() ([]*BitString, error)
Close declares transmission closure (clause 3.4.2.1.1): the buffered remainder, if any, is zero-filled to a full block, conditioned, and returned. The Conditioner cannot be used again afterwards.
func (*Conditioner) Pending ¶
func (c *Conditioner) Pending() int
Pending reports how many bits are buffered awaiting a full block.
type RecoveredFrame ¶
type RecoveredFrame struct {
// Data is the transfer frame.
Data []byte
// Valid is the Quality Indicator of clause 3.14.2: true when every block
// carrying any of this frame's bits verified its CRC, false when the
// frame was recovered from one or more bad blocks.
Valid bool
// Gap is the Sequence Indicator of clause 3.15: false ('zero') when this frame
// is the direct successor of the previous one, true ('one') when a gap
// was detected before it.
Gap bool
}
RecoveredFrame is one transfer frame delivered by Recover, carrying the two per-frame service parameters of annex B.
func Recover ¶
func Recover(blocks []*BitString, rate CodeRate, frameLength int) (frames []RecoveredFrame, badBlocks []int, err error)
Recover reverses Condition: it takes SCPPM encoder input blocks, checks each CRC, and returns the transfer frames.
frameLength is the transfer frame size in octets, at most 65536 (clause 5.2). It is needed because the slicer's zero fill (clause 3.4.2.1.1) is indistinguishable from frame data once it is in the stream: nothing in the conditioning chain records where the real data stopped. Frame length is a managed parameter fixed for a mission phase, so the receiver always knows it. Pass zero to take each frame as running to the next sync marker, which leaves the trailing fill attached to the last one.
A block whose CRC fails is reported through badBlocks rather than silently dropped, and every frame recovered from a failing block comes back with Valid false, because clause 3.14.2 marks frames recovered from an incorrectly decoded codeword as invalid rather than discarding them outright. Each frame's Gap field is the Sequence Indicator of clause 3.15.