Compare commits
24 Commits
v0.2.0
...
3a71b00198
Author | SHA1 | Date | |
---|---|---|---|
3a71b00198 | |||
13be7ca1e7 | |||
098a534199 | |||
322a56335e | |||
f7c688d8db | |||
54bb4bdaaa | |||
1969a26f14 | |||
d28ea7d4da | |||
aeb2e806b8 | |||
938c4ba770 | |||
97c70bf03b | |||
dc6d726e61 | |||
85bfcad111 | |||
03d112cbef | |||
1ec21c1bff | |||
c750f94fba | |||
1d6cf3a75d | |||
f8199ca87a | |||
4c1101f65f | |||
38b789ca6d | |||
d391891991 | |||
65e85f20e0 | |||
a2673c9870 | |||
603f688ac3 |
31
CHANGELOG.md
31
CHANGELOG.md
@ -8,6 +8,37 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- `serde` support is now optional and behind the `serde` feature
|
||||||
|
- `PusTcSecondaryHeaderT` trait renamed to `GenericPusTcSecondaryHeader`
|
||||||
|
- `PusTmSecondaryHeaderT` trait renamed to `GenericPusTmSecondaryHeader`
|
||||||
|
- `SpHeader`: Former `tc` and `tm` methods now named `tc_unseg` and `tm_unseg`.
|
||||||
|
Former `new` method now called `new_from_single_fields`
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- `serde` `Serialize` and `Deserialize` added to all types
|
||||||
|
- Added `const` constructors for `PacketId`, `PacketSeqCtrl` and
|
||||||
|
`SpHeader`
|
||||||
|
- Added `PartialEq` and `Eq` `derive`s to `CdsShortTimeProvider`
|
||||||
|
|
||||||
|
# [v0.3.1] 03.12.2022
|
||||||
|
|
||||||
|
- Small fix for faulty docs.rs build
|
||||||
|
|
||||||
|
# [v0.3.0] 01.12.2022
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- `EcssEnumerationExt` trait which implements `Debug`, `Copy`, `Clone`,
|
||||||
|
`PartialEq` and `Eq` in addition to `EcssEnumeration`
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- `EcssEnumeration` trait: Rename `write_to_bytes`
|
||||||
|
to `write_to_be_bytes`
|
||||||
|
|
||||||
# [v0.2.0] 13.09.2022
|
# [v0.2.0] 13.09.2022
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
22
Cargo.toml
22
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "spacepackets"
|
name = "spacepackets"
|
||||||
version = "0.2.0"
|
version = "0.3.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
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"
|
||||||
@ -12,17 +12,18 @@ categories = ["aerospace", "aerospace::space-protocols", "no-std", "hardware-sup
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zerocopy = "0.6.1"
|
zerocopy = "0.6"
|
||||||
crc = "3.0.0"
|
crc = "3.0"
|
||||||
delegate = "0.7.0"
|
delegate = "0.8"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "1.0.142"
|
version = "1.0"
|
||||||
|
optional = true
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
version = "0.4.20"
|
version = "0.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dependencies.num-traits]
|
[dependencies.num-traits]
|
||||||
@ -30,9 +31,14 @@ version = "0.2"
|
|||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies.postcard]
|
[dev-dependencies.postcard]
|
||||||
version = "1.0.1"
|
version = "1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std", "dep:serde"]
|
||||||
std = ["chrono/std", "chrono/clock", "alloc"]
|
std = ["chrono/std", "chrono/clock", "alloc"]
|
||||||
|
serde = ["chrono/serde"]
|
||||||
alloc = ["postcard/alloc"]
|
alloc = ["postcard/alloc"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "doc_cfg"]
|
||||||
|
@ -33,6 +33,7 @@ Default features:
|
|||||||
- [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
|
- [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
|
||||||
like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
|
like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
|
||||||
Enabled by the `std` feature.
|
Enabled by the `std` feature.
|
||||||
|
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
|
34
src/ecss.rs
34
src/ecss.rs
@ -1,8 +1,10 @@
|
|||||||
//! 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/)
|
||||||
use crate::{ByteConversionError, CcsdsPacket, SizeMissmatch};
|
use crate::{ByteConversionError, CcsdsPacket, SizeMissmatch};
|
||||||
|
use core::fmt::Debug;
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use crc::{Crc, CRC_16_IBM_3740};
|
use crc::{Crc, CRC_16_IBM_3740};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub type CrcType = u16;
|
pub type CrcType = u16;
|
||||||
@ -12,7 +14,8 @@ pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
|||||||
pub const CCSDS_HEADER_LEN: usize = size_of::<crate::zc::SpHeader>();
|
pub const CCSDS_HEADER_LEN: usize = size_of::<crate::zc::SpHeader>();
|
||||||
|
|
||||||
/// 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, Serialize, Deserialize, Debug)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum PusVersion {
|
pub enum PusVersion {
|
||||||
EsaPus = 0,
|
EsaPus = 0,
|
||||||
PusA = 1,
|
PusA = 1,
|
||||||
@ -34,6 +37,7 @@ impl TryFrom<u8> for PusVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum PacketTypeCodes {
|
pub enum PacketTypeCodes {
|
||||||
Boolean = 1,
|
Boolean = 1,
|
||||||
Enumerated = 2,
|
Enumerated = 2,
|
||||||
@ -50,6 +54,7 @@ pub enum PacketTypeCodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum PusError {
|
pub enum PusError {
|
||||||
VersionNotSupported(PusVersion),
|
VersionNotSupported(PusVersion),
|
||||||
IncorrectCrc(u16),
|
IncorrectCrc(u16),
|
||||||
@ -158,7 +163,7 @@ macro_rules! sp_header_impls {
|
|||||||
pub(crate) use ccsds_impl;
|
pub(crate) use ccsds_impl;
|
||||||
pub(crate) use sp_header_impls;
|
pub(crate) use sp_header_impls;
|
||||||
|
|
||||||
/// Generic trait for ECSS enumeration which consist of a PFC field denoting their length
|
/// Generic trait for ECSS enumeration which consist of a PFC field denoting their bit length
|
||||||
/// and an unsigned value. The trait makes no assumptions about the actual type of the unsigned
|
/// and an unsigned value. The trait makes no assumptions about the actual type of the unsigned
|
||||||
/// value and only requires implementors to implement a function which writes the enumeration into
|
/// value and only requires implementors to implement a function which writes the enumeration into
|
||||||
/// a raw byte format.
|
/// a raw byte format.
|
||||||
@ -168,10 +173,12 @@ pub trait EcssEnumeration {
|
|||||||
fn byte_width(&self) -> usize {
|
fn byte_width(&self) -> usize {
|
||||||
(self.pfc() / 8) as usize
|
(self.pfc() / 8) as usize
|
||||||
}
|
}
|
||||||
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait ToBeBytes {
|
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
|
||||||
|
|
||||||
|
pub trait ToBeBytes {
|
||||||
type ByteArray: AsRef<[u8]>;
|
type ByteArray: AsRef<[u8]>;
|
||||||
fn to_be_bytes(&self) -> Self::ByteArray;
|
fn to_be_bytes(&self) -> Self::ByteArray;
|
||||||
}
|
}
|
||||||
@ -208,6 +215,8 @@ impl ToBeBytes for u64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct GenericEcssEnumWrapper<TYPE> {
|
pub struct GenericEcssEnumWrapper<TYPE> {
|
||||||
val: TYPE,
|
val: TYPE,
|
||||||
}
|
}
|
||||||
@ -227,7 +236,7 @@ impl<TYPE: ToBeBytes> EcssEnumeration for GenericEcssEnumWrapper<TYPE> {
|
|||||||
size_of::<TYPE>() as u8 * 8_u8
|
size_of::<TYPE>() as u8 * 8_u8
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||||
if buf.len() < self.byte_width() as usize {
|
if buf.len() < self.byte_width() as usize {
|
||||||
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||||
found: buf.len(),
|
found: buf.len(),
|
||||||
@ -239,6 +248,11 @@ impl<TYPE: ToBeBytes> EcssEnumeration for GenericEcssEnumWrapper<TYPE> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<TYPE: Debug + Copy + Clone + PartialEq + Eq + ToBeBytes> EcssEnumerationExt
|
||||||
|
for GenericEcssEnumWrapper<TYPE>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
pub type EcssEnumU8 = GenericEcssEnumWrapper<u8>;
|
pub type EcssEnumU8 = GenericEcssEnumWrapper<u8>;
|
||||||
pub type EcssEnumU16 = GenericEcssEnumWrapper<u16>;
|
pub type EcssEnumU16 = GenericEcssEnumWrapper<u16>;
|
||||||
pub type EcssEnumU32 = GenericEcssEnumWrapper<u32>;
|
pub type EcssEnumU32 = GenericEcssEnumWrapper<u32>;
|
||||||
@ -254,7 +268,7 @@ mod tests {
|
|||||||
let mut buf = [0, 0, 0];
|
let mut buf = [0, 0, 0];
|
||||||
let my_enum = EcssEnumU8::new(1);
|
let my_enum = EcssEnumU8::new(1);
|
||||||
my_enum
|
my_enum
|
||||||
.write_to_bytes(&mut buf[1..2])
|
.write_to_be_bytes(&mut buf[1..2])
|
||||||
.expect("To byte conversion of u8 failed");
|
.expect("To byte conversion of u8 failed");
|
||||||
assert_eq!(buf[1], 1);
|
assert_eq!(buf[1], 1);
|
||||||
}
|
}
|
||||||
@ -264,7 +278,7 @@ mod tests {
|
|||||||
let mut buf = [0, 0, 0];
|
let mut buf = [0, 0, 0];
|
||||||
let my_enum = EcssEnumU16::new(0x1f2f);
|
let my_enum = EcssEnumU16::new(0x1f2f);
|
||||||
my_enum
|
my_enum
|
||||||
.write_to_bytes(&mut buf[1..3])
|
.write_to_be_bytes(&mut buf[1..3])
|
||||||
.expect("To byte conversion of u8 failed");
|
.expect("To byte conversion of u8 failed");
|
||||||
assert_eq!(buf[1], 0x1f);
|
assert_eq!(buf[1], 0x1f);
|
||||||
assert_eq!(buf[2], 0x2f);
|
assert_eq!(buf[2], 0x2f);
|
||||||
@ -274,7 +288,7 @@ mod tests {
|
|||||||
fn test_slice_u16_too_small() {
|
fn test_slice_u16_too_small() {
|
||||||
let mut buf = [0];
|
let mut buf = [0];
|
||||||
let my_enum = EcssEnumU16::new(0x1f2f);
|
let my_enum = EcssEnumU16::new(0x1f2f);
|
||||||
let res = my_enum.write_to_bytes(&mut buf[0..1]);
|
let res = my_enum.write_to_be_bytes(&mut buf[0..1]);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let error = res.unwrap_err();
|
let error = res.unwrap_err();
|
||||||
match error {
|
match error {
|
||||||
@ -293,7 +307,7 @@ mod tests {
|
|||||||
let mut buf = [0, 0, 0, 0, 0];
|
let mut buf = [0, 0, 0, 0, 0];
|
||||||
let my_enum = EcssEnumU32::new(0x1f2f3f4f);
|
let my_enum = EcssEnumU32::new(0x1f2f3f4f);
|
||||||
my_enum
|
my_enum
|
||||||
.write_to_bytes(&mut buf[1..5])
|
.write_to_be_bytes(&mut buf[1..5])
|
||||||
.expect("To byte conversion of u8 failed");
|
.expect("To byte conversion of u8 failed");
|
||||||
assert_eq!(buf[1], 0x1f);
|
assert_eq!(buf[1], 0x1f);
|
||||||
assert_eq!(buf[2], 0x2f);
|
assert_eq!(buf[2], 0x2f);
|
||||||
@ -305,7 +319,7 @@ mod tests {
|
|||||||
fn test_slice_u32_too_small() {
|
fn test_slice_u32_too_small() {
|
||||||
let mut buf = [0, 0, 0, 0, 0];
|
let mut buf = [0, 0, 0, 0, 0];
|
||||||
let my_enum = EcssEnumU32::new(0x1f2f3f4f);
|
let my_enum = EcssEnumU32::new(0x1f2f3f4f);
|
||||||
let res = my_enum.write_to_bytes(&mut buf[0..3]);
|
let res = my_enum.write_to_be_bytes(&mut buf[0..3]);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
let error = res.unwrap_err();
|
let error = res.unwrap_err();
|
||||||
match error {
|
match error {
|
||||||
|
339
src/lib.rs
339
src/lib.rs
@ -27,21 +27,24 @@
|
|||||||
//! - [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
|
//! - [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which operate on containers
|
||||||
//! like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
|
//! like [`alloc::vec::Vec`](https://doc.rust-lang.org/beta/alloc/vec/struct.Vec.html).
|
||||||
//! Enabled by the `std` feature.
|
//! Enabled by the `std` feature.
|
||||||
|
//! - [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and
|
||||||
|
//! `Deserialize` `derive`s
|
||||||
//!
|
//!
|
||||||
//! ## Module
|
//! ## Module
|
||||||
//!
|
//!
|
||||||
//! This module contains helpers and data structures to generate Space Packets according to the
|
//! This module contains helpers and data structures to generate Space Packets according to the
|
||||||
//! [CCSDS 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf). This includes the
|
//! [CCSDS 133.0-B-2](https://public.ccsds.org/Pubs/133x0b2e1.pdf). This includes the
|
||||||
//! [SpHeader] class to generate the Space Packet Header component common to all space packets
|
//! [SpHeader] class to generate the Space Packet Header component common to all space packets.
|
||||||
//!
|
//!
|
||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use spacepackets::SpHeader;
|
//! use spacepackets::SpHeader;
|
||||||
//! let sp_header = SpHeader::tc(0x42, 12, 0).expect("Error creating SP header");
|
//! let sp_header = SpHeader::tc_unseg(0x42, 12, 0).expect("Error creating SP header");
|
||||||
//! println!("{:?}", sp_header);
|
//! println!("{:?}", sp_header);
|
||||||
//! ```
|
//! ```
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![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))]
|
||||||
@ -50,6 +53,7 @@ extern crate std;
|
|||||||
use crate::ecss::CCSDS_HEADER_LEN;
|
use crate::ecss::CCSDS_HEADER_LEN;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod ecss;
|
pub mod ecss;
|
||||||
@ -61,22 +65,26 @@ pub const MAX_APID: u16 = 2u16.pow(11) - 1;
|
|||||||
pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1;
|
pub const MAX_SEQ_COUNT: u16 = 2u16.pow(14) - 1;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct SizeMissmatch {
|
pub struct SizeMissmatch {
|
||||||
pub found: usize,
|
pub found: usize,
|
||||||
pub expected: usize,
|
pub expected: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum ByteConversionError {
|
pub enum ByteConversionError {
|
||||||
/// The passed slice is too small. Returns the found and expected minimum size
|
/// The passed slice is too small. Returns the passed slice length and expected minimum size
|
||||||
ToSliceTooSmall(SizeMissmatch),
|
ToSliceTooSmall(SizeMissmatch),
|
||||||
/// The provider buffer it soo small. Returns the found and expected minimum size
|
/// The provider buffer is too small. Returns the passed slice length and expected minimum size
|
||||||
FromSliceTooSmall(SizeMissmatch),
|
FromSliceTooSmall(SizeMissmatch),
|
||||||
/// The [zerocopy] library failed to write to bytes
|
/// The [zerocopy] library failed to write to bytes
|
||||||
ZeroCopyToError,
|
ZeroCopyToError,
|
||||||
ZeroCopyFromError,
|
ZeroCopyFromError,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum PacketType {
|
pub enum PacketType {
|
||||||
Tm = 0,
|
Tm = 0,
|
||||||
Tc = 1,
|
Tc = 1,
|
||||||
@ -98,7 +106,8 @@ pub fn packet_type_in_raw_packet_id(packet_id: u16) -> PacketType {
|
|||||||
PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap()
|
PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum SequenceFlags {
|
pub enum SequenceFlags {
|
||||||
ContinuationSegment = 0b00,
|
ContinuationSegment = 0b00,
|
||||||
FirstSegment = 0b01,
|
FirstSegment = 0b01,
|
||||||
@ -122,21 +131,57 @@ impl TryFrom<u8> for SequenceFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PacketId {
|
pub struct PacketId {
|
||||||
pub ptype: PacketType,
|
pub ptype: PacketType,
|
||||||
pub sec_header_flag: bool,
|
pub sec_header_flag: bool,
|
||||||
apid: u16,
|
apid: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketId {
|
impl Default for PacketId {
|
||||||
pub fn new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
|
fn default() -> Self {
|
||||||
let mut pid = PacketId {
|
PacketId {
|
||||||
ptype,
|
ptype: PacketType::Tm,
|
||||||
sec_header_flag,
|
sec_header_flag: false,
|
||||||
apid: 0,
|
apid: 0,
|
||||||
};
|
}
|
||||||
pid.set_apid(apid).then(|| pid)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketId {
|
||||||
|
pub const fn const_tc(sec_header: bool, apid: u16) -> Self {
|
||||||
|
Self::const_new(PacketType::Tc, sec_header, apid)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tm(sec_header: bool, apid: u16) -> Option<Self> {
|
||||||
|
Self::new(PacketType::Tm, sec_header, apid)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn const_new(ptype: PacketType, sec_header: bool, apid: u16) -> Self {
|
||||||
|
if apid > MAX_APID {
|
||||||
|
panic!("APID too large");
|
||||||
|
}
|
||||||
|
PacketId {
|
||||||
|
ptype,
|
||||||
|
sec_header_flag: sec_header,
|
||||||
|
apid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
|
||||||
|
if apid > MAX_APID {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
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
|
||||||
@ -169,27 +214,39 @@ impl From<u16> for PacketId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PacketSequenceCtrl {
|
pub struct PacketSequenceCtrl {
|
||||||
pub seq_flags: SequenceFlags,
|
pub seq_flags: SequenceFlags,
|
||||||
seq_count: u16,
|
seq_count: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PacketSequenceCtrl {
|
impl PacketSequenceCtrl {
|
||||||
/// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT]
|
/// const variant of [PacketSequenceCtrl::new], but panics if the sequence count exceeds
|
||||||
pub fn new(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
|
/// [MAX_SEQ_COUNT].
|
||||||
let mut psc = PacketSequenceCtrl {
|
const fn const_new(seq_flags: SequenceFlags, seq_count: u16) -> PacketSequenceCtrl {
|
||||||
|
if seq_count > MAX_SEQ_COUNT {
|
||||||
|
panic!("Sequence count too large");
|
||||||
|
}
|
||||||
|
PacketSequenceCtrl {
|
||||||
seq_flags,
|
seq_flags,
|
||||||
seq_count: 0,
|
seq_count,
|
||||||
};
|
}
|
||||||
psc.set_seq_count(seq_count).then(|| psc)
|
}
|
||||||
|
|
||||||
|
/// Returns [None] if the passed sequence count exceeds [MAX_SEQ_COUNT].
|
||||||
|
pub fn new(seq_flags: SequenceFlags, seq_count: u16) -> Option<PacketSequenceCtrl> {
|
||||||
|
if seq_count > MAX_SEQ_COUNT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(PacketSequenceCtrl::const_new(seq_flags, seq_count))
|
||||||
}
|
}
|
||||||
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.
|
||||||
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;
|
||||||
@ -230,7 +287,7 @@ macro_rules! sph_from_other {
|
|||||||
const SSC_MASK: u16 = 0x3FFF;
|
const SSC_MASK: u16 = 0x3FFF;
|
||||||
const VERSION_MASK: u16 = 0xE000;
|
const VERSION_MASK: u16 = 0xE000;
|
||||||
|
|
||||||
/// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2
|
/// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2.
|
||||||
pub trait CcsdsPacket {
|
pub trait CcsdsPacket {
|
||||||
fn ccsds_version(&self) -> u8;
|
fn ccsds_version(&self) -> u8;
|
||||||
fn packet_id(&self) -> PacketId;
|
fn packet_id(&self) -> PacketId;
|
||||||
@ -244,7 +301,7 @@ pub trait CcsdsPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND
|
/// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND
|
||||||
/// of the first 2 bytes with 0x1FFF
|
/// of the first 2 bytes with 0x1FFF.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn packet_id_raw(&self) -> u16 {
|
fn packet_id_raw(&self) -> u16 {
|
||||||
self.packet_id().raw()
|
self.packet_id().raw()
|
||||||
@ -255,8 +312,8 @@ pub trait CcsdsPacket {
|
|||||||
self.psc().raw()
|
self.psc().raw()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve Packet Type (TM: 0, TC: 1).
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Retrieve Packet Type (TM: 0, TC: 1)
|
|
||||||
fn ptype(&self) -> PacketType {
|
fn ptype(&self) -> PacketType {
|
||||||
// This call should never fail because only 0 and 1 can be passed to the try_from call
|
// This call should never fail because only 0 and 1 can be passed to the try_from call
|
||||||
self.packet_id().ptype
|
self.packet_id().ptype
|
||||||
@ -273,13 +330,13 @@ pub trait CcsdsPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the secondary header flag. Returns true if a secondary header is present
|
/// Retrieve the secondary header flag. Returns true if a secondary header is present
|
||||||
/// and false if it is not
|
/// and false if it is not.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn sec_header_flag(&self) -> bool {
|
fn sec_header_flag(&self) -> bool {
|
||||||
self.packet_id().sec_header_flag
|
self.packet_id().sec_header_flag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve Application Process ID
|
/// Retrieve Application Process ID.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn apid(&self) -> u16 {
|
fn apid(&self) -> u16 {
|
||||||
self.packet_id().apid
|
self.packet_id().apid
|
||||||
@ -307,16 +364,18 @@ pub trait CcsdsPrimaryHeader {
|
|||||||
) -> Self;
|
) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Space Packet Primary Header according to CCSDS 133.0-B-2
|
/// Space Packet Primary Header according to CCSDS 133.0-B-2.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `version` - CCSDS version field, occupies the first 3 bits of the raw header
|
/// * `version` - CCSDS version field, occupies the first 3 bits of the raw header. Will generally
|
||||||
|
/// be set to 0b000 in all constructors provided by this crate.
|
||||||
/// * `packet_id` - Packet Identifier, which can also be used as a start marker. Occupies the last
|
/// * `packet_id` - Packet Identifier, which can also be used as a start marker. Occupies the last
|
||||||
/// 13 bits of the first two bytes of the raw header
|
/// 13 bits of the first two bytes of the raw header
|
||||||
/// * `psc` - Packet Sequence Control, occupies the third and fourth byte of the raw header
|
/// * `psc` - Packet Sequence Control, occupies the third and fourth byte of the raw header
|
||||||
/// * `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(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct SpHeader {
|
pub struct SpHeader {
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
pub packet_id: PacketId,
|
pub packet_id: PacketId,
|
||||||
@ -325,55 +384,94 @@ pub struct SpHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpHeader {
|
impl Default for SpHeader {
|
||||||
|
/// 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.
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SpHeader {
|
SpHeader {
|
||||||
version: 0,
|
version: 0,
|
||||||
packet_id: PacketId {
|
packet_id: PacketId::default(),
|
||||||
ptype: PacketType::Tm,
|
|
||||||
apid: 0,
|
|
||||||
sec_header_flag: false,
|
|
||||||
},
|
|
||||||
psc: PacketSequenceCtrl {
|
psc: PacketSequenceCtrl {
|
||||||
seq_flags: SequenceFlags::Unsegmented,
|
seq_flags: SequenceFlags::Unsegmented,
|
||||||
seq_count: 0,
|
seq_count: 0,
|
||||||
},
|
},
|
||||||
data_len: 0,
|
data_len: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpHeader {
|
impl SpHeader {
|
||||||
/// Create a new Space Packet Header instance which can be used to create generic
|
pub const fn new(packet_id: PacketId, psc: PacketSequenceCtrl, data_len: u16) -> Self {
|
||||||
/// Space Packets. This will return [None] if the APID or sequence count argument
|
Self {
|
||||||
/// exceed [MAX_APID] or [MAX_SEQ_COUNT] respectively.
|
version: 0,
|
||||||
pub fn new(
|
packet_id,
|
||||||
|
psc,
|
||||||
|
data_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// const variant of the [SpHeader::new_fron_single_fields] function. Panics if the passed
|
||||||
|
/// APID exceeds [MAX_APID] or the passed packet sequence count exceeds [MAX_SEQ_COUNT].
|
||||||
|
const fn const_new_from_single_fields(
|
||||||
ptype: PacketType,
|
ptype: PacketType,
|
||||||
sec_header: bool,
|
sec_header: bool,
|
||||||
apid: u16,
|
apid: u16,
|
||||||
|
seq_flags: SequenceFlags,
|
||||||
|
seq_count: u16,
|
||||||
|
data_len: u16,
|
||||||
|
) -> Self {
|
||||||
|
if seq_count > MAX_SEQ_COUNT {
|
||||||
|
panic!("Sequence count is too large");
|
||||||
|
}
|
||||||
|
if apid > MAX_APID {
|
||||||
|
panic!("APID is too large");
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
psc: PacketSequenceCtrl::const_new(seq_flags, seq_count),
|
||||||
|
packet_id: PacketId::const_new(ptype, sec_header, apid),
|
||||||
|
data_len,
|
||||||
|
version: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new Space Packet Header instance which can be used to create generic
|
||||||
|
/// Space Packets. 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.
|
||||||
|
pub fn new_from_single_fields(
|
||||||
|
ptype: PacketType,
|
||||||
|
sec_header: bool,
|
||||||
|
apid: u16,
|
||||||
|
seq_flags: SequenceFlags,
|
||||||
seq_count: u16,
|
seq_count: u16,
|
||||||
data_len: u16,
|
data_len: u16,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
if seq_count > MAX_SEQ_COUNT || apid > MAX_APID {
|
if seq_count > MAX_SEQ_COUNT || apid > MAX_APID {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut header = SpHeader::default();
|
Some(SpHeader::const_new_from_single_fields(
|
||||||
header.packet_id.sec_header_flag = sec_header;
|
ptype, sec_header, apid, seq_flags, seq_count, data_len,
|
||||||
header.packet_id.apid = apid;
|
))
|
||||||
header.packet_id.ptype = ptype;
|
|
||||||
header.psc.seq_count = seq_count;
|
|
||||||
header.data_len = data_len;
|
|
||||||
Some(header)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
/// set accordingly. The secondary header flag field is set to false.
|
||||||
pub fn tm(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
|
pub fn tm(apid: u16, seq_flags: SequenceFlags, seq_count: u16, data_len: u16) -> Option<Self> {
|
||||||
Self::new(PacketType::Tm, false, apid, seq_count, data_len)
|
Self::new_from_single_fields(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function for telecommand space packet headers. The packet type field will be
|
/// Helper function for telemetry space packet headers. The packet type field will be
|
||||||
/// set accordingly.
|
/// set accordingly. The secondary header flag field is set to false.
|
||||||
pub fn tc(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
|
pub fn tc(apid: u16, seq_flags: SequenceFlags, seq_count: u16, data_len: u16) -> Option<Self> {
|
||||||
Self::new(PacketType::Tc, false, apid, seq_count, data_len)
|
Self::new_from_single_fields(PacketType::Tc, false, apid, seq_flags, seq_count, data_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Variant of [SpHeader::tm] which sets the sequence flag field to [SequenceFlags::Unsegmented]
|
||||||
|
pub fn tm_unseg(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
|
||||||
|
Self::tm(apid, SequenceFlags::Unsegmented, seq_count, data_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Variant of [SpHeader::tc] which sets the sequence flag field to [SequenceFlags::Unsegmented]
|
||||||
|
pub fn tc_unseg(apid: u16, seq_count: u16, data_len: u16) -> Option<Self> {
|
||||||
|
Self::tc(apid, SequenceFlags::Unsegmented, seq_count, data_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
//noinspection RsTraitImplementation
|
||||||
@ -539,23 +637,75 @@ pub mod zc {
|
|||||||
sph_from_other!(SpHeader, crate::SpHeader);
|
sph_from_other!(SpHeader, crate::SpHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::CcsdsPrimaryHeader;
|
use crate::CcsdsPrimaryHeader;
|
||||||
use crate::SpHeader;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
packet_type_in_raw_packet_id, zc, CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType,
|
packet_type_in_raw_packet_id, zc, CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType,
|
||||||
SequenceFlags,
|
|
||||||
};
|
};
|
||||||
|
use crate::{SequenceFlags, SpHeader};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use num::pow;
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use num_traits::pow;
|
use num_traits::pow;
|
||||||
use postcard::from_bytes;
|
#[cfg(feature = "serde")]
|
||||||
#[cfg(feature = "alloc")]
|
use postcard::{from_bytes, to_allocvec};
|
||||||
use postcard::to_allocvec;
|
|
||||||
|
const CONST_SP: SpHeader = SpHeader::new(
|
||||||
|
PacketId::const_tc(true, 0x36),
|
||||||
|
PacketSequenceCtrl::const_new(SequenceFlags::ContinuationSegment, 0x88),
|
||||||
|
0x90,
|
||||||
|
);
|
||||||
|
|
||||||
|
const PACKET_ID_TM: PacketId = PacketId::const_tm(true, 0x22);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_const_packet_id() {
|
||||||
|
assert_eq!(PACKET_ID_TM.apid(), 0x22);
|
||||||
|
assert_eq!(PACKET_ID_TM.sec_header_flag, true);
|
||||||
|
assert_eq!(PACKET_ID_TM.ptype, PacketType::Tm);
|
||||||
|
let const_tc_id = PacketId::const_tc(true, 0x23);
|
||||||
|
assert_eq!(const_tc_id.ptype, PacketType::Tc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_packet_id() {
|
||||||
|
let id_default = PacketId::default();
|
||||||
|
assert_eq!(id_default.ptype, PacketType::Tm);
|
||||||
|
assert_eq!(id_default.apid, 0x000);
|
||||||
|
assert_eq!(id_default.sec_header_flag, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_packet_id_ctors() {
|
||||||
|
let packet_id = PacketId::new(PacketType::Tc, true, 0x1ff);
|
||||||
|
assert!(packet_id.is_some());
|
||||||
|
let packet_id = packet_id.unwrap();
|
||||||
|
assert_eq!(packet_id.apid(), 0x1ff);
|
||||||
|
assert_eq!(packet_id.ptype, PacketType::Tc);
|
||||||
|
assert_eq!(packet_id.sec_header_flag, true);
|
||||||
|
let packet_id_tc = PacketId::tc(true, 0x1ff);
|
||||||
|
assert!(packet_id_tc.is_some());
|
||||||
|
let packet_id_tc = packet_id_tc.unwrap();
|
||||||
|
assert_eq!(packet_id_tc, packet_id);
|
||||||
|
let packet_id_tm = PacketId::tm(true, 0x2ff);
|
||||||
|
assert!(packet_id_tm.is_some());
|
||||||
|
let packet_id_tm = packet_id_tm.unwrap();
|
||||||
|
assert_eq!(packet_id_tm.sec_header_flag, true);
|
||||||
|
assert_eq!(packet_id_tm.ptype, PacketType::Tm);
|
||||||
|
assert_eq!(packet_id_tm.apid, 0x2ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_const_sp_header() {
|
||||||
|
assert_eq!(CONST_SP.sec_header_flag(), true);
|
||||||
|
assert_eq!(CONST_SP.apid(), 0x36);
|
||||||
|
assert_eq!(
|
||||||
|
CONST_SP.sequence_flags(),
|
||||||
|
SequenceFlags::ContinuationSegment
|
||||||
|
);
|
||||||
|
assert_eq!(CONST_SP.seq_count(), 0x88);
|
||||||
|
assert_eq!(CONST_SP.data_len, 0x90);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_seq_flag_helpers() {
|
fn test_seq_flag_helpers() {
|
||||||
@ -638,9 +788,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "serde")]
|
||||||
fn test_serde_sph() {
|
fn test_serde_sph() {
|
||||||
let sp_header = SpHeader::tc(0x42, 12, 0).expect("Error creating SP header");
|
let sp_header = SpHeader::tc_unseg(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());
|
||||||
@ -662,7 +812,7 @@ 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 = SpHeader::tm(0x7, 22, 36).expect("Error creating SP header");
|
let sp_header = SpHeader::tm_unseg(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());
|
||||||
@ -693,28 +843,69 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sp_header_setters() {
|
fn test_setters() {
|
||||||
let mut sp_header = SpHeader::tc(0x42, 12, 0).expect("Error creating SP header");
|
let sp_header = SpHeader::tc(0x42, SequenceFlags::Unsegmented, 25, 0);
|
||||||
assert_eq!(sp_header.apid(), 0x42);
|
assert!(sp_header.is_some());
|
||||||
|
let mut sp_header = sp_header.unwrap();
|
||||||
sp_header.set_apid(0x12);
|
sp_header.set_apid(0x12);
|
||||||
assert_eq!(sp_header.apid(), 0x12);
|
assert_eq!(sp_header.apid(), 0x12);
|
||||||
|
|
||||||
sp_header.set_sec_header_flag();
|
sp_header.set_sec_header_flag();
|
||||||
assert!(sp_header.sec_header_flag());
|
assert!(sp_header.sec_header_flag());
|
||||||
sp_header.clear_sec_header_flag();
|
sp_header.clear_sec_header_flag();
|
||||||
assert!(!sp_header.sec_header_flag());
|
assert!(!sp_header.sec_header_flag());
|
||||||
sp_header.set_seq_count(0x45);
|
|
||||||
assert_eq!(sp_header.seq_count(), 0x45);
|
|
||||||
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
||||||
sp_header.set_packet_type(PacketType::Tm);
|
sp_header.set_packet_type(PacketType::Tm);
|
||||||
assert_eq!(sp_header.ptype(), PacketType::Tm);
|
assert_eq!(sp_header.ptype(), PacketType::Tm);
|
||||||
|
sp_header.set_seq_count(0x45);
|
||||||
|
assert_eq!(sp_header.seq_count(), 0x45);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tc_ctor() {
|
||||||
|
let sp_header = SpHeader::tc(0x42, SequenceFlags::Unsegmented, 25, 0);
|
||||||
|
assert!(sp_header.is_some());
|
||||||
|
let sp_header = sp_header.unwrap();
|
||||||
|
verify_sp_fields(PacketType::Tc, &sp_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tc_ctor_unseg() {
|
||||||
|
let sp_header = SpHeader::tc_unseg(0x42, 25, 0);
|
||||||
|
assert!(sp_header.is_some());
|
||||||
|
let sp_header = sp_header.unwrap();
|
||||||
|
verify_sp_fields(PacketType::Tc, &sp_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tm_ctor() {
|
||||||
|
let sp_header = SpHeader::tm(0x42, SequenceFlags::Unsegmented, 25, 0);
|
||||||
|
assert!(sp_header.is_some());
|
||||||
|
let sp_header = sp_header.unwrap();
|
||||||
|
verify_sp_fields(PacketType::Tm, &sp_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tm_ctor_unseg() {
|
||||||
|
let sp_header = SpHeader::tm_unseg(0x42, 25, 0);
|
||||||
|
assert!(sp_header.is_some());
|
||||||
|
let sp_header = sp_header.unwrap();
|
||||||
|
verify_sp_fields(PacketType::Tm, &sp_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_sp_fields(ptype: PacketType, sp_header: &SpHeader) {
|
||||||
|
assert_eq!(sp_header.ptype(), ptype);
|
||||||
|
assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
|
||||||
|
assert_eq!(sp_header.apid(), 0x42);
|
||||||
|
assert_eq!(sp_header.seq_count(), 25);
|
||||||
|
assert_eq!(sp_header.data_len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_zc_sph() {
|
fn test_zc_sph() {
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
let sp_header = SpHeader::tc(0x7FF, pow(2, 14) - 1, 0).expect("Error creating SP header");
|
let 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);
|
||||||
|
47
src/tc.rs
47
src/tc.rs
@ -9,7 +9,7 @@
|
|||||||
//! use spacepackets::ecss::PusPacket;
|
//! use spacepackets::ecss::PusPacket;
|
||||||
//!
|
//!
|
||||||
//! // Create a ping telecommand with no user application data
|
//! // Create a ping telecommand with no user application data
|
||||||
//! let mut sph = SpHeader::tc(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);
|
||||||
//! let pus_tc = PusTc::new(&mut sph, tc_header, None, true);
|
//! let pus_tc = PusTc::new(&mut sph, tc_header, None, true);
|
||||||
//! println!("{:?}", pus_tc);
|
//! println!("{:?}", pus_tc);
|
||||||
@ -41,6 +41,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ pub const ACK_ALL: u8 = AckOpts::Acceptance as u8
|
|||||||
| AckOpts::Progress as u8
|
| AckOpts::Progress as u8
|
||||||
| AckOpts::Completion as u8;
|
| AckOpts::Completion as u8;
|
||||||
|
|
||||||
pub trait PusTcSecondaryHeaderT {
|
pub trait GenericPusTcSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn ack_flags(&self) -> u8;
|
fn ack_flags(&self) -> u8;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -76,7 +77,7 @@ pub trait PusTcSecondaryHeaderT {
|
|||||||
|
|
||||||
pub mod zc {
|
pub mod zc {
|
||||||
use crate::ecss::{PusError, PusVersion};
|
use crate::ecss::{PusError, PusVersion};
|
||||||
use crate::tc::PusTcSecondaryHeaderT;
|
use crate::tc::GenericPusTcSecondaryHeader;
|
||||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||||
|
|
||||||
#[derive(FromBytes, AsBytes, Unaligned)]
|
#[derive(FromBytes, AsBytes, Unaligned)]
|
||||||
@ -103,7 +104,7 @@ pub mod zc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTcSecondaryHeaderT for PusTcSecondaryHeader {
|
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -136,7 +137,8 @@ pub mod zc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Debug)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PusTcSecondaryHeader {
|
pub struct PusTcSecondaryHeader {
|
||||||
pub service: u8,
|
pub service: u8,
|
||||||
pub subservice: u8,
|
pub subservice: u8,
|
||||||
@ -145,7 +147,7 @@ pub struct PusTcSecondaryHeader {
|
|||||||
pub version: PusVersion,
|
pub version: PusVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTcSecondaryHeaderT for PusTcSecondaryHeader {
|
impl GenericPusTcSecondaryHeader for PusTcSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
self.version
|
self.version
|
||||||
}
|
}
|
||||||
@ -211,14 +213,15 @@ impl PusTcSecondaryHeader {
|
|||||||
/// [postcard](https://docs.rs/postcard/latest/postcard/).
|
/// [postcard](https://docs.rs/postcard/latest/postcard/).
|
||||||
///
|
///
|
||||||
/// There is no spare bytes support yet.
|
/// There is no spare bytes support yet.
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Debug)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PusTc<'slice> {
|
pub struct PusTc<'slice> {
|
||||||
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,
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
raw_data: Option<&'slice [u8]>,
|
raw_data: Option<&'slice [u8]>,
|
||||||
app_data: Option<&'slice [u8]>,
|
app_data: Option<&'slice [u8]>,
|
||||||
crc16: Option<u16>,
|
crc16: Option<u16>,
|
||||||
@ -260,7 +263,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Simplified version of the [PusTc::new] function which allows to only specify service and
|
/// Simplified version of the [PusTc::new] function which allows to only specify service and
|
||||||
/// subservice instead of the full PUS TC secondary header
|
/// subservice instead of the full PUS TC secondary header.
|
||||||
pub fn new_simple(
|
pub fn new_simple(
|
||||||
sph: &mut SpHeader,
|
sph: &mut SpHeader,
|
||||||
service: u8,
|
service: u8,
|
||||||
@ -303,7 +306,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
/// 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.
|
||||||
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_packed() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
|
self.len_packed() as u16 - size_of::<crate::zc::SpHeader>() as u16 - 1;
|
||||||
@ -323,7 +326,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
self.crc16 = Some(digest.finalize())
|
self.crc16 = Some(digest.finalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This helper function calls both [PusTc.update_ccsds_data_len] and [PusTc.calc_own_crc16]
|
/// This helper function calls both [PusTc.update_ccsds_data_len] and [PusTc.calc_own_crc16].
|
||||||
pub fn update_packet_fields(&mut self) {
|
pub fn update_packet_fields(&mut self) {
|
||||||
self.update_ccsds_data_len();
|
self.update_ccsds_data_len();
|
||||||
self.calc_own_crc16();
|
self.calc_own_crc16();
|
||||||
@ -370,6 +373,7 @@ impl<'slice> PusTc<'slice> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||||
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
|
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;
|
||||||
@ -400,7 +404,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: &'slice [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 {
|
||||||
@ -461,7 +465,7 @@ impl PusPacket for PusTc<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
//noinspection RsTraitImplementation
|
||||||
impl PusTcSecondaryHeaderT for PusTc<'_> {
|
impl GenericPusTcSecondaryHeader for PusTc<'_> {
|
||||||
delegate!(to self.sec_header {
|
delegate!(to self.sec_header {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -471,29 +475,29 @@ impl PusTcSecondaryHeaderT for PusTc<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::ecss::PusVersion::PusC;
|
use crate::ecss::PusVersion::PusC;
|
||||||
use crate::ecss::{PusError, PusPacket};
|
use crate::ecss::{PusError, PusPacket};
|
||||||
use crate::tc::ACK_ALL;
|
use crate::tc::ACK_ALL;
|
||||||
use crate::tc::{PusTc, PusTcSecondaryHeader, PusTcSecondaryHeaderT};
|
use crate::tc::{GenericPusTcSecondaryHeader, PusTc, PusTcSecondaryHeader};
|
||||||
use crate::{ByteConversionError, SpHeader};
|
use crate::{ByteConversionError, SpHeader};
|
||||||
use crate::{CcsdsPacket, SequenceFlags};
|
use crate::{CcsdsPacket, SequenceFlags};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
fn base_ping_tc_full_ctor() -> PusTc<'static> {
|
fn base_ping_tc_full_ctor() -> PusTc<'static> {
|
||||||
let mut sph = SpHeader::tc(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);
|
||||||
PusTc::new(&mut sph, tc_header, None, true)
|
PusTc::new(&mut sph, tc_header, None, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_ping_tc_simple_ctor() -> PusTc<'static> {
|
fn base_ping_tc_simple_ctor() -> PusTc<'static> {
|
||||||
let mut sph = SpHeader::tc(0x02, 0x34, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
|
||||||
PusTc::new_simple(&mut sph, 17, 1, None, true)
|
PusTc::new_simple(&mut sph, 17, 1, None, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_ping_tc_simple_ctor_with_app_data(app_data: &'static [u8]) -> PusTc<'static> {
|
fn base_ping_tc_simple_ctor_with_app_data(app_data: &'static [u8]) -> PusTc<'static> {
|
||||||
let mut sph = SpHeader::tc(0x02, 0x34, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
|
||||||
PusTc::new_simple(&mut sph, 17, 1, Some(app_data), true)
|
PusTc::new_simple(&mut sph, 17, 1, Some(app_data), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,7 +537,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_update_func() {
|
fn test_update_func() {
|
||||||
let mut sph = SpHeader::tc(0x02, 0x34, 0).unwrap();
|
let mut sph = SpHeader::tc_unseg(0x02, 0x34, 0).unwrap();
|
||||||
let mut tc = PusTc::new_simple(&mut sph, 17, 1, None, false);
|
let mut tc = PusTc::new_simple(&mut sph, 17, 1, None, false);
|
||||||
tc.calc_crc_on_serialization = false;
|
tc.calc_crc_on_serialization = false;
|
||||||
assert_eq!(tc.data_len(), 0);
|
assert_eq!(tc.data_len(), 0);
|
||||||
@ -559,7 +563,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
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();
|
||||||
@ -624,7 +627,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_buf_too_msall() {
|
fn test_write_buf_too_small() {
|
||||||
let pus_tc = base_ping_tc_simple_ctor();
|
let pus_tc = base_ping_tc_simple_ctor();
|
||||||
let mut test_buf = [0; 12];
|
let mut test_buf = [0; 12];
|
||||||
let res = pus_tc.write_to_bytes(test_buf.as_mut_slice());
|
let res = pus_tc.write_to_bytes(test_buf.as_mut_slice());
|
||||||
@ -697,7 +700,7 @@ mod tests {
|
|||||||
assert_eq!(tc.apid(), 0x02);
|
assert_eq!(tc.apid(), 0x02);
|
||||||
assert_eq!(tc.ack_flags(), ACK_ALL);
|
assert_eq!(tc.ack_flags(), ACK_ALL);
|
||||||
assert_eq!(tc.len_packed(), exp_full_len);
|
assert_eq!(tc.len_packed(), exp_full_len);
|
||||||
let mut comp_header = SpHeader::tc(0x02, 0x34, exp_full_len as u16 - 7).unwrap();
|
let mut comp_header = SpHeader::tc_unseg(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);
|
||||||
}
|
}
|
||||||
|
56
src/time.rs
56
src/time.rs
@ -1,6 +1,6 @@
|
|||||||
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
use crate::{ByteConversionError, SizeMissmatch};
|
use crate::{ByteConversionError, SizeMissmatch};
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, LocalResult, TimeZone, Utc};
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
@ -10,11 +10,15 @@ use crate::time::CcsdsTimeCodes::Cds;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::time::{SystemTime, SystemTimeError};
|
use std::time::{SystemTime, SystemTimeError};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub const CDS_SHORT_LEN: usize = 7;
|
pub const CDS_SHORT_LEN: usize = 7;
|
||||||
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
|
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
|
||||||
pub const SECONDS_PER_DAY: u32 = 86400;
|
pub const SECONDS_PER_DAY: u32 = 86400;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum CcsdsTimeCodes {
|
pub enum CcsdsTimeCodes {
|
||||||
None = 0,
|
None = 0,
|
||||||
CucCcsdsEpoch = 0b001,
|
CucCcsdsEpoch = 0b001,
|
||||||
@ -23,6 +27,8 @@ pub enum CcsdsTimeCodes {
|
|||||||
Ccs = 0b101,
|
Ccs = 0b101,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CDS_SHORT_P_FIELD: u8 = (Cds as u8) << 4;
|
||||||
|
|
||||||
impl TryFrom<u8> for CcsdsTimeCodes {
|
impl TryFrom<u8> for CcsdsTimeCodes {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
@ -39,6 +45,7 @@ impl TryFrom<u8> for CcsdsTimeCodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum TimestampError {
|
pub enum TimestampError {
|
||||||
/// Contains tuple where first value is the expected time code and the second
|
/// Contains tuple where first value is the expected time code and the second
|
||||||
/// value is the found raw value
|
/// value is the found raw value
|
||||||
@ -47,6 +54,7 @@ pub enum TimestampError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[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)
|
||||||
@ -80,7 +88,7 @@ pub trait TimeReader {
|
|||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for generic CCSDS time providers
|
/// Trait for generic CCSDS time providers.
|
||||||
pub trait CcsdsTimeProvider {
|
pub trait CcsdsTimeProvider {
|
||||||
fn len_as_bytes(&self) -> usize;
|
fn len_as_bytes(&self) -> usize;
|
||||||
|
|
||||||
@ -109,23 +117,20 @@ pub trait CcsdsTimeProvider {
|
|||||||
/// timestamp_now.write_to_bytes(&mut raw_stamp).unwrap();
|
/// timestamp_now.write_to_bytes(&mut raw_stamp).unwrap();
|
||||||
/// assert_eq!((raw_stamp[0] >> 4) & 0b111, Cds as u8);
|
/// assert_eq!((raw_stamp[0] >> 4) & 0b111, Cds as u8);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct CdsShortTimeProvider {
|
pub struct CdsShortTimeProvider {
|
||||||
pfield: u8,
|
|
||||||
ccsds_days: u16,
|
ccsds_days: u16,
|
||||||
ms_of_day: u32,
|
ms_of_day: u32,
|
||||||
unix_seconds: i64,
|
unix_seconds: i64,
|
||||||
date_time: Option<DateTime<Utc>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CdsShortTimeProvider {
|
impl CdsShortTimeProvider {
|
||||||
pub fn new(ccsds_days: u16, ms_of_day: u32) -> Self {
|
pub fn new(ccsds_days: u16, ms_of_day: u32) -> Self {
|
||||||
let provider = Self {
|
let provider = Self {
|
||||||
pfield: (Cds as u8) << 4,
|
|
||||||
ccsds_days,
|
ccsds_days,
|
||||||
ms_of_day,
|
ms_of_day,
|
||||||
unix_seconds: 0,
|
unix_seconds: 0,
|
||||||
date_time: None,
|
|
||||||
};
|
};
|
||||||
let unix_days_seconds =
|
let unix_days_seconds =
|
||||||
ccsds_to_unix_days(ccsds_days as i32) as i64 * SECONDS_PER_DAY as i64;
|
ccsds_to_unix_days(ccsds_days as i32) as i64 * SECONDS_PER_DAY as i64;
|
||||||
@ -133,6 +138,7 @@ impl CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||||
pub fn from_now() -> Result<Self, SystemTimeError> {
|
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();
|
||||||
@ -140,17 +146,16 @@ impl CdsShortTimeProvider {
|
|||||||
let unix_days_seconds = epoch - secs_of_day;
|
let unix_days_seconds = epoch - secs_of_day;
|
||||||
let ms_of_day = secs_of_day * 1000 + now.subsec_millis() as u64;
|
let ms_of_day = secs_of_day * 1000 + now.subsec_millis() as u64;
|
||||||
let provider = Self {
|
let provider = Self {
|
||||||
pfield: (Cds as u8) << 4,
|
|
||||||
ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32)
|
ccsds_days: unix_to_ccsds_days((unix_days_seconds / SECONDS_PER_DAY as u64) as i32)
|
||||||
as u16,
|
as u16,
|
||||||
ms_of_day: ms_of_day as u32,
|
ms_of_day: ms_of_day as u32,
|
||||||
unix_seconds: 0,
|
unix_seconds: 0,
|
||||||
date_time: None,
|
|
||||||
};
|
};
|
||||||
Ok(provider.setup(unix_days_seconds as i64, ms_of_day))
|
Ok(provider.setup(unix_days_seconds as i64, ms_of_day))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||||
pub fn update_from_now(&mut self) -> Result<(), SystemTimeError> {
|
pub fn update_from_now(&mut self) -> Result<(), 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();
|
||||||
@ -163,11 +168,11 @@ impl CdsShortTimeProvider {
|
|||||||
|
|
||||||
fn setup(mut self, unix_days_seconds: i64, ms_of_day: u64) -> Self {
|
fn setup(mut self, unix_days_seconds: i64, ms_of_day: u64) -> Self {
|
||||||
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
||||||
self.calc_date_time((ms_of_day % 1000) as u32);
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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 {
|
||||||
Self::ms_of_day(seconds_since_epoch())
|
Self::ms_of_day(seconds_since_epoch())
|
||||||
}
|
}
|
||||||
@ -190,10 +195,13 @@ impl CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_date_time(&mut self, ms_since_last_second: u32) {
|
fn calc_date_time(&self, ms_since_last_second: u32) -> Option<DateTime<Utc>> {
|
||||||
assert!(ms_since_last_second < 1000, "Invalid MS since last second");
|
assert!(ms_since_last_second < 1000, "Invalid MS since last second");
|
||||||
let ns_since_last_sec = ms_since_last_second * 1e6 as u32;
|
let ns_since_last_sec = ms_since_last_second * 1e6 as u32;
|
||||||
self.date_time = Some(Utc.timestamp(self.unix_seconds, ns_since_last_sec));
|
if let LocalResult::Single(val) = Utc.timestamp_opt(self.unix_seconds, ns_since_last_sec) {
|
||||||
|
return Some(val);
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +211,7 @@ impl CcsdsTimeProvider for CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn p_field(&self) -> (usize, [u8; 2]) {
|
fn p_field(&self) -> (usize, [u8; 2]) {
|
||||||
(1, [self.pfield, 0])
|
(1, [CDS_SHORT_P_FIELD, 0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ccdsd_time_code(&self) -> CcsdsTimeCodes {
|
fn ccdsd_time_code(&self) -> CcsdsTimeCodes {
|
||||||
@ -215,7 +223,7 @@ impl CcsdsTimeProvider for CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>> {
|
fn date_time(&self) -> Option<DateTime<Utc>> {
|
||||||
self.date_time
|
self.calc_date_time((self.ms_of_day % 1000) as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +237,7 @@ impl TimeWriter for CdsShortTimeProvider {
|
|||||||
}),
|
}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
buf[0] = self.pfield;
|
buf[0] = CDS_SHORT_P_FIELD;
|
||||||
buf[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice());
|
buf[1..3].copy_from_slice(self.ccsds_days.to_be_bytes().as_slice());
|
||||||
buf[3..7].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice());
|
buf[3..7].copy_from_slice(self.ms_of_day.to_be_bytes().as_slice());
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -260,13 +268,15 @@ impl TimeReader for CdsShortTimeProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError};
|
use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError};
|
||||||
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
||||||
use alloc::format;
|
use alloc::format;
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use postcard::{from_bytes, to_allocvec};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_creation() {
|
fn test_creation() {
|
||||||
@ -274,7 +284,6 @@ mod tests {
|
|||||||
assert_eq!(ccsds_to_unix_days(0), DAYS_CCSDS_TO_UNIX);
|
assert_eq!(ccsds_to_unix_days(0), DAYS_CCSDS_TO_UNIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_current_time() {
|
fn test_get_current_time() {
|
||||||
let sec_floats = seconds_since_epoch();
|
let sec_floats = seconds_since_epoch();
|
||||||
@ -422,7 +431,6 @@ mod tests {
|
|||||||
assert_eq!(read_stamp.ms_of_day, u32::MAX - 1);
|
assert_eq!(read_stamp.ms_of_day, u32::MAX - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now() {
|
fn test_time_now() {
|
||||||
let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
|
let timestamp_now = CdsShortTimeProvider::from_now().unwrap();
|
||||||
@ -448,7 +456,17 @@ mod tests {
|
|||||||
generic_dt_property_equality_check(dt.minute(), compare_stamp.minute(), 0, 59);
|
generic_dt_property_equality_check(dt.minute(), compare_stamp.minute(), 0, 59);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn test_serialization() {
|
||||||
|
let stamp_now = CdsShortTimeProvider::from_now().expect("Error retrieving time");
|
||||||
|
let val = to_allocvec(&stamp_now).expect("Serializing timestamp failed");
|
||||||
|
assert!(val.len() > 0);
|
||||||
|
let stamp_deser: CdsShortTimeProvider =
|
||||||
|
from_bytes(&val).expect("Stamp deserialization failed");
|
||||||
|
assert_eq!(stamp_deser, stamp_now);
|
||||||
|
}
|
||||||
|
|
||||||
fn generic_dt_property_equality_check(first: u32, second: u32, start: u32, end: u32) {
|
fn generic_dt_property_equality_check(first: u32, second: u32, start: u32, end: u32) {
|
||||||
if second < first {
|
if second < first {
|
||||||
assert_eq!(second, start);
|
assert_eq!(second, start);
|
||||||
|
25
src/tm.rs
25
src/tm.rs
@ -9,6 +9,7 @@ use crate::{
|
|||||||
CCSDS_HEADER_LEN,
|
CCSDS_HEADER_LEN,
|
||||||
};
|
};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use zerocopy::AsBytes;
|
use zerocopy::AsBytes;
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ pub const PUC_TM_MIN_SEC_HEADER_LEN: usize = 7;
|
|||||||
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
|
pub const PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA: usize =
|
||||||
CCSDS_HEADER_LEN + PUC_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
|
CCSDS_HEADER_LEN + PUC_TM_MIN_SEC_HEADER_LEN + size_of::<CrcType>();
|
||||||
|
|
||||||
pub trait PusTmSecondaryHeaderT {
|
pub trait GenericPusTmSecondaryHeader {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn sc_time_ref_status(&self) -> u8;
|
fn sc_time_ref_status(&self) -> u8;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -31,6 +32,7 @@ pub trait PusTmSecondaryHeaderT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod zc {
|
pub mod zc {
|
||||||
|
use super::GenericPusTmSecondaryHeader;
|
||||||
use crate::ecss::{PusError, PusVersion};
|
use crate::ecss::{PusError, PusVersion};
|
||||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||||
|
|
||||||
@ -76,7 +78,7 @@ pub mod zc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::PusTmSecondaryHeaderT for PusTmSecHeaderWithoutTimestamp {
|
impl GenericPusTmSecondaryHeader for PusTmSecHeaderWithoutTimestamp {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
PusVersion::try_from(self.pus_version_and_sc_time_ref_status >> 4 & 0b1111)
|
PusVersion::try_from(self.pus_version_and_sc_time_ref_status >> 4 & 0b1111)
|
||||||
.unwrap_or(PusVersion::Invalid)
|
.unwrap_or(PusVersion::Invalid)
|
||||||
@ -104,7 +106,8 @@ pub mod zc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Copy, Clone, Debug)]
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PusTmSecondaryHeader<'slice> {
|
pub struct PusTmSecondaryHeader<'slice> {
|
||||||
pus_version: PusVersion,
|
pus_version: PusVersion,
|
||||||
pub sc_time_ref_status: u8,
|
pub sc_time_ref_status: u8,
|
||||||
@ -147,7 +150,7 @@ impl<'slice> PusTmSecondaryHeader<'slice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusTmSecondaryHeaderT for PusTmSecondaryHeader<'_> {
|
impl GenericPusTmSecondaryHeader for PusTmSecondaryHeader<'_> {
|
||||||
fn pus_version(&self) -> PusVersion {
|
fn pus_version(&self) -> PusVersion {
|
||||||
self.pus_version
|
self.pus_version
|
||||||
}
|
}
|
||||||
@ -198,14 +201,15 @@ impl<'slice> TryFrom<zc::PusTmSecHeader<'slice>> for PusTmSecondaryHeader<'slice
|
|||||||
/// [postcard](https://docs.rs/postcard/latest/postcard/).
|
/// [postcard](https://docs.rs/postcard/latest/postcard/).
|
||||||
///
|
///
|
||||||
/// There is no spare bytes support yet.
|
/// There is no spare bytes support yet.
|
||||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Copy, Clone)]
|
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct PusTm<'slice> {
|
pub struct PusTm<'slice> {
|
||||||
pub sp_header: SpHeader,
|
pub sp_header: SpHeader,
|
||||||
pub sec_header: PusTmSecondaryHeader<'slice>,
|
pub sec_header: PusTmSecondaryHeader<'slice>,
|
||||||
/// 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,
|
||||||
#[serde(skip)]
|
#[cfg_attr(feature = "serde", serde(skip))]
|
||||||
raw_data: Option<&'slice [u8]>,
|
raw_data: Option<&'slice [u8]>,
|
||||||
source_data: Option<&'slice [u8]>,
|
source_data: Option<&'slice [u8]>,
|
||||||
crc16: Option<u16>,
|
crc16: Option<u16>,
|
||||||
@ -352,6 +356,7 @@ impl<'slice> PusTm<'slice> {
|
|||||||
|
|
||||||
/// Append the raw PUS byte representation to a provided [alloc::vec::Vec]
|
/// Append the raw PUS byte representation to a provided [alloc::vec::Vec]
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||||
pub fn append_to_vec(&self, vec: &mut Vec<u8>) -> Result<usize, PusError> {
|
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 =
|
let mut appended_len =
|
||||||
@ -451,7 +456,7 @@ impl PusPacket for PusTm<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//noinspection RsTraitImplementation
|
//noinspection RsTraitImplementation
|
||||||
impl PusTmSecondaryHeaderT for PusTm<'_> {
|
impl GenericPusTmSecondaryHeader for PusTm<'_> {
|
||||||
delegate!(to self.sec_header {
|
delegate!(to self.sec_header {
|
||||||
fn pus_version(&self) -> PusVersion;
|
fn pus_version(&self) -> PusVersion;
|
||||||
fn service(&self) -> u8;
|
fn service(&self) -> u8;
|
||||||
@ -469,13 +474,13 @@ mod tests {
|
|||||||
use crate::SpHeader;
|
use crate::SpHeader;
|
||||||
|
|
||||||
fn base_ping_reply_full_ctor(time_stamp: &[u8]) -> PusTm {
|
fn base_ping_reply_full_ctor(time_stamp: &[u8]) -> PusTm {
|
||||||
let mut sph = SpHeader::tm(0x123, 0x234, 0).unwrap();
|
let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap();
|
||||||
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
|
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, &time_stamp);
|
||||||
PusTm::new(&mut sph, tc_header, None, true)
|
PusTm::new(&mut sph, tc_header, None, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base_hk_reply<'a>(time_stamp: &'a [u8], src_data: &'a [u8]) -> PusTm<'a> {
|
fn base_hk_reply<'a>(time_stamp: &'a [u8], src_data: &'a [u8]) -> PusTm<'a> {
|
||||||
let mut sph = SpHeader::tm(0x123, 0x234, 0).unwrap();
|
let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap();
|
||||||
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, &time_stamp);
|
let tc_header = PusTmSecondaryHeader::new_simple(3, 5, &time_stamp);
|
||||||
PusTm::new(&mut sph, tc_header, Some(src_data), true)
|
PusTm::new(&mut sph, tc_header, Some(src_data), true)
|
||||||
}
|
}
|
||||||
@ -547,7 +552,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_manual_field_update() {
|
fn test_manual_field_update() {
|
||||||
let mut sph = SpHeader::tm(0x123, 0x234, 0).unwrap();
|
let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap();
|
||||||
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_time_stamp());
|
let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_time_stamp());
|
||||||
let mut tm = PusTm::new(&mut sph, tc_header, None, false);
|
let mut tm = PusTm::new(&mut sph, tc_header, None, false);
|
||||||
tm.calc_crc_on_serialization = false;
|
tm.calc_crc_on_serialization = false;
|
||||||
|
Reference in New Issue
Block a user