start adding tests
This commit is contained in:
@@ -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> =
|
||||||
|
|||||||
214
src/lib.rs
214
src/lib.rs
@@ -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);
|
||||||
Some(ChecksumType::Crc16CcittFalse) => 2,
|
}
|
||||||
None => 0,
|
let checksum_len = match checksum {
|
||||||
}
|
Some(ChecksumType::Crc16CcittFalse) => 2,
|
||||||
|
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() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user