CCSDS 124.0-B-1 C++ 1.0.0
CCSDS 124.0-B-1 Lossless Compression
Loading...
Searching...
No Matches
decompressor.hpp
Go to the documentation of this file.
1
26#ifndef CCSDS124_DECOMPRESSOR_HPP
27#define CCSDS124_DECOMPRESSOR_HPP
28
29#include "bitreader.hpp"
30#include "bitvector.hpp"
31#include "config.hpp"
32#include "decoder.hpp"
33#include "error.hpp"
34
35namespace ccsds124 {
36
45template <std::size_t N> class Decompressor {
46public:
52 explicit Decompressor(std::uint8_t robustness = 0) noexcept
53 : robustness_(robustness > MAX_ROBUSTNESS ? static_cast<std::uint8_t>(MAX_ROBUSTNESS)
54 : robustness) {
55 reset();
56 }
57
63 void set_initial_mask(const BitVector<N>& initial_mask) noexcept {
64 initial_mask_ = initial_mask;
65 mask_ = initial_mask;
66 }
67
71 void reset() noexcept {
72 t_ = 0;
73 mask_ = initial_mask_;
74 prev_output_.zero();
75 Xt_.zero();
76 }
77
85 Error decompress_packet(BitReader& reader, BitVector<N>& output) noexcept {
86 // Copy previous output as prediction base
87 output = prev_output_;
88
89 // Clear positive changes tracker
90 Xt_.zero();
91
92 // ====================================================================
93 // Parse h_t: Mask change information
94 // h_t = RLE(X_t) || BIT_4(V_t) || e_t || k_t || c_t || d_t
95 // ====================================================================
96
97 // Decode RLE(X_t) - mask changes
98 BitVector<N> Xt;
99 auto status = rle_decode(reader, Xt);
100 if (status != Error::Ok) {
101 return status;
102 }
103
104 // Read BIT_4(V_t) - effective robustness
105 if (reader.remaining() < 4) {
106 return Error::Underflow;
107 }
108 std::uint32_t vt_raw = reader.read_bits(4);
109 std::uint8_t Vt = static_cast<std::uint8_t>(vt_raw & 0x0FU);
110
111 // Process e_t, k_t, c_t if V_t > 0 and there are changes
112 int ct = 0;
113 std::size_t change_count = Xt.hamming_weight();
114
115 if (Vt > 0 && change_count > 0) {
116 // Read e_t
117 int et = reader.read_bit();
118 if (et < 0) {
119 return Error::Underflow;
120 }
121
122 if (et == 1) {
123 // Read k_t bits and apply mask updates in single pass
124 // Iterate by set bits in Xt using word-by-word traversal
125 for (std::size_t word = 0; word < BitVector<N>::NUM_WORDS; ++word) {
126 std::uint32_t xt_word = Xt.data()[word];
127 // Loop bounded by popcount - guarantees termination
128 for (int bits_remaining = __builtin_popcount(xt_word); bits_remaining > 0;
129 --bits_remaining) {
130 // Find MSB position (forward order)
131 int clz = detail::extract_msb(xt_word);
132 std::size_t global_pos = (word * 32) + static_cast<std::size_t>(clz);
133
134 if (global_pos < N) {
135 int kt_bit = reader.read_bit();
136 if (kt_bit < 0) {
137 return Error::Underflow;
138 }
139 // kt=1 means positive update (mask becomes 0)
140 // kt=0 means negative update (mask becomes 1)
141 if (kt_bit > 0) {
142 mask_.set_bit_unchecked(global_pos, 0);
143 Xt_.set_bit_unchecked(global_pos, 1); // Track positive change
144 } else {
145 mask_.set_bit_unchecked(global_pos, 1);
146 }
147 }
148 }
149 }
150
151 // Read c_t
152 ct = reader.read_bit();
153 if (ct < 0) {
154 return Error::Underflow;
155 }
156 } else {
157 // et = 0: all updates are negative (mask bits become 1)
158 // Iterate by set bits in Xt
159 for (std::size_t word = 0; word < BitVector<N>::NUM_WORDS; ++word) {
160 std::uint32_t xt_word = Xt.data()[word];
161 // Loop bounded by popcount - guarantees termination
162 for (int bits_remaining = __builtin_popcount(xt_word); bits_remaining > 0;
163 --bits_remaining) {
164 int clz = detail::extract_msb(xt_word);
165 std::size_t global_pos = (word * 32) + static_cast<std::size_t>(clz);
166 if (global_pos < N) {
167 mask_.set_bit_unchecked(global_pos, 1);
168 }
169 }
170 }
171 }
172 } else if (Vt == 0 && change_count > 0) {
173 // Vt = 0: toggle mask bits at change positions
174 // Iterate by set bits in Xt
175 for (std::size_t word = 0; word < BitVector<N>::NUM_WORDS; ++word) {
176 std::uint32_t xt_word = Xt.data()[word];
177 // Loop bounded by popcount - guarantees termination
178 for (int bits_remaining = __builtin_popcount(xt_word); bits_remaining > 0;
179 --bits_remaining) {
180 int clz = detail::extract_msb(xt_word);
181 std::size_t global_pos = (word * 32) + static_cast<std::size_t>(clz);
182 if (global_pos < N) {
183 int current_val = mask_.get_bit_unchecked(global_pos);
184 mask_.set_bit_unchecked(global_pos, current_val == 0 ? 1 : 0);
185 }
186 }
187 }
188 }
189 // else: No changes to apply (change_count == 0)
190
191 // Read d_t
192 int dt = reader.read_bit();
193 if (dt < 0) {
194 return Error::Underflow;
195 }
196
197 // ====================================================================
198 // Parse q_t: Optional full mask
199 // ====================================================================
200
201 int rt = 0;
202
203 // dt=1 means both ft=0 and rt=0 (optimization per CCSDS Eq. 13)
204 // dt=0 means we need to read ft and rt from the stream
205 if (dt == 0) {
206 // Read ft flag
207 int ft = reader.read_bit();
208 if (ft < 0) {
209 return Error::Underflow;
210 }
211
212 if (ft == 1) {
213 // Full mask follows: decode RLE(M XOR (M<<))
214 BitVector<N> mask_diff;
215 status = rle_decode(reader, mask_diff);
216 if (status != Error::Ok) {
217 return status;
218 }
219
220 // Reverse the horizontal XOR to get the actual mask.
221 // HXOR encoding: HXOR[i] = M[i] XOR M[i+1], with HXOR[F-1] = M[F-1]
222 // Reversal: start from LSB (position F-1) and work towards MSB (position 0)
223 // M[F-1] = HXOR[F-1] (just copy)
224 // M[i] = HXOR[i] XOR M[i+1] for i < F-1
225
226 // Copy LSB bit directly (position F-1 in bitvector)
227 int current = mask_diff.get_bit(N - 1);
228 mask_.set_bit(N - 1, current);
229
230 // Process remaining bits from F-2 down to 0
231 for (std::size_t i = N - 1; i > 0; --i) {
232 std::size_t pos = i - 1;
233 int hxor_bit = mask_diff.get_bit(pos);
234 // M[pos] = HXOR[pos] XOR M[pos+1] = HXOR[pos] XOR current
235 current = hxor_bit ^ current;
236 mask_.set_bit(pos, current);
237 }
238 }
239
240 // Read rt flag
241 rt = reader.read_bit();
242 if (rt < 0) {
243 return Error::Underflow;
244 }
245 }
246
247 if (rt == 1) {
248 // Full packet follows: COUNT(F) || I_t
249 std::uint32_t packet_length = 0;
250 status = count_decode(reader, packet_length);
251 if (status != Error::Ok) {
252 return status;
253 }
254
255 // Read full packet
256 for (std::size_t i = 0; i < N; ++i) {
257 int bit = reader.read_bit();
258 if (bit < 0) {
259 return Error::Underflow;
260 }
261 output.set_bit(i, bit > 0 ? 1 : 0);
262 }
263 } else {
264 // Compressed: extract unpredictable bits
265 BitVector<N> extraction_mask;
266
267 if (ct == 1 && Vt > 0) {
268 // BE(I_t, (X_t OR M_t))
269 extraction_mask.or_of(mask_, Xt_);
270 } else {
271 // BE(I_t, M_t)
272 extraction_mask = mask_;
273 }
274
275 // Insert unpredictable bits
276 status = bit_insert(reader, output, extraction_mask);
277 if (status != Error::Ok) {
278 return status;
279 }
280 }
281
282 // ====================================================================
283 // Update state for next cycle
284 // ====================================================================
285
286 prev_output_ = output;
287 ++t_;
288
289 return Error::Ok;
290 }
291
295 std::size_t time_index() const noexcept {
296 return t_;
297 }
298
302 std::uint8_t robustness() const noexcept {
303 return robustness_;
304 }
305
309 const BitVector<N>& mask() const noexcept {
310 return mask_;
311 }
312
313private:
314 // Configuration
315 std::uint8_t robustness_;
316
317 // State
318 BitVector<N> initial_mask_;
319 BitVector<N> mask_;
320 BitVector<N> prev_output_;
321 BitVector<N> Xt_; // Positive changes tracker
322
323 // Cycle counter
324 std::size_t t_ = 0;
325};
326
327} // namespace ccsds124
328
329#endif // CCSDS124_DECOMPRESSOR_HPP
Sequential bit reading from compressed data.
Fixed-length bit vector with static allocation.
Sequential bit reader for compressed data.
Definition bitreader.hpp:38
Fixed-length bit vector with compile-time size.
Definition bitvector.hpp:90
int get_bit(std::size_t pos) const noexcept
Get bit value at position.
std::size_t hamming_weight() const noexcept
Count number of set bits (Hamming weight).
void or_of(const BitVector &a, const BitVector &b) noexcept
Compute OR of two vectors.
word_t * data() noexcept
Get raw data pointer (for advanced use).
CCSDS 124.0-B-1 decompressor with static memory allocation.
const BitVector< N > & mask() const noexcept
Get current mask.
void set_initial_mask(const BitVector< N > &initial_mask) noexcept
Set the initial mask.
void reset() noexcept
Reset decompressor to initial state.
Decompressor(std::uint8_t robustness=0) noexcept
Construct decompressor with configuration.
std::uint8_t robustness() const noexcept
Get robustness level.
std::size_t time_index() const noexcept
Get current time index.
Error decompress_packet(BitReader &reader, BitVector< N > &output) noexcept
Decompress a single compressed packet.
CCSDS 124.0-B-1 compile-time configuration.
CCSDS 124.0-B-1 decoding functions (COUNT, RLE, bit insert).
CCSDS 124.0-B-1 error handling.
constexpr std::size_t MAX_ROBUSTNESS
Definition config.hpp:53
int extract_msb(std::uint32_t &word) noexcept
Extract and clear the most significant set bit from a word.
Definition bitvector.hpp:56
Error
Error codes for error-code-based error handling.
Definition error.hpp:29
@ Underflow
Buffer underflow (not enough data)
Error count_decode(BitReader &reader, std::uint32_t &value) noexcept
Counter decoding (inverse of CCSDS Section 5.2.2, Equation 9).
Definition decoder.hpp:50
Error rle_decode(BitReader &reader, BitVector< N > &result) noexcept
Run-length decoding (inverse of CCSDS Section 5.2.3, Equation 10).
Definition decoder.hpp:132
Error bit_insert(BitReader &reader, BitVector< N > &data, const BitVector< N > &mask) noexcept
Bit insertion (inverse of CCSDS Section 5.2.4, Equation 11).
Definition decoder.hpp:175