all code review fixes
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Rust/spacepackets/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2024-03-18 12:38:41 +01:00
parent 9cbdd30939
commit 3fc95f0927
Signed by: muellerr
GPG Key ID: A649FB78196E3849
2 changed files with 81 additions and 40 deletions

View File

@ -39,7 +39,7 @@ pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8;
pub enum FractionalResolution { pub enum FractionalResolution {
/// No fractional part, only second resolution /// No fractional part, only second resolution
Seconds = 0, Seconds = 0,
/// 256 fractional parts, resulting in 1/255 ~= 4 ms fractional resolution /// 255 fractional parts, resulting in 1/255 ~= 4 ms fractional resolution
FourMs = 1, FourMs = 1,
/// 65535 fractional parts, resulting in 1/65535 ~= 15 us fractional resolution /// 65535 fractional parts, resulting in 1/65535 ~= 15 us fractional resolution
FifteenUs = 2, FifteenUs = 2,
@ -67,12 +67,16 @@ impl TryFrom<u8> for FractionalResolution {
#[inline] #[inline]
pub fn convert_fractional_part_to_ns(fractional_part: FractionalPart) -> u64 { pub fn convert_fractional_part_to_ns(fractional_part: FractionalPart) -> u64 {
let div = fractional_res_to_div(fractional_part.0); let div = fractional_res_to_div(fractional_part.0);
assert!(fractional_part.1 < div); assert!(fractional_part.1 <= div);
10_u64.pow(9) * fractional_part.1 as u64 / div as u64 10_u64.pow(9) * fractional_part.1 as u64 / div as u64
} }
#[inline(always)] #[inline(always)]
pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 { pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 {
// We do not use the full possible range for a given resolution. This is because if we did
// that, the largest value would be equal to the counter being incremented by one. Thus, the
// smallest allowed fractions value is 0 while the largest allowed fractions value is the
// closest fractions value to the next counter increment.
2_u32.pow(8 * res as u32) - 1 2_u32.pow(8 * res as u32) - 1
} }
@ -92,19 +96,9 @@ pub fn fractional_part_from_subsec_ns(
panic!("passed nanosecond value larger than 1 second"); panic!("passed nanosecond value larger than 1 second");
} }
let resolution = fractional_res_to_div(res) as u64; let resolution = fractional_res_to_div(res) as u64;
// Use integer division because this can reduce code size of really small systems. // This is probably the cheapest way to calculate the fractional part without using expensive
// First determine the nanoseconds for the smallest segment given the resolution. // floating point division.
// Then divide by that to find out the fractional part. For the calculation of the smallest let fractional_part = ns * (resolution + 1) / sec_as_ns;
// fraction, we perform a ceiling division. This is because if we would use the default
// flooring division, we would divide by a smaller value, thereby allowing the calculation to
// invalid fractional parts which are too large. For the division of the nanoseconds by the
// smallest fraction, a flooring division is correct.
// The multiplication with 100000 is necessary to avoid precision loss during integer division.
// TODO: Floating point division might actually be faster option, but requires additional
// code on small embedded systems..
let fractional_part = ns * 100000 / ((sec_as_ns * 100000 + resolution) / resolution);
// Floating point division.
//let fractional_part = (ns as f64 / ((sec_as_ns as f64) / resolution as f64)).floor() as u32;
Some(FractionalPart(res, fractional_part as u32)) Some(FractionalPart(res, fractional_part as u32))
} }
@ -294,16 +288,22 @@ impl CucTime {
leap_seconds: u32, leap_seconds: u32,
) -> Result<Self, StdTimestampError> { ) -> 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() as i64); let mut counter =
ccsds_epoch u32::try_from(unix_epoch_to_ccsds_epoch(now.as_secs() as i64)).map_err(|_| {
.checked_add(i64::from(leap_seconds)) TimestampError::Cuc(CucError::InvalidCounter {
width: 4,
counter: now.as_secs(),
})
})?;
counter = counter
.checked_add(leap_seconds)
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?; .ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
if fraction_resolution == FractionalResolution::Seconds { if fraction_resolution == FractionalResolution::Seconds {
return Ok(Self::new(ccsds_epoch as u32)); return Ok(Self::new(counter));
} }
let fractions = let fractions =
fractional_part_from_subsec_ns(fraction_resolution, now.subsec_nanos() as u64); fractional_part_from_subsec_ns(fraction_resolution, now.subsec_nanos() as u64);
Self::new_with_fractions(ccsds_epoch as u32, fractions.unwrap()) Self::new_with_fractions(counter, fractions.unwrap())
.map_err(|e| StdTimestampError::Timestamp(e.into())) .map_err(|e| StdTimestampError::Timestamp(e.into()))
} }
@ -318,7 +318,8 @@ impl CucTime {
pub fn update_from_now(&mut self, leap_seconds: u32) -> Result<(), StdTimestampError> { pub fn update_from_now(&mut self, leap_seconds: u32) -> Result<(), StdTimestampError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u32; self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u32;
self.counter self.counter.1 = self
.counter
.1 .1
.checked_add(leap_seconds) .checked_add(leap_seconds)
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?; .ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
@ -360,17 +361,17 @@ impl CucTime {
res: FractionalResolution, res: FractionalResolution,
leap_seconds: u32, leap_seconds: u32,
) -> Result<Self, TimestampError> { ) -> Result<Self, TimestampError> {
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.secs); let mut counter = unix_epoch_to_ccsds_epoch(unix_stamp.secs);
// Negative CCSDS epoch is invalid. // Negative CCSDS epoch is invalid.
if ccsds_epoch < 0 { if counter < 0 {
return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp)); return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp));
} }
ccsds_epoch counter = counter
.checked_add(i64::from(leap_seconds)) .checked_add(i64::from(leap_seconds))
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?; .ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
let fractions = let fractions =
fractional_part_from_subsec_ns(res, unix_stamp.subsec_millis() as u64 * 10_u64.pow(6)); fractional_part_from_subsec_ns(res, unix_stamp.subsec_millis() as u64 * 10_u64.pow(6));
Self::new_generic(WidthCounterPair(4, ccsds_epoch as u32), fractions).map_err(|e| e.into()) Self::new_generic(WidthCounterPair(4, counter as u32), fractions).map_err(|e| e.into())
} }
pub fn new_u16_counter(counter: u16) -> Self { pub fn new_u16_counter(counter: u16) -> Self {
@ -1194,13 +1195,53 @@ mod tests {
} }
#[test] #[test]
fn assert_largest_fractions() { fn test_small_fraction_floored_to_zero() {
let fractions =
fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 59)
.unwrap();
assert_eq!(fractions.1, 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);
}
#[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);
}
#[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);
}
#[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);
}
#[test]
fn test_largest_fractions_with_largest_resolution() {
let fractions = let fractions =
fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 10u64.pow(9) - 1) fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 10u64.pow(9) - 1)
.unwrap(); .unwrap();
// The value can not be larger than representable by 3 bytes // The value can not be larger than representable by 3 bytes
// Assert that the maximum resolution can be reached // Assert that the maximum resolution can be reached
assert_eq!(fractions.1, 2_u32.pow(3 * 8) - 2); assert_eq!(fractions.1, 2_u32.pow(3 * 8) - 1);
} }
fn check_stamp_after_addition(cuc_stamp: &CucTime) { fn check_stamp_after_addition(cuc_stamp: &CucTime) {
@ -1212,7 +1253,7 @@ mod tests {
assert_eq!(cuc_stamp.width_counter_pair().1, 202); assert_eq!(cuc_stamp.width_counter_pair().1, 202);
let fractions = cuc_stamp.width_fractions_pair().unwrap().1; let fractions = cuc_stamp.width_fractions_pair().unwrap().1;
let expected_val = let expected_val =
(0.5 * fractional_res_to_div(FractionalResolution::FifteenUs) as f64).floor() as u32; (0.5 * fractional_res_to_div(FractionalResolution::FifteenUs) as f64).ceil() as u32;
assert_eq!(fractions, expected_val); assert_eq!(fractions, expected_val);
let cuc_stamp2 = cuc_stamp + Duration::from_millis(501); let cuc_stamp2 = cuc_stamp + Duration::from_millis(501);
// What I would roughly expect // What I would roughly expect
@ -1300,7 +1341,7 @@ mod tests {
.expect("failed to create cuc from unix stamp"); .expect("failed to create cuc from unix stamp");
assert_eq!( assert_eq!(
cuc.counter(), cuc.counter(),
(-DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as u32 (-DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as u32 + LEAP_SECONDS
); );
} }

View File

@ -371,16 +371,16 @@ impl UnixTime {
} }
// Calculate the difference in milliseconds between two UnixTimestamps // Calculate the difference in milliseconds between two UnixTimestamps
pub fn diff_in_millis(&self, other: &UnixTime) -> i64 { pub fn diff_in_millis(&self, other: &UnixTime) -> Option<i64> {
let seconds_difference = self.secs - other.secs; let seconds_difference = self.secs.checked_sub(other.secs)?;
// Convert seconds difference to milliseconds // Convert seconds difference to milliseconds
let milliseconds_difference = seconds_difference * 1000; let milliseconds_difference = seconds_difference.checked_mul(1000)?;
// Calculate the difference in subsecond milliseconds directly // Calculate the difference in subsecond milliseconds directly
let subsecond_difference_nanos = self.subsec_nanos as i64 - other.subsec_nanos as i64; let subsecond_difference_nanos = self.subsec_nanos as i64 - other.subsec_nanos as i64;
// Combine the differences // Combine the differences
milliseconds_difference + (subsecond_difference_nanos / 1_000_000) Some(milliseconds_difference + (subsecond_difference_nanos / 1_000_000))
} }
} }
@ -447,11 +447,11 @@ pub struct StampDiff {
} }
impl Sub for UnixTime { impl Sub for UnixTime {
type Output = StampDiff; type Output = Option<StampDiff>;
fn sub(self, rhs: Self) -> Self::Output { fn sub(self, rhs: Self) -> Self::Output {
let difference = self.diff_in_millis(&rhs); let difference = self.diff_in_millis(&rhs)?;
if difference < 0 { Some(if difference < 0 {
StampDiff { StampDiff {
positive_duration: false, positive_duration: false,
duration_absolute: Duration::from_millis(-difference as u64), duration_absolute: Duration::from_millis(-difference as u64),
@ -461,7 +461,7 @@ impl Sub for UnixTime {
positive_duration: true, positive_duration: true,
duration_absolute: Duration::from_millis(difference as u64), duration_absolute: Duration::from_millis(difference as u64),
} }
} })
} }
} }
@ -690,7 +690,7 @@ mod tests {
let StampDiff { let StampDiff {
positive_duration, positive_duration,
duration_absolute, duration_absolute,
} = stamp_later - UnixTime::new(1, 0); } = (stamp_later - UnixTime::new(1, 0)).expect("stamp diff error");
assert!(positive_duration); assert!(positive_duration);
assert_eq!(duration_absolute, Duration::from_secs(1)); assert_eq!(duration_absolute, Duration::from_secs(1));
} }
@ -702,7 +702,7 @@ mod tests {
let StampDiff { let StampDiff {
positive_duration, positive_duration,
duration_absolute, duration_absolute,
} = stamp_later - stamp_earlier; } = (stamp_later - stamp_earlier).expect("stamp diff error");
assert!(positive_duration); assert!(positive_duration);
assert_eq!(duration_absolute, Duration::from_millis(1900)); assert_eq!(duration_absolute, Duration::from_millis(1900));
} }
@ -714,7 +714,7 @@ mod tests {
let StampDiff { let StampDiff {
positive_duration, positive_duration,
duration_absolute, duration_absolute,
} = stamp_earlier - stamp_later; } = (stamp_earlier - stamp_later).expect("stamp diff error");
assert!(!positive_duration); assert!(!positive_duration);
assert_eq!(duration_absolute, Duration::from_millis(1900)); assert_eq!(duration_absolute, Duration::from_millis(1900));
} }