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, serde::Deserialize))]
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, serde::Deserialize))]
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, serde::Deserialize))]
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, serde::Deserialize))]
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
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}