nexrad_decode/messages/
message_header.rs1use crate::messages::definitions::RedundantChannel;
2use crate::messages::message_type::MessageType;
3use crate::messages::primitive_aliases::{Integer1, Integer2, Integer4};
4use crate::util::get_datetime;
5use chrono::{DateTime, Duration, Utc};
6use serde::Deserialize;
7use std::fmt::Debug;
8
9#[cfg(feature = "uom")]
10use uom::si::f64::Information;
11#[cfg(feature = "uom")]
12use uom::si::information::byte;
13
14pub const VARIABLE_LENGTH_MESSAGE_SIZE: u16 = 65535;
17
18#[repr(C)]
24#[derive(Clone, PartialEq, Eq, Hash, Deserialize)]
25pub struct MessageHeader {
26 rpg_unknown: [u8; 12],
27
28 pub segment_size: Integer2,
34
35 pub redundant_channel: Integer1,
46
47 pub message_type: Integer1,
49
50 pub sequence_number: Integer2,
52
53 pub date: Integer2,
56
57 pub time: Integer4,
59
60 pub segment_count: Integer2,
64
65 pub segment_number: Integer2,
69}
70
71impl MessageHeader {
72 #[cfg(feature = "uom")]
76 pub fn segment_size(&self) -> Option<Information> {
77 if self.segment_size < VARIABLE_LENGTH_MESSAGE_SIZE {
78 Some(Information::new::<byte>((self.segment_size * 2) as f64))
79 } else {
80 None
81 }
82 }
83
84 pub fn rda_redundant_channel(&self) -> RedundantChannel {
86 match self.redundant_channel {
87 0 => RedundantChannel::LegacySingleChannel,
88 1 => RedundantChannel::LegacyRedundantChannel1,
89 2 => RedundantChannel::LegacyRedundantChannel2,
90 8 => RedundantChannel::ORDASingleChannel,
91 9 => RedundantChannel::ORDARedundantChannel1,
92 10 => RedundantChannel::ORDARedundantChannel2,
93 _ => panic!("Invalid RDA redundant channel: {}", self.redundant_channel),
94 }
95 }
96
97 pub fn message_type(&self) -> MessageType {
99 match self.message_type {
100 1 => MessageType::RDADigitalRadarData,
101 2 => MessageType::RDAStatusData,
102 3 => MessageType::RDAPerformanceMaintenanceData,
103 4 => MessageType::RDAConsoleMessage,
104 5 => MessageType::RDAVolumeCoveragePattern,
105 6 => MessageType::RDAControlCommands,
106 7 => MessageType::RPGVolumeCoveragePattern,
107 8 => MessageType::RPGClutterCensorZones,
108 9 => MessageType::RPGRequestForData,
109 10 => MessageType::RPGConsoleMessage,
110 11 => MessageType::RDALoopBackTest,
111 12 => MessageType::RPGLoopBackTest,
112 13 => MessageType::RDAClutterFilterBypassMap,
113 14 => MessageType::Spare1,
114 15 => MessageType::RDAClutterFilterMap,
115 16 => MessageType::ReservedFAARMSOnly1,
116 17 => MessageType::ReservedFAARMSOnly2,
117 18 => MessageType::RDAAdaptationData,
118 20 => MessageType::Reserved1,
119 21 => MessageType::Reserved2,
120 22 => MessageType::Reserved3,
121 23 => MessageType::Reserved4,
122 24 => MessageType::ReservedFAARMSOnly3,
123 25 => MessageType::ReservedFAARMSOnly4,
124 26 => MessageType::ReservedFAARMSOnly5,
125 29 => MessageType::Reserved5,
126 31 => MessageType::RDADigitalRadarDataGenericFormat,
127 32 => MessageType::RDAPRFData,
128 33 => MessageType::RDALogData,
129 _ => MessageType::Unknown,
130 }
131 }
132
133 pub fn date_time(&self) -> Option<DateTime<Utc>> {
135 get_datetime(self.date, Duration::milliseconds(self.time as i64))
136 }
137
138 pub fn segmented(&self) -> bool {
143 self.segment_size < VARIABLE_LENGTH_MESSAGE_SIZE
144 }
145
146 pub fn segment_count(&self) -> Option<u16> {
150 if self.segment_size < VARIABLE_LENGTH_MESSAGE_SIZE {
151 Some(self.segment_count)
152 } else {
153 None
154 }
155 }
156
157 pub fn segment_number(&self) -> Option<u16> {
161 if self.segment_size < VARIABLE_LENGTH_MESSAGE_SIZE {
162 Some(self.segment_number)
163 } else {
164 None
165 }
166 }
167
168 pub fn message_size_bytes(&self) -> u32 {
171 match self.segment_count() {
172 Some(_) => self.segment_size as u32 * 2,
173 None => {
174 let segment_number = self.segment_number as u32;
175 let segment_size = self.segment_size as u32;
176 (segment_number << 16) | (segment_size << 1)
177 }
178 }
179 }
180
181 #[cfg(feature = "uom")]
184 pub fn message_size(&self) -> Information {
185 match self.segment_count() {
186 Some(_) => {
187 let segment_size_bytes = self.segment_size << 1;
188 Information::new::<byte>(segment_size_bytes as f64)
189 }
190 None => {
191 let segment_number = self.segment_number as u32;
192 let segment_size = self.segment_size as u32;
193 let message_size_bytes = (segment_number << 16) | segment_size;
194 Information::new::<byte>(message_size_bytes as f64)
195 }
196 }
197 }
198}
199
200#[cfg(not(feature = "uom"))]
201impl Debug for MessageHeader {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 f.debug_struct("MessageHeader")
204 .field("segment_size", &self.segment_size)
205 .field("redundant_channel", &self.rda_redundant_channel())
206 .field("message_type", &self.message_type())
207 .field("sequence_number", &self.sequence_number)
208 .field("date_time", &self.date_time())
209 .field("segment_count", &self.segment_count())
210 .field("segment_number", &self.segment_number())
211 .field("message_size_bytes", &self.message_size_bytes())
212 .finish()
213 }
214}
215
216#[cfg(feature = "uom")]
217impl Debug for MessageHeader {
218 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
219 f.debug_struct("MessageHeader")
220 .field("segment_size", &self.segment_size())
221 .field("redundant_channel", &self.rda_redundant_channel())
222 .field("message_type", &self.message_type())
223 .field("sequence_number", &self.sequence_number)
224 .field("date_time", &self.date_time())
225 .field("segment_count", &self.segment_count())
226 .field("segment_number", &self.segment_number())
227 .field("message_size", &self.message_size())
228 .finish()
229 }
230}