CCSDS 124.0-B-1 C 1.0.0
CCSDS 124.0-B-1 Lossless Compression
Loading...
Searching...
No Matches
cli.c
Go to the documentation of this file.
1
30#include "ccsds124.h"
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34
36static const char *BANNER =
37" \n"
38" \n"
39" ____ ____ ____ ____ ____ _ ____ _ _ \n"
40" / ___/ ___/ ___|| _ \\/ ___| / |___ \\| || | \n"
41"| | | | \\___ \\| | | \\___ \\ | | __) | || |_ \n"
42"| |__| |___ ___) | |_| |___) | | |/ __/|__ _| \n"
43" \\____\\____|____/|____/|____/ |_|_____| |_| \n"
44" \n"
45" by T A N A G R A S P A C E \n"
46" \n";
47
53static void print_version(void) {
54 printf("ccsds124 %d.%d.%d\n",
58}
59
60static void print_help(const char *prog_name) {
61 printf("\n%s\n", BANNER);
62 printf("CCSDS 124.0-B-1 Lossless Compression (v%d.%d.%d)\n",
64 printf("=================================================\n\n");
65 printf("References:\n");
66 printf(" CCSDS 124.0-B-1: https://ccsds.org/Pubs/124x0b1.pdf\n");
67 printf(" Documentation: https://tanagraspace.com/ccsds124\n\n");
68 printf("Citation:\n");
69 printf(" D. Evans, G. Labreche, D. Marszk, S. Bammens, M. Hernandez-Cabronero,\n");
70 printf(" V. Zelenevskiy, V. Shiradhonkar, M. Starcik, and M. Henkel. 2022.\n");
71 printf(" \"Implementing the New CCSDS Housekeeping Data Compression Standard\n");
72 printf(" 124.0-B-1 (based on POCKET+) on OPS-SAT-1,\" Proceedings of the\n");
73 printf(" Small Satellite Conference, Communications, SSC22-XII-03.\n");
74 printf(" https://digitalcommons.usu.edu/smallsat/2022/all2022/133/\n\n");
75 printf("Usage:\n");
76 printf(" %s <input> <packet_size> <pt> <ft> <rt> <robustness>\n", prog_name);
77 printf(" %s -d <input.pkt> <packet_size> <robustness>\n\n", prog_name);
78 printf("Options:\n");
79 printf(" -d Decompress (default is compress)\n");
80 printf(" -h, --help Show this help message\n");
81 printf(" -v, --version Show version information\n\n");
82 printf("Compress arguments:\n");
83 printf(" input Input file to compress\n");
84 printf(" packet_size Packet size in bytes (e.g., 90)\n");
85 printf(" pt New mask period (e.g., 10, 20)\n");
86 printf(" ft Send mask period (e.g., 20, 50)\n");
87 printf(" rt Uncompressed period (e.g., 50, 100)\n");
88 printf(" robustness Robustness level 0-7 (e.g., 1, 2)\n\n");
89 printf("Decompress arguments:\n");
90 printf(" input.pkt Compressed input file\n");
91 printf(" packet_size Original packet size in bytes\n");
92 printf(" robustness Robustness level (must match compression)\n\n");
93 printf("Output:\n");
94 printf(" Compress: <input>.pkt\n");
95 printf(" Decompress: <input>.depkt (or <base>.depkt if input ends in .pkt)\n\n");
96 printf("Examples:\n");
97 printf(" %s data.bin 90 10 20 50 1 # compress\n", prog_name);
98 printf(" %s -d data.bin.pkt 90 1 # decompress\n\n", prog_name);
99}
100
110static void make_decompress_filename(char *output, size_t output_len, const char *input) {
111 size_t len = strlen(input);
112
113 /* Check if input ends with .pkt */
114 if ((len > 4) && (strcmp(&input[len - 4], ".pkt") == 0)) {
115 /* Remove .pkt and add .depkt */
116 size_t base_len = len - 4;
117 if ((base_len + 7) < output_len) {
118 (void)strncpy(output, input, base_len);
119 output[base_len] = '\0';
120 (void)strcat(output, ".depkt");
121 } else {
122 (void)snprintf(output, output_len, "%s.depkt", input);
123 }
124 } else {
125 (void)snprintf(output, output_len, "%s.depkt", input);
126 }
127}
128
141static int do_compress(const char *input_path, int packet_size,
142 int pt_period, int ft_period, int rt_period,
143 int robustness) {
144 /* Read input file */
145 FILE *fin = fopen(input_path, "rb");
146 if (fin == NULL) {
147 fprintf(stderr, "Error: Cannot open input file: %s\n", input_path);
148 return 1;
149 }
150
151 (void)fseek(fin, 0, SEEK_END);
152 long file_len = ftell(fin);
153 (void)fseek(fin, 0, SEEK_SET);
154
155 if (file_len <= 0) {
156 fprintf(stderr, "Error: Input file is empty or invalid\n");
157 (void)fclose(fin);
158 return 1;
159 }
160
161 size_t input_size = (size_t)file_len;
162
163 if ((input_size % (size_t)packet_size) != 0U) {
164 fprintf(stderr, "Error: Input size (%zu) not divisible by packet size (%d)\n",
165 input_size, packet_size);
166 (void)fclose(fin);
167 return 1;
168 }
169
170 uint8_t *input_data = malloc(input_size);
171 if (input_data == NULL) {
172 fprintf(stderr, "Error: Cannot allocate memory\n");
173 (void)fclose(fin);
174 return 1;
175 }
176
177 if (fread(input_data, 1, input_size, fin) != input_size) {
178 fprintf(stderr, "Error: Failed to read input file\n");
179 (void)fclose(fin);
180 free(input_data);
181 return 1;
182 }
183 (void)fclose(fin);
184
185 /* Create output filename */
186 char output_path[1024];
187 (void)snprintf(output_path, sizeof(output_path), "%s.pkt", input_path);
188
189 /* Initialize compressor */
190 size_t packet_length = (size_t)packet_size * 8U;
192 (void)ccsds124_compressor_init(&comp, packet_length, NULL, (uint8_t)robustness,
193 pt_period, ft_period, rt_period);
194
195 /* Allocate output buffer */
196 size_t max_output = input_size * 2U;
197 uint8_t *output_data = malloc(max_output);
198 if (output_data == NULL) {
199 fprintf(stderr, "Error: Cannot allocate output buffer\n");
200 free(input_data);
201 return 1;
202 }
203
204 /* Compress */
205 size_t output_size = 0U;
206 int result = ccsds124_compress(&comp, input_data, input_size,
207 output_data, max_output, &output_size);
208
209 if (result != CCSDS124_OK) {
210 fprintf(stderr, "Error: Compression failed with code %d\n", result);
211 free(input_data);
212 free(output_data);
213 return 1;
214 }
215
216 /* Write output */
217 FILE *fout = fopen(output_path, "wb");
218 if (fout == NULL) {
219 fprintf(stderr, "Error: Cannot create output file: %s\n", output_path);
220 free(input_data);
221 free(output_data);
222 return 1;
223 }
224
225 if (fwrite(output_data, 1, output_size, fout) != output_size) {
226 fprintf(stderr, "Error: Failed to write output file\n");
227 (void)fclose(fout);
228 free(input_data);
229 free(output_data);
230 return 1;
231 }
232 (void)fclose(fout);
233
234 /* Print summary */
235 size_t num_packets = input_size / (size_t)packet_size;
236 double ratio = (double)input_size / (double)output_size;
237 printf("Input: %s (%zu bytes, %zu packets)\n", input_path, input_size, num_packets);
238 printf("Output: %s (%zu bytes)\n", output_path, output_size);
239 printf("Ratio: %.2fx\n", ratio);
240 printf("Parameters: R=%d, pt=%d, ft=%d, rt=%d\n",
241 robustness, pt_period, ft_period, rt_period);
242
243 free(input_data);
244 free(output_data);
245 return 0;
246}
247
257static int do_decompress(const char *input_path, int packet_size, int robustness) {
258 /* Read input file */
259 FILE *fin = fopen(input_path, "rb");
260 if (fin == NULL) {
261 fprintf(stderr, "Error: Cannot open input file: %s\n", input_path);
262 return 1;
263 }
264
265 (void)fseek(fin, 0, SEEK_END);
266 long file_len = ftell(fin);
267 (void)fseek(fin, 0, SEEK_SET);
268
269 if (file_len <= 0) {
270 fprintf(stderr, "Error: Input file is empty or invalid\n");
271 (void)fclose(fin);
272 return 1;
273 }
274
275 size_t input_size = (size_t)file_len;
276 uint8_t *input_data = malloc(input_size);
277 if (input_data == NULL) {
278 fprintf(stderr, "Error: Cannot allocate memory for input\n");
279 (void)fclose(fin);
280 return 1;
281 }
282
283 if (fread(input_data, 1, input_size, fin) != input_size) {
284 fprintf(stderr, "Error: Failed to read input file\n");
285 (void)fclose(fin);
286 free(input_data);
287 return 1;
288 }
289 (void)fclose(fin);
290
291 /* Create output filename */
292 char output_path[1024];
293 make_decompress_filename(output_path, sizeof(output_path), input_path);
294
295 /* Initialize decompressor */
296 size_t packet_length = (size_t)packet_size * 8U;
298 int result = ccsds124_decompressor_init(&decomp, packet_length, NULL, (uint8_t)robustness);
299
300 if (result != CCSDS124_OK) {
301 fprintf(stderr, "Error: Decompressor init failed with code %d\n", result);
302 free(input_data);
303 return 1;
304 }
305
306 /* Allocate output buffer (compression ratios up to 14x observed, use 20x to be safe) */
307 size_t max_output = input_size * 20U;
308 uint8_t *output_data = malloc(max_output);
309 if (output_data == NULL) {
310 fprintf(stderr, "Error: Cannot allocate output buffer\n");
311 free(input_data);
312 return 1;
313 }
314
315 /* Decompress */
316 size_t output_size = 0U;
317 result = ccsds124_decompress(&decomp, input_data, input_size,
318 output_data, max_output, &output_size);
319
320 if (result != CCSDS124_OK) {
321 fprintf(stderr, "Error: Decompression failed with code %d\n", result);
322 free(input_data);
323 free(output_data);
324 return 1;
325 }
326
327 /* Write output file */
328 FILE *fout = fopen(output_path, "wb");
329 if (fout == NULL) {
330 fprintf(stderr, "Error: Cannot create output file: %s\n", output_path);
331 free(input_data);
332 free(output_data);
333 return 1;
334 }
335
336 if (fwrite(output_data, 1, output_size, fout) != output_size) {
337 fprintf(stderr, "Error: Failed to write output file\n");
338 (void)fclose(fout);
339 free(input_data);
340 free(output_data);
341 return 1;
342 }
343 (void)fclose(fout);
344
345 /* Print summary */
346 size_t num_packets = output_size / (size_t)packet_size;
347 double ratio = (double)output_size / (double)input_size;
348 printf("Input: %s (%zu bytes)\n", input_path, input_size);
349 printf("Output: %s (%zu bytes, %zu packets)\n", output_path, output_size, num_packets);
350 printf("Expansion: %.2fx\n", ratio);
351 printf("Parameters: packet_size=%d, R=%d\n", packet_size, robustness);
352
353 free(input_data);
354 free(output_data);
355 return 0;
356}
357
366int main(int argc, char **argv) {
367 int decompress_mode = 0;
368 int arg_offset = 1;
369
370 /* Check for help flag */
371 if ((argc < 2) ||
372 (strcmp(argv[1], "-h") == 0) ||
373 (strcmp(argv[1], "--help") == 0)) {
374 print_help(argv[0]);
375 return (argc < 2) ? 1 : 0;
376 }
377
378 /* Check for version flag */
379 if ((strcmp(argv[1], "-v") == 0) ||
380 (strcmp(argv[1], "--version") == 0)) {
382 return 0;
383 }
384
385 /* Check for decompress flag */
386 if (strcmp(argv[1], "-d") == 0) {
387 decompress_mode = 1;
388 arg_offset = 2;
389 }
390
391 if (decompress_mode != 0) {
392 /* Decompress mode: -d <input.pkt> <packet_size> <robustness> */
393 if (argc != 5) {
394 fprintf(stderr, "Error: Decompress requires 3 arguments after -d\n");
395 fprintf(stderr, "Usage: %s -d <input.pkt> <packet_size> <robustness>\n", argv[0]);
396 return 1;
397 }
398
399 const char *input_path = argv[arg_offset];
400 int packet_size = atoi(argv[arg_offset + 1]);
401 int robustness = atoi(argv[arg_offset + 2]);
402
403 /* Validate parameters */
404 if ((packet_size <= 0) || (packet_size > 8192)) {
405 fprintf(stderr, "Error: packet_size must be 1-8192 bytes\n");
406 return 1;
407 }
408 if ((robustness < 0) || (robustness > 7)) {
409 fprintf(stderr, "Error: robustness must be 0-7\n");
410 return 1;
411 }
412
413 return do_decompress(input_path, packet_size, robustness);
414
415 } else {
416 /* Compress mode: <input> <packet_size> <pt> <ft> <rt> <robustness> */
417 if (argc != 7) {
418 fprintf(stderr, "Error: Compress requires 6 arguments\n");
419 fprintf(stderr, "Usage: %s <input> <packet_size> <pt> <ft> <rt> <robustness>\n", argv[0]);
420 return 1;
421 }
422
423 const char *input_path = argv[1];
424 int packet_size = atoi(argv[2]);
425 int pt_period = atoi(argv[3]);
426 int ft_period = atoi(argv[4]);
427 int rt_period = atoi(argv[5]);
428 int robustness = atoi(argv[6]);
429
430 /* Validate parameters */
431 if ((packet_size <= 0) || (packet_size > 8192)) {
432 fprintf(stderr, "Error: packet_size must be 1-8192 bytes\n");
433 return 1;
434 }
435 if ((robustness < 0) || (robustness > 7)) {
436 fprintf(stderr, "Error: robustness must be 0-7\n");
437 return 1;
438 }
439 if ((pt_period <= 0) || (ft_period <= 0) || (rt_period <= 0)) {
440 fprintf(stderr, "Error: periods must be positive\n");
441 return 1;
442 }
443
444 return do_compress(input_path, packet_size, pt_period, ft_period, rt_period, robustness);
445 }
446}
CCSDS 124.0-B-1 Compression Library - Public API.
static void print_version(void)
Print help message with usage information.
Definition cli.c:53
int main(int argc, char **argv)
CLI entry point.
Definition cli.c:366
static const char * BANNER
ASCII art banner for help output.
Definition cli.c:36
static int do_decompress(const char *input_path, int packet_size, int robustness)
Decompress a file.
Definition cli.c:257
static int do_compress(const char *input_path, int packet_size, int pt_period, int ft_period, int rt_period, int robustness)
Compress a file.
Definition cli.c:141
static void make_decompress_filename(char *output, size_t output_len, const char *input)
Create output filename for decompression.
Definition cli.c:110
static void print_help(const char *prog_name)
Definition cli.c:60
int ccsds124_compressor_init(ccsds124_compressor_t *comp, size_t F, const bitvector_t *initial_mask, uint8_t robustness, int pt_limit, int ft_limit, int rt_limit)
Initialize compressor state.
Definition compress.c:36
int ccsds124_compress(ccsds124_compressor_t *comp, const uint8_t *input_data, size_t input_size, uint8_t *output_buffer, size_t output_buffer_size, size_t *output_size)
Compress entire input data stream.
Definition compress.c:518
int ccsds124_decompress(ccsds124_decompressor_t *decomp, const uint8_t *input_data, size_t input_size, uint8_t *output_buffer, size_t output_buffer_size, size_t *output_size)
Decompress entire compressed data stream.
int ccsds124_decompressor_init(ccsds124_decompressor_t *decomp, size_t F, const bitvector_t *initial_mask, uint8_t robustness)
Initialize decompressor state.
Definition decompress.c:335
#define CCSDS124_OK
Definition ccsds124.h:47
#define CCSDS124_VERSION_MAJOR
Definition ccsds124.h:38
#define CCSDS124_VERSION_PATCH
Definition ccsds124.h:40
#define CCSDS124_VERSION_MINOR
Definition ccsds124.h:39
Compressor state structure.
Definition ccsds124.h:537
Decompressor state structure.
Definition ccsds124.h:872