Merge remote-tracking branch 'origin/main' into time_extensions
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good

This commit is contained in:
Robin Müller 2023-01-17 00:05:23 +01:00
commit 37af989a03
4 changed files with 177 additions and 99 deletions

View File

@ -8,27 +8,58 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
The timestamp of `PusTm` is now optional. See Added and Changed section for details.
## Added ## Added
- CDS timestamp: Added constructor function to create the time provider - `PusTmSecondaryHeader`: New `new_simple_no_timestamp` API to create secondary header without
timestamp.
- `PusTm`: Add `new_simple_no_timestamp` method to create TM without timestamp
- New `UnixTimestamp` abstraction which contains the unix seconds as an `i64`
and an optional subsecond millisecond counter (`u16`)
- `MS_PER_DAY` constant.
### CDS time module
- Implement `Add<Duration>` and `AddAssign<Duration>` for time providers, which allows
easily adding offsets to the providers.
- Implement `TryFrom<DateTime<Utc>>` for time providers.
- `get_dyn_time_provider_from_bytes`: Requires `alloc` support and returns
the correct `TimeProvider` instance wrapped as a boxed trait object
`Box<DynCdsTimeProvider>` by checking the length of days field.
- Added constructor function to create the time provider
from `chrono::DateTime<Utc>` and a generic UNIX timestamp (`i64` seconds from `chrono::DateTime<Utc>` and a generic UNIX timestamp (`i64` seconds
and subsecond milliseconds). and subsecond milliseconds).
- New `UnixTimeStamp` abstraction which contains the unix seconds as an `i64`
and an optional subsecond millisecond counter (`u16`)
- `MAX_DAYS_24_BITS` which contains maximum value which can be supplied - `MAX_DAYS_24_BITS` which contains maximum value which can be supplied
to the days field of a CDS time provider with 24 bits days field width. to the days field of a CDS time provider with 24 bits days field width.
- New `CdsTimestamp` trait which encapsulates common fields for all CDS time providers.
- `from_unix_secs_with_u24_days` and `from_unix_secs_with_u16_days` which create
the time provider from a `UnixTimestamp` reference.
- `from_dt_with_u16_days`, `from_dt_with_u24_days` and their `..._us_precision` and
`..._ps_precision` variants which allow to create time providers from
a `chrono::DateTime<Utc>`.
- Add `from_bytes_with_u24_days` and `from_bytes_with_u16_days` associated methods
## Changed ## Changed
- (breaking) `PusTmSecondaryHeader`: Timestamp is optional now, which translates to a
timestamp of size 0.
- (breaking): `PusTm`: Renamed `time_stamp` method to `timestamp`, also returns
`Optional<&'src_data [u8]>` now.
- (breaking): `PusTmSecondaryHeader`: Renamed `time_stamp` field to `timestamp` for consistency.
- (breaking): Renamed `from_now_with_u24_days_and_us_prec` to `from_now_with_u24_days_us_precision`.
Also did the same for the `u16` variant.
- (breaking): Renamed `from_now_with_u24_days_and_ps_prec` to `from_now_with_u24_days_ps_precision`.
Also did the same for the `u16` variant.
- `CcsdsTimeProvider` trait (breaking): - `CcsdsTimeProvider` trait (breaking):
- Add new `unix_stamp` method returning the new `UnixTimeStamp` struct - Add new `unix_stamp` method returning the new `UnixTimeStamp` struct.
- Add new `subsecond_millis` method returning counter `Option<u16>` - Add new `subsecond_millis` method returning counter `Option<u16>`.
- Default impl for `unix_stamp` which re-uses `subsecond_millis` and - Default impl for `unix_stamp` which re-uses `subsecond_millis` and
existing `unix_seconds` method existing `unix_seconds` method.
- `TimestampError` (breaking): Add `DateBeforeCcsdsEpoch` error type - `TimestampError` (breaking): Add `DateBeforeCcsdsEpoch` error type
because new CDS API allow supplying invalid date times before CCSDS epoch. because new CDS API allow supplying invalid date times before CCSDS epoch.
Make `TimestampError` with `#[non_exhaustive]` attribute to prevent Make `TimestampError` with `#[non_exhaustive]` attribute to prevent
future breakages if new error variants are added future breakages if new error variants are added.
# [v0.4.2] 14.01.2023 # [v0.4.2] 14.01.2023

View File

@ -180,7 +180,7 @@ pub trait CdsCommon {
} }
/// Generic properties for all CDS time providers. /// Generic properties for all CDS time providers.
pub trait CdsTimeStamp: CdsCommon { pub trait CdsTimestamp: CdsCommon {
fn len_of_day_seg(&self) -> LengthOfDaySegment; fn len_of_day_seg(&self) -> LengthOfDaySegment;
} }
@ -388,18 +388,14 @@ impl CdsConverter for ConversionFromNow {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub trait DynCdsTimeProvider: CcsdsTimeProvider + CdsTimeStamp + TimeWriter + Any {} pub trait DynCdsTimeProvider: CcsdsTimeProvider + CdsTimestamp + TimeWriter + Any {}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl DynCdsTimeProvider for TimeProvider<DaysLen16Bits> {} impl DynCdsTimeProvider for TimeProvider<DaysLen16Bits> {}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
impl DynCdsTimeProvider for TimeProvider<DaysLen24Bits> {} impl DynCdsTimeProvider for TimeProvider<DaysLen24Bits> {}
/// This function returns the correct [TimeProvider] instance from a raw byte array /// This function returns the correct [TimeProvider] instance from a raw byte array
/// by checking the days of length field. It also checks the CCSDS time code for correctness. /// by checking the length of days field. It also checks the CCSDS time code for correctness.
///
/// The time provider instance is returned as a [DynCdsTimeProvider] trait object.
/// This function returns the correct [TimeProvider] instance from a raw byte array
/// by checking the days of length field. It also checks the CCSDS time code for correctness.
/// ///
/// # Example /// # Example
/// ///
@ -638,11 +634,13 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
} }
fn from_unix_generic( fn from_unix_generic(
unix_seconds: i64, unix_stamp: &UnixTimestamp,
subsec_millis: u32,
days_len: LengthOfDaySegment, days_len: LengthOfDaySegment,
) -> Result<Self, TimestampError> { ) -> Result<Self, TimestampError> {
let conv_from_dt = ConversionFromUnix::new(unix_seconds, subsec_millis)?; let conv_from_dt = ConversionFromUnix::new(
unix_stamp.unix_seconds,
unix_stamp.subsecond_millis.unwrap_or(0) as u32,
)?;
Self::generic_from_conversion(days_len, conv_from_dt) Self::generic_from_conversion(days_len, conv_from_dt)
} }
@ -763,10 +761,9 @@ impl TimeProvider<DaysLen24Bits> {
/// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or /// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or
/// the CCSDS days value exceeds the allowed bit width (24 bits). /// the CCSDS days value exceeds the allowed bit width (24 bits).
pub fn from_unix_secs_with_u24_days( pub fn from_unix_secs_with_u24_days(
unix_seconds: i64, unix_stamp: &UnixTimestamp,
subsec_millis: u32,
) -> Result<Self, TimestampError> { ) -> Result<Self, TimestampError> {
Self::from_unix_generic(unix_seconds, subsec_millis, LengthOfDaySegment::Long24Bits) Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits)
} }
/// Create a provider from a [`DateTime<Utc>`] struct. /// Create a provider from a [`DateTime<Utc>`] struct.
@ -849,10 +846,9 @@ impl TimeProvider<DaysLen16Bits> {
/// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or /// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or
/// the CCSDS days value exceeds the allowed bit width (24 bits). /// the CCSDS days value exceeds the allowed bit width (24 bits).
pub fn from_unix_secs_with_u16_days( pub fn from_unix_secs_with_u16_days(
unix_seconds: i64, unix_stamp: &UnixTimestamp,
subsec_millis: u32,
) -> Result<Self, TimestampError> { ) -> Result<Self, TimestampError> {
Self::from_unix_generic(unix_seconds, subsec_millis, LengthOfDaySegment::Short16Bits) Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits)
} }
/// Create a provider from a [`DateTime<Utc>`] struct. /// Create a provider from a [`DateTime<Utc>`] struct.
@ -988,13 +984,13 @@ fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
(next_ccsds_days, next_ms_of_day, precision) (next_ccsds_days, next_ms_of_day, precision)
} }
impl CdsTimeStamp for TimeProvider<DaysLen16Bits> { impl CdsTimestamp for TimeProvider<DaysLen16Bits> {
fn len_of_day_seg(&self) -> LengthOfDaySegment { fn len_of_day_seg(&self) -> LengthOfDaySegment {
LengthOfDaySegment::Short16Bits LengthOfDaySegment::Short16Bits
} }
} }
impl CdsTimeStamp for TimeProvider<DaysLen24Bits> { impl CdsTimestamp for TimeProvider<DaysLen24Bits> {
fn len_of_day_seg(&self) -> LengthOfDaySegment { fn len_of_day_seg(&self) -> LengthOfDaySegment {
LengthOfDaySegment::Long24Bits LengthOfDaySegment::Long24Bits
} }
@ -1759,8 +1755,11 @@ mod tests {
fn test_creation_from_unix_stamp_0_u16_days() { fn test_creation_from_unix_stamp_0_u16_days() {
let unix_secs = 0; let unix_secs = 0;
let subsec_millis = 0; let subsec_millis = 0;
let time_provider = TimeProvider::from_unix_secs_with_u16_days(unix_secs, subsec_millis) let time_provider = TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
.expect("creating provider from unix stamp failed"); unix_secs,
subsec_millis,
))
.expect("creating provider from unix stamp failed");
assert_eq!(time_provider.ccsds_days, -DAYS_CCSDS_TO_UNIX as u16) assert_eq!(time_provider.ccsds_days, -DAYS_CCSDS_TO_UNIX as u16)
} }
@ -1768,8 +1767,11 @@ mod tests {
fn test_creation_from_unix_stamp_0_u24_days() { fn test_creation_from_unix_stamp_0_u24_days() {
let unix_secs = 0; let unix_secs = 0;
let subsec_millis = 0; let subsec_millis = 0;
let time_provider = TimeProvider::from_unix_secs_with_u24_days(unix_secs, subsec_millis) let time_provider = TimeProvider::from_unix_secs_with_u24_days(&UnixTimestamp::const_new(
.expect("creating provider from unix stamp failed"); unix_secs,
subsec_millis,
))
.expect("creating provider from unix stamp failed");
assert_eq!(time_provider.ccsds_days, (-DAYS_CCSDS_TO_UNIX) as u32) assert_eq!(time_provider.ccsds_days, (-DAYS_CCSDS_TO_UNIX) as u32)
} }
@ -1781,9 +1783,8 @@ mod tests {
.and_hms_milli_opt(16, 49, 30, subsec_millis) .and_hms_milli_opt(16, 49, 30, subsec_millis)
.unwrap(); .unwrap();
let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc); let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc);
let time_provider = let time_provider = TimeProvider::from_unix_secs_with_u16_days(&datetime_utc.into())
TimeProvider::from_unix_secs_with_u16_days(datetime_utc.timestamp(), subsec_millis) .expect("creating provider from unix stamp failed");
.expect("creating provider from unix stamp failed");
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023 // https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
// Leap years need to be accounted for as well. // Leap years need to be accounted for as well.
assert_eq!(time_provider.ccsds_days, 23754); assert_eq!(time_provider.ccsds_days, 23754);
@ -1799,8 +1800,11 @@ mod tests {
fn test_creation_0_ccsds_days() { fn test_creation_0_ccsds_days() {
let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64; let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64;
let subsec_millis = 0; let subsec_millis = 0;
let time_provider = TimeProvider::from_unix_secs_with_u16_days(unix_secs, subsec_millis) let time_provider = TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
.expect("creating provider from unix stamp failed"); unix_secs,
subsec_millis,
))
.expect("creating provider from unix stamp failed");
assert_eq!(time_provider.ccsds_days, 0) assert_eq!(time_provider.ccsds_days, 0)
} }
@ -1808,7 +1812,10 @@ mod tests {
fn test_invalid_creation_from_unix_stamp_days_too_large() { fn test_invalid_creation_from_unix_stamp_days_too_large() {
let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64; let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64;
let subsec_millis = 0; let subsec_millis = 0;
match TimeProvider::from_unix_secs_with_u16_days(invalid_unix_secs as i64, subsec_millis) { match TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
invalid_unix_secs as i64,
subsec_millis,
)) {
Ok(_) => { Ok(_) => {
panic!("creation should not succeed") panic!("creation should not succeed")
} }
@ -1831,7 +1838,10 @@ mod tests {
// precisely 31-12-1957 23:59:55 // precisely 31-12-1957 23:59:55
let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5; let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5;
let subsec_millis = 0; let subsec_millis = 0;
match TimeProvider::from_unix_secs_with_u16_days(unix_secs as i64, subsec_millis) { match TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
unix_secs as i64,
subsec_millis,
)) {
Ok(_) => { Ok(_) => {
panic!("creation should not succeed") panic!("creation should not succeed")
} }

View File

@ -241,15 +241,8 @@ pub struct UnixTimestamp {
} }
impl UnixTimestamp { impl UnixTimestamp {
pub fn new(unix_seconds: i64) -> Self {
Self {
unix_seconds,
subsecond_millis: None,
}
}
/// Returns none if the subsecond millisecond value is larger than 999. /// Returns none if the subsecond millisecond value is larger than 999.
pub fn new_with_subsecond_millis(unix_seconds: i64, subsec_millis: u16) -> Option<Self> { pub fn new(unix_seconds: i64, subsec_millis: u16) -> Option<Self> {
if subsec_millis > 999 { if subsec_millis > 999 {
return None; return None;
} }
@ -259,6 +252,23 @@ impl UnixTimestamp {
}) })
} }
pub const fn const_new(unix_seconds: i64, subsec_millis: u16) -> Self {
if subsec_millis > 999 {
panic!("subsec milliseconds exceeds 999");
}
Self {
unix_seconds,
subsecond_millis: Some(subsec_millis),
}
}
pub fn new_only_seconds(unix_seconds: i64) -> Self {
Self {
unix_seconds,
subsecond_millis: None,
}
}
pub fn subsecond_millis(&self) -> Option<u16> { pub fn subsecond_millis(&self) -> Option<u16> {
self.subsecond_millis self.subsecond_millis
} }
@ -282,6 +292,22 @@ impl UnixTimestamp {
} }
secs secs
} }
pub fn as_date_time(&self) -> LocalResult<DateTime<Utc>> {
Utc.timestamp_opt(
self.unix_seconds,
self.subsecond_millis.unwrap_or(0) as u32 * 10_u32.pow(6),
)
}
}
impl From<DateTime<Utc>> for UnixTimestamp {
fn from(value: DateTime<Utc>) -> Self {
Self {
unix_seconds: value.timestamp(),
subsecond_millis: Some(value.timestamp_subsec_millis() as u16),
}
}
} }
#[cfg(all(test, feature = "std"))] #[cfg(all(test, feature = "std"))]
@ -315,17 +341,17 @@ mod tests {
#[test] #[test]
fn basic_unix_stamp_test() { fn basic_unix_stamp_test() {
let stamp = UnixTimestamp::new(-200); let stamp = UnixTimestamp::new_only_seconds(-200);
assert_eq!(stamp.unix_seconds, -200); assert_eq!(stamp.unix_seconds, -200);
assert!(stamp.subsecond_millis().is_none()); assert!(stamp.subsecond_millis().is_none());
let stamp = UnixTimestamp::new(250); let stamp = UnixTimestamp::new_only_seconds(250);
assert_eq!(stamp.unix_seconds, 250); assert_eq!(stamp.unix_seconds, 250);
assert!(stamp.subsecond_millis().is_none()); assert!(stamp.subsecond_millis().is_none());
} }
#[test] #[test]
fn basic_float_unix_stamp_test() { fn basic_float_unix_stamp_test() {
let stamp = UnixTimestamp::new_with_subsecond_millis(500, 600).unwrap(); let stamp = UnixTimestamp::new(500, 600).unwrap();
assert!(stamp.subsecond_millis.is_some()); assert!(stamp.subsecond_millis.is_some());
assert_eq!(stamp.unix_seconds, 500); assert_eq!(stamp.unix_seconds, 500);
let subsec_millis = stamp.subsecond_millis().unwrap(); let subsec_millis = stamp.subsecond_millis().unwrap();

115
src/tm.rs
View File

@ -48,7 +48,7 @@ pub mod zc {
pub struct PusTmSecHeader<'slice> { pub struct PusTmSecHeader<'slice> {
pub(crate) zc_header: PusTmSecHeaderWithoutTimestamp, pub(crate) zc_header: PusTmSecHeaderWithoutTimestamp,
pub(crate) timestamp: &'slice [u8], pub(crate) timestamp: Option<&'slice [u8]>,
} }
impl TryFrom<crate::tm::PusTmSecondaryHeader<'_>> for PusTmSecHeaderWithoutTimestamp { impl TryFrom<crate::tm::PusTmSecondaryHeader<'_>> for PusTmSecHeaderWithoutTimestamp {
@ -115,20 +115,17 @@ pub struct PusTmSecondaryHeader<'stamp> {
pub subservice: u8, pub subservice: u8,
pub msg_counter: u16, pub msg_counter: u16,
pub dest_id: u16, pub dest_id: u16,
pub time_stamp: &'stamp [u8], pub timestamp: Option<&'stamp [u8]>,
} }
impl<'stamp> PusTmSecondaryHeader<'stamp> { impl<'stamp> PusTmSecondaryHeader<'stamp> {
pub fn new_simple(service: u8, subservice: u8, time_stamp: &'stamp [u8]) -> Self { pub fn new_simple(service: u8, subservice: u8, timestamp: &'stamp [u8]) -> Self {
PusTmSecondaryHeader { Self::new(service, subservice, 0, 0, Some(timestamp))
pus_version: PusVersion::PusC, }
sc_time_ref_status: 0,
service, /// Like [new_simple] but without a timestamp.
subservice, pub fn new_simple_no_timestamp(service: u8, subservice: u8) -> Self {
msg_counter: 0, Self::new(service, subservice, 0, 0, None)
dest_id: 0,
time_stamp,
}
} }
pub fn new( pub fn new(
@ -136,7 +133,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
subservice: u8, subservice: u8,
msg_counter: u16, msg_counter: u16,
dest_id: u16, dest_id: u16,
time_stamp: &'stamp [u8], timestamp: Option<&'stamp [u8]>,
) -> Self { ) -> Self {
PusTmSecondaryHeader { PusTmSecondaryHeader {
pus_version: PusVersion::PusC, pus_version: PusVersion::PusC,
@ -145,7 +142,7 @@ impl<'stamp> PusTmSecondaryHeader<'stamp> {
subservice, subservice,
msg_counter, msg_counter,
dest_id, dest_id,
time_stamp, timestamp,
} }
} }
} }
@ -187,7 +184,7 @@ impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice
subservice: sec_header.zc_header.subservice(), subservice: sec_header.zc_header.subservice(),
msg_counter: sec_header.zc_header.msg_counter(), msg_counter: sec_header.zc_header.msg_counter(),
dest_id: sec_header.zc_header.dest_id(), dest_id: sec_header.zc_header.dest_id(),
time_stamp: sec_header.timestamp, timestamp: sec_header.timestamp,
}) })
} }
} }
@ -257,15 +254,17 @@ impl<'src_data> PusTm<'src_data> {
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
let mut length = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA; let mut length = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA;
length += self.sec_header.time_stamp.len(); if let Some(timestamp) = self.sec_header.timestamp {
length += timestamp.len();
}
if let Some(src_data) = self.source_data { if let Some(src_data) = self.source_data {
length += src_data.len(); length += src_data.len();
} }
length length
} }
pub fn time_stamp(&self) -> &'src_data [u8] { pub fn timestamp(&self) -> Option<&'src_data [u8]> {
self.sec_header.time_stamp self.sec_header.timestamp
} }
pub fn source_data(&self) -> Option<&'src_data [u8]> { pub fn source_data(&self) -> Option<&'src_data [u8]> {
@ -304,7 +303,9 @@ impl<'src_data> PusTm<'src_data> {
digest.update(sph_zc.as_bytes()); digest.update(sph_zc.as_bytes());
let pus_tc_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap(); let pus_tc_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
digest.update(pus_tc_header.as_bytes()); digest.update(pus_tc_header.as_bytes());
digest.update(self.sec_header.time_stamp); if let Some(stamp) = self.sec_header.timestamp {
digest.update(stamp);
}
if let Some(src_data) = self.source_data { if let Some(src_data) = self.source_data {
digest.update(src_data); digest.update(src_data);
} }
@ -337,9 +338,11 @@ impl<'src_data> PusTm<'src_data> {
.write_to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len]) .write_to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len])
.ok_or(ByteConversionError::ZeroCopyToError)?; .ok_or(ByteConversionError::ZeroCopyToError)?;
curr_idx += sec_header_len; curr_idx += sec_header_len;
let timestamp_len = self.sec_header.time_stamp.len(); if let Some(timestamp) = self.sec_header.timestamp {
slice[curr_idx..curr_idx + timestamp_len].copy_from_slice(self.sec_header.time_stamp); let timestamp_len = timestamp.len();
curr_idx += timestamp_len; slice[curr_idx..curr_idx + timestamp_len].copy_from_slice(timestamp);
curr_idx += timestamp_len;
}
if let Some(src_data) = self.source_data { if let Some(src_data) = self.source_data {
slice[curr_idx..curr_idx + src_data.len()].copy_from_slice(src_data); slice[curr_idx..curr_idx + src_data.len()].copy_from_slice(src_data);
curr_idx += src_data.len(); curr_idx += src_data.len();
@ -361,8 +364,10 @@ impl<'src_data> PusTm<'src_data> {
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> { pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
let mut appended_len = let mut appended_len = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA;
PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + self.sec_header.time_stamp.len(); if let Some(timestamp) = self.sec_header.timestamp {
appended_len += timestamp.len();
}
if let Some(src_data) = self.source_data { if let Some(src_data) = self.source_data {
appended_len += src_data.len(); appended_len += src_data.len();
}; };
@ -374,8 +379,10 @@ impl<'src_data> PusTm<'src_data> {
let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap(); let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap();
vec.extend_from_slice(sec_header.as_bytes()); vec.extend_from_slice(sec_header.as_bytes());
ser_len += sec_header.as_bytes().len(); ser_len += sec_header.as_bytes().len();
vec.extend_from_slice(self.sec_header.time_stamp); if let Some(timestamp) = self.sec_header.timestamp {
ser_len += self.sec_header.time_stamp.len(); ser_len += timestamp.len();
vec.extend_from_slice(timestamp);
}
if let Some(src_data) = self.source_data { if let Some(src_data) = self.source_data {
vec.extend_from_slice(src_data); vec.extend_from_slice(src_data);
ser_len += src_data.len(); ser_len += src_data.len();
@ -414,9 +421,13 @@ impl<'src_data> PusTm<'src_data> {
) )
.ok_or(ByteConversionError::ZeroCopyFromError)?; .ok_or(ByteConversionError::ZeroCopyFromError)?;
current_idx += PUC_TM_MIN_SEC_HEADER_LEN; current_idx += PUC_TM_MIN_SEC_HEADER_LEN;
let mut timestamp = None;
if timestamp_len > 0 {
timestamp = Some(&slice[current_idx..current_idx + timestamp_len]);
}
let zc_sec_header_wrapper = zc::PusTmSecHeader { let zc_sec_header_wrapper = zc::PusTmSecHeader {
zc_header: sec_header_zc, zc_header: sec_header_zc,
timestamp: &slice[current_idx..current_idx + timestamp_len], timestamp,
}; };
current_idx += timestamp_len; current_idx += timestamp_len;
let raw_data = &slice[0..total_len]; let raw_data = &slice[0..total_len];
@ -473,33 +484,33 @@ mod tests {
use crate::ecss::PusVersion::PusC; use crate::ecss::PusVersion::PusC;
use crate::SpHeader; use crate::SpHeader;
fn base_ping_reply_full_ctor(time_stamp: &[u8]) -> PusTm { fn base_ping_reply_full_ctor(timestamp: &[u8]) -> PusTm {
let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap();
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp); let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &timestamp);
PusTm::new(&mut sph, tc_header, None, true) PusTm::new(&mut sph, tc_header, None, true)
} }
fn base_hk_reply<'a>(time_stamp: &'a [u8], src_data: &'a [u8]) -> PusTm<'a> { fn base_hk_reply<'a>(timestamp: &'a [u8], src_data: &'a [u8]) -> PusTm<'a> {
let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap();
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, &time_stamp); let tc_header = PusTmSecondaryHeader::new_simple(3, 5, &timestamp);
PusTm::new(&mut sph, tc_header, Some(src_data), true) PusTm::new(&mut sph, tc_header, Some(src_data), true)
} }
fn dummy_time_stamp() -> &'static [u8] { fn dummy_timestamp() -> &'static [u8] {
return &[0, 1, 2, 3, 4, 5, 6]; return &[0, 1, 2, 3, 4, 5, 6];
} }
#[test] #[test]
fn test_basic() { fn test_basic() {
let time_stamp = dummy_time_stamp(); let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&timestamp);
verify_ping_reply(&pus_tm, false, 22, dummy_time_stamp()); verify_ping_reply(&pus_tm, false, 22, dummy_timestamp());
} }
#[test] #[test]
fn test_serialization_no_source_data() { fn test_serialization_no_source_data() {
let time_stamp = dummy_time_stamp(); let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&timestamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm let ser_len = pus_tm
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
@ -511,7 +522,7 @@ mod tests {
#[test] #[test]
fn test_serialization_with_source_data() { fn test_serialization_with_source_data() {
let src_data = [1, 2, 3]; let src_data = [1, 2, 3];
let hk_reply = base_hk_reply(dummy_time_stamp(), &src_data); let hk_reply = base_hk_reply(dummy_timestamp(), &src_data);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let ser_len = hk_reply let ser_len = hk_reply
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
@ -524,8 +535,8 @@ mod tests {
#[test] #[test]
fn test_setters() { fn test_setters() {
let time_stamp = dummy_time_stamp(); let timestamp = dummy_timestamp();
let mut pus_tm = base_ping_reply_full_ctor(&time_stamp); let mut pus_tm = base_ping_reply_full_ctor(&timestamp);
pus_tm.set_sc_time_ref_status(0b1010); pus_tm.set_sc_time_ref_status(0b1010);
pus_tm.set_dest_id(0x7fff); pus_tm.set_dest_id(0x7fff);
pus_tm.set_msg_counter(0x1f1f); pus_tm.set_msg_counter(0x1f1f);
@ -538,8 +549,8 @@ mod tests {
#[test] #[test]
fn test_deserialization_no_source_data() { fn test_deserialization_no_source_data() {
let time_stamp = dummy_time_stamp(); let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&timestamp);
let mut buf: [u8; 32] = [0; 32]; let mut buf: [u8; 32] = [0; 32];
let ser_len = pus_tm let ser_len = pus_tm
.write_to_bytes(&mut buf) .write_to_bytes(&mut buf)
@ -547,13 +558,13 @@ mod tests {
assert_eq!(ser_len, 22); assert_eq!(ser_len, 22);
let (tm_deserialized, size) = PusTm::from_bytes(&buf, 7).expect("Deserialization failed"); let (tm_deserialized, size) = PusTm::from_bytes(&buf, 7).expect("Deserialization failed");
assert_eq!(ser_len, size); assert_eq!(ser_len, size);
verify_ping_reply(&tm_deserialized, false, 22, dummy_time_stamp()); verify_ping_reply(&tm_deserialized, false, 22, dummy_timestamp());
} }
#[test] #[test]
fn test_manual_field_update() { fn test_manual_field_update() {
let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap();
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_time_stamp()); let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_timestamp());
let mut tm = PusTm::new(&mut sph, tc_header, None, false); let mut tm = PusTm::new(&mut sph, tc_header, None, false);
tm.calc_crc_on_serialization = false; tm.calc_crc_on_serialization = false;
assert_eq!(tm.data_len(), 0x00); assert_eq!(tm.data_len(), 0x00);
@ -573,8 +584,8 @@ mod tests {
#[test] #[test]
fn test_target_buf_too_small() { fn test_target_buf_too_small() {
let time_stamp = dummy_time_stamp(); let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&timestamp);
let mut buf: [u8; 16] = [0; 16]; let mut buf: [u8; 16] = [0; 16];
let res = pus_tm.write_to_bytes(&mut buf); let res = pus_tm.write_to_bytes(&mut buf);
assert!(res.is_err()); assert!(res.is_err());
@ -597,8 +608,8 @@ mod tests {
#[test] #[test]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_append_to_vec() { fn test_append_to_vec() {
let time_stamp = dummy_time_stamp(); let timestamp = dummy_timestamp();
let pus_tm = base_ping_reply_full_ctor(&time_stamp); let pus_tm = base_ping_reply_full_ctor(&timestamp);
let mut vec = Vec::new(); let mut vec = Vec::new();
let res = pus_tm.append_to_vec(&mut vec); let res = pus_tm.append_to_vec(&mut vec);
assert!(res.is_ok()); assert!(res.is_ok());
@ -610,7 +621,7 @@ mod tests {
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn test_append_to_vec_with_src_data() { fn test_append_to_vec_with_src_data() {
let src_data = [1, 2, 3]; let src_data = [1, 2, 3];
let hk_reply = base_hk_reply(dummy_time_stamp(), &src_data); let hk_reply = base_hk_reply(dummy_timestamp(), &src_data);
let mut vec = Vec::new(); let mut vec = Vec::new();
vec.push(4); vec.push(4);
let res = hk_reply.append_to_vec(&mut vec); let res = hk_reply.append_to_vec(&mut vec);
@ -639,7 +650,7 @@ mod tests {
assert_eq!(buf[11], 0x00); assert_eq!(buf[11], 0x00);
assert_eq!(buf[12], 0x00); assert_eq!(buf[12], 0x00);
// Timestamp // Timestamp
assert_eq!(&buf[13..20], dummy_time_stamp()); assert_eq!(&buf[13..20], dummy_timestamp());
let mut digest = CRC_CCITT_FALSE.digest(); let mut digest = CRC_CCITT_FALSE.digest();
digest.update(&buf[0..20]); digest.update(&buf[0..20]);
let crc16 = digest.finalize(); let crc16 = digest.finalize();
@ -651,14 +662,14 @@ mod tests {
tm: &PusTm, tm: &PusTm,
has_user_data: bool, has_user_data: bool,
exp_full_len: usize, exp_full_len: usize,
exp_time_stamp: &[u8], exp_timestamp: &[u8],
) { ) {
assert!(tm.is_tm()); assert!(tm.is_tm());
assert_eq!(PusPacket::service(tm), 17); assert_eq!(PusPacket::service(tm), 17);
assert_eq!(PusPacket::subservice(tm), 2); assert_eq!(PusPacket::subservice(tm), 2);
assert!(tm.sec_header_flag()); assert!(tm.sec_header_flag());
assert_eq!(tm.len_packed(), exp_full_len); assert_eq!(tm.len_packed(), exp_full_len);
assert_eq!(tm.time_stamp(), exp_time_stamp); assert_eq!(tm.timestamp().unwrap(), exp_timestamp);
if has_user_data { if has_user_data {
assert!(!tm.user_data().is_none()); assert!(!tm.user_data().is_none());
} }