Coverage for ccsds124/decode.py: 100%
56 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-29 15:01 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-29 15:01 +0000
1"""
2CCSDS 124.0-B-1 decoding functions (COUNT, RLE, bit insert).
4Implements CCSDS 124.0-B-1 decoding schemes (inverse of encoding):
5- Counter Decoding (COUNT) - inverse of Section 5.2.2
6- Run-Length Decoding (RLE) - inverse of Section 5.2.3
7- Bit Insertion - inverse of Section 5.2.4 BE
9MicroPython compatible - no typing module imports.
10"""
12# Import for type hints only
13if False: # noqa: SIM108
14 from ccsds124.bitreader import BitReader
15 from ccsds124.bitvector import BitVector
18def count_decode(reader: "BitReader") -> int:
19 """
20 Decode a COUNT-encoded value.
22 Decoding rules (inverse of CCSDS Equation 9):
23 - '0' → 1
24 - '10' → 0 (terminator)
25 - '110' + BIT5 → value + 2 (range 2-33)
26 - '111' + BIT_E → value + 2 (range 34+)
28 Args:
29 reader: BitReader to read encoded bits from
31 Returns:
32 Decoded positive integer (0 for terminator, 1+ for values)
33 """
34 # Read first bit
35 first_bit = reader.read_bit()
37 if first_bit == 0:
38 # Case 1: '0' → 1
39 return 1
41 # First bit was 1, read second bit
42 second_bit = reader.read_bit()
44 if second_bit == 0:
45 # Case 2: '10' → terminator (0)
46 return 0
48 # First two bits were '11', read third bit
49 third_bit = reader.read_bit()
51 if third_bit == 0:
52 # Case 3: '110' + BIT5 → value + 2 (range 2-33)
53 value = reader.read_bits(5)
54 return value + 2
56 else:
57 # Case 4: '111' + BIT_E → value + 2 (range 34+)
58 # Need to determine E by reading bits until we can decode
59 # E = 2*floor(log2(value)+1) - 6
60 # We read bits incrementally to find the value
62 # Start with 6 bits (minimum for value >= 32)
63 e = 6
64 value = reader.read_bits(e)
66 # Check if we have the right number of bits
67 # For value v, E should be 2*floor(log2(v)+1) - 6
68 while True:
69 # Calculate expected E for this value
70 if value == 0:
71 expected_e = 0
72 else:
73 # Find floor(log2(value))
74 log_val = 0
75 temp = value
76 while temp > 1:
77 temp >>= 1
78 log_val += 1
79 expected_e = 2 * (log_val + 1) - 6
81 if expected_e == e:
82 # We have the right number of bits
83 break
85 # Need more bits
86 e += 2
87 # Read 2 more bits and shift in
88 extra = reader.read_bits(2)
89 value = (value << 2) | extra
91 return value + 2
94def rle_decode(reader: "BitReader", length: int) -> "BitVector":
95 """
96 Decode an RLE-encoded bit vector.
98 Decoding rules (inverse of CCSDS Equation 10):
99 - Read COUNT values until terminator (0)
100 - Each COUNT value represents position delta to next '1' bit
102 Args:
103 reader: BitReader to read encoded bits from
104 length: Length of the output bit vector
106 Returns:
107 Decoded BitVector
108 """
109 from ccsds124.bitvector import BitVector
111 result = BitVector(length)
113 # Start from end of vector
114 position = length
116 while True:
117 # Decode next count
118 count = count_decode(reader)
120 if count == 0:
121 # Terminator reached
122 break
124 # Move position back by count. A delta beyond the remaining bit
125 # position means the encoding is invalid for this vector length
126 # (GOTCHAS #20): reject it instead of silently skipping.
127 if count > position:
128 raise ValueError("invalid RLE delta: exceeds remaining bit position")
129 position -= count
131 # Set the bit at this position
132 result.set_bit(position, 1)
134 return result
137def bit_insert(reader: "BitReader", data: "BitVector", mask: "BitVector") -> None:
138 """
139 Insert bits into data at positions specified by mask (inverse of BE).
141 Reads bits from reader and inserts them into data at positions
142 where mask has '1' bits. Bits are read in reverse order (highest
143 position to lowest) to match BE extraction order.
145 Args:
146 reader: BitReader to read bits from
147 data: BitVector to insert bits into
148 mask: BitVector indicating which positions to fill
149 """
150 # Collect positions of '1' bits in mask
151 positions = []
152 for i in range(mask.length):
153 if mask.get_bit(i):
154 positions.append(i)
156 # Insert bits in reverse order (highest position to lowest)
157 # This matches the extraction order in bit_extract
158 for i in range(len(positions) - 1, -1, -1):
159 pos = positions[i]
160 bit = reader.read_bit()
161 data.set_bit(pos, bit)
164def bit_insert_forward(
165 reader: "BitReader", data: "BitVector", mask: "BitVector"
166) -> None:
167 """
168 Insert bits in forward order (for kt component).
170 Same as bit_insert but reads bits from lowest position to highest.
172 Args:
173 reader: BitReader to read bits from
174 data: BitVector to insert bits into
175 mask: BitVector indicating which positions to fill
176 """
177 # Insert bits in forward order
178 for i in range(mask.length):
179 if mask.get_bit(i):
180 bit = reader.read_bit()
181 data.set_bit(i, bit)