spacepackets/ecss/
tm.rs

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