Skip to main content

dvb_si/descriptors/
cable_delivery_system.rs

1//! Cable Delivery System Descriptor — ETSI EN 300 468 §6.2.13.1 (tag 0x44).
2//!
3//! Carried inside NIT transport_stream_loop second descriptor loop for
4//! DVB-C transponders.
5
6use crate::error::{Error, Result};
7use crate::traits::Descriptor;
8use dvb_common::{Parse, Serialize};
9
10/// Descriptor tag for cable_delivery_system_descriptor.
11pub const TAG: u8 = 0x44;
12const HEADER_LEN: usize = 2;
13const BODY_LEN: u8 = 11;
14
15/// Reserved future use bits (top 12 bits of bytes 6+7 in the descriptor body).
16const RESERVED_FU_MASK: u16 = 0xFFF0;
17
18/// FEC outer coding scheme.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub enum FecOuter {
22    /// Not defined.
23    NotDefined,
24    /// No outer FEC coding.
25    NoOuterFec,
26    /// Reed-Solomon (204, 188).
27    ReedSolomon204_188,
28    /// Reserved / future use.
29    Reserved(u8),
30}
31
32/// Modulation scheme.
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub enum Modulation {
36    /// Not defined.
37    NotDefined,
38    /// 16-QAM.
39    Qam16,
40    /// 32-QAM.
41    Qam32,
42    /// 64-QAM.
43    Qam64,
44    /// 128-QAM.
45    Qam128,
46    /// 256-QAM.
47    Qam256,
48    /// Reserved / future use.
49    Reserved(u8),
50}
51
52/// FEC inner convolutional code rate.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55pub enum FecInner {
56    /// Not defined.
57    NotDefined,
58    /// Code rate 1/2.
59    Rate1_2,
60    /// Code rate 2/3.
61    Rate2_3,
62    /// Code rate 3/4.
63    Rate3_4,
64    /// Code rate 5/6.
65    Rate5_6,
66    /// Code rate 7/8.
67    Rate7_8,
68    /// Code rate 8/9.
69    Rate8_9,
70    /// Code rate 3/5.
71    Rate3_5,
72    /// Code rate 4/5.
73    Rate4_5,
74    /// Code rate 9/10.
75    Rate9_10,
76    /// No convolutional coding.
77    NoConvCoding,
78    /// Reserved / future use.
79    Reserved(u8),
80}
81
82/// Cable Delivery System Descriptor.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize))]
85pub struct CableDeliverySystemDescriptor {
86    /// 32-bit BCD frequency in 100 kHz (e.g. 0x03460000 = 346.0000 MHz).
87    pub frequency_bcd: u32,
88    /// FEC outer coding scheme.
89    pub fec_outer: FecOuter,
90    /// Modulation scheme.
91    pub modulation: Modulation,
92    /// 28-bit BCD symbol rate in Msym/s (value stored in low 28 bits of u32).
93    pub symbol_rate_bcd: u32,
94    /// FEC inner code rate.
95    pub fec_inner: FecInner,
96}
97
98impl CableDeliverySystemDescriptor {
99    /// Decode the 32-bit BCD `frequency` to Hz (100 Hz field resolution).
100    /// `None` if the BCD nibbles are out of range.
101    ///
102    /// e.g. `0x0346_0000` → `346_000_000` Hz (346.0000 MHz).
103    #[must_use]
104    pub fn frequency_hz(&self) -> Option<u64> {
105        dvb_common::bcd::bcd_to_decimal(u64::from(self.frequency_bcd), 8).map(|v| v * 100)
106    }
107
108    /// Set `frequency` from Hz, encoding to the 8-digit BCD field at the field's
109    /// 100 Hz resolution (finer precision is truncated).
110    ///
111    /// # Errors
112    /// [`ValueOutOfRange`](crate::Error::ValueOutOfRange) on overflow of
113    /// the 8-digit BCD field.
114    pub fn set_frequency_hz(&mut self, hz: u64) -> crate::Result<()> {
115        self.frequency_bcd =
116            super::encode_bcd_field(hz / 100, 8, "CableDeliverySystemDescriptor::frequency")?
117                as u32;
118        Ok(())
119    }
120
121    /// Decode the 28-bit BCD `symbol_rate` to symbols/second (100 sym/s
122    /// resolution). `None` if the BCD nibbles are out of range.
123    #[must_use]
124    pub fn symbol_rate_sps(&self) -> Option<u64> {
125        dvb_common::bcd::bcd_to_decimal(u64::from(self.symbol_rate_bcd), 7).map(|v| v * 100)
126    }
127
128    /// Set `symbol_rate` from symbols/second (100 sym/s field resolution).
129    ///
130    /// # Errors
131    /// [`ValueOutOfRange`](crate::Error::ValueOutOfRange) on overflow of
132    /// the 7-digit BCD field.
133    pub fn set_symbol_rate_sps(&mut self, sps: u64) -> crate::Result<()> {
134        self.symbol_rate_bcd =
135            super::encode_bcd_field(sps / 100, 7, "CableDeliverySystemDescriptor::symbol_rate")?
136                as u32;
137        Ok(())
138    }
139}
140
141fn parse_fec_outer(raw: u8) -> FecOuter {
142    match raw {
143        0x00 => FecOuter::NotDefined,
144        0x01 => FecOuter::NoOuterFec,
145        0x02 => FecOuter::ReedSolomon204_188,
146        other => FecOuter::Reserved(other),
147    }
148}
149
150fn parse_modulation(raw: u8) -> Modulation {
151    match raw {
152        0x00 => Modulation::NotDefined,
153        0x01 => Modulation::Qam16,
154        0x02 => Modulation::Qam32,
155        0x03 => Modulation::Qam64,
156        0x04 => Modulation::Qam128,
157        0x05 => Modulation::Qam256,
158        other => Modulation::Reserved(other),
159    }
160}
161
162fn parse_fec_inner(raw: u8) -> FecInner {
163    match raw {
164        0x00 => FecInner::NotDefined,
165        0x01 => FecInner::Rate1_2,
166        0x02 => FecInner::Rate2_3,
167        0x03 => FecInner::Rate3_4,
168        0x04 => FecInner::Rate5_6,
169        0x05 => FecInner::Rate7_8,
170        0x06 => FecInner::Rate8_9,
171        0x07 => FecInner::Rate3_5,
172        0x08 => FecInner::Rate4_5,
173        0x09 => FecInner::Rate9_10,
174        0x0F => FecInner::NoConvCoding,
175        other => FecInner::Reserved(other),
176    }
177}
178
179fn serialize_fec_outer(fec: FecOuter) -> u8 {
180    match fec {
181        FecOuter::NotDefined => 0x00,
182        FecOuter::NoOuterFec => 0x01,
183        FecOuter::ReedSolomon204_188 => 0x02,
184        FecOuter::Reserved(v) => v,
185    }
186}
187
188fn serialize_modulation(m: Modulation) -> u8 {
189    match m {
190        Modulation::NotDefined => 0x00,
191        Modulation::Qam16 => 0x01,
192        Modulation::Qam32 => 0x02,
193        Modulation::Qam64 => 0x03,
194        Modulation::Qam128 => 0x04,
195        Modulation::Qam256 => 0x05,
196        Modulation::Reserved(v) => v,
197    }
198}
199
200fn serialize_fec_inner(fec: FecInner) -> u8 {
201    match fec {
202        FecInner::NotDefined => 0x00,
203        FecInner::Rate1_2 => 0x01,
204        FecInner::Rate2_3 => 0x02,
205        FecInner::Rate3_4 => 0x03,
206        FecInner::Rate5_6 => 0x04,
207        FecInner::Rate7_8 => 0x05,
208        FecInner::Rate8_9 => 0x06,
209        FecInner::Rate3_5 => 0x07,
210        FecInner::Rate4_5 => 0x08,
211        FecInner::Rate9_10 => 0x09,
212        FecInner::NoConvCoding => 0x0F,
213        FecInner::Reserved(v) => v,
214    }
215}
216
217impl<'a> Parse<'a> for CableDeliverySystemDescriptor {
218    type Error = crate::error::Error;
219    fn parse(bytes: &'a [u8]) -> Result<Self> {
220        if bytes.len() < HEADER_LEN + BODY_LEN as usize {
221            return Err(Error::BufferTooShort {
222                need: HEADER_LEN + BODY_LEN as usize,
223                have: bytes.len(),
224                what: "CableDeliverySystemDescriptor",
225            });
226        }
227        if bytes[0] != TAG {
228            return Err(Error::InvalidDescriptor {
229                tag: bytes[0],
230                reason: "unexpected tag for cable_delivery_system_descriptor",
231            });
232        }
233        let length = bytes[1];
234        if length != BODY_LEN {
235            return Err(Error::InvalidDescriptor {
236                tag: TAG,
237                reason: "body length must equal 11",
238            });
239        }
240
241        let frequency_bcd = u32::from_be_bytes(bytes[2..6].try_into().unwrap());
242
243        let bytes_6_7 = u16::from_be_bytes([bytes[6], bytes[7]]);
244        let fec_outer_raw = (bytes_6_7 & !RESERVED_FU_MASK) as u8;
245
246        let modulation_byte = bytes[8];
247
248        let spec_value = u32::from_be_bytes([0, bytes[9], bytes[10], bytes[11]]);
249        let symbol_rate_bcd = (spec_value << 4) | ((bytes[12] >> 4) & 0x0F) as u32;
250
251        let fec_inner_raw = bytes[12] & 0x0F;
252
253        Ok(Self {
254            frequency_bcd,
255            fec_outer: parse_fec_outer(fec_outer_raw),
256            modulation: parse_modulation(modulation_byte),
257            symbol_rate_bcd,
258            fec_inner: parse_fec_inner(fec_inner_raw),
259        })
260    }
261}
262
263impl Serialize for CableDeliverySystemDescriptor {
264    type Error = crate::error::Error;
265    fn serialized_len(&self) -> usize {
266        HEADER_LEN + BODY_LEN as usize
267    }
268
269    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
270        let len = self.serialized_len();
271        if buf.len() < len {
272            return Err(Error::OutputBufferTooSmall {
273                need: len,
274                have: buf.len(),
275            });
276        }
277
278        buf[0] = TAG;
279        buf[1] = BODY_LEN;
280
281        buf[2..6].copy_from_slice(&self.frequency_bcd.to_be_bytes());
282
283        let reserved_fu = RESERVED_FU_MASK;
284        let fec_outer_byte = reserved_fu | serialize_fec_outer(self.fec_outer) as u16;
285        let [fu_hi, fec_lo] = fec_outer_byte.to_be_bytes();
286        buf[6] = fu_hi;
287        buf[7] = fec_lo;
288
289        buf[8] = serialize_modulation(self.modulation);
290
291        let spec_value = self.symbol_rate_bcd >> 4;
292        buf[9] = (spec_value >> 16) as u8;
293        buf[10] = (spec_value >> 8) as u8;
294        buf[11] = spec_value as u8;
295
296        buf[12] = ((self.symbol_rate_bcd & 0x0F) as u8) << 4 | serialize_fec_inner(self.fec_inner);
297
298        Ok(len)
299    }
300}
301
302impl<'a> Descriptor<'a> for CableDeliverySystemDescriptor {
303    const TAG: u8 = TAG;
304
305    fn descriptor_length(&self) -> u8 {
306        BODY_LEN
307    }
308}
309
310impl<'a> crate::traits::DescriptorDef<'a> for CableDeliverySystemDescriptor {
311    const TAG: u8 = TAG;
312    const NAME: &'static str = "CABLE_DELIVERY_SYSTEM";
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318
319    #[test]
320    fn parse_extracts_frequency_bcd() {
321        let raw: [u8; 13] = [
322            TAG, BODY_LEN, 0x03, 0x46, 0x00, 0x00, 0xFF, 0xF1, 0x05, 0x00, 0x00, 0x00, 0x03,
323        ];
324        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
325        assert_eq!(d.frequency_bcd, 0x03460000);
326    }
327
328    #[test]
329    fn parse_extracts_modulation_qam256() {
330        let raw: [u8; 13] = [
331            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00,
332        ];
333        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
334        assert_eq!(d.modulation, Modulation::Qam256);
335    }
336
337    #[test]
338    fn parse_extracts_fec_outer_reed_solomon() {
339        let raw: [u8; 13] = [
340            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00,
341        ];
342        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
343        assert_eq!(d.fec_outer, FecOuter::ReedSolomon204_188);
344    }
345
346    #[test]
347    fn parse_extracts_fec_inner_rate_3_4() {
348        let raw: [u8; 13] = [
349            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03,
350        ];
351        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
352        assert_eq!(d.fec_inner, FecInner::Rate3_4);
353    }
354
355    #[test]
356    fn parse_extracts_symbol_rate_bcd() {
357        let raw: [u8; 13] = [
358            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x06, 0x87, 0x50, 0x00,
359        ];
360        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
361        assert_eq!(d.symbol_rate_bcd, 0x0687500);
362    }
363
364    #[test]
365    fn parse_preserves_reserved_modulation_in_reserved_variant() {
366        let raw: [u8; 13] = [
367            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x0A, 0x00, 0x00, 0x00, 0x00,
368        ];
369        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
370        assert_eq!(d.modulation, Modulation::Reserved(0x0A));
371    }
372
373    #[test]
374    fn parse_rejects_wrong_tag() {
375        let raw: [u8; 13] = [
376            0x5B, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
377        ];
378        assert!(matches!(
379            CableDeliverySystemDescriptor::parse(&raw).unwrap_err(),
380            Error::InvalidDescriptor { tag: 0x5B, .. }
381        ));
382    }
383
384    #[test]
385    fn parse_rejects_wrong_length() {
386        let raw: [u8; 13] = [
387            TAG, 12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
388        ];
389        assert!(matches!(
390            CableDeliverySystemDescriptor::parse(&raw).unwrap_err(),
391            Error::InvalidDescriptor { tag: TAG, .. }
392        ));
393    }
394
395    #[test]
396    fn serialize_round_trip() {
397        let d = CableDeliverySystemDescriptor {
398            frequency_bcd: 0x03460000,
399            fec_outer: FecOuter::ReedSolomon204_188,
400            modulation: Modulation::Qam256,
401            symbol_rate_bcd: 0x0687500,
402            fec_inner: FecInner::Rate3_4,
403        };
404        let mut buf = vec![0u8; d.serialized_len()];
405        d.serialize_into(&mut buf).unwrap();
406        let parsed = CableDeliverySystemDescriptor::parse(&buf).unwrap();
407        assert_eq!(parsed, d);
408    }
409
410    #[test]
411    fn enum_round_trip_covers_every_defined_variant() {
412        for fec_outer in [
413            FecOuter::NotDefined,
414            FecOuter::NoOuterFec,
415            FecOuter::ReedSolomon204_188,
416        ] {
417            let v = serialize_fec_outer(fec_outer);
418            assert_eq!(parse_fec_outer(v), fec_outer);
419        }
420
421        for mod_ in [
422            Modulation::NotDefined,
423            Modulation::Qam16,
424            Modulation::Qam32,
425            Modulation::Qam64,
426            Modulation::Qam128,
427            Modulation::Qam256,
428        ] {
429            let v = serialize_modulation(mod_);
430            assert_eq!(parse_modulation(v), mod_);
431        }
432
433        for fec_inner in [
434            FecInner::NotDefined,
435            FecInner::Rate1_2,
436            FecInner::Rate2_3,
437            FecInner::Rate3_4,
438            FecInner::Rate5_6,
439            FecInner::Rate7_8,
440            FecInner::Rate8_9,
441            FecInner::Rate3_5,
442            FecInner::Rate4_5,
443            FecInner::Rate9_10,
444            FecInner::NoConvCoding,
445        ] {
446            let v = serialize_fec_inner(fec_inner);
447            assert_eq!(parse_fec_inner(v), fec_inner);
448        }
449    }
450}