dvb_t2mi/payload/
aux_iq.rs1use dvb_common::{Parse, Serialize};
8
9const AUX_ID_MIN: u8 = 1;
11const AUX_ID_MAX: u8 = 0x0F;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct AuxIqPayload<'a> {
24 pub frame_idx: u8,
26 pub aux_id: u8,
28 #[cfg_attr(feature = "serde", serde(borrow))]
30 pub aux_stream_data: &'a [u8],
31}
32
33impl<'a> Parse<'a> for AuxIqPayload<'a> {
34 type Error = crate::error::Error;
35
36 fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
37 if bytes.len() < 3 {
38 return Err(crate::Error::BufferTooShort {
39 need: 3,
40 have: bytes.len(),
41 what: "AuxIqPayload header",
42 });
43 }
44
45 let frame_idx = bytes[0];
46 let aux_id = (bytes[1] >> 4) & 0x0F;
47
48 if !(AUX_ID_MIN..=AUX_ID_MAX).contains(&aux_id) {
49 return Err(crate::Error::ReservedBitsViolation {
50 field: "aux_id",
51 reason: "aux_id out of range 1..=15 (ETSI TS 102 773 §5.2.2)",
52 });
53 }
54
55 let rfu = ((bytes[1] & 0x0F) as u16) << 8 | (bytes[2] as u16);
57 if rfu != 0 {
58 return Err(crate::Error::ReservedBitsViolation {
59 field: "byte 1 [3:0] + byte 2",
60 reason: "12-bit RFU must be zero (ETSI TS 102 773 §5.2.2)",
61 });
62 }
63
64 Ok(AuxIqPayload {
65 frame_idx,
66 aux_id,
67 aux_stream_data: &bytes[3..],
68 })
69 }
70}
71
72impl<'a> crate::traits::PayloadDef<'a> for AuxIqPayload<'a> {
73 const PACKET_TYPE: u8 = 0x01;
74 const NAME: &'static str = "AUX_IQ";
75}
76
77impl Serialize for AuxIqPayload<'_> {
78 type Error = crate::error::Error;
79
80 fn serialized_len(&self) -> usize {
81 3 + self.aux_stream_data.len()
82 }
83
84 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
85 if buf.len() < self.serialized_len() {
86 return Err(crate::Error::OutputBufferTooSmall {
87 need: self.serialized_len(),
88 have: buf.len(),
89 });
90 }
91
92 if !(AUX_ID_MIN..=AUX_ID_MAX).contains(&self.aux_id) {
93 return Err(crate::Error::ReservedBitsViolation {
94 field: "aux_id",
95 reason: "aux_id out of range 1..=15 (ETSI TS 102 773 §5.2.2)",
96 });
97 }
98
99 buf[0] = self.frame_idx;
100 buf[1] = (self.aux_id & 0x0F) << 4; buf[2] = 0; buf[3..3 + self.aux_stream_data.len()].copy_from_slice(self.aux_stream_data);
103
104 Ok(self.serialized_len())
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn parse_extracts_frame_idx_and_aux_id() {
114 let buf = [0x42u8, 0x70, 0x00, 0xCA, 0xFE];
115 let result = AuxIqPayload::parse(&buf).unwrap();
116 assert_eq!(result.frame_idx, 0x42);
117 assert_eq!(result.aux_id, 0x07);
118 }
119
120 #[test]
121 fn parse_rejects_aux_id_zero() {
122 let buf = [0x00u8, 0x00, 0x00, 0xCA];
123 let result = AuxIqPayload::parse(&buf);
124 assert!(result.is_err());
125 }
126
127 #[test]
128 fn parse_rejects_aux_id_above_f() {
129 let _buf = [0x00u8, 0xF0, 0x00, 0xCA];
130 let buf2 = [0x00u8, 0x01, 0x00, 0xCA];
133 let result2 = AuxIqPayload::parse(&buf2);
135 assert!(result2.is_err());
136 }
137
138 #[test]
139 fn parse_rejects_nonzero_rfu_bits() {
140 let buf = [0x00u8, 0x1F, 0xFF, 0xCA];
141 let result = AuxIqPayload::parse(&buf);
143 assert!(result.is_err());
144 }
145
146 #[test]
147 fn parse_preserves_raw_aux_stream_data_bytes() {
148 let data: Vec<u8> = (0..50).collect();
149 let mut buf = vec![0x01u8, 0x50, 0x00];
150 buf.extend_from_slice(&data);
151 let result = AuxIqPayload::parse(&buf).unwrap();
152 assert_eq!(result.aux_stream_data, data.as_slice());
153 }
154
155 #[test]
156 fn parse_rejects_buffer_shorter_than_3() {
157 assert!(AuxIqPayload::parse(&[0x00]).is_err());
158 assert!(AuxIqPayload::parse(&[]).is_err());
159 }
160
161 #[test]
162 fn serialize_round_trip() {
163 let orig = AuxIqPayload {
164 frame_idx: 0xAB,
165 aux_id: 0x05,
166 aux_stream_data: &[0x12, 0x34, 0x56, 0x78],
167 };
168 let mut buf = vec![0u8; orig.serialized_len()];
169 orig.serialize_into(&mut buf).unwrap();
170 let parsed = AuxIqPayload::parse(&buf).unwrap();
171 assert_eq!(orig, parsed);
172 }
173
174 #[test]
175 fn serialize_rejects_invalid_aux_id() {
176 let payload = AuxIqPayload {
177 frame_idx: 0x00,
178 aux_id: 0x00, aux_stream_data: &[],
180 };
181 let mut buf = [0u8; 3];
182 assert!(payload.serialize_into(&mut buf).is_err());
183 }
184
185 #[test]
186 fn serialize_zeros_rfu_byte() {
187 let payload = AuxIqPayload {
188 frame_idx: 0x11,
189 aux_id: 0x0A,
190 aux_stream_data: &[],
191 };
192 let mut buf = [0xFFu8; 3];
193 payload.serialize_into(&mut buf).unwrap();
194 assert_eq!(buf[1] & 0x0F, 0x00);
195 assert_eq!(buf[2], 0x00);
196 }
197}