spacepackets/ecss/
tm_pus_a.rs

1//! This module contains all components required to create ECSS PUS A legacy telemetry.
2//!
3//! # Examples
4//!
5//! ```rust
6//! use spacepackets::time::TimeWriter;
7//! use spacepackets::time::cds::CdsTime;
8//! use spacepackets::{CcsdsPacket, SpHeader};
9//! use spacepackets::ecss::{PusPacket, WritablePusPacket};
10//! use spacepackets::ecss::tm_pus_a::{
11//!     PusTmCreator,
12//!     PusTmReader,
13//!     PusTmSecondaryHeader,
14//!     SecondaryHeaderParameters
15//! };
16//! use arbitrary_int::u11;
17//!
18//! let mut time_buf: [u8; 7] = [0; 7];
19//! let time_now = CdsTime::now_with_u16_days().expect("creating CDS timestamp failed");
20//! // This can definitely hold the timestamp, so it is okay to unwrap.
21//! time_now.write_to_bytes(&mut time_buf).unwrap();
22//!
23//! // Create a ping telemetry with no user source data
24//! let ping_tm = PusTmCreator::new_no_source_data(
25//!     SpHeader::new_from_apid(u11::new(0x02)),
26//!     PusTmSecondaryHeader::new_simple(17, 2, &time_buf),
27//!     true
28//! );
29//! println!("{:?}", ping_tm);
30//! assert_eq!(ping_tm.service_type_id(), 17);
31//! assert_eq!(ping_tm.message_subtype_id(), 2);
32//! assert_eq!(ping_tm.apid().value(), 0x02);
33//!
34//! // Serialize TM into a raw buffer
35//! let mut test_buf: [u8; 32] = [0; 32];
36//! let written_size = ping_tm
37//!     .write_to_bytes(test_buf.as_mut_slice())
38//!     .expect("Error writing TC to buffer");
39//! assert_eq!(written_size, 18);
40//! println!("{:?}", &test_buf[0..written_size]);
41//!
42//! // Deserialize from the raw byte representation
43//! let ping_tm_reader = PusTmReader::new(&test_buf, &SecondaryHeaderParameters::new_minimal(7)).expect("deserialization failed");
44//! assert_eq!(written_size, ping_tm_reader.packet_len());
45//! assert_eq!(ping_tm_reader.service_type_id(), 17);
46//! assert_eq!(ping_tm_reader.message_subtype_id(), 2);
47//! assert_eq!(ping_tm_reader.apid().value(), 0x02);
48//! assert_eq!(ping_tm_reader.timestamp(), &time_buf);
49//! ```
50use crate::crc::{CRC_CCITT_FALSE, CRC_CCITT_FALSE_NO_TABLE};
51use crate::ecss::tm::IsPusTelemetry;
52use crate::ecss::{
53    calc_pus_crc16, crc_from_raw_data, sp_header_impls, user_data_from_raw,
54    verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, MessageTypeId, PusError, PusPacket,
55    PusVersion, WritablePusPacket,
56};
57use crate::util::{UnsignedByteField, UnsignedEnum};
58use crate::{
59    ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SpHeader, CCSDS_HEADER_LEN,
60    MAX_APID,
61};
62use arbitrary_int::traits::Integer;
63use arbitrary_int::{u11, u14, u3, u4};
64use core::mem::size_of;
65#[cfg(feature = "serde")]
66use serde::{Deserialize, Serialize};
67use zerocopy::{FromBytes, IntoBytes};
68
69#[cfg(feature = "alloc")]
70use alloc::vec::Vec;
71use delegate::delegate;
72
73use crate::time::{TimeWriter, TimestampError};
74
75use super::verify_crc16_ccitt_false_from_raw_to_pus_error_no_table;
76
77/// Length without timestamp
78pub const PUS_TM_MIN_SEC_HEADER_LEN: usize = 3;
79/// Minimal length of a PUS TM packet without source data.
80pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
81    CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
82
83/// Generic properties of a PUS-A PUS TM secondary header.
84pub trait GenericPusTmSecondaryHeader {
85    /// PUS version field.
86    fn pus_version(&self) -> PusVersion;
87    /// Service field.
88    fn service(&self) -> u8;
89    /// Subservice field.
90    fn subservice(&self) -> u8;
91    /// Message counter field for the service ID.
92    fn msg_counter(&self) -> Option<u8>;
93    /// Destination ID.
94    fn dest_id(&self) -> Option<UnsignedByteField>;
95    /// Number of spare bytes.
96    fn spare_bytes(&self) -> usize;
97}
98
99/// Managed parameters for the secondary header which can not be deduced from the packet itself.
100#[derive(Debug, Copy, Clone, Eq, PartialEq)]
101pub struct SecondaryHeaderParameters {
102    /// Timestamp length in bytes.
103    pub timestamp_len: usize,
104    /// Does the packet have a message counter?
105    pub has_msg_counter: bool,
106    /// Present and length of the destination ID.
107    pub opt_dest_id_len: Option<usize>,
108    /// Number of spare bytes.
109    pub spare_bytes: usize,
110}
111
112impl SecondaryHeaderParameters {
113    /// Minimal constructor.
114    pub const fn new_minimal(timestamp_len: usize) -> Self {
115        Self {
116            timestamp_len,
117            has_msg_counter: false,
118            opt_dest_id_len: None,
119            spare_bytes: 0,
120        }
121    }
122}
123
124/// PUS TM secondary header.
125#[derive(PartialEq, Eq, Copy, Clone, Debug)]
126#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
127#[cfg_attr(feature = "defmt", derive(defmt::Format))]
128pub struct PusTmSecondaryHeader<'stamp> {
129    pus_version: PusVersion,
130    /// Service field.
131    pub service: u8,
132    /// Subservice field.
133    pub subservice: u8,
134    /// Service message counter.
135    pub msg_counter: Option<u8>,
136    /// Destination ID field.
137    pub dest_id: Option<UnsignedByteField>,
138    /// Timestamp slice.
139    pub timestamp: &'stamp [u8],
140    /// Number of spare bytes to add after the timestamp.
141    pub spare_bytes: usize,
142}
143
144impl<'stamp> PusTmSecondaryHeader<'stamp> {
145    /// Simple constructor.
146    ///
147    /// Allows setting the service, subservice and timestamp and sets default values for the
148    /// other fields.
149    #[inline]
150    pub fn new_simple(service: u8, subservice: u8, timestamp: &'stamp [u8]) -> Self {
151        Self::new(service, subservice, None, None, timestamp, 0)
152    }
153
154    /// Like [Self::new_simple] but without a timestamp.
155    #[inline]
156    pub fn new_simple_no_timestamp(service: u8, subservice: u8) -> Self {
157        Self::new(service, subservice, None, None, &[], 0)
158    }
159
160    /// Generic constructor.
161    #[inline]
162    pub fn new(
163        service: u8,
164        subservice: u8,
165        msg_counter: Option<u8>,
166        dest_id: Option<UnsignedByteField>,
167        timestamp: &'stamp [u8],
168        spare_bytes: usize,
169    ) -> Self {
170        PusTmSecondaryHeader {
171            pus_version: PusVersion::PusA,
172            service,
173            subservice,
174            msg_counter,
175            dest_id,
176            timestamp,
177            spare_bytes,
178        }
179    }
180
181    /// Construct the secondary header from a raw byte slice.
182    pub fn from_bytes(
183        buf: &'stamp [u8],
184        params: &SecondaryHeaderParameters,
185    ) -> Result<PusTmSecondaryHeader<'stamp>, PusError> {
186        let sec_header_len = Self::len_for_params(params);
187        if buf.len() < sec_header_len {
188            return Err(ByteConversionError::FromSliceTooSmall {
189                found: buf.len(),
190                expected: sec_header_len,
191            }
192            .into());
193        }
194        let pus_version = PusVersion::try_from(u4::new((buf[0] >> 4) & 0x0F));
195        if let Err(version_raw) = pus_version {
196            return Err(PusError::VersionNotSupported(version_raw));
197        }
198        let pus_version = pus_version.unwrap();
199        if pus_version != PusVersion::PusA {
200            return Err(PusError::VersionNotSupported(pus_version.raw_value()));
201        }
202        let mut msg_counter = None;
203        let mut current_idx = 3;
204        if params.has_msg_counter {
205            msg_counter = Some(buf[current_idx]);
206            current_idx += 1;
207        }
208        let mut dest_id = None;
209        if let Some(dest_id_len) = params.opt_dest_id_len {
210            dest_id = Some(
211                UnsignedByteField::new_from_be_bytes(
212                    dest_id_len,
213                    &buf[current_idx..current_idx + dest_id_len],
214                )
215                .unwrap(),
216            );
217            current_idx += dest_id_len;
218        }
219        Ok(Self {
220            pus_version,
221            service: buf[1],
222            subservice: buf[2],
223            msg_counter,
224            dest_id,
225            timestamp: &buf[current_idx..current_idx + params.timestamp_len],
226            spare_bytes: params.spare_bytes,
227        })
228    }
229
230    /// Write the PUS TM secondary header to a provided buffer.
231    pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
232        let written_len = self.written_len();
233        if buf.len() < written_len {
234            return Err(ByteConversionError::ToSliceTooSmall {
235                found: buf.len(),
236                expected: PUS_TM_MIN_SEC_HEADER_LEN,
237            });
238        }
239        buf[0] = (self.pus_version as u8) << 4;
240        buf[1] = self.service;
241        buf[2] = self.subservice;
242        let mut current_idx = 3;
243        if let Some(msg_counter) = self.msg_counter {
244            buf[current_idx] = msg_counter;
245            current_idx += 1;
246        }
247        if let Some(dest_id) = self.dest_id {
248            dest_id.write_to_be_bytes(&mut buf[current_idx..current_idx + dest_id.size()])?;
249            current_idx += dest_id.size();
250        }
251        buf[current_idx..current_idx + self.timestamp.len()].copy_from_slice(self.timestamp);
252        current_idx += self.timestamp.len();
253        if self.spare_bytes > 0 {
254            buf[current_idx..current_idx + self.spare_bytes].fill(0);
255        }
256        Ok(written_len)
257    }
258
259    /// Convert the PUS TM packet secondary header to a [alloc::vec::Vec].
260    #[cfg(feature = "alloc")]
261    pub fn to_vec(&self) -> Vec<u8> {
262        let mut vec = alloc::vec![0; self.written_len()];
263        self.write_to_be_bytes(&mut vec).unwrap();
264        vec
265    }
266
267    /// Length of the secondary header when written to bytes.
268    pub fn written_len(&self) -> usize {
269        let mut len = PUS_TM_MIN_SEC_HEADER_LEN + self.timestamp.len() + self.spare_bytes;
270        if let Some(dest_id) = self.dest_id {
271            len += dest_id.size();
272        }
273        if self.msg_counter.is_some() {
274            len += 1;
275        }
276        len
277    }
278
279    /// Length for the provided packet parameters.
280    pub fn len_for_params(params: &SecondaryHeaderParameters) -> usize {
281        let mut len = PUS_TM_MIN_SEC_HEADER_LEN + params.timestamp_len + params.spare_bytes;
282        if let Some(dest_id) = params.opt_dest_id_len {
283            len += dest_id;
284        }
285        if params.has_msg_counter {
286            len += 1;
287        }
288        len
289    }
290}
291
292impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
293    #[inline]
294    fn pus_version(&self) -> PusVersion {
295        self.pus_version
296    }
297
298    #[inline]
299    fn service(&self) -> u8 {
300        self.service
301    }
302
303    #[inline]
304    fn subservice(&self) -> u8 {
305        self.subservice
306    }
307
308    #[inline]
309    fn msg_counter(&self) -> Option<u8> {
310        self.msg_counter
311    }
312
313    #[inline]
314    fn dest_id(&self) -> Option<UnsignedByteField> {
315        self.dest_id
316    }
317
318    #[inline]
319    fn spare_bytes(&self) -> usize {
320        self.spare_bytes
321    }
322}
323
324/// This class models the PUS C telemetry packet. It is the primary data structure to generate the
325/// raw byte representation of PUS telemetry.
326///
327/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the [serde]
328/// feature is used which allows to send around TM packets in a raw byte format using a serde
329/// provider like [postcard](https://docs.rs/postcard/latest/postcard/).
330///
331/// There is no spare bytes support yet.
332///
333/// # Lifetimes
334///
335/// * `'time` - This is the lifetime of the user provided timestamp.
336/// * `'src_data` - This is the lifetime of the user provided source data.
337#[derive(Eq, Debug, Copy, Clone)]
338#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
339#[cfg_attr(feature = "defmt", derive(defmt::Format))]
340pub struct PusTmCreator<'time, 'src_data> {
341    /// Space packet header.
342    pub sp_header: SpHeader,
343    /// PUS TM secondary header.
344    #[cfg_attr(feature = "serde", serde(borrow))]
345    pub sec_header: PusTmSecondaryHeader<'time>,
346    source_data: &'src_data [u8],
347    /// If this is set to false, a manual call to [Self::calc_own_crc16] or
348    /// [Self::update_packet_fields] is necessary for the serialized or cached CRC16 to be valid.
349    pub calc_crc_on_serialization: bool,
350}
351
352impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
353    /// Generates a new struct instance.
354    ///
355    /// # Arguments
356    ///
357    /// * `sp_header` - Space packet header information. The correct packet type and the secondary
358    ///   header flag are set correctly by the constructor.
359    /// * `sec_header` - Information contained in the secondary header, including the service
360    ///   and subservice type
361    /// * `source_data` - Custom application data
362    /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
363    ///   field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set
364    ///   the correct value to this field manually
365    #[inline]
366    pub fn new(
367        mut sp_header: SpHeader,
368        sec_header: PusTmSecondaryHeader<'time>,
369        source_data: &'src_data [u8],
370        set_ccsds_len: bool,
371    ) -> Self {
372        sp_header.set_packet_type(PacketType::Tm);
373        sp_header.set_sec_header_flag();
374        let mut pus_tm = Self {
375            sp_header,
376            source_data,
377            sec_header,
378            calc_crc_on_serialization: true,
379        };
380        if set_ccsds_len {
381            pus_tm.update_ccsds_data_len();
382        }
383        pus_tm
384    }
385
386    /// Simple constructor which builds the [PusTmSecondaryHeader] internally from the
387    /// provided arguments.
388    #[inline]
389    pub fn new_simple(
390        sp_header: SpHeader,
391        service: u8,
392        subservice: u8,
393        time_provider: &impl TimeWriter,
394        stamp_buf: &'time mut [u8],
395        source_data: &'src_data [u8],
396        set_ccsds_len: bool,
397    ) -> Result<Self, TimestampError> {
398        let stamp_size = time_provider.write_to_bytes(stamp_buf)?;
399        let sec_header =
400            PusTmSecondaryHeader::new_simple(service, subservice, &stamp_buf[0..stamp_size]);
401        Ok(Self::new(sp_header, sec_header, source_data, set_ccsds_len))
402    }
403
404    /// Constructor for PUS TM packets without source data.
405    #[inline]
406    pub fn new_no_source_data(
407        sp_header: SpHeader,
408        sec_header: PusTmSecondaryHeader<'time>,
409        set_ccsds_len: bool,
410    ) -> Self {
411        Self::new(sp_header, sec_header, &[], set_ccsds_len)
412    }
413
414    /// Raw timestamp slice.
415    #[inline]
416    pub fn timestamp(&self) -> &[u8] {
417        self.sec_header.timestamp
418    }
419
420    /// Raw source data slice.
421    #[inline]
422    pub fn source_data(&self) -> &[u8] {
423        self.source_data
424    }
425
426    /// Set the destination ID.
427    #[inline]
428    pub fn set_dest_id(&mut self, dest_id: Option<UnsignedByteField>) {
429        self.sec_header.dest_id = dest_id;
430    }
431
432    /// Set the message counter for the current service ID.
433    #[inline]
434    pub fn set_msg_counter(&mut self, msg_counter: Option<u8>) {
435        self.sec_header.msg_counter = msg_counter
436    }
437
438    sp_header_impls!();
439
440    /// This is called automatically if the `set_ccsds_len` argument in the [Self::new] call was
441    /// used.
442    /// If this was not done or the time stamp or source data is set or changed after construction,
443    /// this function needs to be called to ensure that the data length field of the CCSDS header
444    /// is set correctly
445    #[inline]
446    pub fn update_ccsds_data_len(&mut self) {
447        self.sp_header.data_len =
448            self.len_written() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
449    }
450
451    /// This function should be called before the TM packet is serialized if
452    /// [Self::calc_crc_on_serialization] is set to False. It will calculate and cache the CRC16.
453    pub fn calc_own_crc16(&self) -> u16 {
454        let mut digest = CRC_CCITT_FALSE.digest();
455        let sph_zc = crate::zc::SpHeader::from(self.sp_header);
456        digest.update(sph_zc.as_bytes());
457        let mut fixed_header_part: [u8; PUS_TM_MIN_SEC_HEADER_LEN] = [0; PUS_TM_MIN_SEC_HEADER_LEN];
458        fixed_header_part[0] = (self.sec_header.pus_version() as u8) << 4;
459        fixed_header_part[1] = self.sec_header.service;
460        fixed_header_part[2] = self.sec_header.subservice;
461
462        digest.update(fixed_header_part.as_slice());
463        if let Some(msg_counter) = self.sec_header.msg_counter {
464            digest.update(&[msg_counter]);
465        }
466        if let Some(dest_id) = self.sec_header.dest_id {
467            let mut dest_id_buf: [u8; core::mem::size_of::<u64>()] =
468                [0; core::mem::size_of::<u64>()];
469            // Unwrap okay, this can never fail because we created a buffer with the largest
470            // possible size.
471            let len = dest_id.write_to_be_bytes(&mut dest_id_buf).unwrap();
472            digest.update(&dest_id_buf[0..len]);
473        }
474        digest.update(self.sec_header.timestamp);
475        for _ in 0..self.sec_header.spare_bytes {
476            digest.update(&[0]);
477        }
478        digest.update(self.source_data);
479        digest.finalize()
480    }
481
482    /// This helper function calls both [Self::update_ccsds_data_len] and [Self::calc_own_crc16]
483    #[inline]
484    pub fn update_packet_fields(&mut self) {
485        self.update_ccsds_data_len();
486    }
487
488    /// Write the raw PUS byte representation to a provided buffer.
489    pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> {
490        let writer_unfinalized = self.common_write(slice)?;
491        Ok(writer_unfinalized.finalize())
492    }
493
494    /// Write the raw PUS byte representation to a provided buffer.
495    pub fn write_to_bytes_crc_no_table(
496        &self,
497        slice: &mut [u8],
498    ) -> Result<usize, ByteConversionError> {
499        let writer_unfinalized = self.common_write(slice)?;
500        Ok(writer_unfinalized.finalize_crc_no_table())
501    }
502
503    /// Write the raw PUS byte representation to a provided buffer.
504    pub fn write_to_bytes_no_crc(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> {
505        let writer_unfinalized = self.common_write(slice)?;
506        Ok(writer_unfinalized.finalize_no_crc())
507    }
508
509    fn common_write<'a>(
510        &self,
511        slice: &'a mut [u8],
512    ) -> Result<PusTmCreatorWithReservedSourceData<'a>, ByteConversionError> {
513        if self.len_written() > slice.len() {
514            return Err(ByteConversionError::ToSliceTooSmall {
515                found: slice.len(),
516                expected: self.len_written(),
517            });
518        }
519        let mut writer_unfinalized = PusTmCreatorWithReservedSourceData::write_to_bytes_partially(
520            slice,
521            self.sp_header,
522            self.sec_header,
523            self.source_data.len(),
524        )?;
525        writer_unfinalized
526            .source_data_mut()
527            .copy_from_slice(self.source_data);
528        Ok(writer_unfinalized)
529    }
530
531    /// Append the raw PUS byte representation to a provided [alloc::vec::Vec]
532    #[cfg(feature = "alloc")]
533    pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
534        let sph_zc = crate::zc::SpHeader::from(self.sp_header);
535        let mut appended_len = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + self.sec_header.timestamp.len();
536        appended_len += self.source_data.len();
537        let start_idx = vec.len();
538        vec.extend_from_slice(sph_zc.as_bytes());
539        vec.extend_from_slice(&self.sec_header.to_vec());
540        vec.extend_from_slice(self.source_data);
541        let mut digest = CRC_CCITT_FALSE.digest();
542        digest.update(&vec[start_idx..start_idx + appended_len - 2]);
543        vec.extend_from_slice(&digest.finalize().to_be_bytes());
544        Ok(appended_len)
545    }
546}
547
548impl WritablePusPacket for PusTmCreator<'_, '_> {
549    #[inline]
550    fn len_written(&self) -> usize {
551        CCSDS_HEADER_LEN + self.sec_header.written_len() + self.source_data.len() + 2
552    }
553
554    /// Currently, checksum is always added.
555    fn has_checksum(&self) -> bool {
556        true
557    }
558
559    /// Write the raw PUS byte representation to a provided buffer.
560    fn write_to_bytes_no_checksum(&self, slice: &mut [u8]) -> Result<usize, PusError> {
561        Ok(Self::write_to_bytes_no_crc(self, slice)?)
562    }
563
564    fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
565        Ok(Self::write_to_bytes(self, slice)?)
566    }
567
568    fn write_to_bytes_checksum_no_table(&self, slice: &mut [u8]) -> Result<usize, PusError> {
569        Ok(Self::write_to_bytes_crc_no_table(self, slice)?)
570    }
571}
572
573impl PartialEq for PusTmCreator<'_, '_> {
574    #[inline]
575    fn eq(&self, other: &Self) -> bool {
576        self.sp_header == other.sp_header
577            && self.sec_header == other.sec_header
578            && self.source_data == other.source_data
579    }
580}
581
582impl CcsdsPacket for PusTmCreator<'_, '_> {
583    delegate!(to self.sp_header {
584        #[inline]
585        fn ccsds_version(&self) -> u3;
586        #[inline]
587        fn packet_id(&self) -> crate::PacketId;
588        #[inline]
589        fn psc(&self) -> crate::PacketSequenceControl;
590        #[inline]
591        fn data_len(&self) -> u16;
592    });
593}
594
595impl PusPacket for PusTmCreator<'_, '_> {
596    #[inline]
597    fn pus_version(&self) -> Result<PusVersion, u4> {
598        Ok(self.sec_header.pus_version)
599    }
600
601    #[inline]
602    fn message_type_id(&self) -> super::MessageTypeId {
603        super::MessageTypeId {
604            type_id: self.sec_header.service,
605            subtype_id: self.sec_header.subservice,
606        }
607    }
608
609    #[inline]
610    fn service_type_id(&self) -> u8 {
611        self.sec_header.service
612    }
613    #[inline]
614    fn message_subtype_id(&self) -> u8 {
615        self.sec_header.subservice
616    }
617
618    #[inline]
619    fn user_data(&self) -> &[u8] {
620        self.source_data
621    }
622
623    #[inline]
624    fn checksum(&self) -> Option<u16> {
625        Some(self.calc_own_crc16())
626    }
627}
628
629impl GenericPusTmSecondaryHeader for PusTmCreator<'_, '_> {
630    delegate!(to self.sec_header {
631        #[inline]
632        fn pus_version(&self) -> PusVersion;
633        #[inline]
634        fn service(&self) -> u8;
635        #[inline]
636        fn subservice(&self) -> u8;
637        #[inline]
638        fn dest_id(&self) -> Option<UnsignedByteField>;
639        #[inline]
640        fn msg_counter(&self) -> Option<u8>;
641        #[inline]
642        fn spare_bytes(&self) -> usize;
643    });
644}
645
646impl IsPusTelemetry for PusTmCreator<'_, '_> {}
647
648/// A specialized variant of [PusTmCreator] designed for efficiency when handling large source
649/// data.
650///
651/// Unlike [PusTmCreator], this type does not require the user to provide the source data
652/// as a separate slice. Instead, it allows writing the source data directly into the provided
653/// serialization buffer. This eliminates the need for an intermediate buffer and the associated
654/// memory copy, improving performance, particularly when working with large payloads.
655///
656/// **Important:** The total length of the source data must be known and specified in advance
657/// to ensure correct serialization behavior.
658///
659/// Note that this abstraction intentionally omits certain trait implementations that are available
660/// on [PusTmCreator], as they are not applicable in this optimized usage pattern.
661pub struct PusTmCreatorWithReservedSourceData<'buf> {
662    buf: &'buf mut [u8],
663    source_data_offset: usize,
664    full_len: usize,
665}
666
667impl<'buf> PusTmCreatorWithReservedSourceData<'buf> {
668    /// Generates a new instance with reserved space for the user source data.
669    ///
670    /// # Arguments
671    ///
672    /// * `sp_header` - Space packet header information. The correct packet type and the secondary
673    ///   header flag are set correctly by the constructor.
674    /// * `sec_header` - Information contained in the secondary header, including the service
675    ///   and subservice type
676    /// * `src_data_len` - Custom source data length
677    #[inline]
678    pub fn new(
679        buf: &'buf mut [u8],
680        mut sp_header: SpHeader,
681        sec_header: PusTmSecondaryHeader,
682        src_data_len: usize,
683    ) -> Result<Self, ByteConversionError> {
684        sp_header.set_packet_type(PacketType::Tm);
685        sp_header.set_sec_header_flag();
686        let len_written =
687            PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + sec_header.timestamp.len() + src_data_len;
688        if len_written > buf.len() {
689            return Err(ByteConversionError::ToSliceTooSmall {
690                found: buf.len(),
691                expected: len_written,
692            });
693        }
694        sp_header.data_len = len_written as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
695        Self::write_to_bytes_partially(buf, sp_header, sec_header, src_data_len)
696    }
697
698    fn write_to_bytes_partially(
699        buf: &'buf mut [u8],
700        sp_header: SpHeader,
701        sec_header: PusTmSecondaryHeader,
702        src_data_len: usize,
703    ) -> Result<Self, ByteConversionError> {
704        let mut curr_idx = 0;
705        sp_header.write_to_be_bytes(&mut buf[0..CCSDS_HEADER_LEN])?;
706        curr_idx += CCSDS_HEADER_LEN;
707        curr_idx += sec_header.write_to_be_bytes(&mut buf[CCSDS_HEADER_LEN..])?;
708        let source_data_offset = curr_idx;
709        curr_idx += src_data_len;
710        Ok(Self {
711            buf,
712            source_data_offset,
713            full_len: curr_idx + 2,
714        })
715    }
716
717    /// Length of the full packet when written to bytes.
718    #[inline]
719    pub const fn len_written(&self) -> usize {
720        self.full_len
721    }
722
723    /// Mutable access to the source data buffer.
724    #[inline]
725    pub fn source_data_mut(&mut self) -> &mut [u8] {
726        &mut self.buf[self.source_data_offset..self.full_len - 2]
727    }
728
729    /// Access to the source data buffer.
730    #[inline]
731    pub fn source_data(&self) -> &[u8] {
732        &self.buf[self.source_data_offset..self.full_len - 2]
733    }
734
735    /// Source data length.
736    #[inline]
737    pub fn source_data_len(&self) -> usize {
738        self.full_len - 2 - self.source_data_offset
739    }
740
741    /// Finalize the TM packet by calculating and writing the CRC16.
742    ///
743    /// Returns the full packet length.
744    pub fn finalize(self) -> usize {
745        let mut digest = CRC_CCITT_FALSE.digest();
746        digest.update(&self.buf[0..self.full_len - 2]);
747        self.buf[self.full_len - 2..self.full_len]
748            .copy_from_slice(&digest.finalize().to_be_bytes());
749        self.full_len
750    }
751
752    /// Finalize the TM packet by calculating and writing the CRC16 using a table-less
753    /// implementation.
754    ///
755    /// Returns the full packet length.
756    pub fn finalize_crc_no_table(self) -> usize {
757        let mut digest = CRC_CCITT_FALSE_NO_TABLE.digest();
758        digest.update(&self.buf[0..self.full_len - 2]);
759        self.buf[self.full_len - 2..self.full_len]
760            .copy_from_slice(&digest.finalize().to_be_bytes());
761        self.full_len
762    }
763
764    /// Finalize the TM packet without writing the CRC16.
765    ///
766    /// Returns the length WITHOUT the CRC16.
767    #[inline]
768    pub fn finalize_no_crc(self) -> usize {
769        self.full_len - 2
770    }
771}
772
773/// This class models the PUS C telemetry packet. It is the primary data structure to read
774/// a telemetry packet from raw bytes.
775///
776/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the [serde]
777/// feature is used which allows to send around TM packets in a raw byte format using a serde
778/// provider like [postcard](https://docs.rs/postcard/latest/postcard/).
779///
780/// There is no spare bytes support yet.
781///
782/// # Lifetimes
783///
784/// * `'raw_data` - Lifetime of the raw slice this class is constructed from.
785#[derive(Eq, Debug, Copy, Clone)]
786#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
787#[cfg_attr(feature = "defmt", derive(defmt::Format))]
788pub struct PusTmReader<'raw_data> {
789    /// Space packet header.
790    pub sp_header: SpHeader,
791    /// PUS TM secondary header.
792    pub sec_header: PusTmSecondaryHeader<'raw_data>,
793    #[cfg_attr(feature = "serde", serde(skip))]
794    raw_data: &'raw_data [u8],
795    source_data: &'raw_data [u8],
796    crc16: u16,
797}
798
799impl<'raw_data> PusTmReader<'raw_data> {
800    /// Create a [PusTmReader] instance from a raw slice. On success, it returns a tuple containing
801    /// the instance and the found byte length of the packet. The timestamp length needs to be
802    /// known beforehand.
803    ///
804    /// This function will check the CRC-16 of the PUS packet and will return an appropriate
805    /// [PusError] if the check fails.
806    pub fn new(
807        slice: &'raw_data [u8],
808        sec_header_params: &SecondaryHeaderParameters,
809    ) -> Result<Self, PusError> {
810        let tc = Self::new_no_crc_check(slice, sec_header_params)?;
811        verify_crc16_ccitt_false_from_raw_to_pus_error(tc.raw_data(), tc.crc16)?;
812        Ok(tc)
813    }
814
815    /// Like [PusTmReader::new] but uses a table-less CRC implementation.
816    pub fn new_crc_no_table(
817        slice: &'raw_data [u8],
818        sec_header_params: &SecondaryHeaderParameters,
819    ) -> Result<Self, PusError> {
820        let tc = Self::new_no_crc_check(slice, sec_header_params)?;
821        verify_crc16_ccitt_false_from_raw_to_pus_error_no_table(tc.raw_data(), tc.crc16)?;
822        Ok(tc)
823    }
824
825    /// New constructor which does not check the CRC16.
826    pub fn new_no_crc_check(
827        slice: &'raw_data [u8],
828        sec_header_params: &SecondaryHeaderParameters,
829    ) -> Result<Self, PusError> {
830        let raw_data_len = slice.len();
831        if raw_data_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
832            return Err(ByteConversionError::FromSliceTooSmall {
833                found: raw_data_len,
834                expected: PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA,
835            }
836            .into());
837        }
838        let mut current_idx = 0;
839        let (sp_header, _) = SpHeader::from_be_bytes(&slice[0..CCSDS_HEADER_LEN])?;
840        current_idx += CCSDS_HEADER_LEN;
841        let total_len = sp_header.packet_len();
842        if raw_data_len < total_len {
843            return Err(ByteConversionError::FromSliceTooSmall {
844                found: raw_data_len,
845                expected: total_len,
846            }
847            .into());
848        }
849        if total_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
850            return Err(ByteConversionError::FromSliceTooSmall {
851                found: total_len,
852                expected: PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA,
853            }
854            .into());
855        }
856        let sec_header =
857            PusTmSecondaryHeader::from_bytes(&slice[current_idx..], sec_header_params)?;
858        current_idx += sec_header.written_len();
859        let raw_data = &slice[0..total_len];
860        Ok(Self {
861            sp_header,
862            sec_header,
863            raw_data: &slice[0..total_len],
864            source_data: user_data_from_raw(current_idx, total_len, slice, true)?,
865            crc16: crc_from_raw_data(raw_data)?,
866        })
867    }
868
869    /// Length of the full packet as packed in the raw data.
870    #[inline]
871    pub fn len_packed(&self) -> usize {
872        self.sp_header.packet_len()
873    }
874
875    /// Raw source data slice.
876    #[inline]
877    pub fn source_data(&self) -> &[u8] {
878        self.user_data()
879    }
880
881    /// Raw timestamp slice.
882    #[inline]
883    pub fn timestamp(&self) -> &[u8] {
884        self.sec_header.timestamp
885    }
886
887    /// CRC16 checksum of the packet.
888    #[inline]
889    pub fn crc16(&self) -> u16 {
890        self.crc16
891    }
892
893    /// This function will return the slice [Self] was constructed from.
894    #[inline]
895    pub fn raw_data(&self) -> &[u8] {
896        self.raw_data
897    }
898}
899
900impl PartialEq for PusTmReader<'_> {
901    fn eq(&self, other: &Self) -> bool {
902        self.sec_header == other.sec_header
903            && self.source_data == other.source_data
904            && self.sp_header == other.sp_header
905            && self.crc16 == other.crc16
906    }
907}
908
909impl CcsdsPacket for PusTmReader<'_> {
910    delegate!(to self.sp_header {
911        #[inline]
912        fn ccsds_version(&self) -> u3;
913        #[inline]
914        fn packet_id(&self) -> crate::PacketId;
915        #[inline]
916        fn psc(&self) -> crate::PacketSequenceControl;
917        #[inline]
918        fn data_len(&self) -> u16;
919    });
920}
921
922impl PusPacket for PusTmReader<'_> {
923    #[inline]
924    fn pus_version(&self) -> Result<PusVersion, u4> {
925        Ok(self.sec_header.pus_version)
926    }
927
928    #[inline]
929    fn message_type_id(&self) -> super::MessageTypeId {
930        MessageTypeId {
931            type_id: self.sec_header.service,
932            subtype_id: self.sec_header.subservice,
933        }
934    }
935
936    #[inline]
937    fn service_type_id(&self) -> u8 {
938        self.sec_header.service
939    }
940
941    #[inline]
942    fn message_subtype_id(&self) -> u8 {
943        self.sec_header.subservice
944    }
945
946    #[inline]
947    fn user_data(&self) -> &[u8] {
948        self.source_data
949    }
950
951    #[inline]
952    fn checksum(&self) -> Option<u16> {
953        Some(self.crc16())
954    }
955}
956
957impl GenericPusTmSecondaryHeader for PusTmReader<'_> {
958    delegate!(to self.sec_header {
959        #[inline]
960        fn pus_version(&self) -> PusVersion;
961        #[inline]
962        fn service(&self) -> u8;
963        #[inline]
964        fn subservice(&self) -> u8;
965        #[inline]
966        fn dest_id(&self) -> Option<UnsignedByteField>;
967        #[inline]
968        fn msg_counter(&self) -> Option<u8>;
969        #[inline]
970        fn spare_bytes(&self) -> usize;
971    });
972}
973
974impl IsPusTelemetry for PusTmReader<'_> {}
975
976impl PartialEq<PusTmCreator<'_, '_>> for PusTmReader<'_> {
977    fn eq(&self, other: &PusTmCreator<'_, '_>) -> bool {
978        self.sp_header == other.sp_header
979            && self.sec_header == other.sec_header
980            && self.source_data == other.source_data
981    }
982}
983
984impl PartialEq<PusTmReader<'_>> for PusTmCreator<'_, '_> {
985    fn eq(&self, other: &PusTmReader<'_>) -> bool {
986        self.sp_header == other.sp_header
987            && self.sec_header == other.sec_header
988            && self.source_data == other.source_data
989    }
990}
991
992/// Secondary header field not present error.
993#[derive(Debug, thiserror::Error)]
994#[error("this field is not present in the secondary header")]
995pub struct SecondaryHeaderFieldNotPresentError;
996
997/// Destination ID operation error.
998#[derive(Debug, thiserror::Error)]
999pub enum DestIdOperationError {
1000    /// Field is not present in the secondary header.
1001    #[error("this field is not present in the secondary header")]
1002    FieldNotPresent(#[from] SecondaryHeaderFieldNotPresentError),
1003    /// Invalid field length.
1004    #[error("invalid byte field length")]
1005    InvalidFieldLen,
1006    /// Bzyte conversion error.
1007    #[error("byte conversion error")]
1008    ByteConversionError(#[from] ByteConversionError),
1009}
1010
1011/// This is a helper class to update certain fields in a raw PUS telemetry packet directly in place.
1012/// This can be more efficient than creating a full [PusTmReader], modifying the fields and then
1013/// writing it back to another buffer.
1014///
1015/// Please note that the [Self::finish] method has to be called for the PUS TM CRC16 to be valid
1016/// after changing fields of the TM packet. Furthermore, the constructor of this class will not
1017/// do any checks except basic length checks to ensure that all relevant fields can be updated and
1018/// all methods can be called without a panic. If a full validity check of the PUS TM packet is
1019/// required, it is recommended to construct a full [PusTmReader] object from the raw bytestream
1020/// first.
1021pub struct PusTmZeroCopyWriter<'raw> {
1022    raw_tm: &'raw mut [u8],
1023    sec_header_params: SecondaryHeaderParameters,
1024}
1025
1026impl<'raw> PusTmZeroCopyWriter<'raw> {
1027    /// This function will not do any other checks on the raw data other than a length check
1028    /// for all internal fields which can be updated.
1029    ///
1030    /// It is the responsibility of the user to ensure the raw slice contains a valid telemetry
1031    /// packet.
1032    pub fn new(
1033        raw_tm: &'raw mut [u8],
1034        sec_header_params: &SecondaryHeaderParameters,
1035    ) -> Option<Self> {
1036        let raw_tm_len = raw_tm.len();
1037        if raw_tm_len
1038            < CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + sec_header_params.timestamp_len
1039        {
1040            return None;
1041        }
1042        let sp_header = crate::zc::SpHeader::read_from_bytes(&raw_tm[0..CCSDS_HEADER_LEN]).unwrap();
1043        if raw_tm_len < sp_header.packet_len() {
1044            return None;
1045        }
1046        let writer = Self {
1047            raw_tm: &mut raw_tm[..sp_header.packet_len()],
1048            sec_header_params: *sec_header_params,
1049        };
1050        Some(writer)
1051    }
1052
1053    /// Set the sequence count. Returns false and does not update the value if the passed value
1054    /// exceeds [MAX_APID].
1055    #[inline]
1056    pub fn set_apid(&mut self, apid: u11) {
1057        // Clear APID part of the raw packet ID
1058        let updated_apid = ((((self.raw_tm[0] as u16) << 8) | self.raw_tm[1] as u16)
1059            & !MAX_APID.as_u16())
1060            | apid.as_u16();
1061        self.raw_tm[0..2].copy_from_slice(&updated_apid.to_be_bytes());
1062    }
1063
1064    /// Destination ID field if present.
1065    pub fn dest_id(&self) -> Result<Option<UnsignedByteField>, ByteConversionError> {
1066        if self.sec_header_params.opt_dest_id_len.is_none() {
1067            return Ok(None);
1068        }
1069        let mut base_idx = 10;
1070        if self.sec_header_params.has_msg_counter {
1071            base_idx += 1;
1072        }
1073        let dest_id_len = self.sec_header_params.opt_dest_id_len.unwrap();
1074        if self.raw_tm.len() < base_idx + dest_id_len {
1075            return Err(ByteConversionError::FromSliceTooSmall {
1076                found: self.raw_tm.len(),
1077                expected: base_idx + dest_id_len,
1078            });
1079        }
1080        Ok(Some(
1081            UnsignedByteField::new_from_be_bytes(
1082                dest_id_len,
1083                &self.raw_tm[base_idx..base_idx + dest_id_len],
1084            )
1085            .unwrap(),
1086        ))
1087    }
1088
1089    /// Message counter if present.
1090    pub fn msg_counter(&self) -> Option<u8> {
1091        if !self.sec_header_params.has_msg_counter {
1092            return None;
1093        }
1094        Some(self.raw_tm[9])
1095    }
1096
1097    /// This function sets the message counter in the PUS TM secondary header.
1098    ///
1099    /// Please note that usage of this function is only valid if the secondary header has a
1100    /// packet subcounter field, which is a manged parameter which might not be present.
1101    #[inline]
1102    pub fn set_msg_count(
1103        &mut self,
1104        msg_count: u8,
1105    ) -> Result<(), SecondaryHeaderFieldNotPresentError> {
1106        if !self.sec_header_params.has_msg_counter {
1107            return Err(SecondaryHeaderFieldNotPresentError);
1108        }
1109        self.raw_tm[9] = msg_count;
1110        Ok(())
1111    }
1112
1113    /// This function sets the destination ID in the PUS TM secondary header.
1114    #[inline]
1115    pub fn set_destination_id(
1116        &mut self,
1117        dest_id: UnsignedByteField,
1118    ) -> Result<(), DestIdOperationError> {
1119        if self.sec_header_params.opt_dest_id_len.is_none() {
1120            return Err(SecondaryHeaderFieldNotPresentError.into());
1121        }
1122        let dest_id_len = self.sec_header_params.opt_dest_id_len.unwrap();
1123        if dest_id.size() != dest_id_len {
1124            return Err(DestIdOperationError::InvalidFieldLen);
1125        }
1126        let mut base_idx = 10;
1127        if self.sec_header_params.has_msg_counter {
1128            base_idx += 1;
1129        }
1130        if self.raw_tm.len() < base_idx + dest_id_len {
1131            return Err(DestIdOperationError::ByteConversionError(
1132                ByteConversionError::ToSliceTooSmall {
1133                    found: self.raw_tm.len(),
1134                    expected: base_idx + dest_id_len,
1135                },
1136            ));
1137        }
1138        dest_id
1139            .write_to_be_bytes(&mut self.raw_tm[base_idx..base_idx + dest_id_len])
1140            .unwrap();
1141        Ok(())
1142    }
1143
1144    /// Helper API to generate the space packet header portion of the PUS TM from the raw memory.
1145    #[inline]
1146    pub fn sp_header(&self) -> crate::zc::SpHeader {
1147        // Valid minimum length of packet was checked before.
1148        crate::zc::SpHeader::read_from_bytes(&self.raw_tm[0..CCSDS_HEADER_LEN]).unwrap()
1149    }
1150
1151    /// Set the sequence count.
1152    #[inline]
1153    pub fn set_seq_count(&mut self, seq_count: u14) {
1154        let new_psc = (u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000)
1155            | seq_count.as_u16();
1156        self.raw_tm[2..4].copy_from_slice(&new_psc.to_be_bytes());
1157    }
1158
1159    /// This method has to be called after modifying fields to ensure the CRC16 of the telemetry
1160    /// packet remains valid.
1161    pub fn finish(self) {
1162        let slice_len = self.raw_tm.len();
1163        let crc16 = calc_pus_crc16(&self.raw_tm[..slice_len - 2]);
1164        self.raw_tm[slice_len - 2..].copy_from_slice(&crc16.to_be_bytes());
1165    }
1166}
1167
1168impl CcsdsPacket for PusTmZeroCopyWriter<'_> {
1169    #[inline]
1170    fn ccsds_version(&self) -> u3 {
1171        self.sp_header().ccsds_version()
1172    }
1173
1174    #[inline]
1175    fn packet_id(&self) -> crate::PacketId {
1176        self.sp_header().packet_id()
1177    }
1178
1179    #[inline]
1180    fn psc(&self) -> crate::PacketSequenceControl {
1181        self.sp_header().psc()
1182    }
1183
1184    #[inline]
1185    fn data_len(&self) -> u16 {
1186        self.sp_header().data_len()
1187    }
1188}
1189
1190impl PusPacket for PusTmZeroCopyWriter<'_> {
1191    #[inline]
1192    fn pus_version(&self) -> Result<PusVersion, u4> {
1193        PusVersion::try_from(u4::new((self.raw_tm[6] >> 4) & 0b1111))
1194    }
1195
1196    #[inline]
1197    fn message_type_id(&self) -> super::MessageTypeId {
1198        MessageTypeId {
1199            type_id: self.service_type_id(),
1200            subtype_id: self.message_subtype_id(),
1201        }
1202    }
1203
1204    #[inline]
1205    fn service_type_id(&self) -> u8 {
1206        self.raw_tm[7]
1207    }
1208
1209    #[inline]
1210    fn message_subtype_id(&self) -> u8 {
1211        self.raw_tm[8]
1212    }
1213
1214    #[inline]
1215    fn user_data(&self) -> &[u8] {
1216        &self.raw_tm[CCSDS_HEADER_LEN
1217            + PUS_TM_MIN_SEC_HEADER_LEN
1218            + self.sec_header_params.timestamp_len
1219            ..self.sp_header().packet_len() - 2]
1220    }
1221
1222    #[inline]
1223    fn checksum(&self) -> Option<u16> {
1224        Some(u16::from_be_bytes(
1225            self.raw_tm[self.sp_header().packet_len() - 2..self.sp_header().packet_len()]
1226                .try_into()
1227                .unwrap(),
1228        ))
1229    }
1230}
1231
1232#[cfg(test)]
1233mod tests {
1234    use alloc::string::ToString;
1235
1236    use super::*;
1237    use crate::time::cds::CdsTime;
1238    use crate::{ecss::PusVersion::PusA, util::UnsignedByteFieldU16};
1239    use crate::{SpHeader, MAX_SEQ_COUNT};
1240    #[cfg(feature = "serde")]
1241    use postcard::{from_bytes, to_allocvec};
1242
1243    const DUMMY_DATA: &[u8] = &[0, 1, 2];
1244    const MIN_SEC_HEADER_PARAMS: SecondaryHeaderParameters =
1245        SecondaryHeaderParameters::new_minimal(7);
1246
1247    fn ping_reply_no_data<'a, 'b>(
1248        timestamp: &'a [u8],
1249        dest_id: Option<UnsignedByteField>,
1250    ) -> PusTmCreator<'a, 'b> {
1251        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1252        let mut tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
1253        tm_header.dest_id = dest_id;
1254        PusTmCreator::new_no_source_data(sph, tm_header, true)
1255    }
1256
1257    fn ping_reply_with_data_and_additional_fields<'a, 'b>(
1258        data: &'b [u8],
1259        msg_counter: Option<u8>,
1260        dest_id: Option<UnsignedByteField>,
1261        timestamp: &'a [u8],
1262    ) -> PusTmCreator<'a, 'b> {
1263        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1264        let tm_header = PusTmSecondaryHeader::new(17, 2, msg_counter, dest_id, timestamp, 0);
1265        PusTmCreator::new(sph, tm_header, data, true)
1266    }
1267
1268    fn ping_reply_with_data<'a, 'b>(data: &'b [u8], timestamp: &'a [u8]) -> PusTmCreator<'a, 'b> {
1269        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1270        let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
1271        PusTmCreator::new(sph, tm_header, data, true)
1272    }
1273
1274    fn base_hk_reply<'a, 'b>(timestamp: &'a [u8], src_data: &'b [u8]) -> PusTmCreator<'a, 'b> {
1275        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1276        let tc_header = PusTmSecondaryHeader::new_simple(3, 5, timestamp);
1277        PusTmCreator::new(sph, tc_header, src_data, true)
1278    }
1279
1280    fn dummy_timestamp() -> &'static [u8] {
1281        &[0, 1, 2, 3, 4, 5, 6]
1282    }
1283
1284    #[test]
1285    fn test_basic() {
1286        let timestamp = dummy_timestamp();
1287        let pus_tm = ping_reply_no_data(timestamp, None);
1288        verify_ping_reply(&pus_tm, false, 18, dummy_timestamp(), None, None);
1289    }
1290
1291    #[test]
1292    fn test_basic_simple_api() {
1293        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1294        let time_provider = CdsTime::new_with_u16_days(0, 0);
1295        let mut stamp_buf: [u8; 8] = [0; 8];
1296        let pus_tm =
1297            PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
1298                .unwrap();
1299        verify_ping_reply(&pus_tm, false, 18, &[64, 0, 0, 0, 0, 0, 0], None, None);
1300    }
1301
1302    #[test]
1303    fn test_basic_simple_api_with_dest_id_msg_counter() {
1304        let msg_counter = Some(5);
1305        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1306        let pus_tm = ping_reply_with_data_and_additional_fields(
1307            &[],
1308            msg_counter,
1309            dest_id,
1310            dummy_timestamp(),
1311        );
1312        verify_ping_reply(&pus_tm, false, 21, dummy_timestamp(), dest_id, msg_counter);
1313    }
1314
1315    #[test]
1316    fn test_basic_simple_api_with_dest_id() {
1317        let msg_counter = None;
1318        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1319        let pus_tm = ping_reply_with_data_and_additional_fields(
1320            &[],
1321            msg_counter,
1322            dest_id,
1323            dummy_timestamp(),
1324        );
1325        verify_ping_reply(&pus_tm, false, 20, dummy_timestamp(), dest_id, msg_counter);
1326    }
1327
1328    #[test]
1329    fn test_basic_simple_api_with_msg_counter() {
1330        let msg_counter = Some(5);
1331        let dest_id = None;
1332        let pus_tm = ping_reply_with_data_and_additional_fields(
1333            &[],
1334            msg_counter,
1335            dest_id,
1336            dummy_timestamp(),
1337        );
1338        verify_ping_reply(&pus_tm, false, 19, dummy_timestamp(), dest_id, msg_counter);
1339    }
1340
1341    #[test]
1342    fn test_serialization_no_source_data() {
1343        let timestamp = dummy_timestamp();
1344        let pus_tm = ping_reply_no_data(timestamp, None);
1345        let mut buf: [u8; 32] = [0; 32];
1346        let ser_len = pus_tm
1347            .write_to_bytes(&mut buf)
1348            .expect("Serialization failed");
1349        assert_eq!(ser_len, 18);
1350        verify_raw_ping_reply(pus_tm.checksum(), &buf, ser_len, None, None);
1351    }
1352
1353    #[test]
1354    fn test_serialization_with_additional_fields() {
1355        let msg_counter = Some(5);
1356        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1357        let pus_tm = ping_reply_with_data_and_additional_fields(
1358            &[],
1359            msg_counter,
1360            dest_id,
1361            dummy_timestamp(),
1362        );
1363        let mut buf: [u8; 32] = [0; 32];
1364        let ser_len = pus_tm
1365            .write_to_bytes(&mut buf)
1366            .expect("Serialization failed");
1367        assert_eq!(ser_len, 21);
1368        verify_raw_ping_reply(pus_tm.checksum(), &buf, ser_len, msg_counter, dest_id);
1369    }
1370
1371    #[test]
1372    fn test_serialization_with_dest_id() {
1373        let msg_counter = None;
1374        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1375        let pus_tm = ping_reply_with_data_and_additional_fields(
1376            &[],
1377            msg_counter,
1378            dest_id,
1379            dummy_timestamp(),
1380        );
1381        let mut buf: [u8; 32] = [0; 32];
1382        let ser_len = pus_tm
1383            .write_to_bytes(&mut buf)
1384            .expect("Serialization failed");
1385        assert_eq!(ser_len, 20);
1386        verify_raw_ping_reply(pus_tm.checksum(), &buf, ser_len, msg_counter, dest_id);
1387    }
1388
1389    #[test]
1390    fn test_serialization_with_msg_counter() {
1391        let msg_counter = Some(5);
1392        let dest_id = None;
1393        let pus_tm = ping_reply_with_data_and_additional_fields(
1394            &[],
1395            msg_counter,
1396            dest_id,
1397            dummy_timestamp(),
1398        );
1399        let mut buf: [u8; 32] = [0; 32];
1400        let ser_len = pus_tm
1401            .write_to_bytes(&mut buf)
1402            .expect("Serialization failed");
1403        assert_eq!(ser_len, 19);
1404        verify_raw_ping_reply(pus_tm.checksum(), &buf, ser_len, msg_counter, dest_id);
1405    }
1406
1407    #[test]
1408    fn test_serialization_no_source_data_alt_ctor() {
1409        let timestamp = dummy_timestamp();
1410        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1411        let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
1412        let mut buf: [u8; 32] = [0; 32];
1413        let mut pus_tm =
1414            PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0).unwrap();
1415        assert_eq!(pus_tm.source_data_len(), 0);
1416        assert_eq!(pus_tm.source_data(), &[]);
1417        assert_eq!(pus_tm.source_data_mut(), &[]);
1418        let ser_len = pus_tm.finalize();
1419        assert_eq!(ser_len, 18);
1420        verify_raw_ping_reply(None, &buf, ser_len, None, None);
1421    }
1422
1423    #[test]
1424    fn test_serialization_no_source_data_alt_ctor_no_crc() {
1425        let timestamp = dummy_timestamp();
1426        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1427        let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
1428        let mut buf: [u8; 32] = [0; 32];
1429        let mut pus_tm =
1430            PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tm_header, 0).unwrap();
1431        assert_eq!(pus_tm.source_data_len(), 0);
1432        assert_eq!(pus_tm.source_data(), &[]);
1433        assert_eq!(pus_tm.source_data_mut(), &[]);
1434        let ser_len = pus_tm.finalize_no_crc();
1435        assert_eq!(ser_len, 16);
1436        verify_raw_ping_reply_no_crc(&buf, None, None);
1437        assert_eq!(buf[16], 0);
1438        assert_eq!(buf[17], 0);
1439    }
1440
1441    #[test]
1442    fn test_serialization_no_source_data_no_table() {
1443        let timestamp = dummy_timestamp();
1444        let pus_tm = ping_reply_no_data(timestamp, None);
1445        let mut buf: [u8; 32] = [0; 32];
1446        let ser_len = pus_tm
1447            .write_to_bytes_crc_no_table(&mut buf)
1448            .expect("Serialization failed");
1449        assert_eq!(ser_len, 18);
1450        verify_raw_ping_reply(pus_tm.checksum(), &buf, ser_len, None, None);
1451    }
1452
1453    #[test]
1454    fn test_serialization_no_source_data_no_crc() {
1455        let timestamp = dummy_timestamp();
1456        let pus_tm = ping_reply_no_data(timestamp, None);
1457        let mut buf: [u8; 32] = [0; 32];
1458        let ser_len = pus_tm
1459            .write_to_bytes_no_crc(&mut buf)
1460            .expect("Serialization failed");
1461        assert_eq!(ser_len, 16);
1462        assert_eq!(buf[16], 0);
1463        assert_eq!(buf[17], 0);
1464    }
1465
1466    #[test]
1467    fn test_serialization_with_source_data() {
1468        let src_data = [1, 2, 3];
1469        let hk_reply = base_hk_reply(dummy_timestamp(), &src_data);
1470        let mut buf: [u8; 32] = [0; 32];
1471        let ser_len = hk_reply
1472            .write_to_bytes(&mut buf)
1473            .expect("Serialization failed");
1474        assert_eq!(ser_len, 21);
1475        assert_eq!(buf[16], 1);
1476        assert_eq!(buf[17], 2);
1477        assert_eq!(buf[18], 3);
1478    }
1479
1480    #[test]
1481    fn test_serialization_with_source_data_alt_ctor() {
1482        let src_data = &[1, 2, 3];
1483        let mut buf: [u8; 32] = [0; 32];
1484        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1485        let tc_header = PusTmSecondaryHeader::new_simple(3, 5, dummy_timestamp());
1486        let mut hk_reply_unwritten =
1487            PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tc_header, 3).unwrap();
1488        assert_eq!(hk_reply_unwritten.source_data_len(), 3);
1489        assert_eq!(hk_reply_unwritten.source_data(), &[0, 0, 0]);
1490        assert_eq!(hk_reply_unwritten.source_data_mut(), &[0, 0, 0]);
1491        let source_data_mut = hk_reply_unwritten.source_data_mut();
1492        source_data_mut.copy_from_slice(src_data);
1493        let ser_len = hk_reply_unwritten.finalize();
1494        assert_eq!(ser_len, 21);
1495        assert_eq!(buf[16], 1);
1496        assert_eq!(buf[17], 2);
1497        assert_eq!(buf[18], 3);
1498    }
1499
1500    #[test]
1501    fn test_serialization_with_source_data_alt_ctor_no_table() {
1502        let src_data = &[1, 2, 3];
1503        let mut buf: [u8; 32] = [0; 32];
1504        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1505        let tc_header = PusTmSecondaryHeader::new_simple(3, 5, dummy_timestamp());
1506        let mut hk_reply_unwritten =
1507            PusTmCreatorWithReservedSourceData::new(&mut buf, sph, tc_header, 3).unwrap();
1508        assert_eq!(hk_reply_unwritten.source_data_len(), 3);
1509        assert_eq!(hk_reply_unwritten.source_data(), &[0, 0, 0]);
1510        assert_eq!(hk_reply_unwritten.source_data_mut(), &[0, 0, 0]);
1511        let source_data_mut = hk_reply_unwritten.source_data_mut();
1512        source_data_mut.copy_from_slice(src_data);
1513        let ser_len = hk_reply_unwritten.finalize_crc_no_table();
1514        assert_eq!(ser_len, 21);
1515        assert_eq!(buf[16], 1);
1516        assert_eq!(buf[17], 2);
1517        assert_eq!(buf[18], 3);
1518    }
1519
1520    #[test]
1521    fn test_setters() {
1522        let timestamp = dummy_timestamp();
1523        let mut pus_tm = ping_reply_no_data(timestamp, None);
1524        let u16_dest_id = UnsignedByteFieldU16::new(0x7fff).into();
1525        pus_tm.set_dest_id(Some(u16_dest_id));
1526        pus_tm.set_msg_counter(Some(0x1f));
1527        assert_eq!(pus_tm.dest_id(), Some(u16_dest_id));
1528        assert_eq!(pus_tm.msg_counter(), Some(0x1f));
1529        pus_tm.set_apid(u11::new(0x7ff));
1530        assert_eq!(pus_tm.apid().value(), 0x7ff);
1531    }
1532
1533    #[test]
1534    fn test_write_into_vec() {
1535        let timestamp = dummy_timestamp();
1536        let pus_tm = ping_reply_no_data(timestamp, None);
1537        let tm_vec = pus_tm.to_vec().expect("Serialization failed");
1538        assert_eq!(tm_vec.len(), 18);
1539        let tm_deserialized = PusTmReader::new(tm_vec.as_slice(), &MIN_SEC_HEADER_PARAMS)
1540            .expect("Deserialization failed");
1541        assert_eq!(tm_vec.len(), tm_deserialized.packet_len());
1542        verify_ping_reply_with_reader(&tm_deserialized, false, 18, dummy_timestamp(), None, None);
1543    }
1544
1545    #[test]
1546    fn test_deserialization_no_source_data() {
1547        let timestamp = dummy_timestamp();
1548        let pus_tm = ping_reply_no_data(timestamp, None);
1549        let mut buf: [u8; 32] = [0; 32];
1550        let ser_len = pus_tm
1551            .write_to_bytes(&mut buf)
1552            .expect("Serialization failed");
1553        assert_eq!(ser_len, 18);
1554        let tm_deserialized =
1555            PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS).expect("Deserialization failed");
1556        assert_eq!(ser_len, tm_deserialized.packet_len());
1557        assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
1558        assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
1559        assert_eq!(tm_deserialized.crc16(), pus_tm.checksum().unwrap());
1560        verify_ping_reply_with_reader(&tm_deserialized, false, 18, dummy_timestamp(), None, None);
1561    }
1562
1563    fn generic_test_deserialization_no_source_data_with_additional_fields(
1564        src_data: &[u8],
1565        expected_full_len: usize,
1566        msg_counter: Option<u8>,
1567        dest_id: Option<UnsignedByteField>,
1568    ) {
1569        let timestamp = dummy_timestamp();
1570        let pus_tm =
1571            ping_reply_with_data_and_additional_fields(src_data, msg_counter, dest_id, timestamp);
1572        let mut buf: [u8; 32] = [0; 32];
1573        let ser_len = pus_tm
1574            .write_to_bytes(&mut buf)
1575            .expect("Serialization failed");
1576        assert_eq!(ser_len, expected_full_len);
1577        let tm_deserialized = PusTmReader::new(
1578            &buf,
1579            &SecondaryHeaderParameters {
1580                timestamp_len: 7,
1581                has_msg_counter: msg_counter.is_some(),
1582                opt_dest_id_len: dest_id.as_ref().map(|id| id.size()),
1583                spare_bytes: 0,
1584            },
1585        )
1586        .expect("Deserialization failed");
1587        assert_eq!(ser_len, tm_deserialized.packet_len());
1588        assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
1589        assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
1590        assert_eq!(tm_deserialized.crc16(), pus_tm.checksum().unwrap());
1591        verify_ping_reply_with_reader(
1592            &tm_deserialized,
1593            false,
1594            expected_full_len,
1595            dummy_timestamp(),
1596            dest_id,
1597            msg_counter,
1598        );
1599    }
1600
1601    #[test]
1602    fn test_deserialization_with_source_data_dest_id_msg_counter() {
1603        let msg_counter = Some(5);
1604        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1605        generic_test_deserialization_no_source_data_with_additional_fields(
1606            &[1, 2, 3],
1607            24,
1608            msg_counter,
1609            dest_id,
1610        );
1611    }
1612
1613    #[test]
1614    fn test_deserialization_no_source_data_with_dest_id_msg_counter() {
1615        let msg_counter = Some(5);
1616        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1617        generic_test_deserialization_no_source_data_with_additional_fields(
1618            &[],
1619            21,
1620            msg_counter,
1621            dest_id,
1622        );
1623    }
1624
1625    #[test]
1626    fn test_deserialization_no_source_data_with_msg_counter() {
1627        let msg_counter = Some(5);
1628        let dest_id = None;
1629        generic_test_deserialization_no_source_data_with_additional_fields(
1630            &[],
1631            19,
1632            msg_counter,
1633            dest_id,
1634        );
1635    }
1636
1637    #[test]
1638    fn test_deserialization_no_source_data_with_dest_id() {
1639        let msg_counter = None;
1640        let dest_id = Some(UnsignedByteFieldU16::new(0x1f1f).into());
1641        generic_test_deserialization_no_source_data_with_additional_fields(
1642            &[],
1643            20,
1644            msg_counter,
1645            dest_id,
1646        );
1647    }
1648
1649    #[test]
1650    fn test_deserialization_no_source_data_with_trait() {
1651        let timestamp = dummy_timestamp();
1652        let pus_tm = ping_reply_no_data(timestamp, None);
1653        let mut buf: [u8; 32] = [0; 32];
1654        let ser_len =
1655            WritablePusPacket::write_to_bytes(&pus_tm, &mut buf).expect("Serialization failed");
1656        assert_eq!(ser_len, 18);
1657        let tm_deserialized =
1658            PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS).expect("Deserialization failed");
1659        assert_eq!(ser_len, tm_deserialized.packet_len());
1660        assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
1661        assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
1662        assert_eq!(tm_deserialized.crc16(), pus_tm.checksum().unwrap());
1663        verify_ping_reply_with_reader(&tm_deserialized, false, 18, dummy_timestamp(), None, None);
1664    }
1665
1666    #[test]
1667    fn test_deserialization_no_table() {
1668        let timestamp = dummy_timestamp();
1669        let pus_tm = ping_reply_no_data(timestamp, None);
1670        let mut buf: [u8; 32] = [0; 32];
1671        let ser_len = pus_tm
1672            .write_to_bytes(&mut buf)
1673            .expect("Serialization failed");
1674        assert_eq!(ser_len, 18);
1675        let tm_deserialized = PusTmReader::new_crc_no_table(&buf, &MIN_SEC_HEADER_PARAMS)
1676            .expect("Deserialization failed");
1677        assert_eq!(ser_len, tm_deserialized.packet_len());
1678        assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
1679        assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
1680        assert_eq!(tm_deserialized.crc16(), pus_tm.checksum().unwrap());
1681        verify_ping_reply_with_reader(&tm_deserialized, false, 18, dummy_timestamp(), None, None);
1682    }
1683
1684    #[test]
1685    fn test_deserialization_faulty_crc() {
1686        let timestamp = dummy_timestamp();
1687        let pus_tm = ping_reply_no_data(timestamp, None);
1688        let mut buf: [u8; 32] = [0; 32];
1689        let ser_len = pus_tm
1690            .write_to_bytes(&mut buf)
1691            .expect("Serialization failed");
1692        assert_eq!(ser_len, 18);
1693        buf[ser_len - 2] = 0;
1694        buf[ser_len - 1] = 0;
1695        let tm_error = PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS);
1696        assert!(tm_error.is_err());
1697        let tm_error = tm_error.unwrap_err();
1698        if let PusError::ChecksumFailure(crc) = tm_error {
1699            assert_eq!(crc, 0);
1700            assert_eq!(
1701                tm_error.to_string(),
1702                "checksum verification for crc16 0x0000 failed"
1703            );
1704        }
1705    }
1706
1707    #[test]
1708    fn test_manual_field_update() {
1709        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
1710        let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_timestamp());
1711        let mut tm = PusTmCreator::new_no_source_data(sph, tc_header, false);
1712        tm.calc_crc_on_serialization = false;
1713        assert_eq!(tm.data_len(), 0x00);
1714        let mut buf: [u8; 32] = [0; 32];
1715        tm.update_ccsds_data_len();
1716        assert_eq!(tm.data_len(), 11);
1717        tm.calc_own_crc16();
1718        let res = tm.write_to_bytes(&mut buf);
1719        assert!(res.is_ok());
1720        tm.sp_header.data_len = 0;
1721        tm.update_packet_fields();
1722        assert_eq!(tm.data_len(), 11);
1723    }
1724
1725    #[test]
1726    fn test_target_buf_too_small() {
1727        let timestamp = dummy_timestamp();
1728        let pus_tm = ping_reply_no_data(timestamp, None);
1729        let mut buf: [u8; 16] = [0; 16];
1730        let res = pus_tm.write_to_bytes(&mut buf);
1731        assert!(res.is_err());
1732        let error = res.unwrap_err();
1733        if let ByteConversionError::ToSliceTooSmall { found, expected } = error {
1734            assert_eq!(expected, 18);
1735            assert_eq!(found, 16);
1736        } else {
1737            panic!("Invalid error {:?}", error);
1738        }
1739    }
1740
1741    #[test]
1742    #[cfg(feature = "alloc")]
1743    fn test_append_to_vec() {
1744        let timestamp = dummy_timestamp();
1745        let pus_tm = ping_reply_no_data(timestamp, None);
1746        let mut vec = Vec::new();
1747        let res = pus_tm.append_to_vec(&mut vec);
1748        assert!(res.is_ok());
1749        assert_eq!(res.unwrap(), 18);
1750        verify_raw_ping_reply(pus_tm.checksum(), vec.as_slice(), res.unwrap(), None, None);
1751    }
1752
1753    #[test]
1754    #[cfg(feature = "alloc")]
1755    fn test_append_to_vec_with_src_data() {
1756        let src_data = [1, 2, 3];
1757        let hk_reply = base_hk_reply(dummy_timestamp(), &src_data);
1758        let mut vec = Vec::new();
1759        vec.push(4);
1760        let res = hk_reply.append_to_vec(&mut vec);
1761        assert!(res.is_ok());
1762        assert_eq!(res.unwrap(), 21);
1763        assert_eq!(vec.len(), 22);
1764    }
1765
1766    fn verify_raw_ping_reply_no_crc(
1767        buf: &[u8],
1768        msg_counter: Option<u8>,
1769        dest_id: Option<UnsignedByteField>,
1770    ) {
1771        // Secondary header is set -> 0b0000_1001 , APID occupies last bit of first byte
1772        assert_eq!(buf[0], 0x09);
1773        // Rest of APID 0x123
1774        assert_eq!(buf[1], 0x23);
1775        // Unsegmented is the default, and first byte of 0x234 occupies this byte as well
1776        assert_eq!(buf[2], 0xc2);
1777        assert_eq!(buf[3], 0x34);
1778        let mut expected_len = 11;
1779        if let Some(dest_id) = dest_id {
1780            expected_len += dest_id.size();
1781        }
1782        if msg_counter.is_some() {
1783            expected_len += 1;
1784        }
1785        assert_eq!(((buf[4] as u16) << 8) | buf[5] as u16, expected_len as u16);
1786        assert_eq!(buf[6], (PusA as u8) << 4);
1787        assert_eq!(buf[7], 17);
1788        assert_eq!(buf[8], 2);
1789        let mut current_idx = 9;
1790        if let Some(msg_counter) = msg_counter {
1791            assert_eq!(buf[current_idx], msg_counter);
1792            current_idx += 1;
1793        }
1794        if let Some(dest_id) = dest_id {
1795            let extracted_dest_id =
1796                UnsignedByteField::new_from_be_bytes(dest_id.size(), &buf[current_idx..])
1797                    .expect("Failed to extract destination ID");
1798            assert_eq!(extracted_dest_id, dest_id);
1799            current_idx += dest_id.size();
1800        }
1801        // Timestamp
1802        assert_eq!(
1803            &buf[current_idx..current_idx + dummy_timestamp().len()],
1804            dummy_timestamp()
1805        );
1806    }
1807
1808    fn verify_raw_ping_reply(
1809        crc16: Option<u16>,
1810        buf: &[u8],
1811        exp_full_len: usize,
1812        msg_counter: Option<u8>,
1813        dest_id: Option<UnsignedByteField>,
1814    ) {
1815        verify_raw_ping_reply_no_crc(buf, msg_counter, dest_id);
1816        let mut digest = CRC_CCITT_FALSE.digest();
1817        digest.update(&buf[0..exp_full_len - 2]);
1818        let crc16_calced = digest.finalize();
1819        let crc16_read = u16::from_be_bytes([buf[exp_full_len - 2], buf[exp_full_len - 1]]);
1820        assert_eq!(crc16_read, crc16_calced);
1821        if let Some(crc16) = crc16 {
1822            assert_eq!(((crc16 >> 8) & 0xff) as u8, buf[exp_full_len - 2]);
1823            assert_eq!((crc16 & 0xff) as u8, buf[exp_full_len - 1]);
1824        }
1825    }
1826
1827    fn verify_ping_reply(
1828        tm: &PusTmCreator,
1829        has_user_data: bool,
1830        exp_full_len: usize,
1831        exp_timestamp: &[u8],
1832        dest_id: Option<UnsignedByteField>,
1833        msg_counter: Option<u8>,
1834    ) {
1835        assert_eq!(tm.len_written(), exp_full_len);
1836        assert_eq!(tm.timestamp(), exp_timestamp);
1837        assert_eq!(tm.source_data(), tm.user_data());
1838        verify_ping_reply_generic(tm, has_user_data, exp_full_len, dest_id, msg_counter);
1839    }
1840
1841    fn verify_ping_reply_with_reader(
1842        tm: &PusTmReader,
1843        has_user_data: bool,
1844        exp_full_len: usize,
1845        exp_timestamp: &[u8],
1846        dest_id: Option<UnsignedByteField>,
1847        msg_counter: Option<u8>,
1848    ) {
1849        assert_eq!(tm.len_packed(), exp_full_len);
1850        assert_eq!(tm.timestamp(), exp_timestamp);
1851        verify_ping_reply_generic(tm, has_user_data, exp_full_len, dest_id, msg_counter);
1852    }
1853
1854    fn verify_ping_reply_generic(
1855        tm: &(impl GenericPusTmSecondaryHeader + PusPacket),
1856        has_user_data: bool,
1857        exp_full_len: usize,
1858        dest_id: Option<UnsignedByteField>,
1859        msg_counter: Option<u8>,
1860    ) {
1861        assert!(tm.is_tm());
1862        assert_eq!(PusPacket::service_type_id(tm), 17);
1863        assert_eq!(GenericPusTmSecondaryHeader::service(tm), 17);
1864        assert_eq!(PusPacket::message_subtype_id(tm), 2);
1865        assert_eq!(GenericPusTmSecondaryHeader::subservice(tm), 2);
1866        assert!(tm.sec_header_flag());
1867        if has_user_data {
1868            assert!(!tm.user_data().is_empty());
1869        }
1870        assert_eq!(tm.apid().value(), 0x123);
1871        assert_eq!(tm.seq_count().value(), 0x234);
1872        assert_eq!(PusPacket::pus_version(tm).unwrap(), PusVersion::PusA);
1873        assert_eq!(
1874            GenericPusTmSecondaryHeader::pus_version(tm),
1875            PusVersion::PusA
1876        );
1877        assert_eq!(tm.data_len(), exp_full_len as u16 - 7);
1878        assert_eq!(tm.dest_id(), dest_id);
1879        assert_eq!(tm.msg_counter(), msg_counter);
1880    }
1881
1882    #[test]
1883    fn partial_eq_pus_tm() {
1884        let timestamp = dummy_timestamp();
1885        let pus_tm_1 = ping_reply_no_data(timestamp, None);
1886        let pus_tm_2 = ping_reply_no_data(timestamp, None);
1887        assert_eq!(pus_tm_1, pus_tm_2);
1888    }
1889
1890    #[test]
1891    fn partial_eq_serialized_vs_derialized() {
1892        let timestamp = dummy_timestamp();
1893        let pus_tm = ping_reply_no_data(timestamp, None);
1894        let mut buf = [0; 32];
1895        pus_tm.write_to_bytes(&mut buf).unwrap();
1896        assert_eq!(
1897            pus_tm,
1898            PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS).unwrap()
1899        );
1900    }
1901
1902    #[test]
1903    fn test_zero_copy_writer() {
1904        let ping_tm = ping_reply_no_data(dummy_timestamp(), None);
1905        let mut buf: [u8; 64] = [0; 64];
1906        let tm_size = ping_tm
1907            .write_to_bytes(&mut buf)
1908            .expect("writing PUS ping TM failed");
1909        let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size], &MIN_SEC_HEADER_PARAMS)
1910            .expect("Creating zero copy writer failed");
1911        writer.set_seq_count(MAX_SEQ_COUNT);
1912        writer.set_apid(MAX_APID);
1913        writer.finish();
1914        // This performs all necessary checks, including the CRC check.
1915        let tm_read_back =
1916            PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS).expect("Re-creating PUS TM failed");
1917        assert_eq!(tm_read_back.packet_len(), tm_size);
1918        assert!(tm_read_back.msg_counter().is_none());
1919        assert!(tm_read_back.dest_id().is_none());
1920        assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT);
1921        assert_eq!(tm_read_back.apid(), MAX_APID);
1922    }
1923
1924    #[test]
1925    fn test_zero_copy_writer_ccsds_api() {
1926        let dest_id = UnsignedByteFieldU16::new(0x1f1f);
1927        let ping_tm = ping_reply_no_data(dummy_timestamp(), Some(dest_id.into()));
1928        let mut buf: [u8; 64] = [0; 64];
1929        let tm_size = ping_tm
1930            .write_to_bytes(&mut buf)
1931            .expect("writing PUS ping TM failed");
1932        let mut writer = PusTmZeroCopyWriter::new(
1933            &mut buf[..tm_size],
1934            &SecondaryHeaderParameters {
1935                timestamp_len: dummy_timestamp().len(),
1936                has_msg_counter: false,
1937                opt_dest_id_len: Some(2),
1938                spare_bytes: 0,
1939            },
1940        )
1941        .expect("Creating zero copy writer failed");
1942        writer.set_seq_count(MAX_SEQ_COUNT);
1943        writer.set_apid(MAX_APID);
1944        writer
1945            .set_destination_id(UnsignedByteFieldU16::new(0xf1f1).into())
1946            .unwrap();
1947        assert_eq!(PusPacket::service_type_id(&writer), 17);
1948        assert_eq!(PusPacket::message_subtype_id(&writer), 2);
1949        assert_eq!(
1950            writer.dest_id().unwrap().unwrap(),
1951            UnsignedByteFieldU16::new(0xf1f1).into()
1952        );
1953        assert_eq!(writer.apid(), MAX_APID);
1954        assert_eq!(writer.seq_count(), MAX_SEQ_COUNT);
1955    }
1956
1957    #[test]
1958    fn test_zero_copy_pus_api() {
1959        let ping_tm = ping_reply_with_data(DUMMY_DATA, dummy_timestamp());
1960        let mut buf: [u8; 64] = [0; 64];
1961        let tm_size = ping_tm
1962            .write_to_bytes(&mut buf)
1963            .expect("writing PUS ping TM failed");
1964        let crc16_raw = u16::from_be_bytes(buf[tm_size - 2..tm_size].try_into().unwrap());
1965        let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size], &MIN_SEC_HEADER_PARAMS)
1966            .expect("Creating zero copy writer failed");
1967        writer.set_seq_count(MAX_SEQ_COUNT);
1968        writer.set_apid(MAX_APID);
1969        assert_eq!(PusPacket::service_type_id(&writer), 17);
1970        assert_eq!(PusPacket::message_subtype_id(&writer), 2);
1971        assert!(writer.dest_id().unwrap().is_none());
1972        assert!(writer.msg_counter().is_none());
1973        if let Err(err) = writer.set_destination_id(UnsignedByteFieldU16::new(0xf1f1).into()) {
1974            matches!(err, DestIdOperationError::FieldNotPresent(_));
1975        } else {
1976            panic!("setting destination ID should have failed");
1977        }
1978        if let Err(err) = writer.set_msg_count(22) {
1979            matches!(err, SecondaryHeaderFieldNotPresentError);
1980        } else {
1981            panic!("setting destination ID should have failed");
1982        }
1983        assert_eq!(writer.user_data(), DUMMY_DATA);
1984        // Need to check crc16 before finish, because finish will update the CRC.
1985        let crc16 = writer.checksum();
1986        assert!(crc16.is_some());
1987        assert_eq!(crc16.unwrap(), crc16_raw);
1988        writer.finish();
1989    }
1990
1991    #[test]
1992    fn test_sec_header_without_stamp() {
1993        let sec_header = PusTmSecondaryHeader::new_simple_no_timestamp(17, 1);
1994        assert_eq!(sec_header.timestamp, &[]);
1995    }
1996
1997    #[test]
1998    fn test_reader_partial_eq() {
1999        let timestamp = dummy_timestamp();
2000        let pus_tm = ping_reply_no_data(timestamp, None);
2001        let mut buf = [0; 32];
2002        pus_tm.write_to_bytes(&mut buf).unwrap();
2003        let tm_0 = PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS).unwrap();
2004        let tm_1 = PusTmReader::new(&buf, &MIN_SEC_HEADER_PARAMS).unwrap();
2005        assert_eq!(tm_0, tm_1);
2006    }
2007    #[test]
2008    fn test_reader_buf_too_small_2() {
2009        let timestamp = dummy_timestamp();
2010        let pus_tm = ping_reply_no_data(timestamp, None);
2011        let mut buf = [0; 32];
2012        let written = pus_tm.write_to_bytes(&mut buf).unwrap();
2013        let tm_error = PusTmReader::new(
2014            &buf[0..PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + 1],
2015            &MIN_SEC_HEADER_PARAMS,
2016        );
2017        assert!(tm_error.is_err());
2018        let tm_error = tm_error.unwrap_err();
2019        if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall {
2020            found,
2021            expected,
2022        }) = tm_error
2023        {
2024            assert_eq!(found, PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + 1);
2025            assert_eq!(expected, written);
2026        } else {
2027            panic!("unexpected error {tm_error}")
2028        }
2029    }
2030    #[test]
2031    fn test_reader_buf_too_small() {
2032        let timestamp = dummy_timestamp();
2033        let pus_tm = ping_reply_no_data(timestamp, None);
2034        let mut buf = [0; 32];
2035        pus_tm.write_to_bytes(&mut buf).unwrap();
2036        let tm_error = PusTmReader::new(&buf[0..5], &MIN_SEC_HEADER_PARAMS);
2037        assert!(tm_error.is_err());
2038        let tm_error = tm_error.unwrap_err();
2039        if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall {
2040            found,
2041            expected,
2042        }) = tm_error
2043        {
2044            assert_eq!(found, 5);
2045            assert_eq!(expected, PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA);
2046        } else {
2047            panic!("unexpected error {tm_error}")
2048        }
2049    }
2050
2051    #[test]
2052    #[cfg(feature = "serde")]
2053    fn test_serialization_creator_serde() {
2054        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
2055        let time_provider = CdsTime::new_with_u16_days(0, 0);
2056        let mut stamp_buf: [u8; 8] = [0; 8];
2057        let pus_tm =
2058            PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
2059                .unwrap();
2060
2061        let output = to_allocvec(&pus_tm).unwrap();
2062        let output_converted_back: PusTmCreator = from_bytes(&output).unwrap();
2063        assert_eq!(output_converted_back, pus_tm);
2064    }
2065
2066    #[test]
2067    #[cfg(feature = "serde")]
2068    fn test_serialization_reader_serde() {
2069        let sph = SpHeader::new_for_unseg_tm(u11::new(0x123), u14::new(0x234), 0);
2070        let time_provider = CdsTime::new_with_u16_days(0, 0);
2071        let mut stamp_buf: [u8; 8] = [0; 8];
2072        let pus_tm =
2073            PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
2074                .unwrap();
2075        let pus_tm_vec = pus_tm.to_vec().unwrap();
2076        let tm_reader = PusTmReader::new(&pus_tm_vec, &MIN_SEC_HEADER_PARAMS).unwrap();
2077        let output = to_allocvec(&tm_reader).unwrap();
2078        let output_converted_back: PusTmReader = from_bytes(&output).unwrap();
2079        assert_eq!(output_converted_back, tm_reader);
2080    }
2081}