rust_aec/lib.rs
1//! `rust-aec` is a pure Rust decoder for **CCSDS 121.0-B-3 Adaptive Entropy Coding (AEC)**.
2//!
3//! Primary goal: support **GRIB2 Data Representation Template 5.0 = 42 (CCSDS/AEC)** without
4//! requiring native `libaec`.
5//!
6//! # Quick start
7//!
8//! ```
9//! use rust_aec::{decode, flags_from_grib2_ccsds_flags, AecParams};
10//!
11//! // In a real GRIB2 pipeline, `payload` is Section 7 and `num_points` comes from Section 5.
12//! // This snippet focuses on API shape and compiles without external files.
13//! let payload: Vec<u8> = Vec::new();
14//! let num_points: usize = 0;
15//!
16//! let params = AecParams::new(12, 32, 128, flags_from_grib2_ccsds_flags(0x0e));
17//! let decoded = decode(&payload, params, num_points);
18//! assert!(decoded.is_ok());
19//! ```
20
21pub mod bitreader;
22mod decoder;
23pub mod error;
24pub mod params;
25
26pub use crate::error::AecError;
27pub use crate::params::{AecFlags, AecParams};
28
29/// Decode an AEC bitstream into packed sample bytes.
30///
31/// - `input`: CCSDS/AEC payload bitstream.
32/// - `params`: bit width, block size, RSI, and flags.
33/// - `output_samples`: number of samples expected in the output.
34///
35/// Returns a `Vec<u8>` of length `output_samples * bytes_per_sample`, where
36/// `bytes_per_sample = ceil(bits_per_sample / 8)`.
37///
38/// Note: When `AecFlags::MSB` is set, samples are written big-endian (MSB-first)
39/// per sample; otherwise little-endian.
40pub fn decode(input: &[u8], params: AecParams, output_samples: usize) -> Result<Vec<u8>, AecError> {
41 decoder::decode(input, params, output_samples)
42}
43
44/// Decode an AEC bitstream into a caller-provided output buffer.
45///
46/// This is useful when you want to reuse an allocation (e.g. decode many tiles/messages)
47/// without repeatedly allocating a `Vec<u8>`.
48///
49/// The `output` buffer length must be exactly `output_samples * bytes_per_sample`, where
50/// `bytes_per_sample = ceil(bits_per_sample / 8)` (subject to `AecFlags::DATA_3BYTE` rules).
51pub fn decode_into(
52 input: &[u8],
53 params: AecParams,
54 output_samples: usize,
55 output: &mut [u8],
56) -> Result<(), AecError> {
57 decoder::decode_into(input, params, output_samples, output)
58}
59
60/// Helper: convert GRIB2 `ccsdsFlags` (template 5.42) to `AecFlags`.
61pub fn flags_from_grib2_ccsds_flags(ccsds_flags: u8) -> AecFlags {
62 let mut flags = AecFlags::empty();
63
64 if (ccsds_flags & (1 << 0)) != 0 {
65 flags |= AecFlags::DATA_SIGNED;
66 }
67 if (ccsds_flags & (1 << 1)) != 0 {
68 flags |= AecFlags::DATA_3BYTE;
69 }
70 if (ccsds_flags & (1 << 2)) != 0 {
71 flags |= AecFlags::MSB;
72 }
73 if (ccsds_flags & (1 << 3)) != 0 {
74 flags |= AecFlags::DATA_PREPROCESS;
75 }
76 if (ccsds_flags & (1 << 4)) != 0 {
77 flags |= AecFlags::RESTRICTED;
78 }
79 if (ccsds_flags & (1 << 5)) != 0 {
80 flags |= AecFlags::PAD_RSI;
81 }
82
83 flags
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn flags_mapping_smoke() {
92 let f = flags_from_grib2_ccsds_flags(0b0011_1011);
93 assert!(f.contains(AecFlags::DATA_SIGNED));
94 assert!(f.contains(AecFlags::DATA_3BYTE));
95 assert!(!f.contains(AecFlags::MSB));
96 assert!(f.contains(AecFlags::DATA_PREPROCESS));
97 assert!(f.contains(AecFlags::RESTRICTED));
98 assert!(f.contains(AecFlags::PAD_RSI));
99 }
100}