diff --git a/spacepackets/src/ecss.rs b/spacepackets/src/ecss.rs index dc2421a..e3e9084 100644 --- a/spacepackets/src/ecss.rs +++ b/spacepackets/src/ecss.rs @@ -1,9 +1,11 @@ -use crate::CcsdsPacket; +use crate::{CcsdsPacket, PacketError}; use crc::{Crc, CRC_16_IBM_3740}; use serde::{Deserialize, Serialize}; +use std::mem::size_of; /// CRC algorithm used by the PUS standard pub const CRC_CCITT_FALSE: Crc = Crc::::new(&CRC_16_IBM_3740); +pub const CCSDS_HEADER_LEN: usize = size_of::(); /// All PUS versions. Only PUS C is supported by this library #[derive(PartialEq, Copy, Clone, Serialize, Deserialize, Debug)] @@ -16,9 +18,17 @@ pub enum PusVersion { #[derive(Debug, Copy, Clone, PartialEq)] pub enum PusError { VersionNotSupported(PusVersion), + IncorrectCrc(u16), + RawDataTooShort(usize), + NoRawData, + /// CRC16 needs to be calculated first + CrcCalculationMissing, + OtherPacketError(PacketError), } pub trait PusPacket: CcsdsPacket { + const PUS_VERSION: PusVersion = PusVersion::PusC; + fn service(&self) -> u8; fn subservice(&self) -> u8; fn source_id(&self) -> u16; @@ -26,6 +36,4 @@ pub trait PusPacket: CcsdsPacket { fn user_data(&self) -> Option<&[u8]>; fn crc16(&self) -> Option; - /// Verify that the packet is valid. PUS packets have a CRC16 checksum to do this - fn verify(&mut self) -> bool; } diff --git a/spacepackets/src/lib.rs b/spacepackets/src/lib.rs index f36676c..811f847 100644 --- a/spacepackets/src/lib.rs +++ b/spacepackets/src/lib.rs @@ -1,7 +1,7 @@ //! # Space related components including CCSDS and ECSS packet standards -extern crate core; - +use crate::ecss::CCSDS_HEADER_LEN; use serde::{Deserialize, Serialize}; + pub mod ecss; pub mod tc; pub mod tm; @@ -12,8 +12,7 @@ pub enum PacketError { ToBytesSliceTooSmall(usize), /// The [zerocopy] library failed to write to bytes ToBytesZeroCopyError, - /// CRC16 needs to be calculated first - CrcCalculationMissing, + FromBytesZeroCopyError, } #[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] @@ -151,7 +150,7 @@ macro_rules! sph_from_other { other.packet_id(), other.psc(), other.data_len(), - Some(other.version()), + Some(other.ccsds_version()), ) } } @@ -163,12 +162,16 @@ const VERSION_MASK: u16 = 0xE000; /// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2 pub trait CcsdsPacket { - fn version(&self) -> u8; + fn ccsds_version(&self) -> u8; fn packet_id(&self) -> PacketId; fn psc(&self) -> PacketSequenceCtrl; /// Retrieve data length field fn data_len(&self) -> u16; + /// Retrieve the total packet size based on the data length field + fn total_len(&self) -> usize { + usize::from(self.data_len()) + CCSDS_HEADER_LEN + 1 + } /// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND /// of the first 2 bytes with 0x1FFF @@ -287,7 +290,7 @@ pub mod srd { impl CcsdsPacket for SpHeader { #[inline] - fn version(&self) -> u8 { + fn ccsds_version(&self) -> u8 { self.version } @@ -361,7 +364,7 @@ pub mod zc { } } - pub fn from_bytes(slice: impl AsRef<[u8]>) -> Option { + pub fn from_bytes(slice: &(impl AsRef<[u8]> + ?Sized)) -> Option { SpHeader::read_from(slice.as_ref()) } @@ -372,7 +375,7 @@ pub mod zc { impl CcsdsPacket for SpHeader { #[inline] - fn version(&self) -> u8 { + fn ccsds_version(&self) -> u8 { ((self.version_packet_id.get() >> 13) as u8) & 0b111 } @@ -472,7 +475,7 @@ mod tests { #[test] fn test_serde_sph() { let sp_header = SpHeader::tc(0x42, 12).expect("Error creating SP header"); - assert_eq!(sp_header.version(), 0b000); + assert_eq!(sp_header.ccsds_version(), 0b000); assert!(sp_header.is_tc()); assert!(sp_header.sec_header_flag()); assert_eq!(sp_header.ptype(), PacketType::Tc); @@ -490,12 +493,12 @@ mod tests { assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented); assert_eq!(sp_header.packet_id_raw(), 0x1842); assert_eq!(sp_header.psc_raw(), 0xC00C); - assert_eq!(sp_header.version(), 0b000); + assert_eq!(sp_header.ccsds_version(), 0b000); assert_eq!(sp_header.data_len, 0); let mut sp_header = SpHeader::tm(0x7, 22).expect("Error creating SP header"); sp_header.data_len = 36; - assert_eq!(sp_header.version(), 0b000); + assert_eq!(sp_header.ccsds_version(), 0b000); assert!(sp_header.is_tm()); assert!(sp_header.sec_header_flag()); assert_eq!(sp_header.ptype(), PacketType::Tm); @@ -505,7 +508,7 @@ mod tests { assert_eq!(sp_header.packet_id_raw(), 0x0807); assert_eq!(sp_header.psc_raw(), 0xC016); assert_eq!(sp_header.data_len(), 36); - assert_eq!(sp_header.version(), 0b000); + assert_eq!(sp_header.ccsds_version(), 0b000); let from_comp_fields = SpHeader::from_composite_fields( PacketId::new(PacketType::Tc, true, 0x42).unwrap(), @@ -532,7 +535,7 @@ mod tests { assert_eq!(sp_header.ptype(), PacketType::Tc); assert_eq!(sp_header.apid(), 0x7FF); assert_eq!(sp_header.data_len(), 0); - assert_eq!(sp_header.version(), 0b000); + assert_eq!(sp_header.ccsds_version(), 0b000); assert!(sp_header.is_tc()); let sp_header_zc = zc::SpHeader::from(sp_header); let slice = sp_header_zc.as_bytes(); @@ -570,7 +573,7 @@ mod tests { assert!(sp_header.is_some()); let sp_header = sp_header.unwrap(); println!("Header: {:?}", sp_header); - assert_eq!(sp_header.version(), 0b000); + assert_eq!(sp_header.ccsds_version(), 0b000); assert_eq!(sp_header.packet_id_raw(), 0x1FFF); assert_eq!(sp_header.apid(), 0x7FF); assert_eq!(sp_header.ptype(), PacketType::Tc); diff --git a/spacepackets/src/tc.rs b/spacepackets/src/tc.rs index 9032a1a..bb0c6ec 100644 --- a/spacepackets/src/tc.rs +++ b/spacepackets/src/tc.rs @@ -1,3 +1,5 @@ +use crate::ecss::{PusPacket, PusVersion}; +use crate::CCSDS_HEADER_LEN; use std::mem::size_of; type CrcType = u16; @@ -5,11 +7,50 @@ type CrcType = u16; /// PUS C secondary header length is fixed pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::(); pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize = - size_of::() + PUC_TC_SECONDARY_HEADER_LEN + size_of::(); + CCSDS_HEADER_LEN + PUC_TC_SECONDARY_HEADER_LEN + size_of::(); +const PUS_VERSION: PusVersion = PusVersion::PusC; + +#[derive(Copy, Clone, PartialEq, Debug)] +enum AckOpts { + Acceptance = 0b1000, + Start = 0b0100, + Progress = 0b0010, + Completion = 0b0001, +} + +pub const ACK_ALL: u8 = AckOpts::Acceptance as u8 + | AckOpts::Start as u8 + | AckOpts::Progress as u8 + | AckOpts::Completion as u8; + +pub trait PusTcSecondaryHeader { + fn ack_flags(&self) -> u8; + fn service(&self) -> u8; + fn subservice(&self) -> u8; + fn source_id(&self) -> u16; +} + +impl PusTcSecondaryHeader for T { + fn ack_flags(&self) -> u8 { + self.ack_flags() + } + + fn service(&self) -> u8 { + self.service() + } + + fn subservice(&self) -> u8 { + self.subservice() + } + + fn source_id(&self) -> u16 { + self.source_id() + } +} pub mod zc { use crate::ecss::{PusError, PusVersion}; - use crate::tc::srd; + use crate::tc::{srd, PusTcSecondaryHeader}; use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16}; #[derive(FromBytes, AsBytes, Unaligned)] @@ -36,17 +77,40 @@ pub mod zc { } } + impl PusTcSecondaryHeader for PusTcDataFieldHeader { + fn ack_flags(&self) -> u8 { + self.version_ack & 0b1111 + } + + fn service(&self) -> u8 { + self.service + } + + fn subservice(&self) -> u8 { + self.subservice + } + + fn source_id(&self) -> u16 { + self.source_id.get() + } + } + impl PusTcDataFieldHeader { pub fn to_bytes(&self, slice: &mut (impl AsMut<[u8]> + ?Sized)) -> Option<()> { self.write_to(slice.as_mut()) } + + pub fn from_bytes(slice: &(impl AsRef<[u8]> + ?Sized)) -> Option { + Self::read_from(slice.as_ref()) + } } } pub mod srd { - use crate::ecss::{PusPacket, PusVersion, CRC_CCITT_FALSE}; + use crate::ecss::{PusError, PusPacket, PusVersion, CRC_CCITT_FALSE}; use crate::srd::SpHeader; - use crate::{CcsdsPacket, PacketError, PacketId, PacketSequenceCtrl, PacketType}; + use crate::tc::{PusTcSecondaryHeader, ACK_ALL, PUS_TC_MIN_LEN_WITHOUT_APP_DATA, PUS_VERSION}; + use crate::{zc, CcsdsPacket, PacketError, PacketId, PacketSequenceCtrl, PacketType}; use delegate::delegate; use serde::{Deserialize, Serialize}; use std::mem::size_of; @@ -61,12 +125,43 @@ pub mod srd { pub version: PusVersion, } + impl PusTcSecondaryHeader for PusTcDataFieldHeader { + fn ack_flags(&self) -> u8 { + self.ack + } + + fn service(&self) -> u8 { + self.service + } + + fn subservice(&self) -> u8 { + self.subservice + } + + fn source_id(&self) -> u16 { + self.source_id + } + } + impl TryFrom for PusTcDataFieldHeader { + type Error = (); + + fn try_from(value: super::zc::PusTcDataFieldHeader) -> Result { + Ok(PusTcDataFieldHeader { + service: value.service(), + subservice: value.subservice(), + source_id: value.source_id(), + ack: value.ack_flags(), + version: PUS_VERSION, + }) + } + } + impl PusTcDataFieldHeader { pub fn new(service: u8, subservice: u8, ack: u8) -> Self { PusTcDataFieldHeader { service, subservice, - ack, + ack: ack & 0b1111, source_id: 0, version: PusVersion::PusC, } @@ -95,7 +190,7 @@ pub mod srd { sph: *sph, raw_data: None, app_data, - data_field_header: PusTcDataFieldHeader::new(service, subservice, 0b1111), + data_field_header: PusTcDataFieldHeader::new(service, subservice, ACK_ALL), crc16: None, } } @@ -114,6 +209,20 @@ pub mod srd { self.len_packed() as u16 - size_of::() as u16 - 1; } + fn crc_from_raw_data(&self) -> Result { + if let Some(raw_data) = self.raw_data { + if raw_data.len() < 2 { + return Err(PusError::RawDataTooShort(raw_data.len())); + } + return Ok(u16::from_be_bytes( + raw_data[raw_data.len() - 2..raw_data.len()] + .try_into() + .unwrap(), + )); + } + Err(PusError::NoRawData) + } + pub fn calc_crc16(&mut self) { let mut digest = CRC_CCITT_FALSE.digest(); let sph_zc = crate::zc::SpHeader::from(self.sph); @@ -138,9 +247,9 @@ pub mod srd { pub fn copy_to_buf( &self, slice: &mut (impl AsMut<[u8]> + ?Sized), - ) -> Result { + ) -> Result { if self.crc16.is_none() { - return Err(PacketError::CrcCalculationMissing); + return Err(PusError::CrcCalculationMissing); } let mut_slice = slice.as_mut(); let mut curr_idx = 0; @@ -151,11 +260,15 @@ pub mod srd { total_size += app_data.len(); }; if total_size > mut_slice.len() { - return Err(PacketError::ToBytesSliceTooSmall(total_size)); + return Err(PusError::OtherPacketError( + PacketError::ToBytesSliceTooSmall(total_size), + )); } sph_zc .to_bytes(&mut mut_slice[curr_idx..curr_idx + 6]) - .ok_or(PacketError::ToBytesZeroCopyError)?; + .ok_or(PusError::OtherPacketError( + PacketError::ToBytesZeroCopyError, + ))?; curr_idx += 6; // The PUS version is hardcoded to PUS C let pus_tc_header = @@ -163,7 +276,9 @@ pub mod srd { pus_tc_header .to_bytes(&mut mut_slice[curr_idx..curr_idx + tc_header_len]) - .ok_or(PacketError::ToBytesZeroCopyError)?; + .ok_or(PusError::OtherPacketError( + PacketError::ToBytesZeroCopyError, + ))?; curr_idx += tc_header_len; if let Some(app_data) = self.app_data { mut_slice[curr_idx..curr_idx + app_data.len()].copy_from_slice(app_data); @@ -175,9 +290,9 @@ pub mod srd { Ok(curr_idx) } - pub fn append_to_vec(&self, vec: &mut Vec) -> Result { + pub fn append_to_vec(&self, vec: &mut Vec) -> Result { if self.crc16.is_none() { - return Err(PacketError::CrcCalculationMissing); + return Err(PusError::CrcCalculationMissing); } let sph_zc = crate::zc::SpHeader::from(self.sph); let mut appended_len = super::PUS_TC_MIN_LEN_WITHOUT_APP_DATA; @@ -195,34 +310,68 @@ pub mod srd { vec.extend_from_slice(self.crc16.unwrap().to_be_bytes().as_slice()); Ok(appended_len) } + + pub fn new_from_raw_slice( + slice: &'slice (impl AsRef<[u8]> + ?Sized), + ) -> Result { + let slice_ref = slice.as_ref(); + let raw_data_len = slice_ref.len(); + if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA { + return Err(PusError::RawDataTooShort(raw_data_len)); + } + let sph = zc::SpHeader::from_bytes(slice).unwrap(); + let total_len = sph.total_len(); + if raw_data_len < total_len { + return Err(PusError::RawDataTooShort(raw_data_len)); + } + let sec_header = crate::tc::zc::PusTcDataFieldHeader::from_bytes(slice).ok_or( + PusError::OtherPacketError(PacketError::FromBytesZeroCopyError), + )?; + + let mut pus_tc = PusTc { + sph: SpHeader::from(sph), + data_field_header: PusTcDataFieldHeader::try_from(sec_header).unwrap(), + raw_data: Some(slice_ref), + app_data: Some(&slice_ref[PUS_TC_MIN_LEN_WITHOUT_APP_DATA..total_len - 2]), + crc16: None, + }; + pus_tc.verify()?; + Ok(pus_tc) + } + + fn verify(&mut self) -> Result<(), PusError> { + let mut digest = CRC_CCITT_FALSE.digest(); + if self.raw_data.is_none() { + return Err(PusError::NoRawData); + } + let raw_data = self.raw_data.unwrap(); + digest.update(raw_data.as_ref()); + if digest.finalize() == 0 { + return Ok(()); + } + let crc16 = self.crc_from_raw_data()?; + Err(PusError::IncorrectCrc(crc16)) + } } //noinspection RsTraitImplementation impl CcsdsPacket for PusTc<'_> { delegate!(to self.sph { - fn version(&self) -> u8; + fn ccsds_version(&self) -> u8; fn packet_id(&self) -> PacketId; fn psc(&self) -> PacketSequenceCtrl; fn data_len(&self) -> u16; }); } + //noinspection RsTraitImplementation impl PusPacket for PusTc<'_> { - fn service(&self) -> u8 { - self.data_field_header.service - } - - fn subservice(&self) -> u8 { - self.data_field_header.subservice - } - - fn source_id(&self) -> u16 { - self.data_field_header.source_id - } - - fn ack_flags(&self) -> u8 { - self.data_field_header.ack - } + delegate!(to self.data_field_header { + fn service(&self) -> u8; + fn subservice(&self) -> u8; + fn source_id(&self) -> u16; + fn ack_flags(&self) -> u8; + }); fn user_data(&self) -> Option<&[u8]> { self.app_data @@ -231,31 +380,29 @@ pub mod srd { fn crc16(&self) -> Option { self.crc16 } - - fn verify(&mut self) -> bool { - let mut digest = CRC_CCITT_FALSE.digest(); - if self.raw_data.is_none() { - return false; - } - digest.update(self.raw_data.unwrap().as_ref()); - if digest.finalize() == 0 { - return true; - } - false - } } } #[cfg(test)] mod tests { + use crate::ecss::PusPacket; use crate::srd::SpHeader; use crate::tc::srd::PusTc; + use crate::tc::ACK_ALL; + use crate::CcsdsPacket; use postcard::to_stdvec; #[test] fn test_tc() { let mut sph = SpHeader::tc(0x01, 0).unwrap(); let mut pus_tc = PusTc::new(&mut sph, 17, 1, None); + assert_eq!(pus_tc.service(), 17); + assert_eq!(pus_tc.subservice(), 1); + assert_eq!(pus_tc.user_data(), None); + assert_eq!(pus_tc.source_id(), 0); + assert_eq!(pus_tc.apid(), 0x01); + assert_eq!(pus_tc.ack_flags(), ACK_ALL); + assert_eq!(pus_tc.crc16(), None); let _out = to_stdvec(&pus_tc).unwrap(); let mut test_buf = [0; 32]; pus_tc.update_packet_fields();