nexrad_decode/messages/digital_radar_data/
generic_data_block.rs1use crate::messages::digital_radar_data::{ControlFlags, DataBlockId, ScaledMomentValue};
2use crate::messages::primitive_aliases::{
3 Code1, Integer1, Integer2, Integer4, Real4, ScaledInteger2,
4};
5use serde::Deserialize;
6use std::fmt::Debug;
7
8#[cfg(feature = "uom")]
9use uom::si::f64::{Information, Length};
10#[cfg(feature = "uom")]
11use uom::si::information::byte;
12#[cfg(feature = "uom")]
13use uom::si::length::kilometer;
14
15#[derive(Clone, PartialEq)]
17pub struct GenericDataBlock {
18 pub header: GenericDataBlockHeader,
20
21 pub encoded_data: Vec<u8>,
23}
24
25impl GenericDataBlock {
26 pub(crate) fn new(header: GenericDataBlockHeader) -> Self {
28 let word_size_bytes = header.data_word_size as usize / 8;
29 let encoded_data_size = header.number_of_data_moment_gates as usize * word_size_bytes;
30 Self {
31 encoded_data: vec![0; encoded_data_size],
32 header,
33 }
34 }
35
36 pub fn encoded_values(&self) -> &[u8] {
40 &self.encoded_data
41 }
42
43 pub fn decoded_values(&self) -> Vec<ScaledMomentValue> {
47 self.encoded_data
48 .iter()
49 .copied()
50 .map(|raw_value| {
51 if self.header.scale == 0.0 {
52 return ScaledMomentValue::Value(raw_value as f32);
53 }
54
55 match raw_value {
56 0 => ScaledMomentValue::BelowThreshold,
57 1 => ScaledMomentValue::RangeFolded,
58 _ => ScaledMomentValue::Value(
59 (raw_value as f32 - self.header.offset) / self.header.scale,
60 ),
61 }
62 })
63 .collect()
64 }
65
66 #[cfg(feature = "nexrad-model")]
68 pub fn moment_data(&self) -> nexrad_model::data::MomentData {
69 nexrad_model::data::MomentData::from_fixed_point(
70 self.header.scale,
71 self.header.offset,
72 self.encoded_data.clone(),
73 )
74 }
75
76 #[cfg(feature = "nexrad-model")]
78 pub fn into_moment_data(self) -> nexrad_model::data::MomentData {
79 nexrad_model::data::MomentData::from_fixed_point(
80 self.header.scale,
81 self.header.offset,
82 self.encoded_data,
83 )
84 }
85}
86
87impl Debug for GenericDataBlock {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_struct("GenericDataBlock")
90 .field("header", &self.header)
91 .field("data", &self.encoded_data.len())
92 .finish()
93 }
94}
95
96#[derive(Clone, PartialEq, Deserialize)]
98pub struct GenericDataBlockHeader {
99 pub data_block_id: DataBlockId,
101
102 pub reserved: Integer4,
104
105 pub number_of_data_moment_gates: Integer2,
107
108 pub data_moment_range: ScaledInteger2,
110
111 pub data_moment_range_sample_interval: ScaledInteger2,
113
114 pub tover: ScaledInteger2,
117
118 pub snr_threshold: ScaledInteger2,
120
121 pub control_flags: Code1,
129
130 pub data_word_size: Integer1,
132
133 pub scale: Real4,
135
136 pub offset: Real4,
138}
139
140impl GenericDataBlockHeader {
141 #[cfg(feature = "uom")]
143 pub fn data_moment_range(&self) -> Length {
144 Length::new::<kilometer>(self.data_moment_range as f64 * 0.001)
145 }
146
147 #[cfg(feature = "uom")]
149 pub fn data_moment_range_sample_interval(&self) -> Length {
150 Length::new::<kilometer>(self.data_moment_range_sample_interval as f64 * 0.001)
151 }
152
153 pub fn control_flags(&self) -> ControlFlags {
155 match self.control_flags {
156 0 => ControlFlags::None,
157 1 => ControlFlags::RecombinedAzimuthalRadials,
158 2 => ControlFlags::RecombinedRangeGates,
159 3 => ControlFlags::RecombinedRadialsAndRangeGatesToLegacyResolution,
160 _ => panic!("Invalid control flag value: {}", self.control_flags),
161 }
162 }
163
164 #[cfg(feature = "uom")]
166 pub fn moment_size(&self) -> Information {
167 Information::new::<byte>(
168 self.number_of_data_moment_gates as f64 * self.data_word_size as f64 / 8.0,
169 )
170 }
171}
172
173#[cfg(not(feature = "uom"))]
174impl Debug for GenericDataBlockHeader {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 f.debug_struct("GenericDataBlockHeader")
177 .field("data_block_id", &self.data_block_id)
178 .field("reserved", &self.reserved)
179 .field(
180 "number_of_data_moment_gates",
181 &self.number_of_data_moment_gates,
182 )
183 .field("data_moment_range", &self.data_moment_range)
184 .field(
185 "data_moment_range_sample_interval",
186 &self.data_moment_range_sample_interval,
187 )
188 .field("tover", &self.tover)
189 .field("snr_threshold", &self.snr_threshold)
190 .field("control_flags", &self.control_flags())
191 .field("data_word_size", &self.data_word_size)
192 .field("scale", &self.scale)
193 .field("offset", &self.offset)
194 .finish()
195 }
196}
197
198#[cfg(feature = "uom")]
199impl Debug for GenericDataBlockHeader {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 f.debug_struct("GenericDataBlockHeader")
202 .field("data_block_id", &self.data_block_id)
203 .field("reserved", &self.reserved)
204 .field(
205 "number_of_data_moment_gates",
206 &self.number_of_data_moment_gates,
207 )
208 .field("data_moment_range", &self.data_moment_range())
209 .field(
210 "data_moment_range_sample_interval",
211 &self.data_moment_range_sample_interval(),
212 )
213 .field("tover", &self.tover)
214 .field("snr_threshold", &self.snr_threshold)
215 .field("control_flags", &self.control_flags())
216 .field("data_word_size", &self.data_word_size)
217 .field("scale", &self.scale)
218 .field("offset", &self.offset)
219 .finish()
220 }
221}