Coverage for ccsds124/decode.py: 100%

56 statements  

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

3 

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 

8 

9MicroPython compatible - no typing module imports. 

10""" 

11 

12# Import for type hints only 

13if False: # noqa: SIM108 

14 from ccsds124.bitreader import BitReader 

15 from ccsds124.bitvector import BitVector 

16 

17 

18def count_decode(reader: "BitReader") -> int: 

19 """ 

20 Decode a COUNT-encoded value. 

21 

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

27 

28 Args: 

29 reader: BitReader to read encoded bits from 

30 

31 Returns: 

32 Decoded positive integer (0 for terminator, 1+ for values) 

33 """ 

34 # Read first bit 

35 first_bit = reader.read_bit() 

36 

37 if first_bit == 0: 

38 # Case 1: '0' → 1 

39 return 1 

40 

41 # First bit was 1, read second bit 

42 second_bit = reader.read_bit() 

43 

44 if second_bit == 0: 

45 # Case 2: '10' → terminator (0) 

46 return 0 

47 

48 # First two bits were '11', read third bit 

49 third_bit = reader.read_bit() 

50 

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 

55 

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 

61 

62 # Start with 6 bits (minimum for value >= 32) 

63 e = 6 

64 value = reader.read_bits(e) 

65 

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 

80 

81 if expected_e == e: 

82 # We have the right number of bits 

83 break 

84 

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 

90 

91 return value + 2 

92 

93 

94def rle_decode(reader: "BitReader", length: int) -> "BitVector": 

95 """ 

96 Decode an RLE-encoded bit vector. 

97 

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 

101 

102 Args: 

103 reader: BitReader to read encoded bits from 

104 length: Length of the output bit vector 

105 

106 Returns: 

107 Decoded BitVector 

108 """ 

109 from ccsds124.bitvector import BitVector 

110 

111 result = BitVector(length) 

112 

113 # Start from end of vector 

114 position = length 

115 

116 while True: 

117 # Decode next count 

118 count = count_decode(reader) 

119 

120 if count == 0: 

121 # Terminator reached 

122 break 

123 

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 

130 

131 # Set the bit at this position 

132 result.set_bit(position, 1) 

133 

134 return result 

135 

136 

137def bit_insert(reader: "BitReader", data: "BitVector", mask: "BitVector") -> None: 

138 """ 

139 Insert bits into data at positions specified by mask (inverse of BE). 

140 

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. 

144 

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) 

155 

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) 

162 

163 

164def bit_insert_forward( 

165 reader: "BitReader", data: "BitVector", mask: "BitVector" 

166) -> None: 

167 """ 

168 Insert bits in forward order (for kt component). 

169 

170 Same as bit_insert but reads bits from lowest position to highest. 

171 

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)