1use std::fmt;
31
32use num_enum::TryFromPrimitive;
33
34use dvb_common::{Parse, Serialize};
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize))]
39#[repr(u8)]
40#[non_exhaustive]
41pub enum AddressingFunctionTag {
42 TimeOffset = 0x00,
44 FrequencyOffset = 0x01,
46 Power = 0x02,
48 PrivateData = 0x03,
50 CellId = 0x04,
52 Enable = 0x05,
54 Bandwidth = 0x06,
56 AcePapr = 0x10,
58 MisoGroup = 0x11,
60 TrPapr = 0x12,
62 L1AcePapr = 0x13,
64 TxSigFefSeqNum = 0x15,
66 TxSigAuxStreamTxId = 0x16,
68 Frequency = 0x17,
70}
71
72impl From<AddressingFunctionTag> for u8 {
73 fn from(tag: AddressingFunctionTag) -> Self {
74 tag as u8
75 }
76}
77
78impl fmt::Display for AddressingFunctionTag {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 write!(f, "{self:?}")
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize))]
95pub struct AcePaprBody {
96 pub ace_gain: u8,
98 pub ace_maximal_extension: u8,
100 pub ace_clipping_threshold: u8,
102 pub rfu: bool,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize))]
113pub struct MisoGroupBody {
114 pub miso_group: bool,
116 pub rfu: u8,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq)]
128#[cfg_attr(feature = "serde", derive(serde::Serialize))]
129pub struct TrPaprBody {
130 pub rfu1: u8,
132 pub tr_clipping_threshold: u16,
134 pub rfu2: u16,
136 pub number_of_iterations: u16,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
146#[cfg_attr(feature = "serde", derive(serde::Serialize))]
147pub struct L1AcePaprBody {
148 pub l1_ace_max_correction: u16,
150 pub rfu: u16,
152}
153
154#[derive(Debug, Clone, PartialEq, Eq)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize))]
164pub struct TxSigFefSeqNumBody {
165 pub rfu1: u8,
167 pub seq_num_1: u8,
169 pub rfu2: u8,
171 pub seq_num_2: u8,
173 pub rfu3: u32,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize))]
184pub struct TxSigAuxStreamTxIdBody {
185 pub tx_sig_aux_tx_id: u16,
187 pub rfu: u32,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
200#[cfg_attr(feature = "serde", derive(serde::Serialize))]
201pub struct FrequencyBody {
202 pub rf_idx: u8,
204 pub frequency: u32,
206 pub rfu: u8,
208}
209
210const ACE_PAPR_BODY_LEN: usize = 2;
213const MISO_GROUP_BODY_LEN: usize = 1;
214const TR_PAPR_BODY_LEN: usize = 5;
215const L1_ACE_PAPR_BODY_LEN: usize = 4;
216const TX_SIG_FEF_SEQ_NUM_BODY_LEN: usize = 5;
217const TX_SIG_AUX_STREAM_TX_ID_BODY_LEN: usize = 4;
218const FREQUENCY_BODY_LEN: usize = 5;
219
220#[derive(Debug, Clone, PartialEq, Eq)]
234#[cfg_attr(feature = "serde", derive(serde::Serialize))]
235#[non_exhaustive]
236pub enum FunctionBody<'a> {
237 AcePapr(AcePaprBody),
239 MisoGroup(MisoGroupBody),
241 TrPapr(TrPaprBody),
243 L1AcePapr(L1AcePaprBody),
245 TxSigFefSeqNum(TxSigFefSeqNumBody),
247 TxSigAuxStreamTxId(TxSigAuxStreamTxIdBody),
249 Frequency(FrequencyBody),
251 Raw(&'a [u8]),
255}
256
257#[derive(Debug, Clone, PartialEq, Eq)]
264#[cfg_attr(feature = "serde", derive(serde::Serialize))]
265#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
266pub struct TransmitterEntry<'a> {
267 pub transmitter_id: u16,
269 pub functions: Vec<FunctionEntry<'a>>,
271}
272
273#[derive(Debug, Clone, PartialEq, Eq)]
279#[cfg_attr(feature = "serde", derive(serde::Serialize))]
280#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
281pub struct FunctionEntry<'a> {
282 pub tag: u8,
285 pub body: FunctionBody<'a>,
287}
288
289impl<'a> FunctionEntry<'a> {
290 #[must_use]
293 pub fn addressing_tag(&self) -> Option<AddressingFunctionTag> {
294 AddressingFunctionTag::try_from(self.tag).ok()
295 }
296}
297
298#[derive(Debug, Clone, PartialEq, Eq)]
307#[cfg_attr(feature = "serde", derive(serde::Serialize))]
308#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
309pub struct IndividualAddressingPayload<'a> {
310 pub rfu: u8,
312 pub transmitters: Vec<TransmitterEntry<'a>>,
315}
316
317const HEADER_LEN: usize = 2;
320const TX_HEADER_LEN: usize = 3;
321const FUNC_HEADER_LEN: usize = 2;
322
323fn parse_ace_papr(body: &[u8]) -> Result<AcePaprBody, crate::Error> {
326 if body.len() < ACE_PAPR_BODY_LEN {
327 return Err(crate::Error::BufferTooShort {
328 need: ACE_PAPR_BODY_LEN,
329 have: body.len(),
330 what: "ACE-PAPR function body",
331 });
332 }
333 Ok(AcePaprBody {
334 ace_gain: (body[0] >> 3) & 0x1F,
335 ace_maximal_extension: body[0] & 0x07,
336 ace_clipping_threshold: (body[1] >> 1) & 0x7F,
337 rfu: body[1] & 0x01 != 0,
338 })
339}
340
341fn parse_miso_group(body: &[u8]) -> Result<MisoGroupBody, crate::Error> {
342 if body.len() < MISO_GROUP_BODY_LEN {
343 return Err(crate::Error::BufferTooShort {
344 need: MISO_GROUP_BODY_LEN,
345 have: body.len(),
346 what: "MISO group function body",
347 });
348 }
349 Ok(MisoGroupBody {
350 miso_group: body[0] & 0x80 != 0,
351 rfu: body[0] & 0x7F,
352 })
353}
354
355fn parse_tr_papr(body: &[u8]) -> Result<TrPaprBody, crate::Error> {
356 if body.len() < TR_PAPR_BODY_LEN {
357 return Err(crate::Error::BufferTooShort {
358 need: TR_PAPR_BODY_LEN,
359 have: body.len(),
360 what: "TR-PAPR function body",
361 });
362 }
363 Ok(TrPaprBody {
364 rfu1: (body[0] >> 4) & 0x0F,
365 tr_clipping_threshold: ((body[0] as u16 & 0x0F) << 8) | body[1] as u16,
366 rfu2: ((body[2] as u16) << 6) | ((body[3] as u16) >> 2),
367 number_of_iterations: ((body[3] as u16 & 0x03) << 8) | body[4] as u16,
368 })
369}
370
371fn parse_l1_ace_papr(body: &[u8]) -> Result<L1AcePaprBody, crate::Error> {
372 if body.len() < L1_ACE_PAPR_BODY_LEN {
373 return Err(crate::Error::BufferTooShort {
374 need: L1_ACE_PAPR_BODY_LEN,
375 have: body.len(),
376 what: "L1-ACE-PAPR function body",
377 });
378 }
379 Ok(L1AcePaprBody {
380 l1_ace_max_correction: u16::from_be_bytes([body[0], body[1]]),
381 rfu: u16::from_be_bytes([body[2], body[3]]),
382 })
383}
384
385fn parse_tx_sig_fef_seq_num(body: &[u8]) -> Result<TxSigFefSeqNumBody, crate::Error> {
386 if body.len() < TX_SIG_FEF_SEQ_NUM_BODY_LEN {
387 return Err(crate::Error::BufferTooShort {
388 need: TX_SIG_FEF_SEQ_NUM_BODY_LEN,
389 have: body.len(),
390 what: "TX-SIG FEF sequence numbers function body",
391 });
392 }
393 Ok(TxSigFefSeqNumBody {
394 rfu1: (body[0] >> 3) & 0x1F,
395 seq_num_1: body[0] & 0x07,
396 rfu2: (body[1] >> 3) & 0x1F,
397 seq_num_2: body[1] & 0x07,
398 rfu3: (body[2] as u32) << 16 | (body[3] as u32) << 8 | body[4] as u32,
399 })
400}
401
402fn parse_tx_sig_aux_stream_tx_id(body: &[u8]) -> Result<TxSigAuxStreamTxIdBody, crate::Error> {
403 if body.len() < TX_SIG_AUX_STREAM_TX_ID_BODY_LEN {
404 return Err(crate::Error::BufferTooShort {
405 need: TX_SIG_AUX_STREAM_TX_ID_BODY_LEN,
406 have: body.len(),
407 what: "TX-SIG aux stream TX ID function body",
408 });
409 }
410 Ok(TxSigAuxStreamTxIdBody {
411 tx_sig_aux_tx_id: ((body[0] as u16) << 4) | ((body[1] as u16) >> 4),
412 rfu: ((body[1] as u32 & 0x0F) << 16) | (body[2] as u32) << 8 | body[3] as u32,
413 })
414}
415
416fn parse_frequency(body: &[u8]) -> Result<FrequencyBody, crate::Error> {
417 if body.len() < FREQUENCY_BODY_LEN {
418 return Err(crate::Error::BufferTooShort {
419 need: FREQUENCY_BODY_LEN,
420 have: body.len(),
421 what: "Frequency function body",
422 });
423 }
424 Ok(FrequencyBody {
425 rf_idx: (body[0] >> 5) & 0x07,
426 frequency: ((body[0] as u32 & 0x1F) << 27)
427 | (body[1] as u32) << 19
428 | (body[2] as u32) << 11
429 | (body[3] as u32) << 3
430 | ((body[4] >> 5) as u32 & 0x07),
431 rfu: body[4] & 0x1F,
432 })
433}
434
435fn try_parse_typed_body(
441 tag: u8,
442 body_bytes: &[u8],
443) -> Option<Result<FunctionBody<'_>, crate::Error>> {
444 match tag {
445 t if t == AddressingFunctionTag::AcePapr as u8 && body_bytes.len() == ACE_PAPR_BODY_LEN => {
446 Some(parse_ace_papr(body_bytes).map(FunctionBody::AcePapr))
447 }
448 t if t == AddressingFunctionTag::MisoGroup as u8
449 && body_bytes.len() == MISO_GROUP_BODY_LEN =>
450 {
451 Some(parse_miso_group(body_bytes).map(FunctionBody::MisoGroup))
452 }
453 t if t == AddressingFunctionTag::TrPapr as u8 && body_bytes.len() == TR_PAPR_BODY_LEN => {
454 Some(parse_tr_papr(body_bytes).map(FunctionBody::TrPapr))
455 }
456 t if t == AddressingFunctionTag::L1AcePapr as u8
457 && body_bytes.len() == L1_ACE_PAPR_BODY_LEN =>
458 {
459 Some(parse_l1_ace_papr(body_bytes).map(FunctionBody::L1AcePapr))
460 }
461 t if t == AddressingFunctionTag::TxSigFefSeqNum as u8
462 && body_bytes.len() == TX_SIG_FEF_SEQ_NUM_BODY_LEN =>
463 {
464 Some(parse_tx_sig_fef_seq_num(body_bytes).map(FunctionBody::TxSigFefSeqNum))
465 }
466 t if t == AddressingFunctionTag::TxSigAuxStreamTxId as u8
467 && body_bytes.len() == TX_SIG_AUX_STREAM_TX_ID_BODY_LEN =>
468 {
469 Some(parse_tx_sig_aux_stream_tx_id(body_bytes).map(FunctionBody::TxSigAuxStreamTxId))
470 }
471 t if t == AddressingFunctionTag::Frequency as u8
472 && body_bytes.len() == FREQUENCY_BODY_LEN =>
473 {
474 Some(parse_frequency(body_bytes).map(FunctionBody::Frequency))
475 }
476 _ => None,
477 }
478}
479
480fn serialize_ace_papr(body: &AcePaprBody, buf: &mut [u8]) {
483 buf[0] = (body.ace_gain & 0x1F) << 3 | (body.ace_maximal_extension & 0x07);
484 buf[1] = (body.ace_clipping_threshold & 0x7F) << 1 | if body.rfu { 1 } else { 0 };
485}
486
487fn serialize_miso_group(body: &MisoGroupBody, buf: &mut [u8]) {
488 buf[0] = if body.miso_group { 0x80 } else { 0x00 } | (body.rfu & 0x7F);
489}
490
491fn serialize_tr_papr(body: &TrPaprBody, buf: &mut [u8]) {
492 buf[0] = (body.rfu1 & 0x0F) << 4 | ((body.tr_clipping_threshold >> 8) as u8 & 0x0F);
493 buf[1] = (body.tr_clipping_threshold & 0xFF) as u8;
494 buf[2] = (body.rfu2 >> 6) as u8;
495 buf[3] = ((body.rfu2 & 0x3F) as u8) << 2 | ((body.number_of_iterations >> 8) as u8 & 0x03);
496 buf[4] = (body.number_of_iterations & 0xFF) as u8;
497}
498
499fn serialize_l1_ace_papr(body: &L1AcePaprBody, buf: &mut [u8]) {
500 buf[0..2].copy_from_slice(&body.l1_ace_max_correction.to_be_bytes());
501 buf[2..4].copy_from_slice(&body.rfu.to_be_bytes());
502}
503
504fn serialize_tx_sig_fef_seq_num(body: &TxSigFefSeqNumBody, buf: &mut [u8]) {
505 buf[0] = (body.rfu1 & 0x1F) << 3 | (body.seq_num_1 & 0x07);
506 buf[1] = (body.rfu2 & 0x1F) << 3 | (body.seq_num_2 & 0x07);
507 buf[2] = ((body.rfu3 >> 16) & 0xFF) as u8;
508 buf[3] = ((body.rfu3 >> 8) & 0xFF) as u8;
509 buf[4] = (body.rfu3 & 0xFF) as u8;
510}
511
512fn serialize_tx_sig_aux_stream_tx_id(body: &TxSigAuxStreamTxIdBody, buf: &mut [u8]) {
513 buf[0] = ((body.tx_sig_aux_tx_id >> 4) & 0xFF) as u8;
514 buf[1] = ((body.tx_sig_aux_tx_id & 0x0F) as u8) << 4 | ((body.rfu >> 16) & 0x0F) as u8;
515 buf[2] = ((body.rfu >> 8) & 0xFF) as u8;
516 buf[3] = (body.rfu & 0xFF) as u8;
517}
518
519fn serialize_frequency(body: &FrequencyBody, buf: &mut [u8]) {
520 buf[0] = (body.rf_idx & 0x07) << 5 | ((body.frequency >> 27) & 0x1F) as u8;
521 buf[1] = ((body.frequency >> 19) & 0xFF) as u8;
522 buf[2] = ((body.frequency >> 11) & 0xFF) as u8;
523 buf[3] = ((body.frequency >> 3) & 0xFF) as u8;
524 buf[4] = ((body.frequency & 0x07) as u8) << 5 | (body.rfu & 0x1F);
525}
526
527fn body_serialized_len(body: &FunctionBody<'_>) -> usize {
528 match body {
529 FunctionBody::AcePapr(_) => ACE_PAPR_BODY_LEN,
530 FunctionBody::MisoGroup(_) => MISO_GROUP_BODY_LEN,
531 FunctionBody::TrPapr(_) => TR_PAPR_BODY_LEN,
532 FunctionBody::L1AcePapr(_) => L1_ACE_PAPR_BODY_LEN,
533 FunctionBody::TxSigFefSeqNum(_) => TX_SIG_FEF_SEQ_NUM_BODY_LEN,
534 FunctionBody::TxSigAuxStreamTxId(_) => TX_SIG_AUX_STREAM_TX_ID_BODY_LEN,
535 FunctionBody::Frequency(_) => FREQUENCY_BODY_LEN,
536 FunctionBody::Raw(bytes) => bytes.len(),
537 }
538}
539
540fn serialize_body_into(body: &FunctionBody<'_>, buf: &mut [u8]) -> usize {
541 match body {
542 FunctionBody::AcePapr(b) => {
543 serialize_ace_papr(b, buf);
544 ACE_PAPR_BODY_LEN
545 }
546 FunctionBody::MisoGroup(b) => {
547 serialize_miso_group(b, buf);
548 MISO_GROUP_BODY_LEN
549 }
550 FunctionBody::TrPapr(b) => {
551 serialize_tr_papr(b, buf);
552 TR_PAPR_BODY_LEN
553 }
554 FunctionBody::L1AcePapr(b) => {
555 serialize_l1_ace_papr(b, buf);
556 L1_ACE_PAPR_BODY_LEN
557 }
558 FunctionBody::TxSigFefSeqNum(b) => {
559 serialize_tx_sig_fef_seq_num(b, buf);
560 TX_SIG_FEF_SEQ_NUM_BODY_LEN
561 }
562 FunctionBody::TxSigAuxStreamTxId(b) => {
563 serialize_tx_sig_aux_stream_tx_id(b, buf);
564 TX_SIG_AUX_STREAM_TX_ID_BODY_LEN
565 }
566 FunctionBody::Frequency(b) => {
567 serialize_frequency(b, buf);
568 FREQUENCY_BODY_LEN
569 }
570 FunctionBody::Raw(bytes) => {
571 buf[..bytes.len()].copy_from_slice(bytes);
572 bytes.len()
573 }
574 }
575}
576
577fn function_entry_serialized_len(entry: &FunctionEntry<'_>) -> usize {
578 FUNC_HEADER_LEN + body_serialized_len(&entry.body)
579}
580
581fn transmitter_entry_serialized_len(entry: &TransmitterEntry<'_>) -> usize {
582 let func_loop_len: usize = entry
583 .functions
584 .iter()
585 .map(function_entry_serialized_len)
586 .sum();
587 TX_HEADER_LEN + func_loop_len
588}
589
590impl<'a> Parse<'a> for IndividualAddressingPayload<'a> {
593 type Error = crate::error::Error;
594
595 fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
596 if bytes.len() < HEADER_LEN {
597 return Err(crate::Error::BufferTooShort {
598 need: HEADER_LEN,
599 have: bytes.len(),
600 what: "IndividualAddressingPayload header",
601 });
602 }
603
604 let rfu = bytes[0];
605 let individual_addressing_length = bytes[1] as usize;
606 let need = HEADER_LEN + individual_addressing_length;
607 if bytes.len() < need {
608 return Err(crate::Error::BufferTooShort {
609 need,
610 have: bytes.len(),
611 what: "IndividualAddressingPayload data",
612 });
613 }
614
615 let data = &bytes[HEADER_LEN..need];
616 let mut pos: usize = 0;
617 let max_tx = individual_addressing_length / TX_HEADER_LEN + 1;
618 let mut transmitters = Vec::with_capacity(max_tx.min(individual_addressing_length));
619
620 while pos < individual_addressing_length {
621 if pos + TX_HEADER_LEN > individual_addressing_length {
622 return Err(crate::Error::BufferTooShort {
623 need: pos + TX_HEADER_LEN,
624 have: individual_addressing_length,
625 what: "transmitter entry header",
626 });
627 }
628
629 let transmitter_id = u16::from_be_bytes([data[pos], data[pos + 1]]);
630 let function_loop_length = data[pos + 2] as usize;
631 pos += TX_HEADER_LEN;
632
633 let func_end = pos + function_loop_length;
634 if func_end > individual_addressing_length {
635 return Err(crate::Error::BufferTooShort {
636 need: func_end,
637 have: individual_addressing_length,
638 what: "function loop",
639 });
640 }
641
642 let max_funcs = function_loop_length / FUNC_HEADER_LEN + 1;
643 let mut functions = Vec::with_capacity(max_funcs.min(function_loop_length));
644
645 while pos < func_end {
646 if pos + FUNC_HEADER_LEN > func_end {
647 return Err(crate::Error::BufferTooShort {
648 need: pos + FUNC_HEADER_LEN,
649 have: func_end,
650 what: "function entry header",
651 });
652 }
653
654 let tag = data[pos];
655 let function_length = data[pos + 1] as usize;
656 pos += FUNC_HEADER_LEN;
657
658 let body_end = pos + function_length;
659 if body_end > func_end {
660 return Err(crate::Error::BufferTooShort {
661 need: body_end,
662 have: func_end,
663 what: "function body",
664 });
665 }
666
667 let body_bytes = &data[pos..body_end];
668 pos = body_end;
669
670 let body = match try_parse_typed_body(tag, body_bytes) {
671 Some(Ok(typed)) => typed,
672 Some(Err(e)) => return Err(e),
673 None => FunctionBody::Raw(body_bytes),
674 };
675
676 functions.push(FunctionEntry { tag, body });
677 }
678
679 transmitters.push(TransmitterEntry {
680 transmitter_id,
681 functions,
682 });
683 }
684
685 Ok(IndividualAddressingPayload { rfu, transmitters })
686 }
687}
688
689impl<'a> crate::traits::PayloadDef<'a> for IndividualAddressingPayload<'a> {
690 const PACKET_TYPE: u8 = 0x21;
691 const NAME: &'static str = "INDIVIDUAL_ADDRESSING";
692}
693
694impl Serialize for IndividualAddressingPayload<'_> {
697 type Error = crate::error::Error;
698
699 fn serialized_len(&self) -> usize {
700 HEADER_LEN
701 + self
702 .transmitters
703 .iter()
704 .map(transmitter_entry_serialized_len)
705 .sum::<usize>()
706 }
707
708 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
709 let data_len: usize = self
710 .transmitters
711 .iter()
712 .map(transmitter_entry_serialized_len)
713 .sum();
714 let total = HEADER_LEN + data_len;
715
716 if buf.len() < total {
717 return Err(crate::Error::OutputBufferTooSmall {
718 need: total,
719 have: buf.len(),
720 });
721 }
722
723 if data_len > u8::MAX as usize {
724 return Err(crate::Error::ReservedBitsViolation {
725 field: "individual_addressing_length",
726 reason: "transmitter loop exceeds 255 bytes (8-bit length field)",
727 });
728 }
729
730 buf[0] = self.rfu;
731 buf[1] = data_len as u8;
732
733 let mut pos: usize = HEADER_LEN;
734
735 for tx in &self.transmitters {
736 let func_loop_len: usize = tx.functions.iter().map(function_entry_serialized_len).sum();
737 if func_loop_len > u8::MAX as usize {
738 return Err(crate::Error::ReservedBitsViolation {
739 field: "function_loop_length",
740 reason: "function loop exceeds 255 bytes (8-bit length field)",
741 });
742 }
743
744 buf[pos] = (tx.transmitter_id >> 8) as u8;
745 buf[pos + 1] = (tx.transmitter_id & 0xFF) as u8;
746 buf[pos + 2] = func_loop_len as u8;
747 pos += TX_HEADER_LEN;
748
749 for func in &tx.functions {
750 let body_len = body_serialized_len(&func.body);
751 if body_len > u8::MAX as usize {
752 return Err(crate::Error::ReservedBitsViolation {
753 field: "function_length",
754 reason: "function body exceeds 255 bytes (8-bit length field)",
755 });
756 }
757
758 buf[pos] = func.tag;
759 buf[pos + 1] = body_len as u8;
760 pos += FUNC_HEADER_LEN;
761
762 let written = serialize_body_into(&func.body, &mut buf[pos..]);
763 debug_assert_eq!(written, body_len);
764 pos += body_len;
765 }
766 }
767
768 debug_assert_eq!(pos, total);
769 Ok(total)
770 }
771}
772
773impl fmt::Display for IndividualAddressingPayload<'_> {
776 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777 write!(
778 f,
779 "IndividualAddressing {{ rfu: 0x{:02X}, tx_count: {} }}",
780 self.rfu,
781 self.transmitters.len()
782 )
783 }
784}
785
786#[cfg(test)]
789mod tests {
790 use super::*;
791
792 #[test]
793 fn addressing_function_tag_try_from_valid() {
794 assert_eq!(
795 AddressingFunctionTag::try_from(0x10),
796 Ok(AddressingFunctionTag::AcePapr)
797 );
798 assert_eq!(
799 AddressingFunctionTag::try_from(0x17),
800 Ok(AddressingFunctionTag::Frequency)
801 );
802 }
803
804 #[test]
805 fn addressing_function_tag_try_from_rejects_unknown() {
806 assert!(AddressingFunctionTag::try_from(0x14).is_err());
807 assert!(AddressingFunctionTag::try_from(0xFF).is_err());
808 }
809
810 #[test]
811 fn exhaustive_byte_sweep() {
812 let mut matched = 0u16;
813 for byte in 0u8..=0xFF {
814 if let Ok(v) = AddressingFunctionTag::try_from(byte) {
815 assert_eq!(v as u8, byte, "round-trip failed for {byte:#04x}");
816 matched += 1;
817 }
818 }
819 assert_eq!(matched, 14, "expected 14 matched variants");
820 }
821
822 #[test]
823 fn address_function_tag_display() {
824 assert_eq!(AddressingFunctionTag::AcePapr.to_string(), "AcePapr");
825 }
826
827 #[test]
828 fn parse_empty_data_loop() {
829 let buf = [0x00u8, 0x00];
830 let result = IndividualAddressingPayload::parse(&buf).unwrap();
831 assert_eq!(result.rfu, 0x00);
832 assert!(result.transmitters.is_empty());
833 }
834
835 #[test]
836 fn parse_preserves_rfu_byte() {
837 let buf = [0xFFu8, 0x00];
838 let result = IndividualAddressingPayload::parse(&buf).unwrap();
839 assert_eq!(result.rfu, 0xFF);
840 assert!(result.transmitters.is_empty());
841 }
842
843 #[test]
844 fn parse_rejects_short_buffer() {
845 assert!(IndividualAddressingPayload::parse(&[0x00]).is_err());
846 }
847
848 #[test]
849 fn parse_rejects_truncated_data() {
850 assert!(IndividualAddressingPayload::parse(&[0x00, 0x04, 0xAA, 0xBB]).is_err());
851 }
852
853 #[test]
854 fn parse_single_transmitter_single_function_ace_papr() {
855 let func_body = [0xA8u8, 0x54];
856 let func_loop_len = (FUNC_HEADER_LEN + func_body.len()) as u8;
857 let tx_loop = [
858 0x00,
859 0x05,
860 func_loop_len,
861 0x10,
862 ACE_PAPR_BODY_LEN as u8,
863 func_body[0],
864 func_body[1],
865 ];
866 let mut buf = vec![0x00u8, tx_loop.len() as u8];
867 buf.extend_from_slice(&tx_loop);
868
869 let result = IndividualAddressingPayload::parse(&buf).unwrap();
870 assert_eq!(result.transmitters.len(), 1);
871 assert_eq!(result.transmitters[0].transmitter_id, 0x0005);
872 assert_eq!(result.transmitters[0].functions.len(), 1);
873
874 let func = &result.transmitters[0].functions[0];
875 assert_eq!(func.tag, 0x10);
876 assert_eq!(func.addressing_tag(), Some(AddressingFunctionTag::AcePapr));
877
878 match &func.body {
879 FunctionBody::AcePapr(body) => {
880 assert_eq!(body.ace_gain, 0x15);
881 assert_eq!(body.ace_maximal_extension, 0x00);
882 assert_eq!(body.ace_clipping_threshold, 0x2A);
883 assert!(!body.rfu);
884 }
885 other => panic!("expected AcePapr, got {other:?}"),
886 }
887 }
888
889 #[test]
890 fn parse_single_transmitter_single_function_miso_group() {
891 let func_body = [0x85u8];
892 let func_loop_len = (FUNC_HEADER_LEN + func_body.len()) as u8;
893 let tx_loop = [
894 0x00,
895 0x0A,
896 func_loop_len,
897 0x11,
898 MISO_GROUP_BODY_LEN as u8,
899 func_body[0],
900 ];
901 let mut buf = vec![0x00u8, tx_loop.len() as u8];
902 buf.extend_from_slice(&tx_loop);
903
904 let result = IndividualAddressingPayload::parse(&buf).unwrap();
905 assert_eq!(result.transmitters.len(), 1);
906 assert_eq!(result.transmitters[0].transmitter_id, 0x000A);
907
908 let func = &result.transmitters[0].functions[0];
909 assert_eq!(func.tag, 0x11);
910 match &func.body {
911 FunctionBody::MisoGroup(body) => {
912 assert!(body.miso_group);
913 assert_eq!(body.rfu, 0x05);
914 }
915 other => panic!("expected MisoGroup, got {other:?}"),
916 }
917 }
918
919 #[test]
920 fn parse_single_transmitter_single_function_frequency() {
921 let freq_body = FrequencyBody {
922 rf_idx: 3,
923 frequency: 0x80000000,
924 rfu: 0,
925 };
926 let mut func_bytes = [0u8; FREQUENCY_BODY_LEN];
927 serialize_frequency(&freq_body, &mut func_bytes);
928
929 let func_loop_len = (FUNC_HEADER_LEN + func_bytes.len()) as u8;
930 let mut tx_loop = vec![0x00, 0x07, func_loop_len, 0x17, FREQUENCY_BODY_LEN as u8];
931 tx_loop.extend_from_slice(&func_bytes);
932
933 let mut buf = vec![0x00u8, tx_loop.len() as u8];
934 buf.extend_from_slice(&tx_loop);
935
936 let result = IndividualAddressingPayload::parse(&buf).unwrap();
937 let func = &result.transmitters[0].functions[0];
938 assert_eq!(func.tag, 0x17);
939 match &func.body {
940 FunctionBody::Frequency(body) => {
941 assert_eq!(body.rf_idx, 3);
942 assert_eq!(body.frequency, 0x80000000);
943 assert_eq!(body.rfu, 0);
944 }
945 other => panic!("expected Frequency, got {other:?}"),
946 }
947 }
948
949 #[test]
950 fn parse_unknown_tag_produces_raw_body() {
951 let func_body = [0xDE, 0xAD, 0xBE];
952 let func_loop_len = (FUNC_HEADER_LEN + func_body.len()) as u8;
953 let tx_loop = [
954 0x00,
955 0x01,
956 func_loop_len,
957 0x14,
958 func_body.len() as u8,
959 func_body[0],
960 func_body[1],
961 func_body[2],
962 ];
963 let mut buf = vec![0x00u8, tx_loop.len() as u8];
964 buf.extend_from_slice(&tx_loop);
965
966 let result = IndividualAddressingPayload::parse(&buf).unwrap();
967 let func = &result.transmitters[0].functions[0];
968 assert_eq!(func.tag, 0x14);
969 assert_eq!(func.addressing_tag(), None);
970 match &func.body {
971 FunctionBody::Raw(bytes) => assert_eq!(*bytes, &[0xDE, 0xAD, 0xBE]),
972 other => panic!("expected Raw, got {other:?}"),
973 }
974 }
975
976 #[test]
977 fn parse_known_tag_wrong_length_falls_back_to_raw() {
978 let func_body = [0xAA, 0xBB, 0xCC];
979 let func_loop_len = (FUNC_HEADER_LEN + func_body.len()) as u8;
980 let tx_loop = [
981 0x00,
982 0x01,
983 func_loop_len,
984 0x10,
985 func_body.len() as u8,
986 func_body[0],
987 func_body[1],
988 func_body[2],
989 ];
990 let mut buf = vec![0x00u8, tx_loop.len() as u8];
991 buf.extend_from_slice(&tx_loop);
992
993 let result = IndividualAddressingPayload::parse(&buf).unwrap();
994 let func = &result.transmitters[0].functions[0];
995 assert_eq!(func.tag, 0x10);
996 assert_eq!(func.addressing_tag(), Some(AddressingFunctionTag::AcePapr));
997 match &func.body {
998 FunctionBody::Raw(bytes) => assert_eq!(*bytes, &[0xAA, 0xBB, 0xCC]),
999 other => panic!("expected Raw fallback for length mismatch, got {other:?}"),
1000 }
1001 }
1002
1003 #[test]
1004 fn parse_truncated_transmitter_header() {
1005 let buf = [0x00u8, 0x02, 0x00];
1006 assert!(IndividualAddressingPayload::parse(&buf).is_err());
1007 }
1008
1009 #[test]
1010 fn parse_truncated_function_header() {
1011 let tx_loop = [0x00, 0x01, 0x02, 0x10];
1012 let buf = [0x00u8, tx_loop.len() as u8, 0x00, 0x01, 0x02, 0x10];
1013 assert!(IndividualAddressingPayload::parse(&buf).is_err());
1014 }
1015
1016 #[test]
1017 fn parse_function_body_exceeds_function_loop() {
1018 let tx_loop = [0x00, 0x01, 0x04, 0x10, 0xFF, 0xAA, 0xBB];
1019 let mut buf = vec![0x00u8, tx_loop.len() as u8];
1020 buf.extend_from_slice(&tx_loop);
1021 assert!(IndividualAddressingPayload::parse(&buf).is_err());
1022 }
1023
1024 #[test]
1025 fn round_trip_two_transmitters_mixed_bodies() {
1026 let ace_body = AcePaprBody {
1027 ace_gain: 0x0A,
1028 ace_maximal_extension: 0x03,
1029 ace_clipping_threshold: 0x5A,
1030 rfu: true,
1031 };
1032 let raw_body: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
1033
1034 let orig = IndividualAddressingPayload {
1035 rfu: 0xAB,
1036 transmitters: vec![
1037 TransmitterEntry {
1038 transmitter_id: 0x0005,
1039 functions: vec![
1040 FunctionEntry {
1041 tag: 0x10,
1042 body: FunctionBody::AcePapr(ace_body.clone()),
1043 },
1044 FunctionEntry {
1045 tag: 0x14,
1046 body: FunctionBody::Raw(raw_body),
1047 },
1048 ],
1049 },
1050 TransmitterEntry {
1051 transmitter_id: 0x00FF,
1052 functions: vec![FunctionEntry {
1053 tag: 0x11,
1054 body: FunctionBody::MisoGroup(MisoGroupBody {
1055 miso_group: true,
1056 rfu: 0x42,
1057 }),
1058 }],
1059 },
1060 ],
1061 };
1062
1063 let mut buf = vec![0u8; orig.serialized_len()];
1064 orig.serialize_into(&mut buf).unwrap();
1065
1066 assert_eq!(buf[0], 0xAB);
1067
1068 let parsed = IndividualAddressingPayload::parse(&buf).unwrap();
1069 assert_eq!(orig, parsed);
1070 }
1071
1072 #[test]
1073 fn round_trip_all_typed_bodies() {
1074 let orig = IndividualAddressingPayload {
1075 rfu: 0x00,
1076 transmitters: vec![
1077 TransmitterEntry {
1078 transmitter_id: 0x0001,
1079 functions: vec![
1080 FunctionEntry {
1081 tag: 0x10,
1082 body: FunctionBody::AcePapr(AcePaprBody {
1083 ace_gain: 0x1F,
1084 ace_maximal_extension: 0x07,
1085 ace_clipping_threshold: 0x7F,
1086 rfu: true,
1087 }),
1088 },
1089 FunctionEntry {
1090 tag: 0x11,
1091 body: FunctionBody::MisoGroup(MisoGroupBody {
1092 miso_group: false,
1093 rfu: 0x7F,
1094 }),
1095 },
1096 FunctionEntry {
1097 tag: 0x12,
1098 body: FunctionBody::TrPapr(TrPaprBody {
1099 rfu1: 0x0A,
1100 tr_clipping_threshold: 0xABC,
1101 rfu2: 0x1FFF,
1102 number_of_iterations: 0x1FF,
1103 }),
1104 },
1105 ],
1106 },
1107 TransmitterEntry {
1108 transmitter_id: 0x0002,
1109 functions: vec![
1110 FunctionEntry {
1111 tag: 0x13,
1112 body: FunctionBody::L1AcePapr(L1AcePaprBody {
1113 l1_ace_max_correction: 0x1234,
1114 rfu: 0x5678,
1115 }),
1116 },
1117 FunctionEntry {
1118 tag: 0x15,
1119 body: FunctionBody::TxSigFefSeqNum(TxSigFefSeqNumBody {
1120 rfu1: 0x1F,
1121 seq_num_1: 0x07,
1122 rfu2: 0x00,
1123 seq_num_2: 0x05,
1124 rfu3: 0xABCDEF,
1125 }),
1126 },
1127 FunctionEntry {
1128 tag: 0x16,
1129 body: FunctionBody::TxSigAuxStreamTxId(TxSigAuxStreamTxIdBody {
1130 tx_sig_aux_tx_id: 0xFFF,
1131 rfu: 0x0000F,
1132 }),
1133 },
1134 FunctionEntry {
1135 tag: 0x17,
1136 body: FunctionBody::Frequency(FrequencyBody {
1137 rf_idx: 0x05,
1138 frequency: 0x87654321,
1139 rfu: 0x1F,
1140 }),
1141 },
1142 ],
1143 },
1144 ],
1145 };
1146
1147 let mut buf = vec![0u8; orig.serialized_len()];
1148 orig.serialize_into(&mut buf).unwrap();
1149 let parsed = IndividualAddressingPayload::parse(&buf).unwrap();
1150 assert_eq!(orig, parsed);
1151 }
1152
1153 #[test]
1154 fn serialize_empty_data() {
1155 let orig = IndividualAddressingPayload {
1156 rfu: 0x00,
1157 transmitters: vec![],
1158 };
1159 let mut buf = vec![0u8; orig.serialized_len()];
1160 orig.serialize_into(&mut buf).unwrap();
1161 assert_eq!(buf, [0x00, 0x00]);
1162 }
1163
1164 #[test]
1165 fn serialize_detects_data_loop_overflow() {
1166 let mut functions = Vec::new();
1167 for _ in 0..100 {
1168 functions.push(FunctionEntry {
1169 tag: AddressingFunctionTag::MisoGroup as u8,
1170 body: FunctionBody::MisoGroup(MisoGroupBody {
1171 miso_group: false,
1172 rfu: 0,
1173 }),
1174 });
1175 }
1176 let payload = IndividualAddressingPayload {
1177 rfu: 0,
1178 transmitters: vec![TransmitterEntry {
1179 transmitter_id: 0,
1180 functions,
1181 }],
1182 };
1183 let mut buf = vec![0u8; payload.serialized_len()];
1184 let result = payload.serialize_into(&mut buf);
1185 assert!(
1186 matches!(
1187 result.unwrap_err(),
1188 crate::Error::ReservedBitsViolation { .. }
1189 ),
1190 "expected ReservedBitsViolation for overflowing length field"
1191 );
1192 }
1193
1194 #[test]
1195 fn body_parse_round_trip_ace_papr() {
1196 let body = AcePaprBody {
1197 ace_gain: 0x0A,
1198 ace_maximal_extension: 0x05,
1199 ace_clipping_threshold: 0x41,
1200 rfu: true,
1201 };
1202 let mut buf = [0u8; ACE_PAPR_BODY_LEN];
1203 serialize_ace_papr(&body, &mut buf);
1204 let parsed = parse_ace_papr(&buf).unwrap();
1205 assert_eq!(body, parsed);
1206 }
1207
1208 #[test]
1209 fn body_parse_round_trip_tr_papr() {
1210 let body = TrPaprBody {
1211 rfu1: 0x0C,
1212 tr_clipping_threshold: 0xFFF,
1213 rfu2: 0x0ABC,
1214 number_of_iterations: 0x3FF,
1215 };
1216 let mut buf = [0u8; TR_PAPR_BODY_LEN];
1217 serialize_tr_papr(&body, &mut buf);
1218 let parsed = parse_tr_papr(&buf).unwrap();
1219 assert_eq!(body, parsed);
1220 }
1221
1222 #[test]
1223 fn body_parse_round_trip_frequency() {
1224 let body = FrequencyBody {
1225 rf_idx: 0x07,
1226 frequency: 0xFFFFFFFF,
1227 rfu: 0x1F,
1228 };
1229 let mut buf = [0u8; FREQUENCY_BODY_LEN];
1230 serialize_frequency(&body, &mut buf);
1231 let parsed = parse_frequency(&buf).unwrap();
1232 assert_eq!(body, parsed);
1233 }
1234
1235 #[test]
1236 fn body_parse_round_trip_tx_sig_aux_stream_tx_id() {
1237 let body = TxSigAuxStreamTxIdBody {
1238 tx_sig_aux_tx_id: 0xFFF,
1239 rfu: 0xFFFFF,
1240 };
1241 let mut buf = [0u8; TX_SIG_AUX_STREAM_TX_ID_BODY_LEN];
1242 serialize_tx_sig_aux_stream_tx_id(&body, &mut buf);
1243 let parsed = parse_tx_sig_aux_stream_tx_id(&buf).unwrap();
1244 assert_eq!(body, parsed);
1245 }
1246
1247 #[test]
1248 fn function_entry_addressing_tag_method() {
1249 let entry_known = FunctionEntry {
1250 tag: 0x10,
1251 body: FunctionBody::AcePapr(AcePaprBody {
1252 ace_gain: 0,
1253 ace_maximal_extension: 0,
1254 ace_clipping_threshold: 0,
1255 rfu: false,
1256 }),
1257 };
1258 assert_eq!(
1259 entry_known.addressing_tag(),
1260 Some(AddressingFunctionTag::AcePapr)
1261 );
1262
1263 let entry_unknown = FunctionEntry {
1264 tag: 0xFF,
1265 body: FunctionBody::Raw(&[]),
1266 };
1267 assert_eq!(entry_unknown.addressing_tag(), None);
1268 }
1269}