1use crate::error::{Error, Result};
4use crate::util::grib_i16;
5
6#[derive(Debug, Clone, PartialEq)]
8pub enum DataRepresentation {
9 SimplePacking(SimplePackingParams),
11 ComplexPacking(ComplexPackingParams),
13 Unsupported(u16),
15}
16
17#[derive(Debug, Clone, PartialEq)]
19pub struct SimplePackingParams {
20 pub encoded_values: usize,
21 pub reference_value: f32,
22 pub binary_scale: i16,
23 pub decimal_scale: i16,
24 pub bits_per_value: u8,
25 pub original_field_type: u8,
26}
27
28#[derive(Debug, Clone, PartialEq)]
30pub struct ComplexPackingParams {
31 pub encoded_values: usize,
32 pub reference_value: f32,
33 pub binary_scale: i16,
34 pub decimal_scale: i16,
35 pub group_reference_bits: u8,
36 pub original_field_type: u8,
37 pub group_splitting_method: u8,
38 pub missing_value_management: u8,
39 pub primary_missing_substitute: u32,
40 pub secondary_missing_substitute: u32,
41 pub num_groups: usize,
42 pub group_width_reference: u8,
43 pub group_width_bits: u8,
44 pub group_length_reference: u32,
45 pub group_length_increment: u8,
46 pub true_length_last_group: u32,
47 pub scaled_group_length_bits: u8,
48 pub spatial_differencing: Option<SpatialDifferencingParams>,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct SpatialDifferencingParams {
54 pub order: u8,
55 pub descriptor_octets: u8,
56}
57
58impl DataRepresentation {
59 pub fn parse(section_bytes: &[u8]) -> Result<Self> {
60 if section_bytes.len() < 11 {
61 return Err(Error::InvalidSection {
62 section: 5,
63 reason: format!("expected at least 11 bytes, got {}", section_bytes.len()),
64 });
65 }
66 if section_bytes[4] != 5 {
67 return Err(Error::InvalidSection {
68 section: section_bytes[4],
69 reason: "not a data representation section".into(),
70 });
71 }
72
73 let template = u16::from_be_bytes(section_bytes[9..11].try_into().unwrap());
74 match template {
75 0 => parse_simple_packing(section_bytes),
76 2 => parse_complex_packing(section_bytes, false),
77 3 => parse_complex_packing(section_bytes, true),
78 _ => Ok(Self::Unsupported(template)),
79 }
80 }
81
82 pub fn encoded_values(&self) -> Option<usize> {
83 match self {
84 Self::SimplePacking(params) => Some(params.encoded_values),
85 Self::ComplexPacking(params) => Some(params.encoded_values),
86 Self::Unsupported(_) => None,
87 }
88 }
89}
90
91fn parse_simple_packing(data: &[u8]) -> Result<DataRepresentation> {
92 if data.len() < 21 {
93 return Err(Error::InvalidSection {
94 section: 5,
95 reason: format!("template 5.0 requires 21 bytes, got {}", data.len()),
96 });
97 }
98
99 let encoded_values = u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize;
100 let reference_value = f32::from_be_bytes(data[11..15].try_into().unwrap());
101 let binary_scale = grib_i16(&data[15..17]).unwrap();
102 let decimal_scale = grib_i16(&data[17..19]).unwrap();
103 let bits_per_value = data[19];
104 let original_field_type = data[20];
105
106 Ok(DataRepresentation::SimplePacking(SimplePackingParams {
107 encoded_values,
108 reference_value,
109 binary_scale,
110 decimal_scale,
111 bits_per_value,
112 original_field_type,
113 }))
114}
115
116fn parse_complex_packing(
117 data: &[u8],
118 with_spatial_differencing: bool,
119) -> Result<DataRepresentation> {
120 let required = if with_spatial_differencing { 49 } else { 47 };
121 if data.len() < required {
122 return Err(Error::InvalidSection {
123 section: 5,
124 reason: format!(
125 "template 5.{} requires {required} bytes, got {}",
126 if with_spatial_differencing { 3 } else { 2 },
127 data.len()
128 ),
129 });
130 }
131
132 let group_splitting_method = data[21];
133 if group_splitting_method != 1 {
134 return Err(Error::UnsupportedGroupSplittingMethod(
135 group_splitting_method,
136 ));
137 }
138
139 let missing_value_management = data[22];
140 if missing_value_management > 2 {
141 return Err(Error::UnsupportedMissingValueManagement(
142 missing_value_management,
143 ));
144 }
145
146 let spatial_differencing = if with_spatial_differencing {
147 let order = data[47];
148 if !matches!(order, 1 | 2) {
149 return Err(Error::UnsupportedSpatialDifferencingOrder(order));
150 }
151 Some(SpatialDifferencingParams {
152 order,
153 descriptor_octets: data[48],
154 })
155 } else {
156 None
157 };
158
159 Ok(DataRepresentation::ComplexPacking(ComplexPackingParams {
160 encoded_values: u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize,
161 reference_value: f32::from_be_bytes(data[11..15].try_into().unwrap()),
162 binary_scale: grib_i16(&data[15..17]).unwrap(),
163 decimal_scale: grib_i16(&data[17..19]).unwrap(),
164 group_reference_bits: data[19],
165 original_field_type: data[20],
166 group_splitting_method,
167 missing_value_management,
168 primary_missing_substitute: u32::from_be_bytes(data[23..27].try_into().unwrap()),
169 secondary_missing_substitute: u32::from_be_bytes(data[27..31].try_into().unwrap()),
170 num_groups: u32::from_be_bytes(data[31..35].try_into().unwrap()) as usize,
171 group_width_reference: data[35],
172 group_width_bits: data[36],
173 group_length_reference: u32::from_be_bytes(data[37..41].try_into().unwrap()),
174 group_length_increment: data[41],
175 true_length_last_group: u32::from_be_bytes(data[42..46].try_into().unwrap()),
176 scaled_group_length_bits: data[46],
177 spatial_differencing,
178 }))
179}
180
181#[cfg(test)]
182mod tests {
183 use super::{DataRepresentation, SimplePackingParams};
184
185 #[test]
186 fn parses_simple_packing_template() {
187 let mut section = vec![0u8; 21];
188 section[..4].copy_from_slice(&(21u32).to_be_bytes());
189 section[4] = 5;
190 section[5..9].copy_from_slice(&3u32.to_be_bytes());
191 section[9..11].copy_from_slice(&0u16.to_be_bytes());
192 section[11..15].copy_from_slice(&10.0f32.to_be_bytes());
193 section[15..17].copy_from_slice(&2i16.to_be_bytes());
194 section[17..19].copy_from_slice(&1i16.to_be_bytes());
195 section[19] = 8;
196 section[20] = 0;
197
198 assert_eq!(
199 DataRepresentation::parse(§ion).unwrap(),
200 DataRepresentation::SimplePacking(SimplePackingParams {
201 encoded_values: 3,
202 reference_value: 10.0,
203 binary_scale: 2,
204 decimal_scale: 1,
205 bits_per_value: 8,
206 original_field_type: 0,
207 })
208 );
209 }
210}