1use crate::error::{Error, Result};
7use crate::traits::Descriptor;
8use dvb_common::{Parse, Serialize};
9
10pub const TAG: u8 = 0x44;
12const HEADER_LEN: usize = 2;
13const BODY_LEN: u8 = 11;
14
15const RESERVED_FU_MASK: u16 = 0xFFF0;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21pub enum FecOuter {
22 NotDefined,
24 NoOuterFec,
26 ReedSolomon204_188,
28 Reserved(u8),
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub enum Modulation {
36 NotDefined,
38 Qam16,
40 Qam32,
42 Qam64,
44 Qam128,
46 Qam256,
48 Reserved(u8),
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize))]
55pub enum FecInner {
56 NotDefined,
58 Rate1_2,
60 Rate2_3,
62 Rate3_4,
64 Rate5_6,
66 Rate7_8,
68 Rate8_9,
70 Rate3_5,
72 Rate4_5,
74 Rate9_10,
76 NoConvCoding,
78 Reserved(u8),
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize))]
85pub struct CableDeliverySystemDescriptor {
86 pub frequency_bcd: u32,
88 pub fec_outer: FecOuter,
90 pub modulation: Modulation,
92 pub symbol_rate_bcd: u32,
94 pub fec_inner: FecInner,
96}
97
98impl CableDeliverySystemDescriptor {
99 #[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 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 #[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 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}