1use crate::error::{Error, Result};
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11pub const TAG: u8 = 0x5A;
13const HEADER_LEN: usize = 2;
14const BODY_LEN: u8 = 11;
15
16const BW_SHIFT: u8 = 5;
17const PRIORITY_MASK: u8 = 0b0001_0000;
18const TIME_SLICING_MASK: u8 = 0b0000_1000;
19const MPE_FEC_MASK: u8 = 0b0000_0100;
20const RESERVED_FU_MASK: u8 = 0b0000_0011;
21const BW_MASK: u8 = 0b1110_0000;
22
23const CONSTELLATION_SHIFT: u8 = 6;
24const HIERARCHY_SHIFT: u8 = 3;
25const CONSTELLATION_MASK: u8 = 0b1100_0000;
26const HIERARCHY_MASK: u8 = 0b0011_1000;
27const CODE_RATE_HP_MASK: u8 = 0b0000_0111;
28
29const CODE_RATE_LP_SHIFT: u8 = 5;
30const GUARD_INTERVAL_SHIFT: u8 = 3;
31const TRANSMISSION_MODE_SHIFT: u8 = 1;
32const CODE_RATE_LP_MASK: u8 = 0b1110_0000;
33const GUARD_INTERVAL_MASK: u8 = 0b0001_1000;
34const TRANSMISSION_MODE_MASK: u8 = 0b0000_0110;
35const OTHER_FREQ_FLAG_MASK: u8 = 0b0000_0001;
36
37const TRAILING_RESERVED: u32 = 0xFFFF_FFFF;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize))]
42pub enum Bandwidth {
43 Mhz8,
45 Mhz7,
47 Mhz6,
49 Mhz5,
51 Reserved(u8),
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57#[cfg_attr(feature = "serde", derive(serde::Serialize))]
58pub enum Constellation {
59 Qpsk,
61 Qam16,
63 Qam64,
65 Reserved(u8),
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize))]
72pub enum Hierarchy {
73 NonHierarchicalNative,
75 Alpha1Native,
77 Alpha2Native,
79 Alpha4Native,
81 NonHierarchicalInDepth,
83 Alpha1InDepth,
85 Alpha2InDepth,
87 Alpha4InDepth,
89 Reserved(u8),
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize))]
96pub enum CodeRate {
97 Rate1_2,
99 Rate2_3,
101 Rate3_4,
103 Rate5_6,
105 Rate7_8,
107 Reserved(u8),
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize))]
114pub enum GuardInterval {
115 G1_32,
117 G1_16,
119 G1_8,
121 G1_4,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize))]
128pub enum TransmissionMode {
129 Mode2k,
131 Mode8k,
133 Mode4k,
135 Reserved(u8),
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize))]
142pub struct TerrestrialDeliverySystemDescriptor {
143 pub centre_frequency_10hz: u32,
145 pub bandwidth: Bandwidth,
147 pub priority: bool,
149 pub time_slicing_used: bool,
151 pub mpe_fec_used: bool,
153 pub constellation: Constellation,
155 pub hierarchy: Hierarchy,
157 pub code_rate_hp: CodeRate,
159 pub code_rate_lp: CodeRate,
161 pub guard_interval: GuardInterval,
163 pub transmission_mode: TransmissionMode,
165 pub other_frequency_flag: bool,
167}
168
169fn parse_bandwidth(raw: u8) -> Bandwidth {
170 match raw {
171 0 => Bandwidth::Mhz8,
172 1 => Bandwidth::Mhz7,
173 2 => Bandwidth::Mhz6,
174 3 => Bandwidth::Mhz5,
175 other => Bandwidth::Reserved(other),
176 }
177}
178
179fn parse_constellation(raw: u8) -> Constellation {
180 match raw {
181 0 => Constellation::Qpsk,
182 1 => Constellation::Qam16,
183 2 => Constellation::Qam64,
184 other => Constellation::Reserved(other),
185 }
186}
187
188fn parse_hierarchy(raw: u8) -> Hierarchy {
189 match raw {
190 0 => Hierarchy::NonHierarchicalNative,
191 1 => Hierarchy::Alpha1Native,
192 2 => Hierarchy::Alpha2Native,
193 3 => Hierarchy::Alpha4Native,
194 4 => Hierarchy::NonHierarchicalInDepth,
195 5 => Hierarchy::Alpha1InDepth,
196 6 => Hierarchy::Alpha2InDepth,
197 7 => Hierarchy::Alpha4InDepth,
198 other => Hierarchy::Reserved(other),
199 }
200}
201
202fn parse_code_rate(raw: u8) -> CodeRate {
203 match raw {
204 0 => CodeRate::Rate1_2,
205 1 => CodeRate::Rate2_3,
206 2 => CodeRate::Rate3_4,
207 3 => CodeRate::Rate5_6,
208 4 => CodeRate::Rate7_8,
209 other => CodeRate::Reserved(other),
210 }
211}
212
213fn parse_guard_interval(raw: u8) -> GuardInterval {
214 match raw {
215 0 => GuardInterval::G1_32,
216 1 => GuardInterval::G1_16,
217 2 => GuardInterval::G1_8,
218 3 => GuardInterval::G1_4,
219 _ => GuardInterval::G1_32,
220 }
221}
222
223fn parse_transmission_mode(raw: u8) -> TransmissionMode {
224 match raw {
225 0 => TransmissionMode::Mode2k,
226 1 => TransmissionMode::Mode8k,
227 2 => TransmissionMode::Mode4k,
228 other => TransmissionMode::Reserved(other),
229 }
230}
231
232fn serialize_bandwidth(bw: Bandwidth) -> u8 {
233 match bw {
234 Bandwidth::Mhz8 => 0,
235 Bandwidth::Mhz7 => 1,
236 Bandwidth::Mhz6 => 2,
237 Bandwidth::Mhz5 => 3,
238 Bandwidth::Reserved(v) => v,
239 }
240}
241
242fn serialize_constellation(c: Constellation) -> u8 {
243 match c {
244 Constellation::Qpsk => 0,
245 Constellation::Qam16 => 1,
246 Constellation::Qam64 => 2,
247 Constellation::Reserved(v) => v,
248 }
249}
250
251fn serialize_hierarchy(h: Hierarchy) -> u8 {
252 match h {
253 Hierarchy::NonHierarchicalNative => 0,
254 Hierarchy::Alpha1Native => 1,
255 Hierarchy::Alpha2Native => 2,
256 Hierarchy::Alpha4Native => 3,
257 Hierarchy::NonHierarchicalInDepth => 4,
258 Hierarchy::Alpha1InDepth => 5,
259 Hierarchy::Alpha2InDepth => 6,
260 Hierarchy::Alpha4InDepth => 7,
261 Hierarchy::Reserved(v) => v,
262 }
263}
264
265fn serialize_code_rate(cr: CodeRate) -> u8 {
266 match cr {
267 CodeRate::Rate1_2 => 0,
268 CodeRate::Rate2_3 => 1,
269 CodeRate::Rate3_4 => 2,
270 CodeRate::Rate5_6 => 3,
271 CodeRate::Rate7_8 => 4,
272 CodeRate::Reserved(v) => v,
273 }
274}
275
276fn serialize_guard_interval(gi: GuardInterval) -> u8 {
277 match gi {
278 GuardInterval::G1_32 => 0,
279 GuardInterval::G1_16 => 1,
280 GuardInterval::G1_8 => 2,
281 GuardInterval::G1_4 => 3,
282 }
283}
284
285fn serialize_transmission_mode(tm: TransmissionMode) -> u8 {
286 match tm {
287 TransmissionMode::Mode2k => 0,
288 TransmissionMode::Mode8k => 1,
289 TransmissionMode::Mode4k => 2,
290 TransmissionMode::Reserved(v) => v,
291 }
292}
293
294impl<'a> Parse<'a> for TerrestrialDeliverySystemDescriptor {
295 type Error = crate::error::Error;
296 fn parse(bytes: &'a [u8]) -> Result<Self> {
297 if bytes.len() < HEADER_LEN + BODY_LEN as usize {
298 return Err(Error::BufferTooShort {
299 need: HEADER_LEN + BODY_LEN as usize,
300 have: bytes.len(),
301 what: "TerrestrialDeliverySystemDescriptor",
302 });
303 }
304 if bytes[0] != TAG {
305 return Err(Error::InvalidDescriptor {
306 tag: bytes[0],
307 reason: "unexpected tag for terrestrial_delivery_system_descriptor",
308 });
309 }
310 let length = bytes[1];
311 if length != BODY_LEN {
312 return Err(Error::InvalidDescriptor {
313 tag: TAG,
314 reason: "body length must equal 11",
315 });
316 }
317
318 let centre_frequency_10hz = u32::from_be_bytes(bytes[2..6].try_into().unwrap());
319
320 let byte6 = bytes[6];
321 let bw_raw = (byte6 & BW_MASK) >> BW_SHIFT;
322 let priority = (byte6 & PRIORITY_MASK) != 0;
323 let time_slicing_used = (byte6 & TIME_SLICING_MASK) == 0;
324 let mpe_fec_used = (byte6 & MPE_FEC_MASK) == 0;
325
326 let byte7 = bytes[7];
327 let constellation_raw = (byte7 & CONSTELLATION_MASK) >> CONSTELLATION_SHIFT;
328 let hierarchy_raw = (byte7 & HIERARCHY_MASK) >> HIERARCHY_SHIFT;
329 let code_rate_hp_raw = byte7 & CODE_RATE_HP_MASK;
330
331 let byte8 = bytes[8];
332 let code_rate_lp_raw = (byte8 & CODE_RATE_LP_MASK) >> CODE_RATE_LP_SHIFT;
333 let guard_interval_raw = (byte8 & GUARD_INTERVAL_MASK) >> GUARD_INTERVAL_SHIFT;
334 let transmission_mode_raw = (byte8 & TRANSMISSION_MODE_MASK) >> TRANSMISSION_MODE_SHIFT;
335 let other_frequency_flag = (byte8 & OTHER_FREQ_FLAG_MASK) != 0;
336
337 Ok(Self {
338 centre_frequency_10hz,
339 bandwidth: parse_bandwidth(bw_raw),
340 priority,
341 time_slicing_used,
342 mpe_fec_used,
343 constellation: parse_constellation(constellation_raw),
344 hierarchy: parse_hierarchy(hierarchy_raw),
345 code_rate_hp: parse_code_rate(code_rate_hp_raw),
346 code_rate_lp: parse_code_rate(code_rate_lp_raw),
347 guard_interval: parse_guard_interval(guard_interval_raw),
348 transmission_mode: parse_transmission_mode(transmission_mode_raw),
349 other_frequency_flag,
350 })
351 }
352}
353
354impl Serialize for TerrestrialDeliverySystemDescriptor {
355 type Error = crate::error::Error;
356 fn serialized_len(&self) -> usize {
357 HEADER_LEN + BODY_LEN as usize
358 }
359
360 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
361 let len = self.serialized_len();
362 if buf.len() < len {
363 return Err(Error::OutputBufferTooSmall {
364 need: len,
365 have: buf.len(),
366 });
367 }
368 buf[0] = TAG;
369 buf[1] = BODY_LEN;
370
371 buf[2..6].copy_from_slice(&self.centre_frequency_10hz.to_be_bytes());
372
373 let byte6 = (serialize_bandwidth(self.bandwidth) << BW_SHIFT)
374 | if self.priority { PRIORITY_MASK } else { 0 }
375 | if !self.time_slicing_used {
376 TIME_SLICING_MASK
377 } else {
378 0
379 }
380 | if !self.mpe_fec_used { MPE_FEC_MASK } else { 0 }
381 | RESERVED_FU_MASK;
382 buf[6] = byte6;
383
384 let byte7 = (serialize_constellation(self.constellation) << CONSTELLATION_SHIFT)
385 | (serialize_hierarchy(self.hierarchy) << HIERARCHY_SHIFT)
386 | serialize_code_rate(self.code_rate_hp);
387 buf[7] = byte7;
388
389 let byte8 = (serialize_code_rate(self.code_rate_lp) << CODE_RATE_LP_SHIFT)
390 | (serialize_guard_interval(self.guard_interval) << GUARD_INTERVAL_SHIFT)
391 | (serialize_transmission_mode(self.transmission_mode) << TRANSMISSION_MODE_SHIFT)
392 | if self.other_frequency_flag {
393 OTHER_FREQ_FLAG_MASK
394 } else {
395 0
396 };
397 buf[8] = byte8;
398
399 buf[9..13].copy_from_slice(&TRAILING_RESERVED.to_be_bytes());
400
401 Ok(len)
402 }
403}
404
405impl<'a> Descriptor<'a> for TerrestrialDeliverySystemDescriptor {
406 const TAG: u8 = TAG;
407
408 fn descriptor_length(&self) -> u8 {
409 BODY_LEN
410 }
411}
412
413impl<'a> crate::traits::DescriptorDef<'a> for TerrestrialDeliverySystemDescriptor {
414 const TAG: u8 = TAG;
415 const NAME: &'static str = "TERRESTRIAL_DELIVERY_SYSTEM";
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421
422 #[test]
423 fn parse_extracts_centre_frequency_10hz() {
424 let raw = [
425 TAG, BODY_LEN, 0x04, 0xA8, 0x58, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
426 ];
427 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
428 assert_eq!(d.centre_frequency_10hz, 0x04A858F0);
429 }
430
431 #[test]
432 fn parse_extracts_bandwidth_8mhz() {
433 let raw = [
434 TAG, BODY_LEN, 0x04, 0xA8, 0x58, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
435 ];
436 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
437 assert_eq!(d.bandwidth, Bandwidth::Mhz8);
438 }
439
440 #[test]
441 fn parse_extracts_bandwidth_7mhz() {
442 let raw = [
443 TAG,
444 BODY_LEN,
445 0x04,
446 0xA8,
447 0x58,
448 0xF0,
449 (0b001 << BW_SHIFT),
450 0x00,
451 0x00,
452 0x00,
453 0xFF,
454 0xFF,
455 0xFF,
456 0xFF,
457 ];
458 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
459 assert_eq!(d.bandwidth, Bandwidth::Mhz7);
460 }
461
462 #[test]
463 fn parse_extracts_constellation_qam64() {
464 let raw = [
465 TAG,
466 BODY_LEN,
467 0x04,
468 0xA8,
469 0x58,
470 0xF0,
471 0x00,
472 (0b10 << CONSTELLATION_SHIFT),
473 0x00,
474 0xFF,
475 0xFF,
476 0xFF,
477 0xFF,
478 ];
479 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
480 assert_eq!(d.constellation, Constellation::Qam64);
481 }
482
483 #[test]
484 fn parse_extracts_code_rate_hp_and_lp() {
485 let raw = [
486 TAG,
487 BODY_LEN,
488 0x04,
489 0xA8,
490 0x58,
491 0xF0,
492 0x00,
493 0b10 << CONSTELLATION_SHIFT,
494 0b100 << CODE_RATE_LP_SHIFT,
495 0xFF,
496 0xFF,
497 0xFF,
498 0xFF,
499 ];
500 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
501 assert_eq!(d.code_rate_hp, CodeRate::Rate1_2);
502 assert_eq!(d.code_rate_lp, CodeRate::Rate7_8);
503 }
504
505 #[test]
506 fn parse_extracts_guard_interval_1_4() {
507 let raw = [
508 TAG,
509 BODY_LEN,
510 0x04,
511 0xA8,
512 0x58,
513 0xF0,
514 0x00,
515 0x00,
516 0b11 << GUARD_INTERVAL_SHIFT,
517 0xFF,
518 0xFF,
519 0xFF,
520 0xFF,
521 ];
522 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
523 assert_eq!(d.guard_interval, GuardInterval::G1_4);
524 }
525
526 #[test]
527 fn parse_extracts_transmission_mode_8k() {
528 let raw = [
529 TAG,
530 BODY_LEN,
531 0x04,
532 0xA8,
533 0x58,
534 0xF0,
535 0x00,
536 0x00,
537 0b01 << TRANSMISSION_MODE_SHIFT,
538 0xFF,
539 0xFF,
540 0xFF,
541 0xFF,
542 ];
543 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
544 assert_eq!(d.transmission_mode, TransmissionMode::Mode8k);
545 }
546
547 #[test]
548 fn parse_extracts_other_frequency_flag() {
549 let raw = [
550 TAG,
551 BODY_LEN,
552 0x04,
553 0xA8,
554 0x58,
555 0xF0,
556 0x00,
557 0x00,
558 OTHER_FREQ_FLAG_MASK,
559 0xFF,
560 0xFF,
561 0xFF,
562 0xFF,
563 ];
564 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
565 assert!(d.other_frequency_flag);
566 }
567
568 #[test]
569 fn parse_preserves_reserved_bandwidth_in_reserve_variant() {
570 let raw = [
571 TAG,
572 BODY_LEN,
573 0x04,
574 0xA8,
575 0x58,
576 0xF0,
577 (0b111 << BW_SHIFT),
578 0x00,
579 0x00,
580 0x00,
581 0xFF,
582 0xFF,
583 0xFF,
584 0xFF,
585 ];
586 let d = TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap();
587 assert_eq!(d.bandwidth, Bandwidth::Reserved(0b111));
588 }
589
590 #[test]
591 fn parse_rejects_wrong_tag() {
592 let raw = [
593 0x5B, BODY_LEN, 0x04, 0xA8, 0x58, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
594 ];
595 assert!(matches!(
596 TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap_err(),
597 Error::InvalidDescriptor { tag: 0x5B, .. }
598 ));
599 }
600
601 #[test]
602 fn parse_rejects_wrong_length() {
603 let raw = [
604 TAG, 12, 0x04, 0xA8, 0x58, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
605 ];
606 assert!(matches!(
607 TerrestrialDeliverySystemDescriptor::parse(&raw).unwrap_err(),
608 Error::InvalidDescriptor { tag: TAG, .. }
609 ));
610 }
611
612 #[test]
613 fn serialize_round_trip_full_set() {
614 let d = TerrestrialDeliverySystemDescriptor {
615 centre_frequency_10hz: 0x04A858F0,
616 bandwidth: Bandwidth::Mhz8,
617 priority: true,
618 time_slicing_used: false,
619 mpe_fec_used: true,
620 constellation: Constellation::Qam64,
621 hierarchy: Hierarchy::Alpha2Native,
622 code_rate_hp: CodeRate::Rate3_4,
623 code_rate_lp: CodeRate::Rate7_8,
624 guard_interval: GuardInterval::G1_4,
625 transmission_mode: TransmissionMode::Mode8k,
626 other_frequency_flag: true,
627 };
628 let mut buf = vec![0u8; d.serialized_len()];
629 d.serialize_into(&mut buf).unwrap();
630 let parsed = TerrestrialDeliverySystemDescriptor::parse(&buf).unwrap();
631 assert_eq!(parsed, d);
632 }
633}