satrs/encoding/
ccsds.rs

1use spacepackets::{CcsdsPacket, SpHeader};
2
3use crate::{tmtc::PacketSenderRaw, ComponentId};
4
5#[derive(Debug, Copy, Clone, PartialEq, Eq)]
6pub enum SpValidity {
7    Valid,
8    /// The space packet can be assumed to have a valid format, but the packet should
9    /// be skipped.
10    Skip,
11    /// The space packet or space packet header has an invalid format, for example a CRC check
12    /// failed. In that case, the parser loses the packet synchronization and needs to check for
13    /// the start of a new space packet header start again. The space packet header
14    /// [spacepackets::PacketId] can be used as a synchronization marker to detect the start
15    /// of a possible valid packet again.
16    Invalid,
17}
18
19/// Simple trait to allow user code to check the validity of a space packet.
20pub trait SpacePacketValidator {
21    fn validate(&self, sp_header: &SpHeader, raw_buf: &[u8]) -> SpValidity;
22}
23
24#[derive(Default, Debug, PartialEq, Eq)]
25pub struct ParseResult {
26    pub packets_found: u32,
27    /// If an incomplete space packet was found, its start index is indicated by this value.
28    pub incomplete_tail_start: Option<usize>,
29}
30
31/// This function parses a given buffer for tightly packed CCSDS space packets. It uses the
32/// [spacepackets::SpHeader] of the CCSDS packets and a user provided [SpacePacketValidator]
33/// to check whether a received space packet is relevant for processing.
34///
35/// This function is also able to deal with broken tail packets at the end as long a the parser
36/// can read the full 7 bytes which constitue a space packet header plus one byte minimal size.
37/// If broken tail packets are detected, they are moved to the front of the buffer, and the write
38/// index for future write operations will be written to the `next_write_idx` argument.
39///
40/// The parses will behave differently based on the [SpValidity] returned from the user provided
41/// [SpacePacketValidator]:
42///
43///  1. [SpValidity::Valid]: The parser will forward all packets to the given `packet_sender` and
44///     return the number of packets found.If the [PacketSenderRaw::send_packet] calls fails, the
45///     error will be returned.
46///  2. [SpValidity::Invalid]: The parser assumes that the synchronization is lost and tries to
47///     find the start of a new space packet header by scanning all the following bytes.
48///  3. [SpValidity::Skip]: The parser skips the packet using the packet length determined from the
49///     space packet header.
50pub fn parse_buffer_for_ccsds_space_packets<SendError>(
51    buf: &[u8],
52    packet_validator: &(impl SpacePacketValidator + ?Sized),
53    sender_id: ComponentId,
54    packet_sender: &(impl PacketSenderRaw<Error = SendError> + ?Sized),
55) -> Result<ParseResult, SendError> {
56    let mut parse_result = ParseResult::default();
57    let mut current_idx = 0;
58    let buf_len = buf.len();
59    loop {
60        if current_idx + 7 > buf.len() {
61            break;
62        }
63        let sp_header = SpHeader::from_be_bytes(&buf[current_idx..]).unwrap().0;
64        match packet_validator.validate(&sp_header, &buf[current_idx..]) {
65            SpValidity::Valid => {
66                let packet_size = sp_header.total_len();
67                if (current_idx + packet_size) <= buf_len {
68                    packet_sender
69                        .send_packet(sender_id, &buf[current_idx..current_idx + packet_size])?;
70                    parse_result.packets_found += 1;
71                } else {
72                    // Move packet to start of buffer if applicable.
73                    parse_result.incomplete_tail_start = Some(current_idx);
74                }
75                current_idx += packet_size;
76                continue;
77            }
78            SpValidity::Skip => {
79                current_idx += sp_header.total_len();
80            }
81            // We might have lost sync. Try to find the start of a new space packet header.
82            SpValidity::Invalid => {
83                current_idx += 1;
84            }
85        }
86    }
87    Ok(parse_result)
88}
89
90#[cfg(test)]
91mod tests {
92    use spacepackets::{
93        ecss::{tc::PusTcCreator, WritablePusPacket},
94        CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType, SequenceFlags, SpHeader,
95    };
96
97    use crate::{encoding::tests::TcCacher, ComponentId};
98
99    use super::{parse_buffer_for_ccsds_space_packets, SpValidity, SpacePacketValidator};
100
101    const PARSER_ID: ComponentId = 0x05;
102    const TEST_APID_0: u16 = 0x02;
103    const TEST_APID_1: u16 = 0x10;
104    const TEST_PACKET_ID_0: PacketId = PacketId::new_for_tc(true, TEST_APID_0);
105    const TEST_PACKET_ID_1: PacketId = PacketId::new_for_tc(true, TEST_APID_1);
106
107    #[derive(Default)]
108    struct SimpleVerificator {
109        pub enable_second_id: bool,
110    }
111
112    impl SimpleVerificator {
113        pub fn new_with_second_id() -> Self {
114            Self {
115                enable_second_id: true,
116            }
117        }
118    }
119
120    impl SpacePacketValidator for SimpleVerificator {
121        fn validate(&self, sp_header: &SpHeader, _raw_buf: &[u8]) -> super::SpValidity {
122            if sp_header.packet_id() == TEST_PACKET_ID_0
123                || (self.enable_second_id && sp_header.packet_id() == TEST_PACKET_ID_1)
124            {
125                return SpValidity::Valid;
126            }
127            SpValidity::Skip
128        }
129    }
130
131    #[test]
132    fn test_basic() {
133        let sph = SpHeader::new_from_apid(TEST_APID_0);
134        let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
135        let mut buffer: [u8; 32] = [0; 32];
136        let packet_len = ping_tc
137            .write_to_bytes(&mut buffer)
138            .expect("writing packet failed");
139        let tc_cacher = TcCacher::default();
140        let parse_result = parse_buffer_for_ccsds_space_packets(
141            &buffer,
142            &SimpleVerificator::default(),
143            PARSER_ID,
144            &tc_cacher,
145        );
146        assert!(parse_result.is_ok());
147        let parse_result = parse_result.unwrap();
148        assert_eq!(parse_result.packets_found, 1);
149        let mut queue = tc_cacher.tc_queue.borrow_mut();
150        assert_eq!(queue.len(), 1);
151        let packet_with_sender = queue.pop_front().unwrap();
152        assert_eq!(packet_with_sender.packet, buffer[..packet_len]);
153        assert_eq!(packet_with_sender.sender_id, PARSER_ID);
154    }
155
156    #[test]
157    fn test_multi_packet() {
158        let sph = SpHeader::new_from_apid(TEST_APID_0);
159        let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
160        let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
161        let mut buffer: [u8; 32] = [0; 32];
162        let packet_len_ping = ping_tc
163            .write_to_bytes(&mut buffer)
164            .expect("writing packet failed");
165        let packet_len_action = action_tc
166            .write_to_bytes(&mut buffer[packet_len_ping..])
167            .expect("writing packet failed");
168        let tc_cacher = TcCacher::default();
169        let parse_result = parse_buffer_for_ccsds_space_packets(
170            &buffer,
171            &SimpleVerificator::default(),
172            PARSER_ID,
173            &tc_cacher,
174        );
175        assert!(parse_result.is_ok());
176        let parse_result = parse_result.unwrap();
177        assert_eq!(parse_result.packets_found, 2);
178        let mut queue = tc_cacher.tc_queue.borrow_mut();
179        assert_eq!(queue.len(), 2);
180        let packet_with_addr = queue.pop_front().unwrap();
181        assert_eq!(packet_with_addr.packet, buffer[..packet_len_ping]);
182        assert_eq!(packet_with_addr.sender_id, PARSER_ID);
183        let packet_with_addr = queue.pop_front().unwrap();
184        assert_eq!(packet_with_addr.sender_id, PARSER_ID);
185        assert_eq!(
186            packet_with_addr.packet,
187            buffer[packet_len_ping..packet_len_ping + packet_len_action]
188        );
189    }
190
191    #[test]
192    fn test_multi_apid() {
193        let sph = SpHeader::new_from_apid(TEST_APID_0);
194        let ping_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true);
195        let sph = SpHeader::new_from_apid(TEST_APID_1);
196        let action_tc = PusTcCreator::new_simple(sph, 8, 0, &[], true);
197        let mut buffer: [u8; 32] = [0; 32];
198        let packet_len_ping = ping_tc
199            .write_to_bytes(&mut buffer)
200            .expect("writing packet failed");
201        let packet_len_action = action_tc
202            .write_to_bytes(&mut buffer[packet_len_ping..])
203            .expect("writing packet failed");
204        let tc_cacher = TcCacher::default();
205        let verificator = SimpleVerificator::new_with_second_id();
206        let parse_result =
207            parse_buffer_for_ccsds_space_packets(&buffer, &verificator, PARSER_ID, &tc_cacher);
208        assert!(parse_result.is_ok());
209        let parse_result = parse_result.unwrap();
210        assert_eq!(parse_result.packets_found, 2);
211        let mut queue = tc_cacher.tc_queue.borrow_mut();
212        assert_eq!(queue.len(), 2);
213        let packet_with_addr = queue.pop_front().unwrap();
214        assert_eq!(packet_with_addr.packet, buffer[..packet_len_ping]);
215        let packet_with_addr = queue.pop_front().unwrap();
216        assert_eq!(
217            packet_with_addr.packet,
218            buffer[packet_len_ping..packet_len_ping + packet_len_action]
219        );
220    }
221
222    #[test]
223    fn test_split_packet_multi() {
224        let ping_tc =
225            PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
226        let action_tc =
227            PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_1), 8, 0, &[], true);
228        let mut buffer: [u8; 32] = [0; 32];
229        let packet_len_ping = ping_tc
230            .write_to_bytes(&mut buffer)
231            .expect("writing packet failed");
232        let packet_len_action = action_tc
233            .write_to_bytes(&mut buffer[packet_len_ping..])
234            .expect("writing packet failed");
235        let tc_cacher = TcCacher::default();
236        let verificator = SimpleVerificator::new_with_second_id();
237        let parse_result = parse_buffer_for_ccsds_space_packets(
238            &buffer[..packet_len_ping + packet_len_action - 4],
239            &verificator,
240            PARSER_ID,
241            &tc_cacher,
242        );
243        assert!(parse_result.is_ok());
244        let parse_result = parse_result.unwrap();
245        assert_eq!(parse_result.packets_found, 1);
246        assert!(parse_result.incomplete_tail_start.is_some());
247        let incomplete_tail_idx = parse_result.incomplete_tail_start.unwrap();
248        assert_eq!(incomplete_tail_idx, packet_len_ping);
249
250        let queue = tc_cacher.tc_queue.borrow();
251        assert_eq!(queue.len(), 1);
252        // The broken packet was moved to the start, so the next write index should be after the
253        // last segment missing 4 bytes.
254    }
255
256    #[test]
257    fn test_one_split_packet() {
258        let ping_tc =
259            PusTcCreator::new_simple(SpHeader::new_from_apid(TEST_APID_0), 17, 1, &[], true);
260        let mut buffer: [u8; 32] = [0; 32];
261        let packet_len_ping = ping_tc
262            .write_to_bytes(&mut buffer)
263            .expect("writing packet failed");
264        let tc_cacher = TcCacher::default();
265
266        let verificator = SimpleVerificator::new_with_second_id();
267        let parse_result = parse_buffer_for_ccsds_space_packets(
268            &buffer[..packet_len_ping - 4],
269            &verificator,
270            PARSER_ID,
271            &tc_cacher,
272        );
273        assert!(parse_result.is_ok());
274        let parse_result = parse_result.unwrap();
275        assert_eq!(parse_result.packets_found, 0);
276        let queue = tc_cacher.tc_queue.borrow();
277        assert_eq!(queue.len(), 0);
278    }
279
280    #[test]
281    fn test_smallest_packet() {
282        let ccsds_header_only = SpHeader::new(
283            PacketId::new(PacketType::Tc, true, TEST_APID_0),
284            PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0),
285            0,
286        );
287        let mut buf: [u8; 7] = [0; 7];
288        ccsds_header_only
289            .write_to_be_bytes(&mut buf)
290            .expect("writing failed");
291        let verificator = SimpleVerificator::default();
292        let tc_cacher = TcCacher::default();
293        let parse_result =
294            parse_buffer_for_ccsds_space_packets(&buf, &verificator, PARSER_ID, &tc_cacher);
295        assert!(parse_result.is_ok());
296        let parse_result = parse_result.unwrap();
297        assert_eq!(parse_result.packets_found, 1);
298    }
299}