dvb_t2mi/payload/
fef_iq.rs1use std::fmt;
4
5use dvb_common::{Parse, Serialize};
6
7use super::fef_null::S1Field;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
13pub struct FefIqPayload<'a> {
14 pub fef_idx: u8,
16 pub s1_field: S1Field,
18 pub s2_field: u8,
20 pub fef_part_data: &'a [u8],
22}
23
24const FEF_IQ_HEADER_LEN: usize = 3;
25
26impl<'a> Parse<'a> for FefIqPayload<'a> {
27 type Error = crate::error::Error;
28
29 fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
30 if bytes.len() < FEF_IQ_HEADER_LEN {
31 return Err(crate::Error::BufferTooShort {
32 need: FEF_IQ_HEADER_LEN,
33 have: bytes.len(),
34 what: "FefIqPayload header",
35 });
36 }
37
38 if bytes[1] != 0 || bytes[2] & 0x80 != 0 {
41 return Err(crate::Error::ReservedBitsViolation {
42 field: "9-bit rfu",
43 reason: "Must be zero (ETSI TS 102 773 §5.2.10)",
44 });
45 }
46
47 Ok(FefIqPayload {
48 fef_idx: bytes[0],
49 s1_field: S1Field::try_from((bytes[2] >> 4) & 0x07)?,
51 s2_field: bytes[2] & 0x0F,
52 fef_part_data: &bytes[FEF_IQ_HEADER_LEN..],
53 })
54 }
55}
56
57impl<'a> crate::traits::PayloadDef<'a> for FefIqPayload<'a> {
58 const PACKET_TYPE: u8 = 0x31;
59 const NAME: &'static str = "FEF_IQ";
60}
61
62impl Serialize for FefIqPayload<'_> {
63 type Error = crate::error::Error;
64
65 fn serialized_len(&self) -> usize {
66 FEF_IQ_HEADER_LEN + self.fef_part_data.len()
67 }
68
69 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
70 if buf.len() < self.serialized_len() {
71 return Err(crate::Error::OutputBufferTooSmall {
72 need: self.serialized_len(),
73 have: buf.len(),
74 });
75 }
76
77 if self.s2_field > 0x0F {
78 return Err(crate::Error::ReservedBitsViolation {
79 field: "s2_field",
80 reason: "Must fit in 4 bits",
81 });
82 }
83
84 buf[0] = self.fef_idx;
85 buf[1] = 0; buf[2] = ((u8::from(self.s1_field) & 0x07) << 4) | (self.s2_field & 0x0F);
87
88 if !self.fef_part_data.is_empty() {
89 buf[FEF_IQ_HEADER_LEN..FEF_IQ_HEADER_LEN + self.fef_part_data.len()]
90 .copy_from_slice(self.fef_part_data);
91 }
92
93 Ok(self.serialized_len())
94 }
95}
96
97impl fmt::Display for FefIqPayload<'_> {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 write!(
100 f,
101 "FEF I/Q {{ fef_idx: {}, s1: {:?}, s2: {:04b}, data_len: {} }}",
102 self.fef_idx,
103 self.s1_field,
104 self.s2_field,
105 self.fef_part_data.len()
106 )
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn parse_extracts_fields_and_data() {
116 let data = [0xDE, 0xAD];
117 let mut buf = vec![0x05u8, 0x00, 0x2A];
119 buf.extend_from_slice(&data);
120
121 let result = FefIqPayload::parse(&buf).unwrap();
122 assert_eq!(result.fef_idx, 5);
123 assert_eq!(result.s1_field, S1Field::V2);
124 assert_eq!(result.s2_field, 0x0A);
125 assert_eq!(result.fef_part_data, &data[..]);
126 }
127
128 #[test]
129 fn parse_rejects_nonzero_rfu() {
130 let buf = [0x00u8, 0x1F, 0x00];
131 assert!(FefIqPayload::parse(&buf).is_err());
132 }
133
134 #[test]
135 fn serialize_round_trip() {
136 let orig = FefIqPayload {
137 fef_idx: 7,
138 s1_field: S1Field::V3,
139 s2_field: 0x0B,
140 fef_part_data: &[0xCA, 0xFE],
141 };
142 let mut buf = vec![0u8; orig.serialized_len()];
143 orig.serialize_into(&mut buf).unwrap();
144 let parsed = FefIqPayload::parse(&buf).unwrap();
145 assert_eq!(orig, parsed);
146 }
147}