Coverage for ccsds124/encode.py: 100%
57 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 encoding functions (COUNT, RLE, BE).
4Implements CCSDS 124.0-B-1 Section 5.2 encoding schemes:
5- Counter Encoding (COUNT) - Section 5.2.2, Table 5-1, Equation 9
6- Run-Length Encoding (RLE) - Section 5.2.3, Equation 10
7- Bit Extraction (BE) - Section 5.2.4, Equation 11
9MicroPython compatible - no typing module imports.
10"""
12import math
14# Import for type hints only
15if False: # noqa: SIM108
16 from ccsds124.bitbuffer import BitBuffer
17 from ccsds124.bitvector import BitVector
20def count_encode(output: "BitBuffer", a: int) -> None:
21 """
22 Encode a positive integer using COUNT encoding (CCSDS Equation 9).
24 Encoding rules:
25 - A = 1 → '0'
26 - 2 ≤ A ≤ 33 → '110' + BIT5(A-2)
27 - A ≥ 34 → '111' + BIT_E(A-2) where E = 2*floor(log2(A-2)+1) - 6
29 Args:
30 output: BitBuffer to append encoded bits to
31 a: Positive integer to encode (1 to 65535)
33 Raises:
34 ValueError: If a is out of valid range
35 """
36 if a < 1 or a > 65535:
37 raise ValueError(f"COUNT value {a} out of range [1, 65535]")
39 if a == 1:
40 # Case 1: A = 1 → '0'
41 output.append_bit(0)
43 elif a <= 33:
44 # Case 2: 2 ≤ A ≤ 33 → '110' + BIT5(A-2)
45 output.append_bit(1)
46 output.append_bit(1)
47 output.append_bit(0)
49 # Append 5-bit value of (A-2), MSB first
50 value = a - 2
51 for i in range(4, -1, -1):
52 output.append_bit((value >> i) & 1)
54 else:
55 # Case 3: A ≥ 34 → '111' + BIT_E(A-2)
56 output.append_bit(1)
57 output.append_bit(1)
58 output.append_bit(1)
60 # Calculate E = 2*floor(log2(A-2)+1) - 6
61 value = a - 2
62 log_val = math.log2(value)
63 e = 2 * (int(math.floor(log_val)) + 1) - 6
65 # Append E-bit value, MSB first
66 for i in range(e - 1, -1, -1):
67 output.append_bit((value >> i) & 1)
70def rle_encode(output: "BitBuffer", bv: "BitVector") -> None:
71 """
72 Encode a bit vector using RLE encoding (CCSDS Equation 10).
74 RLE(a) = COUNT(C0) || COUNT(C1) || ... || COUNT(C_{H(a)-1}) || '10'
76 where Ci = 1 + (count of consecutive '0' bits before i-th '1' bit)
77 and H(a) = Hamming weight (number of '1' bits)
79 Args:
80 output: BitBuffer to append encoded bits to
81 bv: BitVector to encode
82 """
83 # DeBruijn lookup table for fast LSB finding
84 debruijn_lookup = [
85 1,
86 2,
87 29,
88 3,
89 30,
90 15,
91 25,
92 4,
93 31,
94 23,
95 21,
96 16,
97 26,
98 18,
99 5,
100 9,
101 32,
102 28,
103 14,
104 24,
105 22,
106 20,
107 17,
108 8,
109 27,
110 13,
111 19,
112 7,
113 12,
114 6,
115 11,
116 10,
117 ]
119 # Start from the end of the vector
120 old_bit_position = bv.length
122 # Process words in reverse order (from high to low)
123 for word_idx in range(bv.num_words - 1, -1, -1):
124 word_data = bv._data[word_idx]
126 # Process all set bits in this word
127 while word_data != 0:
128 # Isolate the LSB: x = word & -word
129 lsb = word_data & (-word_data & 0xFFFFFFFF)
131 # Find LSB position using DeBruijn sequence
132 debruijn_index = ((lsb * 0x077CB531) & 0xFFFFFFFF) >> 27
133 bit_position_in_word = debruijn_lookup[debruijn_index]
135 # Count from the other side
136 bit_position_in_word = 32 - bit_position_in_word
138 # Calculate global bit position
139 new_bit_position = (word_idx * 32) + bit_position_in_word
141 # Calculate delta (number of zeros + 1)
142 delta = old_bit_position - new_bit_position
144 # Encode the count
145 count_encode(output, delta)
147 # Update old position for next iteration
148 old_bit_position = new_bit_position
150 # Clear the processed bit
151 word_data ^= lsb
153 # Append terminator '10'
154 output.append_bit(1)
155 output.append_bit(0)
158def bit_extract(output: "BitBuffer", data: "BitVector", mask: "BitVector") -> None:
159 """
160 Extract bits using BE (Bit Extraction) in reverse order (CCSDS Equation 11).
162 BE(a, b) = a_{g_{H(b)-1}} || ... || a_{g_1} || a_{g_0}
164 where g_i is the position of the i-th '1' bit in b (MSB to LSB order)
166 Extracts bits from 'data' at positions where 'mask' has '1' bits.
167 Output order: highest position to lowest (reverse).
169 Args:
170 output: BitBuffer to append extracted bits to
171 data: BitVector to extract bits from
172 mask: BitVector indicating which bits to extract
174 Raises:
175 ValueError: If data and mask have different lengths
176 """
177 if data.length != mask.length:
178 raise ValueError("Data and mask must have same length")
180 # Collect positions of '1' bits in mask
181 positions = []
182 for i in range(mask.length):
183 if mask.get_bit(i):
184 positions.append(i)
186 # Extract bits in reverse order (highest position to lowest)
187 for i in range(len(positions) - 1, -1, -1):
188 pos = positions[i]
189 bit = data.get_bit(pos)
190 output.append_bit(bit)
193def bit_extract_forward(
194 output: "BitBuffer", data: "BitVector", mask: "BitVector"
195) -> None:
196 """
197 Extract bits in forward order (for kt component).
199 Same as bit_extract but outputs bits from lowest position to highest.
201 Args:
202 output: BitBuffer to append extracted bits to
203 data: BitVector to extract bits from
204 mask: BitVector indicating which bits to extract
206 Raises:
207 ValueError: If data and mask have different lengths
208 """
209 if data.length != mask.length:
210 raise ValueError("Data and mask must have same length")
212 # Collect positions of '1' bits in mask and extract in forward order
213 for i in range(mask.length):
214 if mask.get_bit(i):
215 bit = data.get_bit(i)
216 output.append_bit(bit)