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::TimeWriter;
8
//! use spacepackets::time::cds::CdsTime;
9
//! use spacepackets::{CcsdsPacket, SpHeader};
10
//! use spacepackets::ecss::{PusPacket, WritablePusPacket};
11
//! use spacepackets::ecss::tm::{PusTmCreator, PusTmReader, PusTmSecondaryHeader};
12
//!
13
//! let mut time_buf: [u8; 7] = [0; 7];
14
//! let time_now = CdsTime::now_with_u16_days().expect("creating CDS timestamp failed");
15
//! // This can definitely hold the timestamp, so it is okay to unwrap.
16
//! time_now.write_to_bytes(&mut time_buf).unwrap();
17
//!
18
//! // Create a ping telemetry with no user source data
19
//! let ping_tm = PusTmCreator::new_no_source_data(
20
//!     SpHeader::new_from_apid(0x02),
21
//!     PusTmSecondaryHeader::new_simple(17, 2, &time_buf),
22
//!     true
23
//! );
24
//! println!("{:?}", ping_tm);
25
//! assert_eq!(ping_tm.service(), 17);
26
//! assert_eq!(ping_tm.subservice(), 2);
27
//! assert_eq!(ping_tm.apid(), 0x02);
28
//!
29
//! // Serialize TM into a raw buffer
30
//! let mut test_buf: [u8; 32] = [0; 32];
31
//! let written_size = ping_tm
32
//!     .write_to_bytes(test_buf.as_mut_slice())
33
//!     .expect("Error writing TC to buffer");
34
//! assert_eq!(written_size, 22);
35
//! println!("{:?}", &test_buf[0..written_size]);
36
//!
37
//! // Deserialize from the raw byte representation
38
//! let (ping_tm_reader, read_size) = PusTmReader::new(&test_buf, 7).expect("Deserialization failed");
39
//! assert_eq!(written_size, read_size);
40
//! assert_eq!(ping_tm_reader.service(), 17);
41
//! assert_eq!(ping_tm_reader.subservice(), 2);
42
//! assert_eq!(ping_tm_reader.apid(), 0x02);
43
//! assert_eq!(ping_tm_reader.timestamp(), &time_buf);
44
//! ```
45
use crate::ecss::{
46
    calc_pus_crc16, ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw,
47
    verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion,
48
    WritablePusPacket,
49
};
50
use crate::{
51
    ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SpHeader, CCSDS_HEADER_LEN,
52
    CRC_CCITT_FALSE, MAX_APID, MAX_SEQ_COUNT,
53
};
54
use core::mem::size_of;
55
#[cfg(feature = "serde")]
56
use serde::{Deserialize, Serialize};
57
use zerocopy::AsBytes;
58

            
59
#[cfg(feature = "alloc")]
60
use alloc::vec::Vec;
61
use delegate::delegate;
62

            
63
use crate::time::{TimeWriter, TimestampError};
64

            
65
use self::zc::PusTmSecHeaderWithoutTimestamp;
66

            
67
pub trait IsPusTelemetry {}
68

            
69
/// Length without timestamp
70
pub const PUS_TM_MIN_SEC_HEADER_LEN: usize = 7;
71
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
72
    CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
73

            
74
pub trait GenericPusTmSecondaryHeader {
75
    fn pus_version(&self) -> PusVersion;
76
    fn sc_time_ref_status(&self) -> u8;
77
    fn service(&self) -> u8;
78
    fn subservice(&self) -> u8;
79
    fn msg_counter(&self) -> u16;
80
    fn dest_id(&self) -> u16;
81
}
82

            
83
pub mod zc {
84
    use super::GenericPusTmSecondaryHeader;
85
    use crate::ecss::{PusError, PusVersion};
86
    use zerocopy::{AsBytes, FromBytes, FromZeroes, NetworkEndian, Unaligned, U16};
87

            
88
    #[derive(FromBytes, FromZeroes, AsBytes, Unaligned)]
89
    #[repr(C)]
90
    pub struct PusTmSecHeaderWithoutTimestamp {
91
        pus_version_and_sc_time_ref_status: u8,
92
        service: u8,
93
        subservice: u8,
94
        msg_counter: U16<NetworkEndian>,
95
        dest_id: U16<NetworkEndian>,
96
    }
97

            
98
    pub struct PusTmSecHeader<'slice> {
99
        pub(crate) zc_header: PusTmSecHeaderWithoutTimestamp,
100
        pub(crate) timestamp: &'slice [u8],
101
    }
102

            
103
    impl TryFrom<crate::ecss::tm::PusTmSecondaryHeader<'_>> for PusTmSecHeaderWithoutTimestamp {
104
        type Error = PusError;
105
40
        fn try_from(header: crate::ecss::tm::PusTmSecondaryHeader) -> Result<Self, Self::Error> {
106
40
            if header.pus_version != PusVersion::PusC {
107
                return Err(PusError::VersionNotSupported(header.pus_version));
108
40
            }
109
40
            Ok(PusTmSecHeaderWithoutTimestamp {
110
40
                pus_version_and_sc_time_ref_status: ((header.pus_version as u8) << 4)
111
40
                    | header.sc_time_ref_status,
112
40
                service: header.service,
113
40
                subservice: header.subservice,
114
40
                msg_counter: U16::from(header.msg_counter),
115
40
                dest_id: U16::from(header.dest_id),
116
40
            })
117
40
        }
118
    }
119

            
120
    impl PusTmSecHeaderWithoutTimestamp {
121
28
        pub fn write_to_bytes(&self, slice: &mut [u8]) -> Option<()> {
122
28
            self.write_to(slice)
123
28
        }
124

            
125
24
        pub fn from_bytes(slice: &[u8]) -> Option<Self> {
126
24
            Self::read_from(slice)
127
24
        }
128
    }
129

            
130
    impl GenericPusTmSecondaryHeader for PusTmSecHeaderWithoutTimestamp {
131
        #[inline]
132
16
        fn pus_version(&self) -> PusVersion {
133
16
            PusVersion::try_from(self.pus_version_and_sc_time_ref_status >> 4 & 0b1111)
134
16
                .unwrap_or(PusVersion::Invalid)
135
16
        }
136

            
137
        #[inline]
138
16
        fn sc_time_ref_status(&self) -> u8 {
139
16
            self.pus_version_and_sc_time_ref_status & 0b1111
140
16
        }
141

            
142
        #[inline]
143
16
        fn service(&self) -> u8 {
144
16
            self.service
145
16
        }
146

            
147
        #[inline]
148
16
        fn subservice(&self) -> u8 {
149
16
            self.subservice
150
16
        }
151

            
152
        #[inline]
153
20
        fn msg_counter(&self) -> u16 {
154
20
            self.msg_counter.get()
155
20
        }
156

            
157
        #[inline]
158
20
        fn dest_id(&self) -> u16 {
159
20
            self.dest_id.get()
160
20
        }
161
    }
162
}
163

            
164
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
165
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
166
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
167
pub struct PusTmSecondaryHeader<'stamp> {
168
    pus_version: PusVersion,
169
    pub sc_time_ref_status: u8,
170
    pub service: u8,
171
    pub subservice: u8,
172
    pub msg_counter: u16,
173
    pub dest_id: u16,
174
    pub timestamp: &'stamp [u8],
175
}
176

            
177
impl<'stamp> PusTmSecondaryHeader<'stamp> {
178
    #[inline]
179
46
    pub fn new_simple(service: u8, subservice: u8, timestamp: &'stamp [u8]) -> Self {
180
46
        Self::new(service, subservice, 0, 0, timestamp)
181
46
    }
182

            
183
    /// Like [Self::new_simple] but without a timestamp.
184
    #[inline]
185
2
    pub fn new_simple_no_timestamp(service: u8, subservice: u8) -> Self {
186
2
        Self::new(service, subservice, 0, 0, &[])
187
2
    }
188

            
189
    #[inline]
190
48
    pub fn new(
191
48
        service: u8,
192
48
        subservice: u8,
193
48
        msg_counter: u16,
194
48
        dest_id: u16,
195
48
        timestamp: &'stamp [u8],
196
48
    ) -> Self {
197
48
        PusTmSecondaryHeader {
198
48
            pus_version: PusVersion::PusC,
199
48
            sc_time_ref_status: 0,
200
48
            service,
201
48
            subservice,
202
48
            msg_counter,
203
48
            dest_id,
204
48
            timestamp,
205
48
        }
206
48
    }
207
}
208

            
209
impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
210
    #[inline]
211
24
    fn pus_version(&self) -> PusVersion {
212
24
        self.pus_version
213
24
    }
214

            
215
    #[inline]
216
10
    fn sc_time_ref_status(&self) -> u8 {
217
10
        self.sc_time_ref_status
218
10
    }
219

            
220
    #[inline]
221
16
    fn service(&self) -> u8 {
222
16
        self.service
223
16
    }
224

            
225
    #[inline]
226
16
    fn subservice(&self) -> u8 {
227
16
        self.subservice
228
16
    }
229

            
230
    #[inline]
231
12
    fn msg_counter(&self) -> u16 {
232
12
        self.msg_counter
233
12
    }
234

            
235
    #[inline]
236
12
    fn dest_id(&self) -> u16 {
237
12
        self.dest_id
238
12
    }
239
}
240

            
241
impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice> {
242
    type Error = ();
243

            
244
    #[inline]
245
16
    fn try_from(sec_header: zc::PusTmSecHeader<'slice>) -> Result<Self, Self::Error> {
246
16
        Ok(PusTmSecondaryHeader {
247
16
            pus_version: sec_header.zc_header.pus_version(),
248
16
            sc_time_ref_status: sec_header.zc_header.sc_time_ref_status(),
249
16
            service: sec_header.zc_header.service(),
250
16
            subservice: sec_header.zc_header.subservice(),
251
16
            msg_counter: sec_header.zc_header.msg_counter(),
252
16
            dest_id: sec_header.zc_header.dest_id(),
253
16
            timestamp: sec_header.timestamp,
254
16
        })
255
16
    }
256
}
257

            
258
/// This class models the PUS C telemetry packet. It is the primary data structure to generate the
259
/// raw byte representation of PUS telemetry.
260
///
261
/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the [serde]
262
/// feature is used which allows to send around TM packets in a raw byte format using a serde
263
/// provider like [postcard](https://docs.rs/postcard/latest/postcard/).
264
///
265
/// There is no spare bytes support yet.
266
///
267
/// # Lifetimes
268
///
269
/// * `'time` - This is the lifetime of the user provided timestamp.
270
/// * `'src_data` - This is the lifetime of the user provided source data.
271
#[derive(Eq, Debug, Copy, Clone)]
272
2
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
273
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
274
pub struct PusTmCreator<'time, 'src_data> {
275
    pub sp_header: SpHeader,
276
    #[cfg_attr(feature = "serde", serde(borrow))]
277
    pub sec_header: PusTmSecondaryHeader<'time>,
278
    source_data: &'src_data [u8],
279
    /// If this is set to false, a manual call to [Self::calc_own_crc16] or
280
    /// [Self::update_packet_fields] is necessary for the serialized or cached CRC16 to be valid.
281
    pub calc_crc_on_serialization: bool,
282
}
283

            
284
impl<'time, 'src_data> PusTmCreator<'time, 'src_data> {
285
    /// Generates a new struct instance.
286
    ///
287
    /// # Arguments
288
    ///
289
    /// * `sp_header` - Space packet header information. The correct packet type and the secondary
290
    ///     header flag are set correctly by the constructor.
291
    /// * `sec_header` - Information contained in the secondary header, including the service
292
    ///     and subservice type
293
    /// * `source_data` - Custom application data
294
    /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
295
    ///     field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set
296
    ///     the correct value to this field manually
297
    #[inline]
298
46
    pub fn new(
299
46
        mut sp_header: SpHeader,
300
46
        sec_header: PusTmSecondaryHeader<'time>,
301
46
        source_data: &'src_data [u8],
302
46
        set_ccsds_len: bool,
303
46
    ) -> Self {
304
46
        sp_header.set_packet_type(PacketType::Tm);
305
46
        sp_header.set_sec_header_flag();
306
46
        let mut pus_tm = Self {
307
46
            sp_header,
308
46
            source_data,
309
46
            sec_header,
310
46
            calc_crc_on_serialization: true,
311
46
        };
312
46
        if set_ccsds_len {
313
44
            pus_tm.update_ccsds_data_len();
314
44
        }
315
46
        pus_tm
316
46
    }
317

            
318
    #[inline]
319
6
    pub fn new_simple(
320
6
        sp_header: SpHeader,
321
6
        service: u8,
322
6
        subservice: u8,
323
6
        time_provider: &impl TimeWriter,
324
6
        stamp_buf: &'time mut [u8],
325
6
        source_data: &'src_data [u8],
326
6
        set_ccsds_len: bool,
327
6
    ) -> Result<Self, TimestampError> {
328
6
        let stamp_size = time_provider.write_to_bytes(stamp_buf)?;
329
6
        let sec_header =
330
6
            PusTmSecondaryHeader::new_simple(service, subservice, &stamp_buf[0..stamp_size]);
331
6
        Ok(Self::new(sp_header, sec_header, source_data, set_ccsds_len))
332
6
    }
333

            
334
    #[inline]
335
34
    pub fn new_no_source_data(
336
34
        sp_header: SpHeader,
337
34
        sec_header: PusTmSecondaryHeader<'time>,
338
34
        set_ccsds_len: bool,
339
34
    ) -> Self {
340
34
        Self::new(sp_header, sec_header, &[], set_ccsds_len)
341
34
    }
342

            
343
    #[inline]
344
4
    pub fn timestamp(&self) -> &[u8] {
345
4
        self.sec_header.timestamp
346
4
    }
347

            
348
    #[inline]
349
4
    pub fn source_data(&self) -> &[u8] {
350
4
        self.source_data
351
4
    }
352

            
353
    #[inline]
354
2
    pub fn set_dest_id(&mut self, dest_id: u16) {
355
2
        self.sec_header.dest_id = dest_id;
356
2
    }
357

            
358
    #[inline]
359
2
    pub fn set_msg_counter(&mut self, msg_counter: u16) {
360
2
        self.sec_header.msg_counter = msg_counter
361
2
    }
362

            
363
    #[inline]
364
2
    pub fn set_sc_time_ref_status(&mut self, sc_time_ref_status: u8) {
365
2
        self.sec_header.sc_time_ref_status = sc_time_ref_status & 0b1111;
366
2
    }
367

            
368
    sp_header_impls!();
369

            
370
    /// This is called automatically if the `set_ccsds_len` argument in the [Self::new] call was
371
    /// used.
372
    /// If this was not done or the time stamp or source data is set or changed after construction,
373
    /// this function needs to be called to ensure that the data length field of the CCSDS header
374
    /// is set correctly
375
    #[inline]
376
48
    pub fn update_ccsds_data_len(&mut self) {
377
48
        self.sp_header.data_len =
378
48
            self.len_written() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
379
48
    }
380

            
381
    /// This function should be called before the TM packet is serialized if
382
    /// [Self::calc_crc_on_serialization] is set to False. It will calculate and cache the CRC16.
383
8
    pub fn calc_own_crc16(&self) -> u16 {
384
8
        let mut digest = CRC_CCITT_FALSE.digest();
385
8
        let sph_zc = crate::zc::SpHeader::from(self.sp_header);
386
8
        digest.update(sph_zc.as_bytes());
387
8
        let pus_tc_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
388
8
        digest.update(pus_tc_header.as_bytes());
389
8
        digest.update(self.sec_header.timestamp);
390
8
        digest.update(self.source_data);
391
8
        digest.finalize()
392
8
    }
393

            
394
    /// This helper function calls both [Self::update_ccsds_data_len] and [Self::calc_own_crc16]
395
    #[inline]
396
2
    pub fn update_packet_fields(&mut self) {
397
2
        self.update_ccsds_data_len();
398
2
    }
399

            
400
    /// Write the raw PUS byte representation to a provided buffer.
401
30
    pub fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, ByteConversionError> {
402
30
        let mut curr_idx = 0;
403
30
        let total_size = self.len_written();
404
30
        if total_size > slice.len() {
405
2
            return Err(ByteConversionError::ToSliceTooSmall {
406
2
                found: slice.len(),
407
2
                expected: total_size,
408
2
            });
409
28
        }
410
28
        self.sp_header
411
28
            .write_to_be_bytes(&mut slice[0..CCSDS_HEADER_LEN])?;
412
28
        curr_idx += CCSDS_HEADER_LEN;
413
28
        let sec_header_len = size_of::<zc::PusTmSecHeaderWithoutTimestamp>();
414
28
        let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
415
28
        sec_header
416
28
            .write_to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len])
417
28
            .ok_or(ByteConversionError::ZeroCopyToError)?;
418
28
        curr_idx += sec_header_len;
419
28
        slice[curr_idx..curr_idx + self.sec_header.timestamp.len()]
420
28
            .copy_from_slice(self.sec_header.timestamp);
421
28
        curr_idx += self.sec_header.timestamp.len();
422
28
        slice[curr_idx..curr_idx + self.source_data.len()].copy_from_slice(self.source_data);
423
28
        curr_idx += self.source_data.len();
424
28
        let mut digest = CRC_CCITT_FALSE.digest();
425
28
        digest.update(&slice[0..curr_idx]);
426
28
        slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
427
28
        curr_idx += 2;
428
28
        Ok(curr_idx)
429
30
    }
430

            
431
    /// Append the raw PUS byte representation to a provided [alloc::vec::Vec]
432
    #[cfg(feature = "alloc")]
433
4
    pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
434
4
        let sph_zc = crate::zc::SpHeader::from(self.sp_header);
435
4
        let mut appended_len = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + self.sec_header.timestamp.len();
436
4
        appended_len += self.source_data.len();
437
4
        let start_idx = vec.len();
438
4
        vec.extend_from_slice(sph_zc.as_bytes());
439
4
        // The PUS version is hardcoded to PUS C
440
4
        let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
441
4
        vec.extend_from_slice(sec_header.as_bytes());
442
4
        vec.extend_from_slice(self.sec_header.timestamp);
443
4
        vec.extend_from_slice(self.source_data);
444
4
        let mut digest = CRC_CCITT_FALSE.digest();
445
4
        digest.update(&vec[start_idx..start_idx + appended_len - 2]);
446
4
        vec.extend_from_slice(&digest.finalize().to_be_bytes());
447
4
        Ok(appended_len)
448
4
    }
449
}
450

            
451
impl WritablePusPacket for PusTmCreator<'_, '_> {
452
    #[inline]
453
86
    fn len_written(&self) -> usize {
454
86
        PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA
455
86
            + self.sec_header.timestamp.len()
456
86
            + self.source_data.len()
457
86
    }
458
    /// Write the raw PUS byte representation to a provided buffer.
459
4
    fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
460
4
        Ok(Self::write_to_bytes(self, slice)?)
461
4
    }
462
}
463

            
464
impl PartialEq for PusTmCreator<'_, '_> {
465
4
    fn eq(&self, other: &Self) -> bool {
466
4
        self.sp_header == other.sp_header
467
4
            && self.sec_header == other.sec_header
468
4
            && self.source_data == other.source_data
469
4
    }
470
}
471

            
472
impl CcsdsPacket for PusTmCreator<'_, '_> {
473
    ccsds_impl!();
474
}
475

            
476
impl PusPacket for PusTmCreator<'_, '_> {
477
    delegate!(to self.sec_header {
478
        #[inline]
479
8
        fn pus_version(&self) -> PusVersion;
480
        #[inline]
481
4
        fn service(&self) -> u8;
482
        #[inline]
483
4
        fn subservice(&self) -> u8;
484
    });
485

            
486
    #[inline]
487
4
    fn user_data(&self) -> &[u8] {
488
4
        self.source_data
489
4
    }
490

            
491
    #[inline]
492
6
    fn crc16(&self) -> Option<u16> {
493
6
        Some(self.calc_own_crc16())
494
6
    }
495
}
496

            
497
impl GenericPusTmSecondaryHeader for PusTmCreator<'_, '_> {
498
    delegate!(to self.sec_header {
499
        #[inline]
500
4
        fn pus_version(&self) -> PusVersion;
501
        #[inline]
502
4
        fn service(&self) -> u8;
503
        #[inline]
504
4
        fn subservice(&self) -> u8;
505
        #[inline]
506
6
        fn dest_id(&self) -> u16;
507
        #[inline]
508
6
        fn msg_counter(&self) -> u16;
509
        #[inline]
510
6
        fn sc_time_ref_status(&self) -> u8;
511
    });
512
}
513

            
514
impl IsPusTelemetry for PusTmCreator<'_, '_> {}
515

            
516
/// This class models the PUS C telemetry packet. It is the primary data structure to read
517
/// a telemetry packet from raw bytes.
518
///
519
/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the [serde]
520
/// feature is used which allows to send around TM packets in a raw byte format using a serde
521
/// provider like [postcard](https://docs.rs/postcard/latest/postcard/).
522
///
523
/// There is no spare bytes support yet.
524
///
525
/// # Lifetimes
526
///
527
/// * `'raw_data` - Lifetime of the raw slice this class is constructed from.
528
#[derive(Eq, Debug, Copy, Clone)]
529
2
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
530
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
531
pub struct PusTmReader<'raw_data> {
532
    pub sp_header: SpHeader,
533
    pub sec_header: PusTmSecondaryHeader<'raw_data>,
534
    #[cfg_attr(feature = "serde", serde(skip))]
535
    raw_data: &'raw_data [u8],
536
    source_data: &'raw_data [u8],
537
    crc16: u16,
538
}
539

            
540
impl<'raw_data> PusTmReader<'raw_data> {
541
    /// Create a [PusTmReader] instance from a raw slice. On success, it returns a tuple containing
542
    /// the instance and the found byte length of the packet. The timestamp length needs to be
543
    /// known beforehand.
544
    ///
545
    /// This function will check the CRC-16 of the PUS packet and will return an appropriate
546
    /// [PusError] if the check fails.
547
20
    pub fn new(slice: &'raw_data [u8], timestamp_len: usize) -> Result<(Self, usize), PusError> {
548
20
        let raw_data_len = slice.len();
549
20
        if raw_data_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
550
2
            return Err(ByteConversionError::FromSliceTooSmall {
551
2
                found: raw_data_len,
552
2
                expected: PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA,
553
2
            }
554
2
            .into());
555
18
        }
556
18
        let mut current_idx = 0;
557
18
        let (sp_header, _) = SpHeader::from_be_bytes(&slice[0..CCSDS_HEADER_LEN])?;
558
18
        current_idx += 6;
559
18
        let total_len = sp_header.total_len();
560
18
        if raw_data_len < total_len {
561
2
            return Err(ByteConversionError::FromSliceTooSmall {
562
2
                found: raw_data_len,
563
2
                expected: total_len,
564
2
            }
565
2
            .into());
566
16
        }
567
16
        if total_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA {
568
            return Err(ByteConversionError::FromSliceTooSmall {
569
                found: total_len,
570
                expected: PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA,
571
            }
572
            .into());
573
16
        }
574
16
        let sec_header_zc = zc::PusTmSecHeaderWithoutTimestamp::from_bytes(
575
16
            &slice[current_idx..current_idx + PUS_TM_MIN_SEC_HEADER_LEN],
576
16
        )
577
16
        .ok_or(ByteConversionError::ZeroCopyFromError)?;
578
16
        current_idx += PUS_TM_MIN_SEC_HEADER_LEN;
579
16
        let zc_sec_header_wrapper = zc::PusTmSecHeader {
580
16
            zc_header: sec_header_zc,
581
16
            timestamp: &slice[current_idx..current_idx + timestamp_len],
582
16
        };
583
16
        current_idx += timestamp_len;
584
16
        let raw_data = &slice[0..total_len];
585
16
        let pus_tm = Self {
586
16
            sp_header,
587
16
            sec_header: PusTmSecondaryHeader::try_from(zc_sec_header_wrapper).unwrap(),
588
16
            raw_data: &slice[0..total_len],
589
16
            source_data: user_data_from_raw(current_idx, total_len, slice)?,
590
16
            crc16: crc_from_raw_data(raw_data)?,
591
        };
592
16
        verify_crc16_ccitt_false_from_raw_to_pus_error(raw_data, pus_tm.crc16)?;
593
14
        Ok((pus_tm, total_len))
594
20
    }
595

            
596
    #[inline]
597
4
    pub fn len_packed(&self) -> usize {
598
4
        self.sp_header.total_len()
599
4
    }
600

            
601
    #[inline]
602
2
    pub fn source_data(&self) -> &[u8] {
603
2
        self.user_data()
604
2
    }
605

            
606
    #[inline]
607
4
    pub fn timestamp(&self) -> &[u8] {
608
4
        self.sec_header.timestamp
609
4
    }
610

            
611
    /// This function will return the slice [Self] was constructed from.
612
    #[inline]
613
2
    pub fn raw_data(&self) -> &[u8] {
614
2
        self.raw_data
615
2
    }
616
}
617

            
618
impl PartialEq for PusTmReader<'_> {
619
4
    fn eq(&self, other: &Self) -> bool {
620
4
        self.sec_header == other.sec_header
621
4
            && self.source_data == other.source_data
622
4
            && self.sp_header == other.sp_header
623
4
            && self.crc16 == other.crc16
624
4
    }
625
}
626

            
627
impl CcsdsPacket for PusTmReader<'_> {
628
    ccsds_impl!();
629
}
630

            
631
impl PusPacket for PusTmReader<'_> {
632
    delegate!(to self.sec_header {
633
        #[inline]
634
8
        fn pus_version(&self) -> PusVersion;
635
        #[inline]
636
4
        fn service(&self) -> u8;
637
        #[inline]
638
4
        fn subservice(&self) -> u8;
639
    });
640

            
641
    #[inline]
642
4
    fn user_data(&self) -> &[u8] {
643
4
        self.source_data
644
4
    }
645

            
646
    #[inline]
647
2
    fn crc16(&self) -> Option<u16> {
648
2
        Some(self.crc16)
649
2
    }
650
}
651

            
652
impl GenericPusTmSecondaryHeader for PusTmReader<'_> {
653
    delegate!(to self.sec_header {
654
        #[inline]
655
4
        fn pus_version(&self) -> PusVersion;
656
        #[inline]
657
4
        fn service(&self) -> u8;
658
        #[inline]
659
4
        fn subservice(&self) -> u8;
660
        #[inline]
661
6
        fn dest_id(&self) -> u16;
662
        #[inline]
663
6
        fn msg_counter(&self) -> u16;
664
        #[inline]
665
4
        fn sc_time_ref_status(&self) -> u8;
666
    });
667
}
668

            
669
impl IsPusTelemetry for PusTmReader<'_> {}
670

            
671
impl PartialEq<PusTmCreator<'_, '_>> for PusTmReader<'_> {
672
    fn eq(&self, other: &PusTmCreator<'_, '_>) -> bool {
673
        self.sp_header == other.sp_header
674
            && self.sec_header == other.sec_header
675
            && self.source_data == other.source_data
676
    }
677
}
678

            
679
impl PartialEq<PusTmReader<'_>> for PusTmCreator<'_, '_> {
680
2
    fn eq(&self, other: &PusTmReader<'_>) -> bool {
681
2
        self.sp_header == other.sp_header
682
2
            && self.sec_header == other.sec_header
683
2
            && self.source_data == other.source_data
684
2
    }
685
}
686

            
687
/// This is a helper class to update certain fields in a raw PUS telemetry packet directly in place.
688
/// This can be more efficient than creating a full [PusTmReader], modifying the fields and then
689
/// writing it back to another buffer.
690
///
691
/// Please note that the [Self::finish] method has to be called for the PUS TM CRC16 to be valid
692
/// after changing fields of the TM packet. Furthermore, the constructor of this class will not
693
/// do any checks except basic length checks to ensure that all relevant fields can be updated and
694
/// all methods can be called without a panic. If a full validity check of the PUS TM packet is
695
/// required, it is recommended to construct a full [PusTmReader] object from the raw bytestream
696
/// first.
697
pub struct PusTmZeroCopyWriter<'raw> {
698
    raw_tm: &'raw mut [u8],
699
    timestamp_len: usize,
700
}
701

            
702
impl<'raw> PusTmZeroCopyWriter<'raw> {
703
    /// This function will not do any other checks on the raw data other than a length check
704
    /// for all internal fields which can be updated.
705
    ///
706
    /// It is the responsibility of the user to ensure the raw slice contains a valid telemetry
707
    /// packet.
708
6
    pub fn new(raw_tm: &'raw mut [u8], timestamp_len: usize) -> Option<Self> {
709
6
        let raw_tm_len = raw_tm.len();
710
6
        if raw_tm_len < CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + timestamp_len {
711
            return None;
712
6
        }
713
6
        let sp_header = crate::zc::SpHeader::from_bytes(&raw_tm[0..CCSDS_HEADER_LEN]).unwrap();
714
6
        if raw_tm_len < sp_header.total_len() {
715
            return None;
716
6
        }
717
6
        let writer = Self {
718
6
            raw_tm: &mut raw_tm[..sp_header.total_len()],
719
6
            timestamp_len,
720
6
        };
721
6
        Some(writer)
722
6
    }
723

            
724
    /// Set the sequence count. Returns false and does not update the value if the passed value
725
    /// exceeds [MAX_APID].
726
    #[inline]
727
10
    pub fn set_apid(&mut self, apid: u16) -> bool {
728
10
        if apid > MAX_APID {
729
4
            return false;
730
6
        }
731
6
        // Clear APID part of the raw packet ID
732
6
        let updated_apid =
733
6
            ((((self.raw_tm[0] as u16) << 8) | self.raw_tm[1] as u16) & !MAX_APID) | apid;
734
6
        self.raw_tm[0..2].copy_from_slice(&updated_apid.to_be_bytes());
735
6
        true
736
10
    }
737

            
738
    /// This function sets the message counter in the PUS TM secondary header.
739
    #[inline]
740
6
    pub fn set_msg_count(&mut self, msg_count: u16) {
741
6
        self.raw_tm[9..11].copy_from_slice(&msg_count.to_be_bytes());
742
6
    }
743

            
744
    /// This function sets the destination ID in the PUS TM secondary header.
745
    #[inline]
746
6
    pub fn set_destination_id(&mut self, dest_id: u16) {
747
6
        self.raw_tm[11..13].copy_from_slice(&dest_id.to_be_bytes())
748
6
    }
749

            
750
    /// Helper API to generate the space packet header portion of the PUS TM from the raw memory.
751
    #[inline]
752
10
    pub fn sp_header(&self) -> crate::zc::SpHeader {
753
10
        // Valid minimum length of packet was checked before.
754
10
        crate::zc::SpHeader::from_bytes(&self.raw_tm[0..CCSDS_HEADER_LEN]).unwrap()
755
10
    }
756

            
757
    /// Helper API to generate the portion of the secondary header without a timestamp from the
758
    /// raw memory.
759
    #[inline]
760
8
    pub fn sec_header_without_timestamp(&self) -> PusTmSecHeaderWithoutTimestamp {
761
8
        // Valid minimum length of packet was checked before.
762
8
        PusTmSecHeaderWithoutTimestamp::from_bytes(
763
8
            &self.raw_tm[CCSDS_HEADER_LEN..CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN],
764
8
        )
765
8
        .unwrap()
766
8
    }
767

            
768
    /// Set the sequence count. Returns false and does not update the value if the passed value
769
    /// exceeds [MAX_SEQ_COUNT].
770
    #[inline]
771
6
    pub fn set_seq_count(&mut self, seq_count: u16) -> bool {
772
6
        if seq_count > MAX_SEQ_COUNT {
773
            return false;
774
6
        }
775
6
        let new_psc =
776
6
            (u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000) | seq_count;
777
6
        self.raw_tm[2..4].copy_from_slice(&new_psc.to_be_bytes());
778
6
        true
779
6
    }
780

            
781
    /// This method has to be called after modifying fields to ensure the CRC16 of the telemetry
782
    /// packet remains valid.
783
4
    pub fn finish(self) {
784
4
        let slice_len = self.raw_tm.len();
785
4
        let crc16 = calc_pus_crc16(&self.raw_tm[..slice_len - 2]);
786
4
        self.raw_tm[slice_len - 2..].copy_from_slice(&crc16.to_be_bytes());
787
4
    }
788
}
789

            
790
impl CcsdsPacket for PusTmZeroCopyWriter<'_> {
791
    #[inline]
792
    fn ccsds_version(&self) -> u8 {
793
        self.sp_header().ccsds_version()
794
    }
795

            
796
    #[inline]
797
2
    fn packet_id(&self) -> crate::PacketId {
798
2
        self.sp_header().packet_id()
799
2
    }
800

            
801
    #[inline]
802
2
    fn psc(&self) -> crate::PacketSequenceCtrl {
803
2
        self.sp_header().psc()
804
2
    }
805

            
806
    #[inline]
807
    fn data_len(&self) -> u16 {
808
        self.sp_header().data_len()
809
    }
810
}
811

            
812
impl PusPacket for PusTmZeroCopyWriter<'_> {
813
    #[inline]
814
    fn pus_version(&self) -> PusVersion {
815
        self.sec_header_without_timestamp().pus_version()
816
    }
817

            
818
    #[inline]
819
4
    fn service(&self) -> u8 {
820
4
        self.raw_tm[7]
821
4
    }
822

            
823
    #[inline]
824
4
    fn subservice(&self) -> u8 {
825
4
        self.raw_tm[8]
826
4
    }
827

            
828
    #[inline]
829
2
    fn user_data(&self) -> &[u8] {
830
2
        &self.raw_tm[CCSDS_HEADER_LEN + PUS_TM_MIN_SEC_HEADER_LEN + self.timestamp_len
831
2
            ..self.sp_header().total_len() - 2]
832
2
    }
833

            
834
    #[inline]
835
2
    fn crc16(&self) -> Option<u16> {
836
2
        Some(u16::from_be_bytes(
837
2
            self.raw_tm[self.sp_header().total_len() - 2..self.sp_header().total_len()]
838
2
                .try_into()
839
2
                .unwrap(),
840
2
        ))
841
2
    }
842
}
843

            
844
impl GenericPusTmSecondaryHeader for PusTmZeroCopyWriter<'_> {
845
    delegate! {
846
        to self.sec_header_without_timestamp() {
847
            #[inline]
848
            fn pus_version(&self) -> PusVersion;
849
            #[inline]
850
            fn sc_time_ref_status(&self) -> u8;
851
            #[inline]
852
2
            fn msg_counter(&self) -> u16;
853
            #[inline]
854
2
            fn dest_id(&self) -> u16;
855
        }
856
    }
857

            
858
    #[inline]
859
    fn service(&self) -> u8 {
860
        PusPacket::service(self)
861
    }
862

            
863
    #[inline]
864
    fn subservice(&self) -> u8 {
865
        PusPacket::subservice(self)
866
    }
867
}
868

            
869
#[cfg(test)]
870
mod tests {
871
    use alloc::string::ToString;
872

            
873
    use super::*;
874
    use crate::ecss::PusVersion::PusC;
875
    use crate::time::cds::CdsTime;
876
    #[cfg(feature = "serde")]
877
    use crate::time::CcsdsTimeProvider;
878
    use crate::SpHeader;
879
    #[cfg(feature = "serde")]
880
    use postcard::{from_bytes, to_allocvec};
881

            
882
    const DUMMY_DATA: &[u8] = &[0, 1, 2];
883

            
884
32
    fn base_ping_reply_full_ctor(timestamp: &[u8]) -> PusTmCreator {
885
32
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
886
32
        let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
887
32
        PusTmCreator::new_no_source_data(sph, tm_header, true)
888
32
    }
889
2
    fn ping_reply_with_data(timestamp: &[u8]) -> PusTmCreator {
890
2
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
891
2
        let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp);
892
2
        PusTmCreator::new(sph, tm_header, DUMMY_DATA, true)
893
2
    }
894

            
895
4
    fn base_hk_reply<'a, 'b>(timestamp: &'a [u8], src_data: &'b [u8]) -> PusTmCreator<'a, 'b> {
896
4
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
897
4
        let tc_header = PusTmSecondaryHeader::new_simple(3, 5, timestamp);
898
4
        PusTmCreator::new(sph, tc_header, src_data, true)
899
4
    }
900

            
901
48
    fn dummy_timestamp() -> &'static [u8] {
902
48
        &[0, 1, 2, 3, 4, 5, 6]
903
48
    }
904

            
905
    #[test]
906
2
    fn test_basic() {
907
2
        let timestamp = dummy_timestamp();
908
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
909
2
        verify_ping_reply(&pus_tm, false, 22, dummy_timestamp());
910
2
    }
911
    #[test]
912
2
    fn test_basic_simple_api() {
913
2
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
914
2
        let time_provider = CdsTime::new_with_u16_days(0, 0);
915
2
        let mut stamp_buf: [u8; 8] = [0; 8];
916
2
        let pus_tm =
917
2
            PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
918
2
                .unwrap();
919
2
        verify_ping_reply(&pus_tm, false, 22, &[64, 0, 0, 0, 0, 0, 0]);
920
2
    }
921

            
922
    #[test]
923
2
    fn test_serialization_no_source_data() {
924
2
        let timestamp = dummy_timestamp();
925
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
926
2
        let mut buf: [u8; 32] = [0; 32];
927
2
        let ser_len = pus_tm
928
2
            .write_to_bytes(&mut buf)
929
2
            .expect("Serialization failed");
930
2
        assert_eq!(ser_len, 22);
931
2
        verify_raw_ping_reply(pus_tm.crc16().unwrap(), &buf);
932
2
    }
933

            
934
    #[test]
935
2
    fn test_serialization_with_source_data() {
936
2
        let src_data = [1, 2, 3];
937
2
        let hk_reply = base_hk_reply(dummy_timestamp(), &src_data);
938
2
        let mut buf: [u8; 32] = [0; 32];
939
2
        let ser_len = hk_reply
940
2
            .write_to_bytes(&mut buf)
941
2
            .expect("Serialization failed");
942
2
        assert_eq!(ser_len, 25);
943
2
        assert_eq!(buf[20], 1);
944
2
        assert_eq!(buf[21], 2);
945
2
        assert_eq!(buf[22], 3);
946
2
    }
947

            
948
    #[test]
949
2
    fn test_setters() {
950
2
        let timestamp = dummy_timestamp();
951
2
        let mut pus_tm = base_ping_reply_full_ctor(timestamp);
952
2
        pus_tm.set_sc_time_ref_status(0b1010);
953
2
        pus_tm.set_dest_id(0x7fff);
954
2
        pus_tm.set_msg_counter(0x1f1f);
955
2
        assert_eq!(pus_tm.sc_time_ref_status(), 0b1010);
956
2
        assert_eq!(pus_tm.dest_id(), 0x7fff);
957
2
        assert_eq!(pus_tm.msg_counter(), 0x1f1f);
958
2
        assert!(pus_tm.set_apid(0x7ff));
959
2
        assert_eq!(pus_tm.apid(), 0x7ff);
960
2
    }
961

            
962
    #[test]
963
2
    fn test_write_into_vec() {
964
2
        let timestamp = dummy_timestamp();
965
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
966
2
        let tm_vec = pus_tm.to_vec().expect("Serialization failed");
967
2
        assert_eq!(tm_vec.len(), 22);
968
2
        let (tm_deserialized, size) =
969
2
            PusTmReader::new(tm_vec.as_slice(), 7).expect("Deserialization failed");
970
2
        assert_eq!(tm_vec.len(), size);
971
2
        verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp());
972
2
    }
973
    #[test]
974
2
    fn test_deserialization_no_source_data() {
975
2
        let timestamp = dummy_timestamp();
976
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
977
2
        let mut buf: [u8; 32] = [0; 32];
978
2
        let ser_len = pus_tm
979
2
            .write_to_bytes(&mut buf)
980
2
            .expect("Serialization failed");
981
2
        assert_eq!(ser_len, 22);
982
2
        let (tm_deserialized, size) = PusTmReader::new(&buf, 7).expect("Deserialization failed");
983
2
        assert_eq!(ser_len, size);
984
2
        assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data());
985
2
        assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]);
986
2
        assert_eq!(tm_deserialized.crc16().unwrap(), pus_tm.crc16().unwrap());
987
2
        verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp());
988
2
    }
989
    #[test]
990
2
    fn test_deserialization_faulty_crc() {
991
2
        let timestamp = dummy_timestamp();
992
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
993
2
        let mut buf: [u8; 32] = [0; 32];
994
2
        let ser_len = pus_tm
995
2
            .write_to_bytes(&mut buf)
996
2
            .expect("Serialization failed");
997
2
        assert_eq!(ser_len, 22);
998
2
        buf[ser_len - 2] = 0;
999
2
        buf[ser_len - 1] = 0;
2
        let tm_error = PusTmReader::new(&buf, 7);
2
        assert!(tm_error.is_err());
2
        let tm_error = tm_error.unwrap_err();
2
        if let PusError::ChecksumFailure(crc) = tm_error {
2
            assert_eq!(crc, 0);
2
            assert_eq!(
2
                tm_error.to_string(),
2
                "checksum verification for crc16 0x0000 failed"
2
            );
        }
2
    }
    #[test]
2
    fn test_manual_field_update() {
2
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
2
        let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_timestamp());
2
        let mut tm = PusTmCreator::new_no_source_data(sph, tc_header, false);
2
        tm.calc_crc_on_serialization = false;
2
        assert_eq!(tm.data_len(), 0x00);
2
        let mut buf: [u8; 32] = [0; 32];
2
        tm.update_ccsds_data_len();
2
        assert_eq!(tm.data_len(), 15);
2
        tm.calc_own_crc16();
2
        let res = tm.write_to_bytes(&mut buf);
2
        assert!(res.is_ok());
2
        tm.sp_header.data_len = 0;
2
        tm.update_packet_fields();
2
        assert_eq!(tm.data_len(), 15);
2
    }
    #[test]
2
    fn test_target_buf_too_small() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
2
        let mut buf: [u8; 16] = [0; 16];
2
        let res = pus_tm.write_to_bytes(&mut buf);
2
        assert!(res.is_err());
2
        let error = res.unwrap_err();
2
        if let ByteConversionError::ToSliceTooSmall { found, expected } = error {
2
            assert_eq!(expected, 22);
2
            assert_eq!(found, 16);
        } else {
            panic!("Invalid error {:?}", error);
        }
2
    }
    #[test]
    #[cfg(feature = "alloc")]
2
    fn test_append_to_vec() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
2
        let mut vec = Vec::new();
2
        let res = pus_tm.append_to_vec(&mut vec);
2
        assert!(res.is_ok());
2
        assert_eq!(res.unwrap(), 22);
2
        verify_raw_ping_reply(pus_tm.crc16().unwrap(), vec.as_slice());
2
    }
    #[test]
    #[cfg(feature = "alloc")]
2
    fn test_append_to_vec_with_src_data() {
2
        let src_data = [1, 2, 3];
2
        let hk_reply = base_hk_reply(dummy_timestamp(), &src_data);
2
        let mut vec = Vec::new();
2
        vec.push(4);
2
        let res = hk_reply.append_to_vec(&mut vec);
2
        assert!(res.is_ok());
2
        assert_eq!(res.unwrap(), 25);
2
        assert_eq!(vec.len(), 26);
2
    }
4
    fn verify_raw_ping_reply(crc16: u16, buf: &[u8]) {
4
        // Secondary header is set -> 0b0000_1001 , APID occupies last bit of first byte
4
        assert_eq!(buf[0], 0x09);
        // Rest of APID 0x123
4
        assert_eq!(buf[1], 0x23);
        // Unsegmented is the default, and first byte of 0x234 occupies this byte as well
4
        assert_eq!(buf[2], 0xc2);
4
        assert_eq!(buf[3], 0x34);
4
        assert_eq!(((buf[4] as u16) << 8) | buf[5] as u16, 15);
        // SC time ref status is 0
4
        assert_eq!(buf[6], (PusC as u8) << 4);
4
        assert_eq!(buf[7], 17);
4
        assert_eq!(buf[8], 2);
        // MSG counter 0
4
        assert_eq!(buf[9], 0x00);
4
        assert_eq!(buf[10], 0x00);
        // Destination ID
4
        assert_eq!(buf[11], 0x00);
4
        assert_eq!(buf[12], 0x00);
        // Timestamp
4
        assert_eq!(&buf[13..20], dummy_timestamp());
4
        let mut digest = CRC_CCITT_FALSE.digest();
4
        digest.update(&buf[0..20]);
4
        let crc16_calced = digest.finalize();
4
        assert_eq!(((crc16 >> 8) & 0xff) as u8, buf[20]);
4
        assert_eq!((crc16 & 0xff) as u8, buf[21]);
4
        assert_eq!(crc16, crc16_calced);
4
    }
4
    fn verify_ping_reply(
4
        tm: &PusTmCreator,
4
        has_user_data: bool,
4
        exp_full_len: usize,
4
        exp_timestamp: &[u8],
4
    ) {
4
        assert_eq!(tm.len_written(), exp_full_len);
4
        assert_eq!(tm.timestamp(), exp_timestamp);
4
        assert_eq!(tm.source_data(), tm.user_data());
4
        verify_ping_reply_generic(tm, has_user_data, exp_full_len);
4
    }
4
    fn verify_ping_reply_with_reader(
4
        tm: &PusTmReader,
4
        has_user_data: bool,
4
        exp_full_len: usize,
4
        exp_timestamp: &[u8],
4
    ) {
4
        assert_eq!(tm.len_packed(), exp_full_len);
4
        assert_eq!(tm.timestamp(), exp_timestamp);
4
        verify_ping_reply_generic(tm, has_user_data, exp_full_len);
4
    }
8
    fn verify_ping_reply_generic(
8
        tm: &(impl CcsdsPacket + GenericPusTmSecondaryHeader + PusPacket),
8
        has_user_data: bool,
8
        exp_full_len: usize,
8
    ) {
8
        assert!(tm.is_tm());
8
        assert_eq!(PusPacket::service(tm), 17);
8
        assert_eq!(GenericPusTmSecondaryHeader::service(tm), 17);
8
        assert_eq!(PusPacket::subservice(tm), 2);
8
        assert_eq!(GenericPusTmSecondaryHeader::subservice(tm), 2);
8
        assert!(tm.sec_header_flag());
8
        if has_user_data {
            assert!(!tm.user_data().is_empty());
8
        }
8
        assert_eq!(PusPacket::pus_version(tm), PusC);
8
        assert_eq!(tm.apid(), 0x123);
8
        assert_eq!(tm.seq_count(), 0x234);
8
        assert_eq!(PusPacket::pus_version(tm), PusVersion::PusC);
8
        assert_eq!(
8
            GenericPusTmSecondaryHeader::pus_version(tm),
8
            PusVersion::PusC
8
        );
8
        assert_eq!(tm.data_len(), exp_full_len as u16 - 7);
8
        assert_eq!(tm.dest_id(), 0x0000);
8
        assert_eq!(tm.msg_counter(), 0x0000);
8
        assert_eq!(tm.sc_time_ref_status(), 0b0000);
8
    }
    #[test]
2
    fn partial_eq_pus_tm() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm_1 = base_ping_reply_full_ctor(timestamp);
2
        let pus_tm_2 = base_ping_reply_full_ctor(timestamp);
2
        assert_eq!(pus_tm_1, pus_tm_2);
2
    }
    #[test]
2
    fn partial_eq_serialized_vs_derialized() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
2
        let mut buf = [0; 32];
2
        pus_tm.write_to_bytes(&mut buf).unwrap();
2
        assert_eq!(pus_tm, PusTmReader::new(&buf, timestamp.len()).unwrap().0);
2
    }
    #[test]
2
    fn test_zero_copy_writer() {
2
        let ping_tm = base_ping_reply_full_ctor(dummy_timestamp());
2
        let mut buf: [u8; 64] = [0; 64];
2
        let tm_size = ping_tm
2
            .write_to_bytes(&mut buf)
2
            .expect("writing PUS ping TM failed");
2
        let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size], 7)
2
            .expect("Creating zero copy writer failed");
2
        writer.set_destination_id(55);
2
        writer.set_msg_count(100);
2
        writer.set_seq_count(MAX_SEQ_COUNT);
2
        writer.set_apid(MAX_APID);
2
        assert!(!writer.set_apid(MAX_APID + 1));
2
        assert!(!writer.set_apid(MAX_SEQ_COUNT + 1));
2
        writer.finish();
2
        // This performs all necessary checks, including the CRC check.
2
        let (tm_read_back, tm_size_read_back) =
2
            PusTmReader::new(&buf, 7).expect("Re-creating PUS TM failed");
2
        assert_eq!(tm_size_read_back, tm_size);
2
        assert_eq!(tm_read_back.msg_counter(), 100);
2
        assert_eq!(tm_read_back.dest_id(), 55);
2
        assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT);
2
        assert_eq!(tm_read_back.apid(), MAX_APID);
2
    }
    #[test]
2
    fn test_zero_copy_writer_ccsds_api() {
2
        let ping_tm = base_ping_reply_full_ctor(dummy_timestamp());
2
        let mut buf: [u8; 64] = [0; 64];
2
        let tm_size = ping_tm
2
            .write_to_bytes(&mut buf)
2
            .expect("writing PUS ping TM failed");
2
        let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size], 7)
2
            .expect("Creating zero copy writer failed");
2
        writer.set_destination_id(55);
2
        writer.set_msg_count(100);
2
        writer.set_seq_count(MAX_SEQ_COUNT);
2
        writer.set_apid(MAX_APID);
2
        assert_eq!(PusPacket::service(&writer), 17);
2
        assert_eq!(PusPacket::subservice(&writer), 2);
2
        assert_eq!(writer.apid(), MAX_APID);
2
        assert_eq!(writer.seq_count(), MAX_SEQ_COUNT);
2
    }
    #[test]
2
    fn test_zero_copy_pus_api() {
2
        let ping_tm = ping_reply_with_data(dummy_timestamp());
2
        let mut buf: [u8; 64] = [0; 64];
2
        let tm_size = ping_tm
2
            .write_to_bytes(&mut buf)
2
            .expect("writing PUS ping TM failed");
2
        let crc16_raw = u16::from_be_bytes(buf[tm_size - 2..tm_size].try_into().unwrap());
2
        let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size], 7)
2
            .expect("Creating zero copy writer failed");
2
        writer.set_destination_id(55);
2
        writer.set_msg_count(100);
2
        writer.set_seq_count(MAX_SEQ_COUNT);
2
        writer.set_apid(MAX_APID);
2
        assert_eq!(PusPacket::service(&writer), 17);
2
        assert_eq!(PusPacket::subservice(&writer), 2);
2
        assert_eq!(writer.dest_id(), 55);
2
        assert_eq!(writer.msg_counter(), 100);
2
        assert_eq!(writer.sec_header_without_timestamp().dest_id(), 55);
2
        assert_eq!(writer.sec_header_without_timestamp().msg_counter(), 100);
2
        assert_eq!(writer.user_data(), DUMMY_DATA);
        // Need to check crc16 before finish, because finish will update the CRC.
2
        let crc16 = writer.crc16();
2
        assert!(crc16.is_some());
2
        assert_eq!(crc16.unwrap(), crc16_raw);
2
        writer.finish();
2
    }
    #[test]
2
    fn test_sec_header_without_stamp() {
2
        let sec_header = PusTmSecondaryHeader::new_simple_no_timestamp(17, 1);
2
        assert_eq!(sec_header.timestamp, &[]);
2
    }
    #[test]
2
    fn test_reader_partial_eq() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
2
        let mut buf = [0; 32];
2
        pus_tm.write_to_bytes(&mut buf).unwrap();
2
        let (tm_0, _) = PusTmReader::new(&buf, timestamp.len()).unwrap();
2
        let (tm_1, _) = PusTmReader::new(&buf, timestamp.len()).unwrap();
2
        assert_eq!(tm_0, tm_1);
2
    }
    #[test]
2
    fn test_reader_buf_too_small_2() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
2
        let mut buf = [0; 32];
2
        let written = pus_tm.write_to_bytes(&mut buf).unwrap();
2
        let tm_error = PusTmReader::new(
2
            &buf[0..PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + 1],
2
            timestamp.len(),
2
        );
2
        assert!(tm_error.is_err());
2
        let tm_error = tm_error.unwrap_err();
        if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall {
2
            found,
2
            expected,
2
        }) = tm_error
        {
2
            assert_eq!(found, PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + 1);
2
            assert_eq!(expected, written);
        } else {
            panic!("unexpected error {tm_error}")
        }
2
    }
    #[test]
2
    fn test_reader_buf_too_small() {
2
        let timestamp = dummy_timestamp();
2
        let pus_tm = base_ping_reply_full_ctor(timestamp);
2
        let mut buf = [0; 32];
2
        pus_tm.write_to_bytes(&mut buf).unwrap();
2
        let tm_error = PusTmReader::new(&buf[0..5], timestamp.len());
2
        assert!(tm_error.is_err());
2
        let tm_error = tm_error.unwrap_err();
        if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall {
2
            found,
2
            expected,
2
        }) = tm_error
        {
2
            assert_eq!(found, 5);
2
            assert_eq!(expected, PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA);
        } else {
            panic!("unexpected error {tm_error}")
        }
2
    }
    #[test]
    #[cfg(feature = "serde")]
2
    fn test_serialization_creator_serde() {
2
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
2
        let time_provider = CdsTime::new_with_u16_days(0, 0);
2
        let mut stamp_buf: [u8; 8] = [0; 8];
2
        let pus_tm =
2
            PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
2
                .unwrap();
2

            
2
        let output = to_allocvec(&pus_tm).unwrap();
2
        let output_converted_back: PusTmCreator = from_bytes(&output).unwrap();
2
        assert_eq!(output_converted_back, pus_tm);
2
    }
    #[test]
    #[cfg(feature = "serde")]
2
    fn test_serialization_reader_serde() {
2
        let sph = SpHeader::new_for_unseg_tm_checked(0x123, 0x234, 0).unwrap();
2
        let time_provider = CdsTime::new_with_u16_days(0, 0);
2
        let mut stamp_buf: [u8; 8] = [0; 8];
2
        let pus_tm =
2
            PusTmCreator::new_simple(sph, 17, 2, &time_provider, &mut stamp_buf, &[], true)
2
                .unwrap();
2
        let pus_tm_vec = pus_tm.to_vec().unwrap();
2
        let (tm_reader, _) = PusTmReader::new(&pus_tm_vec, time_provider.len_as_bytes()).unwrap();
2
        let output = to_allocvec(&tm_reader).unwrap();
2
        let output_converted_back: PusTmReader = from_bytes(&output).unwrap();
2
        assert_eq!(output_converted_back, tm_reader);
2
    }
}