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 {
/// No fractional part, only second resolution
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,
/// 65535 fractional parts, resulting in 1/65535 ~= 15 us fractional resolution
FifteenUs = 2,
@ -67,12 +67,16 @@ impl TryFrom<u8> for FractionalResolution {
#[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);
assert!(fractional_part.1 <= div);
10_u64.pow(9) * fractional_part.1 as u64 / div as u64
}
#[inline(always)]
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
}
@ -92,19 +96,9 @@ pub fn fractional_part_from_subsec_ns(
panic!("passed nanosecond value larger than 1 second");
}
let resolution = fractional_res_to_div(res) as u64;
// Use integer division because this can reduce code size of really small systems.
// First determine the nanoseconds for the smallest segment given the resolution.
// Then divide by that to find out the fractional part. For the calculation of the smallest
// 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;
// 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))
}
@ -294,16 +288,22 @@ impl CucTime {
leap_seconds: u32,
) -> Result<Self, StdTimestampError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let ccsds_epoch = unix_epoch_to_ccsds_epoch(now.as_secs() as i64);
ccsds_epoch
.checked_add(i64::from(leap_seconds))
let mut counter =
u32::try_from(unix_epoch_to_ccsds_epoch(now.as_secs() as i64)).map_err(|_| {
TimestampError::Cuc(CucError::InvalidCounter {
width: 4,
counter: now.as_secs(),
})
})?;
counter = counter
.checked_add(leap_seconds)
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
if fraction_resolution == FractionalResolution::Seconds {
return Ok(Self::new(ccsds_epoch as u32));
return Ok(Self::new(counter));
}
let fractions =
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()))
}
@ -318,7 +318,8 @@ impl CucTime {
pub fn update_from_now(&mut self, leap_seconds: u32) -> Result<(), StdTimestampError> {
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
self.counter.1 = self
.counter
.1
.checked_add(leap_seconds)
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
@ -360,17 +361,17 @@ impl CucTime {
res: FractionalResolution,
leap_seconds: u32,
) -> 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.
if ccsds_epoch < 0 {
if counter < 0 {
return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp));
}
ccsds_epoch
counter = counter
.checked_add(i64::from(leap_seconds))
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
let fractions =
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 {
@ -1194,13 +1195,53 @@ mod tests {
}
#[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 =
fractional_part_from_subsec_ns(FractionalResolution::SixtyNs, 10u64.pow(9) - 1)
.unwrap();
// 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) - 2);
assert_eq!(fractions.1, 2_u32.pow(3 * 8) - 1);
}
fn check_stamp_after_addition(cuc_stamp: &CucTime) {
@ -1212,7 +1253,7 @@ mod tests {
assert_eq!(cuc_stamp.width_counter_pair().1, 202);
let fractions = cuc_stamp.width_fractions_pair().unwrap().1;
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);
let cuc_stamp2 = cuc_stamp + Duration::from_millis(501);
// What I would roughly expect
@ -1300,7 +1341,7 @@ mod tests {
.expect("failed to create cuc from unix stamp");
assert_eq!(
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
pub fn diff_in_millis(&self, other: &UnixTime) -> i64 {
let seconds_difference = self.secs - other.secs;
pub fn diff_in_millis(&self, other: &UnixTime) -> Option<i64> {
let seconds_difference = self.secs.checked_sub(other.secs)?;
// 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
let subsecond_difference_nanos = self.subsec_nanos as i64 - other.subsec_nanos as i64;
// 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 {
type Output = StampDiff;
type Output = Option<StampDiff>;
fn sub(self, rhs: Self) -> Self::Output {
let difference = self.diff_in_millis(&rhs);
if difference < 0 {
let difference = self.diff_in_millis(&rhs)?;
Some(if difference < 0 {
StampDiff {
positive_duration: false,
duration_absolute: Duration::from_millis(-difference as u64),
@ -461,7 +461,7 @@ impl Sub for UnixTime {
positive_duration: true,
duration_absolute: Duration::from_millis(difference as u64),
}
}
})
}
}
@ -690,7 +690,7 @@ mod tests {
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_later - UnixTime::new(1, 0);
} = (stamp_later - UnixTime::new(1, 0)).expect("stamp diff error");
assert!(positive_duration);
assert_eq!(duration_absolute, Duration::from_secs(1));
}
@ -702,7 +702,7 @@ mod tests {
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_later - stamp_earlier;
} = (stamp_later - stamp_earlier).expect("stamp diff error");
assert!(positive_duration);
assert_eq!(duration_absolute, Duration::from_millis(1900));
}
@ -714,7 +714,7 @@ mod tests {
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_earlier - stamp_later;
} = (stamp_earlier - stamp_later).expect("stamp diff error");
assert!(!positive_duration);
assert_eq!(duration_absolute, Duration::from_millis(1900));
}