diff --git a/src/lib.rs b/src/lib.rs index 7be3cca..b490e79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,10 +13,17 @@ pub mod tc; pub mod time; pub mod tm; +#[derive(Debug, Copy, Clone, PartialEq)] +pub struct SizeMissmatch { + found: usize, + expected: usize, +} #[derive(Debug, Copy, Clone, PartialEq)] pub enum PacketError { - /// The passed slice is too small. Returns the required size of the failed size check - ToBytesSliceTooSmall(usize), + /// The passed slice is too small. Returns the found and expected minimum size + ToBytesSliceTooSmall(SizeMissmatch), + /// The provider buffer it soo small. Returns the found and expected minimum size + FromBytesSliceTooSmall(SizeMissmatch), /// The [zerocopy] library failed to write to bytes ToBytesZeroCopyError, FromBytesZeroCopyError, diff --git a/src/tc.rs b/src/tc.rs index cb48f2c..03c8f26 100644 --- a/src/tc.rs +++ b/src/tc.rs @@ -1,6 +1,6 @@ use crate::ecss::{PusError, PusPacket, PusVersion, CRC_CCITT_FALSE}; use crate::ser::SpHeader; -use crate::{CcsdsPacket, PacketError, PacketType, SequenceFlags, CCSDS_HEADER_LEN}; +use crate::{CcsdsPacket, PacketError, PacketType, SequenceFlags, SizeMissmatch, CCSDS_HEADER_LEN}; use alloc::vec::Vec; use core::mem::size_of; use delegate::delegate; @@ -303,8 +303,7 @@ impl<'slice> PusTc<'slice> { self.calc_own_crc16(); } - pub fn copy_to_buf(&self, slice: &mut (impl AsMut<[u8]> + ?Sized)) -> Result { - let mut_slice = slice.as_mut(); + pub fn copy_to_buf(&self, slice: &mut [u8]) -> Result { let mut curr_idx = 0; let sph_zc = crate::zc::SpHeader::from(self.sph); let tc_header_len = size_of::(); @@ -312,13 +311,16 @@ impl<'slice> PusTc<'slice> { if let Some(app_data) = self.app_data { total_size += app_data.len(); }; - if total_size > mut_slice.len() { + if total_size > slice.len() { return Err(PusError::OtherPacketError( - PacketError::ToBytesSliceTooSmall(total_size), + PacketError::ToBytesSliceTooSmall(SizeMissmatch { + found: slice.len(), + expected: total_size, + }), )); } sph_zc - .to_bytes(&mut mut_slice[curr_idx..curr_idx + 6]) + .to_bytes(&mut slice[curr_idx..curr_idx + 6]) .ok_or(PusError::OtherPacketError( PacketError::ToBytesZeroCopyError, ))?; @@ -327,24 +329,24 @@ impl<'slice> PusTc<'slice> { let pus_tc_header = zc::PusTcDataFieldHeader::try_from(self.data_field_header).unwrap(); pus_tc_header - .to_bytes(&mut mut_slice[curr_idx..curr_idx + tc_header_len]) + .to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len]) .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); + slice[curr_idx..curr_idx + app_data.len()].copy_from_slice(app_data); curr_idx += app_data.len(); } let crc16; if self.calc_crc_on_serialization { - crc16 = Self::calc_crc16(&mut_slice[0..curr_idx]) + crc16 = Self::calc_crc16(&slice[0..curr_idx]) } else if self.crc16.is_none() { return Err(PusError::CrcCalculationMissing); } else { crc16 = self.crc16.unwrap(); } - mut_slice[curr_idx..curr_idx + 2].copy_from_slice(crc16.to_be_bytes().as_slice()); + slice[curr_idx..curr_idx + 2].copy_from_slice(crc16.to_be_bytes().as_slice()); curr_idx += 2; Ok(curr_idx) } diff --git a/src/time.rs b/src/time.rs index 9da0830..cb69a4b 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,8 +1,9 @@ -use crate::PacketError; +use crate::{PacketError, SizeMissmatch}; use chrono::{DateTime, TimeZone, Utc}; +use crate::time::CcsdsTimeCodes::Cds; #[cfg(feature = "std")] -use std::time::SystemTime; +use std::time::{SystemTime, SystemTimeError}; pub const CDS_SHORT_LEN: usize = 7; pub const DAYS_CCSDS_TO_UNIX: i32 = -4383; @@ -16,6 +17,25 @@ pub enum CcsdsTimeCodes { Ccs = 0b101, } +impl TryFrom for CcsdsTimeCodes { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + x if x == CcsdsTimeCodes::None as u8 => Ok(CcsdsTimeCodes::None), + x if x == CcsdsTimeCodes::CucCcsdsEpoch as u8 => Ok(CcsdsTimeCodes::CucCcsdsEpoch), + x if x == CcsdsTimeCodes::CucAgencyEpoch as u8 => Ok(CcsdsTimeCodes::CucAgencyEpoch), + x if x == CcsdsTimeCodes::Cds as u8 => Ok(CcsdsTimeCodes::Cds), + x if x == CcsdsTimeCodes::Ccs as u8 => Ok(CcsdsTimeCodes::Ccs), + _ => Err(()), + } + } +} +pub enum TimestampError { + InvalidTimeCode(CcsdsTimeCodes, u8), + PacketError(PacketError), +} + #[cfg(feature = "std")] pub fn seconds_since_epoch() -> f64 { SystemTime::now() @@ -43,7 +63,7 @@ pub const fn ccsds_to_unix_days(ccsds_days: i32) -> i32 { /// Trait for generic CCSDS time providers trait CcsdsTimeProvider { fn len(&self) -> usize; - fn write_to_bytes(&self, bytes: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<(), PacketError>; + fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<(), PacketError>; /// Returns the pfield of the time provider. The pfield can have one or two bytes depending /// on the extension bit (first bit). The time provider should returns a tuple where the first /// entry denotes the length of the pfield and the second entry is the value of the pfield @@ -72,32 +92,64 @@ impl CdsShortTimeProvider { unix_seconds: 0, date_time: None, }; - let unix_days_seconds = ccsds_to_unix_days(ccsds_days as i32) as i64 * (24 * 60 * 60); + let unix_days_seconds = + ccsds_to_unix_days(ccsds_days as i32) as i64 * SECONDS_PER_DAY as i64; provider.setup(unix_days_seconds as i64, ms_of_day.into()) } #[cfg(feature = "std")] - pub fn from_now() -> Self { - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .expect("Error retrieving UNIX epoch"); + pub fn from_now() -> Result { + let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; let epoch = now.as_secs(); let secs_of_day = epoch % SECONDS_PER_DAY as u64; let unix_days_seconds = epoch - secs_of_day; let ms_of_day = secs_of_day * 1000 + now.subsec_millis() as u64; let provider = Self { pfield: (CcsdsTimeCodes::Cds as u8) << 4, - ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32) as u16, + ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32) + as u16, ms_of_day: ms_of_day as u32, unix_seconds: 0, date_time: None, }; - provider.setup(unix_days_seconds as i64, ms_of_day.into()) + Ok(provider.setup(unix_days_seconds as i64, ms_of_day.into())) + } + + pub fn from_bytes(buf: &[u8]) -> Result { + if buf.len() < CDS_SHORT_LEN { + return Err(TimestampError::PacketError( + PacketError::FromBytesSliceTooSmall(SizeMissmatch { + expected: CDS_SHORT_LEN, + found: buf.len(), + }), + )); + } + let pfield = buf[0]; + match CcsdsTimeCodes::try_from(pfield >> 4 & 0b111) { + Ok(cds_type) => match cds_type { + Cds => (), + _ => { + return Err(TimestampError::InvalidTimeCode( + CcsdsTimeCodes::Cds, + cds_type as u8, + )) + } + }, + _ => { + return Err(TimestampError::InvalidTimeCode( + CcsdsTimeCodes::Cds, + pfield >> 4 & 0b111, + )) + } + }; + let ccsds_days: u16 = u16::from_be_bytes(buf[1..3].try_into().unwrap()); + let ms_of_day: u32 = u32::from_be_bytes(buf[4..].try_into().unwrap()); + Ok(Self::new(ccsds_days, ms_of_day)) } fn setup(mut self, unix_days_seconds: i64, ms_of_day: u64) -> Self { self.calc_unix_seconds(unix_days_seconds, ms_of_day); - self.calc_date_time(unix_days_seconds, (ms_of_day % 1000) as u32); + self.calc_date_time((ms_of_day % 1000) as u32); self } @@ -124,10 +176,10 @@ impl CdsShortTimeProvider { } } - fn calc_date_time(&mut self, unix_days_seconds: i64, ms_since_last_second: u32) { + fn calc_date_time(&mut self, ms_since_last_second: u32) { assert!(ms_since_last_second < 1000, "Invalid MS since last second"); let ns_since_last_sec = ms_since_last_second * 1e6 as u32; - self.date_time = Some(Utc.timestamp(unix_days_seconds, ns_since_last_sec)); + self.date_time = Some(Utc.timestamp(self.unix_seconds, ns_since_last_sec)); } } @@ -136,14 +188,16 @@ impl CcsdsTimeProvider for CdsShortTimeProvider { CDS_SHORT_LEN } - fn write_to_bytes(&self, bytes: &mut (impl AsMut<[u8]> + ?Sized)) -> Result<(), PacketError> { - let slice = bytes.as_mut(); - if slice.len() < self.len() { - return Err(PacketError::ToBytesSliceTooSmall(slice.len())); + fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), PacketError> { + if buf.len() < self.len() { + return Err(PacketError::ToBytesSliceTooSmall(SizeMissmatch { + expected: self.len(), + found: buf.len(), + })); } - slice[0] = self.pfield; - slice[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice()); - slice[4..].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice()); + buf[0] = self.pfield; + buf[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice()); + buf[4..].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice()); Ok(()) } @@ -168,8 +222,6 @@ impl CcsdsTimeProvider for CdsShortTimeProvider { mod tests { use super::*; use chrono::{Datelike, Timelike}; - #[cfg(feature = "std")] - use std::println; #[test] fn test_creation() { @@ -189,7 +241,7 @@ mod tests { let time_stamper = CdsShortTimeProvider::new(0, 0); assert_eq!( time_stamper.unix_seconds(), - (DAYS_CCSDS_TO_UNIX * 24 * 60 * 60) as i64 + (DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64 ); let date_time = time_stamper.date_time(); assert_eq!(date_time.year(), 1958); @@ -213,10 +265,43 @@ mod tests { assert_eq!(date_time.second(), 0); } + #[test] + fn test_packing() {} + #[cfg(feature = "std")] #[test] fn test_time_now() { - let timestamp_now = CdsShortTimeProvider::from_now(); - println!("{}", timestamp_now.date_time()); + let timestamp_now = CdsShortTimeProvider::from_now().unwrap(); + let compare_stamp = Utc::now(); + let dt = timestamp_now.date_time(); + if compare_stamp.year() > dt.year() { + assert_eq!(compare_stamp.year() - dt.year(), 1); + } else { + assert_eq!(dt.year(), compare_stamp.year()); + } + generic_dt_property_equality_check(dt.month(), compare_stamp.month(), 1, 12); + + assert_eq!(dt.day(), compare_stamp.day()); + if compare_stamp.day() < dt.day() { + assert!(dt.day() >= 28); + assert_eq!(compare_stamp.day(), 1); + } else if compare_stamp.day() > dt.day() { + assert_eq!(compare_stamp.day() - dt.day(), 1); + } else { + assert_eq!(compare_stamp.day(), dt.day()); + } + generic_dt_property_equality_check(dt.hour(), compare_stamp.hour(), 0, 23); + generic_dt_property_equality_check(dt.minute(), compare_stamp.minute(), 0, 59); + } + + fn generic_dt_property_equality_check(first: u32, second: u32, start: u32, end: u32) { + if second < first { + assert_eq!(second, start); + assert_eq!(first, end); + } else if second > first { + assert_eq!(second - first, 1); + } else { + assert_eq!(first, second); + } } }