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