Skip to main content

grib/encoder/
simple.rs

1use grib_template_helpers::WriteToBuffer;
2
3use crate::{
4    WriteGrib2DataSections,
5    def::grib2::template::param_set::SimplePacking,
6    encoder::{Encode, bitmap::Bitmap, helpers::BitsRequired, writer},
7};
8
9/// Strategies applied when performing simple packing on numerical sequences.
10/// Simple packing is a method for discretizing continuous numerical values as
11/// integers, and various approaches can be taken during this process.
12#[derive(Debug, PartialEq, Eq, Clone)]
13pub enum SimplePackingStrategy {
14    /// A strategy specifying how many decimal places to consider valid for the
15    /// numbers. This strategy is effective for various types of data, such as
16    /// observation data obtained from specific observation instruments, where
17    /// precision is clearly defined.
18    Decimal(i16),
19}
20
21pub(crate) struct Encoder<'a> {
22    data: &'a [f64],
23    strategy: SimplePackingStrategy,
24}
25
26impl<'a> Encoder<'a> {
27    pub(crate) fn new(data: &'a [f64], strategy: SimplePackingStrategy) -> Self {
28        Self { data, strategy }
29    }
30}
31
32impl<'a> Encode for Encoder<'a> {
33    type Output = Encoded;
34
35    fn encode(&self) -> Self::Output {
36        match self.strategy {
37            SimplePackingStrategy::Decimal(dec) => {
38                let (params, scaled, bitmap) = determine_simple_packing_params(self.data, dec);
39                let coded = if params.num_bits == 0 {
40                    CodedValues::Unique(self.data.len())
41                } else {
42                    let exp = 2_f64.powf(params.exp as f64);
43                    let coded = scaled
44                        .iter()
45                        .map(|value| ((value - params.ref_val as f64) / exp).round() as u32)
46                        .collect::<Vec<_>>();
47                    CodedValues::NonUnique(coded)
48                };
49                Encoded::new(params, coded, bitmap)
50            }
51        }
52    }
53}
54
55#[derive(Debug)]
56pub(crate) struct Encoded {
57    params: SimplePacking,
58    coded: CodedValues,
59    bitmap: Bitmap,
60}
61
62impl Encoded {
63    fn new(params: SimplePacking, coded: CodedValues, bitmap: Bitmap) -> Self {
64        Self {
65            params,
66            coded,
67            bitmap,
68        }
69    }
70
71    pub(crate) fn params(&self) -> &SimplePacking {
72        &self.params
73    }
74}
75
76impl WriteGrib2DataSections for Encoded {
77    fn section5_len(&self) -> usize {
78        21
79    }
80
81    fn write_section5(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
82        let len = self.section5_len();
83        if buf.len() < len {
84            return Err("destination buffer is too small");
85        }
86
87        let mut pos = 0;
88        pos += (len as u32).write_to_buffer(&mut buf[pos..])?; // header.len
89        pos += 5_u8.write_to_buffer(&mut buf[pos..])?; // header.sect_num
90        pos += (self.coded.num_values() as u32).write_to_buffer(&mut buf[pos..])?; // payload.num_encoded_points
91        pos += 0_u16.write_to_buffer(&mut buf[pos..])?; // payload.template_num
92        pos += self.params.write_to_buffer(&mut buf[pos..])?;
93        pos += 0_u8.write_to_buffer(&mut buf[pos..])?; // payload.template.orig_field_type
94
95        Ok(pos)
96    }
97
98    fn section6_len(&self) -> usize {
99        let bitmap_size = if self.bitmap.has_nan() {
100            self.bitmap.num_bytes_required()
101        } else {
102            0
103        };
104        6 + bitmap_size
105    }
106
107    fn write_section6(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
108        let len = self.section6_len();
109        if buf.len() < len {
110            return Err("destination buffer is too small");
111        }
112
113        let mut pos = 0;
114        pos += (len as u32).write_to_buffer(&mut buf[pos..])?;
115        pos += 6_u8.write_to_buffer(&mut buf[pos..])?;
116        if self.bitmap.has_nan() {
117            pos += 0_u8.write_to_buffer(&mut buf[pos..])?;
118            pos += self.bitmap.write_to_buffer(&mut buf[pos..])?;
119        } else {
120            pos += 255_u8.write_to_buffer(&mut buf[pos..])?;
121        }
122
123        Ok(pos)
124    }
125
126    fn section7_len(&self) -> usize {
127        let len = match &self.coded {
128            CodedValues::NonUnique(vec) => {
129                let nbitwise = writer::NBitwise::new(&vec, self.params.num_bits as usize);
130                nbitwise.num_bytes_required()
131            }
132            CodedValues::Unique(_) => 0,
133        };
134        5 + len
135    }
136
137    fn write_section7(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
138        let len = self.section7_len();
139        if buf.len() < len {
140            return Err("destination buffer is too small");
141        }
142
143        let mut pos = 0;
144        pos += (len as u32).write_to_buffer(&mut buf[pos..])?;
145        pos += 7_u8.write_to_buffer(&mut buf[pos..])?;
146        match &self.coded {
147            CodedValues::NonUnique(vec) => {
148                let nbitwise = writer::NBitwise::new(&vec, self.params.num_bits as usize);
149                pos += nbitwise.write_to_buffer(&mut buf[pos..])?;
150            }
151            CodedValues::Unique(_) => {}
152        }
153
154        Ok(pos)
155    }
156}
157
158pub(crate) fn determine_simple_packing_params(
159    values: &[f64],
160    dec: i16,
161) -> (SimplePacking, Vec<f64>, Bitmap) {
162    let mut min = f64::MAX;
163    let mut max = f64::MIN;
164    let mut bitmap = Bitmap::for_values(values);
165    let scaled = values
166        .iter()
167        .filter_map(|value| {
168            if value.is_nan() {
169                bitmap.push(false);
170                None
171            } else {
172                bitmap.push(true);
173                let scaled = value * 10_f64.powf(dec as f64);
174                (min, max) = (scaled.min(min), scaled.max(max));
175                Some(scaled)
176            }
177        })
178        .collect::<Vec<_>>();
179    let ref_val = min as f32;
180    if min == max {
181        let params = SimplePacking {
182            ref_val,
183            exp: 0,
184            dec,
185            num_bits: 0,
186        };
187        (params, Vec::new(), bitmap)
188    } else {
189        let range = max - min;
190        let exp = 0;
191        let num_bits = range.bits_required();
192        // TODO: if `num_bits` is too large, increase `exp` to reduce `num_bits`.
193        let params = SimplePacking {
194            ref_val,
195            exp,
196            dec,
197            num_bits,
198        };
199        (params, scaled, bitmap)
200    }
201}
202
203#[derive(Debug)]
204enum CodedValues {
205    NonUnique(Vec<u32>),
206    Unique(usize),
207}
208
209impl CodedValues {
210    pub(crate) fn num_values(&self) -> usize {
211        match self {
212            Self::NonUnique(vec) => vec.len(),
213            Self::Unique(size) => *size,
214        }
215    }
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    macro_rules! test_decimal_strategy {
223        ($((
224            $name:ident,
225            $input:expr,
226            $decimal:expr,
227            $expected_params:expr,
228        ),)*) => ($(
229            #[test]
230            fn $name() {
231                let values = $input;
232                let encoder = Encoder::new(&values, SimplePackingStrategy::Decimal($decimal));
233                let encoded = encoder.encode();
234                let actual_params = encoded.params();
235                let expected_params = $expected_params;
236                assert_eq!(actual_params, &expected_params);
237                let actual_num_values = encoded.coded.num_values();
238                assert_eq!(actual_num_values, values.len());
239            }
240        )*);
241    }
242
243    test_decimal_strategy! {
244        (
245            decimal_strategy_with_decimal_0,
246            (2..11).map(|val| val as f64).collect::<Vec<_>>(),
247            0,
248            SimplePacking {
249                ref_val: 2.,
250                exp: 0,
251                dec: 0,
252                num_bits: 4,
253            },
254        ),
255        (
256            decimal_strategy_with_decimal_1,
257            (2..11).map(|val| val as f64).collect::<Vec<_>>(),
258            1,
259            SimplePacking {
260                ref_val: 20.,
261                exp: 0,
262                dec: 1,
263                num_bits: 7,
264            },
265        ),
266        (
267            decimal_strategy_with_decimal_0_for_unique_values,
268            vec![10.0_f64; 256],
269            0,
270            SimplePacking {
271                ref_val: 10.0,
272                exp: 0,
273                dec: 0,
274                num_bits: 0,
275            },
276        ),
277    }
278
279    macro_rules! grib2_coded_values_roundtrip_tests {
280        ($(($name:ident, $input:expr, $decimal:expr),)*) => ($(
281            #[test]
282            fn $name() -> Result<(), Box<dyn std::error::Error>> {
283                let values = $input;
284                let encoder = Encoder::new(&values, SimplePackingStrategy::Decimal($decimal));
285                let encoded = encoder.encode();
286                let mut sect5 = vec![0; encoded.section5_len()];
287                let pos = encoded.write_section5(&mut sect5)?;
288                assert_eq!(pos, sect5.len());
289                let mut sect6 = vec![0; encoded.section6_len()];
290                let pos = encoded.write_section6(&mut sect6)?;
291                assert_eq!(pos, sect6.len());
292                let mut sect7 = vec![0; encoded.section7_len()];
293                let pos = encoded.write_section7(&mut sect7)?;
294                assert_eq!(pos, sect7.len());
295                let decoder = crate::Grib2SubmessageDecoder::new(values.len(), sect5, sect6, sect7)?;
296                let actual = decoder.dispatch()?.collect::<Vec<_>>();
297                let expected = values.iter().map(|val| *val as f32).collect::<Vec<_>>();
298                assert_eq!(actual.len(), expected.len());
299                actual
300                    .iter()
301                    .zip(expected.iter())
302                    .all(|(a, b)| (a.is_nan() && b.is_nan()) || (a == b));
303                Ok(())
304            }
305        )*);
306    }
307
308    grib2_coded_values_roundtrip_tests! {
309        (
310            grib2_coded_values_roundtrip_test_with_decimal_0_and_nonunique_values,
311            (2..11).map(|val| val as f64).collect::<Vec<_>>(),
312            0
313        ),
314        (
315            grib2_coded_values_roundtrip_test_with_decimal_0_and_unique_values,
316            vec![10.0_f64; 256],
317            0
318        ),
319        (
320            grib2_coded_values_roundtrip_test_with_data_containing_nan_values,
321            [f64::NAN; 32]
322                .into_iter()
323                .chain([1., 2., 3.].into_iter())
324                .chain([f64::NAN; 32].into_iter())
325                .chain([4., 5., 6., 7.].into_iter())
326                .chain([f64::NAN; 32].into_iter())
327                .chain([8., 9., 10., 11.].into_iter())
328                .collect::<Vec<_>>(),
329            0
330        ),
331    }
332}