space_packet/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![forbid(unsafe_code)]
4//! Generic implementation of the CCSDS 133.0-B-2 Space Packet Protocol (SPP). That is, this crate
5//! concerns itself only with parsing and construction of CCSDS Space Packets, as that is
6//! independent of the precise implementation. Endpoint functionality, i.e., actually consuming and
7//! responding to the packet contents is implementation specific, and hence out of scope.
8//!
9//! Readers of the code are advised to start with the `PacketAssembly`, `PacketTransfer`,
10//! `PacketReception` and `PacketExtraction` traits. These describe the interfaces that application
11//! processes supporting the Space Packet Protocol are expected to expose.
12//!
13//! Tested and formally-verified implementations of the underlying parsing and semantic checking
14//! functionality needed to handle Space Packets is found in the actual `SpacePacket`
15//! implementation. This functionality is included in the hope that it helps write simple and
16//! robust SPP implementations.
17
18use zerocopy::byteorder::network_endian;
19use zerocopy::{ByteEq, CastError, FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
20
21/// The `PacketAssembly` trait describes the "Packet Assembly" function from the CCSDS 133.0-B-2
22/// Space Packet Protocol recommended standard. This function concerns the ability of some protocol
23/// entity to build Space Packets from octet strings (packet data fields). It is the sending
24/// counterpart of the `PacketExtraction` trait.
25///
26/// We deviate slightly from the strict "Packet Assembly" function definition in permitting
27/// population of the octet string only after assembly of a packet with given packet data field
28/// length. This is useful, because it means that no copy is needed to prepend the Space Packet
29/// header to the data field, which saves a `memcpy`.
30pub trait PacketAssembly {
31    /// Generates Space Packets from octet strings. See CCSDS 133.0-B-2 Section 4.2.2 "Packet
32    /// Assembly Function". The Packet Assembly function shall itself keep track of the source
33    /// sequence count of packets for a given packet identification (version, packet type, and
34    /// APID), but all other elements must be provided by the service user. On top, since the
35    /// Packet Assembly function is used in tandem with the Octet String service (which does not
36    /// permit segmentation), the packet sequence flags shall be 0b11 ("Unsegmented").
37    ///
38    /// An error shall be returned if an empty packet data field is requested, since that indicates
39    /// an error on the user input side. In all other cases, no error may be returned - though
40    /// there may be cases where the packet is lost.
41    fn assemble<'a>(
42        &mut self,
43        packet_type: PacketType,
44        apid: Apid,
45        secondary_header_flag: SecondaryHeaderFlag,
46        buffer: &'a mut [u8],
47    ) -> Result<&'a mut SpacePacket, PacketAssemblyError> {
48        let sequence_count = self.packet_sequence_count(packet_type, apid);
49        SpacePacket::assemble(
50            buffer,
51            packet_type,
52            secondary_header_flag,
53            apid,
54            SequenceFlag::Unsegmented,
55            sequence_count,
56        )
57    }
58
59    /// In practice, the primary reason that the `PacketAssembly` function exists is that it is a
60    /// centralized manner to determine the packet sequence count of any newly-created packet. To
61    /// make life easier, we require this as separate method based on which we provide a default
62    /// implementation of `assemble()`.
63    ///
64    /// Implementations of this function shall also result in an appropriate update of the packet
65    /// sequence count.
66    fn packet_sequence_count(&mut self, packet_type: PacketType, apid: Apid)
67    -> PacketSequenceCount;
68}
69
70/// The `PacketTransfer` trait describes the "Packet Transfer" function from the CCSDS 133.0-B-2
71/// Space Packet Protocol recommended standard. It concerns the ability of some protocol entity to
72/// transfer packets towards the appropriate managed data path. It is the sending counterpart of
73/// the `PacketReception` trait.
74pub trait PacketTransfer {
75    /// Inspects an incoming or newly-created Space Packet (its APID, in particular) to determine
76    /// the target packet service entity at the receiving end. Routes this packet towards the
77    /// appropriate managed data path using a service of the underlying OSI reference model layers.
78    fn transfer(&mut self, packet: &SpacePacket);
79}
80
81/// The `PacketReception` trait describes the "Packet Reception" function from the CCSDS 133.0-B-2
82/// Space Packet Protocol recommended standard. It concerns the ability to receive new Space
83/// Packets from some underlying subnetwork layer.
84pub trait PacketReception {
85    /// Polls the message bus to see if new Space Packets have been received for a given APID. If
86    /// so, returns a reference to it. Need not perform any checking, such as data loss checks:
87    /// that may be done by the receiving party.
88    ///
89    /// After reception, the Space Packet shall be removed from the packet receptor: on future
90    /// polls (for the same `self`), it shall no longer be returned.
91    fn receive(&mut self) -> Option<&SpacePacket>;
92}
93
94/// The `PacketExtraction` trait describes the "Packet Extraction" function from the CCSDS 133.0-B-2
95/// Space Packet Protocol recommended standard. It concerns the ability to unpack Space Packets
96/// that have been received from some underlying subnetwork into the transmitted octet strings.
97pub trait PacketExtraction {
98    /// Value that may optionally be returned when extracting a packet to indicate whether (and
99    /// potentially to what degree) the packet sequence count suggests data loss to have occurred.
100    type DataLossIndicator;
101
102    /// Unpacks the given Space Packet into its underlying packet data field. Shall also return
103    /// whether there was a mismatch between the expected and actual Space Packet sequence
104    /// counters: if so, returns an appropriate data loss indicator. Finally, the secondary header
105    /// flag as contained in the primary header may also be returned.
106    fn extract<'a>(
107        &mut self,
108        packet: &'a SpacePacket,
109    ) -> (&'a [u8], SecondaryHeaderFlag, Self::DataLossIndicator) {
110        let packet_type = packet.packet_type();
111        let apid = packet.apid();
112        let secondary_header_flag = packet.secondary_header_flag();
113        let packet_sequence_count = packet.packet_sequence_count();
114        let data_loss_indicator =
115            self.data_loss_indicator(packet_type, apid, packet_sequence_count);
116        let packet_data_field = packet.packet_data_field();
117        (
118            packet_data_field,
119            secondary_header_flag,
120            data_loss_indicator,
121        )
122    }
123
124    /// Given some message ID (packet type and APID) and the sequence count found in the packet,
125    /// determines whether data loss has likely occurred. Updates the packet extractor with
126    /// this new packet sequence count to permit future data loss detection.
127    ///
128    /// This is the "meat" of the Packet Extraction function: the actual extraction of the packet
129    /// itself is otherwise quite trivial. Hence, we separately define this function, with the
130    /// `extract` function derived based on it.
131    fn data_loss_indicator(
132        &mut self,
133        packet_type: PacketType,
134        apid: Apid,
135        packet_sequence_count: PacketSequenceCount,
136    ) -> Self::DataLossIndicator;
137}
138
139/// Space packets are implemented as dynamically-sized structs that contain the primary header as
140/// their first field, followed by the packet data as pure byte array. In this manner,
141/// deserialization can be reduced to a simple byte cast followed by interpretation of the primary
142/// header - without any data copies needed. This is useful for high-throughput applications, and
143/// ensures that no allocation or significant additional memory is needed to consume Space Packets.
144///
145/// This does also mean that Space Packets may only be handled by reference. In the context of this
146/// crate that helps enforce that no spurious copies can be made of the user data (which may be
147/// rather large and would incur additional allocations), albeit at the cost of some convenience.
148///
149/// Any means of constructing a SpacePacket in this crate shall perform a consistency check on any
150/// received bytes. Hence, any SpacePacket object may be assumed to be a valid Space Packet.
151#[repr(C, packed)]
152#[derive(ByteEq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
153pub struct SpacePacket {
154    primary_header: SpacePacketPrimaryHeader,
155    data_field: [u8],
156}
157
158impl SpacePacket {
159    /// Attempts to parse a Space Packet from a given byte slice. If this fails, a reason is
160    /// given for this failure. Shall never panic: rather, an error enum is returned explaining why
161    /// the given octet string is not a valid Space Packet.
162    ///
163    /// This deserialization is fully zero-copy. The `&SpacePacket` returned on success directly
164    /// references the input slice `bytes`, but is merely validated to be a valid Space Packet.
165    pub fn parse(bytes: &[u8]) -> Result<&SpacePacket, InvalidSpacePacket> {
166        // First, we simply cast the packet into a header and check that the byte buffer permits
167        // this: i.e., if it is large enough to contain a header.
168        let primary_header = match SpacePacket::ref_from_bytes(bytes) {
169            Ok(primary_header) => primary_header,
170            Err(CastError::Size(_)) => {
171                return Err(InvalidSpacePacket::SliceTooSmallForSpacePacketHeader {
172                    length: bytes.len(),
173                });
174            }
175            Err(CastError::Alignment(_)) => unreachable!(),
176        };
177
178        // Then, we verify that the resulting packet contents semantically form a valid space
179        // packet.
180        primary_header.validate()?;
181
182        // Finally, we truncate the passed byte slice to exactly accommodate the specified space
183        // packet and construct a Space Packet that consists of only this memory region.
184        let packet_size = primary_header.packet_data_length() + Self::primary_header_size();
185        let packet_bytes = &bytes[..packet_size];
186        let packet = match SpacePacket::ref_from_bytes(packet_bytes) {
187            Ok(primary_header) => primary_header,
188            Err(_) => unreachable!(),
189        };
190
191        Ok(packet)
192    }
193
194    /// Assembles a Space Packet in-place on a given buffer. Computes the required packet data
195    /// length from the passed buffer size. It is assumed that the caller has reserved the first
196    /// six bytes of the buffer for the packet header. All other bytes are assumed to form the
197    /// packet data field.
198    pub fn assemble(
199        buffer: &mut [u8],
200        packet_type: PacketType,
201        secondary_header_flag: SecondaryHeaderFlag,
202        apid: Apid,
203        sequence_flag: SequenceFlag,
204        sequence_count: PacketSequenceCount,
205    ) -> Result<&mut SpacePacket, PacketAssemblyError> {
206        if buffer.len() < 6 {
207            Err(PacketAssemblyError::BufferTooSmall {
208                buffer_length: buffer.len(),
209                packet_length: 6,
210            })
211        } else {
212            Self::construct(
213                buffer,
214                packet_type,
215                secondary_header_flag,
216                apid,
217                sequence_flag,
218                sequence_count,
219                buffer.len() as u16 - 6,
220            )
221        }
222    }
223
224    /// Constructs a Space Packet in-place on a given buffer. May return a
225    /// `SpacePacketConstructionError` if this is not possible for whatever reason. Note that the
226    /// data field is only "allocated" on the buffer, but never further populated. That may be done
227    /// after the SpacePacket is otherwise fully constructed (or before: it is not touched during
228    /// construction).
229    pub fn construct(
230        buffer: &mut [u8],
231        packet_type: PacketType,
232        secondary_header_flag: SecondaryHeaderFlag,
233        apid: Apid,
234        sequence_flag: SequenceFlag,
235        sequence_count: PacketSequenceCount,
236        packet_data_length: u16,
237    ) -> Result<&mut SpacePacket, PacketAssemblyError> {
238        // As per the CCSDS Space Packet Protocol standard, we must reject requests for data field
239        // lengths of zero.
240        if packet_data_length == 0 {
241            return Err(PacketAssemblyError::EmptyDataFieldRequested);
242        }
243
244        // Verify that the packet length as requested may actually fit on the supplied buffer.
245        let packet_length = SpacePacket::primary_header_size() + packet_data_length as usize;
246        let buffer_length = buffer.len();
247        if packet_length > buffer_length {
248            return Err(PacketAssemblyError::BufferTooSmall {
249                buffer_length,
250                packet_length,
251            });
252        }
253
254        // Afterwards, we truncate the buffer to use only the bytes that actually belong to the
255        // packet. With the length check done, the `SpacePacket::mut_from_bytes()` call is known
256        // to be infallible, so we simply unwrap.
257        let packet_bytes = &mut buffer[..packet_length];
258        let packet = SpacePacket::mut_from_bytes(packet_bytes).unwrap();
259
260        // Initialize header bytes to valid values.
261        packet.primary_header.set_apid(apid);
262        packet.primary_header.initialize_packet_version();
263        packet.primary_header.set_packet_type(packet_type);
264        packet
265            .primary_header
266            .set_secondary_header_flag(secondary_header_flag);
267        packet.primary_header.set_sequence_flag(sequence_flag);
268        packet
269            .primary_header
270            .set_packet_sequence_count(sequence_count);
271        packet
272            .primary_header
273            .set_packet_data_length(packet_data_length)?;
274
275        Ok(packet)
276    }
277
278    /// Validates that the Space Packet is valid, in that its fields are coherent. In particular,
279    /// it is verified that the version number is that of a supported Space Packet, and that the
280    /// packet size as stored in the header is not larger than the packet size as permitted by the
281    /// actual memory span of which the packet consists.
282    ///
283    /// Note that this concerns semantic validity. The implementation shall not depend on this for
284    /// memory safety.
285    fn validate(&self) -> Result<(), InvalidSpacePacket> {
286        // First, we check that the primary header is valid and consistent.
287        self.primary_header.validate()?;
288
289        // The packet header contains an indication of the actual amount of bytes stored in the packet.
290        // If this is larger than the size of the actual memory contents, only a partial packet was
291        // received.
292        let packet_size = self.packet_data_length() + Self::primary_header_size();
293        let buffer_size = self.packet_length();
294        if packet_size > buffer_size {
295            return Err(InvalidSpacePacket::PartialPacket {
296                packet_size,
297                buffer_size,
298            });
299        }
300
301        Ok(())
302    }
303
304    /// Returns the size of a Space Packet primary header, in bytes. In the version that is
305    /// presently implemented, that is always 6 bytes.
306    pub const fn primary_header_size() -> usize {
307        6
308    }
309
310    /// Since the Space Packet protocol may technically support alternative packet structures in
311    /// future versions, the 3-bit packet version field may not actually contain a "correct" value.
312    pub fn packet_version(&self) -> PacketVersionNumber {
313        self.primary_header.packet_version()
314    }
315
316    /// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
317    /// packet. Note that the exact definition of telecommand and telemetry may differ per system,
318    /// and indeed the "correct" value here may differ per project.
319    pub fn packet_type(&self) -> PacketType {
320        self.primary_header.packet_type()
321    }
322
323    /// Sets the packet type to the given value.
324    pub fn set_packet_type(&mut self, packet_type: PacketType) {
325        self.primary_header.set_packet_type(packet_type)
326    }
327
328    /// Denotes whether the packet contains a secondary header. If no user field is present, the
329    /// secondary header is mandatory (presumably, to ensure that some data is always transferred,
330    /// considering the Space Packet header itself contains no meaningful data).
331    pub fn secondary_header_flag(&self) -> SecondaryHeaderFlag {
332        self.primary_header.secondary_header_flag()
333    }
334
335    /// Updates the value of the secondary header flag with the provided value.
336    pub fn set_secondary_header_flag(&mut self, secondary_header_flag: SecondaryHeaderFlag) {
337        self.primary_header
338            .set_secondary_header_flag(secondary_header_flag)
339    }
340
341    /// Returns the application process ID stored in the packet. The actual meaning of this APID
342    /// field may differ per implementation: technically, it only represents "some" data path.
343    /// In practice, it will often be a identifier for a data channel, the packet source, or the
344    /// packet destination.
345    pub fn apid(&self) -> Apid {
346        self.primary_header.apid()
347    }
348
349    /// Sets the APID used to route the packet to the given value.
350    pub fn set_apid(&mut self, apid: Apid) {
351        self.primary_header.set_apid(apid)
352    }
353
354    /// Sequence flags may be used to indicate that the data contained in a packet is only part of
355    /// a larger set of application data.
356    pub fn sequence_flag(&self) -> SequenceFlag {
357        self.primary_header.sequence_flag()
358    }
359
360    /// Sets the sequence flag to the provided value.
361    pub fn set_sequence_flag(&mut self, sequence_flag: SequenceFlag) {
362        self.primary_header.set_sequence_flag(sequence_flag)
363    }
364
365    /// The packet sequence count is unique per APID and denotes the sequential binary count of
366    /// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
367    /// may also be a "packet name" that identifies the telecommand packet within its
368    /// communications session.
369    pub fn packet_sequence_count(&self) -> PacketSequenceCount {
370        self.primary_header.packet_sequence_count()
371    }
372
373    /// Sets the packet sequence count to the provided value. This value must be provided by an
374    /// external counter and is not provided at a Space Packet type level because it might differ
375    /// between packet streams.
376    pub fn set_packet_sequence_count(&mut self, sequence_count: PacketSequenceCount) {
377        self.primary_header
378            .set_packet_sequence_count(sequence_count)
379    }
380
381    /// The packet data length field represents the length of the associated packet data field.
382    /// However, it is not stored directly: rather, the "length count" is stored, which is the
383    /// packet data length minus one.
384    pub fn packet_data_length(&self) -> usize {
385        self.primary_header.packet_data_length()
386    }
387
388    /// Sets the packet data length field to the provided value. Note that the given value is not
389    /// stored directly, but rather decremented by one first. Accordingly, and as per the CCSDS
390    /// Space Packet Protocol standard, packet data lengths of 0 are not allowed.
391    pub fn set_packet_data_length(
392        &mut self,
393        packet_data_length: u16,
394    ) -> Result<(), InvalidPacketDataLength> {
395        if packet_data_length == 0 {
396            return Err(InvalidPacketDataLength::EmptyDataField);
397        }
398
399        let buffer_length = self.data_field.len();
400        if packet_data_length as usize > buffer_length {
401            return Err(InvalidPacketDataLength::LargerThanPacketDataBuffer {
402                packet_data_length,
403                buffer_length,
404            });
405        }
406
407        let stored_data_field_length = packet_data_length - 1;
408        self.primary_header
409            .data_length
410            .set(stored_data_field_length);
411        Ok(())
412    }
413
414    /// Returns the total length of the packet in bytes. Note the distinction from the packet data
415    /// length, which refers only to the length of the data field of the packet.
416    pub fn packet_length(&self) -> usize {
417        self.as_bytes().len()
418    }
419
420    /// Returns a reference to the packet data field contained in this Space Packet.
421    pub fn packet_data_field(&self) -> &[u8] {
422        &self.data_field
423    }
424
425    /// Returns a mutable reference to the packet data field contained in this Space Packet.
426    pub fn packet_data_field_mut(&mut self) -> &mut [u8] {
427        &mut self.data_field
428    }
429}
430
431impl core::hash::Hash for SpacePacket {
432    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
433        self.primary_header.hash(state);
434        self.data_field.hash(state);
435    }
436}
437
438/// Representation of only the fixed-size primary header part of a space packet. Used to construct
439/// generic space packets, but mostly useful in permitting composition of derived packet types,
440/// like PUS packets; otherwise, the dynamically-sized data field member would get in the way of
441/// including the primary header directly in derived packets.
442#[repr(C)]
443#[derive(ByteEq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned, Hash)]
444pub struct SpacePacketPrimaryHeader {
445    packet_identification: network_endian::U16,
446    packet_sequence_control: network_endian::U16,
447    data_length: network_endian::U16,
448}
449
450impl SpacePacketPrimaryHeader {
451    /// Validates that the Space Packet primary header is valid, in that its fields are coherent.
452    /// In particular, it is verified that the version number is that of a supported Space Packet.
453    ///
454    /// Note that this concerns semantic validity. The implementation shall not depend on this for
455    /// memory safety.
456    fn validate(&self) -> Result<(), InvalidSpacePacket> {
457        // We verify that the packet version found in the packet header is a version that is
458        // supported by this library.
459        let version = self.packet_version();
460        if !version.is_supported() {
461            return Err(InvalidSpacePacket::UnsupportedPacketVersion { version });
462        }
463
464        // Idle packets may not contain a secondary header field. If we do find that the secondary
465        // header flag is set, we must reject the packet.
466        if self.apid().is_idle() && self.secondary_header_flag() == SecondaryHeaderFlag::Present {
467            return Err(InvalidSpacePacket::IdlePacketWithSecondaryHeader);
468        }
469
470        Ok(())
471    }
472
473    /// Returns the size of a Space Packet primary header, in bytes. In the version that is
474    /// presently implemented, that is always 6 bytes.
475    pub const fn primary_header_size() -> usize {
476        6
477    }
478
479    /// Since the Space Packet protocol may technically support alternative packet structures in
480    /// future versions, the 3-bit packet version field may not actually contain a "correct" value.
481    pub fn packet_version(&self) -> PacketVersionNumber {
482        use core::ops::Shr;
483        PacketVersionNumber(self.packet_identification.as_bytes()[0].shr(5))
484    }
485
486    /// Initializes the packet version to the proper value. Must be a fixed value, so this function
487    /// takes no arguments.
488    pub fn initialize_packet_version(&mut self) {
489        self.packet_identification.as_mut_bytes()[0] &= 0b0001_1111;
490        self.packet_identification.as_mut_bytes()[0] |=
491            PacketVersionNumber::version1_ccsds_packet().0 << 5;
492    }
493
494    /// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
495    /// packet. Note that the exact definition of telecommand and telemetry may differ per system,
496    /// and indeed the "correct" value here may differ per project.
497    pub fn packet_type(&self) -> PacketType {
498        match (self.packet_identification.as_bytes()[0] & 0x10) == 0x10 {
499            true => PacketType::Telecommand,
500            false => PacketType::Telemetry,
501        }
502    }
503
504    /// Sets the packet type to the given value.
505    pub fn set_packet_type(&mut self, packet_type: PacketType) {
506        self.packet_identification.as_mut_bytes()[0] &= 0b1110_1111;
507        self.packet_identification.as_mut_bytes()[0] |= (packet_type as u8) << 4;
508    }
509
510    /// Denotes whether the packet contains a secondary header. If no user field is present, the
511    /// secondary header is mandatory (presumably, to ensure that some data is always transferred,
512    /// considering the Space Packet header itself contains no meaningful data).
513    pub fn secondary_header_flag(&self) -> SecondaryHeaderFlag {
514        match (self.packet_identification.as_bytes()[0] & 0x08) == 0x08 {
515            true => SecondaryHeaderFlag::Present,
516            false => SecondaryHeaderFlag::Absent,
517        }
518    }
519
520    /// Updates the value of the secondary header flag with the provided value.
521    pub fn set_secondary_header_flag(&mut self, secondary_header_flag: SecondaryHeaderFlag) {
522        self.packet_identification.as_mut_bytes()[0] &= 0b1111_0111;
523        self.packet_identification.as_mut_bytes()[0] |= (secondary_header_flag as u8) << 3;
524    }
525
526    /// Returns the application process ID stored in the packet. The actual meaning of this APID
527    /// field may differ per implementation: technically, it only represents "some" data path.
528    /// In practice, it will often be a identifier for a data channel, the packet source, or the
529    /// packet destination.
530    pub fn apid(&self) -> Apid {
531        Apid(self.packet_identification.get() & 0b0000_0111_1111_1111)
532    }
533
534    /// Sets the APID used to route the packet to the given value.
535    pub fn set_apid(&mut self, apid: Apid) {
536        let apid = apid.0.to_be_bytes();
537        self.packet_identification.as_mut_bytes()[0] &= 0b1111_1000;
538        self.packet_identification.as_mut_bytes()[0] |= apid[0] & 0b0000_0111;
539        self.packet_identification.as_mut_bytes()[1] = apid[1];
540    }
541
542    /// Sequence flags may be used to indicate that the data contained in a packet is only part of
543    /// a larger set of application data.
544    pub fn sequence_flag(&self) -> SequenceFlag {
545        use core::ops::Shr;
546        match self.packet_sequence_control.as_bytes()[0].shr(6i32) {
547            0b00 => SequenceFlag::Continuation,
548            0b01 => SequenceFlag::First,
549            0b10 => SequenceFlag::Last,
550            0b11 => SequenceFlag::Unsegmented,
551            _ => unreachable!("Internal error: Reached unreachable code segment"),
552        }
553    }
554
555    /// Sets the sequence flag to the provided value.
556    pub fn set_sequence_flag(&mut self, sequence_flag: SequenceFlag) {
557        self.packet_sequence_control.as_mut_bytes()[0] &= 0b0011_1111;
558        self.packet_sequence_control.as_mut_bytes()[0] |= (sequence_flag as u8) << 6;
559    }
560
561    /// The packet sequence count is unique per APID and denotes the sequential binary count of
562    /// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
563    /// may also be a "packet name" that identifies the telecommand packet within its
564    /// communications session.
565    pub fn packet_sequence_count(&self) -> PacketSequenceCount {
566        PacketSequenceCount(self.packet_sequence_control.get() & 0b0011_1111_1111_1111)
567    }
568
569    /// Sets the packet sequence count to the provided value. This value must be provided by an
570    /// external counter and is not provided at a Space Packet type level because it might differ
571    /// between packet streams.
572    pub fn set_packet_sequence_count(&mut self, sequence_count: PacketSequenceCount) {
573        self.packet_sequence_control.as_mut_bytes()[0] &= 0b1100_0000;
574        self.packet_sequence_control.as_mut_bytes()[0] |=
575            sequence_count.0.to_be_bytes()[0] & 0b0011_1111;
576        self.packet_sequence_control.as_mut_bytes()[1] = sequence_count.0.to_be_bytes()[1];
577    }
578
579    /// The packet data length field represents the length of the associated packet data field.
580    /// However, it is not stored directly: rather, the "length count" is stored, which is the
581    /// packet data length minus one.
582    pub fn packet_data_length(&self) -> usize {
583        self.data_length.get() as usize + 1
584    }
585
586    /// Sets the packet data length field to the provided value. Note that the given value is not
587    /// stored directly, but rather decremented by one first. Accordingly, and as per the CCSDS
588    /// Space Packet Protocol standard, packet data lengths of 0 are not allowed.
589    pub fn set_packet_data_length(
590        &mut self,
591        packet_data_length: u16,
592    ) -> Result<(), InvalidPacketDataLength> {
593        if packet_data_length == 0 {
594            return Err(InvalidPacketDataLength::EmptyDataField);
595        }
596
597        let stored_data_field_length = packet_data_length - 1;
598        self.data_length.set(stored_data_field_length);
599        Ok(())
600    }
601}
602
603/// Because `SpacePacket` is `repr(packed)` and `SpacePacket::data_field` is unsized, the default
604/// `core::fmt::Debug` implementation cannot be derived.
605impl core::fmt::Debug for SpacePacket {
606    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
607        write!(
608            f,
609            "SpacePacket {{ version number: {:?}, packet type: {:?}, secondary header flag: {:?}, APID: {:?}, sequence flags: {:?}, sequence count: {:?}, packet data length: {:?}, packet data: {:?} }}",
610            self.packet_version(),
611            self.packet_type(),
612            self.secondary_header_flag(),
613            self.apid(),
614            self.sequence_flag(),
615            self.packet_sequence_count(),
616            self.packet_data_length(),
617            self.packet_data_field(),
618        )
619    }
620}
621
622/// Representation of the set of errors that may be encountered while deserializing a Space Packet.
623/// Marked as non-exhaustive to permit extension with additional semantic errors in the future
624/// without breaking API.
625#[non_exhaustive]
626#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
627pub enum InvalidSpacePacket {
628    /// Returned when a byte slice is too small to contain any Space Packet (i.e., is smaller than
629    /// a header with a single-byte user data field).
630    SliceTooSmallForSpacePacketHeader { length: usize },
631    /// Returned when a slice does not have a known and supported packet version. For convenience,
632    /// the packet version that is stored at the "conventional" (CCSDS packet version 0) is also
633    /// returned, though it does not need to be meaningful in other packet versions.
634    UnsupportedPacketVersion { version: PacketVersionNumber },
635    /// Returned when the decoded packet is not fully contained in the passed buffer.
636    PartialPacket {
637        packet_size: usize,
638        buffer_size: usize,
639    },
640    /// Returned when the Space Packet is idle (has an 'all ones' APID) but also contains a
641    /// secondary header. This is forbidden by CCSDS 133.0-B-2.
642    IdlePacketWithSecondaryHeader,
643}
644
645/// Representation of the set of errors that may be encountered while constructing a Space Packet.
646/// Marked as non-exhaustive to permit extension with additional semantic errors in the future
647/// without breaking API.
648#[non_exhaustive]
649#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
650pub enum PacketAssemblyError {
651    /// Returned when the underlying buffer does not have sufficient bytes to contain a given space
652    /// packet.
653    BufferTooSmall {
654        buffer_length: usize,
655        packet_length: usize,
656    },
657    /// As per the CCSDS standard, Space Packets shall have at least one byte in their data field.
658    /// Hence, requests for an empty data field must be rejected.
659    EmptyDataFieldRequested,
660}
661
662/// This error may be returned when setting the data field of some newly-constructed Space Packet
663/// if the requested packet data length is 0 (which is generally illegal) or if the requested
664/// packet data length does not fit in the buffer on which the packet must be stored.
665#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
666pub enum InvalidPacketDataLength {
667    EmptyDataField,
668    LargerThanPacketDataBuffer {
669        packet_data_length: u16,
670        buffer_length: usize,
671    },
672}
673
674impl From<InvalidPacketDataLength> for PacketAssemblyError {
675    fn from(value: InvalidPacketDataLength) -> Self {
676        match value {
677            InvalidPacketDataLength::EmptyDataField => PacketAssemblyError::EmptyDataFieldRequested,
678            InvalidPacketDataLength::LargerThanPacketDataBuffer {
679                packet_data_length,
680                buffer_length,
681            } => PacketAssemblyError::BufferTooSmall {
682                buffer_length: buffer_length + SpacePacket::primary_header_size(),
683                packet_length: packet_data_length as usize + SpacePacket::primary_header_size(),
684            },
685        }
686    }
687}
688
689/// The packet version number represents the version of the Space Packet protocol that is used. In
690/// the version presently implemented, this is defined to be zeroes.
691#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
692pub struct PacketVersionNumber(u8);
693
694impl PacketVersionNumber {
695    /// The Space Packet protocol version presently implemented in this crate is based on issue 2
696    /// of the CCSDS SPP blue book, which encompasses only the Version 1 CCSDS Packet, indicated by
697    /// a version number of 0. Other packet structures may be added in the future.
698    pub fn is_supported(&self) -> bool {
699        matches!(self.0, 0b0000_0000u8)
700    }
701
702    /// Returns the packet version number corresponding with the Version 1 CCSDS Packet.
703    pub fn version1_ccsds_packet() -> Self {
704        Self(0)
705    }
706}
707
708/// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
709/// packet. Note that the exact definition of telecommand and telemetry may differ per system,
710/// and indeed the "correct" value here may differ per project.
711#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
712#[cfg_attr(kani, derive(kani::Arbitrary))]
713pub enum PacketType {
714    Telemetry = 0,
715    Telecommand = 1,
716}
717
718/// Denotes whether the packet contains a secondary header. If no user field is present, the
719/// secondary header is mandatory (presumably, to ensure that some data is always transferred,
720/// considering the Space Packet header itself contains no meaningful data).
721#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
722#[cfg_attr(kani, derive(kani::Arbitrary))]
723pub enum SecondaryHeaderFlag {
724    Absent = 0,
725    Present = 1,
726}
727
728/// Returns the application process ID stored in the packet. The actual meaning of this APID
729/// field may differ per implementation: technically, it only represents "some" data path.
730/// In practice, it will often be a identifier for: a data channel, the packet source, or the
731/// packet destination.
732#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
733#[cfg_attr(kani, derive(kani::Arbitrary))]
734pub struct Apid(u16);
735
736impl Apid {
737    const MAX: u16 = 0b0000_0111_1111_1111u16;
738
739    pub fn new(id: u16) -> Self {
740        assert!(
741            id <= Self::MAX,
742            "APIDs may not exceed 2047 (due to maximum of 13 bits in representation)"
743        );
744        Self(id)
745    }
746
747    /// Helper functions used during formal verification to create an APID that is actually within
748    /// the stated bounds, since we cannot use the type system to express this range.
749    #[cfg(kani)]
750    fn any_apid() -> Self {
751        match kani::any() {
752            any @ 0..=Self::MAX => Self(any),
753            _ => Self(42),
754        }
755    }
756
757    /// A special APID value (0x7ff) is reserved for idle Space Packets, i.e., packets that do not
758    /// carry any actual data.
759    pub fn is_idle(&self) -> bool {
760        self.0 == 0x7ff
761    }
762}
763
764/// Sequence flags may be used to indicate that the data contained in a packet is only part of
765/// a larger set of application data.
766#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
767#[cfg_attr(kani, derive(kani::Arbitrary))]
768pub enum SequenceFlag {
769    Continuation = 0b00,
770    First = 0b01,
771    Last = 0b10,
772    #[default]
773    Unsegmented = 0b11,
774}
775
776/// The packet sequence count is unique per APID and denotes the sequential binary count of
777/// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
778/// may also be a "packet name" that identifies the telecommand packet within its
779/// communications session.
780#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Debug, Default)]
781#[cfg_attr(kani, derive(kani::Arbitrary))]
782pub struct PacketSequenceCount(u16);
783
784impl PacketSequenceCount {
785    const MAX: u16 = 0b0011_1111_1111_1111u16;
786
787    /// The packet sequence count is initialized to zero by default.
788    pub fn new() -> Self {
789        Self(0)
790    }
791
792    /// Helper functions used during formal verification to create a packet sequence count that is
793    /// actually within the stated bounds, since we cannot use the type system to express this
794    /// range.
795    #[cfg(kani)]
796    fn any_packet_sequence_count() -> Self {
797        match kani::any() {
798            any @ 0..=Self::MAX => Self(any),
799            _ => Self(42),
800        }
801    }
802
803    /// A good default behaviour is for the packet sequence count to increment by one every time
804    /// a new packet is sent. This method permits a simple wrapping increment to be performed, to
805    /// make this easier.
806    pub fn increment(&mut self) {
807        self.0 += 1;
808        if self.0 > Self::MAX {
809            self.0 = 0;
810        }
811    }
812}
813
814/// Test harness for formal verification.
815#[cfg(kani)]
816mod kani_harness {
817    use super::*;
818    use ::kani;
819
820    /// This test verifies that all possible primary headers may be parsed for all packets up to
821    /// u16::MAX in size, without panics. Note that the packet data field is assumed to always be
822    /// zero here. This is needed to restrict the search space for kani, and is a valid assumption
823    /// because the parsing implementation never touches the packet data field contents.
824    #[kani::proof]
825    fn header_parsing() {
826        let mut bytes = [0u8; u16::MAX as usize];
827        bytes[0] = kani::any();
828        bytes[1] = kani::any();
829        bytes[2] = kani::any();
830        bytes[3] = kani::any();
831        bytes[4] = kani::any();
832        bytes[5] = kani::any();
833        bytes[6] = kani::any();
834
835        let packet = SpacePacket::parse(&bytes);
836        if let Ok(packet) = packet {
837            assert!(packet.packet_length() <= bytes.len());
838            assert_eq!(
839                packet.packet_data_field().len(),
840                packet.packet_data_length()
841            );
842            assert!(packet.apid().0 <= 0b0000_0111_1111_1111);
843        }
844    }
845
846    /// This test verifies that all (!) possible packet construction requests can be handled
847    /// without panics when working with a fixed-size buffer that does not permit all possible
848    /// packet size requests. Here, we do not touch the data field, to prevent exponential blow-up
849    /// of the proof pipeline. Since the packet constructor performs no actions on the packet data
850    /// field beyond returning a reference to it, this makes for a strong proof about the safety of
851    /// this function.
852    ///
853    /// The buffer size is rather arbitrarily chosen to be 1024. This covers a significant amount
854    /// of valid packet sizes, but also ensures that the "error path" is covered, where the
855    /// requested packet is larger than the available buffer.
856    #[kani::proof]
857    fn packet_construction() {
858        let mut bytes = [kani::any(); 1024];
859        let maximum_packet_length = bytes.len();
860        let packet_type = kani::any();
861        let secondary_header_flag = kani::any();
862        let apid = Apid::any_apid();
863        let sequence_flag = kani::any();
864        let sequence_count = PacketSequenceCount::any_packet_sequence_count();
865        let packet_data_length = kani::any();
866
867        let packet = SpacePacket::construct(
868            &mut bytes,
869            packet_type,
870            secondary_header_flag,
871            apid,
872            sequence_flag,
873            sequence_count,
874            packet_data_length,
875        );
876
877        // First, we verify that all valid requests result in a returned packet.
878        let valid_request = packet_data_length != 0
879            && (packet_data_length as usize)
880                <= (maximum_packet_length - SpacePacket::primary_header_size() as usize);
881        if valid_request {
882            assert!(packet.is_ok());
883        }
884
885        // Vice versa, any invalid requests must be rejected.
886        if !valid_request {
887            assert!(!packet.is_ok());
888        }
889
890        // These checks ensure that any returned packet is indeed consistent with the requested
891        // packet header information.
892        if let Ok(packet) = packet {
893            assert!(packet.packet_length() <= maximum_packet_length);
894            assert_eq!(
895                packet.packet_data_field().len(),
896                packet.packet_data_length()
897            );
898
899            assert_eq!(packet.packet_type(), packet_type);
900            assert_eq!(packet.secondary_header_flag(), secondary_header_flag);
901            assert_eq!(packet.apid(), apid);
902            assert_eq!(packet.sequence_flag(), sequence_flag);
903            assert_eq!(packet.packet_sequence_count(), sequence_count);
904            assert_eq!(packet.packet_data_length(), packet_data_length as usize);
905        }
906    }
907}
908
909/// Test generated for harness `kani_harness::packet_construction` after assertion failure. Test
910/// case initially failed on resizing the packet to the proper length when a larger byte buffer was
911/// passed than what was covered by the packet contents.
912#[test]
913fn kani_failure1() {
914    const BYTES: usize = 16;
915    let mut bytes = [0; BYTES];
916    let packet = SpacePacket::construct(
917        &mut bytes,
918        PacketType::Telecommand,
919        SecondaryHeaderFlag::Present,
920        Apid::new(0),
921        SequenceFlag::Unsegmented,
922        PacketSequenceCount(65535),
923        8,
924    );
925
926    if let Ok(packet) = packet {
927        assert!(packet.packet_length() <= BYTES);
928        assert_eq!(
929            packet.packet_data_field().len(),
930            packet.packet_data_length(),
931            "Packet data field length does not match packet data field as stored: {packet:?}"
932        );
933        assert!(packet.apid().0 <= 0b0000_0111_1111_1111);
934    }
935}
936
937/// Deserialization of a relatively trivial packet. Used to verify that all basic deserialization
938/// logic is correct.
939#[test]
940fn deserialize_trivial_packet() {
941    let bytes = &[
942        0b0000_1000u8,
943        0b0000_0000u8,
944        0b1100_0000u8,
945        0b0000_0000u8,
946        0b0000_0000u8,
947        0b0000_0000u8,
948        0b0000_0000u8,
949    ];
950    let packet = SpacePacket::parse(bytes).unwrap();
951
952    assert_eq!(packet.packet_length(), 7);
953    assert_eq!(
954        packet.packet_version(),
955        PacketVersionNumber::version1_ccsds_packet()
956    );
957    assert_eq!(packet.packet_type(), PacketType::Telemetry);
958    assert_eq!(packet.secondary_header_flag(), SecondaryHeaderFlag::Present);
959    assert_eq!(packet.apid(), Apid::new(0));
960    assert_eq!(packet.sequence_flag(), SequenceFlag::Unsegmented);
961    assert_eq!(packet.packet_sequence_count(), PacketSequenceCount(0));
962    assert_eq!(packet.packet_data_length(), 1);
963    assert_eq!(packet.packet_data_field(), &bytes[6..]);
964}
965
966/// Serialization of a relatively trivial packet. Used to verify that all serialization logic is
967/// correct.
968#[test]
969fn serialize_trivial_packet() {
970    let mut bytes = [0u8; 7];
971    let packet = SpacePacket::construct(
972        &mut bytes,
973        PacketType::Telemetry,
974        SecondaryHeaderFlag::Present,
975        Apid::new(0),
976        SequenceFlag::Unsegmented,
977        PacketSequenceCount(0),
978        1,
979    )
980    .unwrap();
981
982    assert_eq!(packet.packet_length(), 7);
983    assert_eq!(
984        packet.packet_version(),
985        PacketVersionNumber::version1_ccsds_packet()
986    );
987    assert_eq!(packet.packet_type(), PacketType::Telemetry);
988    assert_eq!(packet.secondary_header_flag(), SecondaryHeaderFlag::Present);
989    assert_eq!(packet.apid(), Apid::new(0));
990    assert_eq!(packet.sequence_flag(), SequenceFlag::Unsegmented);
991    assert_eq!(packet.packet_sequence_count(), PacketSequenceCount(0));
992    assert_eq!(packet.packet_data_length(), 1);
993    assert_eq!(
994        packet.packet_data_field(),
995        &[
996            0b0000_1000u8,
997            0b0000_0000u8,
998            0b1100_0000u8,
999            0b0000_0000u8,
1000            0b0000_0000u8,
1001            0b0000_0000u8,
1002            0b0000_0000u8,
1003        ][6..]
1004    );
1005}
1006
1007/// Roundtrip serialization and subsequent deserialization of Space Packets shall result in exactly
1008/// identical byte slices for any valid (!) input. We test this by generating 10,000 random space
1009/// packets and seeing whether they remain identical through this transformation.
1010///
1011/// Since this test only considers valid inputs, other unit tests are needed to cover off-nominal
1012/// cases, such as when the buffer is too small or when the requested data field size is 0.
1013#[test]
1014fn roundtrip() {
1015    use rand::{RngCore, SeedableRng};
1016    // Note that we always use the same seed for reproducibility.
1017    let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1018    let mut buffer = [0u8; 16000];
1019    for _ in 0..10_000 {
1020        let packet_type = match rng.next_u32() & 1 {
1021            0 => PacketType::Telemetry,
1022            1 => PacketType::Telecommand,
1023            _ => unreachable!(),
1024        };
1025        let secondary_header_flag = match rng.next_u32() & 1 {
1026            0 => SecondaryHeaderFlag::Absent,
1027            1 => SecondaryHeaderFlag::Present,
1028            _ => unreachable!(),
1029        };
1030        let apid = Apid::new((rng.next_u32() & Apid::MAX as u32) as u16);
1031        let sequence_flag = match rng.next_u32() & 3 {
1032            0b00 => SequenceFlag::Continuation,
1033            0b01 => SequenceFlag::First,
1034            0b10 => SequenceFlag::Last,
1035            0b11 => SequenceFlag::Unsegmented,
1036            _ => unreachable!(),
1037        };
1038        let sequence_count =
1039            PacketSequenceCount((rng.next_u32() & PacketSequenceCount::MAX as u32) as u16);
1040
1041        let packet_data_length = (rng.next_u32() % (buffer.len() as u32 - 7)) as u16 + 1;
1042
1043        let space_packet = SpacePacket::construct(
1044            &mut buffer,
1045            packet_type,
1046            secondary_header_flag,
1047            apid,
1048            sequence_flag,
1049            sequence_count,
1050            packet_data_length,
1051        )
1052        .unwrap();
1053
1054        assert_eq!(
1055            packet_type,
1056            space_packet.packet_type(),
1057            "Serialized packet type ({:?}) does not match with final deserialized packet type ({:?}) for packet ({:?})",
1058            packet_type,
1059            space_packet.packet_type(),
1060            space_packet
1061        );
1062
1063        assert_eq!(
1064            secondary_header_flag,
1065            space_packet.secondary_header_flag(),
1066            "Serialized secondary header flag ({:?}) does not match with final deserialized secondary header flag ({:?}) for packet ({:?})",
1067            secondary_header_flag,
1068            space_packet.secondary_header_flag(),
1069            space_packet
1070        );
1071
1072        assert_eq!(
1073            apid,
1074            space_packet.apid(),
1075            "Serialized APID ({:?}) does not match with final deserialized APID ({:?}) for packet ({:?})",
1076            apid,
1077            space_packet.apid(),
1078            space_packet
1079        );
1080
1081        assert_eq!(
1082            sequence_flag,
1083            space_packet.sequence_flag(),
1084            "Serialized sequence flag ({:?}) does not match with final deserialized sequence flag ({:?}) for packet ({:?})",
1085            sequence_flag,
1086            space_packet.sequence_flag(),
1087            space_packet
1088        );
1089
1090        assert_eq!(
1091            sequence_count,
1092            space_packet.packet_sequence_count(),
1093            "Serialized sequence count ({:?}) does not match with final deserialized sequence count ({:?}) for packet ({:?})",
1094            sequence_count,
1095            space_packet.packet_sequence_count(),
1096            space_packet
1097        );
1098
1099        assert_eq!(
1100            packet_data_length as usize,
1101            space_packet.packet_data_length(),
1102            "Serialized packet type ({:?}) does not match with final deserialized packet type ({:?}) for packet ({:?})",
1103            packet_data_length,
1104            space_packet.packet_data_length(),
1105            space_packet
1106        );
1107    }
1108}
1109
1110/// Empty packet data fields are not permitted by CCSDS 133.0-B-2, so such requests must be
1111/// rejected.
1112#[test]
1113fn empty_packet_data_field() {
1114    let mut bytes = [0u8; 7];
1115    let result = SpacePacket::construct(
1116        &mut bytes,
1117        PacketType::Telemetry,
1118        SecondaryHeaderFlag::Present,
1119        Apid::new(0),
1120        SequenceFlag::Unsegmented,
1121        PacketSequenceCount(0),
1122        0,
1123    );
1124    assert_eq!(result, Err(PacketAssemblyError::EmptyDataFieldRequested));
1125}
1126
1127/// When the buffer to construct a Space Packet in is too small to contain a packet primary header,
1128/// this shall be caught and an error shall be returned, independent of the actual packet request.
1129#[test]
1130fn buffer_too_small_for_header_construction() {
1131    let mut buffer = [0u8; 5];
1132    let buffer_length = buffer.len();
1133    let result = SpacePacket::construct(
1134        &mut buffer,
1135        PacketType::Telemetry,
1136        SecondaryHeaderFlag::Present,
1137        Apid::new(0),
1138        SequenceFlag::Unsegmented,
1139        PacketSequenceCount(0),
1140        1,
1141    );
1142    assert_eq!(
1143        result,
1144        Err(PacketAssemblyError::BufferTooSmall {
1145            buffer_length,
1146            packet_length: 7
1147        })
1148    );
1149}
1150
1151/// When the buffer to construct a Space Packet in is too small to contain the full packet, an
1152/// error shall be returned stating as such.
1153#[test]
1154fn buffer_too_small_for_packet_construction() {
1155    use rand::{RngCore, SeedableRng};
1156    // Note that we always use the same seed for reproducibility.
1157    let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1158    let mut buffer = [0u8; 128];
1159    let buffer_length = buffer.len();
1160
1161    for _ in 0..1000 {
1162        // Generate a pseudo-random packet data length between 128 and u16::MAX.
1163        let packet_data_length = (rng.next_u32() % (u16::MAX - 128) as u32) as u16 + 128;
1164        let result = SpacePacket::construct(
1165            &mut buffer,
1166            PacketType::Telemetry,
1167            SecondaryHeaderFlag::Present,
1168            Apid::new(0),
1169            SequenceFlag::Unsegmented,
1170            PacketSequenceCount(0),
1171            packet_data_length,
1172        );
1173        assert_eq!(
1174            result,
1175            Err(PacketAssemblyError::BufferTooSmall {
1176                buffer_length,
1177                packet_length: packet_data_length as usize + SpacePacket::primary_header_size(),
1178            })
1179        );
1180    }
1181}
1182
1183/// When the buffer to parse a packet from is too small, an error shall be returned to indicate
1184/// this.
1185#[test]
1186fn buffer_too_small_for_parsed_packet() {
1187    use rand::{RngCore, SeedableRng};
1188    // Note that we always use the same seed for reproducibility.
1189    let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1190    let mut buffer = [0u8; 256];
1191
1192    for _ in 0..1000 {
1193        // Generate a pseudo-random packet data length between 128 and 250, so that the resulting
1194        // packet will fit on a 256-byte buffer.
1195        let packet_data_length = (rng.next_u32() % 128) as u16 + 122;
1196
1197        // Construct a valid Space Packet.
1198        let packet = SpacePacket::construct(
1199            &mut buffer,
1200            PacketType::Telemetry,
1201            SecondaryHeaderFlag::Present,
1202            Apid::new(0),
1203            SequenceFlag::Unsegmented,
1204            PacketSequenceCount(0),
1205            packet_data_length,
1206        )
1207        .unwrap();
1208
1209        // Subsequently, truncate the resulting byte sequence to 127 bytes, so that it will always
1210        // be invalid (the stored packet data length will always correspond with a packet larger
1211        // than 127 bytes).
1212        let bytes = &packet.as_bytes()[..127];
1213        let result = SpacePacket::parse(bytes);
1214        assert_eq!(
1215            result,
1216            Err(InvalidSpacePacket::PartialPacket {
1217                packet_size: packet_data_length as usize + SpacePacket::primary_header_size(),
1218                buffer_size: bytes.len()
1219            })
1220        );
1221    }
1222}