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, serde::Deserialize))]
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    #[cfg_attr(feature = "serde", serde(borrow))]
24    pub bbframe: &'a [u8],
25}
26
27impl<'a> Parse<'a> for BbframePayload<'a> {
28    type Error = crate::error::Error;
29
30    fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
31        if bytes.len() < 3 {
32            return Err(crate::Error::BufferTooShort {
33                need: 3,
34                have: bytes.len(),
35                what: "BbframePayload header (frame_idx + plp_id + intl_frame_start + rfu)",
36            });
37        }
38
39        let frame_idx = bytes[0];
40        let plp_id = bytes[1];
41        let intl_frame_start = bytes[2] & 0x80 != 0;
42
43        // RFU: byte 2 bits 6..0 — must be zero
44        if bytes[2] & 0x7F != 0 {
45            return Err(crate::Error::ReservedBitsViolation {
46                field: "byte 2 bits 6..0",
47                reason: "RFU must be zero (ETSI TS 102 773 §5.2.1)",
48            });
49        }
50
51        let bbframe = &bytes[3..];
52
53        Ok(BbframePayload {
54            frame_idx,
55            plp_id,
56            intl_frame_start,
57            bbframe,
58        })
59    }
60}
61
62impl Serialize for BbframePayload<'_> {
63    type Error = crate::error::Error;
64
65    fn serialized_len(&self) -> usize {
66        3 + self.bbframe.len()
67    }
68
69    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
70        if buf.len() < self.serialized_len() {
71            return Err(crate::Error::OutputBufferTooSmall {
72                need: self.serialized_len(),
73                have: buf.len(),
74            });
75        }
76
77        buf[0] = self.frame_idx;
78        buf[1] = self.plp_id;
79        buf[2] = if self.intl_frame_start { 0x80 } else { 0x00 };
80        if !self.bbframe.is_empty() {
81            buf[3..3 + self.bbframe.len()].copy_from_slice(self.bbframe);
82        }
83
84        Ok(self.serialized_len())
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn parse_extracts_frame_idx_and_plp_id() {
94        let buf = [0x42u8, 0x05, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
95        let result = BbframePayload::parse(&buf).unwrap();
96        assert_eq!(result.frame_idx, 0x42);
97        assert_eq!(result.plp_id, 0x05);
98        assert_eq!(result.bbframe, &[0xDE, 0xAD, 0xBE, 0xEF]);
99    }
100
101    #[test]
102    fn parse_extracts_intl_frame_start_flag() {
103        let buf = [0x00u8, 0x00, 0x80];
104        let result = BbframePayload::parse(&buf).unwrap();
105        assert!(result.intl_frame_start);
106
107        let buf2 = [0x00u8, 0x00, 0x00];
108        let result2 = BbframePayload::parse(&buf2).unwrap();
109        assert!(!result2.intl_frame_start);
110    }
111
112    #[test]
113    fn parse_rejects_nonzero_rfu_bits() {
114        let buf = [0x00u8, 0x00, 0x40];
115        let result = BbframePayload::parse(&buf);
116        assert!(result.is_err());
117        assert!(matches!(
118            result.unwrap_err(),
119            crate::Error::ReservedBitsViolation { .. }
120        ));
121    }
122
123    #[test]
124    fn parse_bbframe_body_slices_from_offset_3() {
125        let body: Vec<u8> = (0..100).collect();
126        let mut buf = vec![0x0Fu8, 0x01, 0x00];
127        buf.extend_from_slice(&body);
128        let result = BbframePayload::parse(&buf).unwrap();
129        assert_eq!(result.bbframe, body.as_slice());
130    }
131
132    #[test]
133    fn parse_rejects_body_shorter_than_3_bytes() {
134        assert!(BbframePayload::parse(&[0x00, 0x00]).is_err());
135        assert!(BbframePayload::parse(&[]).is_err());
136    }
137
138    #[test]
139    fn serialize_round_trip_preserves_all_fields() {
140        let orig = BbframePayload {
141            frame_idx: 0xAB,
142            plp_id: 0x07,
143            intl_frame_start: true,
144            bbframe: &[0xCA, 0xFE, 0xBA, 0xBE],
145        };
146        let mut buf = vec![0u8; orig.serialized_len()];
147        orig.serialize_into(&mut buf).unwrap();
148        let parsed = BbframePayload::parse(&buf).unwrap();
149        assert_eq!(orig, parsed);
150    }
151
152    #[test]
153    fn serialize_zeros_rfu_bits() {
154        let payload = BbframePayload {
155            frame_idx: 0x00,
156            plp_id: 0x00,
157            intl_frame_start: false,
158            bbframe: &[],
159        };
160        let mut buf = [0xFFu8; 3];
161        payload.serialize_into(&mut buf).unwrap();
162        assert_eq!(buf[0], 0x00);
163        assert_eq!(buf[1], 0x00);
164        assert_eq!(buf[2] & 0x7F, 0x00);
165    }
166}