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
431/// Representation of only the fixed-size primary header part of a space packet. Used to construct
432/// generic space packets, but mostly useful in permitting composition of derived packet types,
433/// like PUS packets; otherwise, the dynamically-sized data field member would get in the way of
434/// including the primary header directly in derived packets.
435#[repr(C, packed)]
436#[derive(ByteEq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
437pub struct SpacePacketPrimaryHeader {
438 packet_identification: network_endian::U16,
439 packet_sequence_control: network_endian::U16,
440 data_length: network_endian::U16,
441}
442
443impl SpacePacketPrimaryHeader {
444 /// Validates that the Space Packet primary header is valid, in that its fields are coherent.
445 /// In particular, it is verified that the version number is that of a supported Space Packet.
446 ///
447 /// Note that this concerns semantic validity. The implementation shall not depend on this for
448 /// memory safety.
449 fn validate(&self) -> Result<(), InvalidSpacePacket> {
450 // We verify that the packet version found in the packet header is a version that is
451 // supported by this library.
452 let version = self.packet_version();
453 if !version.is_supported() {
454 return Err(InvalidSpacePacket::UnsupportedPacketVersion { version });
455 }
456
457 // Idle packets may not contain a secondary header field. If we do find that the secondary
458 // header flag is set, we must reject the packet.
459 if self.apid().is_idle() && self.secondary_header_flag() == SecondaryHeaderFlag::Present {
460 return Err(InvalidSpacePacket::IdlePacketWithSecondaryHeader);
461 }
462
463 Ok(())
464 }
465
466 /// Returns the size of a Space Packet primary header, in bytes. In the version that is
467 /// presently implemented, that is always 6 bytes.
468 pub const fn primary_header_size() -> usize {
469 6
470 }
471
472 /// Since the Space Packet protocol may technically support alternative packet structures in
473 /// future versions, the 3-bit packet version field may not actually contain a "correct" value.
474 pub fn packet_version(&self) -> PacketVersionNumber {
475 use core::ops::Shr;
476 PacketVersionNumber(self.packet_identification.as_bytes()[0].shr(5))
477 }
478
479 /// Initializes the packet version to the proper value. Must be a fixed value, so this function
480 /// takes no arguments.
481 pub fn initialize_packet_version(&mut self) {
482 self.packet_identification.as_mut_bytes()[0] &= 0b0001_1111;
483 self.packet_identification.as_mut_bytes()[0] |=
484 PacketVersionNumber::version1_ccsds_packet().0 << 5;
485 }
486
487 /// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
488 /// packet. Note that the exact definition of telecommand and telemetry may differ per system,
489 /// and indeed the "correct" value here may differ per project.
490 pub fn packet_type(&self) -> PacketType {
491 match (self.packet_identification.as_bytes()[0] & 0x10) == 0x10 {
492 true => PacketType::Telecommand,
493 false => PacketType::Telemetry,
494 }
495 }
496
497 /// Sets the packet type to the given value.
498 pub fn set_packet_type(&mut self, packet_type: PacketType) {
499 self.packet_identification.as_mut_bytes()[0] &= 0b1110_1111;
500 self.packet_identification.as_mut_bytes()[0] |= (packet_type as u8) << 4;
501 }
502
503 /// Denotes whether the packet contains a secondary header. If no user field is present, the
504 /// secondary header is mandatory (presumably, to ensure that some data is always transferred,
505 /// considering the Space Packet header itself contains no meaningful data).
506 pub fn secondary_header_flag(&self) -> SecondaryHeaderFlag {
507 match (self.packet_identification.as_bytes()[0] & 0x08) == 0x08 {
508 true => SecondaryHeaderFlag::Present,
509 false => SecondaryHeaderFlag::Absent,
510 }
511 }
512
513 /// Updates the value of the secondary header flag with the provided value.
514 pub fn set_secondary_header_flag(&mut self, secondary_header_flag: SecondaryHeaderFlag) {
515 self.packet_identification.as_mut_bytes()[0] &= 0b1111_0111;
516 self.packet_identification.as_mut_bytes()[0] |= (secondary_header_flag as u8) << 3;
517 }
518
519 /// Returns the application process ID stored in the packet. The actual meaning of this APID
520 /// field may differ per implementation: technically, it only represents "some" data path.
521 /// In practice, it will often be a identifier for a data channel, the packet source, or the
522 /// packet destination.
523 pub fn apid(&self) -> Apid {
524 Apid(self.packet_identification.get() & 0b0000_0111_1111_1111)
525 }
526
527 /// Sets the APID used to route the packet to the given value.
528 pub fn set_apid(&mut self, apid: Apid) {
529 let apid = apid.0.to_be_bytes();
530 self.packet_identification.as_mut_bytes()[0] &= 0b1111_1000;
531 self.packet_identification.as_mut_bytes()[0] |= apid[0] & 0b0000_0111;
532 self.packet_identification.as_mut_bytes()[1] = apid[1];
533 }
534
535 /// Sequence flags may be used to indicate that the data contained in a packet is only part of
536 /// a larger set of application data.
537 pub fn sequence_flag(&self) -> SequenceFlag {
538 use core::ops::Shr;
539 match self.packet_sequence_control.as_bytes()[0].shr(6i32) {
540 0b00 => SequenceFlag::Continuation,
541 0b01 => SequenceFlag::First,
542 0b10 => SequenceFlag::Last,
543 0b11 => SequenceFlag::Unsegmented,
544 _ => unreachable!("Internal error: Reached unreachable code segment"),
545 }
546 }
547
548 /// Sets the sequence flag to the provided value.
549 pub fn set_sequence_flag(&mut self, sequence_flag: SequenceFlag) {
550 self.packet_sequence_control.as_mut_bytes()[0] &= 0b0011_1111;
551 self.packet_sequence_control.as_mut_bytes()[0] |= (sequence_flag as u8) << 6;
552 }
553
554 /// The packet sequence count is unique per APID and denotes the sequential binary count of
555 /// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
556 /// may also be a "packet name" that identifies the telecommand packet within its
557 /// communications session.
558 pub fn packet_sequence_count(&self) -> PacketSequenceCount {
559 PacketSequenceCount(self.packet_sequence_control.get() & 0b0011_1111_1111_1111)
560 }
561
562 /// Sets the packet sequence count to the provided value. This value must be provided by an
563 /// external counter and is not provided at a Space Packet type level because it might differ
564 /// between packet streams.
565 pub fn set_packet_sequence_count(&mut self, sequence_count: PacketSequenceCount) {
566 self.packet_sequence_control.as_mut_bytes()[0] &= 0b1100_0000;
567 self.packet_sequence_control.as_mut_bytes()[0] |=
568 sequence_count.0.to_be_bytes()[0] & 0b0011_1111;
569 self.packet_sequence_control.as_mut_bytes()[1] = sequence_count.0.to_be_bytes()[1];
570 }
571
572 /// The packet data length field represents the length of the associated packet data field.
573 /// However, it is not stored directly: rather, the "length count" is stored, which is the
574 /// packet data length minus one.
575 pub fn packet_data_length(&self) -> usize {
576 self.data_length.get() as usize + 1
577 }
578
579 /// Sets the packet data length field to the provided value. Note that the given value is not
580 /// stored directly, but rather decremented by one first. Accordingly, and as per the CCSDS
581 /// Space Packet Protocol standard, packet data lengths of 0 are not allowed.
582 pub fn set_packet_data_length(
583 &mut self,
584 packet_data_length: u16,
585 ) -> Result<(), InvalidPacketDataLength> {
586 if packet_data_length == 0 {
587 return Err(InvalidPacketDataLength::EmptyDataField);
588 }
589
590 let stored_data_field_length = packet_data_length - 1;
591 self.data_length.set(stored_data_field_length);
592 Ok(())
593 }
594}
595
596/// Because `SpacePacket` is `repr(packed)` and `SpacePacket::data_field` is unsized, the default
597/// `core::fmt::Debug` implementation cannot be derived.
598impl core::fmt::Debug for SpacePacket {
599 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
600 write!(
601 f,
602 "SpacePacket {{ version number: {:?}, packet type: {:?}, secondary header flag: {:?}, APID: {:?}, sequence flags: {:?}, sequence count: {:?}, packet data length: {:?}, packet data: {:?} }}",
603 self.packet_version(),
604 self.packet_type(),
605 self.secondary_header_flag(),
606 self.apid(),
607 self.sequence_flag(),
608 self.packet_sequence_count(),
609 self.packet_data_length(),
610 self.packet_data_field(),
611 )
612 }
613}
614
615/// Representation of the set of errors that may be encountered while deserializing a Space Packet.
616/// Marked as non-exhaustive to permit extension with additional semantic errors in the future
617/// without breaking API.
618#[non_exhaustive]
619#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
620pub enum InvalidSpacePacket {
621 /// Returned when a byte slice is too small to contain any Space Packet (i.e., is smaller than
622 /// a header with a single-byte user data field).
623 SliceTooSmallForSpacePacketHeader { length: usize },
624 /// Returned when a slice does not have a known and supported packet version. For convenience,
625 /// the packet version that is stored at the "conventional" (CCSDS packet version 0) is also
626 /// returned, though it does not need to be meaningful in other packet versions.
627 UnsupportedPacketVersion { version: PacketVersionNumber },
628 /// Returned when the decoded packet is not fully contained in the passed buffer.
629 PartialPacket {
630 packet_size: usize,
631 buffer_size: usize,
632 },
633 /// Returned when the Space Packet is idle (has an 'all ones' APID) but also contains a
634 /// secondary header. This is forbidden by CCSDS 133.0-B-2.
635 IdlePacketWithSecondaryHeader,
636}
637
638/// Representation of the set of errors that may be encountered while constructing a Space Packet.
639/// Marked as non-exhaustive to permit extension with additional semantic errors in the future
640/// without breaking API.
641#[non_exhaustive]
642#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
643pub enum PacketAssemblyError {
644 /// Returned when the underlying buffer does not have sufficient bytes to contain a given space
645 /// packet.
646 BufferTooSmall {
647 buffer_length: usize,
648 packet_length: usize,
649 },
650 /// As per the CCSDS standard, Space Packets shall have at least one byte in their data field.
651 /// Hence, requests for an empty data field must be rejected.
652 EmptyDataFieldRequested,
653}
654
655/// This error may be returned when setting the data field of some newly-constructed Space Packet
656/// if the requested packet data length is 0 (which is generally illegal) or if the requested
657/// packet data length does not fit in the buffer on which the packet must be stored.
658#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
659pub enum InvalidPacketDataLength {
660 EmptyDataField,
661 LargerThanPacketDataBuffer {
662 packet_data_length: u16,
663 buffer_length: usize,
664 },
665}
666
667impl From<InvalidPacketDataLength> for PacketAssemblyError {
668 fn from(value: InvalidPacketDataLength) -> Self {
669 match value {
670 InvalidPacketDataLength::EmptyDataField => PacketAssemblyError::EmptyDataFieldRequested,
671 InvalidPacketDataLength::LargerThanPacketDataBuffer {
672 packet_data_length,
673 buffer_length,
674 } => PacketAssemblyError::BufferTooSmall {
675 buffer_length: buffer_length + SpacePacket::primary_header_size(),
676 packet_length: packet_data_length as usize + SpacePacket::primary_header_size(),
677 },
678 }
679 }
680}
681
682/// The packet version number represents the version of the Space Packet protocol that is used. In
683/// the version presently implemented, this is defined to be zeroes.
684#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
685pub struct PacketVersionNumber(u8);
686
687impl PacketVersionNumber {
688 /// The Space Packet protocol version presently implemented in this crate is based on issue 2
689 /// of the CCSDS SPP blue book, which encompasses only the Version 1 CCSDS Packet, indicated by
690 /// a version number of 0. Other packet structures may be added in the future.
691 pub fn is_supported(&self) -> bool {
692 matches!(self.0, 0b0000_0000u8)
693 }
694
695 /// Returns the packet version number corresponding with the Version 1 CCSDS Packet.
696 pub fn version1_ccsds_packet() -> Self {
697 Self(0)
698 }
699}
700
701/// The packet type denotes whether a packet is a telecommand (request) or telemetry (report)
702/// packet. Note that the exact definition of telecommand and telemetry may differ per system,
703/// and indeed the "correct" value here may differ per project.
704#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
705#[cfg_attr(kani, derive(kani::Arbitrary))]
706pub enum PacketType {
707 Telemetry = 0,
708 Telecommand = 1,
709}
710
711/// Denotes whether the packet contains a secondary header. If no user field is present, the
712/// secondary header is mandatory (presumably, to ensure that some data is always transferred,
713/// considering the Space Packet header itself contains no meaningful data).
714#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
715#[cfg_attr(kani, derive(kani::Arbitrary))]
716pub enum SecondaryHeaderFlag {
717 Absent = 0,
718 Present = 1,
719}
720
721/// Returns the application process ID stored in the packet. The actual meaning of this APID
722/// field may differ per implementation: technically, it only represents "some" data path.
723/// In practice, it will often be a identifier for: a data channel, the packet source, or the
724/// packet destination.
725#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
726#[cfg_attr(kani, derive(kani::Arbitrary))]
727pub struct Apid(u16);
728
729impl Apid {
730 const MAX: u16 = 0b0000_0111_1111_1111u16;
731
732 pub fn new(id: u16) -> Self {
733 assert!(
734 id <= Self::MAX,
735 "APIDs may not exceed 2047 (due to maximum of 13 bits in representation)"
736 );
737 Self(id)
738 }
739
740 /// Helper functions used during formal verification to create an APID that is actually within
741 /// the stated bounds, since we cannot use the type system to express this range.
742 #[cfg(kani)]
743 fn any_apid() -> Self {
744 match kani::any() {
745 any @ 0..=Self::MAX => Self(any),
746 _ => Self(42),
747 }
748 }
749
750 /// A special APID value (0x7ff) is reserved for idle Space Packets, i.e., packets that do not
751 /// carry any actual data.
752 pub fn is_idle(&self) -> bool {
753 self.0 == 0x7ff
754 }
755}
756
757/// Sequence flags may be used to indicate that the data contained in a packet is only part of
758/// a larger set of application data.
759#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
760#[cfg_attr(kani, derive(kani::Arbitrary))]
761pub enum SequenceFlag {
762 Continuation = 0b00,
763 First = 0b01,
764 Last = 0b10,
765 #[default]
766 Unsegmented = 0b11,
767}
768
769/// The packet sequence count is unique per APID and denotes the sequential binary count of
770/// each Space Packet (generated per APID). For telecommands (i.e., with packet type 1) this
771/// may also be a "packet name" that identifies the telecommand packet within its
772/// communications session.
773#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Debug, Default)]
774#[cfg_attr(kani, derive(kani::Arbitrary))]
775pub struct PacketSequenceCount(u16);
776
777impl PacketSequenceCount {
778 const MAX: u16 = 0b0011_1111_1111_1111u16;
779
780 /// The packet sequence count is initialized to zero by default.
781 pub fn new() -> Self {
782 Self(0)
783 }
784
785 /// Helper functions used during formal verification to create a packet sequence count that is
786 /// actually within the stated bounds, since we cannot use the type system to express this
787 /// range.
788 #[cfg(kani)]
789 fn any_packet_sequence_count() -> Self {
790 match kani::any() {
791 any @ 0..=Self::MAX => Self(any),
792 _ => Self(42),
793 }
794 }
795
796 /// A good default behaviour is for the packet sequence count to increment by one every time
797 /// a new packet is sent. This method permits a simple wrapping increment to be performed, to
798 /// make this easier.
799 pub fn increment(&mut self) {
800 self.0 += 1;
801 if self.0 > Self::MAX {
802 self.0 = 0;
803 }
804 }
805}
806
807/// Test harness for formal verification.
808#[cfg(kani)]
809mod kani_harness {
810 use super::*;
811 use ::kani;
812
813 /// This test verifies that all possible primary headers may be parsed for all packets up to
814 /// u16::MAX in size, without panics. Note that the packet data field is assumed to always be
815 /// zero here. This is needed to restrict the search space for kani, and is a valid assumption
816 /// because the parsing implementation never touches the packet data field contents.
817 #[kani::proof]
818 fn header_parsing() {
819 let mut bytes = [0u8; u16::MAX as usize];
820 bytes[0] = kani::any();
821 bytes[1] = kani::any();
822 bytes[2] = kani::any();
823 bytes[3] = kani::any();
824 bytes[4] = kani::any();
825 bytes[5] = kani::any();
826 bytes[6] = kani::any();
827
828 let packet = SpacePacket::parse(&bytes);
829 if let Ok(packet) = packet {
830 assert!(packet.packet_length() <= bytes.len());
831 assert_eq!(
832 packet.packet_data_field().len(),
833 packet.packet_data_length()
834 );
835 assert!(packet.apid().0 <= 0b0000_0111_1111_1111);
836 }
837 }
838
839 /// This test verifies that all (!) possible packet construction requests can be handled
840 /// without panics when working with a fixed-size buffer that does not permit all possible
841 /// packet size requests. Here, we do not touch the data field, to prevent exponential blow-up
842 /// of the proof pipeline. Since the packet constructor performs no actions on the packet data
843 /// field beyond returning a reference to it, this makes for a strong proof about the safety of
844 /// this function.
845 ///
846 /// The buffer size is rather arbitrarily chosen to be 1024. This covers a significant amount
847 /// of valid packet sizes, but also ensures that the "error path" is covered, where the
848 /// requested packet is larger than the available buffer.
849 #[kani::proof]
850 fn packet_construction() {
851 let mut bytes = [kani::any(); 1024];
852 let maximum_packet_length = bytes.len();
853 let packet_type = kani::any();
854 let secondary_header_flag = kani::any();
855 let apid = Apid::any_apid();
856 let sequence_flag = kani::any();
857 let sequence_count = PacketSequenceCount::any_packet_sequence_count();
858 let packet_data_length = kani::any();
859
860 let packet = SpacePacket::construct(
861 &mut bytes,
862 packet_type,
863 secondary_header_flag,
864 apid,
865 sequence_flag,
866 sequence_count,
867 packet_data_length,
868 );
869
870 // First, we verify that all valid requests result in a returned packet.
871 let valid_request = packet_data_length != 0
872 && (packet_data_length as usize)
873 <= (maximum_packet_length - SpacePacket::primary_header_size() as usize);
874 if valid_request {
875 assert!(packet.is_ok());
876 }
877
878 // Vice versa, any invalid requests must be rejected.
879 if !valid_request {
880 assert!(!packet.is_ok());
881 }
882
883 // These checks ensure that any returned packet is indeed consistent with the requested
884 // packet header information.
885 if let Ok(packet) = packet {
886 assert!(packet.packet_length() <= maximum_packet_length);
887 assert_eq!(
888 packet.packet_data_field().len(),
889 packet.packet_data_length()
890 );
891
892 assert_eq!(packet.packet_type(), packet_type);
893 assert_eq!(packet.secondary_header_flag(), secondary_header_flag);
894 assert_eq!(packet.apid(), apid);
895 assert_eq!(packet.sequence_flag(), sequence_flag);
896 assert_eq!(packet.packet_sequence_count(), sequence_count);
897 assert_eq!(packet.packet_data_length(), packet_data_length as usize);
898 }
899 }
900}
901
902/// Test generated for harness `kani_harness::packet_construction` after assertion failure. Test
903/// case initially failed on resizing the packet to the proper length when a larger byte buffer was
904/// passed than what was covered by the packet contents.
905#[test]
906fn kani_failure1() {
907 const BYTES: usize = 16;
908 let mut bytes = [0; BYTES];
909 let packet = SpacePacket::construct(
910 &mut bytes,
911 PacketType::Telecommand,
912 SecondaryHeaderFlag::Present,
913 Apid::new(0),
914 SequenceFlag::Unsegmented,
915 PacketSequenceCount(65535),
916 8,
917 );
918
919 if let Ok(packet) = packet {
920 assert!(packet.packet_length() <= BYTES);
921 assert_eq!(
922 packet.packet_data_field().len(),
923 packet.packet_data_length(),
924 "Packet data field length does not match packet data field as stored: {packet:?}"
925 );
926 assert!(packet.apid().0 <= 0b0000_0111_1111_1111);
927 }
928}
929
930/// Deserialization of a relatively trivial packet. Used to verify that all basic deserialization
931/// logic is correct.
932#[test]
933fn deserialize_trivial_packet() {
934 let bytes = &[
935 0b0000_1000u8,
936 0b0000_0000u8,
937 0b1100_0000u8,
938 0b0000_0000u8,
939 0b0000_0000u8,
940 0b0000_0000u8,
941 0b0000_0000u8,
942 ];
943 let packet = SpacePacket::parse(bytes).unwrap();
944
945 assert_eq!(packet.packet_length(), 7);
946 assert_eq!(
947 packet.packet_version(),
948 PacketVersionNumber::version1_ccsds_packet()
949 );
950 assert_eq!(packet.packet_type(), PacketType::Telemetry);
951 assert_eq!(packet.secondary_header_flag(), SecondaryHeaderFlag::Present);
952 assert_eq!(packet.apid(), Apid::new(0));
953 assert_eq!(packet.sequence_flag(), SequenceFlag::Unsegmented);
954 assert_eq!(packet.packet_sequence_count(), PacketSequenceCount(0));
955 assert_eq!(packet.packet_data_length(), 1);
956 assert_eq!(packet.packet_data_field(), &bytes[6..]);
957}
958
959/// Serialization of a relatively trivial packet. Used to verify that all serialization logic is
960/// correct.
961#[test]
962fn serialize_trivial_packet() {
963 let mut bytes = [0u8; 7];
964 let packet = SpacePacket::construct(
965 &mut bytes,
966 PacketType::Telemetry,
967 SecondaryHeaderFlag::Present,
968 Apid::new(0),
969 SequenceFlag::Unsegmented,
970 PacketSequenceCount(0),
971 1,
972 )
973 .unwrap();
974
975 assert_eq!(packet.packet_length(), 7);
976 assert_eq!(
977 packet.packet_version(),
978 PacketVersionNumber::version1_ccsds_packet()
979 );
980 assert_eq!(packet.packet_type(), PacketType::Telemetry);
981 assert_eq!(packet.secondary_header_flag(), SecondaryHeaderFlag::Present);
982 assert_eq!(packet.apid(), Apid::new(0));
983 assert_eq!(packet.sequence_flag(), SequenceFlag::Unsegmented);
984 assert_eq!(packet.packet_sequence_count(), PacketSequenceCount(0));
985 assert_eq!(packet.packet_data_length(), 1);
986 assert_eq!(
987 packet.packet_data_field(),
988 &[
989 0b0000_1000u8,
990 0b0000_0000u8,
991 0b1100_0000u8,
992 0b0000_0000u8,
993 0b0000_0000u8,
994 0b0000_0000u8,
995 0b0000_0000u8,
996 ][6..]
997 );
998}
999
1000/// Roundtrip serialization and subsequent deserialization of Space Packets shall result in exactly
1001/// identical byte slices for any valid (!) input. We test this by generating 10,000 random space
1002/// packets and seeing whether they remain identical through this transformation.
1003///
1004/// Since this test only considers valid inputs, other unit tests are needed to cover off-nominal
1005/// cases, such as when the buffer is too small or when the requested data field size is 0.
1006#[test]
1007fn roundtrip() {
1008 use rand::{RngCore, SeedableRng};
1009 // Note that we always use the same seed for reproducibility.
1010 let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1011 let mut buffer = [0u8; 16000];
1012 for _ in 0..10_000 {
1013 let packet_type = match rng.next_u32() & 1 {
1014 0 => PacketType::Telemetry,
1015 1 => PacketType::Telecommand,
1016 _ => unreachable!(),
1017 };
1018 let secondary_header_flag = match rng.next_u32() & 1 {
1019 0 => SecondaryHeaderFlag::Absent,
1020 1 => SecondaryHeaderFlag::Present,
1021 _ => unreachable!(),
1022 };
1023 let apid = Apid::new((rng.next_u32() & Apid::MAX as u32) as u16);
1024 let sequence_flag = match rng.next_u32() & 3 {
1025 0b00 => SequenceFlag::Continuation,
1026 0b01 => SequenceFlag::First,
1027 0b10 => SequenceFlag::Last,
1028 0b11 => SequenceFlag::Unsegmented,
1029 _ => unreachable!(),
1030 };
1031 let sequence_count =
1032 PacketSequenceCount((rng.next_u32() & PacketSequenceCount::MAX as u32) as u16);
1033
1034 let packet_data_length = (rng.next_u32() % (buffer.len() as u32 - 7)) as u16 + 1;
1035
1036 let space_packet = SpacePacket::construct(
1037 &mut buffer,
1038 packet_type,
1039 secondary_header_flag,
1040 apid,
1041 sequence_flag,
1042 sequence_count,
1043 packet_data_length,
1044 )
1045 .unwrap();
1046
1047 assert_eq!(
1048 packet_type,
1049 space_packet.packet_type(),
1050 "Serialized packet type ({:?}) does not match with final deserialized packet type ({:?}) for packet ({:?})",
1051 packet_type,
1052 space_packet.packet_type(),
1053 space_packet
1054 );
1055
1056 assert_eq!(
1057 secondary_header_flag,
1058 space_packet.secondary_header_flag(),
1059 "Serialized secondary header flag ({:?}) does not match with final deserialized secondary header flag ({:?}) for packet ({:?})",
1060 secondary_header_flag,
1061 space_packet.secondary_header_flag(),
1062 space_packet
1063 );
1064
1065 assert_eq!(
1066 apid,
1067 space_packet.apid(),
1068 "Serialized APID ({:?}) does not match with final deserialized APID ({:?}) for packet ({:?})",
1069 apid,
1070 space_packet.apid(),
1071 space_packet
1072 );
1073
1074 assert_eq!(
1075 sequence_flag,
1076 space_packet.sequence_flag(),
1077 "Serialized sequence flag ({:?}) does not match with final deserialized sequence flag ({:?}) for packet ({:?})",
1078 sequence_flag,
1079 space_packet.sequence_flag(),
1080 space_packet
1081 );
1082
1083 assert_eq!(
1084 sequence_count,
1085 space_packet.packet_sequence_count(),
1086 "Serialized sequence count ({:?}) does not match with final deserialized sequence count ({:?}) for packet ({:?})",
1087 sequence_count,
1088 space_packet.packet_sequence_count(),
1089 space_packet
1090 );
1091
1092 assert_eq!(
1093 packet_data_length as usize,
1094 space_packet.packet_data_length(),
1095 "Serialized packet type ({:?}) does not match with final deserialized packet type ({:?}) for packet ({:?})",
1096 packet_data_length,
1097 space_packet.packet_data_length(),
1098 space_packet
1099 );
1100 }
1101}
1102
1103/// Empty packet data fields are not permitted by CCSDS 133.0-B-2, so such requests must be
1104/// rejected.
1105#[test]
1106fn empty_packet_data_field() {
1107 let mut bytes = [0u8; 7];
1108 let result = SpacePacket::construct(
1109 &mut bytes,
1110 PacketType::Telemetry,
1111 SecondaryHeaderFlag::Present,
1112 Apid::new(0),
1113 SequenceFlag::Unsegmented,
1114 PacketSequenceCount(0),
1115 0,
1116 );
1117 assert_eq!(result, Err(PacketAssemblyError::EmptyDataFieldRequested));
1118}
1119
1120/// When the buffer to construct a Space Packet in is too small to contain a packet primary header,
1121/// this shall be caught and an error shall be returned, independent of the actual packet request.
1122#[test]
1123fn buffer_too_small_for_header_construction() {
1124 let mut buffer = [0u8; 5];
1125 let buffer_length = buffer.len();
1126 let result = SpacePacket::construct(
1127 &mut buffer,
1128 PacketType::Telemetry,
1129 SecondaryHeaderFlag::Present,
1130 Apid::new(0),
1131 SequenceFlag::Unsegmented,
1132 PacketSequenceCount(0),
1133 1,
1134 );
1135 assert_eq!(
1136 result,
1137 Err(PacketAssemblyError::BufferTooSmall {
1138 buffer_length,
1139 packet_length: 7
1140 })
1141 );
1142}
1143
1144/// When the buffer to construct a Space Packet in is too small to contain the full packet, an
1145/// error shall be returned stating as such.
1146#[test]
1147fn buffer_too_small_for_packet_construction() {
1148 use rand::{RngCore, SeedableRng};
1149 // Note that we always use the same seed for reproducibility.
1150 let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1151 let mut buffer = [0u8; 128];
1152 let buffer_length = buffer.len();
1153
1154 for _ in 0..1000 {
1155 // Generate a pseudo-random packet data length between 128 and u16::MAX.
1156 let packet_data_length = (rng.next_u32() % (u16::MAX - 128) as u32) as u16 + 128;
1157 let result = SpacePacket::construct(
1158 &mut buffer,
1159 PacketType::Telemetry,
1160 SecondaryHeaderFlag::Present,
1161 Apid::new(0),
1162 SequenceFlag::Unsegmented,
1163 PacketSequenceCount(0),
1164 packet_data_length,
1165 );
1166 assert_eq!(
1167 result,
1168 Err(PacketAssemblyError::BufferTooSmall {
1169 buffer_length,
1170 packet_length: packet_data_length as usize + SpacePacket::primary_header_size(),
1171 })
1172 );
1173 }
1174}
1175
1176/// When the buffer to parse a packet from is too small, an error shall be returned to indicate
1177/// this.
1178#[test]
1179fn buffer_too_small_for_parsed_packet() {
1180 use rand::{RngCore, SeedableRng};
1181 // Note that we always use the same seed for reproducibility.
1182 let mut rng = rand::rngs::SmallRng::seed_from_u64(42);
1183 let mut buffer = [0u8; 256];
1184
1185 for _ in 0..1000 {
1186 // Generate a pseudo-random packet data length between 128 and 250, so that the resulting
1187 // packet will fit on a 256-byte buffer.
1188 let packet_data_length = (rng.next_u32() % 128) as u16 + 122;
1189
1190 // Construct a valid Space Packet.
1191 let packet = SpacePacket::construct(
1192 &mut buffer,
1193 PacketType::Telemetry,
1194 SecondaryHeaderFlag::Present,
1195 Apid::new(0),
1196 SequenceFlag::Unsegmented,
1197 PacketSequenceCount(0),
1198 packet_data_length,
1199 )
1200 .unwrap();
1201
1202 // Subsequently, truncate the resulting byte sequence to 127 bytes, so that it will always
1203 // be invalid (the stored packet data length will always correspond with a packet larger
1204 // than 127 bytes).
1205 let bytes = &packet.as_bytes()[..127];
1206 let result = SpacePacket::parse(bytes);
1207 assert_eq!(
1208 result,
1209 Err(InvalidSpacePacket::PartialPacket {
1210 packet_size: packet_data_length as usize + SpacePacket::primary_header_size(),
1211 buffer_size: bytes.len()
1212 })
1213 );
1214 }
1215}