more improvements for CUC API
This commit is contained in:
parent
247cc72753
commit
dcf720ad67
419
src/time/cuc.rs
419
src/time/cuc.rs
@ -61,14 +61,14 @@ impl TryFrom<u8> 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<FractionalPart> {
|
||||
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<Self> {
|
||||
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<FractionalPart>,
|
||||
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, CucError> {
|
||||
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, CucError> {
|
||||
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<FractionalPart> {
|
||||
/// 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<FractionalPart>,
|
||||
fractions: FractionalPart,
|
||||
) -> Result<Self, CucError> {
|
||||
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<FractionalResolution>) -> 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<FractionalPart>) {
|
||||
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<Duration> 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<Duration> 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<Duration> 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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user