1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#![allow(clippy::identity_op)]

use std::mem;

use modular_bitfield_msb::prelude::*;
use zerocopy::{AsBytes, ByteSlice, FromBytes, LayoutVerified, Unaligned};

use crate::ccsds;
pub use crate::ccsds::space_packet::*;

#[bitfield(bytes = 20)]
#[derive(Debug, Clone, FromBytes, AsBytes, Unaligned, Default)]
#[repr(C)]
pub struct SecondaryHeader {
    pub version_number: B8,
    pub board_time: B32,
    pub telemetry_id: B8,
    pub global_time_bits: B64,
    pub on_board_subnetwork_time: B32,
    pub destination_flags: B8,
    pub data_recorder_partition: B8,
}

impl SecondaryHeader {
    pub const SIZE: usize = mem::size_of::<Self>();

    const HOUSEKEEPING: u8 = 0b00000001;
    const MISSION: u8 = 0b00000010;
    #[deprecated = "this is not a generic method. it's better to build a proper routing mechanism using destination_flags directly"]
    pub fn is_realtime(&self) -> bool {
        self.destination_flags() & (Self::HOUSEKEEPING | Self::MISSION) != 0
    }

    pub fn global_time(&self) -> f64 {
        f64::from_bits(self.global_time_bits())
    }
}

#[derive(Debug)]
pub struct SpacePacket<B: ByteSlice> {
    pub primary_header: LayoutVerified<B, PrimaryHeader>,
    pub secondary_header: LayoutVerified<B, SecondaryHeader>,
    pub user_data: B,
}

impl<B> SpacePacket<B>
where
    B: ByteSlice,
{
    pub fn from_generic(generic: ccsds::SpacePacket<B>) -> Option<Self> {
        let (secondary_header, user_data) =
            LayoutVerified::<_, SecondaryHeader>::new_unaligned_from_prefix(generic.packet_data)?;
        Some(Self {
            primary_header: generic.primary_header,
            secondary_header,
            user_data,
        })
    }
}