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