diff --git a/CHANGELOG.md b/CHANGELOG.md index 6988ea4..fe57fca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). # [unreleased] +# [v0.5.1] 2023-01-22 + ## Added - `time::cds::TimeProvider` @@ -20,6 +22,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Add `Ord` and `PartialOrd` implementations. - Add `Add` and `AddAssign` implementations. +## Fixed + +- `time::cds::TimeProvider`: Fixed a bug where subsecond milliseconds were not accounted for + when the provider has no submillisecond precision. + # [v0.5.0] 2023-01-20 The timestamp of `PusTm` is now optional. See Added and Changed section for details. diff --git a/Cargo.toml b/Cargo.toml index c3fa456..54d428e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spacepackets" -version = "0.5.0" +version = "0.5.1" edition = "2021" rust-version = "1.60" authors = ["Robin Mueller "] diff --git a/release_checklist.md b/release_checklist.md new file mode 100644 index 0000000..0310bd1 --- /dev/null +++ b/release_checklist.md @@ -0,0 +1,18 @@ +Checklist for new releases +======= + +# Pre-Release + +1. Make sure any new modules are documented sufficiently enough and check docs with + `cargo doc --all-features --open`. +2. Bump version specifier in `Cargo.toml`. +3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new + `unreleased` section. +4. Run `cargo test --all-features`. +5. Run `cargo fmt` and `cargo clippy`. Check `cargo msrv` against MSRV in `Cargo.toml`. +6. Wait for CI/CD results for EGit and Github. These also check cross-compilation for bare-metal + targets. + +# Post-Release + +1. Create a new release on `EGit` based on the release branch. diff --git a/src/time/cds.rs b/src/time/cds.rs index 73b11c5..6fb0e2c 100644 --- a/src/time/cds.rs +++ b/src/time/cds.rs @@ -790,19 +790,19 @@ impl TimeProvider { /// ## Errors /// /// This function will return [TimestampError::DateBeforeCcsdsEpoch] or - /// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or - /// the CCSDS days value exceeds the allowed bit width (24 bits). + /// [TimestampError::CdsError] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00) + /// or the CCSDS days value exceeds the allowed bit width (24 bits). pub fn from_dt_with_u24_days(dt: &DateTime) -> Result { Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits) } - /// Create a provider from a generic UNIX timestamp (seconds since 01-01-1970 00:00:00). + /// Create a provider from a generic UNIX timestamp (seconds since 1970-01-01T00:00:00+00:00). /// /// ## Errors /// /// This function will return [TimestampError::DateBeforeCcsdsEpoch] or - /// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or - /// the CCSDS days value exceeds the allowed bit width (24 bits). + /// [TimestampError::CdsError] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00) + /// or the CCSDS days value exceeds the allowed bit width (24 bits). pub fn from_unix_secs_with_u24_days( unix_stamp: &UnixTimestamp, ) -> Result { @@ -879,13 +879,13 @@ impl TimeProvider { Self::from_now_generic(LengthOfDaySegment::Short16Bits) } - /// Create a provider from a generic UNIX timestamp (seconds since 01-01-1970 00:00:00). + /// Create a provider from a generic UNIX timestamp (seconds since 1970-01-01T00:00:00+00:00). /// /// ## Errors /// /// This function will return [TimestampError::DateBeforeCcsdsEpoch] or - /// [TimestampError::CdsError] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or - /// the CCSDS days value exceeds the allowed bit width (24 bits). + /// [TimestampError::CdsError] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00) + /// or the CCSDS days value exceeds the allowed bit width (24 bits). pub fn from_unix_secs_with_u16_days( unix_stamp: &UnixTimestamp, ) -> Result { @@ -996,8 +996,14 @@ fn add_for_max_ccsds_days_val( _ => None, } } else { + increment_ms_of_day( + &mut next_ms_of_day, + duration.subsec_millis(), + &mut next_ccsds_days, + ); None }; + // The subsecond millisecond were already handled. let full_seconds = duration.as_secs(); let secs_of_day = (full_seconds % SECONDS_PER_DAY as u64) as u32; let ms_of_day = secs_of_day * 1000; @@ -2102,6 +2108,16 @@ mod tests { } } + #[test] + fn test_addition_on_ref() { + // This test case also tests the case where there is no submillis precision but subsecond + // milliseconds. + let provider_ref = &TimeProvider::new_with_u16_days(2, 500); + let new_stamp = provider_ref + Duration::from_millis(2 * 24 * 60 * 60 * 1000 + 500); + assert_eq!(new_stamp.ccsds_days_as_u32(), 4); + assert_eq!(new_stamp.ms_of_day, 1000); + } + fn check_ps_and_carryover(prec: SubmillisPrecision, ms_of_day: u32, val: u32) { if let SubmillisPrecision::Picoseconds(ps) = prec { assert_eq!(ps, val); diff --git a/src/time/cuc.rs b/src/time/cuc.rs index 799769c..9790afb 100644 --- a/src/time/cuc.rs +++ b/src/time/cuc.rs @@ -134,8 +134,8 @@ pub struct FractionalPart(FractionalResolution, u32); /// It has the capability to generate and read timestamps as specified in the CCSDS 301.0-B-4 /// section 3.2 . The preamble field only has one byte, which allows a time code representation /// through the year 2094. The time is represented as a simple binary counter starting from the -/// fixed CCSDS epoch (1958-01-01 00:00:00). It is possible to provide subsecond accuracy using the -/// fractional field with various available [resolutions][FractionalResolution]. +/// fixed CCSDS epoch (1958-01-01T00:00:00+00:00). It is possible to provide subsecond accuracy +/// using the fractional field with various available [resolutions][FractionalResolution]. /// /// Having a preamble field of one byte limits the width of the counter /// type (generally seconds) to 4 bytes and the width of the fractions type to 3 bytes. This limits diff --git a/src/time/mod.rs b/src/time/mod.rs index 9d94b67..57081b2 100644 --- a/src/time/mod.rs +++ b/src/time/mod.rs @@ -157,16 +157,16 @@ pub fn seconds_since_epoch() -> f64 { /// Convert UNIX days to CCSDS days /// -/// - CCSDS epoch: 1958 January 1 -/// - UNIX Epoch: 1970 January 1 +/// - CCSDS epoch: 1958-01-01T00:00:00+00:00 +/// - UNIX Epoch: 1970-01-01T00:00:00+00:00 pub const fn unix_to_ccsds_days(unix_days: i64) -> i64 { unix_days - DAYS_CCSDS_TO_UNIX as i64 } /// Convert CCSDS days to UNIX days /// -/// - CCSDS epoch: 1958 January 1 -/// - UNIX Epoch: 1970 January 1 +/// - CCSDS epoch: 1958-01-01T00:00:00+00:00 +/// - UNIX Epoch: 1970-01-01T00:00:00+00:00 pub const fn ccsds_to_unix_days(ccsds_days: i64) -> i64 { ccsds_days + DAYS_CCSDS_TO_UNIX as i64 } @@ -233,7 +233,7 @@ pub trait CcsdsTimeProvider { fn date_time(&self) -> Option>; } -/// UNIX timestamp: Elapsed seconds since 01-01-1970 00:00:00. +/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00. /// /// Also can optionally include subsecond millisecond for greater accuracy. Please note that a /// subsecond millisecond value of 0 gets converted to [None]. @@ -539,6 +539,14 @@ mod tests { assert_eq!(stamp1.subsecond_millis().unwrap(), 500); } + #[test] + fn test_addition_on_ref() { + let stamp0 = &UnixTimestamp::new(20, 500).unwrap(); + let stamp1 = stamp0 + Duration::from_millis(2500); + assert_eq!(stamp1.unix_seconds, 23); + assert!(stamp1.subsecond_millis().is_none()); + } + #[test] fn test_addition_spillover() { let mut stamp0 = UnixTimestamp::new(1, 900).unwrap();