ccsds_rs/
spp.rs

1//! Provides functionality to create, encode, and decode Space Packet Protocol
2//! packets specified by the CCSDS 133.0-B-2 June 2020 Standard.
3//!
4//! General Usage:
5//! ``` rust
6//! use ccsds::spp::{SpacePacket, PacketType, SequenceFlag};
7//!
8//! # fn main() {
9//!     // Define user specified data.
10//!     let my_payload = "Hello, world!".as_bytes().to_vec();
11//!
12//!     // Generate SpacePacket
13//!     let my_space_packet = SpacePacket::new(
14//!         PacketType::Telecommand,
15//!         false,
16//!         67,
17//!         SequenceFlag::Unsegmented,
18//!         0,
19//!         my_payload // User data (includes secondary header if used)
20//!     );
21//!
22//!     // Encode SpacePacket as vector of bytes for transmission
23//!     let encoded = my_space_packet.encode();
24//!
25//!     // Do something with space packet....
26//!
27//!     // Decoding a space packet.
28//!     let decoded = SpacePacket::decode(&mut encoded.as_slice())
29//!     .expect("Failed to decode SpacePacket!");
30//! # }
31//!
32//! ```
33
34use std::io::Read;
35
36/// SPP Packet as defined by the CCSDS 133.0-B-2 Standard.
37pub struct SpacePacket {
38    pub primary_header: PrimaryHeader,
39    pub payload: Vec<u8>
40}
41
42impl SpacePacket {
43    pub fn new(
44        packet_type: PacketType, 
45        secondary_header: bool,
46        apid: u16,
47        sequence_flag: SequenceFlag,
48        sequence_number: u16,
49        payload: Vec<u8>
50    ) -> Self {
51        assert!(payload.len() <= u16::MAX as usize);
52        assert!(apid <= PrimaryHeader::APID_MASK);
53        assert!(sequence_number <= PrimaryHeader::SEQUENCE_NUMBER_MASK);
54
55        let primary_header = PrimaryHeader {
56            version: PrimaryHeader::VERSION,
57            packet_type,
58            secondary_header,
59            apid,
60            sequence_flag,
61            sequence_number,
62        };
63
64        Self { primary_header, payload }
65    }
66
67    /// Encodes the [SpacePacket] as a vector of bytes.
68    pub fn encode(&self) -> Vec<u8> {
69        let mut encoded = self.primary_header.encode();
70        // Subtract 1 from user data field as specified in CCSDS 133.0-B-2 Standard
71        encoded.extend_from_slice(&u16::to_be_bytes((self.payload.len() - 1) as u16));
72        encoded.extend_from_slice(&self.payload);
73        encoded
74
75    }
76    
77    /// Decodes the primary header from a source that implements [Read]. Returns the result of the
78    /// operation, on success giving the decoded [SpacePacket].
79    pub fn decode<R: Read>(buf: &mut R) -> std::io::Result<Self> {
80        let primary_header = PrimaryHeader::decode(buf)?;
81        
82        let mut tmp = [0u8; 2];
83        buf.read_exact(&mut tmp)?;
84        // Add single byte back to payload length that we subtracted during encoding
85        let payload_len = u16::from_be_bytes(tmp) + 1;
86
87        let mut payload = vec![0u8; payload_len as usize];
88        buf.read_exact(&mut payload)?;
89
90        Ok(Self { primary_header, payload })
91    }
92}
93
94
95/// Indicates if the SPP packet is of the Telemetry or Telecommand types.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum PacketType {
98    Telemetry = 0,
99    Telecommand = 1,
100}
101
102impl PacketType {
103    /// Converts the [PacketType] enum into its bitwise representation to be used in the SPP
104    /// primary header.
105    pub fn to_bits(&self) -> u16 {
106        match self {
107            Self::Telemetry => 0b0,
108            Self::Telecommand => 0b1,
109        }
110    }
111    
112    /// Converts the raw bits (after being shifted) from the packet ID portion of the primary
113    /// header into [PacketType].
114    pub fn from_bits(bits: u16) -> Self {
115        match bits & 0b1 {
116            0b0 => Self::Telemetry,
117            0b1 => Self::Telecommand,
118            _ => unreachable!()
119        }
120    }
121
122    /// returns boolean indicating if instance of [PacketType] is [PacketType::Telecommand]
123    pub fn is_telecommand(&self) -> bool {
124        matches!(self, Self::Telecommand)
125    }
126
127    /// returns boolean indicating if instance of [PacketType] is [PacketType::Telemetry]
128    pub fn is_telemetry(&self) -> bool {
129        matches!(self, Self::Telemetry)
130    }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134/// Sequence flag indicating if the packet is the start, end, or continuation in a sequence of
135/// packets, or is the packet is unsegmented.
136pub enum SequenceFlag {
137    Continuation = 0,
138    Start = 1,
139    End = 2,
140    Unsegmented = 3,
141}
142
143impl SequenceFlag {
144    /// Converts the [SequenceFlag] enum into its bitwise representation to be used in the SPP
145    /// primary header.
146    pub fn to_bits(&self) -> u16 {
147        match self {
148            Self::Continuation => 0b00,
149            Self::Start => 0b01,
150            Self::End => 0b10,
151            Self::Unsegmented => 0b11,
152        }
153    }
154
155    /// Converts the raw bits (after being shifted) from the sequence control portion of the primary
156    /// header into [SequenceFlag].
157    pub fn from_bits(bits: u16) -> Self {
158        match bits & 0b11 {
159            0b00 => Self::Continuation,
160            0b01 => Self::Start,
161            0b10 => Self::End,
162            0b11 => Self::Unsegmented,
163            _ => unreachable!()
164        }
165    }
166
167    /// returns boolean indicating if instance of [SequenceFlag] is [SequenceFlag::Continuation]
168    pub fn is_continuation(&self) -> bool {
169        matches!(self, Self::Continuation)
170    }
171
172    /// returns boolean indicating if instance of [SequenceFlag] is [SequenceFlag::Start]
173    pub fn is_start(&self) -> bool {
174        matches!(self, Self::Start)
175    }
176
177    /// returns boolean indicating if instance of [SequenceFlag] is [SequenceFlag::End]
178    pub fn is_end(&self) -> bool {
179        matches!(self, Self::End)
180    }
181    /// returns boolean indicating if instance of [SequenceFlag] is [SequenceFlag::Unsegmented]
182    pub fn is_unsegmented(&self) -> bool {
183        matches!(self, Self::Unsegmented)
184    }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188/// Primary Header used in the Space Packet Protocol.
189///
190/// This data structure encapsulates the packet version number, packet identification, 
191/// and sequence control field of the primary header of a SPP Packet. It is possible, although
192/// not neccessary to work with the PrimaryHeader Struct directly. 
193///
194/// Typical usage involves creating a SpacePacket, which internally constructs the PrimaryHeader
195/// using the arguments passed.
196/// ``` rust
197/// use ccsds::spp::{SpacePacket, PacketType, SequenceFlag};
198/// # fn main () {
199/// // generates new SpacePacket, internally constructing the PrimaryHeader.
200/// let my_space_packet = SpacePacket::new(
201///     PacketType::Telecommand,
202///     false,
203///     17,
204///     SequenceFlag::Unsegmented,
205///     0,
206///     "Cool Space Data".as_bytes().to_vec()
207/// );
208/// # }
209/// ```
210///
211/// Note that the user data length field is not included as a field within PrimaryHeader,
212/// The data length field is generated at encoding time of the SpacePacket.
213pub struct PrimaryHeader {
214
215    /// Hardcoded to 0b000, but here incase standard changes in the future (3 bits)
216    pub version: u8,
217
218    /// Packet type defined by [PacketType] enum (1 bit)
219    pub packet_type: PacketType,
220
221    /// Indicates if secondary header is used (1 bit)
222    pub secondary_header: bool,
223
224    /// Application process ID of the packet (11 bits)
225    pub apid: u16,
226
227    /// Sequence flag defined by [SequenceFlag] (2 bits)
228    pub sequence_flag: SequenceFlag,
229
230    /// Sequence number (14 bits)
231    pub sequence_number: u16,
232}
233
234impl PrimaryHeader {
235    /// Hardcoded version number for SPP
236    const VERSION: u8 = 0b000;
237
238    /// Number of bits the [VERSION] needs to be shifted in the
239    /// [encode] function.
240    const VERSION_SHIFT: usize = 13;
241    /// Number of bits the [PacketType] bit needs to be shifted in the
242    /// [encode] function.
243    const PACKET_TYPE_SHIFT: usize = 12;
244    /// Number of bits the secondary_header bit needs to be shifted in the
245    /// [encode] function.
246    const SECONDARY_HEADER_SHIFT: usize = 11;
247    /// Number of bits the apid needs to be shifted in the
248    /// [encode] function.
249    const APID_SHIFT: usize = 0;
250    /// Number of bits the [SequenceFlag] bits needs to be shifted in the
251    /// [encode] function.
252    const SEQUENCE_FLAG_SHIFT: usize = 14;
253
254    /// Mask of [PacketType] bit in the decode function.
255    const PACKET_TYPE_MASK: u16 = 0x1000;
256    /// Mask of secondary_header bit in the decode function.
257    const SECONDARY_HEADER_MASK: u16 = 0x0800;
258    /// Mask of apid bits in the decode function.
259    const APID_MASK: u16 = 0x03FF;
260    /// Mask of [SequenceFlag] bits in the decode function.
261    const SEQUENCE_FLAG_MASK: u16 = 0xC000;
262    /// Mask of sequence_number bits in the decode function.
263    const SEQUENCE_NUMBER_MASK: u16 = 0x3FFF;
264
265    /// Encodes the primary header into a vector of big endian bytes as described by CCSDS 133.0-B-2.
266    pub fn encode(&self) -> Vec<u8> {
267        let packet_id =
268            u16::from(Self::VERSION) << Self::VERSION_SHIFT |
269            self.packet_type.to_bits() << Self::PACKET_TYPE_SHIFT |
270            u16::from(self.secondary_header) << Self::SECONDARY_HEADER_SHIFT |
271            self.apid & Self::APID_MASK << Self::APID_SHIFT;
272
273        let sequence_ctl =
274            self.sequence_flag.to_bits() << Self::SEQUENCE_FLAG_SHIFT |
275            self.sequence_number & Self::SEQUENCE_NUMBER_MASK;
276
277        let mut encoded = Vec::new();
278        encoded.extend_from_slice(&u16::to_be_bytes(packet_id));
279        encoded.extend_from_slice(&u16::to_be_bytes(sequence_ctl));
280
281        encoded
282    }
283    
284    /// Decodes the primary header from a source that implements [Read]. Returns the result of the
285    /// operation, on success giving the decoded [PrimaryHeader].
286    pub fn decode<R: Read>(buf: &mut R) -> std::io::Result<Self> {
287        let mut tmp = [0u8; 4];
288        buf.read_exact(&mut tmp)?;
289
290        let packet_id = u16::from_be_bytes([tmp[0], tmp[1]]);
291        let sequence_ctl = u16::from_be_bytes([tmp[2], tmp[3]]);
292
293        let (version, packet_type, secondary_header, apid) = (
294            (packet_id >> Self::VERSION_SHIFT) as u8,
295            PacketType::from_bits((packet_id & Self::PACKET_TYPE_MASK) >> Self::PACKET_TYPE_SHIFT),
296            packet_id & Self::SECONDARY_HEADER_MASK != 0,
297            packet_id & Self::APID_MASK,
298        );
299        
300        let (sequence_flag, sequence_number) = (
301            SequenceFlag::from_bits((sequence_ctl & Self::SEQUENCE_FLAG_MASK) >> Self::SEQUENCE_FLAG_SHIFT),
302            sequence_ctl & Self::SEQUENCE_NUMBER_MASK
303        );
304
305        Ok(Self {version, packet_type, secondary_header, apid, sequence_flag, sequence_number})
306    }
307}
308
309
310
311#[cfg(test)]
312pub mod tests {
313    use super::*;
314    use rstest::rstest;
315
316    #[rstest]
317    fn test_spp_primary_header_codec(
318        #[values(PacketType::Telecommand, PacketType::Telemetry)]
319        packet_type: PacketType,
320        #[values(true, false)]
321        secondary_header: bool,
322        #[values(SequenceFlag::Continuation, SequenceFlag::Start, SequenceFlag::End, SequenceFlag::Unsegmented)]
323        sequence_flag: SequenceFlag,
324    ) {
325        let expected = PrimaryHeader {
326            version: PrimaryHeader::VERSION,
327            packet_type,
328            secondary_header,
329            apid: 0,
330            sequence_flag,
331            sequence_number: 0
332        };
333        let encoded = expected.encode();
334        let found = PrimaryHeader::decode(&mut encoded.as_slice()).unwrap();
335        assert_eq!(expected, found)
336    }
337
338    #[rstest]
339    #[case("Hello, World!".as_bytes().to_vec())]
340    #[case(vec![0])]
341    #[case(vec![0u8; u16::MAX as usize])]
342    fn test_test_spp_packet_codec(
343        #[values(PacketType::Telecommand, PacketType::Telemetry)]
344        packet_type: PacketType,
345        #[values(true, false)]
346        secondary_header: bool,
347        #[values(SequenceFlag::Continuation, SequenceFlag::Start, SequenceFlag::End, SequenceFlag::Unsegmented)]
348        sequence_flag: SequenceFlag,
349        #[case] payload: Vec<u8>,
350    ) {
351        let expected = SpacePacket::new(packet_type, secondary_header, 0, sequence_flag, 0, payload);
352        let encoded = expected.encode();
353        let found = SpacePacket::decode(&mut encoded.as_slice()).unwrap();
354        assert_eq!(expected.primary_header, found.primary_header);
355        assert_eq!(expected.payload, found.payload)
356    }
357}
358
359
360