Compare commits
10 Commits
fc76a975c1
...
b55fe9f443
Author | SHA1 | Date | |
---|---|---|---|
b55fe9f443 | |||
493a09e1a6 | |||
f54cf69d87 | |||
f634a57f93 | |||
256407432d | |||
c59b015a20 | |||
e081504b33 | |||
6eb1b1efbc | |||
51d0a08e7b | |||
6e557c2568 |
@ -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<Duration>` and `AddAssign<Duration>` 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.
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "spacepackets"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||
|
18
release_checklist.md
Normal file
18
release_checklist.md
Normal file
@ -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.
|
@ -790,19 +790,19 @@ impl TimeProvider<DaysLen24Bits> {
|
||||
/// ## 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<Utc>) -> Result<Self, TimestampError> {
|
||||
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<Self, TimestampError> {
|
||||
@ -879,13 +879,13 @@ impl TimeProvider<DaysLen16Bits> {
|
||||
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<Self, TimestampError> {
|
||||
@ -996,8 +996,14 @@ fn add_for_max_ccsds_days_val<T: ProvidesDaysLength>(
|
||||
_ => 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;
|
||||
@ -1038,6 +1044,20 @@ impl Add<Duration> for TimeProvider<DaysLen16Bits> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Duration> for &TimeProvider<DaysLen16Bits> {
|
||||
type Output = TimeProvider<DaysLen16Bits>;
|
||||
|
||||
fn add(self, duration: Duration) -> Self::Output {
|
||||
let (next_ccsds_days, next_ms_of_day, precision) =
|
||||
add_for_max_ccsds_days_val(self, u16::MAX as u32, duration);
|
||||
let mut provider = Self::Output::new_with_u16_days(next_ccsds_days as u16, next_ms_of_day);
|
||||
if let Some(prec) = precision {
|
||||
provider.set_submillis_precision(prec);
|
||||
}
|
||||
provider
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows adding an duration in form of an offset. Please note that the CCSDS days will rollover
|
||||
/// when they overflow, because addition needs to be infallible. The user needs to check for a
|
||||
/// days overflow when this is a possibility and might be a problem.
|
||||
@ -1055,6 +1075,20 @@ impl Add<Duration> for TimeProvider<DaysLen24Bits> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Duration> for &TimeProvider<DaysLen24Bits> {
|
||||
type Output = TimeProvider<DaysLen24Bits>;
|
||||
fn add(self, duration: Duration) -> Self::Output {
|
||||
let (next_ccsds_days, next_ms_of_day, precision) =
|
||||
add_for_max_ccsds_days_val(self, MAX_DAYS_24_BITS, duration);
|
||||
let mut provider =
|
||||
Self::Output::new_with_u24_days(next_ccsds_days, next_ms_of_day).unwrap();
|
||||
if let Some(prec) = precision {
|
||||
provider.set_submillis_precision(prec);
|
||||
}
|
||||
provider
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows adding an duration in form of an offset. Please note that the CCSDS days will rollover
|
||||
/// when they overflow, because addition needs to be infallible. The user needs to check for a
|
||||
/// days overflow when this is a possibility and might be a problem.
|
||||
@ -2074,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);
|
||||
|
@ -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
|
||||
@ -696,6 +696,20 @@ impl Add<Duration> for TimeProviderCcsdsEpoch {
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Duration> for &TimeProviderCcsdsEpoch {
|
||||
type Output = TimeProviderCcsdsEpoch;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -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<DateTime<Utc>>;
|
||||
}
|
||||
|
||||
/// 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].
|
||||
@ -367,14 +367,14 @@ fn get_new_stamp_after_addition(
|
||||
let mut new_subsec_millis =
|
||||
current_stamp.subsecond_millis().unwrap_or(0) + duration.subsec_millis() as u16;
|
||||
let mut new_unix_seconds = current_stamp.unix_seconds;
|
||||
let mut increment_seconds = |value: u64| {
|
||||
let mut increment_seconds = |value: u32| {
|
||||
if new_unix_seconds < 0 {
|
||||
new_unix_seconds = new_unix_seconds
|
||||
.checked_sub_unsigned(value)
|
||||
.checked_sub(value.into())
|
||||
.expect("new unix seconds would exceed i64::MIN");
|
||||
} else {
|
||||
new_unix_seconds = new_unix_seconds
|
||||
.checked_add_unsigned(value)
|
||||
.checked_add(value.into())
|
||||
.expect("new unix seconds would exceed i64::MAX");
|
||||
}
|
||||
};
|
||||
@ -382,18 +382,31 @@ fn get_new_stamp_after_addition(
|
||||
new_subsec_millis -= 1000;
|
||||
increment_seconds(1);
|
||||
}
|
||||
increment_seconds(duration.as_secs());
|
||||
increment_seconds(
|
||||
duration
|
||||
.as_secs()
|
||||
.try_into()
|
||||
.expect("duration seconds exceeds u32::MAX"),
|
||||
);
|
||||
UnixTimestamp::const_new(new_unix_seconds, new_subsec_millis)
|
||||
}
|
||||
|
||||
/// Please note that this operation will panic if the unix seconds after subtraction (for stamps
|
||||
/// before the unix epoch) exceeds [i64::MIN] or exceeds [i64::MAX] after addition.
|
||||
/// Please note that this operation will panic on the following conditions:
|
||||
///
|
||||
/// - Unix seconds after subtraction for stamps before the unix epoch exceeds [i64::MIN].
|
||||
/// - Unix seconds after addition exceeds [i64::MAX].
|
||||
/// - Seconds from duration to add exceeds [u32::MAX].
|
||||
impl AddAssign<Duration> for UnixTimestamp {
|
||||
fn add_assign(&mut self, duration: Duration) {
|
||||
*self = get_new_stamp_after_addition(self, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/// Please note that this operation will panic for the following conditions:
|
||||
///
|
||||
/// - Unix seconds after subtraction for stamps before the unix epoch exceeds [i64::MIN].
|
||||
/// - Unix seconds after addition exceeds [i64::MAX].
|
||||
/// - Unix seconds exceeds [u32::MAX].
|
||||
impl Add<Duration> for UnixTimestamp {
|
||||
type Output = Self;
|
||||
|
||||
@ -526,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();
|
||||
|
Reference in New Issue
Block a user