Skip to main content

grib/encoder/
simple.rs

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