nexrad_decode/messages/
message_header.rs

1use 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
14/// This value in the [MessageHeader::segment_size] field of a message header indicates that the
15/// message is variable-length rather than segmented.
16pub const VARIABLE_LENGTH_MESSAGE_SIZE: u16 = 65535;
17
18/// Message and system configuration information appended to the beginning of all messages.
19///
20/// Note that messages with a segment size of [VARIABLE_LENGTH_MESSAGE_SIZE] are not segmented and
21/// instead variable-length, with the segment count and segment number positions of the header
22/// (bytes 12-15) specifying the size of the full message in bytes.
23#[repr(C)]
24#[derive(Clone, PartialEq, Eq, Hash, Deserialize)]
25pub struct MessageHeader {
26    rpg_unknown: [u8; 12],
27
28    /// Size of this segment in half-words. Note that this only describes this segment's size,
29    /// though there could be multiple segments. In the case of a variable-length message (indicated
30    /// by this field being set to [VARIABLE_LENGTH_MESSAGE_SIZE]), the full message's size is
31    /// determined differently. See [MessageHeader::message_size_bytes] and
32    /// [MessageHeader::segment_count] for more information.
33    pub segment_size: Integer2,
34
35    /// Whether the RDA is operating on a redundant channel.
36    ///
37    /// Legacy:
38    ///  0 = Single Channel (no bits set)
39    ///  1 = Redundant Channel 1 (bit 0 set)
40    ///  2 = Redundant Channel 2 (bit 1 set)
41    /// ORDA
42    ///  8 = Single Channel (bit 3 set)
43    ///  9 = Redundant Channel 1 (bits 0 and 3 set)
44    /// 10 = Redundant Channel 2 (bits 1 and 3 set)
45    pub redundant_channel: Integer1,
46
47    /// Type discriminator.
48    pub message_type: Integer1,
49
50    /// Message sequence number.
51    pub sequence_number: Integer2,
52
53    /// This message's date represented as a count of days since 1 January 1970 00:00 GMT. It is
54    /// also referred-to as a "modified Julian date" where it is the Julian date - 2440586.5.
55    pub date: Integer2,
56
57    /// Milliseconds past midnight, GMT.
58    pub time: Integer4,
59
60    /// Number of segments in this message. If the [MessageHeader::segment_size] is less than
61    /// [VARIABLE_LENGTH_MESSAGE_SIZE], this field is meaningful, otherwise bytes 12-15 (this field
62    /// and [MessageHeader::segment_number]) specify the size of the message in bytes.
63    pub segment_count: Integer2,
64
65    /// This message segment's number. If the [MessageHeader::segment_size] is less than
66    /// [VARIABLE_LENGTH_MESSAGE_SIZE], this field is meaningful, otherwise, bytes 12-15 (this field
67    /// and [MessageHeader::segment_number]) specify the size of the message in bytes.
68    pub segment_number: Integer2,
69}
70
71impl MessageHeader {
72    /// If this message is [MessageHeader::segmented], this indicates this message segment's size.
73    /// Otherwise, this returns [None] and [MessageHeader::message_size] should be used to determine
74    /// the message's full size.
75    #[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    /// Whether the RDA is operating on a redundant channel.
85    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    /// Message type discriminator.
98    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    /// This message's date and time in UTC.
134    pub fn date_time(&self) -> Option<DateTime<Utc>> {
135        get_datetime(self.date, Duration::milliseconds(self.time as i64))
136    }
137
138    /// Whether this message is segmented or variable-length. If the message is segmented, multiple
139    /// message segments compose the full message. If the message is variable-length as indicated by
140    /// the [MessageHeader::segment_size] field being set to [VARIABLE_LENGTH_MESSAGE_SIZE], the
141    /// full message size can be retrieved by [MessageHeader::message_size_bytes].
142    pub fn segmented(&self) -> bool {
143        self.segment_size < VARIABLE_LENGTH_MESSAGE_SIZE
144    }
145
146    /// If the message is [MessageHeader::segmented], this indicates the number of segments in the
147    /// full message, otherwise this returns [None]. [MessageHeader::message_size_bytes] can be used
148    /// to determine the message's full size.
149    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    /// If the message is [MessageHeader::segmented], this indicates this segment's number/sequence
158    /// in the message, otherwise this returns [None]. [MessageHeader::message_size_bytes] can be
159    /// used to determine the message's full size.
160    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    /// The full size of the message in bytes. If the message is [MessageHeader::segmented] then
169    /// this is the segment size, otherwise this is the full variable-length message size.
170    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    /// The full size of the message. If the message is [MessageHeader::segmented] then this is the
182    /// segment size, otherwise this is the full variable-length message size.
183    #[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}