1
//! Common definitions and helpers required to create PUS TMTC packets according to
2
//! [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
//! You can find the PUS telecommand types in the [tc] module and the the PUS telemetry
5
//! types inside the [tm] module.
6
use crate::{ByteConversionError, CcsdsPacket, CRC_CCITT_FALSE};
7
#[cfg(feature = "alloc")]
8
use alloc::vec::Vec;
9
use core::fmt::{Debug, Display, Formatter};
10
use core::mem::size_of;
11
use num_enum::{IntoPrimitive, TryFromPrimitive};
12
#[cfg(feature = "serde")]
13
use serde::{Deserialize, Serialize};
14
#[cfg(feature = "std")]
15
use std::error::Error;
16

            
17
pub mod event;
18
pub mod hk;
19
pub mod scheduling;
20
pub mod tc;
21
pub mod tm;
22
pub mod verification;
23

            
24
pub type CrcType = u16;
25

            
26
2
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
27
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28
#[repr(u8)]
29
#[non_exhaustive]
30
pub enum PusServiceId {
31
    /// Service 1
32
    Verification = 1,
33
    /// Service 2
34
    DeviceAccess = 2,
35
    /// Service 3
36
    Housekeeping = 3,
37
    /// Service 4
38
    ParameterStatistics = 4,
39
    /// Service 5
40
    Event = 5,
41
    /// Service 6
42
    MemoryManagement = 6,
43
    /// Service 8
44
    Action = 8,
45
    /// Service 9
46
    TimeManagement = 9,
47
    /// Service 11
48
    Scheduling = 11,
49
    /// Service 12
50
    OnBoardMonitoring = 12,
51
    /// Service 13
52
    LargePacketTransfer = 13,
53
    /// Service 14
54
    RealTimeForwardingControl = 14,
55
    /// Service 15
56
    StorageAndRetrival = 15,
57
    /// Service 17
58
    Test = 17,
59
    /// Service 18
60
    OpsAndProcedures = 18,
61
    /// Service 19
62
    EventAction = 19,
63
    /// Service 20
64
    Parameter = 20,
65
    /// Service 21
66
    RequestSequencing = 21,
67
    /// Service 22
68
    PositionBasedScheduling = 22,
69
    /// Service 23
70
    FileManagement = 23,
71
}
72

            
73
/// All PUS versions. Only PUS C is supported by this library.
74
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
75
12
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77
#[non_exhaustive]
78
pub enum PusVersion {
79
    EsaPus = 0,
80
    PusA = 1,
81
    PusC = 2,
82
    Invalid = 0b1111,
83
}
84

            
85
impl TryFrom<u8> for PusVersion {
86
    type Error = ();
87

            
88
16
    fn try_from(value: u8) -> Result<Self, Self::Error> {
89
16
        match value {
90
16
            x if x == PusVersion::EsaPus as u8 => Ok(PusVersion::EsaPus),
91
16
            x if x == PusVersion::PusA as u8 => Ok(PusVersion::PusA),
92
16
            x if x == PusVersion::PusC as u8 => Ok(PusVersion::PusC),
93
            _ => Err(()),
94
        }
95
16
    }
96
}
97

            
98
/// ECSS Packet Type Codes (PTC)s.
99
2
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
100
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
101
#[repr(u8)]
102
pub enum PacketTypeCodes {
103
    Boolean = 1,
104
    Enumerated = 2,
105
    UnsignedInt = 3,
106
    SignedInt = 4,
107
    Real = 5,
108
    BitString = 6,
109
    OctetString = 7,
110
    CharString = 8,
111
    AbsoluteTime = 9,
112
    RelativeTime = 10,
113
    Deduced = 11,
114
    Packet = 12,
115
}
116

            
117
pub type Ptc = PacketTypeCodes;
118

            
119
/// ECSS Packet Field Codes (PFC)s for the unsigned [Ptc].
120
2
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
121
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122
#[repr(u8)]
123
pub enum PfcUnsigned {
124
    OneByte = 4,
125
    TwelveBits = 8,
126
    TwoBytes = 12,
127
    ThreeBytes = 13,
128
    FourBytes = 14,
129
    SixBytes = 15,
130
    EightBytes = 16,
131
    OneBit = 17,
132
    TwoBits = 18,
133
    ThreeBits = 19,
134
}
135

            
136
/// ECSS Packet Field Codes (PFC)s for the real (floating point) [Ptc].
137
2
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
138
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
139
#[repr(u8)]
140
pub enum PfcReal {
141
    /// 4 octets simple precision format (IEEE)
142
    Float = 1,
143
    /// 8 octets simple precision format (IEEE)
144
    Double = 2,
145
    /// 4 octets simple precision format (MIL-STD)
146
    FloatMilStd = 3,
147
    /// 8 octets simple precision format (MIL-STD)
148
    DoubleMilStd = 4,
149
}
150

            
151
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
152
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
153
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154
pub enum PusError {
155
    VersionNotSupported(PusVersion),
156
    ChecksumFailure(u16),
157
    /// CRC16 needs to be calculated first
158
    CrcCalculationMissing,
159
    ByteConversion(ByteConversionError),
160
}
161

            
162
impl Display for PusError {
163
8
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
164
8
        match self {
165
2
            PusError::VersionNotSupported(v) => {
166
2
                write!(f, "PUS version {v:?} not supported")
167
            }
168
4
            PusError::ChecksumFailure(crc) => {
169
4
                write!(f, "checksum verification for crc16 {crc:#06x} failed")
170
            }
171
            PusError::CrcCalculationMissing => {
172
                write!(f, "crc16 was not calculated")
173
            }
174
2
            PusError::ByteConversion(e) => {
175
2
                write!(f, "pus error: {e}")
176
            }
177
        }
178
8
    }
179
}
180

            
181
#[cfg(feature = "std")]
182
impl Error for PusError {
183
2
    fn source(&self) -> Option<&(dyn Error + 'static)> {
184
2
        if let PusError::ByteConversion(e) = self {
185
2
            return Some(e);
186
        }
187
        None
188
2
    }
189
}
190

            
191
impl From<ByteConversionError> for PusError {
192
10
    fn from(e: ByteConversionError) -> Self {
193
10
        PusError::ByteConversion(e)
194
10
    }
195
}
196

            
197
/// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry
198
/// (TM) packets. All PUS packets are also a special type of [CcsdsPacket]s.
199
pub trait PusPacket: CcsdsPacket {
200
    const PUS_VERSION: PusVersion = PusVersion::PusC;
201

            
202
    fn pus_version(&self) -> PusVersion;
203
    fn service(&self) -> u8;
204
    fn subservice(&self) -> u8;
205
    fn user_data(&self) -> &[u8];
206
    fn crc16(&self) -> Option<u16>;
207
}
208

            
209
32
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, ByteConversionError> {
210
32
    if raw_data.len() < 2 {
211
        return Err(ByteConversionError::FromSliceTooSmall {
212
            found: raw_data.len(),
213
            expected: 2,
214
        });
215
32
    }
216
32
    Ok(u16::from_be_bytes(
217
32
        raw_data[raw_data.len() - 2..raw_data.len()]
218
32
            .try_into()
219
32
            .unwrap(),
220
32
    ))
221
32
}
222

            
223
4
pub(crate) fn calc_pus_crc16(bytes: &[u8]) -> u16 {
224
4
    let mut digest = CRC_CCITT_FALSE.digest();
225
4
    digest.update(bytes);
226
4
    digest.finalize()
227
4
}
228

            
229
32
pub(crate) fn user_data_from_raw(
230
32
    current_idx: usize,
231
32
    total_len: usize,
232
32
    slice: &[u8],
233
32
) -> Result<&[u8], ByteConversionError> {
234
32
    match current_idx {
235
32
        _ if current_idx > total_len - 2 => Err(ByteConversionError::FromSliceTooSmall {
236
            found: total_len - 2,
237
            expected: current_idx,
238
        }),
239
32
        _ => Ok(&slice[current_idx..total_len - 2]),
240
    }
241
32
}
242

            
243
32
pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error(
244
32
    raw_data: &[u8],
245
32
    crc16: u16,
246
32
) -> Result<(), PusError> {
247
32
    verify_crc16_ccitt_false_from_raw(raw_data)
248
32
        .then_some(())
249
32
        .ok_or(PusError::ChecksumFailure(crc16))
250
32
}
251

            
252
32
pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
253
32
    let mut digest = CRC_CCITT_FALSE.digest();
254
32
    digest.update(raw_data);
255
32
    if digest.finalize() == 0 {
256
28
        return true;
257
4
    }
258
4
    false
259
32
}
260

            
261
macro_rules! ccsds_impl {
262
    () => {
263
        delegate!(to self.sp_header {
264
            #[inline]
265
            fn ccsds_version(&self) -> u8;
266
            #[inline]
267
54
            fn packet_id(&self) -> crate::PacketId;
268
            #[inline]
269
26
            fn psc(&self) -> crate::PacketSequenceCtrl;
270
            #[inline]
271
18
            fn data_len(&self) -> u16;
272
        });
273
    }
274
}
275

            
276
macro_rules! sp_header_impls {
277
    () => {
278
        delegate!(to self.sp_header {
279
            #[inline]
280
4
            pub fn set_apid(&mut self, apid: u16) -> bool;
281
            #[inline]
282
2
            pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
283
            #[inline]
284
2
            pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags);
285
        });
286
    }
287
}
288

            
289
use crate::util::{GenericUnsignedByteField, ToBeBytes, UnsignedEnum};
290
pub(crate) use ccsds_impl;
291
pub(crate) use sp_header_impls;
292

            
293
/// Generic trait for ECSS enumeration which consist of a PFC field denoting their bit length
294
/// and an unsigned value. The trait makes no assumptions about the actual type of the unsigned
295
/// value and only requires implementors to implement a function which writes the enumeration into
296
/// a raw byte format.
297
pub trait EcssEnumeration: UnsignedEnum {
298
    /// Packet Format Code, which denotes the number of bits of the enumeration
299
    fn pfc(&self) -> u8;
300
}
301

            
302
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
303

            
304
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
305
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
306
pub struct GenericEcssEnumWrapper<TYPE: Copy + Into<u64>> {
307
    field: GenericUnsignedByteField<TYPE>,
308
}
309

            
310
impl<TYPE: Copy + Into<u64>> GenericEcssEnumWrapper<TYPE> {
311
2
    pub const fn ptc() -> PacketTypeCodes {
312
2
        PacketTypeCodes::Enumerated
313
2
    }
314

            
315
16
    pub const fn value_typed(&self) -> TYPE {
316
16
        self.field.value_typed()
317
16
    }
318

            
319
12
    pub fn new(val: TYPE) -> Self {
320
12
        Self {
321
12
            field: GenericUnsignedByteField::new(val),
322
12
        }
323
12
    }
324
}
325

            
326
impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericEcssEnumWrapper<TYPE> {
327
12
    fn size(&self) -> usize {
328
12
        (self.pfc() / 8) as usize
329
12
    }
330

            
331
20
    fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
332
20
        self.field.write_to_be_bytes(buf)
333
20
    }
334

            
335
8
    fn value(&self) -> u64 {
336
8
        self.field.value()
337
8
    }
338
}
339

            
340
impl<TYPE: Copy + ToBeBytes + Into<u64>> EcssEnumeration for GenericEcssEnumWrapper<TYPE> {
341
16
    fn pfc(&self) -> u8 {
342
16
        size_of::<TYPE>() as u8 * 8_u8
343
16
    }
344
}
345

            
346
impl<TYPE: Debug + Copy + Clone + PartialEq + Eq + ToBeBytes + Into<u64>> EcssEnumerationExt
347
    for GenericEcssEnumWrapper<TYPE>
348
{
349
}
350

            
351
impl<T: Copy + Into<u64>> From<T> for GenericEcssEnumWrapper<T> {
352
    fn from(value: T) -> Self {
353
        Self::new(value)
354
    }
355
}
356

            
357
macro_rules! generic_ecss_enum_typedefs_and_from_impls {
358
    ($($ty:ty => $Enum:ident),*) => {
359
        $(
360
            pub type $Enum = GenericEcssEnumWrapper<$ty>;
361

            
362
            impl From<$Enum> for $ty {
363
8
                fn from(value: $Enum) -> Self {
364
8
                    value.value_typed()
365
8
                }
366
            }
367
        )*
368
    };
369
}
370

            
371
// Generates EcssEnum<$TY> type definitions as well as a From<$TY> for EcssEnum<$TY>
372
// implementation.
373
generic_ecss_enum_typedefs_and_from_impls! {
374
    u8 => EcssEnumU8,
375
    u16 => EcssEnumU16,
376
    u32 => EcssEnumU32,
377
    u64 => EcssEnumU64
378
}
379

            
380
/// Generic trait for PUS packet abstractions which can written to a raw slice as their raw
381
/// byte representation. This is especially useful for generic abstractions which depend only
382
/// on the serialization of those packets.
383
pub trait WritablePusPacket {
384
    fn len_written(&self) -> usize;
385
    fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError>;
386
    #[cfg(feature = "alloc")]
387
6
    fn to_vec(&self) -> Result<Vec<u8>, PusError> {
388
6
        // This is the correct way to do this. See
389
6
        // [this issue](https://github.com/rust-lang/rust-clippy/issues/4483) for caveats of more
390
6
        // "efficient" implementations.
391
6
        let mut vec = alloc::vec![0; self.len_written()];
392
6
        self.write_to_bytes(&mut vec)?;
393
6
        Ok(vec)
394
6
    }
395
}
396

            
397
#[cfg(test)]
398
mod tests {
399
    use alloc::string::ToString;
400

            
401
    use crate::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, UnsignedEnum};
402
    use crate::ByteConversionError;
403

            
404
    use super::*;
405
    #[cfg(feature = "serde")]
406
    use crate::tests::generic_serde_test;
407

            
408
    #[test]
409
2
    fn test_enum_u8() {
410
2
        let mut buf = [0, 0, 0];
411
2
        let my_enum = EcssEnumU8::new(1);
412
2
        assert_eq!(EcssEnumU8::ptc(), Ptc::Enumerated);
413
2
        assert_eq!(my_enum.size(), 1);
414
2
        assert_eq!(my_enum.pfc(), 8);
415
2
        my_enum
416
2
            .write_to_be_bytes(&mut buf[1..2])
417
2
            .expect("To byte conversion of u8 failed");
418
2
        assert_eq!(buf[1], 1);
419
2
        assert_eq!(my_enum.value(), 1);
420
2
        assert_eq!(my_enum.value_typed(), 1);
421
2
        let enum_as_u8: u8 = my_enum.into();
422
2
        assert_eq!(enum_as_u8, 1);
423
2
        let vec = my_enum.to_vec();
424
2
        assert_eq!(vec, buf[1..2]);
425
2
    }
426

            
427
    #[test]
428
2
    fn test_enum_u16() {
429
2
        let mut buf = [0, 0, 0];
430
2
        let my_enum = EcssEnumU16::new(0x1f2f);
431
2
        my_enum
432
2
            .write_to_be_bytes(&mut buf[1..3])
433
2
            .expect("To byte conversion of u8 failed");
434
2
        assert_eq!(my_enum.size(), 2);
435
2
        assert_eq!(my_enum.pfc(), 16);
436
2
        assert_eq!(buf[1], 0x1f);
437
2
        assert_eq!(buf[2], 0x2f);
438
2
        assert_eq!(my_enum.value(), 0x1f2f);
439
2
        assert_eq!(my_enum.value_typed(), 0x1f2f);
440
2
        let enum_as_raw: u16 = my_enum.into();
441
2
        assert_eq!(enum_as_raw, 0x1f2f);
442
2
        let vec = my_enum.to_vec();
443
2
        assert_eq!(vec, buf[1..3]);
444
2
    }
445

            
446
    #[test]
447
2
    fn test_slice_u16_too_small() {
448
2
        let mut buf = [0];
449
2
        let my_enum = EcssEnumU16::new(0x1f2f);
450
2
        let res = my_enum.write_to_be_bytes(&mut buf[0..1]);
451
2
        assert!(res.is_err());
452
2
        let error = res.unwrap_err();
453
2
        match error {
454
2
            ByteConversionError::ToSliceTooSmall { found, expected } => {
455
2
                assert_eq!(expected, 2);
456
2
                assert_eq!(found, 1);
457
            }
458
            _ => {
459
                panic!("Unexpected error {:?}", error);
460
            }
461
        }
462
2
    }
463

            
464
    #[test]
465
2
    fn test_enum_u32() {
466
2
        let mut buf = [0, 0, 0, 0, 0];
467
2
        let my_enum = EcssEnumU32::new(0x1f2f3f4f);
468
2
        my_enum
469
2
            .write_to_be_bytes(&mut buf[1..5])
470
2
            .expect("To byte conversion of u8 failed");
471
2
        assert_eq!(buf[1], 0x1f);
472
2
        assert_eq!(buf[2], 0x2f);
473
2
        assert_eq!(buf[3], 0x3f);
474
2
        assert_eq!(buf[4], 0x4f);
475
2
        assert_eq!(my_enum.value(), 0x1f2f3f4f);
476
2
        assert_eq!(my_enum.value_typed(), 0x1f2f3f4f);
477
2
        let enum_as_raw: u32 = my_enum.into();
478
2
        assert_eq!(enum_as_raw, 0x1f2f3f4f);
479
2
        let vec = my_enum.to_vec();
480
2
        assert_eq!(vec, buf[1..5]);
481
2
    }
482

            
483
    #[test]
484
2
    fn test_slice_u32_too_small() {
485
2
        let mut buf = [0, 0, 0, 0, 0];
486
2
        let my_enum = EcssEnumU32::new(0x1f2f3f4f);
487
2
        let res = my_enum.write_to_be_bytes(&mut buf[0..3]);
488
2
        assert!(res.is_err());
489
2
        let error = res.unwrap_err();
490
2
        match error {
491
2
            ByteConversionError::ToSliceTooSmall { found, expected } => {
492
2
                assert_eq!(expected, 4);
493
2
                assert_eq!(found, 3);
494
            }
495
            _ => {
496
                panic!("Unexpected error {:?}", error);
497
            }
498
        }
499
2
    }
500

            
501
    #[test]
502
2
    fn test_enum_u64() {
503
2
        let mut buf = [0; 8];
504
2
        let my_enum = EcssEnumU64::new(0x1f2f3f4f5f);
505
2
        my_enum
506
2
            .write_to_be_bytes(&mut buf)
507
2
            .expect("To byte conversion of u64 failed");
508
2
        assert_eq!(buf[3], 0x1f);
509
2
        assert_eq!(buf[4], 0x2f);
510
2
        assert_eq!(buf[5], 0x3f);
511
2
        assert_eq!(buf[6], 0x4f);
512
2
        assert_eq!(buf[7], 0x5f);
513
2
        assert_eq!(my_enum.value(), 0x1f2f3f4f5f);
514
2
        assert_eq!(my_enum.value_typed(), 0x1f2f3f4f5f);
515
2
        let enum_as_raw: u64 = my_enum.into();
516
2
        assert_eq!(enum_as_raw, 0x1f2f3f4f5f);
517
2
        assert_eq!(u64::from_be_bytes(buf), 0x1f2f3f4f5f);
518
2
        let vec = my_enum.to_vec();
519
2
        assert_eq!(vec, buf);
520
2
    }
521

            
522
    #[test]
523
2
    fn test_pus_error_display() {
524
2
        let unsupport_version = PusError::VersionNotSupported(super::PusVersion::EsaPus);
525
2
        let write_str = unsupport_version.to_string();
526
2
        assert_eq!(write_str, "PUS version EsaPus not supported")
527
2
    }
528

            
529
    #[test]
530
2
    fn test_service_id_from_u8() {
531
2
        let verification_id_raw = 1;
532
2
        let verification_id = PusServiceId::try_from(verification_id_raw).unwrap();
533
2
        assert_eq!(verification_id, PusServiceId::Verification);
534
2
    }
535

            
536
    #[test]
537
2
    fn test_ptc_from_u8() {
538
2
        let ptc_raw = Ptc::AbsoluteTime as u8;
539
2
        let ptc = Ptc::try_from(ptc_raw).unwrap();
540
2
        assert_eq!(ptc, Ptc::AbsoluteTime);
541
2
    }
542

            
543
    #[test]
544
2
    fn test_unsigned_pfc_from_u8() {
545
2
        let pfc_raw = PfcUnsigned::OneByte as u8;
546
2
        let pfc = PfcUnsigned::try_from(pfc_raw).unwrap();
547
2
        assert_eq!(pfc, PfcUnsigned::OneByte);
548
2
    }
549

            
550
    #[test]
551
2
    fn test_real_pfc_from_u8() {
552
2
        let pfc_raw = PfcReal::Double as u8;
553
2
        let pfc = PfcReal::try_from(pfc_raw).unwrap();
554
2
        assert_eq!(pfc, PfcReal::Double);
555
2
    }
556

            
557
    #[test]
558
2
    fn test_pus_error_eq_impl() {
559
2
        assert_eq!(
560
2
            PusError::VersionNotSupported(PusVersion::EsaPus),
561
2
            PusError::VersionNotSupported(PusVersion::EsaPus)
562
2
        );
563
2
    }
564

            
565
    #[test]
566
2
    fn test_pus_error_clonable() {
567
2
        let pus_error = PusError::ChecksumFailure(0x0101);
568
2
        let cloned = pus_error;
569
2
        assert_eq!(pus_error, cloned);
570
2
    }
571

            
572
    #[test]
573
    #[cfg(feature = "serde")]
574
2
    fn test_serde_pus_service_id() {
575
2
        generic_serde_test(PusServiceId::Verification);
576
2
    }
577

            
578
    #[test]
579
    #[cfg(feature = "serde")]
580
2
    fn test_serde_ptc() {
581
2
        generic_serde_test(Ptc::AbsoluteTime);
582
2
    }
583

            
584
    #[test]
585
    #[cfg(feature = "serde")]
586
2
    fn test_serde_pfc_unsigned() {
587
2
        generic_serde_test(PfcUnsigned::EightBytes);
588
2
    }
589

            
590
    #[test]
591
    #[cfg(feature = "serde")]
592
2
    fn test_serde_pfc_real() {
593
2
        generic_serde_test(PfcReal::Double);
594
2
    }
595
}