From dcf720ad67b9b40da6ce6f28a66b324dcd569dd4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Mon, 18 Mar 2024 15:13:19 +0100 Subject: [PATCH] more improvements for CUC API --- src/time/cuc.rs | 419 ++++++++++++++++++++++++++---------------------- 1 file changed, 230 insertions(+), 189 deletions(-) diff --git a/src/time/cuc.rs b/src/time/cuc.rs index b18a8be..6671327 100644 --- a/src/time/cuc.rs +++ b/src/time/cuc.rs @@ -61,14 +61,14 @@ impl TryFrom for FractionalResolution { } } -/// Please note that this function will panic if the fractional value is not smaller than +/// Please note that this function will panic if the fractional counter is not smaller than /// the maximum number of fractions allowed for the particular resolution. /// (e.g. passing 270 when the resolution only allows 255 values). #[inline] pub fn convert_fractional_part_to_ns(fractional_part: FractionalPart) -> u64 { - let div = fractional_res_to_div(fractional_part.0); - assert!(fractional_part.1 <= div); - 10_u64.pow(9) * fractional_part.1 as u64 / div as u64 + let div = fractional_res_to_div(fractional_part.resolution); + assert!(fractional_part.counter <= div); + 10_u64.pow(9) * fractional_part.counter as u64 / div as u64 } #[inline(always)] @@ -84,22 +84,22 @@ pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 { /// Please note that this function will panic if the passed nanoseconds exceeds 1 second /// as a nanosecond (10 to the power of 9). Furthermore, it will return [None] if the /// given resolution is [FractionalResolution::Seconds]. -pub fn fractional_part_from_subsec_ns( - res: FractionalResolution, - ns: u64, -) -> Option { +pub fn fractional_part_from_subsec_ns(res: FractionalResolution, ns: u64) -> FractionalPart { if res == FractionalResolution::Seconds { - return None; + return FractionalPart::new_with_seconds_resolution(); } let sec_as_ns = 10_u64.pow(9); if ns > sec_as_ns { panic!("passed nanosecond value larger than 1 second"); } - let resolution = fractional_res_to_div(res) as u64; + let resolution_divisor = fractional_res_to_div(res) as u64; // This is probably the cheapest way to calculate the fractional part without using expensive // floating point division. - let fractional_part = ns * (resolution + 1) / sec_as_ns; - Some(FractionalPart(res, fractional_part as u32)) + let fractional_counter = ns * (resolution_divisor + 1) / sec_as_ns; + FractionalPart { + resolution: res, + counter: fractional_counter as u32, + } } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -146,9 +146,57 @@ impl Error for CucError {} #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct WidthCounterPair(u8, u32); + #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct FractionalPart(FractionalResolution, u32); +pub struct FractionalPart { + pub resolution: FractionalResolution, + pub counter: u32, +} + +impl FractionalPart { + pub const fn new(resolution: FractionalResolution, counter: u32) -> Self { + let div = fractional_res_to_div(resolution); + assert!(counter <= div, "invalid counter for resolution"); + Self { + resolution, + counter, + } + } + + /// An empty fractional part for second resolution only. + pub const fn new_with_seconds_resolution() -> Self { + Self::new(FractionalResolution::Seconds, 0) + } + + /// Helper method which simply calls [Self::new_with_seconds_resolution]. + pub const fn new_empty() -> Self { + Self::new_with_seconds_resolution() + } + + pub fn new_checked(resolution: FractionalResolution, counter: u32) -> Option { + let div = fractional_res_to_div(resolution); + if counter > div { + return None; + } + Some(Self { + resolution, + counter, + }) + } + + pub fn resolution(&self) -> FractionalResolution { + self.resolution + } + + pub fn counter(&self) -> u32 { + self.counter + } + + pub fn no_fractional_part(&self) -> bool { + self.resolution == FractionalResolution::Seconds + } +} /// This object is the abstraction for the CCSDS Unsegmented Time Code (CUC) using the CCSDS epoch /// and a small preamble field. @@ -202,7 +250,7 @@ pub struct FractionalPart(FractionalResolution, u32); pub struct CucTime { pfield: u8, counter: WidthCounterPair, - fractions: Option, + fractions: FractionalPart, } /// This object is a wrapper object around the [CucTime] object which also tracks @@ -232,12 +280,16 @@ impl CucTime { /// Create a time provider with a four byte counter and no fractional part. pub fn new(counter: u32) -> Self { // These values are definitely valid, so it is okay to unwrap here. - Self::new_generic(WidthCounterPair(4, counter), None).unwrap() + Self::new_generic( + WidthCounterPair(4, counter), + FractionalPart::new_with_seconds_resolution(), + ) + .unwrap() } /// Like [CucTime::new] but allow to supply a fractional part as well. pub fn new_with_fractions(counter: u32, fractions: FractionalPart) -> Result { - Self::new_generic(WidthCounterPair(4, counter), Some(fractions)) + Self::new_generic(WidthCounterPair(4, counter), fractions) } /// Fractions with a resolution of ~ 4 ms @@ -245,10 +297,10 @@ impl CucTime { // These values are definitely valid, so it is okay to unwrap here. Self::new_generic( WidthCounterPair(4, counter), - Some(FractionalPart( - FractionalResolution::FourMs, - subsec_fractions as u32, - )), + FractionalPart { + resolution: FractionalResolution::FourMs, + counter: subsec_fractions as u32, + }, ) .unwrap() } @@ -258,10 +310,10 @@ impl CucTime { // These values are definitely valid, so it is okay to unwrap here. Self::new_generic( WidthCounterPair(4, counter), - Some(FractionalPart( - FractionalResolution::FifteenUs, - subsec_fractions as u32, - )), + FractionalPart { + resolution: FractionalResolution::FifteenUs, + counter: subsec_fractions as u32, + }, ) .unwrap() } @@ -272,10 +324,10 @@ impl CucTime { pub fn new_with_fine_fractions(counter: u32, subsec_fractions: u32) -> Result { Self::new_generic( WidthCounterPair(4, counter), - Some(FractionalPart( - FractionalResolution::SixtyNs, - subsec_fractions, - )), + FractionalPart { + resolution: FractionalResolution::SixtyNs, + counter: subsec_fractions, + }, ) } @@ -308,7 +360,7 @@ impl CucTime { } let fractions = fractional_part_from_subsec_ns(fraction_resolution, now.subsec_nanos() as u64); - Self::new_with_fractions(counter, fractions.unwrap()) + Self::new_with_fractions(counter, fractions) .map_err(|e| StdTimestampError::Timestamp(e.into())) } @@ -328,11 +380,12 @@ impl CucTime { .1 .checked_add(leap_seconds) .ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?; - if self.fractions.is_some() { + if let FractionalResolution::Seconds = self.fractions.resolution { self.fractions = fractional_part_from_subsec_ns( - self.fractions.unwrap().0, + self.fractions.resolution, now.subsec_nanos() as u64, ); + return Ok(()); } Ok(()) } @@ -386,7 +439,11 @@ impl CucTime { pub fn new_u16_counter(counter: u16) -> Self { // These values are definitely valid, so it is okay to unwrap here. - Self::new_generic(WidthCounterPair(2, counter as u32), None).unwrap() + Self::new_generic( + WidthCounterPair(2, counter as u32), + FractionalPart::new_with_seconds_resolution(), + ) + .unwrap() } pub fn ccsds_time_code(&self) -> CcsdsTimeCode { @@ -405,7 +462,8 @@ impl CucTime { self.counter.1 } - pub fn width_fractions_pair(&self) -> Option { + /// Subsecond fractional part of the CUC time. + pub fn fractions(&self) -> FractionalPart { self.fractions } @@ -415,7 +473,7 @@ impl CucTime { pub fn set_fractions(&mut self, fractions: FractionalPart) -> Result<(), CucError> { Self::verify_fractions_value(fractions)?; - self.fractions = Some(fractions); + self.fractions = fractions; self.update_p_field_fractions(); Ok(()) } @@ -424,22 +482,16 @@ impl CucTime { /// to 0 if the resolution changes. pub fn set_fractional_resolution(&mut self, res: FractionalResolution) { if res == FractionalResolution::Seconds { - self.fractions = None; + self.fractions = FractionalPart::new_with_seconds_resolution(); } - let mut update_fractions = true; - if let Some(existing_fractions) = self.fractions { - if existing_fractions.0 == res { - update_fractions = false; - } - }; - if update_fractions { - self.fractions = Some(FractionalPart(res, 0)); + if res != self.fractions().resolution() { + self.fractions = FractionalPart::new(res, 0); } } pub fn new_generic( counter: WidthCounterPair, - fractions: Option, + fractions: FractionalPart, ) -> Result { Self::verify_counter_width(counter.0)?; if counter.1 > (2u64.pow(counter.0 as u32 * 8) - 1) as u32 { @@ -448,17 +500,15 @@ impl CucTime { counter: counter.1.into(), }); } - if let Some(fractions) = fractions { - Self::verify_fractions_value(fractions)?; - } + Self::verify_fractions_value(fractions)?; Ok(Self { - pfield: Self::build_p_field(counter.0, fractions.map(|v| v.0)), + pfield: Self::build_p_field(counter.0, fractions.resolution), counter, fractions, }) } - fn build_p_field(counter_width: u8, fractions_width: Option) -> u8 { + fn build_p_field(counter_width: u8, resolution: FractionalResolution) -> u8 { let mut pfield = P_FIELD_BASE; if !(1..=4).contains(&counter_width) { // Okay to panic here, this function is private and all input values should @@ -466,25 +516,20 @@ impl CucTime { panic!("invalid counter width {} for cuc timestamp", counter_width); } pfield |= (counter_width - 1) << 2; - if let Some(fractions_width) = fractions_width { - if !(1..=3).contains(&(fractions_width as u8)) { + if resolution != FractionalResolution::Seconds { + if !(1..=3).contains(&(resolution as u8)) { // Okay to panic here, this function is private and all input values should // have been sanitized - panic!( - "invalid fractions width {:?} for cuc timestamp", - fractions_width - ); + panic!("invalid fractions width {:?} for cuc timestamp", resolution); } - pfield |= fractions_width as u8; + pfield |= resolution as u8; } pfield } fn update_p_field_fractions(&mut self) { self.pfield &= !(0b11); - if let Some(fractions) = self.fractions { - self.pfield |= fractions.0 as u8; - } + self.pfield |= self.fractions.resolution() as u8; } #[inline] @@ -542,10 +587,10 @@ impl CucTime { } fn verify_fractions_value(val: FractionalPart) -> Result<(), CucError> { - if val.1 > 2u32.pow((val.0 as u32) * 8) - 1 { + if val.counter > 2u32.pow((val.resolution as u32) * 8) - 1 { return Err(CucError::InvalidFractions { - resolution: val.0, - value: val.1 as u64, + resolution: val.resolution, + value: val.counter as u64, }); } Ok(()) @@ -556,14 +601,11 @@ impl CucTime { } fn subsec_nanos(&self) -> u32 { - if let Some(fractions) = self.fractions { - if fractions.0 == FractionalResolution::Seconds { - return 0; - } - // Rounding down here is the correct approach. - return convert_fractional_part_to_ns(fractions) as u32; + if self.fractions.resolution() == FractionalResolution::Seconds { + return 0; } - 0 + // Rounding down here is the correct approach. + convert_fractional_part_to_ns(self.fractions) as u32 } } @@ -616,29 +658,29 @@ impl TimeReader for CucTime { _ => panic!("unreachable match arm"), }; current_idx += cntr_len as usize; - let mut fractions = None; + let mut fractions = FractionalPart::new_with_seconds_resolution(); if fractions_len > 0 { match fractions_len { 1 => { - fractions = Some(FractionalPart( + fractions = FractionalPart::new( fractions_len.try_into().unwrap(), buf[current_idx] as u32, - )) + ) } 2 => { - fractions = Some(FractionalPart( + fractions = FractionalPart::new( fractions_len.try_into().unwrap(), u16::from_be_bytes(buf[current_idx..current_idx + 2].try_into().unwrap()) as u32, - )) + ) } 3 => { let mut tmp_buf: [u8; 4] = [0; 4]; tmp_buf[1..4].copy_from_slice(&buf[current_idx..current_idx + 3]); - fractions = Some(FractionalPart( + fractions = FractionalPart::new( fractions_len.try_into().unwrap(), u32::from_be_bytes(tmp_buf), - )) + ) } _ => panic!("unreachable match arm"), } @@ -680,18 +722,15 @@ impl TimeWriter for CucTime { _ => panic!("invalid counter width value"), } current_idx += self.counter.0 as usize; - if let Some(fractions) = self.fractions { - match fractions.0 { - FractionalResolution::FourMs => bytes[current_idx] = fractions.1 as u8, - FractionalResolution::FifteenUs => bytes[current_idx..current_idx + 2] - .copy_from_slice(&(fractions.1 as u16).to_be_bytes()), - FractionalResolution::SixtyNs => bytes[current_idx..current_idx + 3] - .copy_from_slice(&fractions.1.to_be_bytes()[1..4]), - // Should also never happen - _ => panic!("invalid fractions value"), - } - current_idx += fractions.0 as usize; + match self.fractions.resolution() { + FractionalResolution::FourMs => bytes[current_idx] = self.fractions.counter as u8, + FractionalResolution::FifteenUs => bytes[current_idx..current_idx + 2] + .copy_from_slice(&(self.fractions.counter as u16).to_be_bytes()), + FractionalResolution::SixtyNs => bytes[current_idx..current_idx + 3] + .copy_from_slice(&self.fractions.counter.to_be_bytes()[1..4]), + _ => (), } + current_idx += self.fractions.resolution as usize; Ok(current_idx) } @@ -722,11 +761,12 @@ impl CcsdsTimeProvider for CucTimeWithLeapSecs { } } -fn get_provider_values_after_duration_addition( - provider: &CucTime, +// TODO: Introduce more overflow checks here. +fn get_time_values_after_duration_addition( + time: &CucTime, duration: Duration, -) -> (u32, Option) { - let mut new_counter = provider.counter.1; +) -> (u32, FractionalPart) { + let mut new_counter = time.counter.1; let subsec_nanos = duration.subsec_nanos(); let mut increment_counter = |amount: u32| { let mut sum: u64 = 0; @@ -738,7 +778,7 @@ fn get_provider_values_after_duration_addition( } new_counter = sum as u32; }; - match provider.counter.0 { + match time.counter.0 { 1 => counter_inc_handler(u8::MAX as u64), 2 => counter_inc_handler(u16::MAX as u64), 3 => counter_inc_handler((2_u32.pow(24) - 1) as u64), @@ -749,25 +789,21 @@ fn get_provider_values_after_duration_addition( } } }; - let fractional_part = if let Some(fractional_part) = &provider.fractions { - let fractional_increment = - fractional_part_from_subsec_ns(fractional_part.0, subsec_nanos as u64).unwrap(); - let mut increment_fractions = |resolution| { - let mut new_fractions = fractional_part.1 + fractional_increment.1; - let max_fractions = fractional_res_to_div(resolution); - if new_fractions > max_fractions { - increment_counter(1); - new_fractions -= max_fractions; - } - Some(FractionalPart(resolution, new_fractions)) - }; - match fractional_increment.0 { - FractionalResolution::Seconds => None, - _ => increment_fractions(fractional_increment.0), + let resolution = time.fractions().resolution(); + let fractional_increment = fractional_part_from_subsec_ns(resolution, subsec_nanos as u64); + let mut fractional_part = FractionalPart::new_with_seconds_resolution(); + if resolution != FractionalResolution::Seconds { + let mut new_fractions = time.fractions().counter() + fractional_increment.counter; + let max_fractions = fractional_res_to_div(resolution); + if new_fractions > max_fractions { + increment_counter(1); + new_fractions -= max_fractions; } - } else { - None - }; + fractional_part = FractionalPart { + resolution, + counter: new_fractions, + } + } increment_counter(duration.as_secs() as u32); (new_counter, fractional_part) } @@ -775,11 +811,9 @@ fn get_provider_values_after_duration_addition( impl AddAssign for CucTime { fn add_assign(&mut self, duration: Duration) { let (new_counter, new_fractional_part) = - get_provider_values_after_duration_addition(self, duration); + get_time_values_after_duration_addition(self, duration); self.counter.1 = new_counter; - if self.fractions.is_some() { - self.fractions = new_fractional_part; - } + self.fractions = new_fractional_part; } } @@ -788,12 +822,9 @@ impl Add for CucTime { fn add(self, duration: Duration) -> Self::Output { let (new_counter, new_fractional_part) = - get_provider_values_after_duration_addition(&self, duration); - if let Some(fractional_part) = new_fractional_part { - // The generated fractional part should always be valid, so its okay to unwrap here. - return Self::new_with_fractions(new_counter, fractional_part).unwrap(); - } - Self::new(new_counter) + get_time_values_after_duration_addition(&self, duration); + // The generated fractional part should always be valid, so its okay to unwrap here. + Self::new_with_fractions(new_counter, new_fractional_part).unwrap() } } @@ -802,12 +833,9 @@ impl Add for &CucTime { fn add(self, duration: Duration) -> Self::Output { let (new_counter, new_fractional_part) = - get_provider_values_after_duration_addition(self, duration); - if let Some(fractional_part) = new_fractional_part { - // The generated fractional part should always be valid, so its okay to unwrap here. - return Self::Output::new_with_fractions(new_counter, fractional_part).unwrap(); - } - Self::Output::new(new_counter) + get_time_values_after_duration_addition(self, duration); + // The generated fractional part should always be valid, so its okay to unwrap here. + Self::Output::new_with_fractions(new_counter, new_fractional_part).unwrap() } } @@ -835,8 +863,8 @@ mod tests { let counter = zero_cuc.width_counter_pair(); assert_eq!(counter.0, 4); assert_eq!(counter.1, 0); - let fractions = zero_cuc.width_fractions_pair(); - assert!(fractions.is_none()); + let fractions = zero_cuc.fractions(); + assert_eq!(fractions, FractionalPart::new_with_seconds_resolution()); let dt = ccsds_cuc.chrono_date_time(); if let chrono::LocalResult::Single(dt) = dt { assert_eq!(dt.year(), 1958); @@ -851,7 +879,8 @@ mod tests { #[test] fn test_write_no_fractions() { let mut buf: [u8; 16] = [0; 16]; - let zero_cuc = CucTime::new_generic(WidthCounterPair(4, 0x20102030), None); + let zero_cuc = + CucTime::new_generic(WidthCounterPair(4, 0x20102030), FractionalPart::new_empty()); assert!(zero_cuc.is_ok()); let zero_cuc = zero_cuc.unwrap(); let res = zero_cuc.write_to_bytes(&mut buf); @@ -893,12 +922,16 @@ mod tests { #[test] fn test_read_no_fractions() { let mut buf: [u8; 16] = [0; 16]; - let zero_cuc = CucTime::new_generic(WidthCounterPair(4, 0x20102030), None).unwrap(); + let zero_cuc = CucTime::new_generic( + WidthCounterPair(4, 0x20102030), + FractionalPart::new_with_seconds_resolution(), + ) + .unwrap(); zero_cuc.write_to_bytes(&mut buf).unwrap(); let cuc_read_back = CucTime::from_bytes(&buf).expect("reading cuc timestamp failed"); assert_eq!(cuc_read_back, zero_cuc); assert_eq!(cuc_read_back.width_counter_pair().1, 0x20102030); - assert_eq!(cuc_read_back.width_fractions_pair(), None); + assert_eq!(cuc_read_back.fractions(), FractionalPart::new_empty()); } #[test] @@ -937,7 +970,7 @@ mod tests { #[test] fn write_and_read_tiny_stamp() { let mut buf = [0; 2]; - let cuc = CucTime::new_generic(WidthCounterPair(1, 200), None); + let cuc = CucTime::new_generic(WidthCounterPair(1, 200), FractionalPart::new_empty()); assert!(cuc.is_ok()); let cuc = cuc.unwrap(); assert_eq!(cuc.len_as_bytes(), 2); @@ -955,7 +988,7 @@ mod tests { #[test] fn write_slightly_larger_stamp() { let mut buf = [0; 4]; - let cuc = CucTime::new_generic(WidthCounterPair(2, 40000), None); + let cuc = CucTime::new_generic(WidthCounterPair(2, 40000), FractionalPart::new_empty()); assert!(cuc.is_ok()); let cuc = cuc.unwrap(); assert_eq!(cuc.len_as_bytes(), 3); @@ -976,7 +1009,10 @@ mod tests { #[test] fn write_read_three_byte_cntr_stamp() { let mut buf = [0; 4]; - let cuc = CucTime::new_generic(WidthCounterPair(3, 2_u32.pow(24) - 2), None); + let cuc = CucTime::new_generic( + WidthCounterPair(3, 2_u32.pow(24) - 2), + FractionalPart::new_empty(), + ); assert!(cuc.is_ok()); let cuc = cuc.unwrap(); assert_eq!(cuc.len_as_bytes(), 4); @@ -1033,9 +1069,8 @@ mod tests { fn test_write_with_coarse_fractions() { let mut buf: [u8; 16] = [0; 16]; let cuc = CucTime::new_with_coarse_fractions(0x30201060, 120); - assert!(cuc.fractions.is_some()); - assert_eq!(cuc.fractions.unwrap().1, 120); - assert_eq!(cuc.fractions.unwrap().0, FractionalResolution::FourMs); + assert_eq!(cuc.fractions().counter(), 120); + assert_eq!(cuc.fractions().resolution(), FractionalResolution::FourMs); let res = cuc.write_to_bytes(&mut buf); assert!(res.is_ok()); let written = res.unwrap(); @@ -1115,44 +1150,49 @@ mod tests { #[test] fn test_fractional_converter() { - let ns = convert_fractional_part_to_ns(FractionalPart(FractionalResolution::FourMs, 2)); + let ns = convert_fractional_part_to_ns(FractionalPart { + resolution: FractionalResolution::FourMs, + counter: 2, + }); // The formula for this is 2/255 * 10e9 = 7.843.137. assert_eq!(ns, 7843137); // This is the largest value we should be able to pass without this function panicking. - let ns = convert_fractional_part_to_ns(FractionalPart( - FractionalResolution::SixtyNs, - 2_u32.pow(24) - 2, - )); + let ns = convert_fractional_part_to_ns(FractionalPart { + resolution: FractionalResolution::SixtyNs, + counter: 2_u32.pow(24) - 2, + }); assert_eq!(ns, 999999940); } #[test] #[should_panic] fn test_fractional_converter_invalid_input() { - convert_fractional_part_to_ns(FractionalPart(FractionalResolution::FourMs, 256)); + convert_fractional_part_to_ns(FractionalPart { + resolution: FractionalResolution::FourMs, + counter: 256, + }); } #[test] #[should_panic] fn test_fractional_converter_invalid_input_2() { - convert_fractional_part_to_ns(FractionalPart( - FractionalResolution::SixtyNs, - 2_u32.pow(32) - 1, - )); + convert_fractional_part_to_ns(FractionalPart { + resolution: FractionalResolution::SixtyNs, + counter: 2_u32.pow(32) - 1, + }); } #[test] fn fractional_part_formula() { - let fractional_part = - fractional_part_from_subsec_ns(FractionalResolution::FourMs, 7843138).unwrap(); - assert_eq!(fractional_part.1, 2); + let fractional_part = fractional_part_from_subsec_ns(FractionalResolution::FourMs, 7843138); + assert_eq!(fractional_part.counter, 2); } #[test] fn fractional_part_formula_2() { let fractional_part = - fractional_part_from_subsec_ns(FractionalResolution::FourMs, 12000000).unwrap(); - assert_eq!(fractional_part.1, 3); + fractional_part_from_subsec_ns(FractionalResolution::FourMs, 12000000); + assert_eq!(fractional_part.counter, 3); } #[test] @@ -1165,86 +1205,86 @@ mod tests { let fractional_part = fractional_part_from_subsec_ns( FractionalResolution::FifteenUs, hundred_fractions_and_some, - ) - .unwrap(); - assert_eq!(fractional_part.1, 100); + ); + assert_eq!(fractional_part.counter, 100); // Using exactly 101.0 can yield values which will later be rounded down to 100 let hundred_and_one_fractions = (101.001 * one_fraction_with_width_two_in_ns).floor() as u64; let fractional_part = fractional_part_from_subsec_ns( FractionalResolution::FifteenUs, hundred_and_one_fractions, - ) - .unwrap(); - assert_eq!(fractional_part.1, 101); + ); + assert_eq!(fractional_part.counter, 101); } #[test] fn update_fractions() { let mut stamp = CucTime::new(2000); - let res = stamp.set_fractions(FractionalPart(FractionalResolution::SixtyNs, 5000)); + let res = stamp.set_fractions(FractionalPart { + resolution: FractionalResolution::SixtyNs, + counter: 5000, + }); assert!(res.is_ok()); - assert!(stamp.fractions.is_some()); - let fractions = stamp.fractions.unwrap(); - assert_eq!(fractions.0, FractionalResolution::SixtyNs); - assert_eq!(fractions.1, 5000); + assert_eq!( + stamp.fractions().resolution(), + FractionalResolution::SixtyNs + ); + assert_eq!(stamp.fractions().counter(), 5000); } #[test] fn set_fract_resolution() { let mut stamp = CucTime::new(2000); stamp.set_fractional_resolution(FractionalResolution::SixtyNs); - assert!(stamp.fractions.is_some()); - let fractions = stamp.fractions.unwrap(); - assert_eq!(fractions.0, FractionalResolution::SixtyNs); - assert_eq!(fractions.1, 0); + assert_eq!( + stamp.fractions().resolution(), + FractionalResolution::SixtyNs + ); + assert_eq!(stamp.fractions().counter(), 0); let res = stamp.update_from_now(LEAP_SECONDS); assert!(res.is_ok()); } #[test] fn test_small_fraction_floored_to_zero() { - let fractions = fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 59).unwrap(); - assert_eq!(fractions.1, 0); + let fractions = fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 59); + assert_eq!(fractions.counter, 0); } #[test] fn test_small_fraction_becomes_fractional_part() { - let fractions = fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 61).unwrap(); - assert_eq!(fractions.1, 1); + let fractions = fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 61); + assert_eq!(fractions.counter, 1); } #[test] fn test_smallest_resolution_small_nanoseconds_floored_to_zero() { let fractions = - fractional_part_from_subsec_ns(FractionalResolution::FourMs, 3800 * 1e3 as u64) - .unwrap(); - assert_eq!(fractions.1, 0); + fractional_part_from_subsec_ns(FractionalResolution::FourMs, 3800 * 1e3 as u64); + assert_eq!(fractions.counter, 0); } #[test] fn test_smallest_resolution_small_nanoseconds_becomes_one_fraction() { let fractions = - fractional_part_from_subsec_ns(FractionalResolution::FourMs, 4000 * 1e3 as u64) - .unwrap(); - assert_eq!(fractions.1, 1); + fractional_part_from_subsec_ns(FractionalResolution::FourMs, 4000 * 1e3 as u64); + assert_eq!(fractions.counter, 1); } #[test] fn test_smallest_resolution_large_nanoseconds_becomes_largest_fraction() { let fractions = - fractional_part_from_subsec_ns(FractionalResolution::FourMs, 10u64.pow(9) - 1).unwrap(); - assert_eq!(fractions.1, 2_u32.pow(8) - 1); + fractional_part_from_subsec_ns(FractionalResolution::FourMs, 10u64.pow(9) - 1); + assert_eq!(fractions.counter, 2_u32.pow(8) - 1); } #[test] fn test_largest_fractions_with_largest_resolution() { let fractions = - fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 10u64.pow(9) - 1) - .unwrap(); + fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 10u64.pow(9) - 1); // The value can not be larger than representable by 3 bytes // Assert that the maximum resolution can be reached - assert_eq!(fractions.1, 2_u32.pow(3 * 8) - 1); + assert_eq!(fractions.counter, 2_u32.pow(3 * 8) - 1); } fn check_stamp_after_addition(cuc_stamp: &CucTime) { @@ -1254,14 +1294,14 @@ mod tests { CcsdsTimeCode::CucCcsdsEpoch ); assert_eq!(cuc_stamp.width_counter_pair().1, 202); - let fractions = cuc_stamp.width_fractions_pair().unwrap().1; + let fractions = cuc_stamp.fractions().counter(); let expected_val = (0.5 * fractional_res_to_div(FractionalResolution::FifteenUs) as f64).ceil() as u32; assert_eq!(fractions, expected_val); let cuc_stamp2 = cuc_stamp + Duration::from_millis(501); // What I would roughly expect assert_eq!(cuc_stamp2.counter.1, 203); - assert!(cuc_stamp2.fractions.unwrap().1 < 100); + assert!(cuc_stamp2.fractions().counter() < 100); assert!(cuc_stamp2.subsec_millis() <= 1); } @@ -1289,7 +1329,7 @@ mod tests { let duration = Duration::from_millis(2000); cuc_stamp += duration; assert_eq!(cuc_stamp.counter(), 202); - assert_eq!(cuc_stamp.width_fractions_pair(), None); + assert_eq!(cuc_stamp.fractions(), FractionalPart::new_empty()); } #[test] @@ -1298,11 +1338,12 @@ mod tests { let duration = Duration::from_millis(2000); let new_stamp = cuc_stamp + duration; assert_eq!(new_stamp.counter(), 202); - assert_eq!(new_stamp.width_fractions_pair(), None); + assert_eq!(new_stamp.fractions(), FractionalPart::new_empty()); } #[test] fn add_duration_overflow() { - let mut cuc_stamp = CucTime::new_generic(WidthCounterPair(1, 255), None).unwrap(); + let mut cuc_stamp = + CucTime::new_generic(WidthCounterPair(1, 255), FractionalPart::new_empty()).unwrap(); let duration = Duration::from_secs(10); cuc_stamp += duration; assert_eq!(cuc_stamp.counter.1, 10); @@ -1310,7 +1351,7 @@ mod tests { #[test] fn test_invalid_width_param() { - let error = CucTime::new_generic(WidthCounterPair(8, 0), None); + let error = CucTime::new_generic(WidthCounterPair(8, 0), FractionalPart::new_empty()); assert!(error.is_err()); let error = error.unwrap_err(); if let CucError::InvalidCounterWidth(width) = error { @@ -1350,7 +1391,7 @@ mod tests { #[test] fn test_invalid_counter() { - let cuc_error = CucTime::new_generic(WidthCounterPair(1, 256), None); + let cuc_error = CucTime::new_generic(WidthCounterPair(1, 256), FractionalPart::new_empty()); assert!(cuc_error.is_err()); let cuc_error = cuc_error.unwrap_err(); if let CucError::InvalidCounter { width, counter } = cuc_error {