added distinction between CCSDS packet and user data #191

Open
muellerr wants to merge 1 commits from ccsds-reader-user-data into main
2 changed files with 55 additions and 14 deletions

View File

@@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
## Changed
- Added distinction between `CcsdsPacketReader::user_data` and `CcsdsPacketReader::packet_data`.
# [v0.17.0] 2025-11-06 # [v0.17.0] 2025-11-06
## Changed ## Changed

View File

@@ -1528,12 +1528,19 @@ pub enum CcsdsPacketReadError {
} }
/// CCSDS packet reader structure. /// CCSDS packet reader structure.
///
/// This implementation makes one assumption about the passed data: It allows verifying a
/// CRC16-CCITT checksum at the last two bytes of the packet data field and excluding it from the
/// [Self::packet_data] slice. For that purpose, it makes a distinction between the full
/// [Self::packet_data] including the checksum, and [Self::user_data] which does not include
/// the checksum.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CcsdsPacketReader<'buf> { pub struct CcsdsPacketReader<'buf> {
sp_header: SpHeader, sp_header: SpHeader,
packet_data: &'buf [u8], packet_data: &'buf [u8],
checksum: Option<u16>,
} }
impl<'buf> CcsdsPacketReader<'buf> { impl<'buf> CcsdsPacketReader<'buf> {
@@ -1550,7 +1557,7 @@ impl<'buf> CcsdsPacketReader<'buf> {
/// Generic constructor. /// Generic constructor.
pub fn new( pub fn new(
buf: &'buf [u8], buf: &'buf [u8],
checksum: Option<ChecksumType>, checksum_type: Option<ChecksumType>,
) -> Result<Self, CcsdsPacketReadError> { ) -> Result<Self, CcsdsPacketReadError> {
let sp_header = SpHeader::from_be_bytes(&buf[0..CCSDS_HEADER_LEN])?.0; let sp_header = SpHeader::from_be_bytes(&buf[0..CCSDS_HEADER_LEN])?.0;
if sp_header.packet_len() > buf.len() { if sp_header.packet_len() > buf.len() {
@@ -1560,21 +1567,26 @@ impl<'buf> CcsdsPacketReader<'buf> {
} }
.into()); .into());
} }
let user_data = match checksum { let checksum = match checksum_type {
Some(ChecksumType::WithCrc16) => { Some(ChecksumType::WithCrc16) => {
if CRC_CCITT_FALSE.checksum(&buf[0..sp_header.packet_len()]) != 0 { if CRC_CCITT_FALSE.checksum(&buf[0..sp_header.packet_len()]) != 0 {
return Err(CcsdsPacketReadError::CrcError); return Err(CcsdsPacketReadError::CrcError);
} }
&buf[CCSDS_HEADER_LEN..sp_header.packet_len() - 2] Some(u16::from_be_bytes([
buf[sp_header.packet_len() - 2],
buf[sp_header.packet_len() - 1],
]))
} }
Some(ChecksumType::WithCrc16ButIgnored) => { Some(ChecksumType::WithCrc16ButIgnored) => Some(u16::from_be_bytes([
&buf[CCSDS_HEADER_LEN..sp_header.packet_len() - 2] buf[sp_header.packet_len() - 2],
} buf[sp_header.packet_len() - 1],
None => &buf[CCSDS_HEADER_LEN..sp_header.packet_len()], ])),
None => None,
}; };
Ok(Self { Ok(Self {
sp_header, sp_header,
packet_data: user_data, packet_data: &buf[CCSDS_HEADER_LEN..sp_header.packet_len()],
checksum,
}) })
} }
} }
@@ -1598,12 +1610,27 @@ impl CcsdsPacketReader<'_> {
self.sp_header.packet_id.packet_type self.sp_header.packet_id.packet_type
} }
/// Read-only access to the packet data field. /// Read-only access to the full packet data field.
///
/// This might also include the checksum. [Self::user_data] can be used to only retrieve the
/// user data slice without the checksum part.
#[inline] #[inline]
pub fn packet_data(&self) -> &[u8] { pub fn packet_data(&self) -> &[u8] {
self.packet_data self.packet_data
} }
/// Read-only access to the user data field.
///
/// This is the data without the checksum, if the packet has one.
#[inline]
pub fn user_data(&self) -> &[u8] {
if self.checksum.is_some() {
self.packet_data()[0..self.packet_data().len() - 2].as_ref()
} else {
self.packet_data()
}
}
/// 11-bit Application Process ID field. /// 11-bit Application Process ID field.
#[inline] #[inline]
pub fn apid(&self) -> u11 { pub fn apid(&self) -> u11 {
@@ -1633,6 +1660,12 @@ impl CcsdsPacketReader<'_> {
pub fn data_len(&self) -> u16 { pub fn data_len(&self) -> u16 {
self.sp_header.data_len() self.sp_header.data_len()
} }
/// Packet checksum if available.
#[inline]
pub fn checksum(&self) -> Option<u16> {
self.checksum
}
} }
impl CcsdsPacket for CcsdsPacketReader<'_> { impl CcsdsPacket for CcsdsPacketReader<'_> {
@@ -2211,7 +2244,7 @@ pub(crate) mod tests {
let reader = let reader =
CcsdsPacketReader::new(&buf[0..13], Some(ChecksumType::WithCrc16ButIgnored)).unwrap(); CcsdsPacketReader::new(&buf[0..13], Some(ChecksumType::WithCrc16ButIgnored)).unwrap();
// Enforced 1 byte packet length. // Enforced 1 byte packet length.
assert_eq!(reader.packet_data(), &data); assert_eq!(reader.user_data(), &data);
assert_eq!(reader.packet_len(), 13); assert_eq!(reader.packet_len(), 13);
} }
@@ -2415,7 +2448,7 @@ pub(crate) mod tests {
.to_vec(); .to_vec();
let reader = let reader =
CcsdsPacketReader::new(&packet_raw, Some(ChecksumType::WithCrc16ButIgnored)).unwrap(); CcsdsPacketReader::new(&packet_raw, Some(ChecksumType::WithCrc16ButIgnored)).unwrap();
assert_eq!(reader.packet_data(), data); assert_eq!(reader.user_data(), data);
} }
fn generic_test_creator(packet_raw: &[u8], sp_header: &SpHeader, packet_type: PacketType) { fn generic_test_creator(packet_raw: &[u8], sp_header: &SpHeader, packet_type: PacketType) {
@@ -2482,7 +2515,7 @@ pub(crate) mod tests {
} }
fn generic_ccsds_reader_test( fn generic_ccsds_reader_test(
packet_data: &[u8], user_data: &[u8],
packet_raw: &[u8], packet_raw: &[u8],
packet_type: PacketType, packet_type: PacketType,
sp_header: SpHeader, sp_header: SpHeader,
@@ -2493,7 +2526,11 @@ pub(crate) mod tests {
); );
let reader = CcsdsPacketReader::new_with_checksum(packet_raw).unwrap(); let reader = CcsdsPacketReader::new_with_checksum(packet_raw).unwrap();
assert_eq!(*reader.sp_header(), sp_header); assert_eq!(*reader.sp_header(), sp_header);
assert_eq!(reader.packet_data(), packet_data); assert_eq!(
&reader.packet_data()[0..reader.packet_data().len() - 2],
user_data
);
assert_eq!(reader.user_data(), user_data);
assert_eq!(reader.apid(), u11::new(0x1)); assert_eq!(reader.apid(), u11::new(0x1));
assert_eq!( assert_eq!(
reader.packet_id(), reader.packet_id(),
@@ -2704,7 +2741,7 @@ pub(crate) mod tests {
*packet_raw.last_mut().unwrap() = 0; *packet_raw.last_mut().unwrap() = 0;
let reader = let reader =
CcsdsPacketReader::new(&packet_raw, Some(ChecksumType::WithCrc16ButIgnored)).unwrap(); CcsdsPacketReader::new(&packet_raw, Some(ChecksumType::WithCrc16ButIgnored)).unwrap();
assert_eq!(reader.packet_data(), data); assert_eq!(reader.user_data(), data);
} }
#[test] #[test]