1use thiserror::Error;
35
36#[derive(Debug, PartialEq, Clone)]
37pub struct SpacePacket {
39 pub primary_header: PrimaryHeader,
41
42 pub payload: Vec<u8>
44}
45
46impl SpacePacket {
47 const DATA_LENGTH_SIZE: usize = 2;
49
50 const DATA_IDX: usize = 6;
52
53 pub fn new(
54 packet_type: PacketType,
55 secondary_header: bool,
56 apid: u16,
57 sequence_flag: SequenceFlag,
58 sequence_number: u16,
59 payload: Vec<u8>
60 ) -> Self {
61 assert!(payload.len() <= u16::MAX as usize, "user data must be less than 65536");
62 assert!(!payload.is_empty(), "user data cannot be left empty");
63 assert!(apid <= PrimaryHeader::APID_MASK, "application process ID is invalid");
64 assert!(sequence_number <= PrimaryHeader::SEQUENCE_NUMBER_MASK, "sequence number is invalid");
65
66 let primary_header = PrimaryHeader {
67 version: PrimaryHeader::VERSION,
68 packet_type,
69 secondary_header,
70 apid,
71 sequence_flag,
72 sequence_number,
73 };
74
75 Self { primary_header, payload }
76 }
77
78 pub fn encode(&self) -> Vec<u8> {
80 let mut encoded = self.primary_header.encode();
81 encoded.extend_from_slice(&u16::to_be_bytes((self.payload.len() - 1) as u16));
83 encoded.extend_from_slice(&self.payload);
84 encoded
85 }
86
87 pub fn decode(buf: &[u8]) -> Result<Self, Error> {
100 let primary_header = PrimaryHeader::decode(buf)?;
101
102 let data_len_bytes = buf
103 .get(PrimaryHeader::PRIMARY_HEADER_LEN..(PrimaryHeader::PRIMARY_HEADER_LEN + Self::DATA_LENGTH_SIZE))
104 .ok_or(Error::IncompleteHeader)?;
105
106 let payload_len = u16::from_be_bytes([data_len_bytes[0], data_len_bytes[1]]) + 1;
108
109 let payload = buf
110 .get(Self::DATA_IDX..(Self::DATA_IDX + payload_len as usize))
111 .ok_or(Error::InsufficientData { expected: payload_len as usize, found: buf[Self::DATA_IDX..].len() })?
112 .to_vec();
113
114 Ok( Self { primary_header, payload } )
115 }
116}
117
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121pub enum PacketType {
122 Telemetry = 0,
123 Telecommand = 1,
124}
125
126impl PacketType {
127 pub fn to_bits(&self) -> u16 {
130 match self {
131 Self::Telemetry => 0b0,
132 Self::Telecommand => 0b1,
133 }
134 }
135
136 pub fn from_bits(bits: u16) -> Self {
139 match bits & 0b1 {
140 0b0 => Self::Telemetry,
141 0b1 => Self::Telecommand,
142 _ => unreachable!()
143 }
144 }
145
146 pub fn is_telecommand(&self) -> bool {
148 matches!(self, Self::Telecommand)
149 }
150
151 pub fn is_telemetry(&self) -> bool {
153 matches!(self, Self::Telemetry)
154 }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub enum SequenceFlag {
161 Continuation = 0,
162 Start = 1,
163 End = 2,
164 Unsegmented = 3,
165}
166
167impl SequenceFlag {
168 pub fn to_bits(&self) -> u16 {
171 match self {
172 Self::Continuation => 0b00,
173 Self::Start => 0b01,
174 Self::End => 0b10,
175 Self::Unsegmented => 0b11,
176 }
177 }
178
179 pub fn from_bits(bits: u16) -> Self {
182 match bits & 0b11 {
183 0b00 => Self::Continuation,
184 0b01 => Self::Start,
185 0b10 => Self::End,
186 0b11 => Self::Unsegmented,
187 _ => unreachable!()
188 }
189 }
190
191 pub fn is_continuation(&self) -> bool {
193 matches!(self, Self::Continuation)
194 }
195
196 pub fn is_start(&self) -> bool {
198 matches!(self, Self::Start)
199 }
200
201 pub fn is_end(&self) -> bool {
203 matches!(self, Self::End)
204 }
205 pub fn is_unsegmented(&self) -> bool {
207 matches!(self, Self::Unsegmented)
208 }
209}
210
211#[derive(Debug, Clone, Copy, PartialEq, Eq)]
212pub struct PrimaryHeader {
238
239 pub version: u8,
241
242 pub packet_type: PacketType,
244
245 pub secondary_header: bool,
247
248 pub apid: u16,
250
251 pub sequence_flag: SequenceFlag,
253
254 pub sequence_number: u16,
256}
257
258impl PrimaryHeader {
259
260 const PRIMARY_HEADER_LEN: usize = 4;
262
263 const VERSION: u8 = 0b000;
265
266 const VERSION_SHIFT: usize = 13;
269 const PACKET_TYPE_SHIFT: usize = 12;
272 const SECONDARY_HEADER_SHIFT: usize = 11;
275 const APID_SHIFT: usize = 0;
278 const SEQUENCE_FLAG_SHIFT: usize = 14;
281
282 const PACKET_TYPE_MASK: u16 = 0x1000;
284 const SECONDARY_HEADER_MASK: u16 = 0x0800;
286 const APID_MASK: u16 = 0x03FF;
288 const SEQUENCE_FLAG_MASK: u16 = 0xC000;
290 const SEQUENCE_NUMBER_MASK: u16 = 0x3FFF;
292
293 pub fn encode(&self) -> Vec<u8> {
295 let packet_id =
296 u16::from(self.version) << Self::VERSION_SHIFT |
297 self.packet_type.to_bits() << Self::PACKET_TYPE_SHIFT |
298 u16::from(self.secondary_header) << Self::SECONDARY_HEADER_SHIFT |
299 self.apid & Self::APID_MASK << Self::APID_SHIFT;
300
301 let sequence_ctl =
302 self.sequence_flag.to_bits() << Self::SEQUENCE_FLAG_SHIFT |
303 self.sequence_number & Self::SEQUENCE_NUMBER_MASK;
304
305 let mut encoded = Vec::new();
306 encoded.extend_from_slice(&u16::to_be_bytes(packet_id));
307 encoded.extend_from_slice(&u16::to_be_bytes(sequence_ctl));
308
309 encoded
310 }
311
312 pub fn decode(buf: &[u8]) -> Result<Self, Error> {
319 let bytes = buf.get(0..Self::PRIMARY_HEADER_LEN).ok_or(Error::IncompleteHeader)?;
320
321 let packet_id = u16::from_be_bytes([bytes[0], bytes[1]]);
322 let sequence_ctl = u16::from_be_bytes([bytes[2], bytes[3]]);
323
324 let (version, packet_type, secondary_header, apid) = (
325 (packet_id >> Self::VERSION_SHIFT) as u8,
326 PacketType::from_bits((packet_id & Self::PACKET_TYPE_MASK) >> Self::PACKET_TYPE_SHIFT),
327 packet_id & Self::SECONDARY_HEADER_MASK != 0,
328 packet_id & Self::APID_MASK,
329 );
330
331 if version != Self::VERSION {
332 return Err(Error::Unsupported(version))
333 }
334
335 let (sequence_flag, sequence_number) = (
336 SequenceFlag::from_bits((sequence_ctl & Self::SEQUENCE_FLAG_MASK) >> Self::SEQUENCE_FLAG_SHIFT),
337 sequence_ctl & Self::SEQUENCE_NUMBER_MASK
338 );
339
340 Ok(Self {version, packet_type, secondary_header, apid, sequence_flag, sequence_number})
341 }
342}
343
344#[derive(Debug, Error, PartialEq)]
345pub enum Error {
348
349 #[error("space packet protocol version {} not supported", .0)]
352 Unsupported(u8),
353
354 #[error("incomplete primary header")]
356 IncompleteHeader,
357
358 #[error("insufficient data to complete decoding, found {}B but expected {}B", .found, .expected)]
361 InsufficientData{ expected: usize, found: usize },
362}
363
364
365#[cfg(test)]
366pub mod tests {
367 use super::*;
368 use rstest::rstest;
369
370 #[rstest]
371 fn test_spp_primary_header_codec(
372 #[values(PacketType::Telecommand, PacketType::Telemetry)]
373 packet_type: PacketType,
374 #[values(true, false)]
375 secondary_header: bool,
376 #[values(SequenceFlag::Continuation, SequenceFlag::Start, SequenceFlag::End, SequenceFlag::Unsegmented)]
377 sequence_flag: SequenceFlag,
378 ) {
379 let expected = PrimaryHeader {
380 version: PrimaryHeader::VERSION,
381 packet_type,
382 secondary_header,
383 apid: 0,
384 sequence_flag,
385 sequence_number: 0
386 };
387 let encoded = expected.encode();
388 let found = PrimaryHeader::decode(&encoded).unwrap();
389 assert_eq!(expected, found)
390 }
391
392 #[rstest]
393 #[case("Hello, World!".as_bytes().to_vec())]
394 #[case(vec![0])]
395 #[case(vec![0u8; u16::MAX as usize])]
396 fn test_test_spp_packet_codec(
397 #[values(PacketType::Telecommand, PacketType::Telemetry)]
398 packet_type: PacketType,
399 #[values(true, false)]
400 secondary_header: bool,
401 #[values(SequenceFlag::Continuation, SequenceFlag::Start, SequenceFlag::End, SequenceFlag::Unsegmented)]
402 sequence_flag: SequenceFlag,
403 #[case] payload: Vec<u8>,
404 ) {
405 let expected = SpacePacket::new(packet_type, secondary_header, 0, sequence_flag, 0, payload);
406 let encoded = expected.encode();
407 let found = SpacePacket::decode(&encoded).unwrap();
408 assert_eq!(expected.primary_header, found.primary_header);
409 assert_eq!(expected.payload, found.payload)
410 }
411
412 #[rstest]
413 #[should_panic]
414 fn test_empty_user_data() {
415 let expected = SpacePacket::new(PacketType::Telemetry, false, 0, SequenceFlag::Continuation, 0, vec![]);
416 let encoded = expected.encode();
417 let found = SpacePacket::decode(&encoded).unwrap();
418 assert_eq!(expected.primary_header, found.primary_header);
419 assert_eq!(expected.payload, found.payload)
420 }
421
422 #[rstest]
423 fn test_incomplete_header_err(
424 #[values(1, 2, 3, 4, 5)] header_len: usize
425 ) {
426 let forged_header_packet = vec![0u8; header_len];
427 assert_eq!(SpacePacket::decode(&forged_header_packet), Err(Error::IncompleteHeader))
428 }
429
430 #[rstest]
431 #[case(vec![1; 5])]
432 #[case(vec![1; 1])]
433 #[case(vec![1; 128])]
434 #[case(vec![1; 12048])]
435 #[case(vec![1; 60000])]
436 fn test_insufficient_data_err(#[case] payload: Vec<u8>) {
437 let mut packet = PrimaryHeader {
438 version: PrimaryHeader::VERSION,
439 packet_type: PacketType::Telecommand,
440 secondary_header: false,
441 apid: 0,
442 sequence_flag: SequenceFlag::End,
443 sequence_number: 0
444 }.encode();
445
446 let bad_payload_len = payload.len() as u16 + 5 - 1;
447 packet.extend_from_slice(&u16::to_be_bytes(bad_payload_len));
448 packet.extend_from_slice(&payload);
449
450 assert_eq!(SpacePacket::decode(&packet), Err(Error::InsufficientData { expected: (bad_payload_len + 1) as usize, found: payload.len() }))
451 }
452
453
454 #[rstest]
455 fn test_unsupported_err(#[values(1, 2, 3, 4, 5, 6, 7)] version: u8) {
456 let mut packet = SpacePacket::new(
457 PacketType::Telemetry,
458 false,
459 0,
460 SequenceFlag::Continuation,
461 0,
462 vec![1]
463 );
464
465 packet.primary_header.version = version;
466
467 let encoded = packet.encode();
468
469 assert_eq!(SpacePacket::decode(&encoded), Err(Error::Unsupported(version)))
470 }
471}