dvb_si/descriptors/extension/
c2_delivery_system.rs1use super::*;
3
4impl<'a> ExtensionBodyDef<'a> for C2DeliverySystem {
5 const TAG_EXTENSION: u8 = 0x0D;
6 const NAME: &'static str = "C2_DELIVERY_SYSTEM";
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12#[non_exhaustive]
13pub enum C2TuningFrequencyType {
14 DataSliceTuningFrequency,
16 C2SystemCentreFrequency,
18 InitialTuningPositionStaticDataSlice,
20 Reserved(u8),
22}
23
24impl C2TuningFrequencyType {
25 #[must_use]
26 pub fn from_u8(v: u8) -> Self {
28 match v {
29 0 => C2TuningFrequencyType::DataSliceTuningFrequency,
30 1 => C2TuningFrequencyType::C2SystemCentreFrequency,
31 2 => C2TuningFrequencyType::InitialTuningPositionStaticDataSlice,
32 other => C2TuningFrequencyType::Reserved(other),
33 }
34 }
35
36 #[must_use]
37 pub fn to_u8(self) -> u8 {
39 match self {
40 C2TuningFrequencyType::DataSliceTuningFrequency => 0,
41 C2TuningFrequencyType::C2SystemCentreFrequency => 1,
42 C2TuningFrequencyType::InitialTuningPositionStaticDataSlice => 2,
43 C2TuningFrequencyType::Reserved(v) => v,
44 }
45 }
46
47 #[must_use]
48 pub fn name(self) -> &'static str {
50 match self {
51 C2TuningFrequencyType::DataSliceTuningFrequency => "Data Slice tuning frequency",
52 C2TuningFrequencyType::C2SystemCentreFrequency => "C2 system centre frequency",
53 C2TuningFrequencyType::InitialTuningPositionStaticDataSlice => {
54 "initial tuning position for a Static Data Slice"
55 }
56 C2TuningFrequencyType::Reserved(_) => "reserved",
57 }
58 }
59}
60dvb_common::impl_spec_display!(C2TuningFrequencyType, Reserved);
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize))]
65#[non_exhaustive]
66pub enum ActiveOfdmSymbolDuration {
67 Us448,
69 Us597_33,
71 Reserved(u8),
73}
74
75impl ActiveOfdmSymbolDuration {
76 #[must_use]
77 pub fn from_u8(v: u8) -> Self {
79 match v {
80 0 => ActiveOfdmSymbolDuration::Us448,
81 1 => ActiveOfdmSymbolDuration::Us597_33,
82 other => ActiveOfdmSymbolDuration::Reserved(other),
83 }
84 }
85
86 #[must_use]
87 pub fn to_u8(self) -> u8 {
89 match self {
90 ActiveOfdmSymbolDuration::Us448 => 0,
91 ActiveOfdmSymbolDuration::Us597_33 => 1,
92 ActiveOfdmSymbolDuration::Reserved(v) => v,
93 }
94 }
95
96 #[must_use]
97 pub fn name(self) -> &'static str {
99 match self {
100 ActiveOfdmSymbolDuration::Us448 => "448 µs (4k FFT, 8 MHz)",
101 ActiveOfdmSymbolDuration::Us597_33 => "597.33 µs (4k FFT, 6 MHz)",
102 ActiveOfdmSymbolDuration::Reserved(_) => "reserved",
103 }
104 }
105}
106dvb_common::impl_spec_display!(ActiveOfdmSymbolDuration, Reserved);
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize))]
111#[non_exhaustive]
112pub enum C2GuardInterval {
113 G1_128,
115 G1_64,
117 Reserved(u8),
119}
120
121impl C2GuardInterval {
122 #[must_use]
123 pub fn from_u8(v: u8) -> Self {
125 match v {
126 0 => C2GuardInterval::G1_128,
127 1 => C2GuardInterval::G1_64,
128 other => C2GuardInterval::Reserved(other),
129 }
130 }
131
132 #[must_use]
133 pub fn to_u8(self) -> u8 {
135 match self {
136 C2GuardInterval::G1_128 => 0,
137 C2GuardInterval::G1_64 => 1,
138 C2GuardInterval::Reserved(v) => v,
139 }
140 }
141
142 #[must_use]
143 pub fn name(self) -> &'static str {
145 match self {
146 C2GuardInterval::G1_128 => "1/128",
147 C2GuardInterval::G1_64 => "1/64",
148 C2GuardInterval::Reserved(_) => "reserved",
149 }
150 }
151}
152dvb_common::impl_spec_display!(C2GuardInterval, Reserved);
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize))]
157pub struct C2DeliverySystem {
158 pub plp_id: u8,
160 pub data_slice_id: u8,
162 pub c2_system_tuning_frequency: u32,
164 pub c2_system_tuning_frequency_type: C2TuningFrequencyType,
166 pub active_ofdm_symbol_duration: ActiveOfdmSymbolDuration,
168 pub guard_interval: C2GuardInterval,
170}
171
172impl<'a> Parse<'a> for C2DeliverySystem {
173 type Error = crate::error::Error;
174 fn parse(sel: &'a [u8]) -> Result<Self> {
175 if sel.len() < C2_LEN {
176 return Err(Error::BufferTooShort {
177 need: C2_LEN,
178 have: sel.len(),
179 what: "C2_delivery_system body",
180 });
181 }
182 let packed = sel[6];
183 let (freq_bytes, _) = sel[2..]
184 .split_first_chunk::<4>()
185 .ok_or(Error::BufferTooShort {
186 need: C2_LEN,
187 have: sel.len(),
188 what: "C2_delivery_system body",
189 })?;
190 Ok(C2DeliverySystem {
191 plp_id: sel[0],
192 data_slice_id: sel[1],
193 c2_system_tuning_frequency: u32::from_be_bytes(*freq_bytes),
194 c2_system_tuning_frequency_type: C2TuningFrequencyType::from_u8(packed >> 6),
195 active_ofdm_symbol_duration: ActiveOfdmSymbolDuration::from_u8((packed >> 3) & 0x07),
196 guard_interval: C2GuardInterval::from_u8(packed & 0x07),
197 })
198 }
199}
200
201impl Serialize for C2DeliverySystem {
202 type Error = crate::error::Error;
203 fn serialized_len(&self) -> usize {
204 C2_LEN
205 }
206 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
207 let len = self.serialized_len();
208 if buf.len() < len {
209 return Err(Error::OutputBufferTooSmall {
210 need: len,
211 have: buf.len(),
212 });
213 }
214 buf[0] = self.plp_id;
215 buf[1] = self.data_slice_id;
216 buf[2..6].copy_from_slice(&self.c2_system_tuning_frequency.to_be_bytes());
217 buf[6] = (self.c2_system_tuning_frequency_type.to_u8() << 6)
218 | ((self.active_ofdm_symbol_duration.to_u8() & 0x07) << 3)
219 | (self.guard_interval.to_u8() & 0x07);
220 Ok(len)
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use crate::descriptors::extension::test_support::*;
228 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
229
230 #[test]
231 fn c2_tuning_frequency_type_roundtrip() {
232 for b in 0..=0xFFu8 {
233 assert_eq!(C2TuningFrequencyType::from_u8(b).to_u8(), b);
234 }
235 }
236
237 #[test]
238 fn c2_tuning_frequency_type_name() {
239 assert_eq!(
240 C2TuningFrequencyType::DataSliceTuningFrequency.name(),
241 "Data Slice tuning frequency"
242 );
243 assert_eq!(
244 C2TuningFrequencyType::C2SystemCentreFrequency.name(),
245 "C2 system centre frequency"
246 );
247 assert_eq!(
248 C2TuningFrequencyType::InitialTuningPositionStaticDataSlice.name(),
249 "initial tuning position for a Static Data Slice"
250 );
251 assert_eq!(C2TuningFrequencyType::Reserved(3).name(), "reserved");
252 }
253
254 #[test]
255 fn active_ofdm_symbol_duration_roundtrip() {
256 for b in 0..=0xFFu8 {
257 assert_eq!(ActiveOfdmSymbolDuration::from_u8(b).to_u8(), b);
258 }
259 }
260
261 #[test]
262 fn c2_guard_interval_roundtrip() {
263 for b in 0..=0xFFu8 {
264 assert_eq!(C2GuardInterval::from_u8(b).to_u8(), b);
265 }
266 }
267
268 #[test]
269 fn parse_c2_delivery_system() {
270 let packed = (0x01u8 << 3) | 0x01;
271 let sel = [0x05, 0x09, 0x12, 0x34, 0x56, 0x78, packed];
272 let bytes = wrap(0x0D, &sel);
273 let d = ExtensionDescriptor::parse(&bytes).unwrap();
274 match &d.body {
275 ExtensionBody::C2DeliverySystem(b) => {
276 assert_eq!(b.plp_id, 0x05);
277 assert_eq!(b.data_slice_id, 0x09);
278 assert_eq!(b.c2_system_tuning_frequency, 0x1234_5678);
279 assert_eq!(
280 b.c2_system_tuning_frequency_type,
281 C2TuningFrequencyType::DataSliceTuningFrequency
282 );
283 assert_eq!(
284 b.active_ofdm_symbol_duration,
285 ActiveOfdmSymbolDuration::Us597_33
286 );
287 assert_eq!(b.guard_interval, C2GuardInterval::G1_64);
288 }
289 other => panic!("expected C2DeliverySystem, got {other:?}"),
290 }
291 round_trip(&d);
292 }
293
294 #[test]
295 fn parse_c2_delivery_system_centre_frequency() {
296 let packed = 0x01u8 << 6;
297 let sel = [0x05, 0x09, 0x12, 0x34, 0x56, 0x78, packed];
298 let bytes = wrap(0x0D, &sel);
299 let d = ExtensionDescriptor::parse(&bytes).unwrap();
300 match &d.body {
301 ExtensionBody::C2DeliverySystem(b) => {
302 assert_eq!(
303 b.c2_system_tuning_frequency_type,
304 C2TuningFrequencyType::C2SystemCentreFrequency
305 );
306 }
307 other => panic!("expected C2DeliverySystem, got {other:?}"),
308 }
309 round_trip(&d);
310 }
311
312 #[test]
313 fn parse_c2_delivery_system_initial_tuning() {
314 let packed = 0x02u8 << 6;
315 let sel = [0x05, 0x09, 0x12, 0x34, 0x56, 0x78, packed];
316 let bytes = wrap(0x0D, &sel);
317 let d = ExtensionDescriptor::parse(&bytes).unwrap();
318 match &d.body {
319 ExtensionBody::C2DeliverySystem(b) => {
320 assert_eq!(
321 b.c2_system_tuning_frequency_type,
322 C2TuningFrequencyType::InitialTuningPositionStaticDataSlice
323 );
324 }
325 other => panic!("expected C2DeliverySystem, got {other:?}"),
326 }
327 round_trip(&d);
328 }
329
330 #[test]
331 fn parse_c2_delivery_system_reserved_values() {
332 let packed = (0x03u8 << 6) | (0x07u8 << 3) | 0x07;
333 let sel = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, packed];
334 let bytes = wrap(0x0D, &sel);
335 let d = ExtensionDescriptor::parse(&bytes).unwrap();
336 match &d.body {
337 ExtensionBody::C2DeliverySystem(b) => {
338 assert_eq!(
339 b.c2_system_tuning_frequency_type,
340 C2TuningFrequencyType::Reserved(3)
341 );
342 assert_eq!(
343 b.active_ofdm_symbol_duration,
344 ActiveOfdmSymbolDuration::Reserved(7)
345 );
346 assert_eq!(b.guard_interval, C2GuardInterval::Reserved(7));
347 }
348 other => panic!("expected C2DeliverySystem, got {other:?}"),
349 }
350 round_trip(&d);
351 }
352}