spacepackets/
lib.rs

1//! # CCSDS and ECSS packet standards implementations
2//!
3//! This crate contains generic implementations for various
4//! CCSDS (Consultative Committee for Space Data Systems) and
5//! ECSS (European Cooperation for Space Standardization) packet standards.
6//! Currently, this includes the following components:
7//!
8//!  - Space Packet implementation according to
9//!    [CCSDS Blue Book 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf)
10//!  - CCSDS File Delivery Protocol (CFDP) packet implementations according to
11//!    [CCSDS Blue Book 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf)
12//!  - PUS Telecommand and PUS Telemetry implementation according to the
13//!    [ECSS-E-ST-70-41C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
14//!  - CUC (CCSDS Unsegmented Time Code) implementation according to
15//!    [CCSDS 301.0-B-4 3.2](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
16//!  - CDS (CCSDS Day Segmented Time Code) implementation according to
17//!    [CCSDS 301.0-B-4 3.3](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
18//!  - Some helper types to support ASCII timecodes as specified in
19//!    [CCSDS 301.0-B-4 3.5](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
20//!
21//! ## Features
22//!
23//! `spacepackets` supports various runtime environments and is also suitable for `no_std` environments.
24//!
25//! ### Default features
26//!
27//!  - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
28//!  - [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
29//!     like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
30//!     Enabled by the `std` feature.
31//!
32//! ### Optional features
33//!
34//!  - [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and
35//!    `Deserialize` `derives.
36//!  - [`chrono`](https://crates.io/crates/chrono): Add basic support for the `chrono` time library.
37//!  - [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library.
38//!  - [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
39//!    [`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
40//!
41//! ## Module
42//!
43//! This module contains helpers and data structures to generate Space Packets according to the
44//! [CCSDS 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf). This includes the
45//! [SpHeader] class to generate the Space Packet Header component common to all space packets.
46//!
47//! ## Example
48//!
49//! ```rust
50//! use spacepackets::SpHeader;
51//! let sp_header = SpHeader::new_for_unseg_tc_checked(0x42, 12, 1).expect("error creating CCSDS TC header");
52//! println!("{:?}", sp_header);
53//! let mut ccsds_buf: [u8; 32] = [0; 32];
54//! sp_header.write_to_be_bytes(&mut ccsds_buf).expect("Writing CCSDS TC header failed");
55//! println!("{:x?}", &ccsds_buf[0..6]);
56//! ```
57#![no_std]
58#![cfg_attr(docsrs, feature(doc_auto_cfg))]
59#[cfg(feature = "alloc")]
60extern crate alloc;
61#[cfg(any(feature = "std", test))]
62extern crate std;
63
64use core::{fmt::Debug, hash::Hash};
65use crc::{Crc, CRC_16_IBM_3740};
66use delegate::delegate;
67use zerocopy::{FromBytes, IntoBytes};
68
69#[cfg(feature = "serde")]
70use serde::{Deserialize, Serialize};
71
72pub mod cfdp;
73pub mod ecss;
74pub mod seq_count;
75pub mod time;
76pub mod util;
77
78mod private {
79    pub trait Sealed {}
80}
81
82pub const CCSDS_HEADER_LEN: usize = core::mem::size_of::<crate::zc::SpHeader>();
83
84/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard.
85pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
86
87pub const MAX_APID: u16 = 2u16.pow(11) - 1;
88pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1;
89
90/// Generic error type when converting to and from raw byte slices.
91#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
92#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
93#[cfg_attr(feature = "defmt", derive(defmt::Format))]
94pub enum ByteConversionError {
95    /// The passed slice is too small. Returns the passed slice length and expected minimum size
96    #[error("target slice with size {found} is too small, expected size of at least {expected}")]
97    ToSliceTooSmall { found: usize, expected: usize },
98    /// The provider buffer is too small. Returns the passed slice length and expected minimum size
99    #[error("source slice with size {found} too small, expected at least {expected} bytes")]
100    FromSliceTooSmall { found: usize, expected: usize },
101    /// The [zerocopy] library failed to write to bytes
102    #[error("zerocopy serialization error")]
103    ZeroCopyToError,
104    /// The [zerocopy] library failed to read from bytes
105    #[error("zerocopy deserialization error")]
106    ZeroCopyFromError,
107}
108
109/// CCSDS packet type enumeration.
110#[derive(Debug, PartialEq, Eq, Copy, Clone)]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112#[cfg_attr(feature = "defmt", derive(defmt::Format))]
113pub enum PacketType {
114    Tm = 0,
115    Tc = 1,
116}
117
118impl TryFrom<u8> for PacketType {
119    type Error = ();
120
121    fn try_from(value: u8) -> Result<Self, Self::Error> {
122        match value {
123            x if x == PacketType::Tm as u8 => Ok(PacketType::Tm),
124            x if x == PacketType::Tc as u8 => Ok(PacketType::Tc),
125            _ => Err(()),
126        }
127    }
128}
129
130pub fn packet_type_in_raw_packet_id(packet_id: u16) -> PacketType {
131    PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap()
132}
133
134#[derive(Debug, PartialEq, Eq, Copy, Clone)]
135#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137pub enum SequenceFlags {
138    ContinuationSegment = 0b00,
139    FirstSegment = 0b01,
140    LastSegment = 0b10,
141    Unsegmented = 0b11,
142}
143
144impl TryFrom<u8> for SequenceFlags {
145    type Error = ();
146
147    fn try_from(value: u8) -> Result<Self, Self::Error> {
148        match value {
149            x if x == SequenceFlags::ContinuationSegment as u8 => {
150                Ok(SequenceFlags::ContinuationSegment)
151            }
152            x if x == SequenceFlags::FirstSegment as u8 => Ok(SequenceFlags::FirstSegment),
153            x if x == SequenceFlags::LastSegment as u8 => Ok(SequenceFlags::LastSegment),
154            x if x == SequenceFlags::Unsegmented as u8 => Ok(SequenceFlags::Unsegmented),
155            _ => Err(()),
156        }
157    }
158}
159
160/// Abstraction for the CCSDS Packet ID, which forms the last thirteen bits
161/// of the first two bytes in the CCSDS primary header.
162#[derive(Debug, Eq, Copy, Clone)]
163#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
164#[cfg_attr(feature = "defmt", derive(defmt::Format))]
165pub struct PacketId {
166    pub ptype: PacketType,
167    pub sec_header_flag: bool,
168    apid: u16,
169}
170
171impl PartialEq for PacketId {
172    #[inline]
173    fn eq(&self, other: &Self) -> bool {
174        self.raw().eq(&other.raw())
175    }
176}
177
178impl PartialOrd for PacketId {
179    #[inline]
180    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
181        Some(self.cmp(other))
182    }
183}
184
185impl Ord for PacketId {
186    #[inline]
187    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
188        self.raw().cmp(&other.raw())
189    }
190}
191
192impl Hash for PacketId {
193    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
194        let raw = self.raw();
195        raw.hash(state);
196    }
197}
198
199impl Default for PacketId {
200    #[inline]
201    fn default() -> Self {
202        PacketId {
203            ptype: PacketType::Tm,
204            sec_header_flag: false,
205            apid: 0,
206        }
207    }
208}
209
210impl PacketId {
211    /// This constructor will panic if the passed APID exceeds [MAX_APID].
212    /// Use the checked constructor variants to avoid panics.
213    #[inline]
214    pub const fn new_for_tc(sec_header: bool, apid: u16) -> Self {
215        Self::new(PacketType::Tc, sec_header, apid)
216    }
217
218    /// This constructor will panic if the passed APID exceeds [MAX_APID].
219    /// Use the checked constructor variants to avoid panics.
220    #[inline]
221    pub const fn new_for_tm(sec_header: bool, apid: u16) -> Self {
222        Self::new(PacketType::Tm, sec_header, apid)
223    }
224
225    #[inline]
226    pub fn new_for_tc_checked(sec_header: bool, apid: u16) -> Option<Self> {
227        Self::new_checked(PacketType::Tc, sec_header, apid)
228    }
229
230    #[inline]
231    pub fn new_for_tm_checked(sec_header: bool, apid: u16) -> Option<Self> {
232        Self::new_checked(PacketType::Tm, sec_header, apid)
233    }
234
235    /// This constructor will panic if the passed APID exceeds [MAX_APID].
236    /// Use the checked variants to avoid panics.
237    #[inline]
238    pub const fn new(ptype: PacketType, sec_header: bool, apid: u16) -> Self {
239        if apid > MAX_APID {
240            panic!("APID too large");
241        }
242        PacketId {
243            ptype,
244            sec_header_flag: sec_header,
245            apid,
246        }
247    }
248
249    #[inline]
250    pub fn new_checked(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
251        if apid > MAX_APID {
252            return None;
253        }
254        Some(PacketId::new(ptype, sec_header_flag, apid))
255    }
256
257    /// Set a new Application Process ID (APID). If the passed number is invalid, the APID will
258    /// not be set and false will be returned. The maximum allowed value for the 11-bit field is
259    /// 2047
260    #[inline]
261    pub fn set_apid(&mut self, apid: u16) -> bool {
262        if apid > MAX_APID {
263            return false;
264        }
265        self.apid = apid;
266        true
267    }
268
269    #[inline]
270    pub fn apid(&self) -> u16 {
271        self.apid
272    }
273
274    #[inline]
275    pub fn raw(&self) -> u16 {
276        ((self.ptype as u16) << 12) | ((self.sec_header_flag as u16) << 11) | self.apid
277    }
278}
279
280impl From<u16> for PacketId {
281    fn from(raw_id: u16) -> Self {
282        PacketId {
283            ptype: PacketType::try_from(((raw_id >> 12) & 0b1) as u8).unwrap(),
284            sec_header_flag: ((raw_id >> 11) & 0b1) != 0,
285            apid: raw_id & 0x7FF,
286        }
287    }
288}
289
290/// Abstraction for the CCSDS Packet Sequence Control (PSC) field which is the
291/// third and the fourth byte in the CCSDS primary header.
292#[derive(Debug, PartialEq, Eq, Copy, Clone)]
293#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
294#[cfg_attr(feature = "defmt", derive(defmt::Format))]
295pub struct PacketSequenceCtrl {
296    pub seq_flags: SequenceFlags,
297    seq_count: u16,
298}
299
300impl PacketSequenceCtrl {
301    /// This constructor panics if the sequence count exceeds [MAX_SEQ_COUNT].
302    /// Use [Self::new_checked] to avoid panics.
303    #[inline]
304    pub const fn new(seq_flags: SequenceFlags, seq_count: u16) -> PacketSequenceCtrl {
305        if seq_count > MAX_SEQ_COUNT {
306            panic!("Sequence count too large");
307        }
308        PacketSequenceCtrl {
309            seq_flags,
310            seq_count,
311        }
312    }
313
314    /// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT].
315    #[inline]
316    pub fn new_checked(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
317        if seq_count > MAX_SEQ_COUNT {
318            return None;
319        }
320        Some(PacketSequenceCtrl::new(seq_flags, seq_count))
321    }
322
323    #[inline]
324    pub fn raw(&self) -> u16 {
325        ((self.seq_flags as u16) << 14) | self.seq_count
326    }
327
328    /// Set a new sequence count. If the passed number is invalid, the sequence count will not be
329    /// set and false will be returned. The maximum allowed value for the 14-bit field is 16383.
330    #[inline]
331    pub fn set_seq_count(&mut self, ssc: u16) -> bool {
332        if ssc > MAX_SEQ_COUNT {
333            return false;
334        }
335        self.seq_count = ssc;
336        true
337    }
338
339    #[inline]
340    pub fn seq_count(&self) -> u16 {
341        self.seq_count
342    }
343}
344
345impl From<u16> for PacketSequenceCtrl {
346    fn from(raw_id: u16) -> Self {
347        PacketSequenceCtrl {
348            seq_flags: SequenceFlags::try_from(((raw_id >> 14) & 0b11) as u8).unwrap(),
349            seq_count: raw_id & SSC_MASK,
350        }
351    }
352}
353
354macro_rules! sph_from_other {
355    ($Self: path, $other: path) => {
356        impl From<$other> for $Self {
357            fn from(other: $other) -> Self {
358                Self::from_composite_fields(
359                    other.packet_id(),
360                    other.psc(),
361                    other.data_len(),
362                    Some(other.ccsds_version()),
363                )
364            }
365        }
366    };
367}
368
369const SSC_MASK: u16 = 0x3FFF;
370const VERSION_MASK: u16 = 0xE000;
371
372/// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2.
373pub trait CcsdsPacket {
374    fn ccsds_version(&self) -> u8;
375    fn packet_id(&self) -> PacketId;
376    fn psc(&self) -> PacketSequenceCtrl;
377
378    /// Retrieve data length field
379    fn data_len(&self) -> u16;
380    /// Retrieve the total packet size based on the data length field
381    #[inline]
382    fn total_len(&self) -> usize {
383        usize::from(self.data_len()) + CCSDS_HEADER_LEN + 1
384    }
385
386    /// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND
387    /// of the first 2 bytes with 0x1FFF.
388    #[inline]
389    fn packet_id_raw(&self) -> u16 {
390        self.packet_id().raw()
391    }
392    /// Retrieve Packet Sequence Count
393    #[inline]
394    fn psc_raw(&self) -> u16 {
395        self.psc().raw()
396    }
397
398    /// Retrieve Packet Type (TM: 0, TC: 1).
399    #[inline]
400    fn ptype(&self) -> PacketType {
401        // This call should never fail because only 0 and 1 can be passed to the try_from call
402        self.packet_id().ptype
403    }
404
405    #[inline]
406    fn is_tm(&self) -> bool {
407        self.ptype() == PacketType::Tm
408    }
409
410    #[inline]
411    fn is_tc(&self) -> bool {
412        self.ptype() == PacketType::Tc
413    }
414
415    /// Retrieve the secondary header flag. Returns true if a secondary header is present
416    /// and false if it is not.
417    #[inline]
418    fn sec_header_flag(&self) -> bool {
419        self.packet_id().sec_header_flag
420    }
421
422    /// Retrieve Application Process ID.
423    #[inline]
424    fn apid(&self) -> u16 {
425        self.packet_id().apid
426    }
427
428    #[inline]
429    fn seq_count(&self) -> u16 {
430        self.psc().seq_count
431    }
432
433    #[inline]
434    fn sequence_flags(&self) -> SequenceFlags {
435        // This call should never fail because the mask ensures that only valid values are passed
436        // into the try_from function
437        self.psc().seq_flags
438    }
439}
440
441pub trait CcsdsPrimaryHeader {
442    fn from_composite_fields(
443        packet_id: PacketId,
444        psc: PacketSequenceCtrl,
445        data_len: u16,
446        version: Option<u8>,
447    ) -> Self;
448}
449
450/// Space Packet Primary Header according to CCSDS 133.0-B-2.
451///
452/// # Arguments
453///
454/// * `version` - CCSDS version field, occupies the first 3 bits of the raw header. Will generally
455///    be set to 0b000 in all constructors provided by this crate.
456/// * `packet_id` - Packet Identifier, which can also be used as a start marker. Occupies the last
457///    13 bits of the first two bytes of the raw header
458/// * `psc` - Packet Sequence Control, occupies the third and fourth byte of the raw header
459/// * `data_len` - Data length field occupies the fifth and the sixth byte of the raw header
460#[derive(Debug, PartialEq, Eq, Copy, Clone)]
461#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
462#[cfg_attr(feature = "defmt", derive(defmt::Format))]
463pub struct SpHeader {
464    pub version: u8,
465    pub packet_id: PacketId,
466    pub psc: PacketSequenceCtrl,
467    pub data_len: u16,
468}
469
470pub type SpacePacketHeader = SpHeader;
471
472impl Default for SpHeader {
473    /// The default function sets the sequence flag field to [SequenceFlags::Unsegmented] and the
474    /// data length to 0.
475    #[inline]
476    fn default() -> Self {
477        SpHeader {
478            version: 0,
479            packet_id: PacketId::default(),
480            psc: PacketSequenceCtrl {
481                seq_flags: SequenceFlags::Unsegmented,
482                seq_count: 0,
483            },
484            data_len: 0,
485        }
486    }
487}
488
489impl SpHeader {
490    #[inline]
491    pub const fn new(packet_id: PacketId, psc: PacketSequenceCtrl, data_len: u16) -> Self {
492        Self {
493            version: 0,
494            packet_id,
495            psc,
496            data_len,
497        }
498    }
499
500    /// This constructor sets the sequence flag field to [SequenceFlags::Unsegmented] and the data
501    /// length to 0.
502    ///
503    /// This constructor will panic if the APID exceeds [MAX_APID].
504    #[inline]
505    pub const fn new_from_apid(apid: u16) -> Self {
506        SpHeader {
507            version: 0,
508            packet_id: PacketId::new(PacketType::Tm, false, apid),
509            psc: PacketSequenceCtrl {
510                seq_flags: SequenceFlags::Unsegmented,
511                seq_count: 0,
512            },
513            data_len: 0,
514        }
515    }
516
517    /// Checked variant of [Self::new_from_apid].
518    #[inline]
519    pub fn new_from_apid_checked(apid: u16) -> Option<Self> {
520        Some(SpHeader {
521            version: 0,
522            packet_id: PacketId::new_checked(PacketType::Tm, false, apid)?,
523            psc: PacketSequenceCtrl {
524                seq_flags: SequenceFlags::Unsegmented,
525                seq_count: 0,
526            },
527            data_len: 0,
528        })
529    }
530
531    /// This constructor panics if the passed APID exceeds [MAX_APID] or the passed packet sequence
532    /// count exceeds [MAX_SEQ_COUNT].
533    ///
534    /// The checked constructor variants can be used to avoid panics.
535    #[inline]
536    pub const fn new_from_fields(
537        ptype: PacketType,
538        sec_header: bool,
539        apid: u16,
540        seq_flags: SequenceFlags,
541        seq_count: u16,
542        data_len: u16,
543    ) -> Self {
544        if seq_count > MAX_SEQ_COUNT {
545            panic!("Sequence count is too large");
546        }
547        if apid > MAX_APID {
548            panic!("APID is too large");
549        }
550        Self {
551            psc: PacketSequenceCtrl::new(seq_flags, seq_count),
552            packet_id: PacketId::new(ptype, sec_header, apid),
553            data_len,
554            version: 0,
555        }
556    }
557
558    /// Create a new Space Packet Header instance which can be used to create generic
559    /// Space Packets.
560    ///
561    /// This will return [None] if the APID or sequence count argument
562    /// exceed [MAX_APID] or [MAX_SEQ_COUNT] respectively. The version field is set to 0b000.
563    #[inline]
564    pub fn new_from_fields_checked(
565        ptype: PacketType,
566        sec_header: bool,
567        apid: u16,
568        seq_flags: SequenceFlags,
569        seq_count: u16,
570        data_len: u16,
571    ) -> Option<Self> {
572        if seq_count > MAX_SEQ_COUNT || apid > MAX_APID {
573            return None;
574        }
575        Some(SpHeader::new_from_fields(
576            ptype, sec_header, apid, seq_flags, seq_count, data_len,
577        ))
578    }
579
580    /// Helper function for telemetry space packet headers. The packet type field will be
581    /// set accordingly. The secondary header flag field is set to false.
582    #[inline]
583    pub fn new_for_tm_checked(
584        apid: u16,
585        seq_flags: SequenceFlags,
586        seq_count: u16,
587        data_len: u16,
588    ) -> Option<Self> {
589        Self::new_from_fields_checked(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
590    }
591
592    /// Helper function for telemetry space packet headers. The packet type field will be
593    /// set accordingly. The secondary header flag field is set to false.
594    #[inline]
595    pub fn new_for_tc_checked(
596        apid: u16,
597        seq_flags: SequenceFlags,
598        seq_count: u16,
599        data_len: u16,
600    ) -> Option<Self> {
601        Self::new_from_fields_checked(PacketType::Tc, false, apid, seq_flags, seq_count, data_len)
602    }
603
604    /// This is an unchecked constructor which can panic on invalid input.
605    #[inline]
606    pub const fn new_for_tm(
607        apid: u16,
608        seq_flags: SequenceFlags,
609        seq_count: u16,
610        data_len: u16,
611    ) -> Self {
612        Self::new_from_fields(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
613    }
614
615    /// This is an unchecked constructor which can panic on invalid input.
616    #[inline]
617    pub const fn new_for_tc(
618        apid: u16,
619        seq_flags: SequenceFlags,
620        seq_count: u16,
621        data_len: u16,
622    ) -> Self {
623        Self::new_from_fields(PacketType::Tc, false, apid, seq_flags, seq_count, data_len)
624    }
625
626    /// Variant of [SpHeader::new_for_tm_checked] which sets the sequence flag field to [SequenceFlags::Unsegmented]
627    #[inline]
628    pub fn new_for_unseg_tm_checked(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
629        Self::new_for_tm_checked(apid, SequenceFlags::Unsegmented, seq_count, data_len)
630    }
631
632    /// Variant of [SpHeader::new_for_tc_checked] which sets the sequence flag field to [SequenceFlags::Unsegmented]
633    #[inline]
634    pub fn new_for_unseg_tc_checked(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
635        Self::new_for_tc_checked(apid, SequenceFlags::Unsegmented, seq_count, data_len)
636    }
637
638    /// Variant of [SpHeader::new_for_tc] which sets the sequence flag field to [SequenceFlags::Unsegmented].
639    ///
640    /// This is an unchecked constructor which can panic on invalid input.
641    #[inline]
642    pub const fn new_for_unseg_tc(apid: u16, seq_count: u16, data_len: u16) -> Self {
643        Self::new_for_tc(apid, SequenceFlags::Unsegmented, seq_count, data_len)
644    }
645
646    /// Variant of [SpHeader::new_for_tm] which sets the sequence flag field to [SequenceFlags::Unsegmented].
647    ///
648    /// This is an unchecked constructor which can panic on invalid input.
649    #[inline]
650    pub const fn new_for_unseg_tm(apid: u16, seq_count: u16, data_len: u16) -> Self {
651        Self::new_for_tm(apid, SequenceFlags::Unsegmented, seq_count, data_len)
652    }
653
654    delegate! {
655        to self.packet_id {
656            /// Returns [false] and fails if the APID exceeds [MAX_APID]
657            #[inline]
658            pub fn set_apid(&mut self, apid: u16) -> bool;
659        }
660    }
661
662    delegate! {
663        to self.psc {
664            /// Returns [false] and fails if the sequence count exceeds [MAX_SEQ_COUNT]
665            #[inline]
666            pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
667        }
668    }
669
670    #[inline]
671    pub fn set_seq_flags(&mut self, seq_flags: SequenceFlags) {
672        self.psc.seq_flags = seq_flags;
673    }
674
675    #[inline]
676    pub fn set_sec_header_flag(&mut self) {
677        self.packet_id.sec_header_flag = true;
678    }
679
680    #[inline]
681    pub fn clear_sec_header_flag(&mut self) {
682        self.packet_id.sec_header_flag = false;
683    }
684
685    #[inline]
686    pub fn set_packet_type(&mut self, packet_type: PacketType) {
687        self.packet_id.ptype = packet_type;
688    }
689
690    /// Create a struct from a raw slice where the fields have network endianness (big).
691    /// This function also returns the remaining part of the passed slice starting past the read
692    /// CCSDS header.
693    pub fn from_be_bytes(buf: &[u8]) -> Result<(Self, &[u8]), ByteConversionError> {
694        if buf.len() < CCSDS_HEADER_LEN {
695            return Err(ByteConversionError::FromSliceTooSmall {
696                found: buf.len(),
697                expected: CCSDS_HEADER_LEN,
698            });
699        }
700        let zc_header = zc::SpHeader::read_from_bytes(&buf[0..CCSDS_HEADER_LEN])
701            .map_err(|_| ByteConversionError::ZeroCopyFromError)?;
702        Ok((Self::from(zc_header), &buf[CCSDS_HEADER_LEN..]))
703    }
704
705    /// Write the header to a raw buffer using big endian format. This function returns the
706    /// remaining part of the passed slice starting past the written CCSDS header.
707    pub fn write_to_be_bytes<'a>(
708        &self,
709        buf: &'a mut [u8],
710    ) -> Result<&'a mut [u8], ByteConversionError> {
711        if buf.len() < CCSDS_HEADER_LEN {
712            return Err(ByteConversionError::FromSliceTooSmall {
713                found: buf.len(),
714                expected: CCSDS_HEADER_LEN,
715            });
716        }
717        let zc_header: zc::SpHeader = zc::SpHeader::from(*self);
718        zc_header
719            .write_to(&mut buf[0..CCSDS_HEADER_LEN])
720            .map_err(|_| ByteConversionError::ZeroCopyToError)?;
721        Ok(&mut buf[CCSDS_HEADER_LEN..])
722    }
723
724    /// Create a vector containing the CCSDS header.
725    #[cfg(feature = "alloc")]
726    pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
727        let mut vec = alloc::vec![0; CCSDS_HEADER_LEN];
728        // This can not fail.
729        self.write_to_be_bytes(&mut vec[..]).unwrap();
730        vec
731    }
732}
733
734impl CcsdsPacket for SpHeader {
735    #[inline]
736    fn ccsds_version(&self) -> u8 {
737        self.version
738    }
739
740    #[inline]
741    fn packet_id(&self) -> PacketId {
742        self.packet_id
743    }
744
745    #[inline]
746    fn psc(&self) -> PacketSequenceCtrl {
747        self.psc
748    }
749
750    #[inline]
751    fn data_len(&self) -> u16 {
752        self.data_len
753    }
754}
755
756impl CcsdsPrimaryHeader for SpHeader {
757    #[inline]
758    fn from_composite_fields(
759        packet_id: PacketId,
760        psc: PacketSequenceCtrl,
761        data_len: u16,
762        version: Option<u8>,
763    ) -> Self {
764        let mut version_to_set = 0b000;
765        if let Some(version) = version {
766            version_to_set = version;
767        }
768        SpHeader {
769            version: version_to_set,
770            packet_id,
771            psc,
772            data_len,
773        }
774    }
775}
776
777sph_from_other!(SpHeader, crate::zc::SpHeader);
778
779pub mod zc {
780    use crate::{CcsdsPacket, CcsdsPrimaryHeader, PacketId, PacketSequenceCtrl, VERSION_MASK};
781    use zerocopy::byteorder::NetworkEndian;
782    use zerocopy::{FromBytes, Immutable, IntoBytes, Unaligned, U16};
783
784    #[derive(FromBytes, IntoBytes, Immutable, Unaligned, Debug)]
785    #[repr(C)]
786    pub struct SpHeader {
787        version_packet_id: U16<NetworkEndian>,
788        psc: U16<NetworkEndian>,
789        data_len: U16<NetworkEndian>,
790    }
791
792    impl SpHeader {
793        pub fn new(
794            packet_id: PacketId,
795            psc: PacketSequenceCtrl,
796            data_len: u16,
797            version: Option<u8>,
798        ) -> Self {
799            let mut version_packet_id = packet_id.raw();
800            if let Some(version) = version {
801                version_packet_id = ((version as u16) << 13) | packet_id.raw()
802            }
803            SpHeader {
804                version_packet_id: U16::from(version_packet_id),
805                psc: U16::from(psc.raw()),
806                data_len: U16::from(data_len),
807            }
808        }
809    }
810
811    impl CcsdsPacket for SpHeader {
812        #[inline]
813        fn ccsds_version(&self) -> u8 {
814            ((self.version_packet_id.get() >> 13) as u8) & 0b111
815        }
816
817        #[inline]
818        fn packet_id(&self) -> PacketId {
819            PacketId::from(self.packet_id_raw())
820        }
821
822        #[inline]
823        fn psc(&self) -> PacketSequenceCtrl {
824            PacketSequenceCtrl::from(self.psc_raw())
825        }
826
827        #[inline]
828        fn data_len(&self) -> u16 {
829            self.data_len.get()
830        }
831
832        #[inline]
833        fn packet_id_raw(&self) -> u16 {
834            self.version_packet_id.get() & (!VERSION_MASK)
835        }
836
837        #[inline]
838        fn psc_raw(&self) -> u16 {
839            self.psc.get()
840        }
841    }
842
843    impl CcsdsPrimaryHeader for SpHeader {
844        fn from_composite_fields(
845            packet_id: PacketId,
846            psc: PacketSequenceCtrl,
847            data_len: u16,
848            version: Option<u8>,
849        ) -> Self {
850            SpHeader::new(packet_id, psc, data_len, version)
851        }
852    }
853
854    sph_from_other!(SpHeader, crate::SpHeader);
855}
856
857#[cfg(all(test, feature = "std"))]
858pub(crate) mod tests {
859    use std::collections::HashSet;
860
861    #[allow(unused_imports)]
862    use crate::ByteConversionError;
863    #[cfg(feature = "serde")]
864    use crate::CcsdsPrimaryHeader;
865    use crate::{
866        packet_type_in_raw_packet_id, zc, CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType,
867    };
868    use crate::{SequenceFlags, SpHeader};
869    use alloc::vec;
870    #[cfg(feature = "serde")]
871    use core::fmt::Debug;
872    use num_traits::pow;
873    #[cfg(feature = "serde")]
874    use postcard::{from_bytes, to_allocvec};
875    #[cfg(feature = "serde")]
876    use serde::{de::DeserializeOwned, Serialize};
877    use zerocopy::FromBytes;
878
879    const CONST_SP: SpHeader = SpHeader::new(
880        PacketId::new_for_tc(true, 0x36),
881        PacketSequenceCtrl::new(SequenceFlags::ContinuationSegment, 0x88),
882        0x90,
883    );
884
885    const PACKET_ID_TM: PacketId = PacketId::new_for_tm(true, 0x22);
886
887    #[cfg(feature = "serde")]
888    pub(crate) fn generic_serde_test<T: Serialize + DeserializeOwned + PartialEq + Debug>(
889        value: T,
890    ) {
891        let output: alloc::vec::Vec<u8> = to_allocvec(&value).unwrap();
892        let output_converted_back: T = from_bytes(&output).unwrap();
893        assert_eq!(output_converted_back, value);
894    }
895
896    #[test]
897    #[allow(clippy::assertions_on_constants)]
898    fn verify_const_packet_id() {
899        assert_eq!(PACKET_ID_TM.apid(), 0x22);
900        assert!(PACKET_ID_TM.sec_header_flag);
901        assert_eq!(PACKET_ID_TM.ptype, PacketType::Tm);
902        let const_tc_id = PacketId::new_for_tc(true, 0x23);
903        assert_eq!(const_tc_id.ptype, PacketType::Tc);
904    }
905
906    #[test]
907    fn test_default_packet_id() {
908        let id_default = PacketId::default();
909        assert_eq!(id_default.ptype, PacketType::Tm);
910        assert_eq!(id_default.apid, 0x000);
911        assert!(!id_default.sec_header_flag);
912    }
913
914    #[test]
915    fn test_packet_id_ctors() {
916        let packet_id = PacketId::new_checked(PacketType::Tc, true, 0x1ff);
917        assert!(packet_id.is_some());
918        let packet_id = packet_id.unwrap();
919        assert_eq!(packet_id.apid(), 0x1ff);
920        assert_eq!(packet_id.ptype, PacketType::Tc);
921        assert!(packet_id.sec_header_flag);
922        let packet_id_tc = PacketId::new_for_tc_checked(true, 0x1ff);
923        assert!(packet_id_tc.is_some());
924        let packet_id_tc = packet_id_tc.unwrap();
925        assert_eq!(packet_id_tc, packet_id);
926        let packet_id_tm = PacketId::new_for_tm_checked(true, 0x2ff);
927        assert!(packet_id_tm.is_some());
928        let packet_id_tm = packet_id_tm.unwrap();
929        assert!(packet_id_tm.sec_header_flag);
930        assert_eq!(packet_id_tm.ptype, PacketType::Tm);
931        assert_eq!(packet_id_tm.apid, 0x2ff);
932    }
933
934    #[test]
935    fn verify_const_sp_header() {
936        assert!(CONST_SP.sec_header_flag());
937        assert_eq!(CONST_SP.apid(), 0x36);
938        assert_eq!(
939            CONST_SP.sequence_flags(),
940            SequenceFlags::ContinuationSegment
941        );
942        assert_eq!(CONST_SP.seq_count(), 0x88);
943        assert_eq!(CONST_SP.data_len, 0x90);
944    }
945
946    #[test]
947    fn test_seq_flag_helpers() {
948        assert_eq!(
949            SequenceFlags::try_from(0b00).expect("SEQ flag creation failed"),
950            SequenceFlags::ContinuationSegment
951        );
952        assert_eq!(
953            SequenceFlags::try_from(0b01).expect("SEQ flag creation failed"),
954            SequenceFlags::FirstSegment
955        );
956        assert_eq!(
957            SequenceFlags::try_from(0b10).expect("SEQ flag creation failed"),
958            SequenceFlags::LastSegment
959        );
960        assert_eq!(
961            SequenceFlags::try_from(0b11).expect("SEQ flag creation failed"),
962            SequenceFlags::Unsegmented
963        );
964        assert!(SequenceFlags::try_from(0b100).is_err());
965    }
966
967    #[test]
968    fn test_packet_type_helper() {
969        assert_eq!(PacketType::try_from(0b00).unwrap(), PacketType::Tm);
970        assert_eq!(PacketType::try_from(0b01).unwrap(), PacketType::Tc);
971        assert!(PacketType::try_from(0b10).is_err());
972    }
973
974    #[test]
975    fn test_packet_id() {
976        let packet_id =
977            PacketId::new_checked(PacketType::Tm, false, 0x42).expect("Packet ID creation failed");
978        assert_eq!(packet_id.raw(), 0x0042);
979        let packet_id_from_raw = PacketId::from(packet_id.raw());
980        assert_eq!(
981            packet_type_in_raw_packet_id(packet_id.raw()),
982            PacketType::Tm
983        );
984        assert_eq!(packet_id_from_raw, packet_id);
985        let packet_id_from_new = PacketId::new_checked(PacketType::Tm, false, 0x42).unwrap();
986        assert_eq!(packet_id_from_new, packet_id);
987    }
988
989    #[test]
990    fn test_invalid_packet_id() {
991        let packet_id_invalid = PacketId::new_checked(PacketType::Tc, true, 0xFFFF);
992        assert!(packet_id_invalid.is_none());
993    }
994
995    #[test]
996    fn test_invalid_apid_setter() {
997        let mut packet_id =
998            PacketId::new_checked(PacketType::Tm, false, 0x42).expect("Packet ID creation failed");
999        assert!(!packet_id.set_apid(0xffff));
1000    }
1001
1002    #[test]
1003    fn test_invalid_seq_count() {
1004        let mut psc = PacketSequenceCtrl::new_checked(SequenceFlags::ContinuationSegment, 77)
1005            .expect("PSC creation failed");
1006        assert_eq!(psc.seq_count(), 77);
1007        assert!(!psc.set_seq_count(0xffff));
1008    }
1009
1010    #[test]
1011    fn test_packet_seq_ctrl() {
1012        let mut psc = PacketSequenceCtrl::new_checked(SequenceFlags::ContinuationSegment, 77)
1013            .expect("PSC creation failed");
1014        assert_eq!(psc.raw(), 77);
1015        let psc_from_raw = PacketSequenceCtrl::from(psc.raw());
1016        assert_eq!(psc_from_raw, psc);
1017        // Fails because SSC is limited to 14 bits
1018        assert!(!psc.set_seq_count(2u16.pow(15)));
1019        assert_eq!(psc.raw(), 77);
1020
1021        let psc_invalid = PacketSequenceCtrl::new_checked(SequenceFlags::FirstSegment, 0xFFFF);
1022        assert!(psc_invalid.is_none());
1023        let psc_from_new =
1024            PacketSequenceCtrl::new_checked(SequenceFlags::ContinuationSegment, 77).unwrap();
1025        assert_eq!(psc_from_new, psc);
1026    }
1027
1028    #[test]
1029    #[cfg(feature = "serde")]
1030    fn test_serde_sph() {
1031        let sp_header =
1032            SpHeader::new_for_unseg_tc_checked(0x42, 12, 0).expect("Error creating SP header");
1033        assert_eq!(sp_header.ccsds_version(), 0b000);
1034        assert!(sp_header.is_tc());
1035        assert!(!sp_header.sec_header_flag());
1036        assert_eq!(sp_header.ptype(), PacketType::Tc);
1037        assert_eq!(sp_header.seq_count(), 12);
1038        assert_eq!(sp_header.apid(), 0x42);
1039        assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
1040        assert_eq!(sp_header.data_len(), 0);
1041        let output = to_allocvec(&sp_header).unwrap();
1042        let sp_header: SpHeader = from_bytes(&output).unwrap();
1043        assert_eq!(sp_header.version, 0b000);
1044        assert!(!sp_header.packet_id.sec_header_flag);
1045        assert_eq!(sp_header.ptype(), PacketType::Tc);
1046        assert_eq!(sp_header.seq_count(), 12);
1047        assert_eq!(sp_header.apid(), 0x42);
1048        assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
1049        assert_eq!(sp_header.packet_id_raw(), 0x1042);
1050        assert_eq!(sp_header.psc_raw(), 0xC00C);
1051        assert_eq!(sp_header.ccsds_version(), 0b000);
1052        assert_eq!(sp_header.data_len, 0);
1053
1054        let sp_header =
1055            SpHeader::new_for_unseg_tm_checked(0x7, 22, 36).expect("Error creating SP header");
1056        assert_eq!(sp_header.ccsds_version(), 0b000);
1057        assert!(sp_header.is_tm());
1058        assert!(!sp_header.sec_header_flag());
1059        assert_eq!(sp_header.ptype(), PacketType::Tm);
1060        assert_eq!(sp_header.seq_count(), 22);
1061        assert_eq!(sp_header.apid(), 0x07);
1062        assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
1063        assert_eq!(sp_header.packet_id_raw(), 0x0007);
1064        assert_eq!(sp_header.psc_raw(), 0xC016);
1065        assert_eq!(sp_header.data_len(), 36);
1066        assert_eq!(sp_header.ccsds_version(), 0b000);
1067
1068        let from_comp_fields = SpHeader::from_composite_fields(
1069            PacketId::new(PacketType::Tc, true, 0x42),
1070            PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0x7),
1071            0,
1072            None,
1073        );
1074        assert_eq!(from_comp_fields.ptype(), PacketType::Tc);
1075        assert_eq!(from_comp_fields.apid(), 0x42);
1076        assert!(from_comp_fields.sec_header_flag());
1077        assert_eq!(
1078            from_comp_fields.sequence_flags(),
1079            SequenceFlags::Unsegmented
1080        );
1081        assert_eq!(from_comp_fields.seq_count(), 0x7);
1082        assert_eq!(from_comp_fields.data_len(), 0);
1083    }
1084
1085    #[test]
1086    fn test_setters() {
1087        let sp_header = SpHeader::new_for_tc_checked(0x42, SequenceFlags::Unsegmented, 25, 0);
1088        assert!(sp_header.is_some());
1089        let mut sp_header = sp_header.unwrap();
1090        sp_header.set_apid(0x12);
1091        assert_eq!(sp_header.apid(), 0x12);
1092        sp_header.set_sec_header_flag();
1093        assert!(sp_header.sec_header_flag());
1094        sp_header.clear_sec_header_flag();
1095        assert!(!sp_header.sec_header_flag());
1096        assert_eq!(sp_header.ptype(), PacketType::Tc);
1097        sp_header.set_packet_type(PacketType::Tm);
1098        assert_eq!(sp_header.ptype(), PacketType::Tm);
1099        sp_header.set_seq_count(0x45);
1100        assert_eq!(sp_header.seq_count(), 0x45);
1101    }
1102
1103    #[test]
1104    fn test_tc_ctor() {
1105        let sp_header = SpHeader::new_for_tc_checked(0x42, SequenceFlags::Unsegmented, 25, 0);
1106        assert!(sp_header.is_some());
1107        let sp_header = sp_header.unwrap();
1108        verify_sp_fields(PacketType::Tc, &sp_header);
1109    }
1110
1111    #[test]
1112    fn test_tc_ctor_unseg() {
1113        let sp_header = SpHeader::new_for_unseg_tc_checked(0x42, 25, 0);
1114        assert!(sp_header.is_some());
1115        let sp_header = sp_header.unwrap();
1116        verify_sp_fields(PacketType::Tc, &sp_header);
1117    }
1118
1119    #[test]
1120    fn test_tc_ctor_unseg_const() {
1121        let sp_header = SpHeader::new_for_unseg_tc(0x42, 25, 0);
1122        verify_sp_fields(PacketType::Tc, &sp_header);
1123    }
1124
1125    #[test]
1126    fn test_tm_ctor() {
1127        let sp_header = SpHeader::new_for_tm_checked(0x42, SequenceFlags::Unsegmented, 25, 0);
1128        assert!(sp_header.is_some());
1129        let sp_header = sp_header.unwrap();
1130        verify_sp_fields(PacketType::Tm, &sp_header);
1131    }
1132
1133    #[test]
1134    fn test_tm_ctor_const() {
1135        let sp_header = SpHeader::new_for_tm(0x42, SequenceFlags::Unsegmented, 25, 0);
1136        verify_sp_fields(PacketType::Tm, &sp_header);
1137    }
1138
1139    #[test]
1140    fn test_tm_ctor_unseg() {
1141        let sp_header = SpHeader::new_for_unseg_tm_checked(0x42, 25, 0);
1142        assert!(sp_header.is_some());
1143        let sp_header = sp_header.unwrap();
1144        verify_sp_fields(PacketType::Tm, &sp_header);
1145    }
1146
1147    fn verify_sp_fields(ptype: PacketType, sp_header: &SpHeader) {
1148        assert_eq!(sp_header.ptype(), ptype);
1149        assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
1150        assert_eq!(sp_header.apid(), 0x42);
1151        assert_eq!(sp_header.seq_count(), 25);
1152        assert_eq!(sp_header.data_len(), 0);
1153    }
1154
1155    #[test]
1156    fn test_zc_sph() {
1157        use zerocopy::IntoBytes;
1158
1159        let sp_header = SpHeader::new_for_unseg_tc_checked(0x7FF, pow(2, 14) - 1, 0)
1160            .expect("Error creating SP header");
1161        assert_eq!(sp_header.ptype(), PacketType::Tc);
1162        assert_eq!(sp_header.apid(), 0x7FF);
1163        assert_eq!(sp_header.data_len(), 0);
1164        assert_eq!(sp_header.ccsds_version(), 0b000);
1165        assert!(sp_header.is_tc());
1166        let sp_header_zc = zc::SpHeader::from(sp_header);
1167        let slice = sp_header_zc.as_bytes();
1168        assert_eq!(slice.len(), 6);
1169        assert_eq!(slice[0], 0x17);
1170        assert_eq!(slice[1], 0xFF);
1171        assert_eq!(slice[2], 0xFF);
1172        assert_eq!(slice[3], 0xFF);
1173        assert_eq!(slice[4], 0x00);
1174        assert_eq!(slice[5], 0x00);
1175
1176        let mut slice = [0; 6];
1177        sp_header_zc.write_to(slice.as_mut_slice()).unwrap();
1178        assert_eq!(slice.len(), 6);
1179        assert_eq!(slice[0], 0x17);
1180        assert_eq!(slice[1], 0xFF);
1181        assert_eq!(slice[2], 0xFF);
1182        assert_eq!(slice[3], 0xFF);
1183        assert_eq!(slice[4], 0x00);
1184        assert_eq!(slice[5], 0x00);
1185
1186        let mut test_vec = vec![0_u8; 6];
1187        let slice = test_vec.as_mut_slice();
1188        sp_header_zc.write_to(slice).unwrap();
1189        let slice = test_vec.as_slice();
1190        assert_eq!(slice.len(), 6);
1191        assert_eq!(slice[0], 0x17);
1192        assert_eq!(slice[1], 0xFF);
1193        assert_eq!(slice[2], 0xFF);
1194        assert_eq!(slice[3], 0xFF);
1195        assert_eq!(slice[4], 0x00);
1196        assert_eq!(slice[5], 0x00);
1197
1198        let sp_header = zc::SpHeader::read_from_bytes(slice);
1199        assert!(sp_header.is_ok());
1200        let sp_header = sp_header.unwrap();
1201        assert_eq!(sp_header.ccsds_version(), 0b000);
1202        assert_eq!(sp_header.packet_id_raw(), 0x17FF);
1203        assert_eq!(sp_header.apid(), 0x7FF);
1204        assert_eq!(sp_header.ptype(), PacketType::Tc);
1205        assert_eq!(sp_header.data_len(), 0);
1206    }
1207
1208    #[test]
1209    fn packet_id_ord_partial_ord() {
1210        let packet_id_small = PacketId::from(1_u16);
1211        let packet_id_larger = PacketId::from(2_u16);
1212        assert!(packet_id_small < packet_id_larger);
1213        assert!(packet_id_larger > packet_id_small);
1214        assert_eq!(
1215            packet_id_small.cmp(&packet_id_larger),
1216            core::cmp::Ordering::Less
1217        );
1218    }
1219
1220    #[test]
1221    fn packet_id_hashable() {
1222        let mut id_set = HashSet::new();
1223        id_set.insert(PacketId::from(1_u16));
1224    }
1225
1226    #[test]
1227    fn sp_header_from_apid() {
1228        let sp_header = SpHeader::new_from_apid(0x03);
1229        assert_eq!(sp_header.apid(), 0x03);
1230        assert_eq!(sp_header.data_len(), 0);
1231    }
1232
1233    #[test]
1234    fn sp_header_from_apid_checked() {
1235        let sp_header = SpHeader::new_from_apid_checked(0x03).unwrap();
1236        assert_eq!(sp_header.apid(), 0x03);
1237        assert_eq!(sp_header.data_len(), 0);
1238    }
1239
1240    #[cfg(feature = "defmt")]
1241    fn is_defmt_format<T: defmt::Format>(_t: T) {}
1242
1243    #[test]
1244    #[cfg(feature = "defmt")]
1245    fn test_defmt_format() {
1246        is_defmt_format(ByteConversionError::ToSliceTooSmall {
1247            found: 1,
1248            expected: 2,
1249        });
1250    }
1251
1252    #[test]
1253    fn test_sp_header_as_vec() {
1254        let sp_header = SpHeader::new_for_unseg_tc(0x42, 25, 1);
1255        let sp_header_as_vec = sp_header.to_vec();
1256        let sp_header_read_back = SpHeader::from_be_bytes(&sp_header_as_vec)
1257            .expect("Error reading back SP header")
1258            .0;
1259        assert_eq!(sp_header, sp_header_read_back);
1260    }
1261}