start adding tests

This commit is contained in:
Robin Mueller
2025-10-29 13:10:37 +01:00
parent 5b0a24eea4
commit dc27edf7ac
4 changed files with 182 additions and 43 deletions

View File

@@ -1,3 +1,7 @@
//! # CRC checksum support.
//!
//! Thin wrapper around the [crc] crate.
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using /// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard, using
/// a [crc::NoTable] as the CRC implementation. /// a [crc::NoTable] as the CRC implementation.
pub const CRC_CCITT_FALSE_NO_TABLE: crc::Crc<u16, crc::NoTable> = pub const CRC_CCITT_FALSE_NO_TABLE: crc::Crc<u16, crc::NoTable> =

View File

@@ -127,6 +127,7 @@ pub enum ZeroCopyError {
ZeroCopyFromError, ZeroCopyFromError,
} }
/// Invalid payload length which is bounded by [u16::MAX]
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, 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))]
@@ -171,21 +172,33 @@ pub fn packet_type_in_raw_packet_id(packet_id: u16) -> PacketType {
PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap() PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap()
} }
/// Calculate the full CCSDS packet length for a given user data length and optional checksum type.
///
/// Returns [None] if the calculated length allowed by the CCSDS data length field.
#[inline] #[inline]
pub fn ccsds_packet_len_for_user_data_len( pub const fn ccsds_packet_len_for_user_data_len(
data_len: usize, data_len: usize,
checksum: Option<ChecksumType>, checksum: Option<ChecksumType>,
) -> usize { ) -> Option<usize> {
data_len // Special case: A value of zero is not allowed for the data length field.
+ CCSDS_HEADER_LEN if data_len == 0 {
+ match checksum { return Some(7);
}
let checksum_len = match checksum {
Some(ChecksumType::Crc16CcittFalse) => 2, Some(ChecksumType::Crc16CcittFalse) => 2,
None => 0, None => 0,
};
let len = data_len
.saturating_add(CCSDS_HEADER_LEN)
.saturating_add(checksum_len);
if len - CCSDS_HEADER_LEN - 1 > u16::MAX as usize {
return None;
} }
Some(len)
} }
#[inline] #[inline]
pub fn packet_len_for_user_data_len_with_checksum(data_len: usize) -> usize { pub fn packet_len_for_user_data_len_with_checksum(data_len: usize) -> Option<usize> {
ccsds_packet_len_for_user_data_len(data_len, Some(ChecksumType::Crc16CcittFalse)) ccsds_packet_len_for_user_data_len(data_len, Some(ChecksumType::Crc16CcittFalse))
} }
@@ -267,6 +280,7 @@ impl PacketId {
self.apid = apid; self.apid = apid;
} }
/// 11-bit CCSDS Application Process ID (APID) field.
#[inline] #[inline]
pub const fn apid(&self) -> u11 { pub const fn apid(&self) -> u11 {
self.apid self.apid
@@ -447,16 +461,16 @@ pub trait CcsdsPrimaryHeader {
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpHeader { pub struct SpacePacketHeader {
pub version: u3, pub version: u3,
pub packet_id: PacketId, pub packet_id: PacketId,
pub psc: PacketSequenceControl, pub psc: PacketSequenceControl,
pub data_len: u16, pub data_len: u16,
} }
pub type SpacePacketHeader = SpHeader; pub type SpHeader = SpacePacketHeader;
impl Default for SpHeader { impl Default for SpacePacketHeader {
/// The default function sets the sequence flag field to [SequenceFlags::Unsegmented] and the /// The default function sets the sequence flag field to [SequenceFlags::Unsegmented] and the
/// data length to 0. /// data length to 0.
#[inline] #[inline]
@@ -473,8 +487,8 @@ impl Default for SpHeader {
} }
} }
impl SpHeader { impl SpacePacketHeader {
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN; pub const LENGTH: usize = CCSDS_HEADER_LEN;
#[inline] #[inline]
pub const fn new(packet_id: PacketId, psc: PacketSequenceControl, data_len: u16) -> Self { pub const fn new(packet_id: PacketId, psc: PacketSequenceControl, data_len: u16) -> Self {
@@ -490,7 +504,7 @@ impl SpHeader {
/// length to 0. /// length to 0.
#[inline] #[inline]
pub const fn new_from_apid(apid: u11) -> Self { pub const fn new_from_apid(apid: u11) -> Self {
SpHeader { Self {
version: u3::new(0b000), version: u3::new(0b000),
packet_id: PacketId::new(PacketType::Tm, false, apid), packet_id: PacketId::new(PacketType::Tm, false, apid),
psc: PacketSequenceControl { psc: PacketSequenceControl {
@@ -560,7 +574,7 @@ impl SpHeader {
/// Retrieve the total packet size based on the data length field /// Retrieve the total packet size based on the data length field
#[inline] #[inline]
pub fn packet_len(&self) -> usize { pub fn packet_len(&self) -> usize {
usize::from(self.data_len()) + Self::HEADER_LEN + 1 usize::from(self.data_len()) + Self::LENGTH + 1
} }
#[inline] #[inline]
@@ -592,15 +606,15 @@ impl SpHeader {
/// This function also returns the remaining part of the passed slice starting past the read /// This function also returns the remaining part of the passed slice starting past the read
/// CCSDS header. /// CCSDS header.
pub fn from_be_bytes(buf: &[u8]) -> Result<(Self, &[u8]), ByteConversionError> { pub fn from_be_bytes(buf: &[u8]) -> Result<(Self, &[u8]), ByteConversionError> {
if buf.len() < Self::HEADER_LEN { if buf.len() < Self::LENGTH {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
found: buf.len(), found: buf.len(),
expected: CCSDS_HEADER_LEN, expected: CCSDS_HEADER_LEN,
}); });
} }
// Unwrap okay, this can not fail. // Unwrap okay, this can not fail.
let zc_header = zc::SpHeader::read_from_bytes(&buf[0..Self::HEADER_LEN]).unwrap(); let zc_header = zc::SpHeader::read_from_bytes(&buf[0..Self::LENGTH]).unwrap();
Ok((Self::from(zc_header), &buf[Self::HEADER_LEN..])) Ok((Self::from(zc_header), &buf[Self::LENGTH..]))
} }
/// Write the header to a raw buffer using big endian format. This function returns the /// Write the header to a raw buffer using big endian format. This function returns the
@@ -609,7 +623,7 @@ impl SpHeader {
&self, &self,
buf: &'a mut [u8], buf: &'a mut [u8],
) -> Result<&'a mut [u8], ByteConversionError> { ) -> Result<&'a mut [u8], ByteConversionError> {
if buf.len() < Self::HEADER_LEN { if buf.len() < Self::LENGTH {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
found: buf.len(), found: buf.len(),
expected: CCSDS_HEADER_LEN, expected: CCSDS_HEADER_LEN,
@@ -617,48 +631,53 @@ impl SpHeader {
} }
let zc_header: zc::SpHeader = zc::SpHeader::from(*self); let zc_header: zc::SpHeader = zc::SpHeader::from(*self);
// Unwrap okay, this can not fail. // Unwrap okay, this can not fail.
zc_header.write_to(&mut buf[0..Self::HEADER_LEN]).unwrap(); zc_header.write_to(&mut buf[0..Self::LENGTH]).unwrap();
Ok(&mut buf[Self::HEADER_LEN..]) Ok(&mut buf[Self::LENGTH..])
} }
/// Create a vector containing the CCSDS header. /// Create a vector containing the CCSDS header.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_vec(&self) -> alloc::vec::Vec<u8> { pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
let mut vec = alloc::vec![0; Self::HEADER_LEN]; let mut vec = alloc::vec![0; Self::LENGTH];
// This can not fail. // This can not fail.
self.write_to_be_bytes(&mut vec[..]).unwrap(); self.write_to_be_bytes(&mut vec[..]).unwrap();
vec vec
} }
} }
impl CcsdsPacket for SpHeader { impl CcsdsPacket for SpacePacketHeader {
/// CCSDS version field.
#[inline] #[inline]
fn ccsds_version(&self) -> u3 { fn ccsds_version(&self) -> u3 {
self.version self.version
} }
/// Full packet length.
#[inline] #[inline]
fn packet_len(&self) -> usize { fn packet_len(&self) -> usize {
self.packet_len() self.packet_len()
} }
/// CCSDS packet ID field.
#[inline] #[inline]
fn packet_id(&self) -> PacketId { fn packet_id(&self) -> PacketId {
self.packet_id self.packet_id
} }
/// CCSDS packet sequence control.
#[inline] #[inline]
fn psc(&self) -> PacketSequenceControl { fn psc(&self) -> PacketSequenceControl {
self.psc self.psc
} }
/// CCSDS data length field.
#[inline] #[inline]
fn data_len(&self) -> u16 { fn data_len(&self) -> u16 {
self.data_len self.data_len
} }
} }
impl CcsdsPrimaryHeader for SpHeader { impl CcsdsPrimaryHeader for SpacePacketHeader {
#[inline] #[inline]
fn from_composite_fields( fn from_composite_fields(
packet_id: PacketId, packet_id: PacketId,
@@ -716,21 +735,25 @@ pub mod zc {
} }
impl CcsdsPacket for SpHeader { impl CcsdsPacket for SpHeader {
/// CCSDS version field.
#[inline] #[inline]
fn ccsds_version(&self) -> u3 { fn ccsds_version(&self) -> u3 {
u3::new(((self.version_packet_id.get() >> 13) as u8) & 0b111) u3::new(((self.version_packet_id.get() >> 13) as u8) & 0b111)
} }
/// CCSDS packet ID field.
#[inline] #[inline]
fn packet_id(&self) -> PacketId { fn packet_id(&self) -> PacketId {
PacketId::from(self.packet_id_raw()) PacketId::from(self.packet_id_raw())
} }
/// CCSDS packet sequence control field.
#[inline] #[inline]
fn psc(&self) -> PacketSequenceControl { fn psc(&self) -> PacketSequenceControl {
PacketSequenceControl::from(self.psc_raw()) PacketSequenceControl::from(self.psc_raw())
} }
/// CCSDS data length field.
#[inline] #[inline]
fn data_len(&self) -> u16 { fn data_len(&self) -> u16 {
self.data_len.get() self.data_len.get()
@@ -768,6 +791,8 @@ pub mod zc {
/// by the user and then provides mutable or shared access to that memory. This is useful /// by the user and then provides mutable or shared access to that memory. This is useful
/// to avoid an additional slice for the user data and allow copying data directly /// to avoid an additional slice for the user data and allow copying data directly
/// into the packet. /// into the packet.
///
/// Please note that packet creation has to be completed using the [Self::finish] call.
pub struct CcsdsPacketCreatorWithReservedData<'buf> { pub struct CcsdsPacketCreatorWithReservedData<'buf> {
sp_header: SpHeader, sp_header: SpHeader,
buf: &'buf mut [u8], buf: &'buf mut [u8],
@@ -775,23 +800,32 @@ pub struct CcsdsPacketCreatorWithReservedData<'buf> {
} }
impl<'buf> CcsdsPacketCreatorWithReservedData<'buf> { impl<'buf> CcsdsPacketCreatorWithReservedData<'buf> {
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
#[inline] #[inline]
pub fn packet_len_for_user_data_with_checksum(user_data_len: usize) -> usize { pub fn packet_len_for_user_data_with_checksum(user_data_len: usize) -> Option<usize> {
ccsds_packet_len_for_user_data_len(user_data_len, Some(ChecksumType::Crc16CcittFalse)) ccsds_packet_len_for_user_data_len(user_data_len, Some(ChecksumType::Crc16CcittFalse))
} }
pub fn new( pub fn new(
mut sp_header: SpHeader, mut sp_header: SpacePacketHeader,
packet_type: PacketType, packet_type: PacketType,
payload_len: usize, packet_data_len: usize,
buf: &'buf mut [u8], buf: &'buf mut [u8],
checksum: Option<ChecksumType>, checksum: Option<ChecksumType>,
) -> Result<Self, CcsdsPacketCreationError> { ) -> Result<Self, CcsdsPacketCreationError> {
let full_packet_len = match checksum { let full_packet_len = match checksum {
Some(crc_type) => match crc_type { Some(crc_type) => match crc_type {
ChecksumType::Crc16CcittFalse => CCSDS_HEADER_LEN + payload_len + 2, ChecksumType::Crc16CcittFalse => CCSDS_HEADER_LEN + packet_data_len + 2,
}, },
None => CCSDS_HEADER_LEN + payload_len, None => {
// Special case: At least one byte of user data is required.
if packet_data_len == 0 {
CCSDS_HEADER_LEN + 1
} else {
CCSDS_HEADER_LEN + packet_data_len
}
}
}; };
if full_packet_len > buf.len() { if full_packet_len > buf.len() {
return Err(ByteConversionError::ToSliceTooSmall { return Err(ByteConversionError::ToSliceTooSmall {
@@ -801,7 +835,7 @@ impl<'buf> CcsdsPacketCreatorWithReservedData<'buf> {
.into()); .into());
} }
if full_packet_len - CCSDS_HEADER_LEN - 1 > u16::MAX as usize { if full_packet_len - CCSDS_HEADER_LEN - 1 > u16::MAX as usize {
return Err(InvalidPayloadLengthError(payload_len).into()); return Err(InvalidPayloadLengthError(packet_data_len).into());
} }
sp_header.data_len = (full_packet_len - CCSDS_HEADER_LEN - 1) as u16; sp_header.data_len = (full_packet_len - CCSDS_HEADER_LEN - 1) as u16;
sp_header.packet_id.packet_type = packet_type; sp_header.packet_id.packet_type = packet_type;
@@ -863,23 +897,36 @@ impl CcsdsPacketCreatorWithReservedData<'_> {
<Self as CcsdsPacket>::packet_len(self) <Self as CcsdsPacket>::packet_len(self)
} }
/// Space pacekt header.
#[inline] #[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
/// Mutable access to the packet data field.
#[inline] #[inline]
pub fn packet_data_mut(&mut self) -> &mut [u8] { pub fn packet_data_mut(&mut self) -> &mut [u8] {
let len = self.buf.len(); let len = self.buf.len();
&mut self.buf[CCSDS_HEADER_LEN..len - 2] match self.checksum {
Some(ChecksumType::Crc16CcittFalse) => &mut self.buf[CCSDS_HEADER_LEN..len - 2],
None => &mut self.buf[CCSDS_HEADER_LEN..len],
}
} }
/// Read-only access to the packet data field.
#[inline] #[inline]
pub fn packet_data(&mut self) -> &[u8] { pub fn packet_data(&mut self) -> &[u8] {
let len = self.buf.len(); let len = self.buf.len();
&self.buf[CCSDS_HEADER_LEN..len - 2] match self.checksum {
Some(ChecksumType::Crc16CcittFalse) => &self.buf[CCSDS_HEADER_LEN..len - 2],
None => &self.buf[CCSDS_HEADER_LEN..len],
}
} }
/// Finish the packet generation process.
///
/// This packet writes the space packet header. It also calculates and appends the CRC
/// checksum when configured to do so.
pub fn finish(self) -> usize { pub fn finish(self) -> usize {
self.sp_header self.sp_header
.write_to_be_bytes(&mut self.buf[0..CCSDS_HEADER_LEN]) .write_to_be_bytes(&mut self.buf[0..CCSDS_HEADER_LEN])
@@ -897,21 +944,25 @@ impl CcsdsPacketCreatorWithReservedData<'_> {
} }
impl CcsdsPacket for CcsdsPacketCreatorWithReservedData<'_> { impl CcsdsPacket for CcsdsPacketCreatorWithReservedData<'_> {
/// CCSDS version field.
#[inline] #[inline]
fn ccsds_version(&self) -> arbitrary_int::u3 { fn ccsds_version(&self) -> arbitrary_int::u3 {
self.sp_header.ccsds_version() self.sp_header.ccsds_version()
} }
/// CCSDS packet ID field.
#[inline] #[inline]
fn packet_id(&self) -> PacketId { fn packet_id(&self) -> PacketId {
self.sp_header.packet_id() self.sp_header.packet_id()
} }
/// CCSDS packet sequence control field.
#[inline] #[inline]
fn psc(&self) -> PacketSequenceControl { fn psc(&self) -> PacketSequenceControl {
self.sp_header.psc() self.sp_header.psc()
} }
/// CCSDS data length field.
#[inline] #[inline]
fn data_len(&self) -> u16 { fn data_len(&self) -> u16 {
self.sp_header.data_len() self.sp_header.data_len()
@@ -930,8 +981,12 @@ pub struct CcsdsPacketCreator<'app_data> {
} }
impl<'app_data> CcsdsPacketCreator<'app_data> { impl<'app_data> CcsdsPacketCreator<'app_data> {
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
/// Helper function which can be used to determine the full packet length from the user
/// data length, assuming there is a CRC16 appended at the packet.
#[inline] #[inline]
pub fn packet_len_for_user_data_with_checksum(user_data_len: usize) -> usize { pub fn packet_len_for_user_data_with_checksum(user_data_len: usize) -> Option<usize> {
ccsds_packet_len_for_user_data_len(user_data_len, Some(ChecksumType::Crc16CcittFalse)) ccsds_packet_len_for_user_data_len(user_data_len, Some(ChecksumType::Crc16CcittFalse))
} }
@@ -947,12 +1002,18 @@ impl<'app_data> CcsdsPacketCreator<'app_data> {
None => 0, None => 0,
} }
- 1) as u16; - 1) as u16;
let full_packet_len = match checksum { let full_packet_len = match checksum {
Some(crc_type) => match crc_type { Some(crc_type) => match crc_type {
ChecksumType::Crc16CcittFalse => CCSDS_HEADER_LEN + packet_data.len() + 2, ChecksumType::Crc16CcittFalse => CCSDS_HEADER_LEN + packet_data.len() + 2,
}, },
None => CCSDS_HEADER_LEN + packet_data.len(), None => {
// Special case: At least one byte of user data is required.
if packet_data.is_empty() {
CCSDS_HEADER_LEN + 1
} else {
CCSDS_HEADER_LEN + packet_data.len()
}
}
}; };
if full_packet_len - CCSDS_HEADER_LEN - 1 > u16::MAX as usize { if full_packet_len - CCSDS_HEADER_LEN - 1 > u16::MAX as usize {
return Err(InvalidPayloadLengthError(packet_data.len()).into()); return Err(InvalidPayloadLengthError(packet_data.len()).into());
@@ -981,10 +1042,12 @@ impl<'app_data> CcsdsPacketCreator<'app_data> {
} }
impl CcsdsPacketCreator<'_> { impl CcsdsPacketCreator<'_> {
/// Full length when written to bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
ccsds_packet_len_for_user_data_len(self.packet_data.len(), self.checksum) ccsds_packet_len_for_user_data_len(self.packet_data.len(), self.checksum).unwrap()
} }
/// Write the CCSDS packet to the provided buffer.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
let len_written = self.len_written(); let len_written = self.len_written();
if len_written > buf.len() { if len_written > buf.len() {
@@ -1007,6 +1070,7 @@ impl CcsdsPacketCreator<'_> {
Ok(len_written) Ok(len_written)
} }
/// Create a CCSDS packet as a vector.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_vec(&self) -> alloc::vec::Vec<u8> { pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
let mut vec = alloc::vec![0u8; self.len_written()]; let mut vec = alloc::vec![0u8; self.len_written()];
@@ -1036,6 +1100,8 @@ pub struct CcsdsPacketReader<'buf> {
} }
impl<'buf> CcsdsPacketReader<'buf> { impl<'buf> CcsdsPacketReader<'buf> {
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
pub fn new_with_checksum( pub fn new_with_checksum(
buf: &'buf [u8], buf: &'buf [u8],
) -> Result<CcsdsPacketReader<'buf>, CcsdsPacketReadError> { ) -> Result<CcsdsPacketReader<'buf>, CcsdsPacketReadError> {
@@ -1046,9 +1112,7 @@ impl<'buf> CcsdsPacketReader<'buf> {
buf: &'buf [u8], buf: &'buf [u8],
checksum: Option<ChecksumType>, checksum: Option<ChecksumType>,
) -> Result<Self, CcsdsPacketReadError> { ) -> Result<Self, CcsdsPacketReadError> {
let sp_header = SpHeader::from_be_bytes(&buf[0..CCSDS_HEADER_LEN]) let sp_header = SpHeader::from_be_bytes(&buf[0..CCSDS_HEADER_LEN])?.0;
.unwrap()
.0;
if sp_header.packet_len() > buf.len() { if sp_header.packet_len() > buf.len() {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
found: sp_header.packet_len(), found: sp_header.packet_len(),
@@ -1073,36 +1137,43 @@ impl<'buf> CcsdsPacketReader<'buf> {
} }
impl CcsdsPacketReader<'_> { impl CcsdsPacketReader<'_> {
/// Space pacekt header.
#[inline] #[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
/// Read-only access to the packet data field.
#[inline] #[inline]
pub fn packet_data(&self) -> &[u8] { pub fn packet_data(&self) -> &[u8] {
self.packet_data self.packet_data
} }
/// 11-bit Application Process ID field.
#[inline] #[inline]
pub fn apid(&self) -> u11 { pub fn apid(&self) -> u11 {
self.sp_header.apid() self.sp_header.apid()
} }
/// CCSDS packet ID field.
#[inline] #[inline]
pub fn packet_id(&self) -> PacketId { pub fn packet_id(&self) -> PacketId {
self.sp_header.packet_id() self.sp_header.packet_id()
} }
/// Packet sequence control field.
#[inline] #[inline]
pub fn psc(&self) -> PacketSequenceControl { pub fn psc(&self) -> PacketSequenceControl {
self.sp_header.psc() self.sp_header.psc()
} }
/// Full packet length with the CCSDS header.
#[inline] #[inline]
pub fn packet_len(&self) -> usize { pub fn packet_len(&self) -> usize {
<Self as CcsdsPacket>::packet_len(self) <Self as CcsdsPacket>::packet_len(self)
} }
/// Packet data length field.
#[inline] #[inline]
pub fn data_len(&self) -> u16 { pub fn data_len(&self) -> u16 {
self.sp_header.data_len() self.sp_header.data_len()
@@ -1110,21 +1181,25 @@ impl CcsdsPacketReader<'_> {
} }
impl CcsdsPacket for CcsdsPacketReader<'_> { impl CcsdsPacket for CcsdsPacketReader<'_> {
/// CCSDS version field.
#[inline] #[inline]
fn ccsds_version(&self) -> arbitrary_int::u3 { fn ccsds_version(&self) -> arbitrary_int::u3 {
self.sp_header.ccsds_version() self.sp_header.ccsds_version()
} }
/// Packet ID field.
#[inline] #[inline]
fn packet_id(&self) -> PacketId { fn packet_id(&self) -> PacketId {
self.packet_id() self.packet_id()
} }
/// Packet sequence control field.
#[inline] #[inline]
fn psc(&self) -> PacketSequenceControl { fn psc(&self) -> PacketSequenceControl {
self.psc() self.psc()
} }
/// Packet data length without the CCSDS header.
#[inline] #[inline]
fn data_len(&self) -> u16 { fn data_len(&self) -> u16 {
self.data_len() self.data_len()
@@ -1140,7 +1215,9 @@ pub(crate) mod tests {
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::CcsdsPrimaryHeader; use crate::CcsdsPrimaryHeader;
use crate::{ use crate::{
packet_type_in_raw_packet_id, zc, CcsdsPacket, PacketId, PacketSequenceControl, PacketType, ccsds_packet_len_for_user_data_len, packet_type_in_raw_packet_id, zc, CcsdsPacket,
CcsdsPacketCreatorWithReservedData, ChecksumType, PacketId, PacketSequenceControl,
PacketType, SpacePacketHeader, CCSDS_HEADER_LEN,
}; };
use crate::{SequenceFlags, SpHeader}; use crate::{SequenceFlags, SpHeader};
use alloc::vec; use alloc::vec;
@@ -1483,5 +1560,58 @@ pub(crate) mod tests {
} }
#[test] #[test]
fn test_ccsds_size_function() {} fn test_ccsds_size_function() {
assert_eq!(ccsds_packet_len_for_user_data_len(1, None).unwrap(), 7);
// Special case: One dummy byte is required.
assert_eq!(ccsds_packet_len_for_user_data_len(0, None).unwrap(), 7);
assert_eq!(
ccsds_packet_len_for_user_data_len(1, Some(ChecksumType::Crc16CcittFalse)).unwrap(),
9
);
}
#[test]
fn test_ccsds_size_function_invalid_size_no_checksum() {
// This works, because the data field is the user data length minus 1.
assert!(ccsds_packet_len_for_user_data_len(u16::MAX as usize + 1, None).is_some());
// This does not work, data field length exceeded.
assert!(ccsds_packet_len_for_user_data_len(u16::MAX as usize + 2, None).is_none());
}
#[test]
fn test_ccsds_size_function_invalid_size_with_checksum() {
// 2 less bytes available because of the checksum.
assert!(ccsds_packet_len_for_user_data_len(
u16::MAX as usize - 1,
Some(ChecksumType::Crc16CcittFalse)
)
.is_some());
// This is too much.
assert!(ccsds_packet_len_for_user_data_len(
u16::MAX as usize,
Some(ChecksumType::Crc16CcittFalse)
)
.is_none());
}
#[test]
fn test_ccsds_creator_api() {
let mut buf: [u8; 32] = [0; 32];
let mut packet_creator = CcsdsPacketCreatorWithReservedData::new(
SpacePacketHeader::new_from_apid(u11::new(0x1)),
PacketType::Tc,
4,
&mut buf,
Some(ChecksumType::Crc16CcittFalse),
).unwrap();
assert_eq!(packet_creator.packet_len(), 12);
assert_eq!(packet_creator.data_len(), 5);
assert_eq!(packet_creator.apid().value(), 0x1);
assert_eq!(packet_creator.packet_data_mut(), &mut [0, 0, 0, 0]);
assert_eq!(packet_creator.packet_data(), &[0, 0, 0, 0]);
}
#[test]
fn test_ccsds_creator_creation() {
}
} }

View File

@@ -1,3 +1,8 @@
//! # Sequence counter module.
//!
//! CCSDS and ECSS packet standard oftentimes use sequence counters, for example to allow detecting
//! packet gaps. This module provides basic abstractions and helper components to implement
//! sequence counters.
use crate::MAX_SEQ_COUNT; use crate::MAX_SEQ_COUNT;
use arbitrary_int::traits::Integer; use arbitrary_int::traits::Integer;
use core::cell::Cell; use core::cell::Cell;

View File

@@ -1,4 +1,4 @@
/// # Support of the CCSDS Unified Space Data Link Protocol (USLP) //! # Support of the CCSDS Unified Space Data Link Protocol (USLP)
use crate::{crc::CRC_CCITT_FALSE, ByteConversionError}; use crate::{crc::CRC_CCITT_FALSE, ByteConversionError};
/// Only this version is supported by the library /// Only this version is supported by the library