8 Commits

Author SHA1 Message Date
dcf720ad67 more improvements for CUC API
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
Rust/spacepackets/pipeline/pr-main This commit looks good
2024-03-18 15:13:19 +01:00
247cc72753 doc updates
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
Rust/spacepackets/pipeline/pr-main This commit looks good
2024-03-18 14:21:24 +01:00
8161e59ba1 formatting
All checks were successful
Rust/spacepackets/pipeline/pr-main This commit looks good
Rust/spacepackets/pipeline/head This commit looks good
2024-03-18 13:39:08 +01:00
3fc95f0927 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
2024-03-18 12:38:41 +01:00
9cbdd30939 more naming improvements
All checks were successful
Rust/spacepackets/pipeline/pr-main This commit looks good
Rust/spacepackets/pipeline/head This commit looks good
2024-03-14 16:59:58 +01:00
fa450c3445 doc corrections
All checks were successful
Rust/spacepackets/pipeline/pr-main This commit looks good
Rust/spacepackets/pipeline/head This commit looks good
2024-03-14 16:58:12 +01:00
739b810ed3 varous fixes
All checks were successful
Rust/spacepackets/pipeline/head This commit looks good
Rust/spacepackets/pipeline/pr-main This commit looks good
2024-03-14 13:09:59 +01:00
e0bd127d2f Major refactoring of the time API
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
2024-03-14 12:51:19 +01:00
27 changed files with 1106 additions and 985 deletions

View File

@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: 1.65.0 toolchain: 1.61.0
override: true override: true
profile: minimal profile: minimal
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1

View File

@ -8,35 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v0.11.0] 2024-04-16
## Changed
- Moved `CCSDS_HEADER_LEN` constant to the crate root.
## Added
- Added `SpacePacketHeader` type alias for `SpHeader` type.
# [v0.11.0-rc.2] 2024-04-04
## Changed
- Renamed `PacketId` and `PacketSequenceCtrl` `new` method to `new_checked` and former
`new_const` method to `new`.
- Renamed `tc`, `tm`, `tc_unseg` and `tm_unseg` variants for `PacketId` and `SpHeader`
to `new_for_tc_checked`, `new_for_tm_checked`, `new_for_unseg_tc_checked` and
`new_for_unseg_tm_checked`.
- `PusTmCreator` and `PusTcCreator` now expect a regular instance of `SpHeader` instead of
a mutable reference.
## Added
- `SpHeader::new_from_apid` and `SpHeader::new_from_apid_checked` constructor.
- `#[inline]` attribute for a lot of small functions.
# [v0.11.0-rc.1] 2024-04-03
Major API changes for the time API. If you are using the time API, it is strongly recommended Major API changes for the time API. If you are using the time API, it is strongly recommended
to check all the API changes in the **Changed** chapter. to check all the API changes in the **Changed** chapter.
@ -53,14 +24,9 @@ to check all the API changes in the **Changed** chapter.
- Added basic support conversions to the `time` library. Introduce new `chrono` and `timelib` - Added basic support conversions to the `time` library. Introduce new `chrono` and `timelib`
feature gate. feature gate.
- Added `CcsdsTimeProvider::timelib_date_time`. - Added `CcsdsTimeProvider::timelib_date_time`.
- Optional support for `defmt` by adding optional `defmt::Format` derives for common types.
## Changed ## Changed
- `PusTcCreator::new_simple` now expects a valid slice for the source data instead of an optional
slice. For telecommands without application data, `&[]` can be passed.
- `PusTmSecondaryHeader` constructors now expects a valid slice for the time stamp instead of an
optional slice.
- Renamed `CcsdsTimeProvider::date_time` to `CcsdsTimeProvider::chrono_date_time` - Renamed `CcsdsTimeProvider::date_time` to `CcsdsTimeProvider::chrono_date_time`
- Renamed `CcsdsTimeCodes` to `CcsdsTimeCode` - Renamed `CcsdsTimeCodes` to `CcsdsTimeCode`
- Renamed `cds::TimeProvider` to `cds::CdsTime` - Renamed `cds::TimeProvider` to `cds::CdsTime`
@ -68,7 +34,6 @@ to check all the API changes in the **Changed** chapter.
- `UnixTimestamp` renamed to `UnixTime` - `UnixTimestamp` renamed to `UnixTime`
- `UnixTime` seconds are now private and can be retrieved using the `secs` member method. - `UnixTime` seconds are now private and can be retrieved using the `secs` member method.
- `UnixTime::new` renamed to `UnixTime::new_checked`. - `UnixTime::new` renamed to `UnixTime::new_checked`.
- `UnixTime::secs` renamed to `UnixTime::as_secs`.
- `UnixTime` now has a nanosecond subsecond precision. The `new` constructor now expects - `UnixTime` now has a nanosecond subsecond precision. The `new` constructor now expects
nanoseconds as the second argument. nanoseconds as the second argument.
- Added new `UnixTime::new_subsec_millis` and `UnixTime::new_subsec_millis_checked` API - Added new `UnixTime::new_subsec_millis` and `UnixTime::new_subsec_millis_checked` API
@ -78,16 +43,6 @@ to check all the API changes in the **Changed** chapter.
- `CcsdsTimeProvider::date_time` renamed to `CcsdsTimeProvider::chrono_date_time`. - `CcsdsTimeProvider::date_time` renamed to `CcsdsTimeProvider::chrono_date_time`.
- Added `UnixTime::MIN`, `UnixTime::MAX` and `UnixTime::EPOCH`. - Added `UnixTime::MIN`, `UnixTime::MAX` and `UnixTime::EPOCH`.
- Added `UnixTime::timelib_date_time`. - Added `UnixTime::timelib_date_time`.
- Error handling for ECSS and time module is more granular now, with a new
`DateBeforeCcsdsEpochError` error and a `DateBeforeCcsdsEpoch` enum variant for both
`CdsError` and `CucError`.
- `PusTmCreator` now has two lifetimes: One for the raw source data buffer and one for the
raw timestamp.
- Time API `from_now*` API renamed to `now*`.
## Removed
- Legacy `PusTm` and `PusTc` objects.
# [v0.11.0-rc.0] 2024-03-04 # [v0.11.0-rc.0] 2024-03-04

View File

@ -1,8 +1,8 @@
[package] [package]
name = "spacepackets" name = "spacepackets"
version = "0.11.0" version = "0.11.0-rc.0"
edition = "2021" edition = "2021"
rust-version = "1.65" rust-version = "1.61"
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"] authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
description = "Generic implementations for various CCSDS and ECSS packet standards" description = "Generic implementations for various CCSDS and ECSS packet standards"
homepage = "https://egit.irs.uni-stuttgart.de/rust/spacepackets" homepage = "https://egit.irs.uni-stuttgart.de/rust/spacepackets"
@ -48,10 +48,6 @@ optional = true
version = "0.2" version = "0.2"
default-features = false default-features = false
[dependencies.defmt]
version = "0.3"
optional = true
[dev-dependencies] [dev-dependencies]
postcard = "1" postcard = "1"
chrono = "0.4" chrono = "0.4"
@ -63,8 +59,7 @@ serde = ["dep:serde", "chrono/serde"]
alloc = ["postcard/alloc", "chrono/alloc"] alloc = ["postcard/alloc", "chrono/alloc"]
chrono = ["dep:chrono"] chrono = ["dep:chrono"]
timelib = ["dep:time"] timelib = ["dep:time"]
defmt = ["dep:defmt"]
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--cfg", "docs_rs", "--generate-link-to-definition"] rustdoc-args = ["--cfg", "doc_cfg", "--generate-link-to-definition"]

View File

@ -29,6 +29,10 @@ Currently, this includes the following components:
`spacepackets` supports various runtime environments and is also suitable for `no_std` environments. `spacepackets` supports various runtime environments and is also suitable for `no_std` environments.
It also offers optional support for [`serde`](https://serde.rs/). This allows serializing and
deserializing them with an appropriate `serde` provider like
[`postcard`](https://github.com/jamesmunns/postcard).
## Default features ## Default features
- [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library. - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
@ -41,8 +45,6 @@ Currently, this includes the following components:
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s - [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
- [`chrono`](https://crates.io/crates/chrono): Add basic support for the `chrono` time library. - [`chrono`](https://crates.io/crates/chrono): Add basic support for the `chrono` time library.
- [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library. - [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library.
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
# Examples # Examples

View File

@ -15,10 +15,7 @@ RUN rustup install nightly && \
rustup target add thumbv7em-none-eabihf armv7-unknown-linux-gnueabihf && \ rustup target add thumbv7em-none-eabihf armv7-unknown-linux-gnueabihf && \
rustup component add rustfmt clippy llvm-tools-preview rustup component add rustfmt clippy llvm-tools-preview
# Get grcov
RUN curl -sSL https://github.com/mozilla/grcov/releases/download/v0.8.19/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar -xj --directory /usr/local/bin RUN curl -sSL https://github.com/mozilla/grcov/releases/download/v0.8.19/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar -xj --directory /usr/local/bin
# Get nextest
RUN curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
# SSH stuff to allow deployment to doc server # SSH stuff to allow deployment to doc server
RUN adduser --uid 114 jenkins RUN adduser --uid 114 jenkins

View File

@ -31,8 +31,7 @@ pipeline {
} }
stage('Test') { stage('Test') {
steps { steps {
sh 'cargo nextest r --all-features' sh 'cargo test --all-features'
sh 'cargo test --doc'
} }
} }
stage('Check with all features') { stage('Check with all features') {

View File

@ -4,12 +4,11 @@ Checklist for new releases
# Pre-Release # Pre-Release
1. Make sure any new modules are documented sufficiently enough and check docs with 1. Make sure any new modules are documented sufficiently enough and check docs with
`cargo +nightly doc --all-features --config 'build.rustdocflags=["--cfg", "docs_rs"]' --open`. `cargo +nightly doc --all-features --config 'rustdocflags=["--cfg", "doc_cfg"]' --open`.
2. Bump version specifier in `Cargo.toml`. 2. Bump version specifier in `Cargo.toml`.
3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new 3. Update `CHANGELOG.md`: Convert `unreleased` section into version section with date and add new
`unreleased` section. `unreleased` section.
4. Run `cargo test --all-features` or `cargo nextest r --all-features` together with 4. Run `cargo test --all-features`.
`cargo test --doc`.
5. Run `cargo fmt` and `cargo clippy`. Check `cargo msrv` against MSRV in `Cargo.toml`. 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 6. Wait for CI/CD results for EGit and Github. These also check cross-compilation for bare-metal
targets. targets.

View File

@ -20,7 +20,6 @@ pub const MIN_LV_LEN: usize = 1;
/// this will be the lifetime of that data reference. /// this will be the lifetime of that data reference.
#[derive(Debug, Copy, Clone, Eq)] #[derive(Debug, Copy, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Lv<'data> { pub struct Lv<'data> {
data: &'data [u8], data: &'data [u8],
// If the LV was generated from a raw bytestream, this will contain the start of the // If the LV was generated from a raw bytestream, this will contain the start of the
@ -62,7 +61,6 @@ pub(crate) fn generic_len_check_deserialization(
} }
impl<'data> Lv<'data> { impl<'data> Lv<'data> {
#[inline]
pub fn new(data: &[u8]) -> Result<Lv, TlvLvError> { pub fn new(data: &[u8]) -> Result<Lv, TlvLvError> {
if data.len() > u8::MAX as usize { if data.len() > u8::MAX as usize {
return Err(TlvLvError::DataTooLarge(data.len())); return Err(TlvLvError::DataTooLarge(data.len()));
@ -74,7 +72,6 @@ impl<'data> Lv<'data> {
} }
/// Creates a LV with an empty value field. /// Creates a LV with an empty value field.
#[inline]
pub fn new_empty() -> Lv<'data> { pub fn new_empty() -> Lv<'data> {
Lv { Lv {
data: &[], data: &[],
@ -84,7 +81,6 @@ impl<'data> Lv<'data> {
/// Helper function to build a string LV. This is especially useful for the file or directory /// Helper function to build a string LV. This is especially useful for the file or directory
/// path LVs /// path LVs
#[inline]
pub fn new_from_str(str_slice: &str) -> Result<Lv, TlvLvError> { pub fn new_from_str(str_slice: &str) -> Result<Lv, TlvLvError> {
Self::new(str_slice.as_bytes()) Self::new(str_slice.as_bytes())
} }
@ -92,44 +88,38 @@ impl<'data> Lv<'data> {
/// Helper function to build a string LV. This is especially useful for the file or directory /// Helper function to build a string LV. This is especially useful for the file or directory
/// path LVs /// path LVs
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[inline] #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn new_from_string(string: &'data String) -> Result<Lv<'data>, TlvLvError> { pub fn new_from_string(string: &'data String) -> Result<Lv<'data>, TlvLvError> {
Self::new(string.as_bytes()) Self::new(string.as_bytes())
} }
/// Returns the length of the value part, not including the length byte. /// Returns the length of the value part, not including the length byte.
#[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.data.len() self.data.len()
} }
/// Returns the full raw length, including the length byte. /// Returns the full raw length, including the length byte.
#[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
self.len_value() + 1 self.len_value() + 1
} }
/// Checks whether the value field is empty. /// Checks whether the value field is empty.
#[inline]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.data.len() == 0 self.data.len() == 0
} }
#[inline]
pub fn value(&self) -> &[u8] { pub fn value(&self) -> &[u8] {
self.data self.data
} }
/// If the LV was generated from a raw bytestream using [Self::from_bytes], the raw start /// If the LV was generated from a raw bytestream using [Self::from_bytes], the raw start
/// of the LV can be retrieved with this method. /// of the LV can be retrieved with this method.
#[inline]
pub fn raw_data(&self) -> Option<&[u8]> { pub fn raw_data(&self) -> Option<&[u8]> {
self.raw_data self.raw_data
} }
/// Convenience function to extract the value as a [str]. This is useful if the LV is /// Convenience function to extract the value as a [str]. This is useful if the LV is
/// known to contain a [str], for example being a file name. /// known to contain a [str], for example being a file name.
#[inline]
pub fn value_as_str(&self) -> Option<Result<&'data str, Utf8Error>> { pub fn value_as_str(&self) -> Option<Result<&'data str, Utf8Error>> {
if self.is_empty() { if self.is_empty() {
return None; return None;
@ -145,7 +135,6 @@ impl<'data> Lv<'data> {
} }
/// Reads a LV from a raw buffer. /// Reads a LV from a raw buffer.
#[inline]
pub fn from_bytes(buf: &'data [u8]) -> Result<Lv<'data>, ByteConversionError> { pub fn from_bytes(buf: &'data [u8]) -> Result<Lv<'data>, ByteConversionError> {
generic_len_check_deserialization(buf, MIN_LV_LEN)?; generic_len_check_deserialization(buf, MIN_LV_LEN)?;
Self::from_be_bytes_no_len_check(buf) Self::from_be_bytes_no_len_check(buf)
@ -162,7 +151,6 @@ impl<'data> Lv<'data> {
MIN_LV_LEN + self.data.len() MIN_LV_LEN + self.data.len()
} }
#[inline]
pub(crate) fn from_be_bytes_no_len_check( pub(crate) fn from_be_bytes_no_len_check(
buf: &'data [u8], buf: &'data [u8],
) -> Result<Lv<'data>, ByteConversionError> { ) -> Result<Lv<'data>, ByteConversionError> {

View File

@ -18,7 +18,6 @@ pub const CFDP_VERSION_2: u8 = 0b001;
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum PduType { pub enum PduType {
FileDirective = 0, FileDirective = 0,
@ -27,7 +26,6 @@ pub enum PduType {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum Direction { pub enum Direction {
TowardsReceiver = 0, TowardsReceiver = 0,
@ -36,7 +34,6 @@ pub enum Direction {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum TransmissionMode { pub enum TransmissionMode {
Acknowledged = 0, Acknowledged = 0,
@ -45,7 +42,6 @@ pub enum TransmissionMode {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum CrcFlag { pub enum CrcFlag {
NoCrc = 0, NoCrc = 0,
@ -73,7 +69,6 @@ impl From<CrcFlag> for bool {
/// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75) /// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75)
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum SegmentMetadataFlag { pub enum SegmentMetadataFlag {
NotPresent = 0, NotPresent = 0,
@ -83,7 +78,6 @@ pub enum SegmentMetadataFlag {
/// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75) /// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75)
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum SegmentationControl { pub enum SegmentationControl {
NoRecordBoundaryPreservation = 0, NoRecordBoundaryPreservation = 0,
@ -92,7 +86,6 @@ pub enum SegmentationControl {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FaultHandlerCode { pub enum FaultHandlerCode {
NoticeOfCancellation = 0b0001, NoticeOfCancellation = 0b0001,
@ -103,7 +96,6 @@ pub enum FaultHandlerCode {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum ConditionCode { pub enum ConditionCode {
/// This is not an error condition for which a faulty handler override can be specified /// This is not an error condition for which a faulty handler override can be specified
@ -126,7 +118,6 @@ pub enum ConditionCode {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum LargeFileFlag { pub enum LargeFileFlag {
/// 32 bit maximum file size and FSS size /// 32 bit maximum file size and FSS size
@ -138,7 +129,6 @@ pub enum LargeFileFlag {
/// Transaction status for the ACK PDU field according to chapter 5.2.4 of the CFDP standard. /// Transaction status for the ACK PDU field according to chapter 5.2.4 of the CFDP standard.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum TransactionStatus { pub enum TransactionStatus {
/// Transaction is not currently active and the CFDP implementation does not retain a /// Transaction is not currently active and the CFDP implementation does not retain a
@ -156,7 +146,6 @@ pub enum TransactionStatus {
/// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/) /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/)
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum ChecksumType { pub enum ChecksumType {
/// Modular legacy checksum /// Modular legacy checksum
@ -178,7 +167,6 @@ pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvLvError { pub enum TlvLvError {
DataTooLarge(usize), DataTooLarge(usize),
ByteConversion(ByteConversionError), ByteConversion(ByteConversionError),

View File

@ -15,7 +15,6 @@ use serde::{Deserialize, Serialize};
/// For more information, refer to CFDP chapter 5.2.4. /// For more information, refer to CFDP chapter 5.2.4.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AckPdu { pub struct AckPdu {
pdu_header: PduHeader, pdu_header: PduHeader,
directive_code_of_acked_pdu: FileDirectiveType, directive_code_of_acked_pdu: FileDirectiveType,

View File

@ -15,7 +15,6 @@ use super::{CfdpPdu, WritablePduPacket};
/// For more information, refer to CFDP chapter 5.2.2. /// For more information, refer to CFDP chapter 5.2.2.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EofPdu { pub struct EofPdu {
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,

View File

@ -14,7 +14,6 @@ use super::{CfdpPdu, WritablePduPacket};
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum DeliveryCode { pub enum DeliveryCode {
Complete = 0, Complete = 0,
@ -23,7 +22,6 @@ pub enum DeliveryCode {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FileStatus { pub enum FileStatus {
DiscardDeliberately = 0b00, DiscardDeliberately = 0b00,
@ -36,7 +34,6 @@ pub enum FileStatus {
/// ///
/// For more information, refer to CFDP chapter 5.2.3. /// For more information, refer to CFDP chapter 5.2.3.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FinishedPduCreator<'fs_responses> { pub struct FinishedPduCreator<'fs_responses> {
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@ -222,7 +219,6 @@ impl<'buf> Iterator for FilestoreResponseIterator<'buf> {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FinishedPduReader<'buf> { pub struct FinishedPduReader<'buf> {
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,

View File

@ -15,7 +15,6 @@ use super::{CfdpPdu, WritablePduPacket};
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MetadataGenericParams { pub struct MetadataGenericParams {
pub closure_requested: bool, pub closure_requested: bool,
pub checksum_type: ChecksumType, pub checksum_type: ChecksumType,
@ -56,7 +55,6 @@ pub fn build_metadata_opts_from_vec(
/// This abstraction exposes a specialized API for creating metadata PDUs as specified in /// This abstraction exposes a specialized API for creating metadata PDUs as specified in
/// CFDP chapter 5.2.5. /// CFDP chapter 5.2.5.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MetadataPduCreator<'src_name, 'dest_name, 'opts> { pub struct MetadataPduCreator<'src_name, 'dest_name, 'opts> {
pdu_header: PduHeader, pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@ -243,7 +241,6 @@ impl<'opts> Iterator for OptionsIter<'opts> {
/// involved. /// involved.
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MetadataPduReader<'buf> { pub struct MetadataPduReader<'buf> {
pdu_header: PduHeader, pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,

View File

@ -18,7 +18,6 @@ pub mod nak;
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FileDirectiveType { pub enum FileDirectiveType {
EofPdu = 0x04, EofPdu = 0x04,
@ -150,14 +149,12 @@ impl Error for PduError {
} }
impl From<ByteConversionError> for PduError { impl From<ByteConversionError> for PduError {
#[inline]
fn from(value: ByteConversionError) -> Self { fn from(value: ByteConversionError) -> Self {
Self::ByteConversion(value) Self::ByteConversion(value)
} }
} }
impl From<TlvLvError> for PduError { impl From<TlvLvError> for PduError {
#[inline]
fn from(e: TlvLvError) -> Self { fn from(e: TlvLvError) -> Self {
Self::TlvLvError(e) Self::TlvLvError(e)
} }
@ -181,42 +178,33 @@ pub trait WritablePduPacket {
pub trait CfdpPdu { pub trait CfdpPdu {
fn pdu_header(&self) -> &PduHeader; fn pdu_header(&self) -> &PduHeader;
#[inline]
fn source_id(&self) -> UnsignedByteField { fn source_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().source_entity_id self.pdu_header().common_pdu_conf().source_entity_id
} }
#[inline]
fn dest_id(&self) -> UnsignedByteField { fn dest_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().dest_entity_id self.pdu_header().common_pdu_conf().dest_entity_id
} }
#[inline]
fn transaction_seq_num(&self) -> UnsignedByteField { fn transaction_seq_num(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().transaction_seq_num self.pdu_header().common_pdu_conf().transaction_seq_num
} }
#[inline]
fn transmission_mode(&self) -> TransmissionMode { fn transmission_mode(&self) -> TransmissionMode {
self.pdu_header().common_pdu_conf().trans_mode self.pdu_header().common_pdu_conf().trans_mode
} }
#[inline]
fn direction(&self) -> Direction { fn direction(&self) -> Direction {
self.pdu_header().common_pdu_conf().direction self.pdu_header().common_pdu_conf().direction
} }
#[inline]
fn crc_flag(&self) -> CrcFlag { fn crc_flag(&self) -> CrcFlag {
self.pdu_header().common_pdu_conf().crc_flag self.pdu_header().common_pdu_conf().crc_flag
} }
#[inline]
fn file_flag(&self) -> LargeFileFlag { fn file_flag(&self) -> LargeFileFlag {
self.pdu_header().common_pdu_conf().file_flag self.pdu_header().common_pdu_conf().file_flag
} }
#[inline]
fn pdu_type(&self) -> PduType { fn pdu_type(&self) -> PduType {
self.pdu_header().pdu_type() self.pdu_header().pdu_type()
} }
@ -232,7 +220,6 @@ pub trait CfdpPdu {
/// same. /// same.
#[derive(Debug, Copy, Clone, Eq)] #[derive(Debug, Copy, Clone, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CommonPduConfig { pub struct CommonPduConfig {
source_entity_id: UnsignedByteField, source_entity_id: UnsignedByteField,
dest_entity_id: UnsignedByteField, dest_entity_id: UnsignedByteField,
@ -245,7 +232,6 @@ pub struct CommonPduConfig {
// TODO: Builder pattern might be applicable here.. // TODO: Builder pattern might be applicable here..
impl CommonPduConfig { impl CommonPduConfig {
#[inline]
pub fn new( pub fn new(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
dest_id: impl Into<UnsignedByteField>, dest_id: impl Into<UnsignedByteField>,
@ -277,7 +263,6 @@ impl CommonPduConfig {
}) })
} }
#[inline]
pub fn new_with_byte_fields( pub fn new_with_byte_fields(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
dest_id: impl Into<UnsignedByteField>, dest_id: impl Into<UnsignedByteField>,
@ -294,12 +279,10 @@ impl CommonPduConfig {
) )
} }
#[inline]
pub fn source_id(&self) -> UnsignedByteField { pub fn source_id(&self) -> UnsignedByteField {
self.source_entity_id self.source_entity_id
} }
#[inline]
fn source_dest_id_check( fn source_dest_id_check(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
dest_id: impl Into<UnsignedByteField>, dest_id: impl Into<UnsignedByteField>,
@ -322,7 +305,6 @@ impl CommonPduConfig {
Ok((source_id, dest_id)) Ok((source_id, dest_id))
} }
#[inline]
pub fn set_source_and_dest_id( pub fn set_source_and_dest_id(
&mut self, &mut self,
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
@ -334,7 +316,6 @@ impl CommonPduConfig {
Ok(()) Ok(())
} }
#[inline]
pub fn dest_id(&self) -> UnsignedByteField { pub fn dest_id(&self) -> UnsignedByteField {
self.dest_entity_id self.dest_entity_id
} }
@ -343,7 +324,6 @@ impl CommonPduConfig {
impl Default for CommonPduConfig { impl Default for CommonPduConfig {
/// The defaults for the source ID, destination ID and the transaction sequence number is the /// The defaults for the source ID, destination ID and the transaction sequence number is the
/// [UnsignedByteFieldU8] with an intitial value of 0 /// [UnsignedByteFieldU8] with an intitial value of 0
#[inline]
fn default() -> Self { fn default() -> Self {
// The new function can not fail for these input parameters. // The new function can not fail for these input parameters.
Self::new( Self::new(
@ -360,7 +340,6 @@ impl Default for CommonPduConfig {
} }
impl PartialEq for CommonPduConfig { impl PartialEq for CommonPduConfig {
#[inline]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.source_entity_id.value() == other.source_entity_id.value() self.source_entity_id.value() == other.source_entity_id.value()
&& self.dest_entity_id.value() == other.dest_entity_id.value() && self.dest_entity_id.value() == other.dest_entity_id.value()
@ -379,7 +358,6 @@ pub const FIXED_HEADER_LEN: usize = 4;
/// For detailed information, refer to chapter 5.1 of the CFDP standard. /// For detailed information, refer to chapter 5.1 of the CFDP standard.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PduHeader { pub struct PduHeader {
pdu_type: PduType, pdu_type: PduType,
pdu_conf: CommonPduConfig, pdu_conf: CommonPduConfig,
@ -389,7 +367,6 @@ pub struct PduHeader {
} }
impl PduHeader { impl PduHeader {
#[inline]
pub fn new_for_file_data( pub fn new_for_file_data(
pdu_conf: CommonPduConfig, pdu_conf: CommonPduConfig,
pdu_datafield_len: u16, pdu_datafield_len: u16,
@ -405,7 +382,6 @@ impl PduHeader {
) )
} }
#[inline]
pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic( Self::new_generic(
PduType::FileData, PduType::FileData,
@ -415,7 +391,6 @@ impl PduHeader {
SegmentationControl::NoRecordBoundaryPreservation, SegmentationControl::NoRecordBoundaryPreservation,
) )
} }
#[inline]
pub fn new_no_file_data(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { pub fn new_no_file_data(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic( Self::new_generic(
PduType::FileDirective, PduType::FileDirective,
@ -426,7 +401,6 @@ impl PduHeader {
) )
} }
#[inline]
pub fn new_generic( pub fn new_generic(
pdu_type: PduType, pdu_type: PduType,
pdu_conf: CommonPduConfig, pdu_conf: CommonPduConfig,
@ -444,7 +418,6 @@ impl PduHeader {
} }
/// Returns only the length of the PDU header when written to a raw buffer. /// Returns only the length of the PDU header when written to a raw buffer.
#[inline]
pub fn header_len(&self) -> usize { pub fn header_len(&self) -> usize {
FIXED_HEADER_LEN FIXED_HEADER_LEN
+ self.pdu_conf.source_entity_id.size() + self.pdu_conf.source_entity_id.size()
@ -452,14 +425,12 @@ impl PduHeader {
+ self.pdu_conf.dest_entity_id.size() + self.pdu_conf.dest_entity_id.size()
} }
#[inline]
pub fn pdu_datafield_len(&self) -> usize { pub fn pdu_datafield_len(&self) -> usize {
self.pdu_datafield_len.into() self.pdu_datafield_len.into()
} }
/// Returns the full length of the PDU when written to a raw buffer, which is the header length /// Returns the full length of the PDU when written to a raw buffer, which is the header length
/// plus the PDU datafield length. /// plus the PDU datafield length.
#[inline]
pub fn pdu_len(&self) -> usize { pub fn pdu_len(&self) -> usize {
self.header_len() + self.pdu_datafield_len as usize self.header_len() + self.pdu_datafield_len as usize
} }
@ -632,13 +603,10 @@ impl PduHeader {
current_idx, current_idx,
)) ))
} }
#[inline]
pub fn pdu_type(&self) -> PduType { pub fn pdu_type(&self) -> PduType {
self.pdu_type self.pdu_type
} }
#[inline]
pub fn common_pdu_conf(&self) -> &CommonPduConfig { pub fn common_pdu_conf(&self) -> &CommonPduConfig {
&self.pdu_conf &self.pdu_conf
} }
@ -646,8 +614,6 @@ impl PduHeader {
pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag { pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag {
self.seg_metadata_flag self.seg_metadata_flag
} }
#[inline]
pub fn seg_ctrl(&self) -> SegmentationControl { pub fn seg_ctrl(&self) -> SegmentationControl {
self.seg_ctrl self.seg_ctrl
} }

View File

@ -12,7 +12,6 @@ use super::{
/// Helper type to encapsulate both normal file size segment requests and large file size segment /// Helper type to encapsulate both normal file size segment requests and large file size segment
/// requests. /// requests.
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SegmentRequests<'a> { pub enum SegmentRequests<'a> {
U32Pairs(&'a [(u32, u32)]), U32Pairs(&'a [(u32, u32)]),
U64Pairs(&'a [(u64, u64)]), U64Pairs(&'a [(u64, u64)]),
@ -32,7 +31,6 @@ impl SegmentRequests<'_> {
/// It exposes a specialized API which simplifies to generate these NAK PDUs with the /// It exposes a specialized API which simplifies to generate these NAK PDUs with the
/// format according to CFDP chapter 5.2.6. /// format according to CFDP chapter 5.2.6.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NakPduCreator<'seg_reqs> { pub struct NakPduCreator<'seg_reqs> {
pdu_header: PduHeader, pdu_header: PduHeader,
start_of_scope: u64, start_of_scope: u64,
@ -355,7 +353,6 @@ impl<T: SegReqFromBytes> SegmentRequestIter<'_, T> {
/// ///
/// The NAK format is expected to be conforming to CFDP chapter 5.2.6. /// The NAK format is expected to be conforming to CFDP chapter 5.2.6.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NakPduReader<'seg_reqs> { pub struct NakPduReader<'seg_reqs> {
pdu_header: PduHeader, pdu_header: PduHeader,
start_of_scope: u64, start_of_scope: u64,

View File

@ -52,7 +52,6 @@ pub trait WritableTlv {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum TlvType { pub enum TlvType {
FilestoreRequest = 0x00, FilestoreRequest = 0x00,
@ -65,7 +64,6 @@ pub enum TlvType {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvTypeField { pub enum TlvTypeField {
Standard(TlvType), Standard(TlvType),
Custom(u8), Custom(u8),
@ -73,7 +71,6 @@ pub enum TlvTypeField {
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FilestoreActionCode { pub enum FilestoreActionCode {
CreateFile = 0b0000, CreateFile = 0b0000,
@ -121,7 +118,6 @@ impl From<TlvTypeField> for u8 {
/// this will be the lifetime of that data reference. /// this will be the lifetime of that data reference.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Tlv<'data> { pub struct Tlv<'data> {
tlv_type_field: TlvTypeField, tlv_type_field: TlvTypeField,
#[cfg_attr(feature = "serde", serde(borrow))] #[cfg_attr(feature = "serde", serde(borrow))]
@ -228,7 +224,6 @@ pub(crate) fn verify_tlv_type(raw_type: u8, expected_tlv_type: TlvType) -> Resul
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EntityIdTlv { pub struct EntityIdTlv {
entity_id: UnsignedByteField, entity_id: UnsignedByteField,
} }
@ -353,7 +348,6 @@ pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct FilestoreTlvBase<'first_name, 'second_name> { struct FilestoreTlvBase<'first_name, 'second_name> {
pub action_code: FilestoreActionCode, pub action_code: FilestoreActionCode,
#[cfg_attr(feature = "serde", serde(borrow))] #[cfg_attr(feature = "serde", serde(borrow))]
@ -567,7 +561,6 @@ impl GenericTlv for FilestoreRequestTlv<'_, '_> {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FilestoreResponseTlv<'first_name, 'second_name, 'fs_msg> { pub struct FilestoreResponseTlv<'first_name, 'second_name, 'fs_msg> {
#[cfg_attr(feature = "serde", serde(borrow))] #[cfg_attr(feature = "serde", serde(borrow))]
base: FilestoreTlvBase<'first_name, 'second_name>, base: FilestoreTlvBase<'first_name, 'second_name>,

View File

@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum Subservice { pub enum Subservice {
// Regular HK // Regular HK

View File

@ -1,8 +1,8 @@
//! Common definitions and helpers required to create PUS TMTC packets according to //! Common definitions and helpers required to create PUS TMTC packets according to
//! [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/) //! [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/)
//! //!
//! You can find the PUS telecommand types in the [tc] module and the the PUS telemetry //! You can find the PUS telecommand definitions in the [tc] module and ithe PUS telemetry definitions
//! types inside the [tm] module. //! inside the [tm] module.
use crate::{ByteConversionError, CcsdsPacket, CRC_CCITT_FALSE}; use crate::{ByteConversionError, CcsdsPacket, CRC_CCITT_FALSE};
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::vec::Vec; use alloc::vec::Vec;
@ -22,6 +22,7 @@ pub mod tm;
pub mod verification; pub mod verification;
pub type CrcType = u16; pub type CrcType = u16;
pub const CCSDS_HEADER_LEN: usize = size_of::<crate::zc::SpHeader>();
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -73,7 +74,6 @@ pub enum PusServiceId {
/// All PUS versions. Only PUS C is supported by this library. /// All PUS versions. Only PUS C is supported by this library.
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive] #[non_exhaustive]
pub enum PusVersion { pub enum PusVersion {
EsaPus = 0, EsaPus = 0,
@ -150,7 +150,6 @@ pub enum PfcReal {
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PusError { pub enum PusError {
VersionNotSupported(PusVersion), VersionNotSupported(PusVersion),
ChecksumFailure(u16), ChecksumFailure(u16),
@ -206,12 +205,13 @@ pub trait PusPacket: CcsdsPacket {
fn crc16(&self) -> Option<u16>; fn crc16(&self) -> Option<u16>;
} }
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, ByteConversionError> { pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, PusError> {
if raw_data.len() < 2 { if raw_data.len() < 2 {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
found: raw_data.len(), found: raw_data.len(),
expected: 2, expected: 2,
}); }
.into());
} }
Ok(u16::from_be_bytes( Ok(u16::from_be_bytes(
raw_data[raw_data.len() - 2..raw_data.len()] raw_data[raw_data.len() - 2..raw_data.len()]
@ -226,16 +226,35 @@ pub(crate) fn calc_pus_crc16(bytes: &[u8]) -> u16 {
digest.finalize() digest.finalize()
} }
pub(crate) fn crc_procedure(
calc_on_serialization: bool,
cached_crc16: &Option<u16>,
start_idx: usize,
curr_idx: usize,
slice: &[u8],
) -> Result<u16, PusError> {
let crc16;
if calc_on_serialization {
crc16 = calc_pus_crc16(&slice[start_idx..curr_idx])
} else if cached_crc16.is_none() {
return Err(PusError::CrcCalculationMissing);
} else {
crc16 = cached_crc16.unwrap();
}
Ok(crc16)
}
pub(crate) fn user_data_from_raw( pub(crate) fn user_data_from_raw(
current_idx: usize, current_idx: usize,
total_len: usize, total_len: usize,
slice: &[u8], slice: &[u8],
) -> Result<&[u8], ByteConversionError> { ) -> Result<&[u8], PusError> {
match current_idx { match current_idx {
_ if current_idx > total_len - 2 => Err(ByteConversionError::FromSliceTooSmall { _ if current_idx > total_len - 2 => Err(ByteConversionError::FromSliceTooSmall {
found: total_len - 2, found: total_len - 2,
expected: current_idx, expected: current_idx,
}), }
.into()),
_ => Ok(&slice[current_idx..total_len - 2]), _ => Ok(&slice[current_idx..total_len - 2]),
} }
} }
@ -245,7 +264,7 @@ pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error(
crc16: u16, crc16: u16,
) -> Result<(), PusError> { ) -> Result<(), PusError> {
verify_crc16_ccitt_false_from_raw(raw_data) verify_crc16_ccitt_false_from_raw(raw_data)
.then_some(()) .then(|| ())
.ok_or(PusError::ChecksumFailure(crc16)) .ok_or(PusError::ChecksumFailure(crc16))
} }
@ -261,13 +280,9 @@ pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
macro_rules! ccsds_impl { macro_rules! ccsds_impl {
() => { () => {
delegate!(to self.sp_header { delegate!(to self.sp_header {
#[inline]
fn ccsds_version(&self) -> u8; fn ccsds_version(&self) -> u8;
#[inline]
fn packet_id(&self) -> crate::PacketId; fn packet_id(&self) -> crate::PacketId;
#[inline]
fn psc(&self) -> crate::PacketSequenceCtrl; fn psc(&self) -> crate::PacketSequenceCtrl;
#[inline]
fn data_len(&self) -> u16; fn data_len(&self) -> u16;
}); });
} }
@ -276,11 +291,8 @@ macro_rules! ccsds_impl {
macro_rules! sp_header_impls { macro_rules! sp_header_impls {
() => { () => {
delegate!(to self.sp_header { delegate!(to self.sp_header {
#[inline]
pub fn set_apid(&mut self, apid: u16) -> bool; pub fn set_apid(&mut self, apid: u16) -> bool;
#[inline]
pub fn set_seq_count(&mut self, seq_count: u16) -> bool; pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
#[inline]
pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags); pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags);
}); });
} }

View File

@ -54,7 +54,6 @@ pub enum SchedStatus {
} }
impl From<bool> for SchedStatus { impl From<bool> for SchedStatus {
#[inline]
fn from(value: bool) -> Self { fn from(value: bool) -> Self {
if value { if value {
SchedStatus::Enabled SchedStatus::Enabled

View File

@ -9,11 +9,9 @@
//! use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader}; //! use spacepackets::ecss::tc::{PusTcCreator, PusTcReader, PusTcSecondaryHeader};
//! //!
//! // Create a ping telecommand with no user application data //! // Create a ping telecommand with no user application data
//! let pus_tc = PusTcCreator::new_no_app_data( //! let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
//! SpHeader::new_from_apid(0x02), //! let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
//! PusTcSecondaryHeader::new_simple(17, 1), //! let pus_tc = PusTcCreator::new_no_app_data(&mut sph, tc_header, true);
//! true
//! );
//! println!("{:?}", pus_tc); //! println!("{:?}", pus_tc);
//! assert_eq!(pus_tc.service(), 17); //! assert_eq!(pus_tc.service(), 17);
//! assert_eq!(pus_tc.subservice(), 1); //! assert_eq!(pus_tc.subservice(), 1);
@ -50,6 +48,8 @@ use zerocopy::AsBytes;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use alloc::vec::Vec; use alloc::vec::Vec;
pub use legacy_tc::*;
/// PUS C secondary header length is fixed /// PUS C secondary header length is fixed
pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>(); pub const PUC_TC_SECONDARY_HEADER_LEN: usize = size_of::<zc::PusTcSecondaryHeader>();
pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize = pub const PUS_TC_MIN_LEN_WITHOUT_APP_DATA: usize =
@ -61,7 +61,6 @@ pub trait IsPusTelecommand {}
#[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Eq, PartialEq, Copy, Clone, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
enum AckOpts { enum AckOpts {
Acceptance = 0b1000, Acceptance = 0b1000,
@ -113,27 +112,22 @@ pub mod zc {
} }
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader { impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
#[inline]
fn pus_version(&self) -> PusVersion { fn pus_version(&self) -> PusVersion {
PusVersion::try_from(self.version_ack >> 4 & 0b1111).unwrap_or(PusVersion::Invalid) PusVersion::try_from(self.version_ack >> 4 & 0b1111).unwrap_or(PusVersion::Invalid)
} }
#[inline]
fn ack_flags(&self) -> u8 { fn ack_flags(&self) -> u8 {
self.version_ack & 0b1111 self.version_ack & 0b1111
} }
#[inline]
fn service(&self) -> u8 { fn service(&self) -> u8 {
self.service self.service
} }
#[inline]
fn subservice(&self) -> u8 { fn subservice(&self) -> u8 {
self.subservice self.subservice
} }
#[inline]
fn source_id(&self) -> u16 { fn source_id(&self) -> u16 {
self.source_id.get() self.source_id.get()
} }
@ -152,7 +146,6 @@ pub mod zc {
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTcSecondaryHeader { pub struct PusTcSecondaryHeader {
pub service: u8, pub service: u8,
pub subservice: u8, pub subservice: u8,
@ -162,27 +155,22 @@ pub struct PusTcSecondaryHeader {
} }
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader { impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
#[inline]
fn pus_version(&self) -> PusVersion { fn pus_version(&self) -> PusVersion {
self.version self.version
} }
#[inline]
fn ack_flags(&self) -> u8 { fn ack_flags(&self) -> u8 {
self.ack self.ack
} }
#[inline]
fn service(&self) -> u8 { fn service(&self) -> u8 {
self.service self.service
} }
#[inline]
fn subservice(&self) -> u8 { fn subservice(&self) -> u8 {
self.subservice self.subservice
} }
#[inline]
fn source_id(&self) -> u16 { fn source_id(&self) -> u16 {
self.source_id self.source_id
} }
@ -203,7 +191,6 @@ impl TryFrom<zc::PusTcSecondaryHeader> for PusTcSecondaryHeader {
} }
impl PusTcSecondaryHeader { impl PusTcSecondaryHeader {
#[inline]
pub fn new_simple(service: u8, subservice: u8) -> Self { pub fn new_simple(service: u8, subservice: u8) -> Self {
PusTcSecondaryHeader { PusTcSecondaryHeader {
service, service,
@ -214,7 +201,6 @@ impl PusTcSecondaryHeader {
} }
} }
#[inline]
pub fn new(service: u8, subservice: u8, ack: u8, source_id: u16) -> Self { pub fn new(service: u8, subservice: u8, ack: u8, source_id: u16) -> Self {
PusTcSecondaryHeader { PusTcSecondaryHeader {
service, service,
@ -226,6 +212,333 @@ impl PusTcSecondaryHeader {
} }
} }
pub mod legacy_tc {
use crate::ecss::tc::{
zc, GenericPusTcSecondaryHeader, IsPusTelecommand, PusTcSecondaryHeader, ACK_ALL,
PUC_TC_SECONDARY_HEADER_LEN, PUS_TC_MIN_LEN_WITHOUT_APP_DATA,
};
use crate::ecss::{
ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls,
verify_crc16_ccitt_false_from_raw_to_pus_error, PusError, PusPacket, WritablePusPacket,
CCSDS_HEADER_LEN,
};
use crate::ecss::{user_data_from_raw, PusVersion};
use crate::SequenceFlags;
use crate::{ByteConversionError, CcsdsPacket, PacketType, SpHeader, CRC_CCITT_FALSE};
use core::mem::size_of;
use delegate::delegate;
use zerocopy::AsBytes;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// This class models the PUS C telecommand packet. It is the primary data structure to generate the
/// raw byte representation of a PUS telecommand or to deserialize from one from raw bytes.
///
/// This class also derives the [serde::Serialize] and [serde::Deserialize] trait if the
/// [serde] feature is used, which allows to send around TC packets in a raw byte format using a
/// serde provider like [postcard](https://docs.rs/postcard/latest/postcard/).
///
/// There is no spare bytes support yet.
///
/// # Lifetimes
///
/// * `'raw_data` - If the TC is not constructed from a raw slice, this will be the life time of
/// a buffer where the user provided application data will be serialized into. If it
/// is, this is the lifetime of the raw byte slice it is constructed from.
#[derive(Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PusTc<'raw_data> {
sp_header: SpHeader,
pub sec_header: PusTcSecondaryHeader,
/// If this is set to false, a manual call to [PusTc::calc_own_crc16] or
/// [PusTc::update_packet_fields] is necessary for the serialized or cached CRC16 to be valid.
pub calc_crc_on_serialization: bool,
#[cfg_attr(feature = "serde", serde(skip))]
raw_data: Option<&'raw_data [u8]>,
app_data: &'raw_data [u8],
crc16: Option<u16>,
}
impl<'raw_data> PusTc<'raw_data> {
/// Generates a new struct instance.
///
/// # Arguments
///
/// * `sp_header` - Space packet header information. The correct packet type will be set
/// automatically
/// * `sec_header` - Information contained in the data field header, including the service
/// and subservice type
/// * `app_data` - Custom application data
/// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
/// field. If this is not set to true, [PusTc::update_ccsds_data_len] can be called to set
/// the correct value to this field manually
#[deprecated(
since = "0.7.0",
note = "Use specialized PusTcCreator or PusTcReader classes instead"
)]
pub fn new(
sp_header: &mut SpHeader,
sec_header: PusTcSecondaryHeader,
app_data: Option<&'raw_data [u8]>,
set_ccsds_len: bool,
) -> Self {
sp_header.set_packet_type(PacketType::Tc);
sp_header.set_sec_header_flag();
let mut pus_tc = Self {
sp_header: *sp_header,
raw_data: None,
app_data: app_data.unwrap_or(&[]),
sec_header,
calc_crc_on_serialization: true,
crc16: None,
};
if set_ccsds_len {
pus_tc.update_ccsds_data_len();
}
pus_tc
}
/// Simplified version of the [PusTc::new] function which allows to only specify service and
/// subservice instead of the full PUS TC secondary header.
#[deprecated(
since = "0.7.0",
note = "Use specialized PusTcCreator or PusTcReader classes instead"
)]
pub fn new_simple(
sph: &mut SpHeader,
service: u8,
subservice: u8,
app_data: Option<&'raw_data [u8]>,
set_ccsds_len: bool,
) -> Self {
#[allow(deprecated)]
Self::new(
sph,
PusTcSecondaryHeader::new(service, subservice, ACK_ALL, 0),
app_data,
set_ccsds_len,
)
}
pub fn sp_header(&self) -> &SpHeader {
&self.sp_header
}
pub fn set_ack_field(&mut self, ack: u8) -> bool {
if ack > 0b1111 {
return false;
}
self.sec_header.ack = ack & 0b1111;
true
}
pub fn set_source_id(&mut self, source_id: u16) {
self.sec_header.source_id = source_id;
}
sp_header_impls!();
/// Calculate the CCSDS space packet data length field and sets it
/// This is called automatically if the `set_ccsds_len` argument in the [PusTc::new] call was
/// used.
/// If this was not done or the application data is set or changed after construction,
/// this function needs to be called to ensure that the data length field of the CCSDS header
/// is set correctly.
pub fn update_ccsds_data_len(&mut self) {
self.sp_header.data_len =
self.len_written() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
}
/// This function should be called before the TC packet is serialized if
/// [PusTc::calc_crc_on_serialization] is set to False. It will calculate and cache the CRC16.
pub fn calc_own_crc16(&mut self) {
let mut digest = CRC_CCITT_FALSE.digest();
let sph_zc = crate::zc::SpHeader::from(self.sp_header);
digest.update(sph_zc.as_bytes());
let pus_tc_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
digest.update(pus_tc_header.as_bytes());
if !self.app_data.is_empty() {
digest.update(self.app_data);
}
self.crc16 = Some(digest.finalize())
}
/// This helper function calls both [PusTc::update_ccsds_data_len] and [PusTc::calc_own_crc16].
pub fn update_packet_fields(&mut self) {
self.update_ccsds_data_len();
self.calc_own_crc16();
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
let sph_zc = crate::zc::SpHeader::from(self.sp_header);
let appended_len = PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len();
let start_idx = vec.len();
let mut ser_len = 0;
vec.extend_from_slice(sph_zc.as_bytes());
ser_len += sph_zc.as_bytes().len();
// The PUS version is hardcoded to PUS C
let pus_tc_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
vec.extend_from_slice(pus_tc_header.as_bytes());
ser_len += pus_tc_header.as_bytes().len();
vec.extend_from_slice(self.app_data);
ser_len += self.app_data.len();
let crc16 = crc_procedure(
self.calc_crc_on_serialization,
&self.crc16,
start_idx,
ser_len,
&vec[start_idx..ser_len],
)?;
vec.extend_from_slice(crc16.to_be_bytes().as_slice());
Ok(appended_len)
}
/// Create a [PusTc] instance from a raw slice. On success, it returns a tuple containing
/// the instance and the found byte length of the packet.
#[deprecated(
since = "0.7.0",
note = "Use specialized PusTcCreator or PusTcReader classes instead"
)]
pub fn from_bytes(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> {
let raw_data_len = slice.len();
if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
return Err(ByteConversionError::FromSliceTooSmall {
found: raw_data_len,
expected: PUS_TC_MIN_LEN_WITHOUT_APP_DATA,
}
.into());
}
let mut current_idx = 0;
let (sp_header, _) = SpHeader::from_be_bytes(&slice[0..CCSDS_HEADER_LEN])?;
current_idx += CCSDS_HEADER_LEN;
let total_len = sp_header.total_len();
if raw_data_len < total_len || total_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
return Err(ByteConversionError::FromSliceTooSmall {
found: raw_data_len,
expected: total_len,
}
.into());
}
let sec_header = zc::PusTcSecondaryHeader::from_bytes(
&slice[current_idx..current_idx + PUC_TC_SECONDARY_HEADER_LEN],
)
.ok_or(ByteConversionError::ZeroCopyFromError)?;
current_idx += PUC_TC_SECONDARY_HEADER_LEN;
let raw_data = &slice[0..total_len];
let pus_tc = Self {
sp_header,
sec_header: PusTcSecondaryHeader::try_from(sec_header).unwrap(),
raw_data: Some(raw_data),
app_data: user_data_from_raw(current_idx, total_len, slice)?,
calc_crc_on_serialization: false,
crc16: Some(crc_from_raw_data(raw_data)?),
};
verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data,
pus_tc.crc16.expect("CRC16 invalid"),
)?;
Ok((pus_tc, total_len))
}
#[deprecated(since = "0.5.2", note = "use raw_bytes() instead")]
pub fn raw(&self) -> Option<&'raw_data [u8]> {
self.raw_bytes()
}
/// If [Self] was constructed [Self::from_bytes], this function will return the slice it was
/// constructed from. Otherwise, [None] will be returned.
pub fn raw_bytes(&self) -> Option<&'raw_data [u8]> {
self.raw_data
}
}
impl WritablePusPacket for PusTc<'_> {
fn len_written(&self) -> usize {
PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len()
}
/// Write the raw PUS byte representation to a provided buffer.
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError> {
let mut curr_idx = 0;
let tc_header_len = size_of::<zc::PusTcSecondaryHeader>();
let total_size = self.len_written();
if total_size > slice.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: slice.len(),
expected: total_size,
}
.into());
}
self.sp_header.write_to_be_bytes(slice)?;
curr_idx += CCSDS_HEADER_LEN;
let sec_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap();
sec_header
.write_to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len])
.ok_or(ByteConversionError::ZeroCopyToError)?;
curr_idx += tc_header_len;
slice[curr_idx..curr_idx + self.app_data.len()].copy_from_slice(self.app_data);
curr_idx += self.app_data.len();
let crc16 = crc_procedure(
self.calc_crc_on_serialization,
&self.crc16,
0,
curr_idx,
slice,
)?;
slice[curr_idx..curr_idx + 2].copy_from_slice(crc16.to_be_bytes().as_slice());
curr_idx += 2;
Ok(curr_idx)
}
}
impl PartialEq for PusTc<'_> {
fn eq(&self, other: &Self) -> bool {
self.sp_header == other.sp_header
&& self.sec_header == other.sec_header
&& self.app_data == other.app_data
}
}
impl CcsdsPacket for PusTc<'_> {
ccsds_impl!();
}
impl PusPacket for PusTc<'_> {
delegate!(to self.sec_header {
fn pus_version(&self) -> PusVersion;
fn service(&self) -> u8;
fn subservice(&self) -> u8;
});
fn user_data(&self) -> &[u8] {
self.app_data
}
fn crc16(&self) -> Option<u16> {
self.crc16
}
}
impl GenericPusTcSecondaryHeader for PusTc<'_> {
delegate!(to self.sec_header {
fn pus_version(&self) -> PusVersion;
fn service(&self) -> u8;
fn subservice(&self) -> u8;
fn source_id(&self) -> u16;
fn ack_flags(&self) -> u8;
});
}
impl IsPusTelecommand for PusTc<'_> {}
}
/// This class can be used to create PUS C telecommand packet. It is the primary data structure to /// This class can be used to create PUS C telecommand packet. It is the primary data structure to
/// generate the raw byte representation of a PUS telecommand. /// generate the raw byte representation of a PUS telecommand.
/// ///
@ -236,37 +549,35 @@ impl PusTcSecondaryHeader {
/// There is no spare bytes support yet. /// There is no spare bytes support yet.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct PusTcCreator<'raw_data> {
pub struct PusTcCreator<'app_data> {
sp_header: SpHeader, sp_header: SpHeader,
pub sec_header: PusTcSecondaryHeader, pub sec_header: PusTcSecondaryHeader,
app_data: &'app_data [u8], app_data: &'raw_data [u8],
} }
impl<'app_data> PusTcCreator<'app_data> { impl<'raw_data> PusTcCreator<'raw_data> {
/// Generates a new struct instance. /// Generates a new struct instance.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `sp_header` - Space packet header information. The correct packet type and the secondary /// * `sp_header` - Space packet header information. The correct packet type will be set
/// header flag are set correctly by the constructor. /// automatically
/// * `sec_header` - Information contained in the data field header, including the service /// * `sec_header` - Information contained in the data field header, including the service
/// and subservice type /// and subservice type
/// * `app_data` - Custom application data /// * `app_data` - Custom application data
/// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length
/// field. If this is not set to true, [Self::update_ccsds_data_len] can be called to set /// field. If this is not set to true, [PusTc::update_ccsds_data_len] can be called to set
/// the correct value to this field manually /// the correct value to this field manually
#[inline]
pub fn new( pub fn new(
mut sp_header: SpHeader, sp_header: &mut SpHeader,
sec_header: PusTcSecondaryHeader, sec_header: PusTcSecondaryHeader,
app_data: &'app_data [u8], app_data: &'raw_data [u8],
set_ccsds_len: bool, set_ccsds_len: bool,
) -> Self { ) -> Self {
sp_header.set_packet_type(PacketType::Tc); sp_header.set_packet_type(PacketType::Tc);
sp_header.set_sec_header_flag(); sp_header.set_sec_header_flag();
let mut pus_tc = Self { let mut pus_tc = Self {
sp_header, sp_header: *sp_header,
app_data, app_data,
sec_header, sec_header,
}; };
@ -276,44 +587,35 @@ impl<'app_data> PusTcCreator<'app_data> {
pus_tc pus_tc
} }
/// Simplified version of the [Self::new] function which allows to only specify service /// Simplified version of the [PusTcCreator::new] function which allows to only specify service
/// and subservice instead of the full PUS TC secondary header. /// and subservice instead of the full PUS TC secondary header.
#[inline]
pub fn new_simple( pub fn new_simple(
sph: SpHeader, sph: &mut SpHeader,
service: u8, service: u8,
subservice: u8, subservice: u8,
app_data: &'app_data [u8], app_data: Option<&'raw_data [u8]>,
set_ccsds_len: bool, set_ccsds_len: bool,
) -> Self { ) -> Self {
Self::new( Self::new(
sph, sph,
PusTcSecondaryHeader::new(service, subservice, ACK_ALL, 0), PusTcSecondaryHeader::new(service, subservice, ACK_ALL, 0),
app_data, app_data.unwrap_or(&[]),
set_ccsds_len, set_ccsds_len,
) )
} }
#[inline]
pub fn new_no_app_data( pub fn new_no_app_data(
sp_header: SpHeader, sp_header: &mut SpHeader,
sec_header: PusTcSecondaryHeader, sec_header: PusTcSecondaryHeader,
set_ccsds_len: bool, set_ccsds_len: bool,
) -> Self { ) -> Self {
Self::new(sp_header, sec_header, &[], set_ccsds_len) Self::new(sp_header, sec_header, &[], set_ccsds_len)
} }
#[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
#[inline]
pub fn sp_header_mut(&mut self) -> &mut SpHeader {
&mut self.sp_header
}
#[inline]
pub fn set_ack_field(&mut self, ack: u8) -> bool { pub fn set_ack_field(&mut self, ack: u8) -> bool {
if ack > 0b1111 { if ack > 0b1111 {
return false; return false;
@ -322,7 +624,6 @@ impl<'app_data> PusTcCreator<'app_data> {
true true
} }
#[inline]
pub fn set_source_id(&mut self, source_id: u16) { pub fn set_source_id(&mut self, source_id: u16) {
self.sec_header.source_id = source_id; self.sec_header.source_id = source_id;
} }
@ -330,18 +631,18 @@ impl<'app_data> PusTcCreator<'app_data> {
sp_header_impls!(); sp_header_impls!();
/// Calculate the CCSDS space packet data length field and sets it /// Calculate the CCSDS space packet data length field and sets it
/// This is called automatically if the `set_ccsds_len` argument in the [Self::new] call was /// This is called automatically if the `set_ccsds_len` argument in the [PusTc::new] call was
/// used. /// used.
/// If this was not done or the application data is set or changed after construction, /// If this was not done or the application data is set or changed after construction,
/// this function needs to be called to ensure that the data length field of the CCSDS header /// this function needs to be called to ensure that the data length field of the CCSDS header
/// is set correctly. /// is set correctly.
#[inline]
pub fn update_ccsds_data_len(&mut self) { pub fn update_ccsds_data_len(&mut self) {
self.sp_header.data_len = self.sp_header.data_len =
self.len_written() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1; self.len_written() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
} }
/// This function calculates and returns the CRC16 for the current packet. /// This function should be called before the TC packet is serialized if
/// [PusTc::calc_crc_on_serialization] is set to False. It will calculate and cache the CRC16.
pub fn calc_own_crc16(&self) -> u16 { pub fn calc_own_crc16(&self) -> u16 {
let mut digest = CRC_CCITT_FALSE.digest(); let mut digest = CRC_CCITT_FALSE.digest();
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
@ -353,7 +654,8 @@ impl<'app_data> PusTcCreator<'app_data> {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> usize { #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
let sph_zc = crate::zc::SpHeader::from(self.sp_header); let sph_zc = crate::zc::SpHeader::from(self.sp_header);
let mut appended_len = PUS_TC_MIN_LEN_WITHOUT_APP_DATA; let mut appended_len = PUS_TC_MIN_LEN_WITHOUT_APP_DATA;
appended_len += self.app_data.len(); appended_len += self.app_data.len();
@ -366,12 +668,11 @@ impl<'app_data> PusTcCreator<'app_data> {
let mut digest = CRC_CCITT_FALSE.digest(); let mut digest = CRC_CCITT_FALSE.digest();
digest.update(&vec[start_idx..start_idx + appended_len - 2]); digest.update(&vec[start_idx..start_idx + appended_len - 2]);
vec.extend_from_slice(&digest.finalize().to_be_bytes()); vec.extend_from_slice(&digest.finalize().to_be_bytes());
appended_len Ok(appended_len)
} }
} }
impl WritablePusPacket for PusTcCreator<'_> { impl WritablePusPacket for PusTcCreator<'_> {
#[inline]
fn len_written(&self) -> usize { fn len_written(&self) -> usize {
PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len() PUS_TC_MIN_LEN_WITHOUT_APP_DATA + self.app_data.len()
} }
@ -412,20 +713,15 @@ impl CcsdsPacket for PusTcCreator<'_> {
impl PusPacket for PusTcCreator<'_> { impl PusPacket for PusTcCreator<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion; fn pus_version(&self) -> PusVersion;
#[inline]
fn service(&self) -> u8; fn service(&self) -> u8;
#[inline]
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
}); });
#[inline]
fn user_data(&self) -> &[u8] { fn user_data(&self) -> &[u8] {
self.app_data self.app_data
} }
#[inline]
fn crc16(&self) -> Option<u16> { fn crc16(&self) -> Option<u16> {
Some(self.calc_own_crc16()) Some(self.calc_own_crc16())
} }
@ -433,15 +729,10 @@ impl PusPacket for PusTcCreator<'_> {
impl GenericPusTcSecondaryHeader for PusTcCreator<'_> { impl GenericPusTcSecondaryHeader for PusTcCreator<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion; fn pus_version(&self) -> PusVersion;
#[inline]
fn service(&self) -> u8; fn service(&self) -> u8;
#[inline]
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
#[inline]
fn source_id(&self) -> u16; fn source_id(&self) -> u16;
#[inline]
fn ack_flags(&self) -> u8; fn ack_flags(&self) -> u8;
}); });
} }
@ -461,7 +752,6 @@ impl IsPusTelecommand for PusTcCreator<'_> {}
/// * `'raw_data` - Lifetime of the provided raw slice. /// * `'raw_data` - Lifetime of the provided raw slice.
#[derive(Eq, Copy, Clone, Debug)] #[derive(Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PusTcReader<'raw_data> { pub struct PusTcReader<'raw_data> {
#[cfg_attr(feature = "serde", serde(skip))] #[cfg_attr(feature = "serde", serde(skip))]
raw_data: &'raw_data [u8], raw_data: &'raw_data [u8],
@ -473,8 +763,7 @@ pub struct PusTcReader<'raw_data> {
impl<'raw_data> PusTcReader<'raw_data> { impl<'raw_data> PusTcReader<'raw_data> {
/// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing /// Create a [PusTcReader] instance from a raw slice. On success, it returns a tuple containing
/// the instance and the found byte length of the packet. This function also performs a CRC /// the instance and the found byte length of the packet.
/// check and will return an appropriate [PusError] if the check fails.
pub fn new(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> { pub fn new(slice: &'raw_data [u8]) -> Result<(Self, usize), PusError> {
let raw_data_len = slice.len(); let raw_data_len = slice.len();
if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA { if raw_data_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA {
@ -519,29 +808,24 @@ impl<'raw_data> PusTcReader<'raw_data> {
Ok((pus_tc, total_len)) Ok((pus_tc, total_len))
} }
#[inline]
pub fn app_data(&self) -> &[u8] { pub fn app_data(&self) -> &[u8] {
self.user_data() self.user_data()
} }
#[inline]
pub fn raw_data(&self) -> &[u8] { pub fn raw_data(&self) -> &[u8] {
self.raw_data self.raw_data
} }
#[inline]
pub fn len_packed(&self) -> usize { pub fn len_packed(&self) -> usize {
self.sp_header.total_len() self.sp_header.total_len()
} }
#[inline]
pub fn sp_header(&self) -> &SpHeader { pub fn sp_header(&self) -> &SpHeader {
&self.sp_header &self.sp_header
} }
} }
impl PartialEq for PusTcReader<'_> { impl PartialEq for PusTcReader<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.raw_data == other.raw_data self.raw_data == other.raw_data
} }
@ -553,20 +837,15 @@ impl CcsdsPacket for PusTcReader<'_> {
impl PusPacket for PusTcReader<'_> { impl PusPacket for PusTcReader<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion; fn pus_version(&self) -> PusVersion;
#[inline]
fn service(&self) -> u8; fn service(&self) -> u8;
#[inline]
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
}); });
#[inline]
fn user_data(&self) -> &[u8] { fn user_data(&self) -> &[u8] {
self.app_data self.app_data
} }
#[inline]
fn crc16(&self) -> Option<u16> { fn crc16(&self) -> Option<u16> {
Some(self.crc16) Some(self.crc16)
} }
@ -574,15 +853,10 @@ impl PusPacket for PusTcReader<'_> {
impl GenericPusTcSecondaryHeader for PusTcReader<'_> { impl GenericPusTcSecondaryHeader for PusTcReader<'_> {
delegate!(to self.sec_header { delegate!(to self.sec_header {
#[inline]
fn pus_version(&self) -> PusVersion; fn pus_version(&self) -> PusVersion;
#[inline]
fn service(&self) -> u8; fn service(&self) -> u8;
#[inline]
fn subservice(&self) -> u8; fn subservice(&self) -> u8;
#[inline]
fn source_id(&self) -> u16; fn source_id(&self) -> u16;
#[inline]
fn ack_flags(&self) -> u8; fn ack_flags(&self) -> u8;
}); });
} }
@ -620,19 +894,19 @@ mod tests {
use postcard::{from_bytes, to_allocvec}; use postcard::{from_bytes, to_allocvec};
fn base_ping_tc_full_ctor() -> PusTcCreator<'static> { fn base_ping_tc_full_ctor() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap(); let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
let tc_header = PusTcSecondaryHeader::new_simple(17, 1); let tc_header = PusTcSecondaryHeader::new_simple(17, 1);
PusTcCreator::new_no_app_data(sph, tc_header, true) PusTcCreator::new_no_app_data(&mut sph, tc_header, true)
} }
fn base_ping_tc_simple_ctor() -> PusTcCreator<'static> { fn base_ping_tc_simple_ctor() -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap(); let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
PusTcCreator::new_simple(sph, 17, 1, &[], true) PusTcCreator::new_simple(&mut sph, 17, 1, None, true)
} }
fn base_ping_tc_simple_ctor_with_app_data(app_data: &'static [u8]) -> PusTcCreator<'static> { fn base_ping_tc_simple_ctor_with_app_data(app_data: &'static [u8]) -> PusTcCreator<'static> {
let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap(); let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
PusTcCreator::new_simple(sph, 17, 1, app_data, true) PusTcCreator::new_simple(&mut sph, 17, 1, Some(app_data), true)
} }
#[test] #[test]
@ -688,8 +962,8 @@ mod tests {
#[test] #[test]
fn test_update_func() { fn test_update_func() {
let sph = SpHeader::new_for_unseg_tc_checked(0x02, 0x34, 0).unwrap(); let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
let mut tc = PusTcCreator::new_simple(sph, 17, 1, &[], false); let mut tc = PusTcCreator::new_simple(&mut sph, 17, 1, None, false);
assert_eq!(tc.data_len(), 0); assert_eq!(tc.data_len(), 0);
tc.update_ccsds_data_len(); tc.update_ccsds_data_len();
assert_eq!(tc.data_len(), 6); assert_eq!(tc.data_len(), 6);
@ -736,7 +1010,9 @@ mod tests {
fn test_vec_ser_deser() { fn test_vec_ser_deser() {
let pus_tc = base_ping_tc_simple_ctor(); let pus_tc = base_ping_tc_simple_ctor();
let mut test_vec = Vec::new(); let mut test_vec = Vec::new();
let size = pus_tc.append_to_vec(&mut test_vec); let size = pus_tc
.append_to_vec(&mut test_vec)
.expect("Error writing TC to vector");
assert_eq!(size, 13); assert_eq!(size, 13);
verify_test_tc_raw(&test_vec.as_slice()); verify_test_tc_raw(&test_vec.as_slice());
verify_crc_no_app_data(&test_vec.as_slice()); verify_crc_no_app_data(&test_vec.as_slice());
@ -782,7 +1058,9 @@ mod tests {
let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]); let pus_tc = base_ping_tc_simple_ctor_with_app_data(&[1, 2, 3]);
verify_test_tc(&pus_tc, true, 16); verify_test_tc(&pus_tc, true, 16);
let mut test_vec = Vec::new(); let mut test_vec = Vec::new();
let size = pus_tc.append_to_vec(&mut test_vec); let size = pus_tc
.append_to_vec(&mut test_vec)
.expect("Error writing TC to vector");
assert_eq!(test_vec[11], 1); assert_eq!(test_vec[11], 1);
assert_eq!(test_vec[12], 2); assert_eq!(test_vec[12], 2);
assert_eq!(test_vec[13], 3); assert_eq!(test_vec[13], 3);
@ -861,8 +1139,7 @@ mod tests {
if !has_user_data { if !has_user_data {
assert!(tc.user_data().is_empty()); assert!(tc.user_data().is_empty());
} }
let mut comp_header = let mut comp_header = SpHeader::tc_unseg(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
SpHeader::new_for_unseg_tc_checked(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
comp_header.set_sec_header_flag(); comp_header.set_sec_header_flag();
assert_eq!(*tc.sp_header(), comp_header); assert_eq!(*tc.sp_header(), comp_header);
} }
@ -873,8 +1150,7 @@ mod tests {
assert!(tc.user_data().is_empty()); assert!(tc.user_data().is_empty());
} }
assert_eq!(tc.len_packed(), exp_full_len); assert_eq!(tc.len_packed(), exp_full_len);
let mut comp_header = let mut comp_header = SpHeader::tc_unseg(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
SpHeader::new_for_unseg_tc_checked(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
comp_header.set_sec_header_flag(); comp_header.set_sec_header_flag();
assert_eq!(*tc.sp_header(), comp_header); assert_eq!(*tc.sp_header(), comp_header);
} }

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,10 @@
//! //!
//! `spacepackets` supports various runtime environments and is also suitable for `no_std` environments. //! `spacepackets` supports various runtime environments and is also suitable for `no_std` environments.
//! //!
//! It also offers optional support for [`serde`](https://serde.rs/). This allows serializing and
//! deserializing them with an appropriate `serde` provider like
//! [`postcard`](https://github.com/jamesmunns/postcard).
//!
//! ### Default features //! ### Default features
//! //!
//! - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library. //! - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
@ -32,11 +36,7 @@
//! ### Optional features //! ### Optional features
//! //!
//! - [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and //! - [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and
//! `Deserialize` `derives. //! `Deserialize` `derive`s
//! - [`chrono`](https://crates.io/crates/chrono): Add basic support for the `chrono` time library.
//! - [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library.
//! - [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
//! [`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
//! //!
//! ## Module //! ## Module
//! //!
@ -48,19 +48,20 @@
//! //!
//! ```rust //! ```rust
//! use spacepackets::SpHeader; //! use spacepackets::SpHeader;
//! let sp_header = SpHeader::new_for_unseg_tc_checked(0x42, 12, 1).expect("error creating CCSDS TC header"); //! let sp_header = SpHeader::tc_unseg(0x42, 12, 1).expect("Error creating CCSDS TC header");
//! println!("{:?}", sp_header); //! println!("{:?}", sp_header);
//! let mut ccsds_buf: [u8; 32] = [0; 32]; //! let mut ccsds_buf: [u8; 32] = [0; 32];
//! sp_header.write_to_be_bytes(&mut ccsds_buf).expect("Writing CCSDS TC header failed"); //! sp_header.write_to_be_bytes(&mut ccsds_buf).expect("Writing CCSDS TC header failed");
//! println!("{:x?}", &ccsds_buf[0..6]); //! println!("{:x?}", &ccsds_buf[0..6]);
//! ``` //! ```
#![no_std] #![no_std]
#![cfg_attr(docs_rs, feature(doc_auto_cfg))] #![cfg_attr(doc_cfg, feature(doc_cfg))]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
extern crate alloc; extern crate alloc;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
extern crate std; extern crate std;
use crate::ecss::CCSDS_HEADER_LEN;
use core::{ use core::{
fmt::{Debug, Display, Formatter}, fmt::{Debug, Display, Formatter},
hash::Hash, hash::Hash,
@ -83,8 +84,6 @@ mod private {
pub trait Sealed {} pub trait Sealed {}
} }
pub const CCSDS_HEADER_LEN: usize = core::mem::size_of::<crate::zc::SpHeader>();
/// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard. /// CRC algorithm used by the PUS standard, the CCSDS TC standard and the CFDP standard.
pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740); pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
@ -94,7 +93,6 @@ pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1;
/// Generic error type when converting to and from raw byte slices. /// Generic error type when converting to and from raw byte slices.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ByteConversionError { pub enum ByteConversionError {
/// The passed slice is too small. Returns the passed slice length and expected minimum size /// The passed slice is too small. Returns the passed slice length and expected minimum size
ToSliceTooSmall { ToSliceTooSmall {
@ -144,7 +142,6 @@ impl Error for ByteConversionError {}
/// CCSDS packet type enumeration. /// CCSDS packet type enumeration.
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PacketType { pub enum PacketType {
Tm = 0, Tm = 0,
Tc = 1, Tc = 1,
@ -168,7 +165,6 @@ pub fn packet_type_in_raw_packet_id(packet_id: u16) -> PacketType {
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SequenceFlags { pub enum SequenceFlags {
ContinuationSegment = 0b00, ContinuationSegment = 0b00,
FirstSegment = 0b01, FirstSegment = 0b01,
@ -196,7 +192,6 @@ impl TryFrom<u8> for SequenceFlags {
/// of the first two bytes in the CCSDS primary header. /// of the first two bytes in the CCSDS primary header.
#[derive(Debug, Eq, Copy, Clone)] #[derive(Debug, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketId { pub struct PacketId {
pub ptype: PacketType, pub ptype: PacketType,
pub sec_header_flag: bool, pub sec_header_flag: bool,
@ -204,21 +199,18 @@ pub struct PacketId {
} }
impl PartialEq for PacketId { impl PartialEq for PacketId {
#[inline]
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.raw().eq(&other.raw()) self.raw().eq(&other.raw())
} }
} }
impl PartialOrd for PacketId { impl PartialOrd for PacketId {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
impl Ord for PacketId { impl Ord for PacketId {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering { fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.raw().cmp(&other.raw()) self.raw().cmp(&other.raw())
} }
@ -232,7 +224,6 @@ impl Hash for PacketId {
} }
impl Default for PacketId { impl Default for PacketId {
#[inline]
fn default() -> Self { fn default() -> Self {
PacketId { PacketId {
ptype: PacketType::Tm, ptype: PacketType::Tm,
@ -243,34 +234,23 @@ impl Default for PacketId {
} }
impl PacketId { impl PacketId {
/// This constructor will panic if the passed APID exceeds [MAX_APID]. pub const fn const_tc(sec_header: bool, apid: u16) -> Self {
/// Use the checked constructor variants to avoid panics. Self::const_new(PacketType::Tc, sec_header, apid)
#[inline] }
pub const fn new_for_tc(sec_header: bool, apid: u16) -> Self {
pub const fn const_tm(sec_header: bool, apid: u16) -> Self {
Self::const_new(PacketType::Tm, sec_header, apid)
}
pub fn tc(sec_header: bool, apid: u16) -> Option<Self> {
Self::new(PacketType::Tc, sec_header, apid) Self::new(PacketType::Tc, sec_header, apid)
} }
/// This constructor will panic if the passed APID exceeds [MAX_APID]. pub fn tm(sec_header: bool, apid: u16) -> Option<Self> {
/// Use the checked constructor variants to avoid panics.
#[inline]
pub const fn new_for_tm(sec_header: bool, apid: u16) -> Self {
Self::new(PacketType::Tm, sec_header, apid) Self::new(PacketType::Tm, sec_header, apid)
} }
#[inline] pub const fn const_new(ptype: PacketType, sec_header: bool, apid: u16) -> Self {
pub fn new_for_tc_checked(sec_header: bool, apid: u16) -> Option<Self> {
Self::new_checked(PacketType::Tc, sec_header, apid)
}
#[inline]
pub fn new_for_tm_checked(sec_header: bool, apid: u16) -> Option<Self> {
Self::new_checked(PacketType::Tm, sec_header, apid)
}
/// This constructor will panic if the passed APID exceeds [MAX_APID].
/// Use the checked variants to avoid panics.
#[inline]
pub const fn new(ptype: PacketType, sec_header: bool, apid: u16) -> Self {
if apid > MAX_APID { if apid > MAX_APID {
panic!("APID too large"); panic!("APID too large");
} }
@ -281,18 +261,16 @@ impl PacketId {
} }
} }
#[inline] pub fn new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
pub fn new_checked(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
if apid > MAX_APID { if apid > MAX_APID {
return None; return None;
} }
Some(PacketId::new(ptype, sec_header_flag, apid)) Some(PacketId::const_new(ptype, sec_header_flag, apid))
} }
/// Set a new Application Process ID (APID). If the passed number is invalid, the APID will /// Set a new Application Process ID (APID). If the passed number is invalid, the APID will
/// not be set and false will be returned. The maximum allowed value for the 11-bit field is /// not be set and false will be returned. The maximum allowed value for the 11-bit field is
/// 2047 /// 2047
#[inline]
pub fn set_apid(&mut self, apid: u16) -> bool { pub fn set_apid(&mut self, apid: u16) -> bool {
if apid > MAX_APID { if apid > MAX_APID {
return false; return false;
@ -301,7 +279,6 @@ impl PacketId {
true true
} }
#[inline]
pub fn apid(&self) -> u16 { pub fn apid(&self) -> u16 {
self.apid self.apid
} }
@ -326,17 +303,15 @@ impl From<u16> for PacketId {
/// third and the fourth byte in the CCSDS primary header. /// third and the fourth byte in the CCSDS primary header.
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketSequenceCtrl { pub struct PacketSequenceCtrl {
pub seq_flags: SequenceFlags, pub seq_flags: SequenceFlags,
seq_count: u16, seq_count: u16,
} }
impl PacketSequenceCtrl { impl PacketSequenceCtrl {
/// This constructor panics if the sequence count exceeds [MAX_SEQ_COUNT]. /// const variant of [PacketSequenceCtrl::new], but panics if the sequence count exceeds
/// Use [Self::new_checked] to avoid panics. /// [MAX_SEQ_COUNT].
#[inline] pub const fn const_new(seq_flags: SequenceFlags, seq_count: u16) -> PacketSequenceCtrl {
pub const fn new(seq_flags: SequenceFlags, seq_count: u16) -> PacketSequenceCtrl {
if seq_count > MAX_SEQ_COUNT { if seq_count > MAX_SEQ_COUNT {
panic!("Sequence count too large"); panic!("Sequence count too large");
} }
@ -347,22 +322,18 @@ impl PacketSequenceCtrl {
} }
/// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT]. /// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT].
#[inline] pub fn new(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
pub fn new_checked(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
if seq_count > MAX_SEQ_COUNT { if seq_count > MAX_SEQ_COUNT {
return None; return None;
} }
Some(PacketSequenceCtrl::new(seq_flags, seq_count)) Some(PacketSequenceCtrl::const_new(seq_flags, seq_count))
} }
#[inline]
pub fn raw(&self) -> u16 { pub fn raw(&self) -> u16 {
((self.seq_flags as u16) << 14) | self.seq_count ((self.seq_flags as u16) << 14) | self.seq_count
} }
/// Set a new sequence count. If the passed number is invalid, the sequence count will not be /// Set a new sequence count. If the passed number is invalid, the sequence count will not be
/// set and false will be returned. The maximum allowed value for the 14-bit field is 16383. /// set and false will be returned. The maximum allowed value for the 14-bit field is 16383.
#[inline]
pub fn set_seq_count(&mut self, ssc: u16) -> bool { pub fn set_seq_count(&mut self, ssc: u16) -> bool {
if ssc > MAX_SEQ_COUNT { if ssc > MAX_SEQ_COUNT {
return false; return false;
@ -371,7 +342,6 @@ impl PacketSequenceCtrl {
true true
} }
#[inline]
pub fn seq_count(&self) -> u16 { pub fn seq_count(&self) -> u16 {
self.seq_count self.seq_count
} }
@ -413,7 +383,6 @@ pub trait CcsdsPacket {
/// Retrieve data length field /// Retrieve data length field
fn data_len(&self) -> u16; fn data_len(&self) -> u16;
/// Retrieve the total packet size based on the data length field /// Retrieve the total packet size based on the data length field
#[inline]
fn total_len(&self) -> usize { fn total_len(&self) -> usize {
usize::from(self.data_len()) + CCSDS_HEADER_LEN + 1 usize::from(self.data_len()) + CCSDS_HEADER_LEN + 1
} }
@ -494,7 +463,6 @@ pub trait CcsdsPrimaryHeader {
/// * `data_len` - Data length field occupies the fifth and the sixth byte of the raw header /// * `data_len` - Data length field occupies the fifth and the sixth byte of the raw header
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpHeader { pub struct SpHeader {
pub version: u8, pub version: u8,
pub packet_id: PacketId, pub packet_id: PacketId,
@ -502,12 +470,9 @@ pub struct SpHeader {
pub data_len: u16, pub data_len: u16,
} }
pub type SpacePacketHeader = SpHeader;
impl Default for SpHeader { impl Default for SpHeader {
/// The default function sets the sequence flag field to [SequenceFlags::Unsegmented]. The data /// The default function sets the sequence flag field to [SequenceFlags::Unsegmented]. The data
/// length field is set to 1, which denotes an empty space packets. /// length field is set to 1, which denotes an empty space packets.
#[inline]
fn default() -> Self { fn default() -> Self {
SpHeader { SpHeader {
version: 0, version: 0,
@ -522,7 +487,6 @@ impl Default for SpHeader {
} }
impl SpHeader { impl SpHeader {
#[inline]
pub const fn new(packet_id: PacketId, psc: PacketSequenceCtrl, data_len: u16) -> Self { pub const fn new(packet_id: PacketId, psc: PacketSequenceCtrl, data_len: u16) -> Self {
Self { Self {
version: 0, version: 0,
@ -532,43 +496,9 @@ impl SpHeader {
} }
} }
/// This constructor sets the sequence flag field to [SequenceFlags::Unsegmented]. The data /// const variant of the [SpHeader::new_fron_single_fields] function. Panics if the passed
/// length field is set to 1, which denotes an empty space packets. /// APID exceeds [MAX_APID] or the passed packet sequence count exceeds [MAX_SEQ_COUNT].
/// const fn const_new_from_single_fields(
/// This constructor will panic if the APID exceeds [MAX_APID].
#[inline]
pub const fn new_from_apid(apid: u16) -> Self {
SpHeader {
version: 0,
packet_id: PacketId::new(PacketType::Tm, false, apid),
psc: PacketSequenceCtrl {
seq_flags: SequenceFlags::Unsegmented,
seq_count: 0,
},
data_len: 1,
}
}
/// Checked variant of [Self::new_from_apid].
#[inline]
pub fn new_from_apid_checked(apid: u16) -> Option<Self> {
Some(SpHeader {
version: 0,
packet_id: PacketId::new_checked(PacketType::Tm, false, apid)?,
psc: PacketSequenceCtrl {
seq_flags: SequenceFlags::Unsegmented,
seq_count: 0,
},
data_len: 1,
})
}
/// This constructor panics if the passed APID exceeds [MAX_APID] or the passed packet sequence
/// count exceeds [MAX_SEQ_COUNT].
///
/// The checked constructor variants can be used to avoid panics.
#[inline]
const fn new_from_fields(
ptype: PacketType, ptype: PacketType,
sec_header: bool, sec_header: bool,
apid: u16, apid: u16,
@ -583,20 +513,17 @@ impl SpHeader {
panic!("APID is too large"); panic!("APID is too large");
} }
Self { Self {
psc: PacketSequenceCtrl::new(seq_flags, seq_count), psc: PacketSequenceCtrl::const_new(seq_flags, seq_count),
packet_id: PacketId::new(ptype, sec_header, apid), packet_id: PacketId::const_new(ptype, sec_header, apid),
data_len, data_len,
version: 0, version: 0,
} }
} }
/// Create a new Space Packet Header instance which can be used to create generic /// Create a new Space Packet Header instance which can be used to create generic
/// Space Packets. /// Space Packets. This will return [None] if the APID or sequence count argument
///
/// This will return [None] if the APID or sequence count argument
/// exceed [MAX_APID] or [MAX_SEQ_COUNT] respectively. The version field is set to 0b000. /// exceed [MAX_APID] or [MAX_SEQ_COUNT] respectively. The version field is set to 0b000.
#[inline] pub fn new_from_single_fields(
pub fn new_from_fields_checked(
ptype: PacketType, ptype: PacketType,
sec_header: bool, sec_header: bool,
apid: u16, apid: u16,
@ -607,117 +534,56 @@ impl SpHeader {
if seq_count > MAX_SEQ_COUNT || apid > MAX_APID { if seq_count > MAX_SEQ_COUNT || apid > MAX_APID {
return None; return None;
} }
Some(SpHeader::new_from_fields( Some(SpHeader::const_new_from_single_fields(
ptype, sec_header, apid, seq_flags, seq_count, data_len, ptype, sec_header, apid, seq_flags, seq_count, data_len,
)) ))
} }
/// Helper function for telemetry space packet headers. The packet type field will be /// Helper function for telemetry space packet headers. The packet type field will be
/// set accordingly. The secondary header flag field is set to false. /// set accordingly. The secondary header flag field is set to false.
#[inline] pub fn tm(apid: u16, seq_flags: SequenceFlags, seq_count: u16, data_len: u16) -> Option<Self> {
pub fn new_for_tm_checked( Self::new_from_single_fields(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
apid: u16,
seq_flags: SequenceFlags,
seq_count: u16,
data_len: u16,
) -> Option<Self> {
Self::new_from_fields_checked(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
} }
/// Helper function for telemetry space packet headers. The packet type field will be /// Helper function for telemetry space packet headers. The packet type field will be
/// set accordingly. The secondary header flag field is set to false. /// set accordingly. The secondary header flag field is set to false.
#[inline] pub fn tc(apid: u16, seq_flags: SequenceFlags, seq_count: u16, data_len: u16) -> Option<Self> {
pub fn new_for_tc_checked( Self::new_from_single_fields(PacketType::Tc, false, apid, seq_flags, seq_count, data_len)
apid: u16,
seq_flags: SequenceFlags,
seq_count: u16,
data_len: u16,
) -> Option<Self> {
Self::new_from_fields_checked(PacketType::Tc, false, apid, seq_flags, seq_count, data_len)
} }
/// This is an unchecked constructor which can panic on invalid input. /// Variant of [SpHeader::tm] which sets the sequence flag field to [SequenceFlags::Unsegmented]
#[inline] pub fn tm_unseg(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
pub const fn new_for_tm( Self::tm(apid, SequenceFlags::Unsegmented, seq_count, data_len)
apid: u16,
seq_flags: SequenceFlags,
seq_count: u16,
data_len: u16,
) -> Self {
Self::new_from_fields(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
} }
/// This is an unchecked constructor which can panic on invalid input. /// Variant of [SpHeader::tc] which sets the sequence flag field to [SequenceFlags::Unsegmented]
#[inline] pub fn tc_unseg(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
pub const fn new_for_tc( Self::tc(apid, SequenceFlags::Unsegmented, seq_count, data_len)
apid: u16,
seq_flags: SequenceFlags,
seq_count: u16,
data_len: u16,
) -> Self {
Self::new_from_fields(PacketType::Tc, false, apid, seq_flags, seq_count, data_len)
} }
/// Variant of [SpHeader::new_for_tm_checked] which sets the sequence flag field to [SequenceFlags::Unsegmented] //noinspection RsTraitImplementation
#[inline] delegate!(to self.packet_id {
pub fn new_for_unseg_tm_checked(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> { /// Returns [false] and fails if the APID exceeds [MAX_APID]
Self::new_for_tm_checked(apid, SequenceFlags::Unsegmented, seq_count, data_len) pub fn set_apid(&mut self, apid: u16) -> bool;
} });
/// Variant of [SpHeader::new_for_tc_checked] which sets the sequence flag field to [SequenceFlags::Unsegmented] delegate!(to self.psc {
#[inline] /// Returns [false] and fails if the sequence count exceeds [MAX_SEQ_COUNT]
pub fn new_for_unseg_tc_checked(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> { pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
Self::new_for_tc_checked(apid, SequenceFlags::Unsegmented, seq_count, data_len) });
}
/// Variant of [SpHeader::new_for_tc] which sets the sequence flag field to [SequenceFlags::Unsegmented].
///
/// This is an unchecked constructor which can panic on invalid input.
#[inline]
pub const fn new_for_unseg_tc(apid: u16, seq_count: u16, data_len: u16) -> Self {
Self::new_for_tc(apid, SequenceFlags::Unsegmented, seq_count, data_len)
}
/// Variant of [SpHeader::new_for_tm] which sets the sequence flag field to [SequenceFlags::Unsegmented].
///
/// This is an unchecked constructor which can panic on invalid input.
#[inline]
pub const fn new_for_unseg_tm(apid: u16, seq_count: u16, data_len: u16) -> Self {
Self::new_for_tm(apid, SequenceFlags::Unsegmented, seq_count, data_len)
}
delegate! {
to self.packet_id {
/// Returns [false] and fails if the APID exceeds [MAX_APID]
#[inline]
pub fn set_apid(&mut self, apid: u16) -> bool;
}
}
delegate! {
to self.psc {
/// Returns [false] and fails if the sequence count exceeds [MAX_SEQ_COUNT]
#[inline]
pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
}
}
#[inline]
pub fn set_seq_flags(&mut self, seq_flags: SequenceFlags) { pub fn set_seq_flags(&mut self, seq_flags: SequenceFlags) {
self.psc.seq_flags = seq_flags; self.psc.seq_flags = seq_flags;
} }
#[inline]
pub fn set_sec_header_flag(&mut self) { pub fn set_sec_header_flag(&mut self) {
self.packet_id.sec_header_flag = true; self.packet_id.sec_header_flag = true;
} }
#[inline]
pub fn clear_sec_header_flag(&mut self) { pub fn clear_sec_header_flag(&mut self) {
self.packet_id.sec_header_flag = false; self.packet_id.sec_header_flag = false;
} }
#[inline]
pub fn set_packet_type(&mut self, packet_type: PacketType) { pub fn set_packet_type(&mut self, packet_type: PacketType) {
self.packet_id.ptype = packet_type; self.packet_id.ptype = packet_type;
} }
@ -780,7 +646,6 @@ impl CcsdsPacket for SpHeader {
} }
impl CcsdsPrimaryHeader for SpHeader { impl CcsdsPrimaryHeader for SpHeader {
#[inline]
fn from_composite_fields( fn from_composite_fields(
packet_id: PacketId, packet_id: PacketId,
psc: PacketSequenceCtrl, psc: PacketSequenceCtrl,
@ -848,12 +713,9 @@ pub mod zc {
((self.version_packet_id.get() >> 13) as u8) & 0b111 ((self.version_packet_id.get() >> 13) as u8) & 0b111
} }
#[inline]
fn packet_id(&self) -> PacketId { fn packet_id(&self) -> PacketId {
PacketId::from(self.packet_id_raw()) PacketId::from(self.packet_id_raw())
} }
#[inline]
fn psc(&self) -> PacketSequenceCtrl { fn psc(&self) -> PacketSequenceCtrl {
PacketSequenceCtrl::from(self.psc_raw()) PacketSequenceCtrl::from(self.psc_raw())
} }
@ -863,12 +725,10 @@ pub mod zc {
self.data_len.get() self.data_len.get()
} }
#[inline]
fn packet_id_raw(&self) -> u16 { fn packet_id_raw(&self) -> u16 {
self.version_packet_id.get() & (!VERSION_MASK) self.version_packet_id.get() & (!VERSION_MASK)
} }
#[inline]
fn psc_raw(&self) -> u16 { fn psc_raw(&self) -> u16 {
self.psc.get() self.psc.get()
} }
@ -892,8 +752,6 @@ pub mod zc {
pub(crate) mod tests { pub(crate) mod tests {
use std::collections::HashSet; use std::collections::HashSet;
#[allow(unused_imports)]
use crate::ByteConversionError;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::CcsdsPrimaryHeader; use crate::CcsdsPrimaryHeader;
use crate::{ use crate::{
@ -910,12 +768,12 @@ pub(crate) mod tests {
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
const CONST_SP: SpHeader = SpHeader::new( const CONST_SP: SpHeader = SpHeader::new(
PacketId::new_for_tc(true, 0x36), PacketId::const_tc(true, 0x36),
PacketSequenceCtrl::new(SequenceFlags::ContinuationSegment, 0x88), PacketSequenceCtrl::const_new(SequenceFlags::ContinuationSegment, 0x88),
0x90, 0x90,
); );
const PACKET_ID_TM: PacketId = PacketId::new_for_tm(true, 0x22); const PACKET_ID_TM: PacketId = PacketId::const_tm(true, 0x22);
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
pub(crate) fn generic_serde_test<T: Serialize + DeserializeOwned + PartialEq + Debug>( pub(crate) fn generic_serde_test<T: Serialize + DeserializeOwned + PartialEq + Debug>(
@ -927,12 +785,11 @@ pub(crate) mod tests {
} }
#[test] #[test]
#[allow(clippy::assertions_on_constants)]
fn verify_const_packet_id() { fn verify_const_packet_id() {
assert_eq!(PACKET_ID_TM.apid(), 0x22); assert_eq!(PACKET_ID_TM.apid(), 0x22);
assert!(PACKET_ID_TM.sec_header_flag); assert!(PACKET_ID_TM.sec_header_flag);
assert_eq!(PACKET_ID_TM.ptype, PacketType::Tm); assert_eq!(PACKET_ID_TM.ptype, PacketType::Tm);
let const_tc_id = PacketId::new_for_tc(true, 0x23); let const_tc_id = PacketId::const_tc(true, 0x23);
assert_eq!(const_tc_id.ptype, PacketType::Tc); assert_eq!(const_tc_id.ptype, PacketType::Tc);
} }
@ -941,32 +798,32 @@ pub(crate) mod tests {
let id_default = PacketId::default(); let id_default = PacketId::default();
assert_eq!(id_default.ptype, PacketType::Tm); assert_eq!(id_default.ptype, PacketType::Tm);
assert_eq!(id_default.apid, 0x000); assert_eq!(id_default.apid, 0x000);
assert!(!id_default.sec_header_flag); assert_eq!(id_default.sec_header_flag, false);
} }
#[test] #[test]
fn test_packet_id_ctors() { fn test_packet_id_ctors() {
let packet_id = PacketId::new_checked(PacketType::Tc, true, 0x1ff); let packet_id = PacketId::new(PacketType::Tc, true, 0x1ff);
assert!(packet_id.is_some()); assert!(packet_id.is_some());
let packet_id = packet_id.unwrap(); let packet_id = packet_id.unwrap();
assert_eq!(packet_id.apid(), 0x1ff); assert_eq!(packet_id.apid(), 0x1ff);
assert_eq!(packet_id.ptype, PacketType::Tc); assert_eq!(packet_id.ptype, PacketType::Tc);
assert!(packet_id.sec_header_flag); assert_eq!(packet_id.sec_header_flag, true);
let packet_id_tc = PacketId::new_for_tc_checked(true, 0x1ff); let packet_id_tc = PacketId::tc(true, 0x1ff);
assert!(packet_id_tc.is_some()); assert!(packet_id_tc.is_some());
let packet_id_tc = packet_id_tc.unwrap(); let packet_id_tc = packet_id_tc.unwrap();
assert_eq!(packet_id_tc, packet_id); assert_eq!(packet_id_tc, packet_id);
let packet_id_tm = PacketId::new_for_tm_checked(true, 0x2ff); let packet_id_tm = PacketId::tm(true, 0x2ff);
assert!(packet_id_tm.is_some()); assert!(packet_id_tm.is_some());
let packet_id_tm = packet_id_tm.unwrap(); let packet_id_tm = packet_id_tm.unwrap();
assert!(packet_id_tm.sec_header_flag); assert_eq!(packet_id_tm.sec_header_flag, true);
assert_eq!(packet_id_tm.ptype, PacketType::Tm); assert_eq!(packet_id_tm.ptype, PacketType::Tm);
assert_eq!(packet_id_tm.apid, 0x2ff); assert_eq!(packet_id_tm.apid, 0x2ff);
} }
#[test] #[test]
fn verify_const_sp_header() { fn verify_const_sp_header() {
assert!(CONST_SP.sec_header_flag()); assert_eq!(CONST_SP.sec_header_flag(), true);
assert_eq!(CONST_SP.apid(), 0x36); assert_eq!(CONST_SP.apid(), 0x36);
assert_eq!( assert_eq!(
CONST_SP.sequence_flags(), CONST_SP.sequence_flags(),
@ -1007,7 +864,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_packet_id() { fn test_packet_id() {
let packet_id = let packet_id =
PacketId::new_checked(PacketType::Tm, false, 0x42).expect("Packet ID creation failed"); PacketId::new(PacketType::Tm, false, 0x42).expect("Packet ID creation failed");
assert_eq!(packet_id.raw(), 0x0042); assert_eq!(packet_id.raw(), 0x0042);
let packet_id_from_raw = PacketId::from(packet_id.raw()); let packet_id_from_raw = PacketId::from(packet_id.raw());
assert_eq!( assert_eq!(
@ -1015,26 +872,26 @@ pub(crate) mod tests {
PacketType::Tm PacketType::Tm
); );
assert_eq!(packet_id_from_raw, packet_id); assert_eq!(packet_id_from_raw, packet_id);
let packet_id_from_new = PacketId::new_checked(PacketType::Tm, false, 0x42).unwrap(); let packet_id_from_new = PacketId::new(PacketType::Tm, false, 0x42).unwrap();
assert_eq!(packet_id_from_new, packet_id); assert_eq!(packet_id_from_new, packet_id);
} }
#[test] #[test]
fn test_invalid_packet_id() { fn test_invalid_packet_id() {
let packet_id_invalid = PacketId::new_checked(PacketType::Tc, true, 0xFFFF); let packet_id_invalid = PacketId::new(PacketType::Tc, true, 0xFFFF);
assert!(packet_id_invalid.is_none()); assert!(packet_id_invalid.is_none());
} }
#[test] #[test]
fn test_invalid_apid_setter() { fn test_invalid_apid_setter() {
let mut packet_id = let mut packet_id =
PacketId::new_checked(PacketType::Tm, false, 0x42).expect("Packet ID creation failed"); PacketId::new(PacketType::Tm, false, 0x42).expect("Packet ID creation failed");
assert!(!packet_id.set_apid(0xffff)); assert!(!packet_id.set_apid(0xffff));
} }
#[test] #[test]
fn test_invalid_seq_count() { fn test_invalid_seq_count() {
let mut psc = PacketSequenceCtrl::new_checked(SequenceFlags::ContinuationSegment, 77) let mut psc = PacketSequenceCtrl::new(SequenceFlags::ContinuationSegment, 77)
.expect("PSC creation failed"); .expect("PSC creation failed");
assert_eq!(psc.seq_count(), 77); assert_eq!(psc.seq_count(), 77);
assert!(!psc.set_seq_count(0xffff)); assert!(!psc.set_seq_count(0xffff));
@ -1042,7 +899,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_packet_seq_ctrl() { fn test_packet_seq_ctrl() {
let mut psc = PacketSequenceCtrl::new_checked(SequenceFlags::ContinuationSegment, 77) let mut psc = PacketSequenceCtrl::new(SequenceFlags::ContinuationSegment, 77)
.expect("PSC creation failed"); .expect("PSC creation failed");
assert_eq!(psc.raw(), 77); assert_eq!(psc.raw(), 77);
let psc_from_raw = PacketSequenceCtrl::from(psc.raw()); let psc_from_raw = PacketSequenceCtrl::from(psc.raw());
@ -1051,18 +908,16 @@ pub(crate) mod tests {
assert!(!psc.set_seq_count(2u16.pow(15))); assert!(!psc.set_seq_count(2u16.pow(15)));
assert_eq!(psc.raw(), 77); assert_eq!(psc.raw(), 77);
let psc_invalid = PacketSequenceCtrl::new_checked(SequenceFlags::FirstSegment, 0xFFFF); let psc_invalid = PacketSequenceCtrl::new(SequenceFlags::FirstSegment, 0xFFFF);
assert!(psc_invalid.is_none()); assert!(psc_invalid.is_none());
let psc_from_new = let psc_from_new = PacketSequenceCtrl::new(SequenceFlags::ContinuationSegment, 77).unwrap();
PacketSequenceCtrl::new_checked(SequenceFlags::ContinuationSegment, 77).unwrap();
assert_eq!(psc_from_new, psc); assert_eq!(psc_from_new, psc);
} }
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
fn test_serde_sph() { fn test_serde_sph() {
let sp_header = let sp_header = SpHeader::tc_unseg(0x42, 12, 0).expect("Error creating SP header");
SpHeader::new_for_unseg_tc_checked(0x42, 12, 0).expect("Error creating SP header");
assert_eq!(sp_header.ccsds_version(), 0b000); assert_eq!(sp_header.ccsds_version(), 0b000);
assert!(sp_header.is_tc()); assert!(sp_header.is_tc());
assert!(!sp_header.sec_header_flag()); assert!(!sp_header.sec_header_flag());
@ -1084,8 +939,7 @@ pub(crate) mod tests {
assert_eq!(sp_header.ccsds_version(), 0b000); assert_eq!(sp_header.ccsds_version(), 0b000);
assert_eq!(sp_header.data_len, 0); assert_eq!(sp_header.data_len, 0);
let sp_header = let sp_header = SpHeader::tm_unseg(0x7, 22, 36).expect("Error creating SP header");
SpHeader::new_for_unseg_tm_checked(0x7, 22, 36).expect("Error creating SP header");
assert_eq!(sp_header.ccsds_version(), 0b000); assert_eq!(sp_header.ccsds_version(), 0b000);
assert!(sp_header.is_tm()); assert!(sp_header.is_tm());
assert!(!sp_header.sec_header_flag()); assert!(!sp_header.sec_header_flag());
@ -1099,8 +953,8 @@ pub(crate) mod tests {
assert_eq!(sp_header.ccsds_version(), 0b000); assert_eq!(sp_header.ccsds_version(), 0b000);
let from_comp_fields = SpHeader::from_composite_fields( let from_comp_fields = SpHeader::from_composite_fields(
PacketId::new(PacketType::Tc, true, 0x42), PacketId::new(PacketType::Tc, true, 0x42).unwrap(),
PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0x7), PacketSequenceCtrl::new(SequenceFlags::Unsegmented, 0x7).unwrap(),
0, 0,
None, None,
); );
@ -1117,7 +971,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_setters() { fn test_setters() {
let sp_header = SpHeader::new_for_tc_checked(0x42, SequenceFlags::Unsegmented, 25, 0); let sp_header = SpHeader::tc(0x42, SequenceFlags::Unsegmented, 25, 0);
assert!(sp_header.is_some()); assert!(sp_header.is_some());
let mut sp_header = sp_header.unwrap(); let mut sp_header = sp_header.unwrap();
sp_header.set_apid(0x12); sp_header.set_apid(0x12);
@ -1135,7 +989,7 @@ pub(crate) mod tests {
#[test] #[test]
fn test_tc_ctor() { fn test_tc_ctor() {
let sp_header = SpHeader::new_for_tc_checked(0x42, SequenceFlags::Unsegmented, 25, 0); let sp_header = SpHeader::tc(0x42, SequenceFlags::Unsegmented, 25, 0);
assert!(sp_header.is_some()); assert!(sp_header.is_some());
let sp_header = sp_header.unwrap(); let sp_header = sp_header.unwrap();
verify_sp_fields(PacketType::Tc, &sp_header); verify_sp_fields(PacketType::Tc, &sp_header);
@ -1143,35 +997,23 @@ pub(crate) mod tests {
#[test] #[test]
fn test_tc_ctor_unseg() { fn test_tc_ctor_unseg() {
let sp_header = SpHeader::new_for_unseg_tc_checked(0x42, 25, 0); let sp_header = SpHeader::tc_unseg(0x42, 25, 0);
assert!(sp_header.is_some()); assert!(sp_header.is_some());
let sp_header = sp_header.unwrap(); let sp_header = sp_header.unwrap();
verify_sp_fields(PacketType::Tc, &sp_header); verify_sp_fields(PacketType::Tc, &sp_header);
} }
#[test]
fn test_tc_ctor_unseg_const() {
let sp_header = SpHeader::new_for_unseg_tc(0x42, 25, 0);
verify_sp_fields(PacketType::Tc, &sp_header);
}
#[test] #[test]
fn test_tm_ctor() { fn test_tm_ctor() {
let sp_header = SpHeader::new_for_tm_checked(0x42, SequenceFlags::Unsegmented, 25, 0); let sp_header = SpHeader::tm(0x42, SequenceFlags::Unsegmented, 25, 0);
assert!(sp_header.is_some()); assert!(sp_header.is_some());
let sp_header = sp_header.unwrap(); let sp_header = sp_header.unwrap();
verify_sp_fields(PacketType::Tm, &sp_header); verify_sp_fields(PacketType::Tm, &sp_header);
} }
#[test]
fn test_tm_ctor_const() {
let sp_header = SpHeader::new_for_tm(0x42, SequenceFlags::Unsegmented, 25, 0);
verify_sp_fields(PacketType::Tm, &sp_header);
}
#[test] #[test]
fn test_tm_ctor_unseg() { fn test_tm_ctor_unseg() {
let sp_header = SpHeader::new_for_unseg_tm_checked(0x42, 25, 0); let sp_header = SpHeader::tm_unseg(0x42, 25, 0);
assert!(sp_header.is_some()); assert!(sp_header.is_some());
let sp_header = sp_header.unwrap(); let sp_header = sp_header.unwrap();
verify_sp_fields(PacketType::Tm, &sp_header); verify_sp_fields(PacketType::Tm, &sp_header);
@ -1189,8 +1031,8 @@ pub(crate) mod tests {
fn test_zc_sph() { fn test_zc_sph() {
use zerocopy::AsBytes; use zerocopy::AsBytes;
let sp_header = SpHeader::new_for_unseg_tc_checked(0x7FF, pow(2, 14) - 1, 0) let sp_header =
.expect("Error creating SP header"); SpHeader::tc_unseg(0x7FF, pow(2, 14) - 1, 0).expect("Error creating SP header");
assert_eq!(sp_header.ptype(), PacketType::Tc); assert_eq!(sp_header.ptype(), PacketType::Tc);
assert_eq!(sp_header.apid(), 0x7FF); assert_eq!(sp_header.apid(), 0x7FF);
assert_eq!(sp_header.data_len(), 0); assert_eq!(sp_header.data_len(), 0);
@ -1255,28 +1097,4 @@ pub(crate) mod tests {
let mut id_set = HashSet::new(); let mut id_set = HashSet::new();
id_set.insert(PacketId::from(1_u16)); id_set.insert(PacketId::from(1_u16));
} }
#[test]
fn sp_header_from_apid() {
let sp_header = SpHeader::new_from_apid(0x03);
assert_eq!(sp_header.apid(), 0x03);
}
#[test]
fn sp_header_from_apid_checked() {
let sp_header = SpHeader::new_from_apid_checked(0x03).unwrap();
assert_eq!(sp_header.apid(), 0x03);
}
#[cfg(feature = "defmt")]
fn is_defmt_format<T: defmt::Format>(_t: T) {}
#[test]
#[cfg(feature = "defmt")]
fn test_defmt_format() {
is_defmt_format(ByteConversionError::ToSliceTooSmall {
found: 1,
expected: 2,
});
}
} }

View File

@ -40,11 +40,13 @@ pub mod alloc_mod_chrono {
}; };
/// Generates a time code formatter using the [FMT_STR_CODE_A_WITH_SIZE] format. /// Generates a time code formatter using the [FMT_STR_CODE_A_WITH_SIZE] format.
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "alloc", feature = "chrono"))))]
pub fn generate_time_code_a(date: &DateTime<Utc>) -> DelayedFormat<StrftimeItems<'static>> { pub fn generate_time_code_a(date: &DateTime<Utc>) -> DelayedFormat<StrftimeItems<'static>> {
date.format(FMT_STR_CODE_A_WITH_SIZE.0) date.format(FMT_STR_CODE_A_WITH_SIZE.0)
} }
/// Generates a time code formatter using the [FMT_STR_CODE_A_TERMINATED_WITH_SIZE] format. /// Generates a time code formatter using the [FMT_STR_CODE_A_TERMINATED_WITH_SIZE] format.
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "alloc", feature = "chrono"))))]
pub fn generate_time_code_a_terminated( pub fn generate_time_code_a_terminated(
date: &DateTime<Utc>, date: &DateTime<Utc>,
) -> DelayedFormat<StrftimeItems<'static>> { ) -> DelayedFormat<StrftimeItems<'static>> {
@ -52,11 +54,13 @@ pub mod alloc_mod_chrono {
} }
/// Generates a time code formatter using the [FMT_STR_CODE_B_WITH_SIZE] format. /// Generates a time code formatter using the [FMT_STR_CODE_B_WITH_SIZE] format.
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "alloc", feature = "chrono"))))]
pub fn generate_time_code_b(date: &DateTime<Utc>) -> DelayedFormat<StrftimeItems<'static>> { pub fn generate_time_code_b(date: &DateTime<Utc>) -> DelayedFormat<StrftimeItems<'static>> {
date.format(FMT_STR_CODE_B_WITH_SIZE.0) date.format(FMT_STR_CODE_B_WITH_SIZE.0)
} }
/// Generates a time code formatter using the [FMT_STR_CODE_B_TERMINATED_WITH_SIZE] format. /// Generates a time code formatter using the [FMT_STR_CODE_B_TERMINATED_WITH_SIZE] format.
#[cfg_attr(doc_cfg, doc(cfg(all(feature = "alloc", feature = "chrono"))))]
pub fn generate_time_code_b_terminated( pub fn generate_time_code_b_terminated(
date: &DateTime<Utc>, date: &DateTime<Utc>,
) -> DelayedFormat<StrftimeItems<'static>> { ) -> DelayedFormat<StrftimeItems<'static>> {

View File

@ -34,9 +34,8 @@ use core::any::Any;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{ use super::{
ccsds_to_unix_days, unix_to_ccsds_days, CcsdsTimeCode, CcsdsTimeProvider, ccsds_to_unix_days, unix_to_ccsds_days, CcsdsTimeCode, CcsdsTimeProvider, TimeReader,
DateBeforeCcsdsEpochError, TimeReader, TimeWriter, TimestampError, UnixTime, MS_PER_DAY, TimeWriter, TimestampError, UnixTime, MS_PER_DAY, SECONDS_PER_DAY,
SECONDS_PER_DAY,
}; };
/// Base value for the preamble field for a time field parser to determine the time field type. /// Base value for the preamble field for a time field parser to determine the time field type.
@ -78,7 +77,6 @@ impl ProvidesDaysLength for DaysLen24Bits {
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum LengthOfDaySegment { pub enum LengthOfDaySegment {
Short16Bits = 0, Short16Bits = 0,
Long24Bits = 1, Long24Bits = 1,
@ -95,14 +93,12 @@ pub enum SubmillisPrecision {
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CdsError { pub enum CdsError {
/// CCSDS days value exceeds maximum allowed size or is negative /// CCSDS days value exceeds maximum allowed size or is negative
InvalidCcsdsDays(i64), InvalidCcsdsDays(i64),
/// There are distinct constructors depending on the days field width detected in the preamble /// There are distinct constructors depending on the days field width detected in the preamble
/// field. This error will be returned if there is a missmatch. /// field. This error will be returned if there is a missmatch.
InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment), InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment),
DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError),
} }
impl Display for CdsError { impl Display for CdsError {
@ -117,27 +113,12 @@ impl Display for CdsError {
"wrong constructor for length of day {length_of_day:?} detected in preamble", "wrong constructor for length of day {length_of_day:?} detected in preamble",
) )
} }
CdsError::DateBeforeCcsdsEpoch(e) => write!(f, "date before CCSDS epoch: {e}"),
} }
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl Error for CdsError { impl Error for CdsError {}
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
CdsError::DateBeforeCcsdsEpoch(e) => Some(e),
_ => None,
}
}
}
impl From<DateBeforeCcsdsEpochError> for CdsError {
fn from(value: DateBeforeCcsdsEpochError) -> Self {
Self::DateBeforeCcsdsEpoch(value)
}
}
pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment { pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
if (pfield >> 2) & 0b1 == 1 { if (pfield >> 2) & 0b1 == 1 {
return LengthOfDaySegment::Long24Bits; return LengthOfDaySegment::Long24Bits;
@ -145,7 +126,6 @@ pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
LengthOfDaySegment::Short16Bits LengthOfDaySegment::Short16Bits
} }
#[inline]
pub fn precision_from_pfield(pfield: u8) -> SubmillisPrecision { pub fn precision_from_pfield(pfield: u8) -> SubmillisPrecision {
match pfield & 0b11 { match pfield & 0b11 {
0b01 => SubmillisPrecision::Microseconds, 0b01 => SubmillisPrecision::Microseconds,
@ -179,7 +159,7 @@ pub fn precision_from_pfield(pfield: u8) -> SubmillisPrecision {
/// use spacepackets::time::cds::{CdsTime, length_of_day_segment_from_pfield, LengthOfDaySegment}; /// use spacepackets::time::cds::{CdsTime, length_of_day_segment_from_pfield, LengthOfDaySegment};
/// use spacepackets::time::{TimeWriter, CcsdsTimeCode, CcsdsTimeProvider}; /// use spacepackets::time::{TimeWriter, CcsdsTimeCode, CcsdsTimeProvider};
/// ///
/// let timestamp_now = CdsTime::now_with_u16_days().unwrap(); /// let timestamp_now = CdsTime::from_now_with_u16_days().unwrap();
/// let mut raw_stamp = [0; 7]; /// let mut raw_stamp = [0; 7];
/// { /// {
/// let written = timestamp_now.write_to_bytes(&mut raw_stamp).unwrap(); /// let written = timestamp_now.write_to_bytes(&mut raw_stamp).unwrap();
@ -246,11 +226,11 @@ impl ConversionFromUnix {
unix_seconds: i64, unix_seconds: i64,
subsec_nanos: u32, subsec_nanos: u32,
precision: SubmillisPrecision, precision: SubmillisPrecision,
) -> Result<Self, DateBeforeCcsdsEpochError> { ) -> Result<Self, TimestampError> {
let (unix_days, secs_of_day) = calc_unix_days_and_secs_of_day(unix_seconds); let (unix_days, secs_of_day) = calc_unix_days_and_secs_of_day(unix_seconds);
let ccsds_days = unix_to_ccsds_days(unix_days); let ccsds_days = unix_to_ccsds_days(unix_days);
if ccsds_days == 0 && (secs_of_day > 0 || subsec_nanos > 0) || ccsds_days < 0 { if ccsds_days == 0 && (secs_of_day > 0 || subsec_nanos > 0) || ccsds_days < 0 {
return Err(DateBeforeCcsdsEpochError( return Err(TimestampError::DateBeforeCcsdsEpoch(
UnixTime::new_checked(unix_seconds, subsec_nanos) UnixTime::new_checked(unix_seconds, subsec_nanos)
.expect("unix timestamp creation failed"), .expect("unix timestamp creation failed"),
)); ));
@ -273,29 +253,24 @@ impl ConversionFromUnix {
} }
impl CdsCommon for ConversionFromUnix { impl CdsCommon for ConversionFromUnix {
#[inline]
fn submillis_precision(&self) -> SubmillisPrecision { fn submillis_precision(&self) -> SubmillisPrecision {
self.submilis_prec self.submilis_prec
} }
#[inline]
fn ms_of_day(&self) -> u32 { fn ms_of_day(&self) -> u32 {
self.ms_of_day self.ms_of_day
} }
#[inline]
fn ccsds_days_as_u32(&self) -> u32 { fn ccsds_days_as_u32(&self) -> u32 {
self.ccsds_days self.ccsds_days
} }
#[inline]
fn submillis(&self) -> u32 { fn submillis(&self) -> u32 {
self.submillis self.submillis
} }
} }
impl CdsConverter for ConversionFromUnix { impl CdsConverter for ConversionFromUnix {
#[inline]
fn unix_days_seconds(&self) -> i64 { fn unix_days_seconds(&self) -> i64 {
self.unix_days_seconds self.unix_days_seconds
} }
@ -308,31 +283,24 @@ struct ConversionFromChronoDatetime {
} }
impl CdsCommon for ConversionFromChronoDatetime { impl CdsCommon for ConversionFromChronoDatetime {
#[inline]
fn submillis_precision(&self) -> SubmillisPrecision { fn submillis_precision(&self) -> SubmillisPrecision {
self.submillis_prec self.submillis_prec
} }
delegate! { delegate! {
to self.unix_conversion { to self.unix_conversion {
#[inline]
fn ms_of_day(&self) -> u32; fn ms_of_day(&self) -> u32;
#[inline]
fn ccsds_days_as_u32(&self) -> u32; fn ccsds_days_as_u32(&self) -> u32;
} }
} }
#[inline]
fn submillis(&self) -> u32 { fn submillis(&self) -> u32 {
self.submillis self.submillis
} }
} }
impl CdsConverter for ConversionFromChronoDatetime { impl CdsConverter for ConversionFromChronoDatetime {
delegate! {to self.unix_conversion { delegate! {to self.unix_conversion { fn unix_days_seconds(&self) -> i64; }}
#[inline]
fn unix_days_seconds(&self) -> i64;
}}
} }
#[inline] #[inline]
@ -350,19 +318,19 @@ fn calc_unix_days_and_secs_of_day(unix_seconds: i64) -> (i64, u32) {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
impl ConversionFromChronoDatetime { impl ConversionFromChronoDatetime {
fn new(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, DateBeforeCcsdsEpochError> { fn new(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, TimestampError> {
Self::new_generic(dt, SubmillisPrecision::Absent) Self::new_generic(dt, SubmillisPrecision::Absent)
} }
fn new_with_submillis_us_prec( fn new_with_submillis_us_prec(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, DateBeforeCcsdsEpochError> { ) -> Result<Self, TimestampError> {
Self::new_generic(dt, SubmillisPrecision::Microseconds) Self::new_generic(dt, SubmillisPrecision::Microseconds)
} }
fn new_with_submillis_ps_prec( fn new_with_submillis_ps_prec(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, DateBeforeCcsdsEpochError> { ) -> Result<Self, TimestampError> {
Self::new_generic(dt, SubmillisPrecision::Picoseconds) Self::new_generic(dt, SubmillisPrecision::Picoseconds)
} }
@ -370,10 +338,10 @@ impl ConversionFromChronoDatetime {
fn new_generic( fn new_generic(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
prec: SubmillisPrecision, prec: SubmillisPrecision,
) -> Result<Self, DateBeforeCcsdsEpochError> { ) -> Result<Self, TimestampError> {
// The CDS timestamp does not support timestamps before the CCSDS epoch. // The CDS timestamp does not support timestamps before the CCSDS epoch.
if dt.year() < 1958 { if dt.year() < 1958 {
return Err(DateBeforeCcsdsEpochError(UnixTime::from(*dt))); return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTime::from(*dt)));
} }
// The contained values in the conversion should be all positive now // The contained values in the conversion should be all positive now
let unix_conversion = let unix_conversion =
@ -466,10 +434,13 @@ impl CdsConverter for ConversionFromNow {
} }
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub trait DynCdsTimeProvider: CcsdsTimeProvider + CdsTimestamp + TimeWriter + Any {} pub trait DynCdsTimeProvider: CcsdsTimeProvider + CdsTimestamp + TimeWriter + Any {}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl DynCdsTimeProvider for CdsTime<DaysLen16Bits> {} impl DynCdsTimeProvider for CdsTime<DaysLen16Bits> {}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl DynCdsTimeProvider for CdsTime<DaysLen24Bits> {} impl DynCdsTimeProvider for CdsTime<DaysLen24Bits> {}
/// This function returns the correct [CdsTime] instance from a raw byte array /// This function returns the correct [CdsTime] instance from a raw byte array
@ -499,6 +470,7 @@ impl DynCdsTimeProvider for CdsTime<DaysLen24Bits> {}
/// } /// }
/// ``` /// ```
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
pub fn get_dyn_time_provider_from_bytes( pub fn get_dyn_time_provider_from_bytes(
buf: &[u8], buf: &[u8],
) -> Result<Box<dyn DynCdsTimeProvider>, TimestampError> { ) -> Result<Box<dyn DynCdsTimeProvider>, TimestampError> {
@ -708,7 +680,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
fn from_dt_generic( fn from_dt_generic(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
days_len: LengthOfDaySegment, days_len: LengthOfDaySegment,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
let conv_from_dt = ConversionFromChronoDatetime::new(dt)?; let conv_from_dt = ConversionFromChronoDatetime::new(dt)?;
Self::generic_from_conversion(days_len, conv_from_dt) Self::generic_from_conversion(days_len, conv_from_dt)
} }
@ -717,7 +689,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
fn from_dt_generic_us_prec( fn from_dt_generic_us_prec(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
days_len: LengthOfDaySegment, days_len: LengthOfDaySegment,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_us_prec(dt)?; let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_us_prec(dt)?;
Self::generic_from_conversion(days_len, conv_from_dt) Self::generic_from_conversion(days_len, conv_from_dt)
} }
@ -726,7 +698,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
fn from_dt_generic_ps_prec( fn from_dt_generic_ps_prec(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
days_len: LengthOfDaySegment, days_len: LengthOfDaySegment,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_ps_prec(dt)?; let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_ps_prec(dt)?;
Self::generic_from_conversion(days_len, conv_from_dt) Self::generic_from_conversion(days_len, conv_from_dt)
} }
@ -735,41 +707,43 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
unix_stamp: &UnixTime, unix_stamp: &UnixTime,
days_len: LengthOfDaySegment, days_len: LengthOfDaySegment,
submillis_prec: SubmillisPrecision, submillis_prec: SubmillisPrecision,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
let conv_from_dt = let conv_from_dt =
ConversionFromUnix::new(unix_stamp.secs, unix_stamp.subsec_nanos, submillis_prec)?; ConversionFromUnix::new(unix_stamp.secs, unix_stamp.subsec_nanos, submillis_prec)?;
Self::generic_from_conversion(days_len, conv_from_dt) Self::generic_from_conversion(days_len, conv_from_dt)
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn now_generic(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> { fn from_now_generic(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
let conversion_from_now = ConversionFromNow::new()?; let conversion_from_now = ConversionFromNow::new()?;
Self::generic_from_conversion(days_len, conversion_from_now) Self::generic_from_conversion(days_len, conversion_from_now)
.map_err(|e| StdTimestampError::Timestamp(TimestampError::from(e))) .map_err(StdTimestampError::Timestamp)
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn now_generic_with_us_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> { fn from_now_generic_us_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
let conversion_from_now = ConversionFromNow::new_with_submillis_us_prec()?; let conversion_from_now = ConversionFromNow::new_with_submillis_us_prec()?;
Self::generic_from_conversion(days_len, conversion_from_now) Self::generic_from_conversion(days_len, conversion_from_now)
.map_err(|e| StdTimestampError::Timestamp(TimestampError::from(e))) .map_err(StdTimestampError::Timestamp)
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
fn from_now_generic_ps_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> { fn from_now_generic_ps_prec(days_len: LengthOfDaySegment) -> Result<Self, StdTimestampError> {
let conversion_from_now = ConversionFromNow::new_with_submillis_ps_prec()?; let conversion_from_now = ConversionFromNow::new_with_submillis_ps_prec()?;
Self::generic_from_conversion(days_len, conversion_from_now) Self::generic_from_conversion(days_len, conversion_from_now)
.map_err(|e| StdTimestampError::Timestamp(TimestampError::from(e))) .map_err(StdTimestampError::Timestamp)
} }
fn generic_from_conversion<C: CdsConverter>( fn generic_from_conversion<C: CdsConverter>(
days_len: LengthOfDaySegment, days_len: LengthOfDaySegment,
converter: C, converter: C,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
let ccsds_days: ProvidesDaysLen::FieldType = converter let ccsds_days: ProvidesDaysLen::FieldType =
.ccsds_days_as_u32() converter.ccsds_days_as_u32().try_into().map_err(|_| {
.try_into() TimestampError::Cds(CdsError::InvalidCcsdsDays(
.map_err(|_| CdsError::InvalidCcsdsDays(converter.ccsds_days_as_u32().into()))?; converter.ccsds_days_as_u32().into(),
))
})?;
let mut provider = Self { let mut provider = Self {
pfield: Self::generate_p_field(days_len, converter.submillis_precision()), pfield: Self::generate_p_field(days_len, converter.submillis_precision()),
ccsds_days, ccsds_days,
@ -802,6 +776,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> { pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
let conversion_from_now = self.generic_conversion_from_now()?; let conversion_from_now = self.generic_conversion_from_now()?;
let ccsds_days: ProvidesDaysLen::FieldType = conversion_from_now let ccsds_days: ProvidesDaysLen::FieldType = conversion_from_now
@ -837,19 +812,22 @@ impl CdsTime<DaysLen24Bits> {
/// Generate a time stamp from the current time using the system clock. /// Generate a time stamp from the current time using the system clock.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now_with_u24_days() -> Result<Self, StdTimestampError> { #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
Self::now_generic(LengthOfDaySegment::Long24Bits) pub fn from_now_with_u24_days() -> Result<Self, StdTimestampError> {
Self::from_now_generic(LengthOfDaySegment::Long24Bits)
} }
/// Create a provider from a [`chrono::DateTime<chrono::Utc>`] struct. /// Create a provider from a [`chrono::DateTime<chrono::Utc>`] struct.
/// ///
/// ## Errors /// ## Errors
/// ///
/// This function will return [CdsError::DateBeforeCcsdsEpoch] if the time is before the CCSDS /// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
/// epoch (1958-01-01T00:00:00+00:00) or the CCSDS days value exceeds the allowed bit width /// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
/// (24 bits). /// or the CCSDS days value exceeds the allowed bit width (24 bits).
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_dt_with_u24_days(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, CdsError> { pub fn from_dt_with_u24_days(
dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, TimestampError> {
Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits) Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits)
} }
@ -857,13 +835,13 @@ impl CdsTime<DaysLen24Bits> {
/// ///
/// ## Errors /// ## Errors
/// ///
/// This function will return [CdsError::DateBeforeCcsdsEpoch] if the time is before the CCSDS /// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
/// epoch (1958-01-01T00:00:00+00:00) or the CCSDS days value exceeds the allowed bit width /// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
/// (24 bits). /// or the CCSDS days value exceeds the allowed bit width (24 bits).
pub fn from_unix_time_with_u24_day( pub fn from_unix_stamp_with_u24_days(
unix_stamp: &UnixTime, unix_stamp: &UnixTime,
submillis_prec: SubmillisPrecision, submillis_prec: SubmillisPrecision,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits, submillis_prec) Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits, submillis_prec)
} }
@ -871,7 +849,7 @@ impl CdsTime<DaysLen24Bits> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_dt_with_u24_days_us_precision( pub fn from_dt_with_u24_days_us_precision(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Long24Bits) Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Long24Bits)
} }
@ -879,20 +857,22 @@ impl CdsTime<DaysLen24Bits> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_dt_with_u24_days_ps_precision( pub fn from_dt_with_u24_days_ps_precision(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Long24Bits) Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Long24Bits)
} }
/// Like [Self::now_with_u24_days] but with microsecond sub-millisecond precision. /// Like [Self::from_now_with_u24_days] but with microsecond sub-millisecond precision.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now_with_u24_days_us_precision() -> Result<Self, StdTimestampError> { #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
Self::now_generic_with_us_prec(LengthOfDaySegment::Long24Bits) pub fn from_now_with_u24_days_us_precision() -> Result<Self, StdTimestampError> {
Self::from_now_generic_us_prec(LengthOfDaySegment::Long24Bits)
} }
/// Like [Self::now_with_u24_days] but with picoseconds sub-millisecond precision. /// Like [Self::from_now_with_u24_days] but with picoseconds sub-millisecond precision.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now_with_u24_days_ps_precision() -> Result<Self, StdTimestampError> { #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
Self::now_generic_with_us_prec(LengthOfDaySegment::Long24Bits) pub fn from_now_with_u24_days_ps_precision() -> Result<Self, StdTimestampError> {
Self::from_now_generic_us_prec(LengthOfDaySegment::Long24Bits)
} }
pub fn from_bytes_with_u24_days(buf: &[u8]) -> Result<Self, TimestampError> { pub fn from_bytes_with_u24_days(buf: &[u8]) -> Result<Self, TimestampError> {
@ -931,31 +911,34 @@ impl CdsTime<DaysLen16Bits> {
/// Create a provider from a [`chrono::DateTime<Utc>`] struct. /// Create a provider from a [`chrono::DateTime<Utc>`] struct.
/// ///
/// This function will return a [CdsError::DateBeforeCcsdsEpoch] if the time is before the /// This function will return a [TimestampError::DateBeforeCcsdsEpoch] or a
/// CCSDS epoch (01-01-1958 00:00:00) or the CCSDS days value exceeds the allowed bit width /// [TimestampError::Cds] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or
/// (16 bits). /// the CCSDS days value exceeds the allowed bit width (16 bits).
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_dt_with_u16_days(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, CdsError> { pub fn from_dt_with_u16_days(
dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, TimestampError> {
Self::from_dt_generic(dt, LengthOfDaySegment::Short16Bits) Self::from_dt_generic(dt, LengthOfDaySegment::Short16Bits)
} }
/// Generate a time stamp from the current time using the system clock. /// Generate a time stamp from the current time using the system clock.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now_with_u16_days() -> Result<Self, StdTimestampError> { #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
Self::now_generic(LengthOfDaySegment::Short16Bits) pub fn from_now_with_u16_days() -> Result<Self, StdTimestampError> {
Self::from_now_generic(LengthOfDaySegment::Short16Bits)
} }
/// Create a provider from a generic UNIX timestamp (seconds since 1970-01-01T00:00:00+00:00). /// Create a provider from a generic UNIX timestamp (seconds since 1970-01-01T00:00:00+00:00).
/// ///
/// ## Errors /// ## Errors
/// ///
/// This function will return [CdsError::DateBeforeCcsdsEpoch] if the time is before the CCSDS /// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
/// epoch (1958-01-01T00:00:00+00:00) or the CCSDS days value exceeds the allowed bit width /// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
/// (24 bits). /// or the CCSDS days value exceeds the allowed bit width (24 bits).
pub fn from_unix_time_with_u16_days( pub fn from_unix_stamp_with_u16_days(
unix_stamp: &UnixTime, unix_stamp: &UnixTime,
submillis_prec: SubmillisPrecision, submillis_prec: SubmillisPrecision,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits, submillis_prec) Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits, submillis_prec)
} }
@ -963,7 +946,7 @@ impl CdsTime<DaysLen16Bits> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_dt_with_u16_days_us_precision( pub fn from_dt_with_u16_days_us_precision(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Short16Bits) Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Short16Bits)
} }
@ -971,18 +954,20 @@ impl CdsTime<DaysLen16Bits> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
pub fn from_dt_with_u16_days_ps_precision( pub fn from_dt_with_u16_days_ps_precision(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
) -> Result<Self, CdsError> { ) -> Result<Self, TimestampError> {
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Short16Bits) Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Short16Bits)
} }
/// Like [Self::now_with_u16_days] but with microsecond sub-millisecond precision. /// Like [Self::from_now_with_u16_days] but with microsecond sub-millisecond precision.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now_with_u16_days_us_precision() -> Result<Self, StdTimestampError> { #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
Self::now_generic_with_us_prec(LengthOfDaySegment::Short16Bits) pub fn from_now_with_u16_days_us_precision() -> Result<Self, StdTimestampError> {
Self::from_now_generic_us_prec(LengthOfDaySegment::Short16Bits)
} }
/// Like [Self::now_with_u16_days] but with picosecond sub-millisecond precision. /// Like [Self::from_now_with_u16_days] but with picosecond sub-millisecond precision.
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn from_now_with_u16_days_ps_precision() -> Result<Self, StdTimestampError> { pub fn from_now_with_u16_days_ps_precision() -> Result<Self, StdTimestampError> {
Self::from_now_generic_ps_prec(LengthOfDaySegment::Short16Bits) Self::from_now_generic_ps_prec(LengthOfDaySegment::Short16Bits)
} }
@ -1183,7 +1168,7 @@ impl AddAssign<Duration> for CdsTime<DaysLen24Bits> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen16Bits> { impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen16Bits> {
type Error = CdsError; type Error = TimestampError;
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> { fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
let conversion = ConversionFromChronoDatetime::new(&dt)?; let conversion = ConversionFromChronoDatetime::new(&dt)?;
@ -1193,7 +1178,8 @@ impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen16Bits> {
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen24Bits> { impl TryFrom<chrono::DateTime<chrono::Utc>> for CdsTime<DaysLen24Bits> {
type Error = CdsError; type Error = TimestampError;
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> { fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
let conversion = ConversionFromChronoDatetime::new(&dt)?; let conversion = ConversionFromChronoDatetime::new(&dt)?;
Self::generic_from_conversion(LengthOfDaySegment::Long24Bits, conversion) Self::generic_from_conversion(LengthOfDaySegment::Long24Bits, conversion)
@ -1623,14 +1609,14 @@ mod tests {
#[test] #[test]
fn test_time_now() { fn test_time_now() {
let timestamp_now = CdsTime::now_with_u16_days().unwrap(); let timestamp_now = CdsTime::from_now_with_u16_days().unwrap();
let compare_stamp = chrono::Utc::now(); let compare_stamp = chrono::Utc::now();
generic_now_test(timestamp_now, compare_stamp); generic_now_test(timestamp_now, compare_stamp);
} }
#[test] #[test]
fn test_time_now_us_prec() { fn test_time_now_us_prec() {
let timestamp_now = CdsTime::now_with_u16_days_us_precision().unwrap(); let timestamp_now = CdsTime::from_now_with_u16_days_us_precision().unwrap();
let compare_stamp = chrono::Utc::now(); let compare_stamp = chrono::Utc::now();
generic_now_test(timestamp_now, compare_stamp); generic_now_test(timestamp_now, compare_stamp);
} }
@ -1651,7 +1637,7 @@ mod tests {
#[test] #[test]
fn test_time_now_ps_prec_u24_days() { fn test_time_now_ps_prec_u24_days() {
let timestamp_now = CdsTime::now_with_u24_days_ps_precision().unwrap(); let timestamp_now = CdsTime::from_now_with_u24_days_ps_precision().unwrap();
let compare_stamp = chrono::Utc::now(); let compare_stamp = chrono::Utc::now();
generic_now_test(timestamp_now, compare_stamp); generic_now_test(timestamp_now, compare_stamp);
} }
@ -1936,7 +1922,7 @@ mod tests {
fn test_creation_from_unix_stamp_0_u16_days() { fn test_creation_from_unix_stamp_0_u16_days() {
let unix_secs = 0; let unix_secs = 0;
let subsec_millis = 0; let subsec_millis = 0;
let time_provider = CdsTime::from_unix_time_with_u16_days( let time_provider = CdsTime::from_unix_stamp_with_u16_days(
&UnixTime::new(unix_secs, subsec_millis), &UnixTime::new(unix_secs, subsec_millis),
SubmillisPrecision::Absent, SubmillisPrecision::Absent,
) )
@ -1948,7 +1934,7 @@ mod tests {
fn test_creation_from_unix_stamp_0_u24_days() { fn test_creation_from_unix_stamp_0_u24_days() {
let unix_secs = 0; let unix_secs = 0;
let subsec_millis = 0; let subsec_millis = 0;
let time_provider = CdsTime::from_unix_time_with_u24_day( let time_provider = CdsTime::from_unix_stamp_with_u24_days(
&UnixTime::new(unix_secs, subsec_millis), &UnixTime::new(unix_secs, subsec_millis),
SubmillisPrecision::Absent, SubmillisPrecision::Absent,
) )
@ -1965,9 +1951,11 @@ mod tests {
.unwrap() .unwrap()
.and_local_timezone(chrono::Utc) .and_local_timezone(chrono::Utc)
.unwrap(); .unwrap();
let time_provider = let time_provider = CdsTime::from_unix_stamp_with_u16_days(
CdsTime::from_unix_time_with_u16_days(&datetime_utc.into(), SubmillisPrecision::Absent) &datetime_utc.into(),
.expect("creating provider from unix stamp failed"); SubmillisPrecision::Absent,
)
.expect("creating provider from unix stamp failed");
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023 // https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
// Leap years need to be accounted for as well. // Leap years need to be accounted for as well.
assert_eq!(time_provider.ccsds_days, 23754); assert_eq!(time_provider.ccsds_days, 23754);
@ -1983,7 +1971,7 @@ mod tests {
fn test_creation_0_ccsds_days() { fn test_creation_0_ccsds_days() {
let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64; let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64;
let subsec_millis = 0; let subsec_millis = 0;
let time_provider = CdsTime::from_unix_time_with_u16_days( let time_provider = CdsTime::from_unix_stamp_with_u16_days(
&UnixTime::new(unix_secs, subsec_millis), &UnixTime::new(unix_secs, subsec_millis),
SubmillisPrecision::Absent, SubmillisPrecision::Absent,
) )
@ -1995,7 +1983,7 @@ mod tests {
fn test_invalid_creation_from_unix_stamp_days_too_large() { fn test_invalid_creation_from_unix_stamp_days_too_large() {
let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64; let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64;
let subsec_millis = 0; let subsec_millis = 0;
match CdsTime::from_unix_time_with_u16_days( match CdsTime::from_unix_stamp_with_u16_days(
&UnixTime::new(invalid_unix_secs, subsec_millis), &UnixTime::new(invalid_unix_secs, subsec_millis),
SubmillisPrecision::Absent, SubmillisPrecision::Absent,
) { ) {
@ -2003,12 +1991,12 @@ mod tests {
panic!("creation should not succeed") panic!("creation should not succeed")
} }
Err(e) => { Err(e) => {
if let CdsError::InvalidCcsdsDays(days) = e { if let TimestampError::Cds(CdsError::InvalidCcsdsDays(days)) = e {
assert_eq!( assert_eq!(
days, days,
unix_to_ccsds_days(invalid_unix_secs / SECONDS_PER_DAY as i64) unix_to_ccsds_days(invalid_unix_secs / SECONDS_PER_DAY as i64)
); );
assert_eq!(e.to_string(), "invalid ccsds days 69919"); assert_eq!(e.to_string(), "cds error: invalid ccsds days 69919");
} else { } else {
panic!("unexpected error {}", e) panic!("unexpected error {}", e)
} }
@ -2022,7 +2010,7 @@ mod tests {
// precisely 31-12-1957 23:59:55 // precisely 31-12-1957 23:59:55
let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5; let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5;
let subsec_millis = 0; let subsec_millis = 0;
match CdsTime::from_unix_time_with_u16_days( match CdsTime::from_unix_stamp_with_u16_days(
&UnixTime::new(unix_secs as i64, subsec_millis), &UnixTime::new(unix_secs as i64, subsec_millis),
SubmillisPrecision::Absent, SubmillisPrecision::Absent,
) { ) {
@ -2030,8 +2018,8 @@ mod tests {
panic!("creation should not succeed") panic!("creation should not succeed")
} }
Err(e) => { Err(e) => {
if let CdsError::DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError(unix_dt)) = e { if let TimestampError::DateBeforeCcsdsEpoch(dt) = e {
let dt = unix_dt.chrono_date_time(); let dt = dt.chrono_date_time();
if let chrono::LocalResult::Single(dt) = dt { if let chrono::LocalResult::Single(dt) = dt {
assert_eq!(dt.year(), 1957); assert_eq!(dt.year(), 1957);
assert_eq!(dt.month(), 12); assert_eq!(dt.month(), 12);
@ -2261,9 +2249,7 @@ mod tests {
.unwrap(); .unwrap();
let time_provider = CdsTime::from_dt_with_u24_days(&datetime_utc); let time_provider = CdsTime::from_dt_with_u24_days(&datetime_utc);
assert!(time_provider.is_err()); assert!(time_provider.is_err());
if let CdsError::DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError(dt)) = if let TimestampError::DateBeforeCcsdsEpoch(dt) = time_provider.unwrap_err() {
time_provider.unwrap_err()
{
assert_eq!(dt, datetime_utc.into()); assert_eq!(dt, datetime_utc.into());
} }
} }
@ -2322,7 +2308,7 @@ mod tests {
#[test] #[test]
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
fn test_serialization() { fn test_serialization() {
let stamp_now = CdsTime::now_with_u16_days().expect("Error retrieving time"); let stamp_now = CdsTime::from_now_with_u16_days().expect("Error retrieving time");
let val = to_allocvec(&stamp_now).expect("Serializing timestamp failed"); let val = to_allocvec(&stamp_now).expect("Serializing timestamp failed");
assert!(val.len() > 0); assert!(val.len() > 0);
let stamp_deser: CdsTime = from_bytes(&val).expect("Stamp deserialization failed"); let stamp_deser: CdsTime = from_bytes(&val).expect("Stamp deserialization failed");

View File

@ -17,8 +17,7 @@ use crate::ByteConversionError;
use super::StdTimestampError; use super::StdTimestampError;
use super::{ use super::{
ccsds_epoch_to_unix_epoch, ccsds_time_code_from_p_field, unix_epoch_to_ccsds_epoch, ccsds_epoch_to_unix_epoch, ccsds_time_code_from_p_field, unix_epoch_to_ccsds_epoch,
CcsdsTimeCode, CcsdsTimeProvider, DateBeforeCcsdsEpochError, TimeReader, TimeWriter, CcsdsTimeCode, CcsdsTimeProvider, TimeReader, TimeWriter, TimestampError, UnixTime,
TimestampError, UnixTime,
}; };
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::error::Error; use std::error::Error;
@ -37,7 +36,6 @@ pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8;
#[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))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FractionalResolution { pub enum FractionalResolution {
/// No fractional part, only second resolution /// No fractional part, only second resolution
Seconds = 0, Seconds = 0,
@ -106,7 +104,6 @@ pub fn fractional_part_from_subsec_ns(res: FractionalResolution, ns: u64) -> Fra
#[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))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CucError { pub enum CucError {
InvalidCounterWidth(u8), InvalidCounterWidth(u8),
/// Invalid counter supplied. /// Invalid counter supplied.
@ -119,7 +116,6 @@ pub enum CucError {
value: u64, value: u64,
}, },
LeapSecondCorrectionError, LeapSecondCorrectionError,
DateBeforeCcsdsEpoch(DateBeforeCcsdsEpochError),
} }
impl Display for CucError { impl Display for CucError {
@ -140,34 +136,16 @@ impl Display for CucError {
CucError::LeapSecondCorrectionError => { CucError::LeapSecondCorrectionError => {
write!(f, "error while correcting for leap seconds") write!(f, "error while correcting for leap seconds")
} }
CucError::DateBeforeCcsdsEpoch(e) => {
write!(f, "date before ccsds epoch: {e}")
}
} }
} }
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl Error for CucError { impl Error for CucError {}
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
CucError::DateBeforeCcsdsEpoch(e) => Some(e),
_ => None,
}
}
}
impl From<DateBeforeCcsdsEpochError> for CucError {
fn from(e: DateBeforeCcsdsEpochError) -> Self {
Self::DateBeforeCcsdsEpoch(e)
}
}
/// Tuple object where the first value is the width of the counter and the second value
/// is the counter value.
#[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 struct WidthCounterPair(pub u8, pub u32); pub struct WidthCounterPair(u8, 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))]
@ -177,7 +155,6 @@ pub struct FractionalPart {
} }
impl FractionalPart { impl FractionalPart {
#[inline]
pub const fn new(resolution: FractionalResolution, counter: u32) -> Self { pub const fn new(resolution: FractionalResolution, counter: u32) -> Self {
let div = fractional_res_to_div(resolution); let div = fractional_res_to_div(resolution);
assert!(counter <= div, "invalid counter for resolution"); assert!(counter <= div, "invalid counter for resolution");
@ -188,18 +165,15 @@ impl FractionalPart {
} }
/// An empty fractional part for second resolution only. /// An empty fractional part for second resolution only.
#[inline]
pub const fn new_with_seconds_resolution() -> Self { pub const fn new_with_seconds_resolution() -> Self {
Self::new(FractionalResolution::Seconds, 0) Self::new(FractionalResolution::Seconds, 0)
} }
/// Helper method which simply calls [Self::new_with_seconds_resolution]. /// Helper method which simply calls [Self::new_with_seconds_resolution].
#[inline]
pub const fn new_empty() -> Self { pub const fn new_empty() -> Self {
Self::new_with_seconds_resolution() Self::new_with_seconds_resolution()
} }
#[inline]
pub fn new_checked(resolution: FractionalResolution, counter: u32) -> Option<Self> { pub fn new_checked(resolution: FractionalResolution, counter: u32) -> Option<Self> {
let div = fractional_res_to_div(resolution); let div = fractional_res_to_div(resolution);
if counter > div { if counter > div {
@ -211,17 +185,14 @@ impl FractionalPart {
}) })
} }
#[inline]
pub fn resolution(&self) -> FractionalResolution { pub fn resolution(&self) -> FractionalResolution {
self.resolution self.resolution
} }
#[inline]
pub fn counter(&self) -> u32 { pub fn counter(&self) -> u32 {
self.counter self.counter
} }
#[inline]
pub fn no_fractional_part(&self) -> bool { pub fn no_fractional_part(&self) -> bool {
self.resolution == FractionalResolution::Seconds self.resolution == FractionalResolution::Seconds
} }
@ -258,7 +229,7 @@ impl FractionalPart {
/// const LEAP_SECONDS: u32 = 37; /// const LEAP_SECONDS: u32 = 37;
/// ///
/// // Highest fractional resolution /// // Highest fractional resolution
/// let timestamp_now = CucTime::now(FractionalResolution::SixtyNs, LEAP_SECONDS) /// let timestamp_now = CucTime::from_now(FractionalResolution::SixtyNs, LEAP_SECONDS)
/// .expect("creating cuc stamp failed"); /// .expect("creating cuc stamp failed");
/// let mut raw_stamp = [0; 16]; /// let mut raw_stamp = [0; 16];
/// { /// {
@ -292,7 +263,6 @@ pub struct CucTimeWithLeapSecs {
} }
impl CucTimeWithLeapSecs { impl CucTimeWithLeapSecs {
#[inline]
pub fn new(time: CucTime, leap_seconds: u32) -> Self { pub fn new(time: CucTime, leap_seconds: u32) -> Self {
Self { time, leap_seconds } Self { time, leap_seconds }
} }
@ -308,7 +278,6 @@ pub fn pfield_len(pfield: u8) -> usize {
impl CucTime { impl CucTime {
/// Create a time provider with a four byte counter and no fractional part. /// Create a time provider with a four byte counter and no fractional part.
#[inline]
pub fn new(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_generic( Self::new_generic(
@ -319,13 +288,11 @@ impl CucTime {
} }
/// Like [CucTime::new] but allow to supply a fractional part as well. /// Like [CucTime::new] but allow to supply a fractional part as well.
#[inline]
pub fn new_with_fractions(counter: u32, fractions: FractionalPart) -> Result<Self, CucError> { pub fn new_with_fractions(counter: u32, fractions: FractionalPart) -> Result<Self, CucError> {
Self::new_generic(WidthCounterPair(4, counter), fractions) Self::new_generic(WidthCounterPair(4, counter), fractions)
} }
/// Fractions with a resolution of ~ 4 ms /// Fractions with a resolution of ~ 4 ms
#[inline]
pub fn new_with_coarse_fractions(counter: u32, subsec_fractions: u8) -> Self { pub fn new_with_coarse_fractions(counter: u32, subsec_fractions: u8) -> 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_generic( Self::new_generic(
@ -339,7 +306,6 @@ impl CucTime {
} }
/// Fractions with a resolution of ~ 16 us /// Fractions with a resolution of ~ 16 us
#[inline]
pub fn new_with_medium_fractions(counter: u32, subsec_fractions: u16) -> Self { pub fn new_with_medium_fractions(counter: u32, subsec_fractions: 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_generic( Self::new_generic(
@ -355,7 +321,6 @@ impl CucTime {
/// Fractions with a resolution of ~ 60 ns. The fractional part value is limited by the /// Fractions with a resolution of ~ 60 ns. The fractional part value is limited by the
/// 24 bits of the fractional field, so this function will fail with /// 24 bits of the fractional field, so this function will fail with
/// [CucError::InvalidFractions] if the fractional value exceeds the value. /// [CucError::InvalidFractions] if the fractional value exceeds the value.
#[inline]
pub fn new_with_fine_fractions(counter: u32, subsec_fractions: u32) -> Result<Self, CucError> { pub fn new_with_fine_fractions(counter: u32, subsec_fractions: u32) -> Result<Self, CucError> {
Self::new_generic( Self::new_generic(
WidthCounterPair(4, counter), WidthCounterPair(4, counter),
@ -374,7 +339,8 @@ impl CucTime {
/// must be applied on top of the UTC based time retrieved from the system in addition to the /// must be applied on top of the UTC based time retrieved from the system in addition to the
/// conversion to the CCSDS epoch. /// conversion to the CCSDS epoch.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now( #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn from_now(
fraction_resolution: FractionalResolution, fraction_resolution: FractionalResolution,
leap_seconds: u32, leap_seconds: u32,
) -> Result<Self, StdTimestampError> { ) -> Result<Self, StdTimestampError> {
@ -405,6 +371,7 @@ impl CucTime {
/// must be applied on top of the UTC based time retrieved from the system in addition to the /// must be applied on top of the UTC based time retrieved from the system in addition to the
/// conversion to the CCSDS epoch. /// conversion to the CCSDS epoch.
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
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;
@ -424,36 +391,38 @@ impl CucTime {
} }
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
pub fn from_chrono_date_time( pub fn from_chrono_date_time(
dt: &chrono::DateTime<chrono::Utc>, dt: &chrono::DateTime<chrono::Utc>,
res: FractionalResolution, res: FractionalResolution,
leap_seconds: u32, leap_seconds: u32,
) -> Result<Self, CucError> { ) -> Result<Self, TimestampError> {
// Year before CCSDS epoch is invalid. // Year before CCSDS epoch is invalid.
if dt.year() < 1958 { if dt.year() < 1958 {
return Err(DateBeforeCcsdsEpochError(UnixTime::from(*dt)).into()); return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTime::from(*dt)));
} }
let counter = dt let counter = dt
.timestamp() .timestamp()
.checked_add(i64::from(leap_seconds)) .checked_add(i64::from(leap_seconds))
.ok_or(CucError::LeapSecondCorrectionError)?; .ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
Self::new_generic( Self::new_generic(
WidthCounterPair(4, counter as u32), WidthCounterPair(4, counter as u32),
fractional_part_from_subsec_ns(res, dt.timestamp_subsec_nanos() as u64), fractional_part_from_subsec_ns(res, dt.timestamp_subsec_nanos() as u64),
) )
.map_err(|e| e.into())
} }
/// Generates a CUC timestamp from a UNIX timestamp with a width of 4. This width is able /// Generates a CUC timestamp from a UNIX timestamp with a width of 4. This width is able
/// to accomodate all possible UNIX timestamp values. /// to accomodate all possible UNIX timestamp values.
pub fn from_unix_time( pub fn from_unix_stamp(
unix_time: &UnixTime, unix_stamp: &UnixTime,
res: FractionalResolution, res: FractionalResolution,
leap_seconds: u32, leap_seconds: u32,
) -> Result<Self, CucError> { ) -> Result<Self, TimestampError> {
let counter = unix_epoch_to_ccsds_epoch(unix_time.secs); let counter = unix_epoch_to_ccsds_epoch(unix_stamp.secs);
// Negative CCSDS epoch is invalid. // Negative CCSDS epoch is invalid.
if counter < 0 { if counter < 0 {
return Err(DateBeforeCcsdsEpochError(*unix_time).into()); return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp));
} }
// We already excluded negative values, so the conversion to u64 should always work. // We already excluded negative values, so the conversion to u64 should always work.
let mut counter = u32::try_from(counter).map_err(|_| CucError::InvalidCounter { let mut counter = u32::try_from(counter).map_err(|_| CucError::InvalidCounter {
@ -462,66 +431,46 @@ impl CucTime {
})?; })?;
counter = counter counter = counter
.checked_add(leap_seconds) .checked_add(leap_seconds)
.ok_or(CucError::LeapSecondCorrectionError)?; .ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
let fractions = let fractions =
fractional_part_from_subsec_ns(res, unix_time.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, counter as u32), fractions) Self::new_generic(WidthCounterPair(4, counter as u32), fractions).map_err(|e| e.into())
} }
/// Most generic constructor which allows full configurability for the counter and for the pub fn new_u16_counter(counter: u16) -> Self {
/// fractions. // These values are definitely valid, so it is okay to unwrap here.
#[inline] Self::new_generic(
pub fn new_generic( WidthCounterPair(2, counter as u32),
width_and_counter: WidthCounterPair, FractionalPart::new_with_seconds_resolution(),
fractions: FractionalPart, )
) -> Result<Self, CucError> { .unwrap()
Self::verify_counter_width(width_and_counter.0)?;
if width_and_counter.1 > (2u64.pow(width_and_counter.0 as u32 * 8) - 1) as u32 {
return Err(CucError::InvalidCounter {
width: width_and_counter.0,
counter: width_and_counter.1.into(),
});
}
Self::verify_fractions_value(fractions)?;
Ok(Self {
pfield: Self::build_p_field(width_and_counter.0, fractions.resolution),
counter: width_and_counter,
fractions,
})
} }
#[inline]
pub fn ccsds_time_code(&self) -> CcsdsTimeCode { pub fn ccsds_time_code(&self) -> CcsdsTimeCode {
CcsdsTimeCode::CucCcsdsEpoch CcsdsTimeCode::CucCcsdsEpoch
} }
#[inline]
pub fn width_counter_pair(&self) -> WidthCounterPair { pub fn width_counter_pair(&self) -> WidthCounterPair {
self.counter self.counter
} }
#[inline] pub fn width(&self) -> u8 {
pub fn counter_width(&self) -> u8 {
self.counter.0 self.counter.0
} }
#[inline]
pub fn counter(&self) -> u32 { pub fn counter(&self) -> u32 {
self.counter.1 self.counter.1
} }
/// Subsecond fractional part of the CUC time. /// Subsecond fractional part of the CUC time.
#[inline]
pub fn fractions(&self) -> FractionalPart { pub fn fractions(&self) -> FractionalPart {
self.fractions self.fractions
} }
#[inline]
pub fn to_leap_sec_helper(&self, leap_seconds: u32) -> CucTimeWithLeapSecs { pub fn to_leap_sec_helper(&self, leap_seconds: u32) -> CucTimeWithLeapSecs {
CucTimeWithLeapSecs::new(*self, leap_seconds) CucTimeWithLeapSecs::new(*self, leap_seconds)
} }
#[inline]
pub fn set_fractions(&mut self, fractions: FractionalPart) -> Result<(), CucError> { pub fn set_fractions(&mut self, fractions: FractionalPart) -> Result<(), CucError> {
Self::verify_fractions_value(fractions)?; Self::verify_fractions_value(fractions)?;
self.fractions = fractions; self.fractions = fractions;
@ -531,7 +480,6 @@ impl CucTime {
/// Set a fractional resolution. Please note that this function will reset the fractional value /// Set a fractional resolution. Please note that this function will reset the fractional value
/// to 0 if the resolution changes. /// to 0 if the resolution changes.
#[inline]
pub fn set_fractional_resolution(&mut self, res: FractionalResolution) { pub fn set_fractional_resolution(&mut self, res: FractionalResolution) {
if res == FractionalResolution::Seconds { if res == FractionalResolution::Seconds {
self.fractions = FractionalPart::new_with_seconds_resolution(); self.fractions = FractionalPart::new_with_seconds_resolution();
@ -541,7 +489,25 @@ impl CucTime {
} }
} }
#[inline] pub fn new_generic(
counter: WidthCounterPair,
fractions: FractionalPart,
) -> Result<Self, CucError> {
Self::verify_counter_width(counter.0)?;
if counter.1 > (2u64.pow(counter.0 as u32 * 8) - 1) as u32 {
return Err(CucError::InvalidCounter {
width: counter.0,
counter: counter.1.into(),
});
}
Self::verify_fractions_value(fractions)?;
Ok(Self {
pfield: Self::build_p_field(counter.0, fractions.resolution),
counter,
fractions,
})
}
fn build_p_field(counter_width: u8, resolution: FractionalResolution) -> u8 { fn build_p_field(counter_width: u8, resolution: FractionalResolution) -> u8 {
let mut pfield = P_FIELD_BASE; let mut pfield = P_FIELD_BASE;
if !(1..=4).contains(&counter_width) { if !(1..=4).contains(&counter_width) {
@ -561,7 +527,6 @@ impl CucTime {
pfield pfield
} }
#[inline]
fn update_p_field_fractions(&mut self) { fn update_p_field_fractions(&mut self) {
self.pfield &= !(0b11); self.pfield &= !(0b11);
self.pfield |= self.fractions.resolution() as u8; self.pfield |= self.fractions.resolution() as u8;
@ -606,7 +571,6 @@ impl CucTime {
) )
} }
#[inline]
pub fn len_packed_from_pfield(pfield: u8) -> usize { pub fn len_packed_from_pfield(pfield: u8) -> usize {
let mut base_len: usize = 1; let mut base_len: usize = 1;
base_len += Self::len_cntr_from_pfield(pfield) as usize; base_len += Self::len_cntr_from_pfield(pfield) as usize;
@ -615,7 +579,6 @@ impl CucTime {
} }
/// Verifies the raw width parameter. /// Verifies the raw width parameter.
#[inline]
fn verify_counter_width(width: u8) -> Result<(), CucError> { fn verify_counter_width(width: u8) -> Result<(), CucError> {
if width == 0 || width > 4 { if width == 0 || width > 4 {
return Err(CucError::InvalidCounterWidth(width)); return Err(CucError::InvalidCounterWidth(width));
@ -623,7 +586,6 @@ impl CucTime {
Ok(()) Ok(())
} }
#[inline]
fn verify_fractions_value(val: FractionalPart) -> Result<(), CucError> { fn verify_fractions_value(val: FractionalPart) -> Result<(), CucError> {
if val.counter > 2u32.pow((val.resolution as u32) * 8) - 1 { if val.counter > 2u32.pow((val.resolution as u32) * 8) - 1 {
return Err(CucError::InvalidFractions { return Err(CucError::InvalidFractions {
@ -634,12 +596,10 @@ impl CucTime {
Ok(()) Ok(())
} }
#[inline]
fn len_as_bytes(&self) -> usize { fn len_as_bytes(&self) -> usize {
Self::len_packed_from_pfield(self.pfield) Self::len_packed_from_pfield(self.pfield)
} }
#[inline]
fn subsec_nanos(&self) -> u32 { fn subsec_nanos(&self) -> u32 {
if self.fractions.resolution() == FractionalResolution::Seconds { if self.fractions.resolution() == FractionalResolution::Seconds {
return 0; return 0;
@ -780,27 +740,22 @@ impl TimeWriter for CucTime {
} }
impl CcsdsTimeProvider for CucTimeWithLeapSecs { impl CcsdsTimeProvider for CucTimeWithLeapSecs {
#[inline]
fn len_as_bytes(&self) -> usize { fn len_as_bytes(&self) -> usize {
self.time.len_as_bytes() self.time.len_as_bytes()
} }
#[inline]
fn p_field(&self) -> (usize, [u8; 2]) { fn p_field(&self) -> (usize, [u8; 2]) {
(1, [self.time.pfield, 0]) (1, [self.time.pfield, 0])
} }
#[inline]
fn ccdsd_time_code(&self) -> CcsdsTimeCode { fn ccdsd_time_code(&self) -> CcsdsTimeCode {
self.time.ccsds_time_code() self.time.ccsds_time_code()
} }
#[inline]
fn unix_secs(&self) -> i64 { fn unix_secs(&self) -> i64 {
self.time.unix_secs(self.leap_seconds) self.time.unix_secs(self.leap_seconds)
} }
#[inline]
fn subsec_nanos(&self) -> u32 { fn subsec_nanos(&self) -> u32 {
self.time.subsec_nanos() self.time.subsec_nanos()
} }
@ -901,7 +856,7 @@ mod tests {
// Do not include leap second corrections, which do not apply to dates before 1972. // Do not include leap second corrections, which do not apply to dates before 1972.
let zero_cuc = CucTime::new(0); let zero_cuc = CucTime::new(0);
assert_eq!(zero_cuc.len_as_bytes(), 5); assert_eq!(zero_cuc.len_as_bytes(), 5);
assert_eq!(zero_cuc.counter_width(), zero_cuc.width_counter_pair().0); assert_eq!(zero_cuc.width(), zero_cuc.width_counter_pair().0);
assert_eq!(zero_cuc.counter(), zero_cuc.width_counter_pair().1); assert_eq!(zero_cuc.counter(), zero_cuc.width_counter_pair().1);
let ccsds_cuc = zero_cuc.to_leap_sec_helper(0); let ccsds_cuc = zero_cuc.to_leap_sec_helper(0);
assert_eq!(ccsds_cuc.ccdsd_time_code(), CcsdsTimeCode::CucCcsdsEpoch); assert_eq!(ccsds_cuc.ccdsd_time_code(), CcsdsTimeCode::CucCcsdsEpoch);
@ -949,7 +904,7 @@ mod tests {
#[test] #[test]
fn test_datetime_now() { fn test_datetime_now() {
let now = chrono::Utc::now(); let now = chrono::Utc::now();
let cuc_now = CucTime::now(FractionalResolution::SixtyNs, LEAP_SECONDS); let cuc_now = CucTime::from_now(FractionalResolution::SixtyNs, LEAP_SECONDS);
assert!(cuc_now.is_ok()); assert!(cuc_now.is_ok());
let cuc_now = cuc_now.unwrap(); let cuc_now = cuc_now.unwrap();
let ccsds_cuc = cuc_now.to_leap_sec_helper(LEAP_SECONDS); let ccsds_cuc = cuc_now.to_leap_sec_helper(LEAP_SECONDS);
@ -1287,7 +1242,6 @@ mod tests {
); );
assert_eq!(stamp.fractions().counter(), 0); assert_eq!(stamp.fractions().counter(), 0);
let res = stamp.update_from_now(LEAP_SECONDS); let res = stamp.update_from_now(LEAP_SECONDS);
assert!(res.is_ok()); assert!(res.is_ok());
} }
@ -1416,11 +1370,19 @@ mod tests {
assert_eq!(cuc.counter(), dt.timestamp() as u32 + LEAP_SECONDS); assert_eq!(cuc.counter(), dt.timestamp() as u32 + LEAP_SECONDS);
} }
#[test]
fn test_new_u16_width() {
let cuc = CucTime::new_u16_counter(0);
assert_eq!(cuc.width(), 2);
assert_eq!(cuc.counter(), 0);
}
#[test] #[test]
fn from_unix_stamp() { fn from_unix_stamp() {
let unix_stamp = UnixTime::new(0, 0); let unix_stamp = UnixTime::new(0, 0);
let cuc = CucTime::from_unix_time(&unix_stamp, FractionalResolution::Seconds, LEAP_SECONDS) let cuc =
.expect("failed to create cuc from unix stamp"); CucTime::from_unix_stamp(&unix_stamp, FractionalResolution::Seconds, LEAP_SECONDS)
.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 + LEAP_SECONDS (-DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as u32 + LEAP_SECONDS
@ -1443,7 +1405,7 @@ mod tests {
#[test] #[test]
fn test_stamp_to_vec() { fn test_stamp_to_vec() {
let stamp = CucTime::new(100); let stamp = CucTime::new_u16_counter(100);
let stamp_vec = stamp.to_vec().unwrap(); let stamp_vec = stamp.to_vec().unwrap();
let mut buf: [u8; 16] = [0; 16]; let mut buf: [u8; 16] = [0; 16];
stamp.write_to_bytes(&mut buf).unwrap(); stamp.write_to_bytes(&mut buf).unwrap();

View File

@ -33,7 +33,6 @@ pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CcsdsTimeCode { pub enum CcsdsTimeCode {
CucCcsdsEpoch = 0b001, CucCcsdsEpoch = 0b001,
CucAgencyEpoch = 0b010, CucAgencyEpoch = 0b010,
@ -66,27 +65,13 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result<CcsdsTimeCode, u8> {
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DateBeforeCcsdsEpochError(UnixTime);
impl Display for DateBeforeCcsdsEpochError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "date before ccsds epoch: {:?}", self.0)
}
}
#[cfg(feature = "std")]
impl Error for DateBeforeCcsdsEpochError {}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive] #[non_exhaustive]
pub enum TimestampError { pub enum TimestampError {
InvalidTimeCode { expected: CcsdsTimeCode, found: u8 }, InvalidTimeCode { expected: CcsdsTimeCode, found: u8 },
ByteConversion(ByteConversionError), ByteConversion(ByteConversionError),
Cds(cds::CdsError), Cds(cds::CdsError),
Cuc(cuc::CucError), Cuc(cuc::CucError),
DateBeforeCcsdsEpoch(UnixTime),
CustomEpochNotSupported, CustomEpochNotSupported,
} }
@ -108,6 +93,9 @@ impl Display for TimestampError {
TimestampError::ByteConversion(e) => { TimestampError::ByteConversion(e) => {
write!(f, "time stamp: {e}") write!(f, "time stamp: {e}")
} }
TimestampError::DateBeforeCcsdsEpoch(e) => {
write!(f, "datetime with date before ccsds epoch: {e:?}")
}
TimestampError::CustomEpochNotSupported => { TimestampError::CustomEpochNotSupported => {
write!(f, "custom epochs are not supported") write!(f, "custom epochs are not supported")
} }
@ -126,7 +114,6 @@ impl Error for TimestampError {
} }
} }
} }
impl From<cds::CdsError> for TimestampError { impl From<cds::CdsError> for TimestampError {
fn from(e: cds::CdsError) -> Self { fn from(e: cds::CdsError) -> Self {
TimestampError::Cds(e) TimestampError::Cds(e)
@ -140,6 +127,7 @@ impl From<cuc::CucError> for TimestampError {
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std_mod { pub mod std_mod {
use crate::time::TimestampError; use crate::time::TimestampError;
use std::time::SystemTimeError; use std::time::SystemTimeError;
@ -147,7 +135,7 @@ pub mod std_mod {
#[derive(Debug, Clone, Error)] #[derive(Debug, Clone, Error)]
pub enum StdTimestampError { pub enum StdTimestampError {
#[error("system time error: {0:?}")] #[error("system time error: {0}")]
SystemTime(#[from] SystemTimeError), SystemTime(#[from] SystemTimeError),
#[error("timestamp error: {0}")] #[error("timestamp error: {0}")]
Timestamp(#[from] TimestampError), Timestamp(#[from] TimestampError),
@ -155,6 +143,7 @@ pub mod std_mod {
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn seconds_since_epoch() -> f64 { pub fn seconds_since_epoch() -> f64 {
SystemTime::now() SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH) .duration_since(SystemTime::UNIX_EPOCH)
@ -166,7 +155,6 @@ pub fn seconds_since_epoch() -> f64 {
/// ///
/// - CCSDS epoch: 1958-01-01T00:00:00+00:00 /// - CCSDS epoch: 1958-01-01T00:00:00+00:00
/// - UNIX Epoch: 1970-01-01T00:00:00+00:00 /// - UNIX Epoch: 1970-01-01T00:00:00+00:00
#[inline]
pub const fn unix_to_ccsds_days(unix_days: i64) -> i64 { pub const fn unix_to_ccsds_days(unix_days: i64) -> i64 {
unix_days - DAYS_CCSDS_TO_UNIX as i64 unix_days - DAYS_CCSDS_TO_UNIX as i64
} }
@ -175,24 +163,22 @@ pub const fn unix_to_ccsds_days(unix_days: i64) -> i64 {
/// ///
/// - CCSDS epoch: 1958-01-01T00:00:00+00:00 /// - CCSDS epoch: 1958-01-01T00:00:00+00:00
/// - UNIX Epoch: 1970-01-01T00:00:00+00:00 /// - UNIX Epoch: 1970-01-01T00:00:00+00:00
#[inline]
pub const fn ccsds_to_unix_days(ccsds_days: i64) -> i64 { pub const fn ccsds_to_unix_days(ccsds_days: i64) -> i64 {
ccsds_days + DAYS_CCSDS_TO_UNIX as i64 ccsds_days + DAYS_CCSDS_TO_UNIX as i64
} }
/// Similar to [unix_to_ccsds_days] but converts the epoch instead, which is the number of elpased /// Similar to [unix_to_ccsds_days] but converts the epoch instead, which is the number of elpased
/// seconds since the CCSDS and UNIX epoch times. /// seconds since the CCSDS and UNIX epoch times.
#[inline]
pub const fn unix_epoch_to_ccsds_epoch(unix_epoch: i64) -> i64 { pub const fn unix_epoch_to_ccsds_epoch(unix_epoch: i64) -> i64 {
unix_epoch - (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64) unix_epoch - (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
} }
#[inline]
pub const fn ccsds_epoch_to_unix_epoch(ccsds_epoch: i64) -> i64 { pub const fn ccsds_epoch_to_unix_epoch(ccsds_epoch: i64) -> i64 {
ccsds_epoch + (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64) ccsds_epoch + (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn ms_of_day_using_sysclock() -> u32 { pub fn ms_of_day_using_sysclock() -> u32 {
ms_of_day(seconds_since_epoch()) ms_of_day(seconds_since_epoch())
} }
@ -213,6 +199,7 @@ pub trait TimeWriter {
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError>; fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError>;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn to_vec(&self) -> Result<alloc::vec::Vec<u8>, TimestampError> { fn to_vec(&self) -> Result<alloc::vec::Vec<u8>, TimestampError> {
let mut vec = alloc::vec![0; self.len_written()]; let mut vec = alloc::vec![0; self.len_written()];
self.write_to_bytes(&mut vec)?; self.write_to_bytes(&mut vec)?;
@ -251,11 +238,13 @@ pub trait CcsdsTimeProvider {
} }
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> { fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
chrono::Utc.timestamp_opt(self.unix_secs(), self.subsec_nanos()) chrono::Utc.timestamp_opt(self.unix_secs(), self.subsec_nanos())
} }
#[cfg(feature = "timelib")] #[cfg(feature = "timelib")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> { fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
Ok(time::OffsetDateTime::from_unix_timestamp(self.unix_secs())? Ok(time::OffsetDateTime::from_unix_timestamp(self.unix_secs())?
+ time::Duration::nanoseconds(self.subsec_nanos().into())) + time::Duration::nanoseconds(self.subsec_nanos().into()))
@ -269,7 +258,6 @@ pub trait CcsdsTimeProvider {
/// similarly to other common time formats and libraries. /// similarly to other common time formats and libraries.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UnixTime { pub struct UnixTime {
secs: i64, secs: i64,
subsec_nanos: u32, subsec_nanos: u32,
@ -353,7 +341,8 @@ impl UnixTime {
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn now() -> Result<Self, SystemTimeError> { #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn from_now() -> Result<Self, SystemTimeError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let epoch = now.as_secs(); let epoch = now.as_secs();
Ok(Self::new(epoch as i64, now.subsec_nanos())) Ok(Self::new(epoch as i64, now.subsec_nanos()))
@ -364,18 +353,20 @@ impl UnixTime {
self.secs as f64 + (self.subsec_nanos as f64 / 1_000_000_000.0) self.secs as f64 + (self.subsec_nanos as f64 / 1_000_000_000.0)
} }
pub fn as_secs(&self) -> i64 { pub fn secs(&self) -> i64 {
self.secs self.secs
} }
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
pub fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> { pub fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
Utc.timestamp_opt(self.secs, self.subsec_nanos) Utc.timestamp_opt(self.secs, self.subsec_nanos)
} }
#[cfg(feature = "timelib")] #[cfg(feature = "timelib")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
pub fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> { pub fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
Ok(time::OffsetDateTime::from_unix_timestamp(self.as_secs())? Ok(time::OffsetDateTime::from_unix_timestamp(self.secs())?
+ time::Duration::nanoseconds(self.subsec_nanos().into())) + time::Duration::nanoseconds(self.subsec_nanos().into()))
} }
@ -394,6 +385,7 @@ impl UnixTime {
} }
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
impl From<chrono::DateTime<chrono::Utc>> for UnixTime { impl From<chrono::DateTime<chrono::Utc>> for UnixTime {
fn from(value: chrono::DateTime<chrono::Utc>) -> Self { fn from(value: chrono::DateTime<chrono::Utc>) -> Self {
Self::new(value.timestamp(), value.timestamp_subsec_nanos()) Self::new(value.timestamp(), value.timestamp_subsec_nanos())
@ -401,6 +393,7 @@ impl From<chrono::DateTime<chrono::Utc>> for UnixTime {
} }
#[cfg(feature = "timelib")] #[cfg(feature = "timelib")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
impl From<time::OffsetDateTime> for UnixTime { impl From<time::OffsetDateTime> for UnixTime {
fn from(value: time::OffsetDateTime) -> Self { fn from(value: time::OffsetDateTime) -> Self {
Self::new(value.unix_timestamp(), value.nanosecond()) Self::new(value.unix_timestamp(), value.nanosecond())
@ -657,7 +650,7 @@ mod tests {
fn test_addition() { fn test_addition() {
let mut stamp0 = UnixTime::new_only_secs(1); let mut stamp0 = UnixTime::new_only_secs(1);
stamp0 += Duration::from_secs(5); stamp0 += Duration::from_secs(5);
assert_eq!(stamp0.as_secs(), 6); assert_eq!(stamp0.secs(), 6);
assert_eq!(stamp0.subsec_millis(), 0); assert_eq!(stamp0.subsec_millis(), 0);
let stamp1 = stamp0 + Duration::from_millis(500); let stamp1 = stamp0 + Duration::from_millis(500);
assert_eq!(stamp1.secs, 6); assert_eq!(stamp1.secs, 6);
@ -686,7 +679,7 @@ mod tests {
#[test] #[test]
fn test_from_now() { fn test_from_now() {
let stamp_now = UnixTime::now().unwrap(); let stamp_now = UnixTime::from_now().unwrap();
let dt_now = stamp_now.chrono_date_time().unwrap(); let dt_now = stamp_now.chrono_date_time().unwrap();
assert!(dt_now.year() >= 2020); assert!(dt_now.year() >= 2020);
} }

View File

@ -15,12 +15,10 @@ pub trait ToBeBytes {
impl ToBeBytes for () { impl ToBeBytes for () {
type ByteArray = [u8; 0]; type ByteArray = [u8; 0];
#[inline]
fn written_len(&self) -> usize { fn written_len(&self) -> usize {
0 0
} }
#[inline]
fn to_be_bytes(&self) -> Self::ByteArray { fn to_be_bytes(&self) -> Self::ByteArray {
[] []
} }
@ -29,12 +27,9 @@ impl ToBeBytes for () {
impl ToBeBytes for u8 { impl ToBeBytes for u8 {
type ByteArray = [u8; 1]; type ByteArray = [u8; 1];
#[inline]
fn written_len(&self) -> usize { fn written_len(&self) -> usize {
1 1
} }
#[inline]
fn to_be_bytes(&self) -> Self::ByteArray { fn to_be_bytes(&self) -> Self::ByteArray {
u8::to_be_bytes(*self) u8::to_be_bytes(*self)
} }
@ -43,12 +38,9 @@ impl ToBeBytes for u8 {
impl ToBeBytes for u16 { impl ToBeBytes for u16 {
type ByteArray = [u8; 2]; type ByteArray = [u8; 2];
#[inline]
fn written_len(&self) -> usize { fn written_len(&self) -> usize {
2 2
} }
#[inline]
fn to_be_bytes(&self) -> Self::ByteArray { fn to_be_bytes(&self) -> Self::ByteArray {
u16::to_be_bytes(*self) u16::to_be_bytes(*self)
} }
@ -57,12 +49,9 @@ impl ToBeBytes for u16 {
impl ToBeBytes for u32 { impl ToBeBytes for u32 {
type ByteArray = [u8; 4]; type ByteArray = [u8; 4];
#[inline]
fn written_len(&self) -> usize { fn written_len(&self) -> usize {
4 4
} }
#[inline]
fn to_be_bytes(&self) -> Self::ByteArray { fn to_be_bytes(&self) -> Self::ByteArray {
u32::to_be_bytes(*self) u32::to_be_bytes(*self)
} }
@ -71,12 +60,9 @@ impl ToBeBytes for u32 {
impl ToBeBytes for u64 { impl ToBeBytes for u64 {
type ByteArray = [u8; 8]; type ByteArray = [u8; 8];
#[inline]
fn written_len(&self) -> usize { fn written_len(&self) -> usize {
8 8
} }
#[inline]
fn to_be_bytes(&self) -> Self::ByteArray { fn to_be_bytes(&self) -> Self::ByteArray {
u64::to_be_bytes(*self) u64::to_be_bytes(*self)
} }
@ -91,6 +77,7 @@ pub trait UnsignedEnum {
fn value(&self) -> u64; fn value(&self) -> u64;
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
fn to_vec(&self) -> alloc::vec::Vec<u8> { fn to_vec(&self) -> alloc::vec::Vec<u8> {
let mut buf = alloc::vec![0; self.size()]; let mut buf = alloc::vec![0; self.size()];
self.write_to_be_bytes(&mut buf).unwrap(); self.write_to_be_bytes(&mut buf).unwrap();
@ -118,7 +105,6 @@ pub enum UnsignedByteFieldError {
} }
impl From<ByteConversionError> for UnsignedByteFieldError { impl From<ByteConversionError> for UnsignedByteFieldError {
#[inline]
fn from(value: ByteConversionError) -> Self { fn from(value: ByteConversionError) -> Self {
Self::ByteConversionError(value) Self::ByteConversionError(value)
} }
@ -146,24 +132,20 @@ impl Error for UnsignedByteFieldError {}
/// Type erased variant. /// Type erased variant.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct UnsignedByteField { pub struct UnsignedByteField {
width: usize, width: usize,
value: u64, value: u64,
} }
impl UnsignedByteField { impl UnsignedByteField {
#[inline]
pub const fn new(width: usize, value: u64) -> Self { pub const fn new(width: usize, value: u64) -> Self {
Self { width, value } Self { width, value }
} }
#[inline]
pub const fn value_const(&self) -> u64 { pub const fn value_const(&self) -> u64 {
self.value self.value
} }
#[inline]
pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result<Self, UnsignedByteFieldError> { pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result<Self, UnsignedByteFieldError> {
if width > buf.len() { if width > buf.len() {
return Err(ByteConversionError::FromSliceTooSmall { return Err(ByteConversionError::FromSliceTooSmall {
@ -196,12 +178,10 @@ impl UnsignedByteField {
} }
impl UnsignedEnum for UnsignedByteField { impl UnsignedEnum for UnsignedByteField {
#[inline]
fn size(&self) -> usize { fn size(&self) -> usize {
self.width self.width
} }
#[inline]
fn value(&self) -> u64 { fn value(&self) -> u64 {
self.value_const() self.value_const()
} }
@ -241,7 +221,6 @@ impl UnsignedEnum for UnsignedByteField {
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GenericUnsignedByteField<TYPE: Copy + Into<u64>> { pub struct GenericUnsignedByteField<TYPE: Copy + Into<u64>> {
value: TYPE, value: TYPE,
} }
@ -257,7 +236,6 @@ impl<TYPE: Copy + Into<u64>> GenericUnsignedByteField<TYPE> {
} }
impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericUnsignedByteField<TYPE> { impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericUnsignedByteField<TYPE> {
#[inline]
fn size(&self) -> usize { fn size(&self) -> usize {
self.value.written_len() self.value.written_len()
} }
@ -273,7 +251,6 @@ impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericUnsignedByteFie
Ok(self.value.written_len()) Ok(self.value.written_len())
} }
#[inline]
fn value(&self) -> u64 { fn value(&self) -> u64 {
self.value_typed().into() self.value_typed().into()
} }
@ -299,7 +276,6 @@ impl From<UnsignedByteFieldU8> for UnsignedByteField {
impl TryFrom<UnsignedByteField> for UnsignedByteFieldU8 { impl TryFrom<UnsignedByteField> for UnsignedByteFieldU8 {
type Error = UnsignedByteFieldError; type Error = UnsignedByteFieldError;
#[inline]
fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> { fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> {
if value.width != 1 { if value.width != 1 {
return Err(UnsignedByteFieldError::InvalidWidth { return Err(UnsignedByteFieldError::InvalidWidth {
@ -312,7 +288,6 @@ impl TryFrom<UnsignedByteField> for UnsignedByteFieldU8 {
} }
impl From<UnsignedByteFieldU16> for UnsignedByteField { impl From<UnsignedByteFieldU16> for UnsignedByteField {
#[inline]
fn from(value: UnsignedByteFieldU16) -> Self { fn from(value: UnsignedByteFieldU16) -> Self {
Self::new(2, value.value as u64) Self::new(2, value.value as u64)
} }
@ -321,7 +296,6 @@ impl From<UnsignedByteFieldU16> for UnsignedByteField {
impl TryFrom<UnsignedByteField> for UnsignedByteFieldU16 { impl TryFrom<UnsignedByteField> for UnsignedByteFieldU16 {
type Error = UnsignedByteFieldError; type Error = UnsignedByteFieldError;
#[inline]
fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> { fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> {
if value.width != 2 { if value.width != 2 {
return Err(UnsignedByteFieldError::InvalidWidth { return Err(UnsignedByteFieldError::InvalidWidth {
@ -334,7 +308,6 @@ impl TryFrom<UnsignedByteField> for UnsignedByteFieldU16 {
} }
impl From<UnsignedByteFieldU32> for UnsignedByteField { impl From<UnsignedByteFieldU32> for UnsignedByteField {
#[inline]
fn from(value: UnsignedByteFieldU32) -> Self { fn from(value: UnsignedByteFieldU32) -> Self {
Self::new(4, value.value as u64) Self::new(4, value.value as u64)
} }
@ -343,7 +316,6 @@ impl From<UnsignedByteFieldU32> for UnsignedByteField {
impl TryFrom<UnsignedByteField> for UnsignedByteFieldU32 { impl TryFrom<UnsignedByteField> for UnsignedByteFieldU32 {
type Error = UnsignedByteFieldError; type Error = UnsignedByteFieldError;
#[inline]
fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> { fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> {
if value.width != 4 { if value.width != 4 {
return Err(UnsignedByteFieldError::InvalidWidth { return Err(UnsignedByteFieldError::InvalidWidth {
@ -356,7 +328,6 @@ impl TryFrom<UnsignedByteField> for UnsignedByteFieldU32 {
} }
impl From<UnsignedByteFieldU64> for UnsignedByteField { impl From<UnsignedByteFieldU64> for UnsignedByteField {
#[inline]
fn from(value: UnsignedByteFieldU64) -> Self { fn from(value: UnsignedByteFieldU64) -> Self {
Self::new(8, value.value) Self::new(8, value.value)
} }
@ -365,7 +336,6 @@ impl From<UnsignedByteFieldU64> for UnsignedByteField {
impl TryFrom<UnsignedByteField> for UnsignedByteFieldU64 { impl TryFrom<UnsignedByteField> for UnsignedByteFieldU64 {
type Error = UnsignedByteFieldError; type Error = UnsignedByteFieldError;
#[inline]
fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> { fn try_from(value: UnsignedByteField) -> Result<Self, Self::Error> {
if value.width != 8 { if value.width != 8 {
return Err(UnsignedByteFieldError::InvalidWidth { return Err(UnsignedByteFieldError::InvalidWidth {