CCSDS 124.0-B-1 C++ 1.0.0
CCSDS 124.0-B-1 Lossless Compression
Loading...
Searching...
No Matches
cli.cpp
Go to the documentation of this file.
1
25#include <ccsds124/ccsds124.hpp>
26
27#include <cstdio>
28#include <cstdlib>
29#include <cstring>
30#include <fstream>
31#include <vector>
32
33using namespace ccsds124;
34
35static constexpr const char* BANNER = " \n"
36 " \n"
37 " ____ ____ ____ ____ ____ _ ____ _ _ \n"
38 " / ___/ ___/ ___|| _ \\/ ___| / |___ \\| || | \n"
39 "| | | | \\___ \\| | | \\___ \\ | | __) | || |_ \n"
40 "| |__| |___ ___) | |_| |___) | | |/ __/|__ _| \n"
41 " \\____\\____|____/|____/|____/ |_|_____| |_| \n"
42 " \n"
43 " by T A N A G R A S P A C E \n"
44 " \n";
45
46static void print_version() {
47 std::printf("ccsds124 %s (C++)\n", version());
48}
49
50static void print_help(const char* prog_name) {
51 std::printf("\n%s\n", BANNER);
52 std::printf("CCSDS 124.0-B-1 Lossless Compression (v%s C++)\n", version());
53 std::printf("=================================================\n\n");
54 std::printf("References:\n");
55 std::printf(" CCSDS 124.0-B-1: https://ccsds.org/Pubs/124x0b1.pdf\n");
56 std::printf(" Documentation: https://tanagraspace.com/ccsds124\n\n");
57 std::printf("Usage:\n");
58 std::printf(" %s <input> <packet_size> <pt> <ft> <rt> <robustness>\n", prog_name);
59 std::printf(" %s -d <input.pkt> <packet_size> <robustness>\n\n", prog_name);
60 std::printf("Options:\n");
61 std::printf(" -d Decompress (default is compress)\n");
62 std::printf(" -h, --help Show this help message\n");
63 std::printf(" -v, --version Show version information\n\n");
64 std::printf("Compress arguments:\n");
65 std::printf(" input Input file to compress\n");
66 std::printf(" packet_size Packet size in bytes (e.g., 90)\n");
67 std::printf(" pt New mask period (e.g., 10, 20)\n");
68 std::printf(" ft Send mask period (e.g., 20, 50)\n");
69 std::printf(" rt Uncompressed period (e.g., 50, 100)\n");
70 std::printf(" robustness Robustness level 0-7 (e.g., 1, 2)\n\n");
71 std::printf("Decompress arguments:\n");
72 std::printf(" input.pkt Compressed input file\n");
73 std::printf(" packet_size Original packet size in bytes\n");
74 std::printf(" robustness Robustness level (must match compression)\n\n");
75 std::printf("Output:\n");
76 std::printf(" Compress: <input>.pkt\n");
77 std::printf(" Decompress: <input>.depkt (or <base>.depkt if input ends in .pkt)\n\n");
78 std::printf("Examples:\n");
79 std::printf(" %s data.bin 90 10 20 50 1 # compress\n", prog_name);
80 std::printf(" %s -d data.bin.pkt 90 1 # decompress\n\n", prog_name);
81}
82
83static std::string make_decompress_filename(const std::string& input) {
84 // Check if input ends with .pkt
85 if (input.size() > 4 && input.substr(input.size() - 4) == ".pkt") {
86 return input.substr(0, input.size() - 4) + ".depkt";
87 }
88 return input + ".depkt";
89}
90
91static std::vector<std::uint8_t> read_file(const std::string& path) {
92 std::ifstream file(path, std::ios::binary | std::ios::ate);
93 if (!file) {
94 return {};
95 }
96
97 std::streamsize size = file.tellg();
98 file.seekg(0, std::ios::beg);
99
100 std::vector<std::uint8_t> buffer(static_cast<std::size_t>(size));
101 if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
102 return {};
103 }
104
105 return buffer;
106}
107
108static bool write_file(const std::string& path, const std::uint8_t* data, std::size_t size) {
109 std::ofstream file(path, std::ios::binary);
110 if (!file) {
111 return false;
112 }
113 file.write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));
114 return file.good();
115}
116
117static int do_compress(const char* input_path, int packet_size, int pt_period, int ft_period,
118 int rt_period, int robustness) {
119 // Read input file
120 auto input_data = read_file(input_path);
121 if (input_data.empty()) {
122 std::fprintf(stderr, "Error: Cannot read input file: %s\n", input_path);
123 return 1;
124 }
125
126 std::size_t input_size = input_data.size();
127
128 if ((input_size % static_cast<std::size_t>(packet_size)) != 0) {
129 std::fprintf(stderr, "Error: Input size (%zu) not divisible by packet size (%d)\n",
130 input_size, packet_size);
131 return 1;
132 }
133
134 // Create output filename
135 std::string output_path = std::string(input_path) + ".pkt";
136
137 // Allocate output buffer
138 std::vector<std::uint8_t> output_data(input_size * 2);
139 std::size_t output_size = 0;
140
141 // Compress (using 720-bit packets for 90-byte packets)
142 Error result;
143 if (packet_size == 90) {
144 result = compress<720>(
145 input_data.data(), input_size, output_data.data(), output_data.size(), output_size,
146 static_cast<std::uint8_t>(robustness), pt_period, ft_period, rt_period);
147 } else {
148 std::fprintf(stderr, "Error: Only 90-byte packets supported in CLI\n");
149 return 1;
150 }
151
152 if (result != Error::Ok) {
153 std::fprintf(stderr, "Error: Compression failed with code %d\n", static_cast<int>(result));
154 return 1;
155 }
156
157 // Write output
158 if (!write_file(output_path, output_data.data(), output_size)) {
159 std::fprintf(stderr, "Error: Cannot write output file: %s\n", output_path.c_str());
160 return 1;
161 }
162
163 // Print summary
164 std::size_t num_packets = input_size / static_cast<std::size_t>(packet_size);
165 double ratio = static_cast<double>(input_size) / static_cast<double>(output_size);
166 std::printf("Input: %s (%zu bytes, %zu packets)\n", input_path, input_size, num_packets);
167 std::printf("Output: %s (%zu bytes)\n", output_path.c_str(), output_size);
168 std::printf("Ratio: %.2fx\n", ratio);
169 std::printf("Parameters: R=%d, pt=%d, ft=%d, rt=%d\n", robustness, pt_period, ft_period,
170 rt_period);
171
172 return 0;
173}
174
175static int do_decompress(const char* input_path, int packet_size, int robustness) {
176 // Read input file
177 auto input_data = read_file(input_path);
178 if (input_data.empty()) {
179 std::fprintf(stderr, "Error: Cannot read input file: %s\n", input_path);
180 return 1;
181 }
182
183 std::size_t input_size = input_data.size();
184
185 // Create output filename
186 std::string output_path = make_decompress_filename(input_path);
187
188 // Allocate output buffer (compression ratios up to 14x observed, use 20x to be safe)
189 std::vector<std::uint8_t> output_data(input_size * 20);
190 std::size_t output_size = 0;
191
192 // Decompress
193 Error result;
194 if (packet_size == 90) {
195 result =
196 decompress<720>(input_data.data(), input_size, output_data.data(), output_data.size(),
197 output_size, static_cast<std::uint8_t>(robustness));
198 } else {
199 std::fprintf(stderr, "Error: Only 90-byte packets supported in CLI\n");
200 return 1;
201 }
202
203 if (result != Error::Ok) {
204 std::fprintf(stderr, "Error: Decompression failed with code %d\n",
205 static_cast<int>(result));
206 return 1;
207 }
208
209 // Write output
210 if (!write_file(output_path, output_data.data(), output_size)) {
211 std::fprintf(stderr, "Error: Cannot write output file: %s\n", output_path.c_str());
212 return 1;
213 }
214
215 // Print summary
216 std::size_t num_packets = output_size / static_cast<std::size_t>(packet_size);
217 double ratio = static_cast<double>(output_size) / static_cast<double>(input_size);
218 std::printf("Input: %s (%zu bytes)\n", input_path, input_size);
219 std::printf("Output: %s (%zu bytes, %zu packets)\n", output_path.c_str(), output_size,
220 num_packets);
221 std::printf("Expansion: %.2fx\n", ratio);
222 std::printf("Parameters: packet_size=%d, R=%d\n", packet_size, robustness);
223
224 return 0;
225}
226
227int main(int argc, char** argv) {
228 bool decompress_mode = false;
229 int arg_offset = 1;
230
231 // Check for help flag
232 if (argc < 2 || std::strcmp(argv[1], "-h") == 0 || std::strcmp(argv[1], "--help") == 0) {
233 print_help(argv[0]);
234 return (argc < 2) ? 1 : 0;
235 }
236
237 // Check for version flag
238 if (std::strcmp(argv[1], "-v") == 0 || std::strcmp(argv[1], "--version") == 0) {
240 return 0;
241 }
242
243 // Check for decompress flag
244 if (std::strcmp(argv[1], "-d") == 0) {
245 decompress_mode = true;
246 arg_offset = 2;
247 }
248
249 if (decompress_mode) {
250 // Decompress mode: -d <input.pkt> <packet_size> <robustness>
251 if (argc != 5) {
252 std::fprintf(stderr, "Error: Decompress requires 3 arguments after -d\n");
253 std::fprintf(stderr, "Usage: %s -d <input.pkt> <packet_size> <robustness>\n", argv[0]);
254 return 1;
255 }
256
257 const char* input_path = argv[arg_offset];
258 int packet_size = std::atoi(argv[arg_offset + 1]);
259 int robustness = std::atoi(argv[arg_offset + 2]);
260
261 // Validate parameters
262 if (packet_size <= 0 || packet_size > 8192) {
263 std::fprintf(stderr, "Error: packet_size must be 1-8192 bytes\n");
264 return 1;
265 }
266 if (robustness < 0 || robustness > 7) {
267 std::fprintf(stderr, "Error: robustness must be 0-7\n");
268 return 1;
269 }
270
271 return do_decompress(input_path, packet_size, robustness);
272
273 } else {
274 // Compress mode: <input> <packet_size> <pt> <ft> <rt> <robustness>
275 if (argc != 7) {
276 std::fprintf(stderr, "Error: Compress requires 6 arguments\n");
277 std::fprintf(stderr, "Usage: %s <input> <packet_size> <pt> <ft> <rt> <robustness>\n",
278 argv[0]);
279 return 1;
280 }
281
282 const char* input_path = argv[1];
283 int packet_size = std::atoi(argv[2]);
284 int pt_period = std::atoi(argv[3]);
285 int ft_period = std::atoi(argv[4]);
286 int rt_period = std::atoi(argv[5]);
287 int robustness = std::atoi(argv[6]);
288
289 // Validate parameters
290 if (packet_size <= 0 || packet_size > 8192) {
291 std::fprintf(stderr, "Error: packet_size must be 1-8192 bytes\n");
292 return 1;
293 }
294 if (robustness < 0 || robustness > 7) {
295 std::fprintf(stderr, "Error: robustness must be 0-7\n");
296 return 1;
297 }
298 if (pt_period <= 0 || ft_period <= 0 || rt_period <= 0) {
299 std::fprintf(stderr, "Error: periods must be positive\n");
300 return 1;
301 }
302
303 return do_compress(input_path, packet_size, pt_period, ft_period, rt_period, robustness);
304 }
305}
High-level CCSDS 124.0-B-1 compression API.
static std::vector< std::uint8_t > read_file(const std::string &path)
Definition cli.cpp:91
static bool write_file(const std::string &path, const std::uint8_t *data, std::size_t size)
Definition cli.cpp:108
int main(int argc, char **argv)
Definition cli.cpp:227
static void print_version()
Definition cli.cpp:46
static int do_decompress(const char *input_path, int packet_size, int robustness)
Definition cli.cpp:175
static constexpr const char * BANNER
Definition cli.cpp:35
static int do_compress(const char *input_path, int packet_size, int pt_period, int ft_period, int rt_period, int robustness)
Definition cli.cpp:117
static void print_help(const char *prog_name)
Definition cli.cpp:50
static std::string make_decompress_filename(const std::string &input)
Definition cli.cpp:83
const char * version() noexcept
Get library version.
Definition ccsds124.hpp:228
Error
Error codes for error-code-based error handling.
Definition error.hpp:29