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, serde::Deserialize))]
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, serde::Deserialize))]
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, serde::Deserialize))]
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, serde::Deserialize))]
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
98fn parse_fec_outer(raw: u8) -> FecOuter {
99    match raw {
100        0x00 => FecOuter::NotDefined,
101        0x01 => FecOuter::NoOuterFec,
102        0x02 => FecOuter::ReedSolomon204_188,
103        other => FecOuter::Reserved(other),
104    }
105}
106
107fn parse_modulation(raw: u8) -> Modulation {
108    match raw {
109        0x00 => Modulation::NotDefined,
110        0x01 => Modulation::Qam16,
111        0x02 => Modulation::Qam32,
112        0x03 => Modulation::Qam64,
113        0x04 => Modulation::Qam128,
114        0x05 => Modulation::Qam256,
115        other => Modulation::Reserved(other),
116    }
117}
118
119fn parse_fec_inner(raw: u8) -> FecInner {
120    match raw {
121        0x00 => FecInner::NotDefined,
122        0x01 => FecInner::Rate1_2,
123        0x02 => FecInner::Rate2_3,
124        0x03 => FecInner::Rate3_4,
125        0x04 => FecInner::Rate5_6,
126        0x05 => FecInner::Rate7_8,
127        0x06 => FecInner::Rate8_9,
128        0x07 => FecInner::Rate3_5,
129        0x08 => FecInner::Rate4_5,
130        0x09 => FecInner::Rate9_10,
131        0x0F => FecInner::NoConvCoding,
132        other => FecInner::Reserved(other),
133    }
134}
135
136fn serialize_fec_outer(fec: FecOuter) -> u8 {
137    match fec {
138        FecOuter::NotDefined => 0x00,
139        FecOuter::NoOuterFec => 0x01,
140        FecOuter::ReedSolomon204_188 => 0x02,
141        FecOuter::Reserved(v) => v,
142    }
143}
144
145fn serialize_modulation(m: Modulation) -> u8 {
146    match m {
147        Modulation::NotDefined => 0x00,
148        Modulation::Qam16 => 0x01,
149        Modulation::Qam32 => 0x02,
150        Modulation::Qam64 => 0x03,
151        Modulation::Qam128 => 0x04,
152        Modulation::Qam256 => 0x05,
153        Modulation::Reserved(v) => v,
154    }
155}
156
157fn serialize_fec_inner(fec: FecInner) -> u8 {
158    match fec {
159        FecInner::NotDefined => 0x00,
160        FecInner::Rate1_2 => 0x01,
161        FecInner::Rate2_3 => 0x02,
162        FecInner::Rate3_4 => 0x03,
163        FecInner::Rate5_6 => 0x04,
164        FecInner::Rate7_8 => 0x05,
165        FecInner::Rate8_9 => 0x06,
166        FecInner::Rate3_5 => 0x07,
167        FecInner::Rate4_5 => 0x08,
168        FecInner::Rate9_10 => 0x09,
169        FecInner::NoConvCoding => 0x0F,
170        FecInner::Reserved(v) => v,
171    }
172}
173
174impl<'a> Parse<'a> for CableDeliverySystemDescriptor {
175    type Error = crate::error::Error;
176    fn parse(bytes: &'a [u8]) -> Result<Self> {
177        if bytes.len() < HEADER_LEN + BODY_LEN as usize {
178            return Err(Error::BufferTooShort {
179                need: HEADER_LEN + BODY_LEN as usize,
180                have: bytes.len(),
181                what: "CableDeliverySystemDescriptor",
182            });
183        }
184        if bytes[0] != TAG {
185            return Err(Error::InvalidDescriptor {
186                tag: bytes[0],
187                reason: "unexpected tag for cable_delivery_system_descriptor",
188            });
189        }
190        let length = bytes[1];
191        if length != BODY_LEN {
192            return Err(Error::InvalidDescriptor {
193                tag: TAG,
194                reason: "body length must equal 11",
195            });
196        }
197
198        let frequency_bcd = u32::from_be_bytes(bytes[2..6].try_into().unwrap());
199
200        let bytes_6_7 = u16::from_be_bytes([bytes[6], bytes[7]]);
201        let fec_outer_raw = (bytes_6_7 & !RESERVED_FU_MASK) as u8;
202
203        let modulation_byte = bytes[8];
204
205        let spec_value = u32::from_be_bytes([0, bytes[9], bytes[10], bytes[11]]);
206        let symbol_rate_bcd = (spec_value << 4) | ((bytes[12] >> 4) & 0x0F) as u32;
207
208        let fec_inner_raw = bytes[12] & 0x0F;
209
210        Ok(Self {
211            frequency_bcd,
212            fec_outer: parse_fec_outer(fec_outer_raw),
213            modulation: parse_modulation(modulation_byte),
214            symbol_rate_bcd,
215            fec_inner: parse_fec_inner(fec_inner_raw),
216        })
217    }
218}
219
220impl Serialize for CableDeliverySystemDescriptor {
221    type Error = crate::error::Error;
222    fn serialized_len(&self) -> usize {
223        HEADER_LEN + BODY_LEN as usize
224    }
225
226    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
227        let len = self.serialized_len();
228        if buf.len() < len {
229            return Err(Error::OutputBufferTooSmall {
230                need: len,
231                have: buf.len(),
232            });
233        }
234
235        buf[0] = TAG;
236        buf[1] = BODY_LEN;
237
238        buf[2..6].copy_from_slice(&self.frequency_bcd.to_be_bytes());
239
240        let reserved_fu = RESERVED_FU_MASK;
241        let fec_outer_byte = reserved_fu | serialize_fec_outer(self.fec_outer) as u16;
242        let [fu_hi, fec_lo] = fec_outer_byte.to_be_bytes();
243        buf[6] = fu_hi;
244        buf[7] = fec_lo;
245
246        buf[8] = serialize_modulation(self.modulation);
247
248        let spec_value = self.symbol_rate_bcd >> 4;
249        buf[9] = (spec_value >> 16) as u8;
250        buf[10] = (spec_value >> 8) as u8;
251        buf[11] = spec_value as u8;
252
253        buf[12] = ((self.symbol_rate_bcd & 0x0F) as u8) << 4 | serialize_fec_inner(self.fec_inner);
254
255        Ok(len)
256    }
257}
258
259impl<'a> Descriptor<'a> for CableDeliverySystemDescriptor {
260    const TAG: u8 = TAG;
261
262    fn descriptor_length(&self) -> u8 {
263        BODY_LEN
264    }
265}
266
267impl<'a> crate::traits::DescriptorDef<'a> for CableDeliverySystemDescriptor {
268    const TAG: u8 = TAG;
269    const NAME: &'static str = "CABLE_DELIVERY_SYSTEM";
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275
276    #[test]
277    fn parse_extracts_frequency_bcd() {
278        let raw: [u8; 13] = [
279            TAG, BODY_LEN, 0x03, 0x46, 0x00, 0x00, 0xFF, 0xF1, 0x05, 0x00, 0x00, 0x00, 0x03,
280        ];
281        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
282        assert_eq!(d.frequency_bcd, 0x03460000);
283    }
284
285    #[test]
286    fn parse_extracts_modulation_qam256() {
287        let raw: [u8; 13] = [
288            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x05, 0x00, 0x00, 0x00, 0x00,
289        ];
290        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
291        assert_eq!(d.modulation, Modulation::Qam256);
292    }
293
294    #[test]
295    fn parse_extracts_fec_outer_reed_solomon() {
296        let raw: [u8; 13] = [
297            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00,
298        ];
299        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
300        assert_eq!(d.fec_outer, FecOuter::ReedSolomon204_188);
301    }
302
303    #[test]
304    fn parse_extracts_fec_inner_rate_3_4() {
305        let raw: [u8; 13] = [
306            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x03,
307        ];
308        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
309        assert_eq!(d.fec_inner, FecInner::Rate3_4);
310    }
311
312    #[test]
313    fn parse_extracts_symbol_rate_bcd() {
314        let raw: [u8; 13] = [
315            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x06, 0x87, 0x50, 0x00,
316        ];
317        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
318        assert_eq!(d.symbol_rate_bcd, 0x0687500);
319    }
320
321    #[test]
322    fn parse_preserves_reserved_modulation_in_reserved_variant() {
323        let raw: [u8; 13] = [
324            TAG, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x0A, 0x00, 0x00, 0x00, 0x00,
325        ];
326        let d = CableDeliverySystemDescriptor::parse(&raw).unwrap();
327        assert_eq!(d.modulation, Modulation::Reserved(0x0A));
328    }
329
330    #[test]
331    fn parse_rejects_wrong_tag() {
332        let raw: [u8; 13] = [
333            0x5B, BODY_LEN, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
334        ];
335        assert!(matches!(
336            CableDeliverySystemDescriptor::parse(&raw).unwrap_err(),
337            Error::InvalidDescriptor { tag: 0x5B, .. }
338        ));
339    }
340
341    #[test]
342    fn parse_rejects_wrong_length() {
343        let raw: [u8; 13] = [
344            TAG, 12, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00,
345        ];
346        assert!(matches!(
347            CableDeliverySystemDescriptor::parse(&raw).unwrap_err(),
348            Error::InvalidDescriptor { tag: TAG, .. }
349        ));
350    }
351
352    #[test]
353    fn serialize_round_trip() {
354        let d = CableDeliverySystemDescriptor {
355            frequency_bcd: 0x03460000,
356            fec_outer: FecOuter::ReedSolomon204_188,
357            modulation: Modulation::Qam256,
358            symbol_rate_bcd: 0x0687500,
359            fec_inner: FecInner::Rate3_4,
360        };
361        let mut buf = vec![0u8; d.serialized_len()];
362        d.serialize_into(&mut buf).unwrap();
363        let parsed = CableDeliverySystemDescriptor::parse(&buf).unwrap();
364        assert_eq!(parsed, d);
365    }
366
367    #[test]
368    fn enum_round_trip_covers_every_defined_variant() {
369        for fec_outer in [
370            FecOuter::NotDefined,
371            FecOuter::NoOuterFec,
372            FecOuter::ReedSolomon204_188,
373        ] {
374            let v = serialize_fec_outer(fec_outer);
375            assert_eq!(parse_fec_outer(v), fec_outer);
376        }
377
378        for mod_ in [
379            Modulation::NotDefined,
380            Modulation::Qam16,
381            Modulation::Qam32,
382            Modulation::Qam64,
383            Modulation::Qam128,
384            Modulation::Qam256,
385        ] {
386            let v = serialize_modulation(mod_);
387            assert_eq!(parse_modulation(v), mod_);
388        }
389
390        for fec_inner in [
391            FecInner::NotDefined,
392            FecInner::Rate1_2,
393            FecInner::Rate2_3,
394            FecInner::Rate3_4,
395            FecInner::Rate5_6,
396            FecInner::Rate7_8,
397            FecInner::Rate8_9,
398            FecInner::Rate3_5,
399            FecInner::Rate4_5,
400            FecInner::Rate9_10,
401            FecInner::NoConvCoding,
402        ] {
403            let v = serialize_fec_inner(fec_inner);
404            assert_eq!(parse_fec_inner(v), fec_inner);
405        }
406    }
407}