continued cuc impl

This commit is contained in:
Robin Müller 2022-12-09 16:51:48 +01:00
parent 5958d19eb4
commit 8d0de0dce4
No known key found for this signature in database
GPG Key ID: BE6480244DFE612C

View File

@ -43,6 +43,28 @@ pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 {
2_u32.pow(8 * res as u32) - 1 2_u32.pow(8 * res as u32) - 1
} }
/// Calculate the fractional part for a given resolution and subsecond nanoseconds.
/// 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> {
if res == FractionalResolution::Seconds {
return None;
}
let sec_as_ns = 10_u64.pow(9);
if ns > sec_as_ns {
panic!("passed nanosecond value larger than 1 second");
}
// First determine the nanoseconds for the smallest segment given the resolution.
// Then divide by that to find out the fractional part. An integer division floors
// which is what we want here.
let fractional_part = ns / (sec_as_ns / fractional_res_to_div(res) as u64);
Some(FractionalPart(res, fractional_part as u32))
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CucError { pub enum CucError {
@ -195,14 +217,55 @@ impl TimeProviderCcsdsEpoch {
} }
impl TimeProviderCcsdsEpoch { impl TimeProviderCcsdsEpoch {
pub fn new_default(counter: u32) -> Self { pub fn new(counter: u32) -> Self {
// These values are definitely valid, so it is okay to unwrap here. // These values are definitely valid, so it is okay to unwrap here.
Self::new(WidthCounterPair(4, counter), None).unwrap() Self::new_generic(WidthCounterPair(4, counter), None).unwrap()
}
pub fn new_with_coarse_fractions(counter: u32, subsec_fractions: u8) -> Self {
// 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,
)),
)
.unwrap()
}
pub fn new_with_submillis_fractions(counter: u32, subsec_fractions: u16) -> Self {
// 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,
)),
)
.unwrap()
}
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 as u32,
)),
)
}
pub fn new_with_fractions(
counter: u32,
fractions: Option<FractionalPart>,
) -> Result<Self, CucError> {
Self::new_generic(WidthCounterPair(4, counter), fractions)
} }
pub fn new_u16_counter(counter: u16) -> Self { pub fn new_u16_counter(counter: u16) -> Self {
// These values are definitely valid, so it is okay to unwrap here. // These values are definitely valid, so it is okay to unwrap here.
Self::new(WidthCounterPair(2, counter as u32), None).unwrap() Self::new_generic(WidthCounterPair(2, counter as u32), None).unwrap()
} }
/// This function will return the current time as a CUC timestamp. /// This function will return the current time as a CUC timestamp.
@ -212,12 +275,8 @@ impl TimeProviderCcsdsEpoch {
pub fn from_now(fraction_resolution: FractionalResolution) -> Result<Self, StdTimestampError> { pub fn from_now(fraction_resolution: FractionalResolution) -> Result<Self, StdTimestampError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let ccsds_epoch = unix_epoch_to_ccsds_epoch(now.as_secs()); let ccsds_epoch = unix_epoch_to_ccsds_epoch(now.as_secs());
let mut fractions = None; let fractions =
if fraction_resolution != FractionalResolution::Seconds { fractional_part_from_subsec_ns(fraction_resolution, now.subsec_nanos() as u64);
let fractional_part = 10_u64.pow(9) * now.subsec_nanos() as u64
/ fractional_res_to_div(fraction_resolution) as u64;
fractions = Some(FractionalPart(fraction_resolution, fractional_part as u32));
}
Ok(Self { Ok(Self {
pfield: 0, pfield: 0,
counter: WidthCounterPair(4, ccsds_epoch as u32), counter: WidthCounterPair(4, ccsds_epoch as u32),
@ -225,6 +284,19 @@ impl TimeProviderCcsdsEpoch {
}) })
} }
#[cfg(feature = "std")]
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs()) as u32;
if self.fractions.is_some() {
self.fractions = fractional_part_from_subsec_ns(
self.fractions.unwrap().0,
now.subsec_nanos() as u64,
);
}
Ok(())
}
pub fn width_counter_pair(&self) -> WidthCounterPair { pub fn width_counter_pair(&self) -> WidthCounterPair {
self.counter self.counter
} }
@ -241,7 +313,20 @@ impl TimeProviderCcsdsEpoch {
Ok(()) Ok(())
} }
pub fn new( /// Set a fractional resolution. Please note that this function will reset the fractional value
/// to 0 if the resolution changes.
pub fn set_fractional_resolution(&mut self, res: FractionalResolution) {
if res == FractionalResolution::Seconds {
self.fractions = None;
}
if let Some(existing_fractions) = self.fractions {
if existing_fractions.0 != res {
self.fractions = Some(FractionalPart(res, 0));
}
}
}
pub fn new_generic(
counter: WidthCounterPair, counter: WidthCounterPair,
fractions: Option<FractionalPart>, fractions: Option<FractionalPart>,
) -> Result<Self, CucError> { ) -> Result<Self, CucError> {
@ -324,7 +409,7 @@ impl TimeReader for TimeProviderCcsdsEpoch {
_ => panic!("unreachable match arm"), _ => panic!("unreachable match arm"),
} }
} }
let provider = Self::new(WidthCounterPair(cntr_len, counter), fractions)?; let provider = Self::new_generic(WidthCounterPair(cntr_len, counter), fractions)?;
Ok(provider) Ok(provider)
} }
} }
@ -379,6 +464,7 @@ impl TimeWriter for TimeProviderCcsdsEpoch {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use crate::time::cuc::{convert_fractional_part_to_ns, FractionalPart, FractionalResolution}; use crate::time::cuc::{convert_fractional_part_to_ns, FractionalPart, FractionalResolution};
#[test] #[test]
@ -411,4 +497,32 @@ mod tests {
2_u32.pow(32) - 1, 2_u32.pow(32) - 1,
)); ));
} }
#[test]
fn fractional_part_formula() {
let fractional_part =
7843137 / (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FourMs) as u64);
assert_eq!(fractional_part, 2);
}
#[test]
fn fractional_part_formula_2() {
let fractional_part =
12000000 / (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FourMs) as u64);
assert_eq!(fractional_part, 3);
}
#[test]
fn fractional_part_formula_3() {
let one_fraction_with_width_two_in_ns = 10_u64.pow(9) / (2_u32.pow(8 * 2) - 1) as u64;
assert_eq!(one_fraction_with_width_two_in_ns, 15259);
let hundred_fractions_and_some = 100 * one_fraction_with_width_two_in_ns + 7000;
let fractional_part = hundred_fractions_and_some
/ (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FifteenUs) as u64);
assert_eq!(fractional_part, 100);
let hundred_and_one_fractions = 101 * one_fraction_with_width_two_in_ns;
let fractional_part = hundred_and_one_fractions
/ (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FifteenUs) as u64);
assert_eq!(fractional_part, 101);
}
} }