LCOV - code coverage report
Current view: top level - src - compress.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 195 195
Test Date: 2026-06-29 15:02:00 Functions: 100.0 % 8 8

            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, &params);
     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 */
        

Generated by: LCOV version 2.0-1