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))]
15#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
16pub struct BbframePayload<'a> {
17    /// FRAME_IDX of first T2 frame the IF is mapped to.
18    pub frame_idx: u8,
19    /// PLP_ID per EN 302 755.
20    pub plp_id: u8,
21    /// `true` = first BBFRAME of an interleaving frame.
22    pub intl_frame_start: bool,
23    /// The raw BBFrame data (Kbch bits = bytes when aligned).
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<'a> crate::traits::PayloadDef<'a> for BbframePayload<'a> {
63    const PACKET_TYPE: u8 = 0x00;
64    const NAME: &'static str = "BBFRAME";
65}
66
67impl Serialize for BbframePayload<'_> {
68    type Error = crate::error::Error;
69
70    fn serialized_len(&self) -> usize {
71        3 + self.bbframe.len()
72    }
73
74    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
75        if buf.len() < self.serialized_len() {
76            return Err(crate::Error::OutputBufferTooSmall {
77                need: self.serialized_len(),
78                have: buf.len(),
79            });
80        }
81
82        buf[0] = self.frame_idx;
83        buf[1] = self.plp_id;
84        buf[2] = if self.intl_frame_start { 0x80 } else { 0x00 };
85        if !self.bbframe.is_empty() {
86            buf[3..3 + self.bbframe.len()].copy_from_slice(self.bbframe);
87        }
88
89        Ok(self.serialized_len())
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn parse_extracts_frame_idx_and_plp_id() {
99        let buf = [0x42u8, 0x05, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
100        let result = BbframePayload::parse(&buf).unwrap();
101        assert_eq!(result.frame_idx, 0x42);
102        assert_eq!(result.plp_id, 0x05);
103        assert_eq!(result.bbframe, &[0xDE, 0xAD, 0xBE, 0xEF]);
104    }
105
106    #[test]
107    fn parse_extracts_intl_frame_start_flag() {
108        let buf = [0x00u8, 0x00, 0x80];
109        let result = BbframePayload::parse(&buf).unwrap();
110        assert!(result.intl_frame_start);
111
112        let buf2 = [0x00u8, 0x00, 0x00];
113        let result2 = BbframePayload::parse(&buf2).unwrap();
114        assert!(!result2.intl_frame_start);
115    }
116
117    #[test]
118    fn parse_rejects_nonzero_rfu_bits() {
119        let buf = [0x00u8, 0x00, 0x40];
120        let result = BbframePayload::parse(&buf);
121        assert!(result.is_err());
122        assert!(matches!(
123            result.unwrap_err(),
124            crate::Error::ReservedBitsViolation { .. }
125        ));
126    }
127
128    #[test]
129    fn parse_bbframe_body_slices_from_offset_3() {
130        let body: Vec<u8> = (0..100).collect();
131        let mut buf = vec![0x0Fu8, 0x01, 0x00];
132        buf.extend_from_slice(&body);
133        let result = BbframePayload::parse(&buf).unwrap();
134        assert_eq!(result.bbframe, body.as_slice());
135    }
136
137    #[test]
138    fn parse_rejects_body_shorter_than_3_bytes() {
139        assert!(BbframePayload::parse(&[0x00, 0x00]).is_err());
140        assert!(BbframePayload::parse(&[]).is_err());
141    }
142
143    #[test]
144    fn serialize_round_trip_preserves_all_fields() {
145        let orig = BbframePayload {
146            frame_idx: 0xAB,
147            plp_id: 0x07,
148            intl_frame_start: true,
149            bbframe: &[0xCA, 0xFE, 0xBA, 0xBE],
150        };
151        let mut buf = vec![0u8; orig.serialized_len()];
152        orig.serialize_into(&mut buf).unwrap();
153        let parsed = BbframePayload::parse(&buf).unwrap();
154        assert_eq!(orig, parsed);
155    }
156
157    #[test]
158    fn serialize_zeros_rfu_bits() {
159        let payload = BbframePayload {
160            frame_idx: 0x00,
161            plp_id: 0x00,
162            intl_frame_start: false,
163            bbframe: &[],
164        };
165        let mut buf = [0xFFu8; 3];
166        payload.serialize_into(&mut buf).unwrap();
167        assert_eq!(buf[0], 0x00);
168        assert_eq!(buf[1], 0x00);
169        assert_eq!(buf[2] & 0x7F, 0x00);
170    }
171}