Compare commits
37 Commits
9b091e3a3a
...
v0.4.2
Author | SHA1 | Date | |
---|---|---|---|
8fddaefab1 | |||
6e593e4e27 | |||
ef5a4e2924 | |||
aa5c206c1c | |||
1c702f933f | |||
6989558f93 | |||
33e9b40c39 | |||
008359ec71 | |||
2385e7812b | |||
76ea418711 | |||
b68f1c3752 | |||
7c9bdb4512 | |||
8ffa7efa4d | |||
d972dd5223 | |||
481de83fdb | |||
09b305f529 | |||
78c5787e07 | |||
e9e33b0335 | |||
455be77f4a | |||
c748657499 | |||
2e90cba5bd | |||
f290d2a54e | |||
da695e4705 | |||
5c222735d4 | |||
1432c298b3 | |||
1b45082ace | |||
4c20158dcc | |||
c879181093 | |||
6c88e94742 | |||
3fb2fbd20c | |||
ec8a2e1d24 | |||
192e2f2c76 | |||
5df221759f | |||
f137bd2549 | |||
630bffec51 | |||
a14ae37cac | |||
2758699601 |
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@@ -20,6 +20,21 @@ jobs:
|
|||||||
command: check
|
command: check
|
||||||
args: --release
|
args: --release
|
||||||
|
|
||||||
|
msrv:
|
||||||
|
name: Check with MSRV
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.60.0
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --release
|
||||||
|
|
||||||
cross-check:
|
cross-check:
|
||||||
name: Check Cross
|
name: Check Cross
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -58,6 +73,21 @@ jobs:
|
|||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
|
||||||
|
check-doc:
|
||||||
|
name: Check Documentation Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: doc
|
||||||
|
args: --all-features
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
name: Clippy
|
name: Clippy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
24
CHANGELOG.md
24
CHANGELOG.md
@@ -8,6 +8,25 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
# [v0.4.2] 14.01.2023
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- CDS timestamp: Fixed another small logic error for stamp creation from the current
|
||||||
|
time with picosecond precision.
|
||||||
|
PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/8
|
||||||
|
|
||||||
|
# [v0.4.1] 14.01.2023
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- CDS timestamp: The conversion function from the current time were buggy
|
||||||
|
when specifying picoseconds precision, which could lead to overflow
|
||||||
|
multiplications and/or incorrect precision fields.
|
||||||
|
PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/7
|
||||||
|
|
||||||
|
# [v0.4.0] 10.01.2023
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- Remove `Default` derive on CDS time provider. This can lead to uninitialized preamble fields.
|
- Remove `Default` derive on CDS time provider. This can lead to uninitialized preamble fields.
|
||||||
@@ -27,13 +46,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
CUC PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/4/files
|
- `SpHeader` getter function `sp_header` added for `PusTc`
|
||||||
|
PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/6
|
||||||
- Added PFC enumerations: `ecss::UnsignedPfc` and `ecss::RealPfc`.
|
- Added PFC enumerations: `ecss::UnsignedPfc` and `ecss::RealPfc`.
|
||||||
PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/5
|
PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/5
|
||||||
- Added `std::error::Error` implementation for all error enumerations if the `std` feature
|
- Added `std::error::Error` implementation for all error enumerations if the `std` feature
|
||||||
is enabled.
|
is enabled.
|
||||||
- CUC timestamp implementation as specified in CCSDS 301.0-B-4 section 3.2.
|
- CUC timestamp implementation as specified in CCSDS 301.0-B-4 section 3.2.
|
||||||
|
PR: https://egit.irs.uni-stuttgart.de/rust/spacepackets/pulls/4/files
|
||||||
- ACII timestamps as specified in CCSDS 301.0-B-4 section 3.5.
|
- ACII timestamps as specified in CCSDS 301.0-B-4 section 3.5.
|
||||||
- Added MSRV in `Cargo.toml` with the `rust-version` field set to Rust 1.60.
|
- Added MSRV in `Cargo.toml` with the `rust-version` field set to Rust 1.60.
|
||||||
- `serde` `Serialize` and `Deserialize` added to all types.
|
- `serde` `Serialize` and `Deserialize` added to all types.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "spacepackets"
|
name = "spacepackets"
|
||||||
version = "0.3.1"
|
version = "0.4.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
@@ -14,11 +14,11 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zerocopy = "0.6"
|
zerocopy = "0.6"
|
||||||
crc = "3.0"
|
crc = "3"
|
||||||
delegate = "0.8"
|
delegate = "0.8"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0"
|
version = "1"
|
||||||
optional = true
|
optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
@@ -15,7 +15,9 @@ Currently, this includes the following components:
|
|||||||
[CCSDS Blue Book 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf)
|
[CCSDS Blue Book 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf)
|
||||||
- PUS Telecommand and PUS Telemetry implementation according to the
|
- PUS Telecommand and PUS Telemetry implementation according to the
|
||||||
[ECSS-E-ST-70-41C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
[ECSS-E-ST-70-41C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
||||||
- CDS Short Time Code implementation according to
|
- CUC (CCSDS Unsegmented Time Code) implementation according to
|
||||||
|
[CCSDS 301.0-B-4 3.2](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
|
- CDS (CCSDS Day Segmented Time Code) implementation according to
|
||||||
[CCSDS 301.0-B-4 3.3](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
[CCSDS 301.0-B-4 3.3](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
- Some helper types to support ASCII timecodes ad specified in
|
- Some helper types to support ASCII timecodes ad specified in
|
||||||
[CCSDS 301.0-B-4 3.5](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
[CCSDS 301.0-B-4 3.5](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
|
@@ -10,6 +10,6 @@ ARG DEBIAN_FRONTEND=noninteractive
|
|||||||
# set CROSS_CONTAINER_IN_CONTAINER to inform `cross` that it is executed from within a container
|
# set CROSS_CONTAINER_IN_CONTAINER to inform `cross` that it is executed from within a container
|
||||||
ENV CROSS_CONTAINER_IN_CONTAINER=true
|
ENV CROSS_CONTAINER_IN_CONTAINER=true
|
||||||
|
|
||||||
# TODO: installing cross is problematic, permission issues
|
RUN rustup install nightly && \
|
||||||
RUN 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
|
rustup component add rustfmt clippy
|
||||||
|
18
automation/Jenkinsfile
vendored
18
automation/Jenkinsfile
vendored
@@ -13,19 +13,29 @@ pipeline {
|
|||||||
sh 'cargo clippy'
|
sh 'cargo clippy'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo +nightly doc --all-features'
|
||||||
|
}
|
||||||
|
}
|
||||||
stage('Rustfmt') {
|
stage('Rustfmt') {
|
||||||
steps {
|
steps {
|
||||||
sh 'cargo fmt'
|
sh 'cargo fmt --all --check'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Test') {
|
stage('Test') {
|
||||||
steps {
|
steps {
|
||||||
sh 'cargo test'
|
sh 'cargo test --all-features'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Check') {
|
stage('Check with all features') {
|
||||||
steps {
|
steps {
|
||||||
sh 'cargo check'
|
sh 'cargo check --all-features'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check with no features') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --no-default-features'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Check Cross Embedded Bare Metal') {
|
stage('Check Cross Embedded Bare Metal') {
|
||||||
|
10
src/ecss.rs
10
src/ecss.rs
@@ -18,10 +18,15 @@ pub const CCSDS_HEADER_LEN: usize = size_of::<crate::zc::SpHeader>();
|
|||||||
#[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))]
|
||||||
pub enum PusServiceId {
|
pub enum PusServiceId {
|
||||||
|
/// Service 1
|
||||||
Verification = 1,
|
Verification = 1,
|
||||||
|
/// Service 3
|
||||||
Housekeeping = 3,
|
Housekeeping = 3,
|
||||||
|
/// Service 5
|
||||||
Event = 5,
|
Event = 5,
|
||||||
|
/// Service 8
|
||||||
Action = 8,
|
Action = 8,
|
||||||
|
/// Service 17
|
||||||
Test = 17,
|
Test = 17,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +53,7 @@ impl TryFrom<u8> for PusVersion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ECSS Packet Type Codes (PTC)s.
|
||||||
#[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))]
|
||||||
pub enum PacketTypeCodes {
|
pub enum PacketTypeCodes {
|
||||||
@@ -67,6 +73,7 @@ pub enum PacketTypeCodes {
|
|||||||
|
|
||||||
pub type Ptc = PacketTypeCodes;
|
pub type Ptc = PacketTypeCodes;
|
||||||
|
|
||||||
|
/// ECSS Packet Field Codes (PFC)s for the unsigned [Ptc].
|
||||||
#[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))]
|
||||||
pub enum UnsignedPfc {
|
pub enum UnsignedPfc {
|
||||||
@@ -82,6 +89,7 @@ pub enum UnsignedPfc {
|
|||||||
ThreeBits = 19,
|
ThreeBits = 19,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ECSS Packet Field Codes (PFC)s for the real (floating point) [Ptc].
|
||||||
#[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))]
|
||||||
pub enum RealPfc {
|
pub enum RealPfc {
|
||||||
@@ -152,6 +160,8 @@ impl From<ByteConversionError> for PusError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry
|
||||||
|
/// (TM) packets. All PUS packets are also a special type of [CcsdsPacket]s.
|
||||||
pub trait PusPacket: CcsdsPacket {
|
pub trait PusPacket: CcsdsPacket {
|
||||||
const PUS_VERSION: PusVersion = PusVersion::PusC;
|
const PUS_VERSION: PusVersion = PusVersion::PusC;
|
||||||
|
|
||||||
|
13
src/lib.rs
13
src/lib.rs
@@ -9,9 +9,11 @@
|
|||||||
//! [CCSDS Blue Book 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf)
|
//! [CCSDS Blue Book 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf)
|
||||||
//! - PUS Telecommand and PUS Telemetry implementation according to the
|
//! - PUS Telecommand and PUS Telemetry implementation according to the
|
||||||
//! [ECSS-E-ST-70-41C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
//! [ECSS-E-ST-70-41C standard](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
||||||
//! - CDS Short Time Code implementation according to
|
//! - CUC (CCSDS Unsegmented Time Code) implementation according to
|
||||||
//! [CCSDS CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
//! [CCSDS 301.0-B-4 3.2](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
//! - Some helper types and functions to support ASCII timecodes ad specified in
|
//! - CDS (CCSDS Day Segmented Time Code) implementation according to
|
||||||
|
//! [CCSDS 301.0-B-4 3.3](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
|
//! - Some helper types to support ASCII timecodes ad specified in
|
||||||
//! [CCSDS 301.0-B-4 3.5](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
//! [CCSDS 301.0-B-4 3.5](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
//!
|
//!
|
||||||
//! ## Features
|
//! ## Features
|
||||||
@@ -127,6 +129,7 @@ impl Display for ByteConversionError {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Error for ByteConversionError {}
|
impl Error for ByteConversionError {}
|
||||||
|
|
||||||
|
/// 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))]
|
||||||
pub enum PacketType {
|
pub enum PacketType {
|
||||||
@@ -175,6 +178,8 @@ impl TryFrom<u8> for SequenceFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Abstraction for the CCSDS Packet ID, which forms the last thirteen bits
|
||||||
|
/// of the first two bytes 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))]
|
||||||
pub struct PacketId {
|
pub struct PacketId {
|
||||||
@@ -258,6 +263,8 @@ impl From<u16> for PacketId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Abstraction for the CCSDS Packet Sequence Control (PSC) field which is the
|
||||||
|
/// 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))]
|
||||||
pub struct PacketSequenceCtrl {
|
pub struct PacketSequenceCtrl {
|
||||||
|
20
src/tc.rs
20
src/tc.rs
@@ -215,19 +215,19 @@ impl PusTcSecondaryHeader {
|
|||||||
/// There is no spare bytes support yet.
|
/// There is no spare bytes support yet.
|
||||||
#[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))]
|
||||||
pub struct PusTc<'slice> {
|
pub struct PusTc<'app_data> {
|
||||||
sp_header: SpHeader,
|
sp_header: SpHeader,
|
||||||
pub sec_header: PusTcSecondaryHeader,
|
pub sec_header: PusTcSecondaryHeader,
|
||||||
/// If this is set to false, a manual call to [PusTc::calc_own_crc16] or
|
/// 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.
|
/// [PusTc::update_packet_fields] is necessary for the serialized or cached CRC16 to be valid.
|
||||||
pub calc_crc_on_serialization: bool,
|
pub calc_crc_on_serialization: bool,
|
||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
raw_data: Option<&'slice [u8]>,
|
raw_data: Option<&'app_data [u8]>,
|
||||||
app_data: Option<&'slice [u8]>,
|
app_data: Option<&'app_data [u8]>,
|
||||||
crc16: Option<u16>,
|
crc16: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'slice> PusTc<'slice> {
|
impl<'app_data> PusTc<'app_data> {
|
||||||
/// Generates a new struct instance.
|
/// Generates a new struct instance.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -243,7 +243,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
sp_header: &mut SpHeader,
|
sp_header: &mut SpHeader,
|
||||||
sec_header: PusTcSecondaryHeader,
|
sec_header: PusTcSecondaryHeader,
|
||||||
app_data: Option<&'slice [u8]>,
|
app_data: Option<&'app_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);
|
||||||
@@ -268,7 +268,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
sph: &mut SpHeader,
|
sph: &mut SpHeader,
|
||||||
service: u8,
|
service: u8,
|
||||||
subservice: u8,
|
subservice: u8,
|
||||||
app_data: Option<&'slice [u8]>,
|
app_data: Option<&'app_data [u8]>,
|
||||||
set_ccsds_len: bool,
|
set_ccsds_len: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
@@ -279,6 +279,10 @@ impl<'slice> PusTc<'slice> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sp_header(&self) -> &SpHeader {
|
||||||
|
&self.sp_header
|
||||||
|
}
|
||||||
|
|
||||||
pub fn len_packed(&self) -> usize {
|
pub fn len_packed(&self) -> usize {
|
||||||
let mut length = PUS_TC_MIN_LEN_WITHOUT_APP_DATA;
|
let mut length = PUS_TC_MIN_LEN_WITHOUT_APP_DATA;
|
||||||
if let Some(app_data) = self.app_data {
|
if let Some(app_data) = self.app_data {
|
||||||
@@ -401,7 +405,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
|
|
||||||
/// Create a [PusTc] instance from a raw slice. On success, it returns a tuple containing
|
/// 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.
|
/// the instance and the found byte length of the packet.
|
||||||
pub fn from_bytes(slice: &'slice [u8]) -> Result<(Self, usize), PusError> {
|
pub fn from_bytes(slice: &'app_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 {
|
||||||
return Err(PusError::RawDataTooShort(raw_data_len));
|
return Err(PusError::RawDataTooShort(raw_data_len));
|
||||||
@@ -431,7 +435,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
Ok((pus_tc, total_len))
|
Ok((pus_tc, total_len))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raw(&self) -> Option<&'slice [u8]> {
|
pub fn raw(&self) -> Option<&'app_data [u8]> {
|
||||||
self.raw_data
|
self.raw_data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,10 @@
|
|||||||
//! See [chrono::DateTime::format] for a usage example of the generated
|
//! See [chrono::DateTime::format] for a usage example of the generated
|
||||||
//! [chrono::format::DelayedFormat] structs.
|
//! [chrono::format::DelayedFormat] structs.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use chrono::format::{DelayedFormat, StrftimeItems};
|
use chrono::{
|
||||||
use chrono::{DateTime, Utc};
|
format::{DelayedFormat, StrftimeItems},
|
||||||
|
DateTime, Utc,
|
||||||
|
};
|
||||||
|
|
||||||
/// Tuple of format string and formatted size for time code A.
|
/// Tuple of format string and formatted size for time code A.
|
||||||
///
|
///
|
||||||
|
@@ -6,7 +6,8 @@ use super::*;
|
|||||||
use crate::private::Sealed;
|
use crate::private::Sealed;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
const CDS_SHORT_P_FIELD: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
/// Base value for the preamble field for a time field parser to determine the time field type.
|
||||||
|
pub const P_FIELD_BASE: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
||||||
pub const MIN_CDS_FIELD_LEN: usize = 7;
|
pub const MIN_CDS_FIELD_LEN: usize = 7;
|
||||||
|
|
||||||
/// Generic trait implemented by token structs to specify the length of day field at type
|
/// Generic trait implemented by token structs to specify the length of day field at type
|
||||||
@@ -169,8 +170,8 @@ impl ConversionFromNow {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
SubmillisPrecision::Picoseconds(_) => {
|
SubmillisPrecision::Picoseconds(_) => {
|
||||||
prec = Some(SubmillisPrecision::Microseconds(
|
prec = Some(SubmillisPrecision::Picoseconds(
|
||||||
(now.subsec_nanos() * 1000) as u16,
|
(now.subsec_nanos() % 10_u32.pow(6)) * 1000,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -391,7 +392,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
day_seg_len: LengthOfDaySegment,
|
day_seg_len: LengthOfDaySegment,
|
||||||
submillis_prec: Option<SubmillisPrecision>,
|
submillis_prec: Option<SubmillisPrecision>,
|
||||||
) -> u8 {
|
) -> u8 {
|
||||||
let mut pfield = CDS_SHORT_P_FIELD | ((day_seg_len as u8) << 2);
|
let mut pfield = P_FIELD_BASE | ((day_seg_len as u8) << 2);
|
||||||
if let Some(submillis_prec) = submillis_prec {
|
if let Some(submillis_prec) = submillis_prec {
|
||||||
match submillis_prec {
|
match submillis_prec {
|
||||||
SubmillisPrecision::Microseconds(_) => pfield |= 0b01,
|
SubmillisPrecision::Microseconds(_) => pfield |= 0b01,
|
||||||
|
@@ -6,6 +6,9 @@ use super::*;
|
|||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
const MIN_CUC_LEN: usize = 2;
|
const MIN_CUC_LEN: usize = 2;
|
||||||
|
|
||||||
|
/// Base value for the preamble field for a time field parser to determine the time field type.
|
||||||
|
pub const P_FIELD_BASE: u8 = (CcsdsTimeCodes::CucCcsdsEpoch as u8) << 4;
|
||||||
/// Maximum length if the preamble field is not extended.
|
/// Maximum length if the preamble field is not extended.
|
||||||
pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8;
|
pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8;
|
||||||
|
|
||||||
@@ -310,7 +313,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_p_field(counter_width: u8, fractions_width: Option<FractionalResolution>) -> u8 {
|
fn build_p_field(counter_width: u8, fractions_width: Option<FractionalResolution>) -> u8 {
|
||||||
let mut pfield = (CcsdsTimeCodes::CucCcsdsEpoch as u8) << 4;
|
let mut pfield = P_FIELD_BASE;
|
||||||
if !(1..=4).contains(&counter_width) {
|
if !(1..=4).contains(&counter_width) {
|
||||||
// Okay to panic here, this function is private and all input values should
|
// Okay to panic here, this function is private and all input values should
|
||||||
// have been sanitized
|
// have been sanitized
|
||||||
@@ -877,29 +880,39 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn fractional_part_formula() {
|
fn fractional_part_formula() {
|
||||||
let fractional_part =
|
let fractional_part =
|
||||||
7843137 / (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FourMs) as u64);
|
fractional_part_from_subsec_ns(FractionalResolution::FourMs, 7843138).unwrap();
|
||||||
assert_eq!(fractional_part, 2);
|
assert_eq!(fractional_part.1, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fractional_part_formula_2() {
|
fn fractional_part_formula_2() {
|
||||||
let fractional_part =
|
let fractional_part =
|
||||||
12000000 / (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FourMs) as u64);
|
fractional_part_from_subsec_ns(FractionalResolution::FourMs, 12000000).unwrap();
|
||||||
assert_eq!(fractional_part, 3);
|
assert_eq!(fractional_part.1, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fractional_part_formula_3() {
|
fn fractional_part_formula_3() {
|
||||||
let one_fraction_with_width_two_in_ns = 10_u64.pow(9) / (2_u32.pow(8 * 2) - 1) as u64;
|
let one_fraction_with_width_two_in_ns =
|
||||||
assert_eq!(one_fraction_with_width_two_in_ns, 15259);
|
10_u64.pow(9) as f64 / (2_u32.pow(8 * 2) - 1) as f64;
|
||||||
let hundred_fractions_and_some = 100 * one_fraction_with_width_two_in_ns + 7000;
|
assert_eq!(one_fraction_with_width_two_in_ns.ceil(), 15260.0);
|
||||||
let fractional_part = hundred_fractions_and_some
|
let hundred_fractions_and_some =
|
||||||
/ (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FifteenUs) as u64);
|
(100.0 * one_fraction_with_width_two_in_ns).floor() as u64 + 7000;
|
||||||
assert_eq!(fractional_part, 100);
|
let fractional_part = fractional_part_from_subsec_ns(
|
||||||
let hundred_and_one_fractions = 101 * one_fraction_with_width_two_in_ns;
|
FractionalResolution::FifteenUs,
|
||||||
let fractional_part = hundred_and_one_fractions
|
hundred_fractions_and_some,
|
||||||
/ (10_u64.pow(9) / fractional_res_to_div(FractionalResolution::FifteenUs) as u64);
|
)
|
||||||
assert_eq!(fractional_part, 101);
|
.unwrap();
|
||||||
|
assert_eq!(fractional_part.1, 100);
|
||||||
|
// Using exactly 101.0 can yield values which will later be rounded down to 100
|
||||||
|
let hundred_and_one_fractions =
|
||||||
|
(101.001 * one_fraction_with_width_two_in_ns).floor() as u64;
|
||||||
|
let fractional_part = fractional_part_from_subsec_ns(
|
||||||
|
FractionalResolution::FifteenUs,
|
||||||
|
hundred_and_one_fractions,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(fractional_part.1, 101);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -934,17 +947,4 @@ mod tests {
|
|||||||
// Assert that the maximum resolution can be reached
|
// Assert that the maximum resolution can be reached
|
||||||
assert_eq!(fractions.1, 2_u32.pow(3 * 8) - 2);
|
assert_eq!(fractions.1, 2_u32.pow(3 * 8) - 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extern crate test;
|
|
||||||
// use test::Bencher;
|
|
||||||
//
|
|
||||||
// #[bench]
|
|
||||||
// fn speed_test(b: &mut Bencher) {
|
|
||||||
// let ns = 10_u32.pow(9) - 1;
|
|
||||||
// let sec_as_ns = ns + 1;
|
|
||||||
// let resolution = 2_u32.pow(3 * 8) - 1;
|
|
||||||
// b.iter(|| {
|
|
||||||
// ns * 100000 / ((sec_as_ns * 100000 + resolution) / resolution)
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
35
src/tm.rs
35
src/tm.rs
@@ -108,18 +108,18 @@ 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))]
|
||||||
pub struct PusTmSecondaryHeader<'slice> {
|
pub struct PusTmSecondaryHeader<'stamp> {
|
||||||
pus_version: PusVersion,
|
pus_version: PusVersion,
|
||||||
pub sc_time_ref_status: u8,
|
pub sc_time_ref_status: u8,
|
||||||
pub service: u8,
|
pub service: u8,
|
||||||
pub subservice: u8,
|
pub subservice: u8,
|
||||||
pub msg_counter: u16,
|
pub msg_counter: u16,
|
||||||
pub dest_id: u16,
|
pub dest_id: u16,
|
||||||
pub time_stamp: &'slice [u8],
|
pub time_stamp: &'stamp [u8],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'slice> PusTmSecondaryHeader<'slice> {
|
impl<'stamp> PusTmSecondaryHeader<'stamp> {
|
||||||
pub fn new_simple(service: u8, subservice: u8, time_stamp: &'slice [u8]) -> Self {
|
pub fn new_simple(service: u8, subservice: u8, time_stamp: &'stamp [u8]) -> Self {
|
||||||
PusTmSecondaryHeader {
|
PusTmSecondaryHeader {
|
||||||
pus_version: PusVersion::PusC,
|
pus_version: PusVersion::PusC,
|
||||||
sc_time_ref_status: 0,
|
sc_time_ref_status: 0,
|
||||||
@@ -136,7 +136,7 @@ impl<'slice> PusTmSecondaryHeader<'slice> {
|
|||||||
subservice: u8,
|
subservice: u8,
|
||||||
msg_counter: u16,
|
msg_counter: u16,
|
||||||
dest_id: u16,
|
dest_id: u16,
|
||||||
time_stamp: &'slice [u8],
|
time_stamp: &'stamp [u8],
|
||||||
) -> Self {
|
) -> Self {
|
||||||
PusTmSecondaryHeader {
|
PusTmSecondaryHeader {
|
||||||
pus_version: PusVersion::PusC,
|
pus_version: PusVersion::PusC,
|
||||||
@@ -201,21 +201,26 @@ impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice
|
|||||||
/// provider like [postcard](https://docs.rs/postcard/latest/postcard/).
|
/// provider like [postcard](https://docs.rs/postcard/latest/postcard/).
|
||||||
///
|
///
|
||||||
/// There is no spare bytes support yet.
|
/// There is no spare bytes support yet.
|
||||||
|
///
|
||||||
|
/// # Lifetimes
|
||||||
|
///
|
||||||
|
/// * `'src_data` - Life time of a buffer where the user provided time stamp and source data will
|
||||||
|
/// be serialized into.
|
||||||
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PusTm<'slice> {
|
pub struct PusTm<'src_data> {
|
||||||
pub sp_header: SpHeader,
|
pub sp_header: SpHeader,
|
||||||
pub sec_header: PusTmSecondaryHeader<'slice>,
|
pub sec_header: PusTmSecondaryHeader<'src_data>,
|
||||||
/// If this is set to false, a manual call to [PusTm::calc_own_crc16] or
|
/// If this is set to false, a manual call to [PusTm::calc_own_crc16] or
|
||||||
/// [PusTm::update_packet_fields] is necessary for the serialized or cached CRC16 to be valid.
|
/// [PusTm::update_packet_fields] is necessary for the serialized or cached CRC16 to be valid.
|
||||||
pub calc_crc_on_serialization: bool,
|
pub calc_crc_on_serialization: bool,
|
||||||
#[cfg_attr(feature = "serde", serde(skip))]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
raw_data: Option<&'slice [u8]>,
|
raw_data: Option<&'src_data [u8]>,
|
||||||
source_data: Option<&'slice [u8]>,
|
source_data: Option<&'src_data [u8]>,
|
||||||
crc16: Option<u16>,
|
crc16: Option<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'slice> PusTm<'slice> {
|
impl<'src_data> PusTm<'src_data> {
|
||||||
/// Generates a new struct instance.
|
/// Generates a new struct instance.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@@ -230,8 +235,8 @@ impl<'slice> PusTm<'slice> {
|
|||||||
/// the correct value to this field manually
|
/// the correct value to this field manually
|
||||||
pub fn new(
|
pub fn new(
|
||||||
sp_header: &mut SpHeader,
|
sp_header: &mut SpHeader,
|
||||||
sec_header: PusTmSecondaryHeader<'slice>,
|
sec_header: PusTmSecondaryHeader<'src_data>,
|
||||||
source_data: Option<&'slice [u8]>,
|
source_data: Option<&'src_data [u8]>,
|
||||||
set_ccsds_len: bool,
|
set_ccsds_len: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
sp_header.set_packet_type(PacketType::Tm);
|
sp_header.set_packet_type(PacketType::Tm);
|
||||||
@@ -259,11 +264,11 @@ impl<'slice> PusTm<'slice> {
|
|||||||
length
|
length
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn time_stamp(&self) -> &'slice [u8] {
|
pub fn time_stamp(&self) -> &'src_data [u8] {
|
||||||
self.sec_header.time_stamp
|
self.sec_header.time_stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_data(&self) -> Option<&'slice [u8]> {
|
pub fn source_data(&self) -> Option<&'src_data [u8]> {
|
||||||
self.source_data
|
self.source_data
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,7 +395,7 @@ impl<'slice> PusTm<'slice> {
|
|||||||
/// the instance and the found byte length of the packet. The timestamp length needs to be
|
/// the instance and the found byte length of the packet. The timestamp length needs to be
|
||||||
/// known beforehand.
|
/// known beforehand.
|
||||||
pub fn from_bytes(
|
pub fn from_bytes(
|
||||||
slice: &'slice [u8],
|
slice: &'src_data [u8],
|
||||||
timestamp_len: usize,
|
timestamp_len: usize,
|
||||||
) -> Result<(Self, usize), PusError> {
|
) -> Result<(Self, usize), PusError> {
|
||||||
let raw_data_len = slice.len();
|
let raw_data_len = slice.len();
|
||||||
|
Reference in New Issue
Block a user