1
//! This module contains all components required to create a ECSS PUS C telecommand 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::{CcsdsPacket, SpHeader};
8
//! use spacepackets::ecss::{PusPacket, WritablePusPacket};
9
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
10
//!
11
//! // Create a ping telecommand with no user application data
12
//! let pus_tc = PusTcCreator::new_no_app_data(
13
//!     SpHeader::new_from_apid(0x02),
14
//!     PusTcSecondaryHeader::new_simple(17, 1),
15
//!     true
16
//! );
17
//! println!("{:?}", pus_tc);
18
//! assert_eq!(pus_tc.service(), 17);
19
//! assert_eq!(pus_tc.subservice(), 1);
20
//! assert_eq!(pus_tc.apid(), 0x02);
21
//!
22
//! // Serialize TC into a raw buffer
23
//! let mut test_buf: [u8; 32] = [0; 32];
24
//! let size = pus_tc
25
//!     .write_to_bytes(test_buf.as_mut_slice())
26
//!     .expect("Error writing TC to buffer");
27
//! assert_eq!(size, 13);
28
//! println!("{:?}", &test_buf[0..size]);
29
//!
30
//! // Deserialize from the raw byte representation
31
//! let pus_tc_deserialized = PusTcReader::new(&test_buf).expect("Deserialization failed");
32
//! assert_eq!(pus_tc.service(), 17);
33
//! assert_eq!(pus_tc.subservice(), 1);
34
//! assert_eq!(pus_tc.apid(), 0x02);
35
//! ```
36
use crate::ecss::{
37
    ccsds_impl, crc_from_raw_data, sp_header_impls, user_data_from_raw,
38
    verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion,
39
    WritablePusPacket,
40
};
41
use crate::{ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, CCSDS_HEADER_LEN};
42
use crate::{SpHeader, CRC_CCITT_FALSE};
43
use core::mem::size_of;
44
use delegate::delegate;
45
use num_enum::{IntoPrimitive, TryFromPrimitive};
46
#[cfg(feature = "serde")]
47
use serde::{Deserialize, Serialize};
48
use zerocopy::AsBytes;
49

            
50
#[cfg(feature = "alloc")]
51
use alloc::vec::Vec;
52

            
53
/// PUS C secondary header length is fixed
54
pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>();
55
pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize =
56
    CCSDS_HEADER_LEN + PUC_TC_SECONDARY_HEADER_LEN + size_of::<CrcType>();
57
const PUS_VERSION: PusVersion = PusVersion::PusC;
58

            
59
/// Marker trait for PUS telecommand structures.
60
pub trait IsPusTelecommand {}
61

            
62
2
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
63
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
64
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65
#[repr(u8)]
66
enum AckOpts {
67
    Acceptance = 0b1000,
68
    Start = 0b0100,
69
    Progress = 0b0010,
70
    Completion = 0b0001,
71
}
72

            
73
pub const ACK_ALL: u8 = AckOpts::Acceptance as u8
74
    | AckOpts::Start as u8
75
    | AckOpts::Progress as u8
76
    | AckOpts::Completion as u8;
77

            
78
pub trait GenericPusTcSecondaryHeader {
79
    fn pus_version(&self) -> PusVersion;
80
    fn ack_flags(&self) -> u8;
81
    fn service(&self) -> u8;
82
    fn subservice(&self) -> u8;
83
    fn source_id(&self) -> u16;
84
}
85

            
86
pub mod zc {
87
    use crate::ecss::tc::GenericPusTcSecondaryHeader;
88
    use crate::ecss::{PusError, PusVersion};
89
    use zerocopy::{AsBytes, FromBytes, FromZeroes, NetworkEndian, Unaligned, U16};
90

            
91
    #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
92
    #[repr(C)]
93
    pub struct PusTcSecondaryHeader {
94
        version_ack: u8,
95
        service: u8,
96
        subservice: u8,
97
        source_id: U16<NetworkEndian>,
98
    }
99

            
100
    impl TryFrom<crate::ecss::tc::PusTcSecondaryHeader> for PusTcSecondaryHeader {
101
        type Error = PusError;
102
32
        fn try_from(value: crate::ecss::tc::PusTcSecondaryHeader) -> Result<Self, Self::Error> {
103
32
            if value.version != PusVersion::PusC {
104
                return Err(PusError::VersionNotSupported(value.version));
105
32
            }
106
32
            Ok(PusTcSecondaryHeader {
107
32
                version_ack: ((value.version as u8) << 4) | value.ack,
108
32
                service: value.service,
109
32
                subservice: value.subservice,
110
32
                source_id: U16::from(value.source_id),
111
32
            })
112
32
        }
113
    }
114

            
115
    impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
116
        #[inline]
117
        fn pus_version(&self) -> PusVersion {
118
            PusVersion::try_from(self.version_ack >> 4 & 0b1111).unwrap_or(PusVersion::Invalid)
119
        }
120

            
121
        #[inline]
122
16
        fn ack_flags(&self) -> u8 {
123
16
            self.version_ack & 0b1111
124
16
        }
125

            
126
        #[inline]
127
16
        fn service(&self) -> u8 {
128
16
            self.service
129
16
        }
130

            
131
        #[inline]
132
16
        fn subservice(&self) -> u8 {
133
16
            self.subservice
134
16
        }
135

            
136
        #[inline]
137
16
        fn source_id(&self) -> u16 {
138
16
            self.source_id.get()
139
16
        }
140
    }
141

            
142
    impl PusTcSecondaryHeader {
143
22
        pub fn write_to_bytes(&self, slice: &mut [u8]) -> Option<()> {
144
22
            self.write_to(slice)
145
22
        }
146

            
147
16
        pub fn from_bytes(slice: &[u8]) -> Option<Self> {
148
16
            Self::read_from(slice)
149
16
        }
150
    }
151
}
152

            
153
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
154
2
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
155
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
156
pub struct PusTcSecondaryHeader {
157
    pub service: u8,
158
    pub subservice: u8,
159
    pub source_id: u16,
160
    pub ack: u8,
161
    pub version: PusVersion,
162
}
163

            
164
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
165
    #[inline]
166
36
    fn pus_version(&self) -> PusVersion {
167
36
        self.version
168
36
    }
169

            
170
    #[inline]
171
14
    fn ack_flags(&self) -> u8 {
172
14
        self.ack
173
14
    }
174

            
175
    #[inline]
176
24
    fn service(&self) -> u8 {
177
24
        self.service
178
24
    }
179

            
180
    #[inline]
181
24
    fn subservice(&self) -> u8 {
182
24
        self.subservice
183
24
    }
184

            
185
    #[inline]
186
14
    fn source_id(&self) -> u16 {
187
14
        self.source_id
188
14
    }
189
}
190

            
191
impl TryFrom<zc::PusTcSecondaryHeader> for PusTcSecondaryHeader {
192
    type Error = ();
193

            
194
16
    fn try_from(value: zc::PusTcSecondaryHeader) -> Result<Self, Self::Error> {
195
16
        Ok(PusTcSecondaryHeader {
196
16
            service: value.service(),
197
16
            subservice: value.subservice(),
198
16
            source_id: value.source_id(),
199
16
            ack: value.ack_flags(),
200
16
            version: PUS_VERSION,
201
16
        })
202
16
    }
203
}
204

            
205
impl PusTcSecondaryHeader {
206
    #[inline]
207
4
    pub fn new_simple(service: u8, subservice: u8) -> Self {
208
4
        PusTcSecondaryHeader {
209
4
            service,
210
4
            subservice,
211
4
            ack: ACK_ALL,
212
4
            source_id: 0,
213
4
            version: PusVersion::PusC,
214
4
        }
215
4
    }
216

            
217
    #[inline]
218
34
    pub fn new(service: u8, subservice: u8, ack: u8, source_id: u16) -> Self {
219
34
        PusTcSecondaryHeader {
220
34
            service,
221
34
            subservice,
222
34
            ack: ack & 0b1111,
223
34
            source_id,
224
34
            version: PusVersion::PusC,
225
34
        }
226
34
    }
227
}
228

            
229
/// This class can be used to create PUS C telecommand packet. It is the primary data structure to
230
/// generate the raw byte representation of a PUS telecommand.
231
///
232
/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the
233
/// [serde] feature is used, which allows to send around TC packets in a raw byte format using a
234
/// serde provider like [postcard](https://docs.rs/postcard/latest/postcard/).
235
///
236
/// There is no spare bytes support yet.
237
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
238
2
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
240
pub struct PusTcCreator<'app_data> {
241
    sp_header: SpHeader,
242
    pub sec_header: PusTcSecondaryHeader,
243
    app_data: &'app_data [u8],
244
}
245

            
246
impl<'app_data> PusTcCreator<'app_data> {
247
    /// Generates a new struct instance.
248
    ///
249
    /// # Arguments
250
    ///
251
    /// * `sp_header` - Space packet header information. The correct packet type and the secondary
252
    ///     header flag are set correctly by the constructor.
253
    /// * `sec_header` - Information contained in the data field header, including the service
254
    ///     and subservice type
255
    /// * `app_data` - Custom application data
256
    /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
257
    ///     field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set
258
    ///     the correct value to this field manually
259
    #[inline]
260
38
    pub fn new(
261
38
        mut sp_header: SpHeader,
262
38
        sec_header: PusTcSecondaryHeader,
263
38
        app_data: &'app_data [u8],
264
38
        set_ccsds_len: bool,
265
38
    ) -> Self {
266
38
        sp_header.set_packet_type(PacketType::Tc);
267
38
        sp_header.set_sec_header_flag();
268
38
        let mut pus_tc = Self {
269
38
            sp_header,
270
38
            app_data,
271
38
            sec_header,
272
38
        };
273
38
        if set_ccsds_len {
274
36
            pus_tc.update_ccsds_data_len();
275
36
        }
276
38
        pus_tc
277
38
    }
278

            
279
    /// Simplified version of the [Self::new] function which allows to only specify service
280
    /// and subservice instead of the full PUS TC secondary header.
281
    #[inline]
282
34
    pub fn new_simple(
283
34
        sph: SpHeader,
284
34
        service: u8,
285
34
        subservice: u8,
286
34
        app_data: &'app_data [u8],
287
34
        set_ccsds_len: bool,
288
34
    ) -> Self {
289
34
        Self::new(
290
34
            sph,
291
34
            PusTcSecondaryHeader::new(service, subservice, ACK_ALL, 0),
292
34
            app_data,
293
34
            set_ccsds_len,
294
34
        )
295
34
    }
296

            
297
    #[inline]
298
4
    pub fn new_no_app_data(
299
4
        sp_header: SpHeader,
300
4
        sec_header: PusTcSecondaryHeader,
301
4
        set_ccsds_len: bool,
302
4
    ) -> Self {
303
4
        Self::new(sp_header, sec_header, &[], set_ccsds_len)
304
4
    }
305

            
306
    #[inline]
307
6
    pub fn sp_header(&self) -> &SpHeader {
308
6
        &self.sp_header
309
6
    }
310

            
311
    #[inline]
312
    pub fn sp_header_mut(&mut self) -> &mut SpHeader {
313
        &mut self.sp_header
314
    }
315

            
316
    #[inline]
317
2
    pub fn set_ack_field(&mut self, ack: u8) -> bool {
318
2
        if ack > 0b1111 {
319
            return false;
320
2
        }
321
2
        self.sec_header.ack = ack & 0b1111;
322
2
        true
323
2
    }
324

            
325
    #[inline]
326
2
    pub fn set_source_id(&mut self, source_id: u16) {
327
2
        self.sec_header.source_id = source_id;
328
2
    }
329

            
330
    sp_header_impls!();
331

            
332
    /// Calculate the CCSDS space packet data length field and sets it
333
    /// This is called automatically if the `set_ccsds_len` argument in the [Self::new] call was
334
    /// used.
335
    /// If this was not done or the application data is set or changed after construction,
336
    /// this function needs to be called to ensure that the data length field of the CCSDS header
337
    /// is set correctly.
338
    #[inline]
339
38
    pub fn update_ccsds_data_len(&mut self) {
340
38
        self.sp_header.data_len =
341
38
            self.len_written() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
342
38
    }
343

            
344
    /// This function calculates and returns the CRC16 for the current packet.
345
6
    pub fn calc_own_crc16(&self) -> u16 {
346
6
        let mut digest = CRC_CCITT_FALSE.digest();
347
6
        let sph_zc = crate::zc::SpHeader::from(self.sp_header);
348
6
        digest.update(sph_zc.as_bytes());
349
6
        let pus_tc_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
350
6
        digest.update(pus_tc_header.as_bytes());
351
6
        digest.update(self.app_data);
352
6
        digest.finalize()
353
6
    }
354

            
355
    #[cfg(feature = "alloc")]
356
4
    pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize {
357
4
        let sph_zc = crate::zc::SpHeader::from(self.sp_header);
358
4
        let mut appended_len = PUS_TC_MIN_LEN_WITHOUT_APP_DATA;
359
4
        appended_len += self.app_data.len();
360
4
        let start_idx = vec.len();
361
4
        vec.extend_from_slice(sph_zc.as_bytes());
362
4
        // The PUS version is hardcoded to PUS C
363
4
        let pus_tc_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
364
4
        vec.extend_from_slice(pus_tc_header.as_bytes());
365
4
        vec.extend_from_slice(self.app_data);
366
4
        let mut digest = CRC_CCITT_FALSE.digest();
367
4
        digest.update(&vec[start_idx..start_idx + appended_len - 2]);
368
4
        vec.extend_from_slice(&digest.finalize().to_be_bytes());
369
4
        appended_len
370
4
    }
371
}
372

            
373
impl WritablePusPacket for PusTcCreator<'_> {
374
    #[inline]
375
64
    fn len_written(&self) -> usize {
376
64
        PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len()
377
64
    }
378

            
379
    /// Write the raw PUS byte representation to a provided buffer.
380
24
    fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
381
24
        let mut curr_idx = 0;
382
24
        let tc_header_len = size_of::<zc::PusTcSecondaryHeader>();
383
24
        let total_size = self.len_written();
384
24
        if total_size > slice.len() {
385
2
            return Err(ByteConversionError::ToSliceTooSmall {
386
2
                found: slice.len(),
387
2
                expected: total_size,
388
2
            }
389
2
            .into());
390
22
        }
391
22
        self.sp_header.write_to_be_bytes(slice)?;
392
22
        curr_idx += CCSDS_HEADER_LEN;
393
22
        let sec_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
394
22
        sec_header
395
22
            .write_to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len])
396
22
            .ok_or(ByteConversionError::ZeroCopyToError)?;
397

            
398
22
        curr_idx += tc_header_len;
399
22
        slice[curr_idx..curr_idx + self.app_data.len()].copy_from_slice(self.app_data);
400
22
        curr_idx += self.app_data.len();
401
22
        let mut digest = CRC_CCITT_FALSE.digest();
402
22
        digest.update(&slice[0..curr_idx]);
403
22
        slice[curr_idx..curr_idx + 2].copy_from_slice(&digest.finalize().to_be_bytes());
404
22
        curr_idx += 2;
405
22
        Ok(curr_idx)
406
24
    }
407
}
408

            
409
impl CcsdsPacket for PusTcCreator<'_> {
410
    ccsds_impl!();
411
}
412

            
413
impl PusPacket for PusTcCreator<'_> {
414
    delegate!(to self.sec_header {
415
        #[inline]
416
12
        fn pus_version(&self) -> PusVersion;
417
        #[inline]
418
6
        fn service(&self) -> u8;
419
        #[inline]
420
6
        fn subservice(&self) -> u8;
421
    });
422

            
423
    #[inline]
424
2
    fn user_data(&self) -> &[u8] {
425
2
        self.app_data
426
2
    }
427

            
428
    #[inline]
429
2
    fn crc16(&self) -> Option<u16> {
430
2
        Some(self.calc_own_crc16())
431
2
    }
432
}
433

            
434
impl GenericPusTcSecondaryHeader for PusTcCreator<'_> {
435
    delegate!(to self.sec_header {
436
        #[inline]
437
6
        fn pus_version(&self) -> PusVersion;
438
        #[inline]
439
6
        fn service(&self) -> u8;
440
        #[inline]
441
6
        fn subservice(&self) -> u8;
442
        #[inline]
443
8
        fn source_id(&self) -> u16;
444
        #[inline]
445
8
        fn ack_flags(&self) -> u8;
446
    });
447
}
448

            
449
impl IsPusTelecommand for PusTcCreator<'_> {}
450

            
451
/// This class can be used to read a PUS TC telecommand from raw memory.
452
///
453
/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the
454
/// [serde] feature is used, which allows to send around TC packets in a raw byte format using a
455
/// serde provider like [postcard](https://docs.rs/postcard/latest/postcard/).
456
///
457
/// There is no spare bytes support yet.
458
///
459
/// # Lifetimes
460
///
461
/// * `'raw_data` - Lifetime of the provided raw slice.
462
#[derive(Eq, Copy, Clone, Debug)]
463
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
464
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
465
pub struct PusTcReader<'raw_data> {
466
    #[cfg_attr(feature = "serde", serde(skip))]
467
    raw_data: &'raw_data [u8],
468
    sp_header: SpHeader,
469
    sec_header: PusTcSecondaryHeader,
470
    app_data: &'raw_data [u8],
471
    crc16: u16,
472
}
473

            
474
impl<'raw_data> PusTcReader<'raw_data> {
475
    /// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing
476
    /// the instance and the found byte length of the packet. This function also performs a CRC
477
    /// check and will return an appropriate [PusError] if the check fails.
478
20
    pub fn new(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> {
479
20
        let raw_data_len = slice.len();
480
20
        if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
481
2
            return Err(ByteConversionError::FromSliceTooSmall {
482
2
                found: raw_data_len,
483
2
                expected: PUS_TC_MIN_LEN_WITHOUT_APP_DATA,
484
2
            }
485
2
            .into());
486
18
        }
487
18
        let mut current_idx = 0;
488
18
        let (sp_header, _) = SpHeader::from_be_bytes(&slice[0..CCSDS_HEADER_LEN])?;
489
18
        current_idx += CCSDS_HEADER_LEN;
490
18
        let total_len = sp_header.total_len();
491
18
        if raw_data_len < total_len {
492
2
            return Err(ByteConversionError::FromSliceTooSmall {
493
2
                found: raw_data_len,
494
2
                expected: total_len,
495
2
            }
496
2
            .into());
497
16
        }
498
16
        if total_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
499
            return Err(ByteConversionError::FromSliceTooSmall {
500
                found: total_len,
501
                expected: PUS_TC_MIN_LEN_WITHOUT_APP_DATA,
502
            }
503
            .into());
504
16
        }
505
16
        let sec_header = zc::PusTcSecondaryHeader::from_bytes(
506
16
            &slice[current_idx..current_idx + PUC_TC_SECONDARY_HEADER_LEN],
507
16
        )
508
16
        .ok_or(ByteConversionError::ZeroCopyFromError)?;
509
16
        current_idx += PUC_TC_SECONDARY_HEADER_LEN;
510
16
        let raw_data = &slice[0..total_len];
511
16
        let pus_tc = Self {
512
16
            sp_header,
513
16
            sec_header: PusTcSecondaryHeader::try_from(sec_header).unwrap(),
514
16
            raw_data,
515
16
            app_data: user_data_from_raw(current_idx, total_len, slice)?,
516
16
            crc16: crc_from_raw_data(raw_data)?,
517
        };
518
16
        verify_crc16_ccitt_false_from_raw_to_pus_error(raw_data, pus_tc.crc16)?;
519
14
        Ok((pus_tc, total_len))
520
20
    }
521

            
522
    #[inline]
523
2
    pub fn app_data(&self) -> &[u8] {
524
2
        self.user_data()
525
2
    }
526

            
527
    #[inline]
528
2
    pub fn raw_data(&self) -> &[u8] {
529
2
        self.raw_data
530
2
    }
531

            
532
    #[inline]
533
6
    pub fn len_packed(&self) -> usize {
534
6
        self.sp_header.total_len()
535
6
    }
536

            
537
    #[inline]
538
6
    pub fn sp_header(&self) -> &SpHeader {
539
6
        &self.sp_header
540
6
    }
541
}
542

            
543
impl PartialEq for PusTcReader<'_> {
544
    #[inline]
545
2
    fn eq(&self, other: &Self) -> bool {
546
2
        self.raw_data == other.raw_data
547
2
    }
548
}
549

            
550
impl CcsdsPacket for PusTcReader<'_> {
551
    ccsds_impl!();
552
}
553

            
554
impl PusPacket for PusTcReader<'_> {
555
    delegate!(to self.sec_header {
556
        #[inline]
557
12
        fn pus_version(&self) -> PusVersion;
558
        #[inline]
559
6
        fn service(&self) -> u8;
560
        #[inline]
561
6
        fn subservice(&self) -> u8;
562
    });
563

            
564
    #[inline]
565
14
    fn user_data(&self) -> &[u8] {
566
14
        self.app_data
567
14
    }
568

            
569
    #[inline]
570
2
    fn crc16(&self) -> Option<u16> {
571
2
        Some(self.crc16)
572
2
    }
573
}
574

            
575
impl GenericPusTcSecondaryHeader for PusTcReader<'_> {
576
    delegate!(to self.sec_header {
577
        #[inline]
578
6
        fn pus_version(&self) -> PusVersion;
579
        #[inline]
580
6
        fn service(&self) -> u8;
581
        #[inline]
582
6
        fn subservice(&self) -> u8;
583
        #[inline]
584
6
        fn source_id(&self) -> u16;
585
        #[inline]
586
6
        fn ack_flags(&self) -> u8;
587
    });
588
}
589

            
590
impl IsPusTelecommand for PusTcReader<'_> {}
591

            
592
impl PartialEq<PusTcCreator<'_>> for PusTcReader<'_> {
593
2
    fn eq(&self, other: &PusTcCreator) -> bool {
594
2
        self.sp_header == other.sp_header
595
2
            && self.sec_header == other.sec_header
596
2
            && self.app_data == other.app_data
597
2
    }
598
}
599

            
600
impl PartialEq<PusTcReader<'_>> for PusTcCreator<'_> {
601
2
    fn eq(&self, other: &PusTcReader) -> bool {
602
2
        self.sp_header == other.sp_header
603
2
            && self.sec_header == other.sec_header
604
2
            && self.app_data == other.app_data
605
2
    }
606
}
607

            
608
#[cfg(all(test, feature = "std"))]
609
mod tests {
610
    use std::error::Error;
611

            
612
    use super::*;
613
    use crate::ecss::PusVersion::PusC;
614
    use crate::ecss::{PusError, PusPacket, WritablePusPacket};
615
    use crate::{ByteConversionError, SpHeader};
616
    use crate::{CcsdsPacket, SequenceFlags};
617
    use alloc::string::ToString;
618
    use alloc::vec::Vec;
619
    #[cfg(feature = "serde")]
620
    use postcard::{from_bytes, to_allocvec};
621

            
622
4
    fn base_ping_tc_full_ctor() -> PusTcCreator<'static> {
623
4
        let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap();
624
4
        let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
625
4
        PusTcCreator::new_no_app_data(sph, tc_header, true)
626
4
    }
627

            
628
22
    fn base_ping_tc_simple_ctor() -> PusTcCreator<'static> {
629
22
        let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap();
630
22
        PusTcCreator::new_simple(sph, 17, 1, &[], true)
631
22
    }
632

            
633
10
    fn base_ping_tc_simple_ctor_with_app_data(app_data: &'static [u8]) -> PusTcCreator<'static> {
634
10
        let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap();
635
10
        PusTcCreator::new_simple(sph, 17, 1, app_data, true)
636
10
    }
637

            
638
    #[test]
639
2
    fn test_tc_fields() {
640
2
        let pus_tc = base_ping_tc_full_ctor();
641
2
        verify_test_tc(&pus_tc, false, 13);
642
2
    }
643

            
644
    #[test]
645
2
    fn test_serialization() {
646
2
        let pus_tc = base_ping_tc_simple_ctor();
647
2
        let mut test_buf: [u8; 32] = [0; 32];
648
2
        let size = pus_tc
649
2
            .write_to_bytes(test_buf.as_mut_slice())
650
2
            .expect("Error writing TC to buffer");
651
2
        assert_eq!(size, 13);
652
2
        assert_eq!(
653
2
            pus_tc.crc16().unwrap(),
654
2
            u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap())
655
2
        );
656
2
    }
657

            
658
    #[test]
659
2
    fn test_deserialization() {
660
2
        let pus_tc = base_ping_tc_simple_ctor();
661
2
        let mut test_buf: [u8; 32] = [0; 32];
662
2
        let size = pus_tc
663
2
            .write_to_bytes(test_buf.as_mut_slice())
664
2
            .expect("Error writing TC to buffer");
665
2
        assert_eq!(size, 13);
666
2
        let (tc_from_raw, size) =
667
2
            PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
668
2
        assert_eq!(size, 13);
669
2
        verify_test_tc_with_reader(&tc_from_raw, false, 13);
670
2
        assert!(tc_from_raw.user_data().is_empty());
671
2
        verify_test_tc_raw(&test_buf);
672
2
        verify_crc_no_app_data(&test_buf);
673
2
    }
674

            
675
    #[test]
676
2
    fn test_writing_into_vec() {
677
2
        let pus_tc = base_ping_tc_simple_ctor();
678
2
        let tc_vec = pus_tc.to_vec().expect("Error writing TC to buffer");
679
2
        assert_eq!(tc_vec.len(), 13);
680
2
        let (tc_from_raw, size) = PusTcReader::new(tc_vec.as_slice())
681
2
            .expect("Creating PUS TC struct from raw buffer failed");
682
2
        assert_eq!(size, 13);
683
2
        verify_test_tc_with_reader(&tc_from_raw, false, 13);
684
2
        assert!(tc_from_raw.user_data().is_empty());
685
2
        verify_test_tc_raw(&tc_vec);
686
2
        verify_crc_no_app_data(&tc_vec);
687
2
    }
688

            
689
    #[test]
690
2
    fn test_update_func() {
691
2
        let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap();
692
2
        let mut tc = PusTcCreator::new_simple(sph, 17, 1, &[], false);
693
2
        assert_eq!(tc.data_len(), 0);
694
2
        tc.update_ccsds_data_len();
695
2
        assert_eq!(tc.data_len(), 6);
696
2
    }
697
    #[test]
698
2
    fn test_deserialization_with_app_data() {
699
2
        let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
700
2
        let mut test_buf: [u8; 32] = [0; 32];
701
2
        let size = pus_tc
702
2
            .write_to_bytes(test_buf.as_mut_slice())
703
2
            .expect("Error writing TC to buffer");
704
2
        assert_eq!(size, 16);
705
2
        let (tc_from_raw, size) =
706
2
            PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
707
2
        assert_eq!(size, 16);
708
2
        verify_test_tc_with_reader(&tc_from_raw, true, 16);
709
2
        let user_data = tc_from_raw.user_data();
710
2
        assert_eq!(tc_from_raw.user_data(), tc_from_raw.app_data());
711
2
        assert_eq!(tc_from_raw.raw_data(), &test_buf[..size]);
712
2
        assert_eq!(
713
2
            tc_from_raw.crc16().unwrap(),
714
2
            u16::from_be_bytes(test_buf[size - 2..size].try_into().unwrap())
715
2
        );
716
2
        assert_eq!(user_data[0], 1);
717
2
        assert_eq!(user_data[1], 2);
718
2
        assert_eq!(user_data[2], 3);
719
2
    }
720

            
721
    #[test]
722
2
    fn test_reader_eq() {
723
2
        let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
724
2
        let mut test_buf: [u8; 32] = [0; 32];
725
2
        pus_tc
726
2
            .write_to_bytes(test_buf.as_mut_slice())
727
2
            .expect("Error writing TC to buffer");
728
2
        let (tc_from_raw_0, _) =
729
2
            PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
730
2
        let (tc_from_raw_1, _) =
731
2
            PusTcReader::new(&test_buf).expect("Creating PUS TC struct from raw buffer failed");
732
2
        assert_eq!(tc_from_raw_0, tc_from_raw_1);
733
2
    }
734

            
735
    #[test]
736
2
    fn test_vec_ser_deser() {
737
2
        let pus_tc = base_ping_tc_simple_ctor();
738
2
        let mut test_vec = Vec::new();
739
2
        let size = pus_tc.append_to_vec(&mut test_vec);
740
2
        assert_eq!(size, 13);
741
2
        verify_test_tc_raw(&test_vec.as_slice());
742
2
        verify_crc_no_app_data(&test_vec.as_slice());
743
2
    }
744

            
745
    #[test]
746
2
    fn test_incorrect_crc() {
747
2
        let pus_tc = base_ping_tc_simple_ctor();
748
2
        let mut test_buf: [u8; 32] = [0; 32];
749
2
        pus_tc
750
2
            .write_to_bytes(test_buf.as_mut_slice())
751
2
            .expect("Error writing TC to buffer");
752
2
        test_buf[12] = 0;
753
2
        test_buf[11] = 0;
754
2
        let res = PusTcReader::new(&test_buf);
755
2
        assert!(res.is_err());
756
2
        let err = res.unwrap_err();
757
2
        if let PusError::ChecksumFailure(crc) = err {
758
2
            assert_eq!(crc, 0);
759
2
            assert_eq!(
760
2
                err.to_string(),
761
2
                "checksum verification for crc16 0x0000 failed"
762
2
            );
763
        } else {
764
            panic!("unexpected error {err}");
765
        }
766
2
    }
767

            
768
    #[test]
769
2
    fn test_manual_crc_calculation() {
770
2
        let pus_tc = base_ping_tc_simple_ctor();
771
2
        let mut test_buf: [u8; 32] = [0; 32];
772
2
        pus_tc.calc_own_crc16();
773
2
        pus_tc
774
2
            .write_to_bytes(test_buf.as_mut_slice())
775
2
            .expect("Error writing TC to buffer");
776
2
        verify_test_tc_raw(&test_buf);
777
2
        verify_crc_no_app_data(&test_buf);
778
2
    }
779

            
780
    #[test]
781
2
    fn test_with_application_data_vec() {
782
2
        let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
783
2
        verify_test_tc(&pus_tc, true, 16);
784
2
        let mut test_vec = Vec::new();
785
2
        let size = pus_tc.append_to_vec(&mut test_vec);
786
2
        assert_eq!(test_vec[11], 1);
787
2
        assert_eq!(test_vec[12], 2);
788
2
        assert_eq!(test_vec[13], 3);
789
2
        assert_eq!(size, 16);
790
2
    }
791

            
792
    #[test]
793
2
    fn test_write_buf_too_small() {
794
2
        let pus_tc = base_ping_tc_simple_ctor();
795
2
        let mut test_buf = [0; 12];
796
2
        let res = pus_tc.write_to_bytes(test_buf.as_mut_slice());
797
2
        assert!(res.is_err());
798
2
        let err = res.unwrap_err();
799
2
        if let PusError::ByteConversion(e) = err {
800
2
            assert_eq!(
801
2
                e,
802
2
                ByteConversionError::ToSliceTooSmall {
803
2
                    found: 12,
804
2
                    expected: 13
805
2
                }
806
2
            );
807
2
            assert_eq!(
808
2
                err.to_string(),
809
2
                "pus error: target slice with size 12 is too small, expected size of at least 13"
810
2
            );
811
2
            assert_eq!(err.source().unwrap().to_string(), e.to_string());
812
        } else {
813
            panic!("unexpected error {err}");
814
        }
815
2
    }
816

            
817
    #[test]
818
2
    fn test_with_application_data_buf() {
819
2
        let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
820
2
        verify_test_tc(&pus_tc, true, 16);
821
2
        let mut test_buf: [u8; 32] = [0; 32];
822
2
        let size = pus_tc
823
2
            .write_to_bytes(test_buf.as_mut_slice())
824
2
            .expect("Error writing TC to buffer");
825
2
        assert_eq!(test_buf[11], 1);
826
2
        assert_eq!(test_buf[12], 2);
827
2
        assert_eq!(test_buf[13], 3);
828
2
        assert_eq!(size, 16);
829
2
    }
830

            
831
    #[test]
832
2
    fn test_custom_setters() {
833
2
        let mut pus_tc = base_ping_tc_simple_ctor();
834
2
        let mut test_buf: [u8; 32] = [0; 32];
835
2
        pus_tc.set_apid(0x7ff);
836
2
        pus_tc.set_seq_count(0x3fff);
837
2
        pus_tc.set_ack_field(0b11);
838
2
        pus_tc.set_source_id(0xffff);
839
2
        pus_tc.set_seq_flags(SequenceFlags::Unsegmented);
840
2
        assert_eq!(pus_tc.source_id(), 0xffff);
841
2
        assert_eq!(pus_tc.seq_count(), 0x3fff);
842
2
        assert_eq!(pus_tc.ack_flags(), 0b11);
843
2
        assert_eq!(pus_tc.apid(), 0x7ff);
844
2
        assert_eq!(pus_tc.sequence_flags(), SequenceFlags::Unsegmented);
845
2
        pus_tc.calc_own_crc16();
846
2
        pus_tc
847
2
            .write_to_bytes(test_buf.as_mut_slice())
848
2
            .expect("Error writing TC to buffer");
849
2
        assert_eq!(test_buf[0], 0x1f);
850
2
        assert_eq!(test_buf[1], 0xff);
851
2
        assert_eq!(test_buf[2], 0xff);
852
2
        assert_eq!(test_buf[3], 0xff);
853
2
        assert_eq!(test_buf[6], 0x23);
854
        // Source ID 0
855
2
        assert_eq!(test_buf[9], 0xff);
856
2
        assert_eq!(test_buf[10], 0xff);
857
2
    }
858

            
859
6
    fn verify_test_tc(tc: &PusTcCreator, has_user_data: bool, exp_full_len: usize) {
860
6
        verify_test_tc_generic(tc);
861
6
        if !has_user_data {
862
2
            assert!(tc.user_data().is_empty());
863
4
        }
864
6
        let mut comp_header =
865
6
            SpHeader::new_for_unseg_tc_checked(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
866
6
        comp_header.set_sec_header_flag();
867
6
        assert_eq!(*tc.sp_header(), comp_header);
868
6
    }
869

            
870
6
    fn verify_test_tc_with_reader(tc: &PusTcReader, has_user_data: bool, exp_full_len: usize) {
871
6
        verify_test_tc_generic(tc);
872
6
        if !has_user_data {
873
4
            assert!(tc.user_data().is_empty());
874
2
        }
875
6
        assert_eq!(tc.len_packed(), exp_full_len);
876
6
        let mut comp_header =
877
6
            SpHeader::new_for_unseg_tc_checked(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
878
6
        comp_header.set_sec_header_flag();
879
6
        assert_eq!(*tc.sp_header(), comp_header);
880
6
    }
881

            
882
12
    fn verify_test_tc_generic(tc: &(impl CcsdsPacket + PusPacket + GenericPusTcSecondaryHeader)) {
883
12
        assert_eq!(PusPacket::service(tc), 17);
884
12
        assert_eq!(GenericPusTcSecondaryHeader::service(tc), 17);
885
12
        assert_eq!(PusPacket::subservice(tc), 1);
886
12
        assert_eq!(GenericPusTcSecondaryHeader::subservice(tc), 1);
887
12
        assert!(tc.sec_header_flag());
888
12
        assert_eq!(PusPacket::pus_version(tc), PusC);
889
12
        assert_eq!(tc.seq_count(), 0x34);
890
12
        assert_eq!(tc.source_id(), 0);
891
12
        assert_eq!(tc.apid(), 0x02);
892
12
        assert_eq!(tc.ack_flags(), ACK_ALL);
893
12
        assert_eq!(PusPacket::pus_version(tc), PusVersion::PusC);
894
12
        assert_eq!(
895
12
            GenericPusTcSecondaryHeader::pus_version(tc),
896
12
            PusVersion::PusC
897
12
        );
898
12
    }
899
8
    fn verify_test_tc_raw(slice: &impl AsRef<[u8]>) {
900
8
        // Reference comparison implementation:
901
8
        // https://github.com/us-irs/py-spacepackets/blob/v0.13.0/tests/ecss/test_pus_tc.py
902
8
        let slice = slice.as_ref();
903
8
        // 0x1801 is the generic
904
8
        assert_eq!(slice[0], 0x18);
905
        // APID is 0x01
906
8
        assert_eq!(slice[1], 0x02);
907
        // Unsegmented packets
908
8
        assert_eq!(slice[2], 0xc0);
909
        // Sequence count 0x34
910
8
        assert_eq!(slice[3], 0x34);
911
8
        assert_eq!(slice[4], 0x00);
912
        // Space data length of 6 equals total packet length of 13
913
8
        assert_eq!(slice[5], 0x06);
914
        // PUS Version C 0b0010 and ACK flags 0b1111
915
8
        assert_eq!(slice[6], 0x2f);
916
        // Service 17
917
8
        assert_eq!(slice[7], 0x11);
918
        // Subservice 1
919
8
        assert_eq!(slice[8], 0x01);
920
        // Source ID 0
921
8
        assert_eq!(slice[9], 0x00);
922
8
        assert_eq!(slice[10], 0x00);
923
8
    }
924

            
925
8
    fn verify_crc_no_app_data(slice: &impl AsRef<[u8]>) {
926
8
        // Reference comparison implementation:
927
8
        // https://github.com/us-irs/py-spacepackets/blob/v0.13.0/tests/ecss/test_pus_tc.py
928
8
        let slice = slice.as_ref();
929
8
        assert_eq!(slice[11], 0xee);
930
8
        assert_eq!(slice[12], 0x63);
931
8
    }
932

            
933
    #[test]
934
2
    fn partial_eq_pus_tc() {
935
2
        // new vs new simple
936
2
        let pus_tc_1 = base_ping_tc_simple_ctor();
937
2
        let pus_tc_2 = base_ping_tc_full_ctor();
938
2
        assert_eq!(pus_tc_1, pus_tc_2);
939
2
    }
940

            
941
    #[test]
942
2
    fn partial_eq_serialized_vs_derialized() {
943
2
        let pus_tc = base_ping_tc_simple_ctor();
944
2
        let mut buf = [0; 32];
945
2
        pus_tc.write_to_bytes(&mut buf).unwrap();
946
2
        assert_eq!(pus_tc, PusTcReader::new(&buf).unwrap().0);
947
2
        assert_eq!(PusTcReader::new(&buf).unwrap().0, pus_tc);
948
2
    }
949

            
950
    #[test]
951
2
    fn test_ack_opts_from_raw() {
952
2
        let ack_opts_raw = AckOpts::Start as u8;
953
2
        let ack_opts = AckOpts::try_from(ack_opts_raw).unwrap();
954
2
        assert_eq!(ack_opts, AckOpts::Start);
955
2
    }
956

            
957
    #[test]
958
2
    fn test_reader_buf_too_small() {
959
2
        let app_data = &[1, 2, 3, 4];
960
2
        let pus_tc = base_ping_tc_simple_ctor_with_app_data(app_data);
961
2
        let mut buf = [0; 32];
962
2
        let written_len = pus_tc.write_to_bytes(&mut buf).unwrap();
963
2
        let error = PusTcReader::new(&buf[0..PUS_TC_MIN_LEN_WITHOUT_APP_DATA + 1]);
964
2
        assert!(error.is_err());
965
2
        let error = error.unwrap_err();
966
        if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall {
967
2
            found,
968
2
            expected,
969
2
        }) = error
970
        {
971
2
            assert_eq!(found, PUS_TC_MIN_LEN_WITHOUT_APP_DATA + 1);
972
2
            assert_eq!(expected, written_len);
973
        } else {
974
            panic!("unexpected error {error}")
975
        }
976
2
    }
977

            
978
    #[test]
979
2
    fn test_reader_input_too_small() {
980
2
        let buf: [u8; 5] = [0; 5];
981
2
        let error = PusTcReader::new(&buf);
982
2
        assert!(error.is_err());
983
2
        let error = error.unwrap_err();
984
        if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall {
985
2
            found,
986
2
            expected,
987
2
        }) = error
988
        {
989
2
            assert_eq!(found, 5);
990
2
            assert_eq!(expected, PUS_TC_MIN_LEN_WITHOUT_APP_DATA);
991
        } else {
992
            panic!("unexpected error {error}")
993
        }
994
2
    }
995

            
996
    #[test]
997
    #[cfg(feature = "serde")]
998
2
    fn test_serialization_tc_serde() {
999
2
        let pus_tc = base_ping_tc_simple_ctor();
2
        let output = to_allocvec(&pus_tc).unwrap();
2
        let output_converted_back: PusTcCreator = from_bytes(&output).unwrap();
2
        assert_eq!(output_converted_back, pus_tc);
2
    }
}