Coverage for ccsds124/encode.py: 100%

57 statements  

« 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). 

3 

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 

8 

9MicroPython compatible - no typing module imports. 

10""" 

11 

12import math 

13 

14# Import for type hints only 

15if False: # noqa: SIM108 

16 from ccsds124.bitbuffer import BitBuffer 

17 from ccsds124.bitvector import BitVector 

18 

19 

20def count_encode(output: "BitBuffer", a: int) -> None: 

21 """ 

22 Encode a positive integer using COUNT encoding (CCSDS Equation 9). 

23 

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 

28 

29 Args: 

30 output: BitBuffer to append encoded bits to 

31 a: Positive integer to encode (1 to 65535) 

32 

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]") 

38 

39 if a == 1: 

40 # Case 1: A = 1 → '0' 

41 output.append_bit(0) 

42 

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) 

48 

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) 

53 

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) 

59 

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 

64 

65 # Append E-bit value, MSB first 

66 for i in range(e - 1, -1, -1): 

67 output.append_bit((value >> i) & 1) 

68 

69 

70def rle_encode(output: "BitBuffer", bv: "BitVector") -> None: 

71 """ 

72 Encode a bit vector using RLE encoding (CCSDS Equation 10). 

73 

74 RLE(a) = COUNT(C0) || COUNT(C1) || ... || COUNT(C_{H(a)-1}) || '10' 

75 

76 where Ci = 1 + (count of consecutive '0' bits before i-th '1' bit) 

77 and H(a) = Hamming weight (number of '1' bits) 

78 

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 ] 

118 

119 # Start from the end of the vector 

120 old_bit_position = bv.length 

121 

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] 

125 

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) 

130 

131 # Find LSB position using DeBruijn sequence 

132 debruijn_index = ((lsb * 0x077CB531) & 0xFFFFFFFF) >> 27 

133 bit_position_in_word = debruijn_lookup[debruijn_index] 

134 

135 # Count from the other side 

136 bit_position_in_word = 32 - bit_position_in_word 

137 

138 # Calculate global bit position 

139 new_bit_position = (word_idx * 32) + bit_position_in_word 

140 

141 # Calculate delta (number of zeros + 1) 

142 delta = old_bit_position - new_bit_position 

143 

144 # Encode the count 

145 count_encode(output, delta) 

146 

147 # Update old position for next iteration 

148 old_bit_position = new_bit_position 

149 

150 # Clear the processed bit 

151 word_data ^= lsb 

152 

153 # Append terminator '10' 

154 output.append_bit(1) 

155 output.append_bit(0) 

156 

157 

158def bit_extract(output: "BitBuffer", data: "BitVector", mask: "BitVector") -> None: 

159 """ 

160 Extract bits using BE (Bit Extraction) in reverse order (CCSDS Equation 11). 

161 

162 BE(a, b) = a_{g_{H(b)-1}} || ... || a_{g_1} || a_{g_0} 

163 

164 where g_i is the position of the i-th '1' bit in b (MSB to LSB order) 

165 

166 Extracts bits from 'data' at positions where 'mask' has '1' bits. 

167 Output order: highest position to lowest (reverse). 

168 

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 

173 

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") 

179 

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) 

185 

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) 

191 

192 

193def bit_extract_forward( 

194 output: "BitBuffer", data: "BitVector", mask: "BitVector" 

195) -> None: 

196 """ 

197 Extract bits in forward order (for kt component). 

198 

199 Same as bit_extract but outputs bits from lowest position to highest. 

200 

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 

205 

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") 

211 

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)