Skip to main content

leodos_protocols/coding/
crc.rs

1//! CCSDS Space Packet Protocol with CRC-16 Support
2//!
3//! Spec: https://ccsds.org/Pubs/232x0b4e1c1.pdf
4
5use crate::network::spp::Apid;
6use crate::network::spp::BuildError;
7use crate::network::spp::PacketType;
8use crate::network::spp::PacketVersion;
9use crate::network::spp::PrimaryHeader;
10use crate::network::spp::SequenceCount;
11use crate::network::spp::SpacePacket;
12use crate::network::spp::SpacePacketData;
13
14use core::fmt::Display;
15use core::mem::size_of;
16use core::ops::Deref;
17use core::ops::DerefMut;
18use zerocopy::FromBytes;
19use zerocopy::IntoBytes;
20use zerocopy::byteorder::network_endian::U16;
21
22/// A wrapper around a `SpacePacket` that automatically manages
23/// a trailing CRC-16 checksum.
24pub struct CrcSpacePacket<'a, 'b> {
25    packet: &'a mut SpacePacket,
26    crc_bytes: &'a mut [u8],
27    crc_alg: &'b crc::Crc<u16>,
28}
29
30/// An error that can occur during CRC-aware Space Packet construction.
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
32pub enum BuilderError {
33    /// The underlying Space Packet build failed.
34    Spec(BuildError),
35    /// The buffer is too small to hold the packet and its CRC.
36    BufferTooSmall {
37        /// Minimum number of bytes needed.
38        required: usize,
39        /// Actual buffer size provided.
40        provided: usize,
41    },
42}
43
44impl Display for BuilderError {
45    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
46        match self {
47            BuilderError::Spec(e) => write!(f, "Specification error: {e}"),
48            BuilderError::BufferTooSmall { required, provided } => {
49                write!(
50                    f,
51                    "Buffer too small for CRC packet: required {required}, provided {provided}"
52                )
53            }
54        }
55    }
56}
57
58/// An error related to CRC operations.
59#[derive(Copy, Clone, Eq, PartialEq, Debug)]
60pub enum CrcError {
61    /// An error occurred during the underlying packet build.
62    Build(BuilderError),
63    /// The buffer was too small to hold the packet and its CRC.
64    BufferTooSmall {
65        /// Minimum number of bytes needed.
66        required: usize,
67        /// Actual buffer size provided.
68        provided: usize,
69    },
70    /// The calculated CRC did not match the one in the buffer.
71    ValidationFailed {
72        /// CRC value stored in the packet.
73        expected: u16,
74        /// CRC value recomputed from the packet contents.
75        calculated: u16,
76    },
77    /// An error occurred parsing the underlying data field.
78    DataField(crate::network::spp::DataFieldError),
79}
80
81impl core::fmt::Display for CrcError {
82    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83        match self {
84            CrcError::Build(e) => write!(f, "Build error: {e}"),
85            CrcError::BufferTooSmall { required, provided } => {
86                write!(
87                    f,
88                    "Buffer too small for CRC packet: required {required}, provided {provided}"
89                )
90            }
91            CrcError::ValidationFailed {
92                expected,
93                calculated,
94            } => {
95                write!(
96                    f,
97                    "CRC validation failed: expected {expected:#06X}, calculated {calculated:#06X}"
98                )
99            }
100            CrcError::DataField(e) => write!(f, "Data field error: {e}"),
101        }
102    }
103}
104
105impl From<BuilderError> for CrcError {
106    fn from(e: BuilderError) -> Self {
107        CrcError::Build(e)
108    }
109}
110impl From<crate::network::spp::DataFieldError> for CrcError {
111    fn from(e: crate::network::spp::DataFieldError) -> Self {
112        CrcError::DataField(e)
113    }
114}
115
116impl<'a, 'b> CrcSpacePacket<'a, 'b> {
117    /// Creates a new CRC-protected Space Packet in the provided buffer.
118    pub fn new(
119        buffer: &'a mut [u8],
120        apid: Apid,
121        packet_type: PacketType,
122        sequence_count: SequenceCount,
123        secondary_header_flag: crate::network::spp::SecondaryHeaderFlag,
124        sequence_flag: crate::network::spp::SequenceFlag,
125        data_field_len: u16,
126        crc_alg: &'b crc::Crc<u16>,
127    ) -> Result<CrcSpacePacket<'a, 'b>, CrcError> {
128        let required_len = size_of::<PrimaryHeader>() + data_field_len as usize;
129        if required_len + 2 > buffer.len() {
130            return Err(CrcError::BufferTooSmall {
131                required: required_len + 2,
132                provided: buffer.len(),
133            });
134        }
135
136        let (packet_buf, crc_buf) = buffer[..required_len + 2].split_at_mut(required_len);
137        let packet = SpacePacket::mut_from_bytes(packet_buf).unwrap();
138
139        // Build the header
140        packet.set_version(PacketVersion::VERSION_1);
141        packet.set_packet_type(packet_type);
142        packet.set_apid(apid);
143        packet.set_sequence_count(sequence_count);
144        packet.set_data_field_len(data_field_len);
145        packet.set_secondary_header_flag(secondary_header_flag);
146        packet.set_sequence_flag(sequence_flag);
147
148        // Create the wrapper
149        let mut crc_packet = CrcSpacePacket {
150            packet,
151            crc_bytes: crc_buf,
152            crc_alg: crc_alg,
153        };
154
155        // Set the initial CRC
156        crc_packet.update_crc();
157
158        Ok(crc_packet)
159    }
160    /// Writes data to the packet's data field and automatically updates the CRC.
161    ///
162    /// This is the safe, CRC-aware way to set the packet's payload.
163    pub fn set_data<T: SpacePacketData>(
164        &mut self,
165        data: &T,
166    ) -> Result<(), crate::network::spp::DataFieldError> {
167        self.packet.set_data_field(data)?;
168        self.update_crc();
169        Ok(())
170    }
171
172    /// Validates the CRC and returns a typed, zero-copy view of the data field.
173    pub fn data_as<T: SpacePacketData>(&self) -> Result<&T, CrcError> {
174        self.validate()?;
175        Ok(self.packet.data_as::<T>()?)
176    }
177
178    /// Validates the CRC and returns an immutable slice of the data field.
179    pub fn data(&self) -> Result<&[u8], CrcError> {
180        self.validate()?;
181        Ok(self.packet.data_field())
182    }
183
184    /// Validates the current CRC against the packet's contents.
185    pub fn validate(&self) -> Result<(), CrcError> {
186        let expected = U16::read_from_bytes(self.crc_bytes).unwrap().get();
187        let calculated = self.crc_alg.checksum(self.packet.as_bytes());
188        if expected == calculated {
189            Ok(())
190        } else {
191            Err(CrcError::ValidationFailed {
192                expected,
193                calculated,
194            })
195        }
196    }
197
198    /// Forces a recalculation and update of the CRC value.
199    /// This is called automatically by `set_data_field`.
200    pub fn update_crc(&mut self) {
201        let calculated = self.crc_alg.checksum(self.packet.as_bytes());
202        U16::new(calculated)
203            .write_to_prefix(self.crc_bytes)
204            .unwrap();
205    }
206}
207
208// Allow direct access to the underlying SpacePacket header fields (e.g., `crc_packet.apid()`).
209impl<'a, 'b> Deref for CrcSpacePacket<'a, 'b> {
210    type Target = SpacePacket;
211    fn deref(&self) -> &Self::Target {
212        self.packet
213    }
214}
215impl<'a, 'b> DerefMut for CrcSpacePacket<'a, 'b> {
216    fn deref_mut(&mut self) -> &mut Self::Target {
217        self.packet
218    }
219}