Skip to main content

dvb_t2mi/payload/
bbframe.rs

1//! T2-MI payload type 0x00: Baseband Frame (BBFRAME) — §5.2.1.
2
3use dvb_common::{Parse, Serialize};
4
5/// BBFrame payload (type 0x00) per ETSI TS 102 773 §5.2.1.
6///
7/// Layout:
8/// - byte 0: frame_idx (8 bits) — FRAME_IDX of first T2 frame the IF is mapped to
9/// - byte 1: plp_id (8 bits) — PLP_ID per EN 302 755
10/// - byte 2 bit 7: intl_frame_start (1 bit) — 1 = first BBFRAME of an IF; 0 = subsequent
11/// - byte 2 bits 6..0: rfu (7 bits) — must be 0
12/// - bytes 3..: bbframe (Kbch bits) — encoded BBFRAME body
13#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15pub struct BbframePayload<'a> {
16    /// FRAME_IDX of first T2 frame the IF is mapped to.
17    pub frame_idx: u8,
18    /// PLP_ID per EN 302 755.
19    pub plp_id: u8,
20    /// `true` = first BBFRAME of an interleaving frame.
21    pub intl_frame_start: bool,
22    /// The raw BBFrame data (Kbch bits = bytes when aligned).
23    pub bbframe: &'a [u8],
24}
25
26impl<'a> Parse<'a> for BbframePayload<'a> {
27    type Error = crate::error::Error;
28
29    fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
30        if bytes.len() < 3 {
31            return Err(crate::Error::BufferTooShort {
32                need: 3,
33                have: bytes.len(),
34                what: "BbframePayload header (frame_idx + plp_id + intl_frame_start + rfu)",
35            });
36        }
37
38        let frame_idx = bytes[0];
39        let plp_id = bytes[1];
40        let intl_frame_start = bytes[2] & 0x80 != 0;
41
42        // RFU: byte 2 bits 6..0 — must be zero
43        if bytes[2] & 0x7F != 0 {
44            return Err(crate::Error::ReservedBitsViolation {
45                field: "byte 2 bits 6..0",
46                reason: "RFU must be zero (ETSI TS 102 773 §5.2.1)",
47            });
48        }
49
50        let bbframe = &bytes[3..];
51
52        Ok(BbframePayload {
53            frame_idx,
54            plp_id,
55            intl_frame_start,
56            bbframe,
57        })
58    }
59}
60
61impl<'a> crate::traits::PayloadDef<'a> for BbframePayload<'a> {
62    const PACKET_TYPE: u8 = 0x00;
63    const NAME: &'static str = "BBFRAME";
64}
65
66impl Serialize for BbframePayload<'_> {
67    type Error = crate::error::Error;
68
69    fn serialized_len(&self) -> usize {
70        3 + self.bbframe.len()
71    }
72
73    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
74        if buf.len() < self.serialized_len() {
75            return Err(crate::Error::OutputBufferTooSmall {
76                need: self.serialized_len(),
77                have: buf.len(),
78            });
79        }
80
81        buf[0] = self.frame_idx;
82        buf[1] = self.plp_id;
83        buf[2] = if self.intl_frame_start { 0x80 } else { 0x00 };
84        if !self.bbframe.is_empty() {
85            buf[3..3 + self.bbframe.len()].copy_from_slice(self.bbframe);
86        }
87
88        Ok(self.serialized_len())
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn parse_extracts_frame_idx_and_plp_id() {
98        let buf = [0x42u8, 0x05, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
99        let result = BbframePayload::parse(&buf).unwrap();
100        assert_eq!(result.frame_idx, 0x42);
101        assert_eq!(result.plp_id, 0x05);
102        assert_eq!(result.bbframe, &[0xDE, 0xAD, 0xBE, 0xEF]);
103    }
104
105    #[test]
106    fn parse_extracts_intl_frame_start_flag() {
107        let buf = [0x00u8, 0x00, 0x80];
108        let result = BbframePayload::parse(&buf).unwrap();
109        assert!(result.intl_frame_start);
110
111        let buf2 = [0x00u8, 0x00, 0x00];
112        let result2 = BbframePayload::parse(&buf2).unwrap();
113        assert!(!result2.intl_frame_start);
114    }
115
116    #[test]
117    fn parse_rejects_nonzero_rfu_bits() {
118        let buf = [0x00u8, 0x00, 0x40];
119        let result = BbframePayload::parse(&buf);
120        assert!(result.is_err());
121        assert!(matches!(
122            result.unwrap_err(),
123            crate::Error::ReservedBitsViolation { .. }
124        ));
125    }
126
127    #[test]
128    fn parse_bbframe_body_slices_from_offset_3() {
129        let body: Vec<u8> = (0..100).collect();
130        let mut buf = vec![0x0Fu8, 0x01, 0x00];
131        buf.extend_from_slice(&body);
132        let result = BbframePayload::parse(&buf).unwrap();
133        assert_eq!(result.bbframe, body.as_slice());
134    }
135
136    #[test]
137    fn parse_rejects_body_shorter_than_3_bytes() {
138        assert!(BbframePayload::parse(&[0x00, 0x00]).is_err());
139        assert!(BbframePayload::parse(&[]).is_err());
140    }
141
142    #[test]
143    fn serialize_round_trip_preserves_all_fields() {
144        let orig = BbframePayload {
145            frame_idx: 0xAB,
146            plp_id: 0x07,
147            intl_frame_start: true,
148            bbframe: &[0xCA, 0xFE, 0xBA, 0xBE],
149        };
150        let mut buf = vec![0u8; orig.serialized_len()];
151        orig.serialize_into(&mut buf).unwrap();
152        let parsed = BbframePayload::parse(&buf).unwrap();
153        assert_eq!(orig, parsed);
154    }
155
156    #[test]
157    fn serialize_zeros_rfu_bits() {
158        let payload = BbframePayload {
159            frame_idx: 0x00,
160            plp_id: 0x00,
161            intl_frame_start: false,
162            bbframe: &[],
163        };
164        let mut buf = [0xFFu8; 3];
165        payload.serialize_into(&mut buf).unwrap();
166        assert_eq!(buf[0], 0x00);
167        assert_eq!(buf[1], 0x00);
168        assert_eq!(buf[2] & 0x7F, 0x00);
169    }
170}