Add AddAssign impl and addition unittests
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
This commit is contained in:
parent
3f6c4c6f46
commit
39bf0c6a61
138
src/time/cds.rs
138
src/time/cds.rs
@ -11,6 +11,7 @@ use core::fmt::Debug;
|
|||||||
use core::ops::Add;
|
use core::ops::Add;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
use std::ops::AddAssign;
|
||||||
|
|
||||||
/// Base value for the preamble field for a time field parser to determine the time field type.
|
/// Base value for the preamble field for a time field parser to determine the time field type.
|
||||||
pub const P_FIELD_BASE: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
pub const P_FIELD_BASE: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
||||||
@ -352,8 +353,8 @@ 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 by checking the days of length
|
/// This function returns the correct [TimeProvider] instance from a raw byte array
|
||||||
/// field. It also checks the CCSDS time code for correctness.
|
/// by checking the days of length field. It also checks the CCSDS time code for correctness.
|
||||||
///
|
///
|
||||||
/// The time provider instance is returned as a [DynCdsTimeProvider] trait object.
|
/// The time provider instance is returned as a [DynCdsTimeProvider] trait object.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
@ -362,10 +363,7 @@ pub fn get_dyn_time_provider_from_bytes(
|
|||||||
) -> Result<Box<dyn DynCdsTimeProvider>, TimestampError> {
|
) -> Result<Box<dyn DynCdsTimeProvider>, TimestampError> {
|
||||||
let time_code = ccsds_time_code_from_p_field(buf[0]);
|
let time_code = ccsds_time_code_from_p_field(buf[0]);
|
||||||
if let Err(e) = time_code {
|
if let Err(e) = time_code {
|
||||||
return Err(TimestampError::InvalidTimeCode(
|
return Err(TimestampError::InvalidTimeCode(CcsdsTimeCodes::Cds, e));
|
||||||
CcsdsTimeCodes::Cds,
|
|
||||||
e,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let time_code = time_code.unwrap();
|
let time_code = time_code.unwrap();
|
||||||
if time_code != CcsdsTimeCodes::Cds {
|
if time_code != CcsdsTimeCodes::Cds {
|
||||||
@ -847,7 +845,7 @@ impl TimeProvider<DaysLen16Bits> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
||||||
time_provider: TimeProvider<T>,
|
time_provider: &TimeProvider<T>,
|
||||||
max_days_val: u32,
|
max_days_val: u32,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
) -> (u32, u32, Option<SubmillisPrecision>) {
|
) -> (u32, u32, Option<SubmillisPrecision>) {
|
||||||
@ -856,9 +854,9 @@ fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
|||||||
let mut precision = None;
|
let mut precision = None;
|
||||||
// Increment CCSDS days by a certain amount while also accounting for overflow.
|
// Increment CCSDS days by a certain amount while also accounting for overflow.
|
||||||
let increment_days = |ccsds_days: &mut u32, days_inc: u32| {
|
let increment_days = |ccsds_days: &mut u32, days_inc: u32| {
|
||||||
let days_addition = *ccsds_days + days_inc;
|
let days_addition: u64 = *ccsds_days as u64 + days_inc as u64;
|
||||||
if days_addition >= (max_days_val - 1) {
|
if days_addition > max_days_val as u64 {
|
||||||
*ccsds_days = days_addition - max_days_val;
|
*ccsds_days = (days_addition - max_days_val as u64) as u32;
|
||||||
} else {
|
} else {
|
||||||
*ccsds_days += days_inc;
|
*ccsds_days += days_inc;
|
||||||
}
|
}
|
||||||
@ -866,9 +864,9 @@ fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
|||||||
// Increment MS of day by a certain amount while also accounting for overflow, where
|
// Increment MS of day by a certain amount while also accounting for overflow, where
|
||||||
// the new value exceeds the MS of a day.
|
// the new value exceeds the MS of a day.
|
||||||
let increment_ms_of_day = |ms_of_day: &mut u32, ms_inc: u32, ccsds_days: &mut u32| {
|
let increment_ms_of_day = |ms_of_day: &mut u32, ms_inc: u32, ccsds_days: &mut u32| {
|
||||||
let ms_addition = *ms_of_day + ms_inc;
|
*ms_of_day += ms_inc;
|
||||||
if ms_addition >= MS_PER_DAY {
|
if *ms_of_day >= MS_PER_DAY {
|
||||||
*ms_of_day = ms_addition - MS_PER_DAY;
|
*ms_of_day -= MS_PER_DAY;
|
||||||
// Re-use existing closure to always amount for overflow.
|
// Re-use existing closure to always amount for overflow.
|
||||||
increment_days(ccsds_days, 1);
|
increment_days(ccsds_days, 1);
|
||||||
}
|
}
|
||||||
@ -877,7 +875,7 @@ fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
|||||||
match submillis_prec {
|
match submillis_prec {
|
||||||
SubmillisPrecision::Absent => {}
|
SubmillisPrecision::Absent => {}
|
||||||
SubmillisPrecision::Microseconds(mut us) => {
|
SubmillisPrecision::Microseconds(mut us) => {
|
||||||
let micros = duration.as_micros();
|
let micros = duration.subsec_micros();
|
||||||
let submilli_micros = (micros % 1000) as u16;
|
let submilli_micros = (micros % 1000) as u16;
|
||||||
us += submilli_micros;
|
us += submilli_micros;
|
||||||
if us >= 1000 {
|
if us >= 1000 {
|
||||||
@ -886,16 +884,26 @@ fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
|||||||
precision = Some(SubmillisPrecision::Microseconds(carryover_us));
|
precision = Some(SubmillisPrecision::Microseconds(carryover_us));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SubmillisPrecision::Picoseconds(_ps) => {}
|
SubmillisPrecision::Picoseconds(mut ps) => {
|
||||||
|
let nanos = duration.subsec_nanos();
|
||||||
|
let submilli_nanos = nanos % 10_u32.pow(6);
|
||||||
|
ps += submilli_nanos * 1000;
|
||||||
|
if ps >= 10_u32.pow(6) {
|
||||||
|
let carry_over_ps = ps - 10_u32.pow(6);
|
||||||
|
increment_ms_of_day(&mut next_ms_of_day, 1, &mut next_ccsds_days);
|
||||||
|
precision = Some(SubmillisPrecision::Picoseconds(carry_over_ps))
|
||||||
|
}
|
||||||
|
}
|
||||||
SubmillisPrecision::Reserved => {}
|
SubmillisPrecision::Reserved => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let full_ms = duration.as_millis();
|
let full_seconds = duration.as_secs();
|
||||||
let ms_of_day = (full_ms % MS_PER_DAY as u128) as u32;
|
let secs_of_day = (full_seconds % SECONDS_PER_DAY as u64) as u32;
|
||||||
|
let ms_of_day = secs_of_day * 1000;
|
||||||
increment_ms_of_day(&mut next_ms_of_day, ms_of_day, &mut next_ccsds_days);
|
increment_ms_of_day(&mut next_ms_of_day, ms_of_day, &mut next_ccsds_days);
|
||||||
increment_days(
|
increment_days(
|
||||||
&mut next_ccsds_days,
|
&mut next_ccsds_days,
|
||||||
(full_ms as u32 - ms_of_day) / MS_PER_DAY,
|
(full_seconds as u32 - secs_of_day) / SECONDS_PER_DAY,
|
||||||
);
|
);
|
||||||
(next_ccsds_days, next_ms_of_day, precision)
|
(next_ccsds_days, next_ms_of_day, precision)
|
||||||
}
|
}
|
||||||
@ -908,7 +916,7 @@ impl Add<Duration> for TimeProvider<DaysLen16Bits> {
|
|||||||
|
|
||||||
fn add(self, duration: Duration) -> Self::Output {
|
fn add(self, duration: Duration) -> Self::Output {
|
||||||
let (next_ccsds_days, next_ms_of_day, precision) =
|
let (next_ccsds_days, next_ms_of_day, precision) =
|
||||||
add_for_max_ccsds_days_val(self, u16::MAX as u32, duration);
|
add_for_max_ccsds_days_val(&self, u16::MAX as u32, duration);
|
||||||
let mut provider = Self::new_with_u16_days(next_ccsds_days as u16, next_ms_of_day);
|
let mut provider = Self::new_with_u16_days(next_ccsds_days as u16, next_ms_of_day);
|
||||||
if let Some(prec) = precision {
|
if let Some(prec) = precision {
|
||||||
provider.set_submillis_precision(prec);
|
provider.set_submillis_precision(prec);
|
||||||
@ -925,7 +933,7 @@ impl Add<Duration> for TimeProvider<DaysLen24Bits> {
|
|||||||
|
|
||||||
fn add(self, duration: Duration) -> Self::Output {
|
fn add(self, duration: Duration) -> Self::Output {
|
||||||
let (next_ccsds_days, next_ms_of_day, precision) =
|
let (next_ccsds_days, next_ms_of_day, precision) =
|
||||||
add_for_max_ccsds_days_val(self, MAX_DAYS_24_BITS, duration);
|
add_for_max_ccsds_days_val(&self, MAX_DAYS_24_BITS, duration);
|
||||||
let mut provider = Self::new_with_u24_days(next_ccsds_days, next_ms_of_day).unwrap();
|
let mut provider = Self::new_with_u24_days(next_ccsds_days, next_ms_of_day).unwrap();
|
||||||
if let Some(prec) = precision {
|
if let Some(prec) = precision {
|
||||||
provider.set_submillis_precision(prec);
|
provider.set_submillis_precision(prec);
|
||||||
@ -934,6 +942,32 @@ impl Add<Duration> for TimeProvider<DaysLen24Bits> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows adding an duration in form of an offset. Please note that the CCSDS days will rollover
|
||||||
|
/// when they overflow, because addition needs to be infallible. The user needs to check for a
|
||||||
|
/// days overflow when this is a possibility and might be a problem.
|
||||||
|
impl AddAssign<Duration> for TimeProvider<DaysLen16Bits> {
|
||||||
|
fn add_assign(&mut self, duration: Duration) {
|
||||||
|
let (next_ccsds_days, next_ms_of_day, precision) =
|
||||||
|
add_for_max_ccsds_days_val(self, u16::MAX as u32, duration);
|
||||||
|
self.ccsds_days = next_ccsds_days as u16;
|
||||||
|
self.ms_of_day = next_ms_of_day;
|
||||||
|
self.submillis_precision = precision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows adding an duration in form of an offset. Please note that the CCSDS days will rollover
|
||||||
|
/// when they overflow, because addition needs to be infallible. The user needs to check for a
|
||||||
|
/// days overflow when this is a possibility and might be a problem.
|
||||||
|
impl AddAssign<Duration> for TimeProvider<DaysLen24Bits> {
|
||||||
|
fn add_assign(&mut self, duration: Duration) {
|
||||||
|
let (next_ccsds_days, next_ms_of_day, precision) =
|
||||||
|
add_for_max_ccsds_days_val(self, MAX_DAYS_24_BITS, duration);
|
||||||
|
self.ccsds_days = next_ccsds_days;
|
||||||
|
self.ms_of_day = next_ms_of_day;
|
||||||
|
self.submillis_precision = precision;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<DateTime<Utc>> for TimeProvider<DaysLen16Bits> {
|
impl TryFrom<DateTime<Utc>> for TimeProvider<DaysLen16Bits> {
|
||||||
type Error = TimestampError;
|
type Error = TimestampError;
|
||||||
|
|
||||||
@ -1399,7 +1433,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_creation_from_dt() {
|
fn test_creation_from_dt_u16_days() {
|
||||||
let subsec_millis = 250;
|
let subsec_millis = 250;
|
||||||
let naivedatetime_utc = NaiveDate::from_ymd_opt(2023, 01, 14)
|
let naivedatetime_utc = NaiveDate::from_ymd_opt(2023, 01, 14)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -1421,6 +1455,29 @@ mod tests {
|
|||||||
assert_eq!(time_provider, time_provider_2);
|
assert_eq!(time_provider, time_provider_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_creation_from_dt_u24_days() {
|
||||||
|
let subsec_millis = 250;
|
||||||
|
let naivedatetime_utc = NaiveDate::from_ymd_opt(2023, 01, 14)
|
||||||
|
.unwrap()
|
||||||
|
.and_hms_milli_opt(16, 49, 30, subsec_millis)
|
||||||
|
.unwrap();
|
||||||
|
let datetime_utc = DateTime::<Utc>::from_utc(naivedatetime_utc, Utc);
|
||||||
|
let time_provider = TimeProvider::from_dt_with_u24_days(&datetime_utc).unwrap();
|
||||||
|
// 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.
|
||||||
|
assert_eq!(time_provider.ccsds_days, 23754);
|
||||||
|
assert_eq!(
|
||||||
|
time_provider.ms_of_day,
|
||||||
|
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
||||||
|
);
|
||||||
|
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
||||||
|
let time_provider_2: TimeProvider<DaysLen24Bits> =
|
||||||
|
datetime_utc.try_into().expect("conversion failed");
|
||||||
|
// Test the TryInto trait impl
|
||||||
|
assert_eq!(time_provider, time_provider_2);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_creation_from_dt_us_prec() {
|
fn test_creation_from_dt_us_prec() {
|
||||||
// 250 ms + 500 us
|
// 250 ms + 500 us
|
||||||
@ -1566,6 +1623,45 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addition_u16_days_day_increment() {
|
||||||
|
let mut provider = TimeProvider::new_with_u16_days(0, MS_PER_DAY - 5 * 1000);
|
||||||
|
let seconds_offset = Duration::from_secs(10);
|
||||||
|
assert_eq!(provider.ccsds_days, 0);
|
||||||
|
assert_eq!(provider.ms_of_day, MS_PER_DAY - 5 * 1000);
|
||||||
|
provider += seconds_offset;
|
||||||
|
assert_eq!(provider.ccsds_days, 1);
|
||||||
|
assert_eq!(provider.ms_of_day, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addition_u16_days() {
|
||||||
|
let mut provider = TimeProvider::new_with_u16_days(0, 0);
|
||||||
|
let seconds_offset = Duration::from_secs(5);
|
||||||
|
assert_eq!(provider.ccsds_days, 0);
|
||||||
|
assert_eq!(provider.ms_of_day, 0);
|
||||||
|
provider += seconds_offset;
|
||||||
|
assert_eq!(provider.ms_of_day, 5000);
|
||||||
|
// Add one day and test Add operator
|
||||||
|
let provider2 = provider + Duration::from_secs(60 * 60 * 24);
|
||||||
|
assert_eq!(provider2.ccsds_days, 1);
|
||||||
|
assert_eq!(provider2.ms_of_day, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_addition_u24_days() {
|
||||||
|
let mut provider = TimeProvider::new_with_u24_days(u16::MAX as u32, 0).unwrap();
|
||||||
|
let seconds_offset = Duration::from_secs(5);
|
||||||
|
assert_eq!(provider.ccsds_days, u16::MAX as u32);
|
||||||
|
assert_eq!(provider.ms_of_day, 0);
|
||||||
|
provider += seconds_offset;
|
||||||
|
assert_eq!(provider.ms_of_day, 5000);
|
||||||
|
// Add one day and test Add operator
|
||||||
|
let provider2 = provider + Duration::from_secs(60 * 60 * 24);
|
||||||
|
assert_eq!(provider2.ccsds_days, u16::MAX as u32 + 1);
|
||||||
|
assert_eq!(provider2.ms_of_day, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
fn test_serialization() {
|
fn test_serialization() {
|
||||||
|
Loading…
Reference in New Issue
Block a user