1use super::*;
3
4impl<'a> ExtensionBodyDef<'a> for T2DeliverySystem {
5 const TAG_EXTENSION: u8 = 0x04;
6 const NAME: &'static str = "T2_DELIVERY_SYSTEM";
7}
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[non_exhaustive]
17pub enum T2SisoMiso {
18 Siso,
20 Miso,
22 Reserved(u8),
24}
25
26impl T2SisoMiso {
27 #[must_use]
28 pub fn from_u8(v: u8) -> Self {
30 match v {
31 0 => T2SisoMiso::Siso,
32 1 => T2SisoMiso::Miso,
33 other => T2SisoMiso::Reserved(other),
34 }
35 }
36
37 #[must_use]
38 pub fn to_u8(self) -> u8 {
40 match self {
41 T2SisoMiso::Siso => 0,
42 T2SisoMiso::Miso => 1,
43 T2SisoMiso::Reserved(v) => v,
44 }
45 }
46
47 #[must_use]
48 pub fn name(self) -> &'static str {
50 match self {
51 T2SisoMiso::Siso => "SISO",
52 T2SisoMiso::Miso => "MISO",
53 T2SisoMiso::Reserved(_) => "reserved",
54 }
55 }
56}
57dvb_common::impl_spec_display!(T2SisoMiso, Reserved);
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize))]
62#[non_exhaustive]
63pub enum T2Bandwidth {
64 Mhz8,
66 Mhz7,
68 Mhz6,
70 Mhz5,
72 Mhz10,
74 Mhz1_712,
76 Reserved(u8),
78}
79
80impl T2Bandwidth {
81 #[must_use]
82 pub fn from_u8(v: u8) -> Self {
84 match v {
85 0 => T2Bandwidth::Mhz8,
86 1 => T2Bandwidth::Mhz7,
87 2 => T2Bandwidth::Mhz6,
88 3 => T2Bandwidth::Mhz5,
89 4 => T2Bandwidth::Mhz10,
90 5 => T2Bandwidth::Mhz1_712,
91 other => T2Bandwidth::Reserved(other),
92 }
93 }
94
95 #[must_use]
96 pub fn to_u8(self) -> u8 {
98 match self {
99 T2Bandwidth::Mhz8 => 0,
100 T2Bandwidth::Mhz7 => 1,
101 T2Bandwidth::Mhz6 => 2,
102 T2Bandwidth::Mhz5 => 3,
103 T2Bandwidth::Mhz10 => 4,
104 T2Bandwidth::Mhz1_712 => 5,
105 T2Bandwidth::Reserved(v) => v,
106 }
107 }
108
109 #[must_use]
110 pub fn name(self) -> &'static str {
112 match self {
113 T2Bandwidth::Mhz8 => "8 MHz",
114 T2Bandwidth::Mhz7 => "7 MHz",
115 T2Bandwidth::Mhz6 => "6 MHz",
116 T2Bandwidth::Mhz5 => "5 MHz",
117 T2Bandwidth::Mhz10 => "10 MHz",
118 T2Bandwidth::Mhz1_712 => "1.712 MHz",
119 T2Bandwidth::Reserved(_) => "reserved",
120 }
121 }
122}
123dvb_common::impl_spec_display!(T2Bandwidth, Reserved);
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize))]
128#[non_exhaustive]
129pub enum T2GuardInterval {
130 G1_32,
132 G1_16,
134 G1_8,
136 G1_4,
138 G1_128,
140 G19_128,
142 G19_256,
144 Reserved(u8),
146}
147
148impl T2GuardInterval {
149 #[must_use]
150 pub fn from_u8(v: u8) -> Self {
152 match v {
153 0 => T2GuardInterval::G1_32,
154 1 => T2GuardInterval::G1_16,
155 2 => T2GuardInterval::G1_8,
156 3 => T2GuardInterval::G1_4,
157 4 => T2GuardInterval::G1_128,
158 5 => T2GuardInterval::G19_128,
159 6 => T2GuardInterval::G19_256,
160 other => T2GuardInterval::Reserved(other),
161 }
162 }
163
164 #[must_use]
165 pub fn to_u8(self) -> u8 {
167 match self {
168 T2GuardInterval::G1_32 => 0,
169 T2GuardInterval::G1_16 => 1,
170 T2GuardInterval::G1_8 => 2,
171 T2GuardInterval::G1_4 => 3,
172 T2GuardInterval::G1_128 => 4,
173 T2GuardInterval::G19_128 => 5,
174 T2GuardInterval::G19_256 => 6,
175 T2GuardInterval::Reserved(v) => v,
176 }
177 }
178
179 #[must_use]
180 pub fn name(self) -> &'static str {
182 match self {
183 T2GuardInterval::G1_32 => "1/32",
184 T2GuardInterval::G1_16 => "1/16",
185 T2GuardInterval::G1_8 => "1/8",
186 T2GuardInterval::G1_4 => "1/4",
187 T2GuardInterval::G1_128 => "1/128",
188 T2GuardInterval::G19_128 => "19/128",
189 T2GuardInterval::G19_256 => "19/256",
190 T2GuardInterval::Reserved(_) => "reserved",
191 }
192 }
193}
194dvb_common::impl_spec_display!(T2GuardInterval, Reserved);
195
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198#[cfg_attr(feature = "serde", derive(serde::Serialize))]
199#[non_exhaustive]
200pub enum T2TransmissionMode {
201 Mode2k,
203 Mode8k,
205 Mode4k,
207 Mode1k,
209 Mode16k,
211 Mode32k,
213 Reserved(u8),
215}
216
217impl T2TransmissionMode {
218 #[must_use]
219 pub fn from_u8(v: u8) -> Self {
221 match v {
222 0 => T2TransmissionMode::Mode2k,
223 1 => T2TransmissionMode::Mode8k,
224 2 => T2TransmissionMode::Mode4k,
225 3 => T2TransmissionMode::Mode1k,
226 4 => T2TransmissionMode::Mode16k,
227 5 => T2TransmissionMode::Mode32k,
228 other => T2TransmissionMode::Reserved(other),
229 }
230 }
231
232 #[must_use]
233 pub fn to_u8(self) -> u8 {
235 match self {
236 T2TransmissionMode::Mode2k => 0,
237 T2TransmissionMode::Mode8k => 1,
238 T2TransmissionMode::Mode4k => 2,
239 T2TransmissionMode::Mode1k => 3,
240 T2TransmissionMode::Mode16k => 4,
241 T2TransmissionMode::Mode32k => 5,
242 T2TransmissionMode::Reserved(v) => v,
243 }
244 }
245
246 #[must_use]
247 pub fn name(self) -> &'static str {
249 match self {
250 T2TransmissionMode::Mode2k => "2k",
251 T2TransmissionMode::Mode8k => "8k",
252 T2TransmissionMode::Mode4k => "4k",
253 T2TransmissionMode::Mode1k => "1k",
254 T2TransmissionMode::Mode16k => "16k",
255 T2TransmissionMode::Mode32k => "32k",
256 T2TransmissionMode::Reserved(_) => "reserved",
257 }
258 }
259}
260dvb_common::impl_spec_display!(T2TransmissionMode, Reserved);
261
262#[derive(Debug, Clone, PartialEq, Eq)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize))]
269pub struct T2Cell {
270 pub cell_id: u16,
272 pub centre_frequencies: Vec<u32>,
275 pub subcells: Vec<T2Subcell>,
277}
278
279impl T2Cell {
280 #[must_use]
282 pub fn centre_frequencies_hz(&self) -> Vec<u64> {
283 self.centre_frequencies
284 .iter()
285 .map(|&f| u64::from(f) * 10)
286 .collect()
287 }
288}
289
290#[derive(Debug, Clone, Copy, PartialEq, Eq)]
292#[cfg_attr(feature = "serde", derive(serde::Serialize))]
293pub struct T2Subcell {
294 pub cell_id_extension: u8,
296 pub transposer_frequency: u32,
298}
299
300impl T2Subcell {
301 #[must_use]
303 pub fn transposer_frequency_hz(&self) -> u64 {
304 u64::from(self.transposer_frequency) * 10
305 }
306}
307
308#[derive(Debug, Clone, PartialEq, Eq)]
310#[cfg_attr(feature = "serde", derive(serde::Serialize))]
311pub struct T2DeliverySystem {
312 pub plp_id: u8,
314 pub t2_system_id: u16,
316 pub siso_miso: Option<T2SisoMiso>,
318 pub bandwidth: Option<T2Bandwidth>,
320 pub guard_interval: Option<T2GuardInterval>,
322 pub transmission_mode: Option<T2TransmissionMode>,
324 pub other_frequency_flag: Option<bool>,
326 pub tfs_flag: Option<bool>,
328 pub cells: Vec<T2Cell>,
330}
331
332impl<'a> Parse<'a> for T2DeliverySystem {
333 type Error = crate::error::Error;
334 fn parse(sel: &'a [u8]) -> Result<Self> {
335 if sel.len() < T2_FIXED_PREFIX_LEN {
336 return Err(Error::BufferTooShort {
337 need: T2_FIXED_PREFIX_LEN,
338 have: sel.len(),
339 what: "T2_delivery_system body",
340 });
341 }
342 let plp_id = sel[0];
343 let t2_system_id = u16::from_be_bytes([sel[1], sel[2]]);
344 let mut pos = T2_FIXED_PREFIX_LEN;
345 let (
346 siso_miso,
347 bandwidth,
348 guard_interval,
349 transmission_mode,
350 other_frequency_flag,
351 tfs_flag,
352 ) = if sel.len() > T2_FIXED_PREFIX_LEN {
353 if sel.len() < T2_FIXED_PREFIX_LEN + T2_FLAGS_BLOCK_LEN {
354 return Err(Error::BufferTooShort {
355 need: T2_FIXED_PREFIX_LEN + T2_FLAGS_BLOCK_LEN,
356 have: sel.len(),
357 what: "T2_delivery_system body",
358 });
359 }
360 let b0 = sel[pos];
361 let b1 = sel[pos + 1];
362 pos += T2_FLAGS_BLOCK_LEN;
363 (
364 Some(T2SisoMiso::from_u8(b0 >> 6)),
365 Some(T2Bandwidth::from_u8((b0 >> 2) & 0x0F)),
366 Some(T2GuardInterval::from_u8(b1 >> 5)),
367 Some(T2TransmissionMode::from_u8((b1 >> 2) & 0x07)),
368 Some((b1 & 0x02) != 0),
369 Some((b1 & 0x01) != 0),
370 )
371 } else {
372 (None, None, None, None, None, None)
373 };
374 let cells = if siso_miso.is_some() {
375 let tfs = tfs_flag.unwrap();
376 let mut cells = Vec::new();
377 while pos < sel.len() {
378 if pos + 2 > sel.len() {
379 return Err(Error::BufferTooShort {
380 need: pos + 2,
381 have: sel.len(),
382 what: "T2_delivery_system body",
383 });
384 }
385 let cell_id = u16::from_be_bytes([sel[pos], sel[pos + 1]]);
386 pos += 2;
387 let centre_frequencies = if tfs {
388 if pos >= sel.len() {
389 return Err(Error::BufferTooShort {
390 need: pos + 1,
391 have: sel.len(),
392 what: "T2_delivery_system body",
393 });
394 }
395 let freq_loop_len = sel[pos] as usize;
396 pos += 1;
397 if freq_loop_len % 4 != 0 {
398 return Err(invalid(
399 "T2_delivery_system: frequency_loop_length not a multiple of 4",
400 ));
401 }
402 if pos + freq_loop_len > sel.len() {
403 return Err(Error::BufferTooShort {
404 need: pos + freq_loop_len,
405 have: sel.len(),
406 what: "T2_delivery_system body",
407 });
408 }
409 let end = pos + freq_loop_len;
410 let mut freqs = Vec::with_capacity(freq_loop_len / 4);
411 while pos < end {
412 freqs.push(u32::from_be_bytes([
413 sel[pos],
414 sel[pos + 1],
415 sel[pos + 2],
416 sel[pos + 3],
417 ]));
418 pos += 4;
419 }
420 freqs
421 } else {
422 if pos + 4 > sel.len() {
423 return Err(Error::BufferTooShort {
424 need: pos + 4,
425 have: sel.len(),
426 what: "T2_delivery_system body",
427 });
428 }
429 let freq =
430 u32::from_be_bytes([sel[pos], sel[pos + 1], sel[pos + 2], sel[pos + 3]]);
431 pos += 4;
432 vec![freq]
433 };
434 if pos >= sel.len() {
435 return Err(Error::BufferTooShort {
436 need: pos + 1,
437 have: sel.len(),
438 what: "T2_delivery_system body",
439 });
440 }
441 let subcell_loop_len = sel[pos] as usize;
442 pos += 1;
443 if subcell_loop_len % 5 != 0 {
444 return Err(invalid(
445 "T2_delivery_system: subcell_info_loop_length not a multiple of 5",
446 ));
447 }
448 if pos + subcell_loop_len > sel.len() {
449 return Err(Error::BufferTooShort {
450 need: pos + subcell_loop_len,
451 have: sel.len(),
452 what: "T2_delivery_system body",
453 });
454 }
455 let end = pos + subcell_loop_len;
456 let mut subcells = Vec::with_capacity(subcell_loop_len / 5);
457 while pos < end {
458 subcells.push(T2Subcell {
459 cell_id_extension: sel[pos],
460 transposer_frequency: u32::from_be_bytes([
461 sel[pos + 1],
462 sel[pos + 2],
463 sel[pos + 3],
464 sel[pos + 4],
465 ]),
466 });
467 pos += 5;
468 }
469 cells.push(T2Cell {
470 cell_id,
471 centre_frequencies,
472 subcells,
473 });
474 }
475 cells
476 } else {
477 Vec::new()
478 };
479 Ok(T2DeliverySystem {
480 plp_id,
481 t2_system_id,
482 siso_miso,
483 bandwidth,
484 guard_interval,
485 transmission_mode,
486 other_frequency_flag,
487 tfs_flag,
488 cells,
489 })
490 }
491}
492
493impl Serialize for T2DeliverySystem {
494 type Error = crate::error::Error;
495 fn serialized_len(&self) -> usize {
496 let mut len = T2_FIXED_PREFIX_LEN;
497 if self.siso_miso.is_some() {
498 len += T2_FLAGS_BLOCK_LEN;
499 let tfs = self.tfs_flag.unwrap_or(false);
500 for cell in &self.cells {
501 len += 2; if tfs {
503 len += 1 + cell.centre_frequencies.len() * 4;
504 } else {
505 len += 4;
506 }
507 len += 1 + cell.subcells.len() * 5;
508 }
509 }
510 len
511 }
512 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
513 let len = self.serialized_len();
514 if buf.len() < len {
515 return Err(Error::OutputBufferTooSmall {
516 need: len,
517 have: buf.len(),
518 });
519 }
520 buf[0] = self.plp_id;
521 buf[1..3].copy_from_slice(&self.t2_system_id.to_be_bytes());
522 let mut p = T2_FIXED_PREFIX_LEN;
523 if let (Some(sm), Some(bw), Some(gi), Some(tm), Some(off), Some(tfs)) = (
524 self.siso_miso,
525 self.bandwidth,
526 self.guard_interval,
527 self.transmission_mode,
528 self.other_frequency_flag,
529 self.tfs_flag,
530 ) {
531 buf[p] = (sm.to_u8() << 6) | ((bw.to_u8() & 0x0F) << 2) | 0x03;
532 buf[p + 1] = (gi.to_u8() << 5)
533 | ((tm.to_u8() & 0x07) << 2)
534 | (u8::from(off) << 1)
535 | u8::from(tfs);
536 p += T2_FLAGS_BLOCK_LEN;
537 for cell in &self.cells {
538 buf[p..p + 2].copy_from_slice(&cell.cell_id.to_be_bytes());
539 p += 2;
540 if tfs {
541 let freq_len = (cell.centre_frequencies.len() * 4) as u8;
542 buf[p] = freq_len;
543 p += 1;
544 for &freq in &cell.centre_frequencies {
545 buf[p..p + 4].copy_from_slice(&freq.to_be_bytes());
546 p += 4;
547 }
548 } else {
549 let freq = cell.centre_frequencies.first().copied().unwrap_or(0);
550 buf[p..p + 4].copy_from_slice(&freq.to_be_bytes());
551 p += 4;
552 }
553 let subcell_len = (cell.subcells.len() * 5) as u8;
554 buf[p] = subcell_len;
555 p += 1;
556 for sc in &cell.subcells {
557 buf[p] = sc.cell_id_extension;
558 buf[p + 1..p + 5].copy_from_slice(&sc.transposer_frequency.to_be_bytes());
559 p += 5;
560 }
561 }
562 }
563 Ok(len)
564 }
565}
566
567#[cfg(test)]
568mod tests {
569 use super::*;
570 use crate::descriptors::extension::test_support::*;
571 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
572
573 #[test]
574 fn t2_bandwidth_roundtrip() {
575 for b in 0..=0xFFu8 {
576 assert_eq!(T2Bandwidth::from_u8(b).to_u8(), b);
577 }
578 }
579
580 #[test]
581 fn t2_guard_interval_roundtrip() {
582 for b in 0..=0xFFu8 {
583 assert_eq!(T2GuardInterval::from_u8(b).to_u8(), b);
584 }
585 }
586
587 #[test]
588 fn t2_transmission_mode_roundtrip() {
589 for b in 0..=0xFFu8 {
590 assert_eq!(T2TransmissionMode::from_u8(b).to_u8(), b);
591 }
592 }
593
594 #[test]
595 fn parse_t2_minimal() {
596 let sel = [0x07, 0x12, 0x34];
598 let bytes = wrap(0x04, &sel);
599 let d = ExtensionDescriptor::parse(&bytes).unwrap();
600 match &d.body {
601 ExtensionBody::T2DeliverySystem(b) => {
602 assert_eq!(b.plp_id, 0x07);
603 assert_eq!(b.t2_system_id, 0x1234);
604 assert_eq!(b.siso_miso, None);
605 assert!(b.cells.is_empty());
606 }
607 other => panic!("expected T2DeliverySystem, got {other:?}"),
608 }
609 round_trip(&d);
610 }
611
612 #[test]
613 fn parse_t2_structured_flags_and_cells() {
614 let b0: u8 = ((0x04 & 0x0F) << 2) | 0x03; let b1: u8 = (0x06 << 5) | ((0x03 & 0x07) << 2) | (u8::from(false) << 1) | u8::from(true);
618 let cell1 = [0x12, 0x34, 0x00, 0x00];
620 let f1 = 0x01020304u32;
622 let f2 = 0x05060708u32;
623 let f3 = 0x090A0B0Cu32;
624 let sc1_id = 0x10u8;
625 let sc1_freq = 0x11121314u32;
626 let sc2_id = 0x20u8;
627 let sc2_freq = 0x21222324u32;
628 let mut cell2 = Vec::new();
629 cell2.extend_from_slice(&0x5678u16.to_be_bytes());
630 cell2.push(12);
631 cell2.extend_from_slice(&f1.to_be_bytes());
632 cell2.extend_from_slice(&f2.to_be_bytes());
633 cell2.extend_from_slice(&f3.to_be_bytes());
634 cell2.push(10);
635 cell2.push(sc1_id);
636 cell2.extend_from_slice(&sc1_freq.to_be_bytes());
637 cell2.push(sc2_id);
638 cell2.extend_from_slice(&sc2_freq.to_be_bytes());
639 let mut sel = vec![0x07, 0x12, 0x34, b0, b1];
640 sel.extend_from_slice(&cell1);
641 sel.extend_from_slice(&cell2);
642 let bytes = wrap(0x04, &sel);
643 let d = ExtensionDescriptor::parse(&bytes).unwrap();
644 match &d.body {
645 ExtensionBody::T2DeliverySystem(b) => {
646 assert_eq!(b.plp_id, 0x07);
647 assert_eq!(b.t2_system_id, 0x1234);
648 assert_eq!(b.siso_miso, Some(T2SisoMiso::Siso));
649 assert_eq!(b.bandwidth, Some(T2Bandwidth::Mhz10));
650 assert_eq!(b.guard_interval, Some(T2GuardInterval::G19_256));
651 assert_eq!(b.transmission_mode, Some(T2TransmissionMode::Mode1k));
652 assert_eq!(b.other_frequency_flag, Some(false));
653 assert_eq!(b.tfs_flag, Some(true));
654 assert_eq!(b.cells.len(), 2);
655 assert_eq!(b.cells[0].cell_id, 0x1234);
657 assert!(b.cells[0].centre_frequencies.is_empty());
658 assert!(b.cells[0].subcells.is_empty());
659 assert_eq!(b.cells[1].cell_id, 0x5678);
661 assert_eq!(b.cells[1].centre_frequencies, vec![f1, f2, f3]);
662 assert_eq!(b.cells[1].subcells.len(), 2);
663 assert_eq!(b.cells[1].subcells[0].cell_id_extension, sc1_id);
664 assert_eq!(b.cells[1].subcells[0].transposer_frequency, sc1_freq);
665 assert_eq!(b.cells[1].subcells[1].cell_id_extension, sc2_id);
666 assert_eq!(b.cells[1].subcells[1].transposer_frequency, sc2_freq);
667
668 assert_eq!(
670 b.cells[1].subcells[0].transposer_frequency_hz(),
671 u64::from(sc1_freq) * 10
672 );
673 assert_eq!(
674 b.cells[1].centre_frequencies_hz(),
675 vec![u64::from(f1) * 10, u64::from(f2) * 10, u64::from(f3) * 10]
676 );
677 }
678 other => panic!("expected T2DeliverySystem, got {other:?}"),
679 }
680 round_trip(&d);
681 }
682
683 #[test]
684 fn tsduck_t2_reference() {
685 let bytes = from_hex(
686 "7f240456789a13cd12340000678a0c075bcd1505e30a780fd22c320a1217ea6406fa0aa9fc59",
687 );
688 let d = ExtensionDescriptor::parse(&bytes).unwrap();
689 match &d.body {
690 ExtensionBody::T2DeliverySystem(b) => {
691 assert_eq!(b.plp_id, 0x56);
692 assert_eq!(b.t2_system_id, 0x789A);
693 assert_eq!(b.siso_miso, Some(T2SisoMiso::Siso));
694 assert_eq!(b.bandwidth, Some(T2Bandwidth::Mhz10));
695 assert_eq!(b.guard_interval, Some(T2GuardInterval::G19_256));
696 assert_eq!(b.transmission_mode, Some(T2TransmissionMode::Mode1k));
697 assert_eq!(b.other_frequency_flag, Some(false));
698 assert_eq!(b.tfs_flag, Some(true));
699 assert_eq!(b.cells.len(), 2);
700
701 assert_eq!(b.cells[0].cell_id, 0x1234);
702 assert!(b.cells[0].centre_frequencies.is_empty());
703 assert!(b.cells[0].subcells.is_empty());
704
705 assert_eq!(b.cells[1].cell_id, 0x678A);
706 assert_eq!(
707 b.cells[1].centre_frequencies,
708 vec![0x075BCD15, 0x05E30A78, 0x0FD22C32]
709 );
710 assert_eq!(b.cells[1].subcells.len(), 2);
711 assert_eq!(b.cells[1].subcells[0].cell_id_extension, 0x12);
712 assert_eq!(b.cells[1].subcells[0].transposer_frequency, 0x17EA6406);
713 assert_eq!(b.cells[1].subcells[1].cell_id_extension, 0xFA);
714 assert_eq!(b.cells[1].subcells[1].transposer_frequency, 0x0AA9FC59);
715 }
716 other => panic!("expected T2DeliverySystem, got {other:?}"),
717 }
718 let mut out = vec![0u8; d.serialized_len()];
719 let n = d.serialize_into(&mut out).unwrap();
720 assert_eq!(
721 out[..n],
722 bytes[..],
723 "byte-exact re-serialize for tsduck T2 reference"
724 );
725 }
726}