1use super::*;
6
7impl<'a> ExtensionBodyDef<'a> for S2XSatelliteDeliverySystem<'a> {
8 const TAG_EXTENSION: u8 = 0x17;
9 const NAME: &'static str = "S2X_SATELLITE_DELIVERY_SYSTEM";
10}
11#[derive(Debug, Clone, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17pub struct S2XChannelBond {
18 pub frequency: u32,
20 pub orbital_position: u16,
22 pub west_east_flag: bool,
24 pub polarization: u8,
26 pub multiple_input_stream_flag: bool,
28 pub roll_off: u8,
30 pub symbol_rate: u32,
32 pub input_stream_identifier: Option<u8>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize))]
39#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
40pub struct S2XSatelliteDeliverySystem<'a> {
41 pub receiver_profiles: u8,
43 pub s2x_mode: u8,
45 pub scrambling_sequence_selector: bool,
47 pub ts_gs_s2x_mode: u8,
49 pub scrambling_sequence_index: Option<u32>,
51 pub frequency: u32,
53 pub orbital_position: u16,
55 pub west_east_flag: bool,
57 pub polarization: u8,
59 pub multiple_input_stream_flag: bool,
61 pub roll_off: u8,
63 pub symbol_rate: u32,
65 pub input_stream_identifier: Option<u8>,
67 pub timeslice_number: Option<u8>,
69 pub channel_bonds: Vec<S2XChannelBond>,
71 pub reserved_tail: &'a [u8],
73}
74
75const BOND_BASE_LEN: usize = S2X_PRIMARY_LEN;
76
77fn parse_channel_common(
78 sel: &[u8],
79 pos: &mut usize,
80) -> Result<(u32, u16, bool, u8, bool, u8, u32)> {
81 if sel.len() < *pos + BOND_BASE_LEN {
82 return Err(Error::BufferTooShort {
83 need: *pos + BOND_BASE_LEN,
84 have: sel.len(),
85 what: "S2X body",
86 });
87 }
88 let frequency = u32::from_be_bytes([sel[*pos], sel[*pos + 1], sel[*pos + 2], sel[*pos + 3]]);
89 let orbital_position = u16::from_be_bytes([sel[*pos + 4], sel[*pos + 5]]);
90 let pb = sel[*pos + 6];
91 let west_east_flag = (pb & 0x80) != 0;
92 let polarization = (pb >> 5) & 0x03;
93 let multiple_input_stream_flag = (pb & 0x10) != 0;
94 let roll_off = pb & 0x07;
95 let symbol_rate = (u32::from(sel[*pos + 7] & 0x0F) << 24)
96 | (u32::from(sel[*pos + 8]) << 16)
97 | (u32::from(sel[*pos + 9]) << 8)
98 | u32::from(sel[*pos + 10]);
99 *pos += BOND_BASE_LEN;
100 Ok((
101 frequency,
102 orbital_position,
103 west_east_flag,
104 polarization,
105 multiple_input_stream_flag,
106 roll_off,
107 symbol_rate,
108 ))
109}
110
111fn write_channel_common(
112 buf: &mut [u8],
113 p: &mut usize,
114 frequency: u32,
115 orbital_position: u16,
116 packed: u8,
117 symbol_rate: u32,
118) {
119 buf[*p..*p + 4].copy_from_slice(&frequency.to_be_bytes());
120 buf[*p + 4..*p + 6].copy_from_slice(&orbital_position.to_be_bytes());
121 buf[*p + 6] = packed;
122 let sr = symbol_rate & 0x0FFF_FFFF;
123 buf[*p + 7] = (sr >> 24) as u8 & 0x0F;
124 buf[*p + 8] = (sr >> 16) as u8;
125 buf[*p + 9] = (sr >> 8) as u8;
126 buf[*p + 10] = sr as u8;
127 *p += BOND_BASE_LEN;
128}
129
130fn pack_we_pol_mis_ro(we: bool, pol: u8, mis: bool, ro: u8) -> u8 {
131 (u8::from(we) << 7) | ((pol & 0x03) << 5) | (u8::from(mis) << 4) | (ro & 0x07)
132}
133
134impl<'a> Parse<'a> for S2XSatelliteDeliverySystem<'a> {
135 type Error = crate::error::Error;
136 fn parse(sel: &'a [u8]) -> Result<Self> {
137 if sel.len() < 2 {
139 return Err(Error::BufferTooShort {
140 need: 2,
141 have: sel.len(),
142 what: "S2X body",
143 });
144 }
145 let receiver_profiles = sel[0] >> 3;
146 let b1 = sel[1];
147 let s2x_mode = (b1 >> 6) & 0x03;
150 let scrambling_sequence_selector = (b1 & 0x20) != 0;
151 let ts_gs_s2x_mode = b1 & 0x03;
152 let mut pos = 2;
153 let scrambling_sequence_index = if scrambling_sequence_selector {
154 if sel.len() < pos + S2X_SCRAMBLING_LEN {
155 return Err(Error::BufferTooShort {
156 need: pos + S2X_SCRAMBLING_LEN,
157 have: sel.len(),
158 what: "S2X body",
159 });
160 }
161 let idx = (u32::from(sel[pos] & 0x03) << 16)
162 | (u32::from(sel[pos + 1]) << 8)
163 | u32::from(sel[pos + 2]);
164 pos += S2X_SCRAMBLING_LEN;
165 Some(idx)
166 } else {
167 None
168 };
169 let (
173 frequency,
174 orbital_position,
175 west_east_flag,
176 polarization,
177 multiple_input_stream_flag,
178 roll_off,
179 symbol_rate,
180 ) = parse_channel_common(sel, &mut pos)?;
181 let input_stream_identifier = if multiple_input_stream_flag {
182 if sel.len() < pos + 1 {
183 return Err(Error::BufferTooShort {
184 need: pos + 1,
185 have: sel.len(),
186 what: "S2X body",
187 });
188 }
189 let isi = sel[pos];
190 pos += 1;
191 Some(isi)
192 } else {
193 None
194 };
195 let timeslice_number = if s2x_mode == 2 {
196 if sel.len() < pos + 1 {
197 return Err(Error::BufferTooShort {
198 need: pos + 1,
199 have: sel.len(),
200 what: "S2X body",
201 });
202 }
203 let ts = sel[pos];
204 pos += 1;
205 Some(ts)
206 } else {
207 None
208 };
209 let (channel_bonds, reserved_tail) = if s2x_mode == 3 {
210 if sel.len() < pos + 1 {
212 return Err(Error::BufferTooShort {
213 need: pos + 1,
214 have: sel.len(),
215 what: "S2X body",
216 });
217 }
218 let bond_byte = sel[pos];
219 pos += 1;
220 let num_channel_bonds = (bond_byte & 0x01) as usize + 1;
222 let mut bonds = Vec::with_capacity(num_channel_bonds);
223 for _ in 0..num_channel_bonds {
224 let (freq, orb, we, pol, mis, ro, sr) = parse_channel_common(sel, &mut pos)?;
225 let isi = if mis {
226 if sel.len() < pos + 1 {
227 return Err(Error::BufferTooShort {
228 need: pos + 1,
229 have: sel.len(),
230 what: "S2X body",
231 });
232 }
233 let v = sel[pos];
234 pos += 1;
235 Some(v)
236 } else {
237 None
238 };
239 bonds.push(S2XChannelBond {
240 frequency: freq,
241 orbital_position: orb,
242 west_east_flag: we,
243 polarization: pol,
244 multiple_input_stream_flag: mis,
245 roll_off: ro,
246 symbol_rate: sr,
247 input_stream_identifier: isi,
248 });
249 }
250 (bonds, &sel[pos..])
251 } else {
252 (Vec::new(), &sel[pos..])
253 };
254 Ok(S2XSatelliteDeliverySystem {
255 receiver_profiles,
256 s2x_mode,
257 scrambling_sequence_selector,
258 ts_gs_s2x_mode,
259 scrambling_sequence_index,
260 frequency,
261 orbital_position,
262 west_east_flag,
263 polarization,
264 multiple_input_stream_flag,
265 roll_off,
266 symbol_rate,
267 input_stream_identifier,
268 timeslice_number,
269 channel_bonds,
270 reserved_tail,
271 })
272 }
273}
274
275impl Serialize for S2XSatelliteDeliverySystem<'_> {
276 type Error = crate::error::Error;
277 fn serialized_len(&self) -> usize {
278 let bond_len: usize = if self.s2x_mode == 3 {
279 1 + self
280 .channel_bonds
281 .iter()
282 .map(|b| BOND_BASE_LEN + usize::from(b.input_stream_identifier.is_some()))
283 .sum::<usize>()
284 } else {
285 0
286 };
287 2 + if self.scrambling_sequence_selector {
288 S2X_SCRAMBLING_LEN
289 } else {
290 0
291 } + S2X_PRIMARY_LEN
292 + usize::from(self.input_stream_identifier.is_some())
293 + usize::from(self.timeslice_number.is_some())
294 + bond_len
295 + self.reserved_tail.len()
296 }
297 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
298 let len = self.serialized_len();
299 if buf.len() < len {
300 return Err(Error::OutputBufferTooSmall {
301 need: len,
302 have: buf.len(),
303 });
304 }
305 buf[0] = self.receiver_profiles << 3;
306 buf[1] = ((self.s2x_mode & 0x03) << 6)
307 | (u8::from(self.scrambling_sequence_selector) << 5)
308 | (self.ts_gs_s2x_mode & 0x03);
309 let mut p = 2;
310 if self.scrambling_sequence_selector {
311 let idx = self.scrambling_sequence_index.unwrap_or(0) & 0x3FFFF;
312 buf[p] = (idx >> 16) as u8 & 0x03;
313 buf[p + 1] = (idx >> 8) as u8;
314 buf[p + 2] = idx as u8;
315 p += S2X_SCRAMBLING_LEN;
316 }
317 write_channel_common(
318 buf,
319 &mut p,
320 self.frequency,
321 self.orbital_position,
322 pack_we_pol_mis_ro(
323 self.west_east_flag,
324 self.polarization,
325 self.multiple_input_stream_flag,
326 self.roll_off,
327 ),
328 self.symbol_rate,
329 );
330 if let Some(isi) = self.input_stream_identifier {
331 buf[p] = isi;
332 p += 1;
333 }
334 if let Some(ts) = self.timeslice_number {
335 buf[p] = ts;
336 p += 1;
337 }
338 if self.s2x_mode == 3 {
339 buf[p] = (self.channel_bonds.len() as u8).saturating_sub(1) & 0x01;
341 p += 1;
342 for bond in &self.channel_bonds {
343 write_channel_common(
344 buf,
345 &mut p,
346 bond.frequency,
347 bond.orbital_position,
348 pack_we_pol_mis_ro(
349 bond.west_east_flag,
350 bond.polarization,
351 bond.multiple_input_stream_flag,
352 bond.roll_off,
353 ),
354 bond.symbol_rate,
355 );
356 if let Some(isi) = bond.input_stream_identifier {
357 buf[p] = isi;
358 p += 1;
359 }
360 }
361 }
362 buf[p..p + self.reserved_tail.len()].copy_from_slice(self.reserved_tail);
363 Ok(len)
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370 use crate::descriptors::extension::test_support::*;
371 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
372
373 #[test]
374 fn parse_s2x_primary_with_isi_and_timeslice() {
375 let b0 = 0x05 << 3;
377 let b1 = (0x02 << 6) | 0x01; let mut sel = vec![b0, b1];
379 sel.extend_from_slice(&0x0102_0304u32.to_be_bytes()); sel.extend_from_slice(&0x00C8u16.to_be_bytes()); sel.push((1 << 7) | (0x02 << 5) | (1 << 4) | 0x03); let sr: u32 = 0x0AB_CDEF; sel.push((sr >> 24) as u8 & 0x0F);
384 sel.push((sr >> 16) as u8);
385 sel.push((sr >> 8) as u8);
386 sel.push(sr as u8);
387 sel.push(0x42); sel.push(0x09); let bytes = wrap(0x17, &sel);
390 let d = ExtensionDescriptor::parse(&bytes).unwrap();
391 match &d.body {
392 ExtensionBody::S2XSatelliteDeliverySystem(b) => {
393 assert_eq!(b.receiver_profiles, 0x05);
394 assert_eq!(b.s2x_mode, 2);
395 assert!(!b.scrambling_sequence_selector);
396 assert_eq!(b.ts_gs_s2x_mode, 1);
397 assert_eq!(b.frequency, 0x0102_0304);
398 assert_eq!(b.orbital_position, 0x00C8);
399 assert!(b.west_east_flag);
400 assert_eq!(b.polarization, 2);
401 assert!(b.multiple_input_stream_flag);
402 assert_eq!(b.roll_off, 3);
403 assert_eq!(b.symbol_rate, 0x0AB_CDEF);
404 assert_eq!(b.input_stream_identifier, Some(0x42));
405 assert_eq!(b.timeslice_number, Some(0x09));
406 assert!(b.channel_bonds.is_empty());
407 assert!(b.reserved_tail.is_empty());
408 }
409 other => panic!("expected S2X, got {other:?}"),
410 }
411 round_trip(&d);
412 }
413
414 #[test]
415 fn parse_s2x_with_scrambling_index() {
416 let b0 = 0x01 << 3;
417 let b1 = (0x01 << 6) | 0x20; let mut sel = vec![b0, b1];
419 sel.push(0x02);
421 sel.push(0xAB);
422 sel.push(0xCD);
423 sel.extend_from_slice(&0u32.to_be_bytes()); sel.extend_from_slice(&0u16.to_be_bytes()); sel.push(0x00); sel.extend_from_slice(&[0, 0, 0, 0]); let bytes = wrap(0x17, &sel);
428 let d = ExtensionDescriptor::parse(&bytes).unwrap();
429 match &d.body {
430 ExtensionBody::S2XSatelliteDeliverySystem(b) => {
431 assert!(b.scrambling_sequence_selector);
432 assert_eq!(b.scrambling_sequence_index, Some(0x2ABCD));
433 assert_eq!(b.input_stream_identifier, None);
434 assert_eq!(b.timeslice_number, None);
435 assert!(b.channel_bonds.is_empty());
436 assert!(b.reserved_tail.is_empty());
437 }
438 other => panic!("expected S2X, got {other:?}"),
439 }
440 round_trip(&d);
441 }
442
443 #[test]
444 fn parse_s2x_mode1_tail_preserved() {
445 let b0 = 0x01 << 3;
447 let b1 = 0x01 << 6; let mut sel = vec![b0, b1];
449 sel.extend_from_slice(&0u32.to_be_bytes());
450 sel.extend_from_slice(&0u16.to_be_bytes());
451 sel.push(0x00); sel.extend_from_slice(&[0, 0, 0, 0]); sel.extend_from_slice(&[0xAA, 0xBB, 0xCC]); let bytes = wrap(0x17, &sel);
455 let d = ExtensionDescriptor::parse(&bytes).unwrap();
456 match &d.body {
457 ExtensionBody::S2XSatelliteDeliverySystem(b) => {
458 assert_eq!(b.s2x_mode, 1);
459 assert_eq!(b.timeslice_number, None);
460 assert!(b.channel_bonds.is_empty());
461 assert_eq!(b.reserved_tail, &[0xAA, 0xBB, 0xCC]);
462 }
463 other => panic!("expected S2X, got {other:?}"),
464 }
465 round_trip(&d);
466 }
467
468 #[test]
469 fn parse_s2x_mode3_channel_bonds() {
470 let b0 = 0x01 << 3;
472 let b1 = 0x03 << 6; let mut sel = vec![b0, b1];
474 sel.extend_from_slice(&0x1111_1111u32.to_be_bytes()); sel.extend_from_slice(&0x0001u16.to_be_bytes()); sel.push(0x00); sel.extend_from_slice(&[0x00, 0x00, 0x00, 0x00]); sel.push(0x01);
482
483 sel.extend_from_slice(&0x2222_2222u32.to_be_bytes());
486 sel.extend_from_slice(&0x0002u16.to_be_bytes());
487 sel.push((1 << 7) | (0x02 << 5) | (1 << 4) | 0x03); let sr: u32 = 0x0AB_CDEF;
489 sel.push((sr >> 24) as u8 & 0x0F);
490 sel.push((sr >> 16) as u8);
491 sel.push((sr >> 8) as u8);
492 sel.push(sr as u8);
493 sel.push(0x77); sel.extend_from_slice(&0x3333_3333u32.to_be_bytes());
498 sel.extend_from_slice(&0x0003u16.to_be_bytes());
499 sel.push((0x01 << 5) | 0x04); let sr2: u32 = 0x005_4321;
501 sel.push((sr2 >> 24) as u8 & 0x0F);
502 sel.push((sr2 >> 16) as u8);
503 sel.push((sr2 >> 8) as u8);
504 sel.push(sr2 as u8);
505
506 let bytes = wrap(0x17, &sel);
507 let d = ExtensionDescriptor::parse(&bytes).unwrap();
508 match &d.body {
509 ExtensionBody::S2XSatelliteDeliverySystem(b) => {
510 assert_eq!(b.s2x_mode, 3);
511 assert_eq!(b.channel_bonds.len(), 2);
512
513 let b0 = &b.channel_bonds[0];
514 assert_eq!(b0.frequency, 0x2222_2222);
515 assert_eq!(b0.orbital_position, 0x0002);
516 assert!(b0.west_east_flag);
517 assert_eq!(b0.polarization, 2);
518 assert!(b0.multiple_input_stream_flag);
519 assert_eq!(b0.roll_off, 3);
520 assert_eq!(b0.symbol_rate, 0x0AB_CDEF);
521 assert_eq!(b0.input_stream_identifier, Some(0x77));
522
523 let b1 = &b.channel_bonds[1];
524 assert_eq!(b1.frequency, 0x3333_3333);
525 assert_eq!(b1.orbital_position, 0x0003);
526 assert!(!b1.west_east_flag);
527 assert_eq!(b1.polarization, 1);
528 assert!(!b1.multiple_input_stream_flag);
529 assert_eq!(b1.roll_off, 4);
530 assert_eq!(b1.symbol_rate, 0x005_4321);
531 assert_eq!(b1.input_stream_identifier, None);
532
533 assert!(b.reserved_tail.is_empty());
534 }
535 other => panic!("expected S2X, got {other:?}"),
536 }
537 round_trip(&d);
538 }
539
540 #[test]
541 fn tsduck_s2x_mode3_byte_exact() {
542 let hex = "7f2a1750e3023456876543210037250456789601065432180340f600246754bd00654367123451000087642e";
545 let bytes = from_hex(hex);
546 let d = ExtensionDescriptor::parse(&bytes)
547 .unwrap_or_else(|e| panic!("parse tsduck s2x: {e:?}"));
548
549 assert_eq!(d.kind(), Some(ExtensionTag::S2XSatelliteDeliverySystem));
550 match &d.body {
551 ExtensionBody::S2XSatelliteDeliverySystem(b) => {
552 assert_eq!(b.s2x_mode, 3);
553 assert!(b.scrambling_sequence_selector);
554 assert_eq!(b.scrambling_sequence_index, Some(0x023456));
555 assert!(!b.channel_bonds.is_empty());
556 assert_eq!(b.channel_bonds.len(), 2);
557
558 let b0 = &b.channel_bonds[0];
559 assert_eq!(b0.frequency, 0x0654_3218);
560 assert_eq!(b0.orbital_position, 0x0340);
561 assert!(b0.west_east_flag);
562 assert_eq!(b0.polarization, 3);
563 assert!(b0.multiple_input_stream_flag);
564 assert_eq!(b0.roll_off, 6);
565 assert_eq!(b0.symbol_rate, 0x0024_6754);
566 assert_eq!(b0.input_stream_identifier, Some(0xBD));
567
568 let b1 = &b.channel_bonds[1];
569 assert_eq!(b1.frequency, 0x0065_4367);
570 assert_eq!(b1.orbital_position, 0x1234);
571 assert!(!b1.west_east_flag);
572 assert_eq!(b1.polarization, 2);
573 assert!(b1.multiple_input_stream_flag);
574 assert_eq!(b1.roll_off, 1);
575 assert_eq!(b1.symbol_rate, 0x0000_8764);
576 assert_eq!(b1.input_stream_identifier, Some(0x2E));
577
578 assert!(b.reserved_tail.is_empty());
579 }
580 other => panic!("expected S2X, got {other:?}"),
581 }
582
583 let mut out = vec![0u8; d.serialized_len()];
585 let n = d.serialize_into(&mut out).unwrap();
586 assert_eq!(
587 &out[..n],
588 &bytes[..],
589 "S2X mode 3 byte-exact re-serialize failed"
590 );
591 }
592}