Line data Source code
1 : /**
2 : * @file compress.c
3 : * @brief CCSDS 124.0-B-1 compression algorithm implementation.
4 : *
5 : * @cond INTERNAL
6 : * ============================================================================
7 : * _____ ____
8 : * |_ _|_ _ _ __ __ _ __ _ _ __ __ _ / ___| _ __ __ _ ___ ___
9 : * | |/ _` | '_ \ / _` |/ _` | '__/ _` | \___ \| '_ \ / _` |/ __/ _ \
10 : * | | (_| | | | | (_| | (_| | | | (_| | ___) | |_) | (_| | (_| __/
11 : * |_|\__,_|_| |_|\__,_|\__, |_| \__,_| |____/| .__/ \__,_|\___\___|
12 : * |___/ |_|
13 : * ============================================================================
14 : * @endcond
15 : *
16 : * Implements CCSDS 124.0-B-1 Section 5.3 (Encoding Step):
17 : * - Compressor initialization and state management
18 : * - Main compression algorithm
19 : * - Output packet encoding: oₜ = hₜ ∥ qₜ ∥ uₜ
20 : *
21 : * @authors Georges Labrèche <georges@tanagraspace.com> — https://georges.fyi
22 : * @authors Claude Code (Anthropic) <noreply@anthropic.com>
23 : *
24 : * @see https://ccsds.org/Pubs/124x0b1.pdf CCSDS 124.0-B-1 Standard
25 : */
26 :
27 : #include "ccsds124.h"
28 : #include <string.h>
29 :
30 : /**
31 : * @name Compressor Initialization
32 : * @{
33 : */
34 :
35 :
36 187 : int ccsds124_compressor_init(
37 : ccsds124_compressor_t *comp,
38 : size_t F,
39 : const bitvector_t *initial_mask,
40 : uint8_t robustness,
41 : int pt_limit,
42 : int ft_limit,
43 : int rt_limit
44 : ) {
45 : int result = CCSDS124_ERROR_INVALID_ARG;
46 :
47 187 : if (comp != NULL) {
48 186 : if ((F == 0U) || (F > (size_t)CCSDS124_MAX_PACKET_LENGTH)) {
49 : /* Invalid packet length - result already set */
50 182 : } else if (robustness > (uint8_t)CCSDS124_MAX_ROBUSTNESS) {
51 : /* Invalid robustness - result already set */
52 : } else {
53 : /* Store configuration */
54 178 : comp->F = F;
55 178 : comp->robustness = robustness;
56 178 : comp->pt_limit = pt_limit;
57 178 : comp->ft_limit = ft_limit;
58 178 : comp->rt_limit = rt_limit;
59 :
60 : /* Initialize all bit vectors */
61 178 : (void)bitvector_init(&comp->mask, F);
62 178 : (void)bitvector_init(&comp->prev_mask, F);
63 178 : (void)bitvector_init(&comp->build, F);
64 178 : (void)bitvector_init(&comp->prev_input, F);
65 178 : (void)bitvector_init(&comp->initial_mask, F);
66 :
67 : /* Initialize change history */
68 3026 : for (size_t i = 0U; i < CCSDS124_MAX_HISTORY; i++) {
69 2848 : (void)bitvector_init(&comp->change_history[i], F);
70 2848 : bitvector_zero(&comp->change_history[i]);
71 : }
72 :
73 : /* Initialize flag history for cₜ calculation */
74 3026 : for (size_t i = 0U; i < CCSDS124_MAX_VT_HISTORY; i++) {
75 2848 : comp->new_mask_flag_history[i] = 0U;
76 : }
77 178 : comp->flag_history_index = 0;
78 :
79 : /* Set initial mask */
80 178 : if (initial_mask != NULL) {
81 2 : bitvector_copy(&comp->initial_mask, initial_mask);
82 2 : bitvector_copy(&comp->mask, initial_mask);
83 : } else {
84 176 : bitvector_zero(&comp->initial_mask);
85 176 : bitvector_zero(&comp->mask);
86 : }
87 :
88 : /* Initialize work buffers (pre-allocated to avoid per-packet init) */
89 178 : (void)bitvector_init(&comp->work_prev_build, F);
90 178 : (void)bitvector_init(&comp->work_change, F);
91 178 : (void)bitvector_init(&comp->work_Xt, F);
92 178 : (void)bitvector_init(&comp->work_inverted, F);
93 178 : (void)bitvector_init(&comp->work_shifted, F);
94 178 : (void)bitvector_init(&comp->work_diff, F);
95 :
96 : /* Reset state */
97 178 : ccsds124_compressor_reset(comp);
98 :
99 : result = CCSDS124_OK;
100 : }
101 : }
102 :
103 187 : return result;
104 : }
105 :
106 :
107 246 : void ccsds124_compressor_reset(ccsds124_compressor_t *comp) {
108 246 : if (comp != NULL) {
109 : /* Reset time index */
110 246 : comp->t = 0U;
111 246 : comp->history_index = 0U;
112 :
113 : /* Reset mask to initial */
114 246 : bitvector_copy(&comp->mask, &comp->initial_mask);
115 246 : bitvector_zero(&comp->prev_mask);
116 :
117 : /* Clear build and prev_input */
118 246 : bitvector_zero(&comp->build);
119 246 : bitvector_zero(&comp->prev_input);
120 :
121 : /* Clear change history */
122 4182 : for (size_t i = 0U; i < CCSDS124_MAX_HISTORY; i++) {
123 3936 : bitvector_zero(&comp->change_history[i]);
124 : }
125 :
126 : /* Reset countdown counters (match reference implementation behavior) */
127 246 : comp->pt_counter = comp->pt_limit;
128 246 : comp->ft_counter = comp->ft_limit;
129 246 : comp->rt_counter = comp->rt_limit;
130 : }
131 246 : }
132 :
133 : /** @} */ /* End of Compressor Initialization */
134 :
135 : /**
136 : * @name Internal Helper Functions
137 : * @{
138 : */
139 :
140 : /**
141 : * @brief Get default compression parameters.
142 : *
143 : * Initializes parameters with robustness from compressor and all flags cleared.
144 : *
145 : * @param[out] params Pointer to parameters structure.
146 : * @param[in] comp Pointer to compressor for robustness level.
147 : *
148 : * @note Caller should handle CCSDS initialization requirements
149 : * (ft=1, rt=1 for first Rt+1 packets).
150 : */
151 : static void get_default_params(
152 : ccsds124_params_t *params,
153 : const ccsds124_compressor_t *comp
154 : ) {
155 : /* Default parameters */
156 6 : params->min_robustness = comp->robustness;
157 6 : params->new_mask_flag = 0;
158 6 : params->send_mask_flag = 0;
159 6 : params->uncompressed_flag = 0;
160 :
161 : /* Note: Caller should handle CCSDS initialization requirements
162 : * (ft=1, rt=1 for first Rt+1 packets) */
163 : }
164 :
165 : /** @} */ /* End of Internal Helper Functions */
166 :
167 : /**
168 : * @name CCSDS Helper Functions
169 : * @{
170 : */
171 :
172 :
173 189244 : void ccsds124_compute_robustness_window(
174 : bitvector_t *Xt,
175 : const ccsds124_compressor_t *comp,
176 : const bitvector_t *current_change
177 : ) {
178 : /* Xₜ = <(Dₜ₋ᴿₜ OR Dₜ₋ᴿₜ₊₁ OR ... OR Dₜ)>
179 : * Where <a> means reverse the bit order */
180 :
181 : /* Use Xt directly as accumulator to avoid stack allocations */
182 189244 : Xt->length = comp->F;
183 189244 : Xt->num_words = (((comp->F + 7U) / 8U) + 3U) / 4U;
184 :
185 189244 : if ((comp->robustness == 0U) || (comp->t == 0U)) {
186 : /* Xₜ = Dₜ (no reversal - RLE processes LSB to MSB directly) */
187 11479 : bitvector_copy(Xt, current_change);
188 : } else {
189 : /* Start with current change, accumulate OR directly into Xt */
190 177765 : bitvector_copy(Xt, current_change);
191 :
192 : /* Determine how many historical changes to include */
193 177765 : size_t num_changes = (comp->t < (size_t)comp->robustness) ? comp->t : (size_t)comp->robustness;
194 :
195 : /* OR with historical changes (going backwards from current)
196 : * Use in-place OR: Xt = Xt OR history[i] */
197 592184 : for (size_t i = 1U; i <= num_changes; i++) {
198 414419 : size_t hist_idx = ((comp->history_index + (size_t)CCSDS124_MAX_HISTORY) - i) % (size_t)CCSDS124_MAX_HISTORY;
199 : const bitvector_t *hist = &comp->change_history[hist_idx];
200 :
201 : /* In-place OR at word level */
202 8042874 : for (size_t w = 0U; w < Xt->num_words; w++) {
203 7628455 : Xt->data[w] |= hist->data[w];
204 : }
205 : }
206 : }
207 189244 : }
208 :
209 :
210 189244 : uint8_t ccsds124_compute_effective_robustness(
211 : const ccsds124_compressor_t *comp,
212 : const bitvector_t *current_change
213 : ) {
214 : /* Vₜ = Rₜ + Cₜ (CCSDS Section 5.3.2.2)
215 : * where Cₜ = number of consecutive iterations with no mask changes
216 : *
217 : * For t ≤ Rt: Vt = Rt
218 : * For t > Rt: Vt = Rt + Ct */
219 :
220 : (void)current_change; /* Unused - Ct is computed from history */
221 :
222 189244 : uint8_t Rt = comp->robustness;
223 : uint8_t Vt = Rt;
224 :
225 : /* For t > Rt, compute Ct */
226 189244 : if (comp->t > (size_t)Rt) {
227 : /* Count backwards through history starting from Rt+1 positions back
228 : * (matching reference: start = pt_history_index + Rt + 1) */
229 : uint8_t Ct = 0U;
230 : int done = 0;
231 :
232 1034842 : for (size_t i = (size_t)Rt + 1U; (i <= 15U) && (i <= comp->t) && (done == 0); i++) {
233 846063 : size_t hist_idx = ((comp->history_index + (size_t)CCSDS124_MAX_HISTORY) - i) % (size_t)CCSDS124_MAX_HISTORY;
234 846063 : if (bitvector_hamming_weight(&comp->change_history[hist_idx]) > 0U) {
235 : done = 1; /* Found a change, stop counting */
236 : } else {
237 684344 : Ct++;
238 684344 : if (Ct >= (15U - Rt)) {
239 : done = 1; /* Cap at maximum Ct value */
240 : }
241 : }
242 : }
243 :
244 188779 : Vt = Rt + Ct;
245 188779 : if (Vt > 15U) {
246 : Vt = 15U; /* Cap at 15 (4 bits) */
247 : }
248 : }
249 :
250 189244 : return Vt;
251 : }
252 :
253 :
254 104918 : int ccsds124_has_positive_updates(
255 : const bitvector_t *Xt,
256 : const bitvector_t *mask
257 : ) {
258 : int result = 0; /* No positive updates by default */
259 :
260 : /* eₜ = 1 if any changed bits (in Xₜ) are predictable (mask bit = 0)
261 : * This is equivalent to: (Xt AND (NOT mask)) != 0
262 : * Use word-level operations for efficiency */
263 :
264 104918 : if ((Xt != NULL) && (mask != NULL)) {
265 104918 : size_t num_words = Xt->num_words;
266 104918 : if (mask->num_words < num_words) {
267 : num_words = mask->num_words;
268 : }
269 :
270 2022655 : for (size_t i = 0U; (i < num_words) && (result == 0); i++) {
271 : /* Check if any bit in Xt is set where mask is clear */
272 1917737 : if ((Xt->data[i] & ~mask->data[i]) != 0U) {
273 : result = 1;
274 : }
275 : }
276 : }
277 :
278 104918 : return result;
279 : }
280 :
281 :
282 2 : int ccsds124_compute_ct_flag(
283 : const ccsds124_compressor_t *comp,
284 : uint8_t Vt,
285 : int current_new_mask_flag
286 : ) {
287 : int result = 0;
288 :
289 : /* cₜ = 1 if new_mask_flag was set 2+ times in last Vₜ+1 iterations
290 : * (including current packet) - matches reference implementation */
291 :
292 2 : if (Vt != 0U) {
293 : /* Count how many times new_mask_flag was set.
294 : * Reference checks from current (i=pt_history_index) to current+Vt,
295 : * which is Vt+1 entries including current packet */
296 : int count = 0;
297 :
298 : /* Include current packet's flag */
299 186607 : if (current_new_mask_flag != 0) {
300 : count++;
301 : }
302 :
303 : /* Check history for Vt previous entries */
304 210838 : size_t iterations_to_check = ((size_t)Vt <= comp->t) ? (size_t)Vt : comp->t;
305 :
306 1430630 : for (size_t i = 0U; i < iterations_to_check; i++) {
307 : /* Calculate history index going backwards from previous */
308 1219792 : size_t hist_idx = ((comp->flag_history_index + (size_t)CCSDS124_MAX_VT_HISTORY) - 1U - i) % (size_t)CCSDS124_MAX_VT_HISTORY;
309 1219792 : if (comp->new_mask_flag_history[hist_idx] != 0U) {
310 70606 : count++;
311 : }
312 : }
313 :
314 210838 : if (count >= 2) {
315 : result = 1;
316 : }
317 : }
318 :
319 2 : return result;
320 : }
321 :
322 : /** @} */ /* End of CCSDS Helper Functions */
323 :
324 : /**
325 : * @name Main Compression Functions
326 : * @{
327 : */
328 :
329 :
330 189246 : int ccsds124_compress_packet(
331 : ccsds124_compressor_t *comp,
332 : const bitvector_t *input,
333 : bitbuffer_t *output,
334 : const ccsds124_params_t *params
335 : ) {
336 : int result = CCSDS124_ERROR_INVALID_ARG;
337 :
338 189246 : if ((comp == NULL) || (input == NULL) || (output == NULL)) {
339 : /* Invalid arguments - result already set */
340 189244 : } else if (input->length != comp->F) {
341 : /* Invalid input length - result already set */
342 : } else {
343 : /* Get parameters (use defaults if NULL) */
344 : ccsds124_params_t local_params;
345 : const ccsds124_params_t *effective_params = params;
346 189242 : if (params == NULL) {
347 : get_default_params(&local_params, comp);
348 : effective_params = &local_params;
349 : }
350 :
351 : /* Clear output buffer */
352 189242 : bitbuffer_clear(output);
353 :
354 : /* ====================================================================
355 : * STEP 1: Update Mask and Build Vectors (CCSDS Section 4)
356 : * ==================================================================== */
357 :
358 : /* Use pre-allocated prev_mask from struct */
359 189242 : bitvector_copy(&comp->prev_mask, &comp->mask);
360 :
361 : /* Use pre-allocated work buffer for prev_build */
362 189242 : bitvector_copy(&comp->work_prev_build, &comp->build);
363 :
364 : /* Update build vector (Equation 6) */
365 189242 : if (comp->t > 0U) {
366 189095 : ccsds124_update_build(&comp->build, input, &comp->prev_input,
367 189095 : effective_params->new_mask_flag, comp->t);
368 : }
369 :
370 : /* Update mask vector (Equation 7) */
371 189242 : if (comp->t > 0U) {
372 189095 : ccsds124_update_mask(&comp->mask, input, &comp->prev_input,
373 189095 : &comp->work_prev_build, effective_params->new_mask_flag);
374 : }
375 :
376 : /* Compute change vector (Equation 8) - use pre-allocated work buffer */
377 189242 : ccsds124_compute_change(&comp->work_change, &comp->mask, &comp->prev_mask, comp->t);
378 :
379 : /* Store change in history (circular buffer) */
380 189242 : bitvector_copy(&comp->change_history[comp->history_index], &comp->work_change);
381 :
382 : /* ====================================================================
383 : * STEP 2: Encode Output Packet (CCSDS Section 5.3)
384 : * oₜ = hₜ ∥ qₜ ∥ uₜ
385 : * Full CCSDS encoding per ALGORITHM.md
386 : * ==================================================================== */
387 :
388 : /* Calculate Xₜ (robustness window) - use pre-allocated work buffer */
389 189242 : ccsds124_compute_robustness_window(&comp->work_Xt, comp, &comp->work_change);
390 :
391 : /* Calculate Vₜ (effective robustness) */
392 189242 : uint8_t Vt = ccsds124_compute_effective_robustness(comp, &comp->work_change);
393 :
394 : /* Calculate ḋₜ flag */
395 189242 : uint8_t dt = ((effective_params->send_mask_flag == 0U) && (effective_params->uncompressed_flag == 0U)) ? 1U : 0U;
396 :
397 : /* ====================================================================
398 : * Component hₜ: Mask change information
399 : * hₜ = RLE(Xₜ) ∥ BIT₄(Vₜ) ∥ eₜ ∥ kₜ ∥ cₜ ∥ ḋₜ
400 : * ==================================================================== */
401 :
402 : /* 1. RLE(Xₜ) - Run-length encode the robustness window */
403 189242 : (void)ccsds124_rle_encode(output, &comp->work_Xt);
404 :
405 : /* 2. BIT₄(Vₜ) - 4-bit effective robustness level
406 : * CCSDS encodes Vt directly (reference implementation confirmed) */
407 946210 : for (int i = 3; i >= 0; i--) {
408 756968 : uint32_t vt_shifted = (uint32_t)Vt >> (uint32_t)i;
409 756968 : uint32_t vt_masked = vt_shifted & 1U;
410 756968 : int bit_val = (int)vt_masked;
411 756968 : (void)bitbuffer_append_bit(output, bit_val);
412 : }
413 :
414 : /* 3. eₜ, kₜ, cₜ - Only if Vₜ > 0 and there are mask changes */
415 189242 : if ((Vt > 0U) && (bitvector_hamming_weight(&comp->work_Xt) > 0U)) {
416 : /* Calculate eₜ */
417 104916 : int et = ccsds124_has_positive_updates(&comp->work_Xt, &comp->mask);
418 :
419 104916 : (void)bitbuffer_append_bit(output, et);
420 :
421 104916 : if (et != 0) {
422 : /* kₜ - Output '1' for positive updates (mask=0), '0' for negative updates (mask=1)
423 : * Reference implementation shows kt outputs 1 when mask bit is 0 at changed positions */
424 :
425 : /* Extract INVERTED mask values (1 where mask=0, 0 where mask=1)
426 : * Use pre-allocated work buffer for inverted mask */
427 24231 : bitvector_not(&comp->work_inverted, &comp->mask);
428 :
429 24231 : (void)ccsds124_bit_extract_forward(output, &comp->work_inverted, &comp->work_Xt);
430 :
431 : /* Calculate and encode cₜ */
432 24231 : int ct = ccsds124_compute_ct_flag(comp, Vt, effective_params->new_mask_flag);
433 :
434 24231 : (void)bitbuffer_append_bit(output, ct);
435 : }
436 : }
437 :
438 : /* 4. ḋₜ - Flag indicating if both ḟₜ and ṙₜ are zero */
439 189242 : (void)bitbuffer_append_bit(output, (int)dt);
440 :
441 : /* ====================================================================
442 : * Component qₜ: Optional full mask
443 : * qₜ = ∅ if ḋₜ=1, '1' ∥ RLE(<(Mₜ XOR (Mₜ≪))>) if ḟₜ=1, '0' otherwise
444 : * ==================================================================== */
445 :
446 189242 : if (dt == 0U) { /* Only if ḋₜ = 0 */
447 5275 : if (effective_params->send_mask_flag != 0U) {
448 5005 : (void)bitbuffer_append_bit(output, 1); /* Flag: mask follows */
449 :
450 : /* Encode mask as RLE(M XOR (M<<)) - use pre-allocated work buffers */
451 5005 : bitvector_left_shift(&comp->work_shifted, &comp->mask);
452 5005 : bitvector_xor(&comp->work_diff, &comp->mask, &comp->work_shifted);
453 :
454 5005 : (void)ccsds124_rle_encode(output, &comp->work_diff);
455 : } else {
456 270 : (void)bitbuffer_append_bit(output, 0); /* Flag: no mask */
457 : }
458 : }
459 :
460 : /* ====================================================================
461 : * Component uₜ: Unpredictable bits or full input
462 : * Per ALGORITHM.md, depends on ḋₜ, ṙₜ, cₜ
463 : * ==================================================================== */
464 :
465 189242 : if (effective_params->uncompressed_flag != 0U) {
466 : /* '1' ∥ COUNT(F) ∥ Iₜ */
467 2589 : (void)bitbuffer_append_bit(output, 1); /* Flag: full input follows */
468 :
469 2589 : (void)ccsds124_count_encode(output, (uint32_t)comp->F);
470 :
471 2589 : (void)bitbuffer_append_bitvector(output, input);
472 : } else {
473 186653 : if (dt == 0U) {
474 : /* '0' ∥ BE(...) */
475 2686 : (void)bitbuffer_append_bit(output, 0); /* Flag: compressed */
476 : }
477 :
478 : /* Determine extraction mask based on cₜ */
479 186653 : int ct = ccsds124_compute_ct_flag(comp, Vt, effective_params->new_mask_flag);
480 :
481 186653 : if ((ct != 0) && (Vt > 0U)) {
482 : /* BE(Iₜ, (Xₜ OR Mₜ)) - extract bits where mask OR changes are set
483 : * Reuse work_diff as extraction mask (not used at this point) */
484 14679 : bitvector_or(&comp->work_diff, &comp->mask, &comp->work_Xt); /* Mₜ OR Xₜ */
485 :
486 14679 : (void)ccsds124_bit_extract(output, input, &comp->work_diff);
487 : } else {
488 : /* BE(Iₜ, Mₜ) - extract only unpredictable bits */
489 171974 : (void)ccsds124_bit_extract(output, input, &comp->mask);
490 : }
491 : }
492 :
493 : /* ====================================================================
494 : * STEP 3: Update State for Next Cycle
495 : * ==================================================================== */
496 :
497 : /* Save current input and mask as previous for next iteration */
498 189242 : bitvector_copy(&comp->prev_input, input);
499 189242 : bitvector_copy(&comp->prev_mask, &comp->mask);
500 :
501 : /* Track new_mask_flag for cₜ calculation */
502 189242 : comp->new_mask_flag_history[comp->flag_history_index] = effective_params->new_mask_flag;
503 189242 : comp->flag_history_index = (comp->flag_history_index + 1U) % (size_t)CCSDS124_MAX_VT_HISTORY;
504 :
505 : /* Advance time */
506 189242 : comp->t++;
507 :
508 : /* Advance history index (circular buffer) */
509 189242 : comp->history_index = (comp->history_index + 1U) % (size_t)CCSDS124_MAX_HISTORY;
510 :
511 : result = CCSDS124_OK;
512 : }
513 :
514 189246 : return result;
515 : }
516 :
517 :
518 73 : int ccsds124_compress(
519 : ccsds124_compressor_t *comp,
520 : const uint8_t *input_data,
521 : size_t input_size,
522 : uint8_t *output_buffer,
523 : size_t output_buffer_size,
524 : size_t *output_size
525 : ) {
526 : int result = CCSDS124_ERROR_INVALID_ARG;
527 :
528 73 : if ((comp == NULL) || (input_data == NULL) || (output_buffer == NULL) || (output_size == NULL)) {
529 : /* Invalid arguments - result already set */
530 : } else {
531 : /* Calculate packet size in bytes */
532 69 : size_t packet_size_bytes = (comp->F + 7U) / 8U;
533 :
534 : /* Verify input size is a multiple of packet size */
535 69 : if ((input_size % packet_size_bytes) != 0U) {
536 : /* Invalid input size - result already set */
537 : } else {
538 : /* Calculate number of packets */
539 67 : size_t num_packets = input_size / packet_size_bytes;
540 :
541 : /* Reset compressor state */
542 67 : ccsds124_compressor_reset(comp);
543 :
544 : /* Output accumulation */
545 : size_t total_output_bytes = 0U;
546 : int compress_error = 0;
547 :
548 : /* Process each packet */
549 188291 : for (size_t i = 0U; (i < num_packets) && (compress_error == 0); i++) {
550 : bitvector_t input_vec;
551 : bitbuffer_t packet_output;
552 :
553 188224 : (void)bitvector_init(&input_vec, comp->F);
554 188224 : bitbuffer_init(&packet_output);
555 :
556 : /* Load packet data */
557 188224 : (void)bitvector_from_bytes(&input_vec, &input_data[i * packet_size_bytes], packet_size_bytes);
558 :
559 : /* Compute parameters */
560 : ccsds124_params_t params;
561 188224 : params.min_robustness = comp->robustness;
562 :
563 : /* If limits are set, manage parameters automatically using countdown counters
564 : * (matches reference implementation exactly) */
565 188224 : if ((comp->pt_limit > 0) && (comp->ft_limit > 0) && (comp->rt_limit > 0)) {
566 : /* Reference implementation: packet 1 handled separately, loop starts at packet 2 (1-indexed).
567 : * In 0-indexed: packet 0 = handled separately, counters start at packet 1. */
568 188222 : if (i == 0U) {
569 : /* First packet: fixed init values, counters not checked */
570 66 : params.send_mask_flag = 1;
571 66 : params.uncompressed_flag = 1;
572 66 : params.new_mask_flag = 0;
573 : } else {
574 : /* Packets 1+: check and update countdown counters */
575 : /* ft counter */
576 188156 : if (comp->ft_counter == 1) {
577 4532 : params.send_mask_flag = 1;
578 4532 : comp->ft_counter = comp->ft_limit;
579 : } else {
580 183624 : comp->ft_counter--;
581 183624 : params.send_mask_flag = 0;
582 : }
583 :
584 : /* pt counter */
585 188156 : if (comp->pt_counter == 1) {
586 10704 : params.new_mask_flag = 1;
587 10704 : comp->pt_counter = comp->pt_limit;
588 : } else {
589 177452 : comp->pt_counter--;
590 177452 : params.new_mask_flag = 0;
591 : }
592 :
593 : /* rt counter */
594 188156 : if (comp->rt_counter == 1) {
595 2104 : params.uncompressed_flag = 1;
596 2104 : comp->rt_counter = comp->rt_limit;
597 : } else {
598 186052 : comp->rt_counter--;
599 186052 : params.uncompressed_flag = 0;
600 : }
601 :
602 : /* Override for remaining init packets: CCSDS requires first Rt+1 packets
603 : * to have ft=1, rt=1, pt=0. Reference checks: if (i < Rt+2) in 1-indexed.
604 : * In 0-indexed: if (i <= Rt). Counters are still decremented per reference. */
605 188156 : if (i <= (size_t)comp->robustness) {
606 198 : params.send_mask_flag = 1;
607 198 : params.uncompressed_flag = 1;
608 198 : params.new_mask_flag = 0;
609 : }
610 : }
611 : } else {
612 : /* Manual control: use defaults (ft=0, rt=0, pt=0 for normal operation) */
613 2 : params.send_mask_flag = 0;
614 2 : params.uncompressed_flag = 0;
615 2 : params.new_mask_flag = 0;
616 : }
617 :
618 : /* Compress packet */
619 188224 : int packet_result = ccsds124_compress_packet(comp, &input_vec, &packet_output, ¶ms);
620 188224 : if (packet_result != CCSDS124_OK) {
621 : result = packet_result;
622 : compress_error = 1;
623 : } else {
624 : /* Convert packet to bytes with byte-boundary padding */
625 : uint8_t packet_bytes[CCSDS124_MAX_OUTPUT_BYTES];
626 188224 : size_t packet_size = bitbuffer_to_bytes(&packet_output, packet_bytes, sizeof(packet_bytes));
627 :
628 : /* Check if output buffer has space */
629 188224 : if ((total_output_bytes + packet_size) > output_buffer_size) {
630 : result = CCSDS124_ERROR_OVERFLOW;
631 : compress_error = 1;
632 : } else {
633 : /* Append to output buffer */
634 188224 : (void)memcpy(&output_buffer[total_output_bytes], packet_bytes, packet_size);
635 : total_output_bytes += packet_size;
636 : }
637 : }
638 : }
639 :
640 : /* Return total output size if successful */
641 67 : if (compress_error == 0) {
642 67 : *output_size = total_output_bytes;
643 : result = CCSDS124_OK;
644 : }
645 : }
646 : }
647 :
648 73 : return result;
649 : }
650 :
651 : /** @} */ /* End of Main Compression Functions */
|