add docs and minor changes #178

Merged
muellerr merged 1 commits from add-docs-minor-changes into main 2025-10-31 11:45:09 +01:00
15 changed files with 478 additions and 75 deletions

View File

@@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- `CdsCommon` renamed to `CdsBase`
- Simplified CDS short timestamp, contains one less field which reduced serialization length.
- Renamed `UnsignedEnum::value` to `UnsignedEnum::value_raw`, `value` is reserved for the `const`
value getter.
- Renamed `CcsdsPrimaryHeader::from_composite_fields` to
`CcsdsPrimaryHeader::new_from_composite_fields`
## Added

View File

@@ -8,6 +8,7 @@ use std::string::String;
use super::TlvLvDataTooLargeError;
/// Minmum length of a CFDP length-value structure in bytes.
pub const MIN_LV_LEN: usize = 1;
/// Generic CFDP length-value (LV) abstraction as specified in CFDP 5.1.8.
@@ -63,8 +64,10 @@ pub(crate) fn generic_len_check_deserialization(
}
impl<'data> Lv<'data> {
/// Minimum length of a LV structure in bytes.
pub const MIN_LEN: usize = MIN_LV_LEN;
/// Generic constructor.
#[inline]
pub fn new(data: &[u8]) -> Result<Lv<'_>, TlvLvDataTooLargeError> {
if data.len() > u8::MAX as usize {
@@ -118,6 +121,7 @@ impl<'data> Lv<'data> {
self.data.len() == 0
}
/// Raw value part of the LV.
#[inline]
pub fn value(&self) -> &[u8] {
self.data

View File

@@ -13,43 +13,55 @@ pub const CFDP_VERSION_2_NAME: &str = "CCSDS 727.0-B-5";
/// Currently, only this version is supported.
pub const CFDP_VERSION_2: u8 = 0b001;
/// PDU type.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum PduType {
/// File directive PDU.
FileDirective = 0,
/// File data PDU.
FileData = 1,
}
/// PDU direction.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum Direction {
/// Going towards the file receiver.
TowardsReceiver = 0,
/// Going towards the file sender.
TowardsSender = 1,
}
/// PDU transmission mode.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum TransmissionMode {
/// Acknowledged (class 1) transfer.
Acknowledged = 0,
/// Unacknowledged (class 2) transfer.
Unacknowledged = 1,
}
/// CRC flag.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum CrcFlag {
/// No CRC for the packet.
NoCrc = 0,
/// Packet has CRC.
WithCrc = 1,
}
@@ -78,7 +90,9 @@ impl From<CrcFlag> for bool {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum SegmentMetadataFlag {
/// Segment metadata not present.
NotPresent = 0,
/// Segment metadata present.
Present = 1,
}
@@ -89,22 +103,30 @@ pub enum SegmentMetadataFlag {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum SegmentationControl {
/// No record boundary preservation.
NoRecordBoundaryPreservation = 0,
/// With record boundary preservation.
WithRecordBoundaryPreservation = 1,
}
/// Fault handler codes according to the CFDP standard.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u3, exhaustive = false)]
#[repr(u8)]
pub enum FaultHandlerCode {
/// Notice of cancellation fault handler code.
NoticeOfCancellation = 0b0001,
/// Notice of suspension fault handler code.
NoticeOfSuspension = 0b0010,
/// Ignore error fault handler code.
IgnoreError = 0b0011,
/// Abandon transaction fault handler code.
AbandonTransaction = 0b0100,
}
/// CFDP condition codes.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -113,15 +135,25 @@ pub enum FaultHandlerCode {
pub enum ConditionCode {
/// This is not an error condition for which a faulty handler override can be specified
NoError = 0b0000,
/// Positive acknowledgement limit reached.
PositiveAckLimitReached = 0b0001,
/// Keep-alive limit reached.
KeepAliveLimitReached = 0b0010,
/// Invalid transmission mode.
InvalidTransmissionMode = 0b0011,
/// Filestore rejection.
FilestoreRejection = 0b0100,
/// File checksum error.
FileChecksumFailure = 0b0101,
/// File size error.
FileSizeError = 0b0110,
/// NAK limit reached.
NakLimitReached = 0b0111,
/// Inactivity detected.
InactivityDetected = 0b1000,
/// Check limit reached.
CheckLimitReached = 0b1010,
/// Unsupported checksum type.
UnsupportedChecksumType = 0b1011,
/// Not an actual fault condition for which fault handler overrides can be specified
SuspendRequestReceived = 0b1110,
@@ -129,6 +161,7 @@ pub enum ConditionCode {
CancelRequestReceived = 0b1111,
}
/// Large file flag.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -151,6 +184,7 @@ pub enum TransactionStatus {
/// Transaction is not currently active and the CFDP implementation does not retain a
/// transaction history.
Undefined = 0b00,
/// Transaction is currently active.
Active = 0b01,
/// Transaction was active in the past and was terminated.
Terminated = 0b10,
@@ -168,10 +202,13 @@ pub enum TransactionStatus {
pub enum ChecksumType {
/// Modular legacy checksum
Modular = 0,
/// CRC32 Proximity-1.
Crc32Proximity1 = 1,
/// CRC32C.
Crc32C = 2,
/// Polynomial: 0x4C11DB7. Preferred checksum for now.
/// CRC32. Polynomial: 0x4C11DB7. Preferred checksum for now.
Crc32 = 3,
/// Null checksum (no checksum).
NullChecksum = 15,
}
@@ -181,8 +218,10 @@ impl Default for ChecksumType {
}
}
/// Raw null checksum.
pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
/// TLV or LV data larger than allowed [u8::MAX].
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -199,16 +238,21 @@ pub struct InvalidTlvTypeFieldError {
expected: Option<u8>,
}
/// Generic TLV/LV error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvLvError {
/// Data too large error.
#[error("{0}")]
DataTooLarge(#[from] TlvLvDataTooLargeError),
/// Byte conversion error.
#[error("byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError),
/// Invalid TLV type field error.
#[error("{0}")]
InvalidTlvTypeField(#[from] InvalidTlvTypeFieldError),
/// Invalid value length.
#[error("invalid value length {0}")]
InvalidValueLength(usize),
/// Only applies to filestore requests and responses. Second name was missing where one is

View File

@@ -626,7 +626,7 @@ mod tests {
assert_eq!(finished_pdu_vec.len(), 12);
assert_eq!(finished_pdu_vec[9], TlvType::EntityId.into());
assert_eq!(finished_pdu_vec[10], 1);
assert_eq!(finished_pdu_vec[11], TEST_DEST_ID.value_typed());
assert_eq!(finished_pdu_vec[11], TEST_DEST_ID.value());
assert_eq!(
finished_pdu.fault_location().unwrap().entity_id(),
&TEST_DEST_ID.into()

View File

@@ -19,9 +19,11 @@ use super::{InvalidTlvTypeFieldError, TlvLvDataTooLargeError};
pub mod msg_to_user;
/// Minimum length of a type-length-value structure, including type and length fields.
pub const MIN_TLV_LEN: usize = 2;
pub trait GenericTlv {
/// TLV type field.
fn tlv_type_field(&self) -> TlvTypeField;
/// Checks whether the type field contains one of the standard types specified in the CFDP
@@ -45,7 +47,9 @@ pub trait GenericTlv {
}
}
/// Readable TLV structure trait.
pub trait ReadableTlv {
/// Value field of the TLV.
fn value(&self) -> &[u8];
/// Checks whether the value field is empty.
@@ -68,9 +72,15 @@ pub trait ReadableTlv {
}
}
/// Writable TLV structure trait.
pub trait WritableTlv {
/// Write the TLV to bytes.
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
/// Length of the written TLV.
fn len_written(&self) -> usize;
/// Convenience method to write the TLV to an owned [alloc::vec::Vec].
#[cfg(feature = "alloc")]
fn to_vec(&self) -> Vec<u8> {
let mut buf = vec![0; self.len_written()];
@@ -79,16 +89,23 @@ pub trait WritableTlv {
}
}
/// TLV type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum TlvType {
/// Filestore request.
FilestoreRequest = 0x00,
/// Filestore response.
FilestoreResponse = 0x01,
/// Message to user.
MsgToUser = 0x02,
/// Fault handler.
FaultHandler = 0x04,
/// Flow label.
FlowLabel = 0x05,
/// Entity ID.
EntityId = 0x06,
}

View File

@@ -7,13 +7,21 @@ use serde::{Deserialize, Serialize};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum Subservice {
/// Telemetry - Info report.
TmInfoReport = 1,
/// Telemetry - Low severity report.
TmLowSeverityReport = 2,
/// Telemetry - Medium severity report.
TmMediumSeverityReport = 3,
/// Telemetry - High severity report.
TmHighSeverityReport = 4,
/// Telecommand - Enable event generation.
TcEnableEventGeneration = 5,
/// Telecommand - Disable event generation.
TcDisableEventGeneration = 6,
/// Telecommand - Report disabled list.
TcReportDisabledList = 7,
/// Telemetry - Disabled events report.
TmDisabledEventsReport = 8,
}

View File

@@ -9,13 +9,21 @@ use serde::{Deserialize, Serialize};
#[repr(u8)]
pub enum Subservice {
// Regular HK
/// Telecommand - Create Housekeeping Report Structure.
TcCreateHkReportStructure = 1,
/// Telecommand - Delete HK report structures.
TcDeleteHkReportStructures = 3,
/// Telecommand - Enable HK generation.
TcEnableHkGeneration = 5,
/// Telecommand - Disable HK generation.
TcDisableHkGeneration = 6,
/// Telecommand - Report HK report structures.
TcReportHkReportStructures = 9,
/// Telemetry - HK report.
TmHkPacket = 25,
/// Telecommand - Generate one-shot report.
TcGenerateOneShotHk = 27,
/// Telecommand - Modify collection interval.
TcModifyHkCollectionInterval = 31,
// Diagnostics HK

View File

@@ -25,53 +25,55 @@ pub mod tm;
pub mod tm_pus_a;
pub mod verification;
/// Type alias for the CRC16 type.
pub type CrcType = u16;
/// Standard PUS service IDs.
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
#[non_exhaustive]
pub enum PusServiceId {
/// Service 1
/// Service 1 Verification
Verification = 1,
/// Service 2
/// Service 2 Device Access
DeviceAccess = 2,
/// Service 3
/// Service 3 Housekeeping
Housekeeping = 3,
/// Service 4
/// Service 4 Parameter Statistics
ParameterStatistics = 4,
/// Service 5
/// Service 5 Event
Event = 5,
/// Service 6
/// Service 6 Memory Management
MemoryManagement = 6,
/// Service 8
/// Service 8 Action
Action = 8,
/// Service 9
/// Service 9 Time Management
TimeManagement = 9,
/// Service 11
/// Service 11 Scheduling
Scheduling = 11,
/// Service 12
/// Service 12 On-Board Monitoring
OnBoardMonitoring = 12,
/// Service 13
/// Service 13 Large Packet Transfer
LargePacketTransfer = 13,
/// Service 14
/// Service 14 Real-Time Forwarding Control
RealTimeForwardingControl = 14,
/// Service 15
/// Service 15 Storage And Retrival
StorageAndRetrival = 15,
/// Service 17
/// Service 17 Test
Test = 17,
/// Service 18
/// Service 18 Operations And Procedures
OpsAndProcedures = 18,
/// Service 19
/// Service 19 Event Action
EventAction = 19,
/// Service 20
/// Service 20 Parameter
Parameter = 20,
/// Service 21
/// Service 21 Request Sequencing
RequestSequencing = 21,
/// Service 22
/// Service 22 Position Based Scheduling
PositionBasedScheduling = 22,
/// Service 23
/// Service 23 File Management
FileManagement = 23,
}
@@ -83,8 +85,11 @@ pub enum PusServiceId {
#[repr(u8)]
#[non_exhaustive]
pub enum PusVersion {
/// ESA PUS
EsaPus = 0,
/// PUS A
PusA = 1,
/// PUS C
PusC = 2,
}
@@ -107,20 +112,33 @@ impl TryFrom<u4> for PusVersion {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum PacketTypeCodes {
/// Boolean.
Boolean = 1,
/// Enumerated.
Enumerated = 2,
/// Unsigned Integer.
UnsignedInt = 3,
/// Signed Integer.
SignedInt = 4,
/// Real (floating point).
Real = 5,
/// Bit string.
BitString = 6,
/// Octet (byte) string.
OctetString = 7,
/// Character string.
CharString = 8,
/// Absolute time.
AbsoluteTime = 9,
/// Relative time.
RelativeTime = 10,
/// Deduced.
Deduced = 11,
/// Packet.
Packet = 12,
}
/// Type alias for the ECSS Packet Type Codes (PTC)s.
pub type Ptc = PacketTypeCodes;
/// ECSS Packet Field Codes (PFC)s for the unsigned [Ptc].
@@ -129,15 +147,25 @@ pub type Ptc = PacketTypeCodes;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum PfcUnsigned {
/// 1 byte.
OneByte = 4,
/// 12 bits.
TwelveBits = 8,
/// 2 bytes.
TwoBytes = 12,
/// 3 bytes.
ThreeBytes = 13,
/// 4 bytes.
FourBytes = 14,
/// 6 bytes.
SixBytes = 15,
/// 8 bytes.
EightBytes = 16,
/// 1 bit.
OneBit = 17,
/// 2 bits.
TwoBits = 18,
/// 3 bits.
ThreeBits = 19,
}
@@ -157,12 +185,15 @@ pub enum PfcReal {
DoubleMilStd = 4,
}
/// Generic PUS error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PusError {
/// PUS version is not supported.
#[error("PUS version {0:?} not supported")]
VersionNotSupported(u4),
/// Checksum failure.
#[error("checksum verification for crc16 {0:#06x} failed")]
ChecksumFailure(u16),
/// CRC16 needs to be calculated first
@@ -175,12 +206,21 @@ pub enum 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 {
/// PUS version.
fn pus_version(&self) -> Result<PusVersion, u4>;
/// Service ID.
fn service(&self) -> u8;
/// Subservice ID.
fn subservice(&self) -> u8;
/// User data field.
fn user_data(&self) -> &[u8];
/// CRC-16-CCITT checksum.
fn checksum(&self) -> Option<u16>;
/// The presence of the CRC-16-CCITT checksum is optional.
fn has_checksum(&self) -> bool {
self.checksum().is_some()
@@ -226,6 +266,7 @@ pub(crate) fn user_data_from_raw(
}
}
/// Verify the CRC16 of a raw packet and return a [PusError] on failure.
pub fn verify_crc16_ccitt_false_from_raw_to_pus_error(
raw_data: &[u8],
crc16: u16,
@@ -235,6 +276,8 @@ pub fn verify_crc16_ccitt_false_from_raw_to_pus_error(
.ok_or(PusError::ChecksumFailure(crc16))
}
/// Verify the CRC16 of a raw packet using a table-less implementation and return a [PusError] on
/// failure.
pub fn verify_crc16_ccitt_false_from_raw_to_pus_error_no_table(
raw_data: &[u8],
crc16: u16,
@@ -267,10 +310,15 @@ pub fn verify_crc16_ccitt_false_from_raw_no_table(raw_data: &[u8]) -> bool {
macro_rules! sp_header_impls {
() => {
delegate!(to self.sp_header {
/// Set the CCSDS APID.
#[inline]
pub fn set_apid(&mut self, apid: u11);
/// Set the CCSDS sequence count.
#[inline]
pub fn set_seq_count(&mut self, seq_count: u14);
/// Set the CCSDS sequence flags.
#[inline]
pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags);
});
@@ -289,27 +337,28 @@ pub trait EcssEnumeration: UnsignedEnum {
fn pfc(&self) -> u8;
}
/// Extension trait for [EcssEnumeration] which adds common trait bounds.
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
/// ECSS enumerated type wrapper.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GenericEcssEnumWrapper<TYPE: Copy + Into<u64>> {
field: GenericUnsignedByteField<TYPE>,
}
pub struct GenericEcssEnumWrapper<TYPE: Copy + Into<u64>>(GenericUnsignedByteField<TYPE>);
impl<TYPE: Copy + Into<u64>> GenericEcssEnumWrapper<TYPE> {
/// Returns [PacketTypeCodes::Enumerated].
pub const fn ptc() -> PacketTypeCodes {
PacketTypeCodes::Enumerated
}
pub const fn value_typed(&self) -> TYPE {
self.field.value_typed()
/// Value.
pub const fn value(&self) -> TYPE {
self.0.value()
}
pub fn new(val: TYPE) -> Self {
Self {
field: GenericUnsignedByteField::new(val),
}
/// Generic constructor.
pub const fn new(val: TYPE) -> Self {
Self(GenericUnsignedByteField::new(val))
}
}
@@ -319,11 +368,11 @@ impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericEcssEnumWrapper
}
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
self.field.write_to_be_bytes(buf)
self.0.write_to_be_bytes(buf)
}
fn value(&self) -> u64 {
self.field.value()
fn value_raw(&self) -> u64 {
self.0.value().into()
}
}
@@ -347,11 +396,12 @@ impl<T: Copy + Into<u64>> From<T> for GenericEcssEnumWrapper<T> {
macro_rules! generic_ecss_enum_typedefs_and_from_impls {
($($ty:ty => $Enum:ident),*) => {
$(
/// Type alias for ECSS enumeration wrapper around `$ty`
pub type $Enum = GenericEcssEnumWrapper<$ty>;
impl From<$Enum> for $ty {
fn from(value: $Enum) -> Self {
value.value_typed()
value.value()
}
}
)*
@@ -412,6 +462,7 @@ pub trait WritablePusPacket {
Ok(curr_idx)
}
/// Converts the packet into an owned [alloc::vec::Vec].
#[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, PusError> {
// This is the correct way to do this. See
@@ -423,6 +474,7 @@ pub trait WritablePusPacket {
}
}
/// PUS packet creator configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -465,7 +517,7 @@ mod tests {
.expect("To byte conversion of u8 failed");
assert_eq!(buf[1], 1);
assert_eq!(my_enum.value(), 1);
assert_eq!(my_enum.value_typed(), 1);
assert_eq!(my_enum.value(), 1);
let enum_as_u8: u8 = my_enum.into();
assert_eq!(enum_as_u8, 1);
let vec = my_enum.to_vec();
@@ -484,7 +536,7 @@ mod tests {
assert_eq!(buf[1], 0x1f);
assert_eq!(buf[2], 0x2f);
assert_eq!(my_enum.value(), 0x1f2f);
assert_eq!(my_enum.value_typed(), 0x1f2f);
assert_eq!(my_enum.value(), 0x1f2f);
let enum_as_raw: u16 = my_enum.into();
assert_eq!(enum_as_raw, 0x1f2f);
let vec = my_enum.to_vec();
@@ -521,7 +573,7 @@ mod tests {
assert_eq!(buf[3], 0x3f);
assert_eq!(buf[4], 0x4f);
assert_eq!(my_enum.value(), 0x1f2f3f4f);
assert_eq!(my_enum.value_typed(), 0x1f2f3f4f);
assert_eq!(my_enum.value(), 0x1f2f3f4f);
let enum_as_raw: u32 = my_enum.into();
assert_eq!(enum_as_raw, 0x1f2f3f4f);
let vec = my_enum.to_vec();
@@ -559,7 +611,7 @@ mod tests {
assert_eq!(buf[6], 0x4f);
assert_eq!(buf[7], 0x5f);
assert_eq!(my_enum.value(), 0x1f2f3f4f5f);
assert_eq!(my_enum.value_typed(), 0x1f2f3f4f5f);
assert_eq!(my_enum.value(), 0x1f2f3f4f5f);
let enum_as_raw: u64 = my_enum.into();
assert_eq!(enum_as_raw, 0x1f2f3f4f5f);
assert_eq!(u64::from_be_bytes(buf), 0x1f2f3f4f5f);

View File

@@ -61,6 +61,8 @@
//! ```
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
// TODO: Add docs everywhere.
//#![warn(missing_docs)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(any(feature = "std", test))]
@@ -102,6 +104,7 @@ pub const MAX_SEQ_COUNT: u14 = u14::MAX;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ChecksumType {
/// Default CRC16-CCITT checksum.
Crc16CcittFalse,
}
@@ -112,12 +115,23 @@ pub enum ChecksumType {
pub enum ByteConversionError {
/// The passed slice is too small. Returns the passed slice length and expected minimum size
#[error("target slice with size {found} is too small, expected size of at least {expected}")]
ToSliceTooSmall { found: usize, expected: usize },
ToSliceTooSmall {
/// Found slice size.
found: usize,
/// Expected slice size.
expected: usize,
},
/// The provider buffer is too small. Returns the passed slice length and expected minimum size
#[error("source slice with size {found} too small, expected at least {expected} bytes")]
FromSliceTooSmall { found: usize, expected: usize },
FromSliceTooSmall {
/// Found slice size.
found: usize,
/// Expected slice size.
expected: usize,
},
}
/// [zerocopy] serialization and deserialization errors.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -137,12 +151,15 @@ pub enum ZeroCopyError {
#[error("invalid payload length: {0}")]
pub struct InvalidPayloadLengthError(usize);
/// Errors during CCSDS packet creation.
#[derive(thiserror::Error, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CcsdsPacketCreationError {
/// Byte conversion error.
#[error("byte conversion: {0}")]
ByteConversion(#[from] ByteConversionError),
/// Invalid payload length which exceeded [u16::MAX].
#[error("invalid payload length: {0}")]
InvalidPayloadLength(#[from] InvalidPayloadLengthError),
}
@@ -154,22 +171,30 @@ pub enum CcsdsPacketCreationError {
#[bitbybit::bitenum(u1, exhaustive = true)]
#[repr(u8)]
pub enum PacketType {
/// Telemetry packet.
Tm = 0,
/// Telecommand packet.
Tc = 1,
}
/// CCSDS packet sequence flags.
#[derive(Debug, PartialEq, Eq, num_enum::TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u2, exhaustive = true)]
#[repr(u8)]
pub enum SequenceFlags {
/// Continuation segment of a segmented packet.
ContinuationSegment = 0b00,
/// First segment of a sequence.
FirstSegment = 0b01,
/// Last segment of a sequence.
LastSegment = 0b10,
/// Unsegmented packet.
Unsegmented = 0b11,
}
/// Retrieve the [PacketType] from a raw packet ID.
#[inline]
pub fn packet_type_in_raw_packet_id(packet_id: u16) -> PacketType {
PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap()
@@ -200,6 +225,9 @@ pub const fn ccsds_packet_len_for_user_data_len(
Some(len)
}
/// Calculate the full CCSDS packet length for a given user data length.
///
/// Returns [None] if the packet length exceeds the maximum allowed size [u16::MAX].
#[inline]
pub fn ccsds_packet_len_for_user_data_len_with_checksum(data_len: usize) -> Option<usize> {
ccsds_packet_len_for_user_data_len(data_len, Some(ChecksumType::Crc16CcittFalse))
@@ -211,8 +239,11 @@ pub fn ccsds_packet_len_for_user_data_len_with_checksum(data_len: usize) -> Opti
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketId {
/// Packet type (telemetry or telecommand).
pub packet_type: PacketType,
/// Secondary header flag.
pub sec_header_flag: bool,
/// Application Process ID (APID).
pub apid: u11,
}
@@ -256,16 +287,19 @@ impl Default for PacketId {
}
impl PacketId {
/// Generic constructor for telecommands.
#[inline]
pub const fn new_for_tc(sec_header: bool, apid: u11) -> Self {
Self::new(PacketType::Tc, sec_header, apid)
}
/// Generic constructor for telemetry.
#[inline]
pub const fn new_for_tm(sec_header: bool, apid: u11) -> Self {
Self::new(PacketType::Tm, sec_header, apid)
}
/// Generic constructor.
#[inline]
pub const fn new(packet_type: PacketType, sec_header_flag: bool, apid: u11) -> Self {
PacketId {
@@ -289,6 +323,7 @@ impl PacketId {
self.apid
}
/// Raw numeric value.
#[inline]
pub const fn raw(&self) -> u16 {
((self.packet_type as u16) << 12)
@@ -307,6 +342,7 @@ impl From<u16> for PacketId {
}
}
/// Deprecated type alias.
#[deprecated(since = "0.16.0", note = "use PacketSequenceControl instead")]
pub type PacketSequenceCtrl = PacketSequenceControl;
@@ -316,11 +352,14 @@ pub type PacketSequenceCtrl = PacketSequenceControl;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketSequenceControl {
/// CCSDS sequence flags.
pub seq_flags: SequenceFlags,
/// CCSDS sequence count.
pub seq_count: u14,
}
impl PacketSequenceControl {
/// Generic constructor.
#[inline]
pub const fn new(seq_flags: SequenceFlags, seq_count: u14) -> PacketSequenceControl {
PacketSequenceControl {
@@ -329,6 +368,7 @@ impl PacketSequenceControl {
}
}
/// Raw value.
#[inline]
pub const fn raw(&self) -> u16 {
((self.seq_flags as u16) << 14) | self.seq_count.value()
@@ -348,7 +388,7 @@ macro_rules! sph_from_other {
($Self: path, $other: path) => {
impl From<$other> for $Self {
fn from(other: $other) -> Self {
Self::from_composite_fields(
Self::new_from_composite_fields(
other.packet_id(),
other.psc(),
other.data_len(),
@@ -364,19 +404,33 @@ const VERSION_MASK: u16 = 0xE000;
/// Generic trait to access fields of a CCSDS space packet header according to CCSDS 133.0-B-2.
pub trait CcsdsPacket {
/// CCSDS version field.
fn ccsds_version(&self) -> u3;
/// CCSDS packet ID.
///
/// First two bytes of the CCSDS primary header without the first three bits.
fn packet_id(&self) -> PacketId;
/// CCSDS packet sequence control.
///
/// Third and fourth byte of the CCSDS primary header.
fn psc(&self) -> PacketSequenceControl;
/// Retrieve data length field
/// Data length field.
///
/// Please note that this is NOT the full packet length.
/// The full length can be calculated by adding the header length [CCSDS_HEADER_LEN] + 1 or
/// using [Self::packet_len].
fn data_len(&self) -> u16;
/// Retrieve the total packet size based on the data length field
/// Total packet size based on the data length field
#[inline]
fn packet_len(&self) -> usize {
usize::from(self.data_len()) + CCSDS_HEADER_LEN + 1
}
/// Deprecated alias for [Self::packet_len].
#[deprecated(since = "0.16.0", note = "use packet_len instead")]
#[inline]
fn total_len(&self) -> usize {
@@ -395,40 +449,45 @@ pub trait CcsdsPacket {
self.psc().raw()
}
/// CCSDS packet type.
#[inline]
fn packet_type(&self) -> PacketType {
// This call should never fail because only 0 and 1 can be passed to the try_from call
self.packet_id().packet_type
}
/// Is this a telemetry packet?
#[inline]
fn is_tm(&self) -> bool {
self.packet_type() == PacketType::Tm
}
/// Is this a telecommand packet?
#[inline]
fn is_tc(&self) -> bool {
self.packet_type() == PacketType::Tc
}
/// Retrieve the secondary header flag. Returns true if a secondary header is present
/// CCSDS secondary header flag. Returns true if a secondary header is present
/// and false if it is not.
#[inline]
fn sec_header_flag(&self) -> bool {
self.packet_id().sec_header_flag
}
/// Retrieve Application Process ID.
/// CCSDS Application Process ID (APID).
#[inline]
fn apid(&self) -> u11 {
self.packet_id().apid
}
/// CCSDS sequence count.
#[inline]
fn seq_count(&self) -> u14 {
self.psc().seq_count
}
/// CCSDS sequence flags.
#[inline]
fn sequence_flags(&self) -> SequenceFlags {
// This call should never fail because the mask ensures that only valid values are passed
@@ -437,8 +496,10 @@ pub trait CcsdsPacket {
}
}
/// Helper trait to generate the primary header from the composite fields.
pub trait CcsdsPrimaryHeader {
fn from_composite_fields(
/// Constructor.
fn new_from_composite_fields(
packet_id: PacketId,
psc: PacketSequenceControl,
data_len: u16,
@@ -447,25 +508,23 @@ pub trait CcsdsPrimaryHeader {
}
/// Space Packet Primary Header according to CCSDS 133.0-B-2.
///
/// # Arguments
///
/// * `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
/// 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
/// * `data_len` - Data length field occupies the fifth and the sixth byte of the raw header
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpacePacketHeader {
/// 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.
pub version: u3,
/// CCSDS 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
pub packet_id: PacketId,
/// CCSDS Packet Sequence Control, occupies the third and fourth byte of the raw header
pub psc: PacketSequenceControl,
/// Data length field occupies the fifth and the sixth byte of the raw header
pub data_len: u16,
}
/// Alias for [SpacePacketHeader].
pub type SpHeader = SpacePacketHeader;
impl Default for SpacePacketHeader {
@@ -486,8 +545,10 @@ impl Default for SpacePacketHeader {
}
impl SpacePacketHeader {
/// Length of the CCSDS primary header.
pub const LENGTH: usize = CCSDS_HEADER_LEN;
/// Generic constructor.
#[inline]
pub const fn new(packet_id: PacketId, psc: PacketSequenceControl, data_len: u16) -> Self {
Self {
@@ -513,6 +574,7 @@ impl SpacePacketHeader {
}
}
/// Constructor from individual fields.
#[inline]
pub const fn new_from_fields(
ptype: PacketType,
@@ -530,6 +592,7 @@ impl SpacePacketHeader {
}
}
/// Constructor for telemetry packets.
#[inline]
pub const fn new_for_tm(
apid: u11,
@@ -540,6 +603,7 @@ impl SpacePacketHeader {
Self::new_from_fields(PacketType::Tm, false, apid, seq_flags, seq_count, data_len)
}
/// Constructor for telecommand packets.
#[inline]
pub const fn new_for_tc(
apid: u11,
@@ -564,6 +628,7 @@ impl SpacePacketHeader {
delegate! {
to self.packet_id {
/// Set the application process ID (APID).
#[inline]
pub fn set_apid(&mut self, apid: u11);
}
@@ -575,26 +640,31 @@ impl SpacePacketHeader {
usize::from(self.data_len()) + Self::LENGTH + 1
}
/// Set the CCSDS sequence count.
#[inline]
pub fn set_seq_count(&mut self, seq_count: u14) {
self.psc.seq_count = seq_count;
}
/// Set the CCSDS sequence flags.
#[inline]
pub fn set_seq_flags(&mut self, seq_flags: SequenceFlags) {
self.psc.seq_flags = seq_flags;
}
/// Set the CCSDS secondary header flag.
#[inline]
pub fn set_sec_header_flag(&mut self) {
self.packet_id.sec_header_flag = true;
}
/// Clear the CCSDS secondary header flag.
#[inline]
pub fn clear_sec_header_flag(&mut self) {
self.packet_id.sec_header_flag = false;
}
/// Set the CCSDS packet type.
#[inline]
pub fn set_packet_type(&mut self, packet_type: PacketType) {
self.packet_id.packet_type = packet_type;
@@ -677,7 +747,7 @@ impl CcsdsPacket for SpacePacketHeader {
impl CcsdsPrimaryHeader for SpacePacketHeader {
#[inline]
fn from_composite_fields(
fn new_from_composite_fields(
packet_id: PacketId,
psc: PacketSequenceControl,
data_len: u16,
@@ -698,6 +768,7 @@ impl CcsdsPrimaryHeader for SpacePacketHeader {
sph_from_other!(SpHeader, crate::zc::SpHeader);
/// [zerocopy] based CCSDS Space Packet Primary Header implementation.
pub mod zc {
use crate::{CcsdsPacket, CcsdsPrimaryHeader, PacketId, PacketSequenceControl, VERSION_MASK};
use arbitrary_int::traits::Integer;
@@ -705,6 +776,7 @@ pub mod zc {
use zerocopy::byteorder::NetworkEndian;
use zerocopy::{FromBytes, Immutable, IntoBytes, Unaligned, U16};
/// [zerocopy] space packet header.
#[derive(FromBytes, IntoBytes, Immutable, Unaligned, Debug)]
#[repr(C)]
pub struct SpHeader {
@@ -714,6 +786,7 @@ pub mod zc {
}
impl SpHeader {
/// Generic constructor.
pub fn new(
packet_id: PacketId,
psc: PacketSequenceControl,
@@ -769,7 +842,7 @@ pub mod zc {
}
impl CcsdsPrimaryHeader for SpHeader {
fn from_composite_fields(
fn new_from_composite_fields(
packet_id: PacketId,
psc: PacketSequenceControl,
data_len: u16,
@@ -799,8 +872,11 @@ pub struct CcsdsPacketCreatorWithReservedData<'buf> {
}
impl<'buf> CcsdsPacketCreatorWithReservedData<'buf> {
/// CCSDS header length.
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
/// Calculate the full CCSDS packet length for a given user data length and with a CRC16
/// checksum.
#[inline]
pub fn packet_len_for_user_data_with_checksum(user_data_len: usize) -> Option<usize> {
ccsds_packet_len_for_user_data_len(user_data_len, Some(ChecksumType::Crc16CcittFalse))
@@ -896,11 +972,13 @@ impl<'buf> CcsdsPacketCreatorWithReservedData<'buf> {
}
impl CcsdsPacketCreatorWithReservedData<'_> {
/// Raw full buffer this packet is constructed in.
#[inline]
pub fn raw_buffer(&self) -> &[u8] {
self.buf
}
/// Full packet length.
#[inline]
pub fn packet_len(&self) -> usize {
<Self as CcsdsPacket>::packet_len(self)
@@ -986,7 +1064,9 @@ impl CcsdsPacket for CcsdsPacketCreatorWithReservedData<'_> {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CcsdsPacketId {
/// CCSDS Packet ID.
pub packet_id: PacketId,
/// CCSDS Packet Sequence Control.
pub psc: PacketSequenceControl,
}
@@ -998,6 +1078,7 @@ impl Hash for CcsdsPacketId {
}
impl CcsdsPacketId {
/// Generic constructor.
#[inline]
pub const fn new(packet_id: PacketId, psc: PacketSequenceControl) -> Self {
Self { packet_id, psc }
@@ -1012,6 +1093,7 @@ impl CcsdsPacketId {
}
}
/// Raw numeric value.
#[inline]
pub const fn raw(&self) -> u32 {
((self.packet_id.raw() as u32) << 16) | self.psc.raw() as u32
@@ -1133,6 +1215,7 @@ pub struct CcsdsPacketCreator<'app_data> {
}
impl<'app_data> CcsdsPacketCreator<'app_data> {
/// CCSDS header length.
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
/// Helper function which can be used to determine the full packet length from the user
@@ -1211,6 +1294,7 @@ impl CcsdsPacketCreator<'_> {
.write_to_bytes(buf, self.len_written(), self.packet_data)
}
/// CCSDS space packet header.
#[inline]
pub fn sp_header(&self) -> &SpHeader {
&self.common.sp_header
@@ -1256,7 +1340,9 @@ pub struct CcsdsPacketCreatorOwned {
packet_data: alloc::vec::Vec<u8>,
}
#[cfg(feature = "alloc")]
impl CcsdsPacketCreatorOwned {
/// CCSDS header length.
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
/// Helper function which can be used to determine the full packet length from the user
@@ -1332,6 +1418,7 @@ impl CcsdsPacketCreatorOwned {
.write_to_bytes(buf, self.len_written(), &self.packet_data)
}
/// CCSDS space packet header.
#[inline]
pub fn sp_header(&self) -> &SpHeader {
&self.common.sp_header
@@ -1344,6 +1431,7 @@ impl CcsdsPacketCreatorOwned {
}
}
#[cfg(feature = "alloc")]
impl CcsdsPacket for CcsdsPacketCreatorOwned {
/// CCSDS version field.
#[inline]
@@ -1370,12 +1458,15 @@ impl CcsdsPacket for CcsdsPacketCreatorOwned {
}
}
/// CCSDS packet read error.
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CcsdsPacketReadError {
/// Byte conversion error.
#[error("byte conversion: {0}")]
ByteConversion(#[from] ByteConversionError),
/// CRC error.
#[error("CRC error")]
CrcError,
}
@@ -1390,14 +1481,17 @@ pub struct CcsdsPacketReader<'buf> {
}
impl<'buf> CcsdsPacketReader<'buf> {
/// CCSDS header length.
pub const HEADER_LEN: usize = CCSDS_HEADER_LEN;
/// Constructor which expects a CRC16 checksum.
pub fn new_with_checksum(
buf: &'buf [u8],
) -> Result<CcsdsPacketReader<'buf>, CcsdsPacketReadError> {
Self::new(buf, Some(ChecksumType::Crc16CcittFalse))
}
/// Generic constructor.
pub fn new(
buf: &'buf [u8],
checksum: Option<ChecksumType>,
@@ -1672,7 +1766,7 @@ pub(crate) mod tests {
assert_eq!(sp_header.data_len(), 36);
assert_eq!(sp_header.ccsds_version().value(), 0b000);
let from_comp_fields = SpHeader::from_composite_fields(
let from_comp_fields = SpHeader::new_from_composite_fields(
PacketId::new(PacketType::Tc, true, u11::new(0x42)),
PacketSequenceControl::new(SequenceFlags::Unsegmented, u14::new(0x7)),
0,

View File

@@ -14,28 +14,37 @@ use paste::paste;
/// static structs when using the interior mutability pattern. This can be achieved by using
/// [Cell], [core::cell::RefCell] or atomic types.
pub trait SequenceCounter {
/// Raw type of the counter.
type Raw: Into<u64>;
/// Bit width of the counter.
const MAX_BIT_WIDTH: usize;
/// Get the current sequence count value.
fn get(&self) -> Self::Raw;
/// Increment the sequence count by one.
fn increment(&self);
/// Increment the sequence count by one, mutable API.
fn increment_mut(&mut self) {
self.increment();
}
/// Get the current sequence count value and increment the counter by one.
fn get_and_increment(&self) -> Self::Raw {
let val = self.get();
self.increment();
val
}
/// Get the current sequence count value and increment the counter by one, mutable API.
fn get_and_increment_mut(&mut self) -> Self::Raw {
self.get_and_increment()
}
}
/// Simple sequence counter which wraps at [T::MAX].
#[derive(Clone)]
pub struct SequenceCounterSimple<T: Copy> {
seq_count: Cell<T>,
@@ -48,12 +57,15 @@ macro_rules! impl_for_primitives {
$(
paste! {
impl SequenceCounterSimple<$ty> {
/// Constructor with a custom maximum value.
pub fn [<new_custom_max_val_ $ty>](max_val: $ty) -> Self {
Self {
seq_count: Cell::new(0),
max_val,
}
}
/// Generic constructor.
pub fn [<new_ $ty>]() -> Self {
Self {
seq_count: Cell::new(0),
@@ -275,6 +287,7 @@ macro_rules! sync_clonable_seq_counter_impl {
}
impl [<SequenceCounterSyncCustomWrap $ty:upper>] {
/// Generic constructor.
pub fn new(max_val: $ty) -> Self {
Self {
seq_count: core::sync::atomic::[<Atomic $ty:upper>]::new(0),

View File

@@ -31,6 +31,7 @@ pub const FMT_STR_CODE_B_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3f", 21);
/// Three digits are used for the decimal fraction and a terminator is added at the end.
pub const FMT_STR_CODE_B_TERMINATED_WITH_SIZE: (&str, usize) = ("%Y-%jT%T%.3fZ", 22);
/// Functions requiring both [chrono] and [alloc] support.
#[cfg(all(feature = "alloc", feature = "chrono"))]
pub mod alloc_mod_chrono {
use super::*;

View File

@@ -37,12 +37,15 @@ use super::{
/// Base value for the preamble field for a time field parser to determine the time field type.
pub const P_FIELD_BASE: u8 = (CcsdsTimeCode::Cds as u8) << 4;
/// Minimum allowed length for a CDS timestamp.
pub const MIN_CDS_FIELD_LEN: usize = 7;
/// Maximum allowed days value for 24 bit days field.
pub const MAX_DAYS_24_BITS: u32 = 2_u32.pow(24) - 1;
/// Generic trait implemented by token structs to specify the length of day field at type
/// level. This trait is only meant to be implemented in this crate and therefore sealed.
pub trait ProvidesDaysLength: Sealed + Clone {
/// Raw field type.
type FieldType: Debug
+ Copy
+ Clone
@@ -73,24 +76,33 @@ impl ProvidesDaysLength for DaysLen24Bits {
type FieldType = u32;
}
/// Length of day segment.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum LengthOfDaySegment {
/// Shorter 16 bits length of days field.
Short16Bits = 0,
/// Larger 24 bits length of days field.
Long24Bits = 1,
}
/// Sub-millisecond precision indicator.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SubmillisPrecision {
/// No sub-millisecond precision present.
Absent = 0b00,
/// Microsecond precision present.
Microseconds = 0b01,
/// Picoseconds precision present.
Picoseconds = 0b10,
/// Reserved.
Reserved = 0b11,
}
/// CDS timestamp error.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -102,10 +114,12 @@ pub enum CdsError {
/// field. This error will be returned if there is a missmatch.
#[error("wrong constructor for length of day {0:?} detected in preamble")]
InvalidCtorForDaysOfLenInPreamble(LengthOfDaySegment),
/// Date is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
#[error("date before CCSDS epoch: {0}")]
DateBeforeCcsdsEpoch(#[from] DateBeforeCcsdsEpochError),
}
/// Retrieve the [LengthOfDaySegment] from the p-field byte.
pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
if (pfield >> 2) & 0b1 == 1 {
return LengthOfDaySegment::Long24Bits;
@@ -113,6 +127,7 @@ pub fn length_of_day_segment_from_pfield(pfield: u8) -> LengthOfDaySegment {
LengthOfDaySegment::Short16Bits
}
/// Retrieve the [SubmillisPrecision] from the p-field byte.
#[inline]
pub fn precision_from_pfield(pfield: u8) -> SubmillisPrecision {
match pfield & 0b11 {
@@ -180,19 +195,29 @@ pub struct CdsTime<DaysLen: ProvidesDaysLength = DaysLen16Bits> {
///
/// Also exists to encapsulate properties used by private converters.
pub trait CdsBase {
/// Sub-millisecond precision indicator.
fn submillis_precision(&self) -> SubmillisPrecision;
/// Sub-milliseconds.
fn submillis(&self) -> u32;
/// Milliseconds of day.
fn ms_of_day(&self) -> u32;
/// CCSDS days since the epoch as [u32].
fn ccsds_days_as_u32(&self) -> u32;
}
/// Generic properties for all CDS time providers.
pub trait CdsTimestamp: CdsBase {
/// Length of day segment.
fn len_of_day_seg(&self) -> LengthOfDaySegment;
}
/// Generic implementation of [CcsdsTimeProvider] for CDS time providers.
#[cfg(feature = "alloc")]
pub trait DynCdsTimeProvider: CcsdsTimeProvider + CdsTimestamp + TimeWriter + Any {}
#[cfg(feature = "alloc")]
impl DynCdsTimeProvider for CdsTime<DaysLen16Bits> {}
#[cfg(feature = "alloc")]
@@ -294,11 +319,13 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
true
}
/// Clear the submillisecond field.
pub fn clear_submillis(&mut self) {
self.pfield &= !(0b11);
self.submillis = 0;
}
/// CCSDS days since the CCSDS epoch.
pub fn ccsds_days(&self) -> ProvidesDaysLen::FieldType {
self.ccsds_days
}
@@ -522,6 +549,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CdsTime<ProvidesDaysLen> {
pfield
}
/// Update the time from the current time.
#[cfg(feature = "std")]
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
let conversion_from_now = self.generic_conversion_from_now()?;
@@ -612,6 +640,7 @@ impl CdsTime<DaysLen24Bits> {
Self::now_generic_with_us_prec(LengthOfDaySegment::Long24Bits)
}
/// Constructor from the CDS timestamp as a raw byte array.
pub fn from_bytes_with_u24_days(buf: &[u8]) -> Result<Self, TimestampError> {
let submillis_precision =
Self::generic_raw_read_checks(buf, LengthOfDaySegment::Long24Bits)?;
@@ -704,6 +733,7 @@ impl CdsTime<DaysLen16Bits> {
Self::from_now_generic_ps_prec(LengthOfDaySegment::Short16Bits)
}
/// Write the CDS timestamp to a raw byte array.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
self.length_check(buf, self.len_as_bytes())?;
buf[0] = self.pfield;
@@ -721,6 +751,7 @@ impl CdsTime<DaysLen16Bits> {
Ok(self.len_as_bytes())
}
/// Constructor from the CDS timestamp as a raw byte array.
pub fn from_bytes_with_u16_days(buf: &[u8]) -> Result<Self, TimestampError> {
let submillis_precision =
Self::generic_raw_read_checks(buf, LengthOfDaySegment::Short16Bits)?;
@@ -892,6 +923,7 @@ impl TimeWriter for CdsTime<DaysLen16Bits> {
}
impl CdsTime<DaysLen24Bits> {
/// Write the CDS timestamp to a raw byte array.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
self.length_check(buf, self.len_as_bytes())?;
buf[0] = self.pfield;

View File

@@ -32,6 +32,7 @@ pub const P_FIELD_BASE: u8 = (CcsdsTimeCode::CucCcsdsEpoch as u8) << 4;
/// Maximum length if the preamble field is not extended.
pub const MAX_CUC_LEN_SMALL_PREAMBLE: usize = 8;
/// Fractional resolution for the fractional part of the CUC time code.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -70,13 +71,14 @@ pub fn convert_fractional_part_to_ns(fractional_part: FractionalPart) -> u64 {
10_u64.pow(9) * fractional_part.counter as u64 / div as u64
}
/// Convert the fractional resolution to the divisor used to calculate the fractional part.
#[inline(always)]
pub const fn fractional_res_to_div(res: FractionalResolution) -> u32 {
// We do not use the full possible range for a given resolution. This is because if we did
// that, the largest value would be equal to the counter being incremented by one. Thus, the
// smallest allowed fractions value is 0 while the largest allowed fractions value is the
// closest fractions value to the next counter increment.
2_u32.pow(8 * res as u32) - 1
(1u32 << (8 * res as u32)) - 1
}
/// Calculate the fractional part for a given resolution and subsecond nanoseconds.
@@ -101,22 +103,34 @@ pub fn fractional_part_from_subsec_ns(res: FractionalResolution, ns: u64) -> Fra
}
}
/// CUC error.
#[derive(Copy, Clone, PartialEq, Eq, Debug, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CucError {
/// Invalid CUC counter width.
#[error("invalid cuc counter byte width {0}")]
InvalidCounterWidth(u8),
/// Invalid counter supplied.
#[error("invalid cuc counter {counter} for width {width}")]
InvalidCounter { width: u8, counter: u64 },
InvalidCounter {
/// Width.
width: u8,
/// Counter.
counter: u64,
},
/// Invalid fractions.
#[error("invalid cuc fractional part {value} for resolution {resolution:?}")]
InvalidFractions {
/// Resolution.
resolution: FractionalResolution,
/// Value.
value: u64,
},
/// Error while correcting for leap seconds.
#[error("error while correcting for leap seconds")]
LeapSecondCorrectionError,
/// Data is before the CCSDS epoch.
#[error("date before ccsds epoch: {0}")]
DateBeforeCcsdsEpoch(#[from] DateBeforeCcsdsEpochError),
}
@@ -127,14 +141,20 @@ pub enum CucError {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct WidthCounterPair(pub u8, pub u32);
/// Fractional part of the CUC time code.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FractionalPart {
/// Resolution.
pub resolution: FractionalResolution,
/// Counter.
pub counter: u32,
}
impl FractionalPart {
/// Generic constructor.
///
/// This function will panic if the counter is smaller than the calculated divisor.
#[inline]
pub const fn new(resolution: FractionalResolution, counter: u32) -> Self {
let div = fractional_res_to_div(resolution);
@@ -157,6 +177,7 @@ impl FractionalPart {
Self::new_with_seconds_resolution()
}
/// Check constructor which verifies that the counter is larger than the divisor.
#[inline]
pub fn new_checked(resolution: FractionalResolution, counter: u32) -> Option<Self> {
let div = fractional_res_to_div(resolution);
@@ -169,16 +190,19 @@ impl FractionalPart {
})
}
/// Fractional resolution.
#[inline]
pub fn resolution(&self) -> FractionalResolution {
self.resolution
}
/// Counter value.
#[inline]
pub fn counter(&self) -> u32 {
self.counter
}
/// Check whether the timestamp does not have a fractional part.
#[inline]
pub fn no_fractional_part(&self) -> bool {
self.resolution == FractionalResolution::Seconds
@@ -245,17 +269,21 @@ pub struct CucTime {
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CucTimeWithLeapSecs {
/// CUC time.
pub time: CucTime,
/// Leap seconds.
pub leap_seconds: u32,
}
impl CucTimeWithLeapSecs {
/// Generic constructor.
#[inline]
pub fn new(time: CucTime, leap_seconds: u32) -> Self {
Self { time, leap_seconds }
}
}
/// p-field length.
#[inline]
pub fn pfield_len(pfield: u8) -> usize {
if ((pfield >> 7) & 0b1) == 1 {
@@ -381,6 +409,7 @@ impl CucTime {
Ok(())
}
/// Creates a CUC timestamp from a Chrono DateTime object.
#[cfg(feature = "chrono")]
pub fn from_chrono_date_time(
dt: &chrono::DateTime<chrono::Utc>,
@@ -448,21 +477,25 @@ impl CucTime {
})
}
/// CCSDS time code.
#[inline]
pub fn ccsds_time_code(&self) -> CcsdsTimeCode {
CcsdsTimeCode::CucCcsdsEpoch
}
/// Width and counter pair.
#[inline]
pub fn width_counter_pair(&self) -> WidthCounterPair {
self.counter
}
/// Counter width.
#[inline]
pub fn counter_width(&self) -> u8 {
self.counter.0
}
/// Counter value.
#[inline]
pub fn counter(&self) -> u32 {
self.counter.1
@@ -474,11 +507,13 @@ impl CucTime {
self.fractions
}
/// Convert to the leap seconds helper.
#[inline]
pub fn to_leap_sec_helper(&self, leap_seconds: u32) -> CucTimeWithLeapSecs {
CucTimeWithLeapSecs::new(*self, leap_seconds)
}
/// Set the fractional part.
#[inline]
pub fn set_fractions(&mut self, fractions: FractionalPart) -> Result<(), CucError> {
Self::verify_fractions_value(fractions)?;
@@ -525,16 +560,19 @@ impl CucTime {
self.pfield |= self.fractions.resolution() as u8;
}
/// Length of the counter from the p-field.
#[inline]
pub fn len_cntr_from_pfield(pfield: u8) -> u8 {
((pfield >> 2) & 0b11) + 1
}
/// Length of the fractional part from the p-field.
#[inline]
pub fn len_fractions_from_pfield(pfield: u8) -> u8 {
pfield & 0b11
}
/// UNIX seconds.
#[inline]
pub fn unix_secs(&self, leap_seconds: u32) -> i64 {
ccsds_epoch_to_unix_epoch(self.counter.1 as i64)
@@ -542,6 +580,7 @@ impl CucTime {
.unwrap()
}
/// Subsecond milliseconds part of the CUC time.
#[inline]
pub fn subsec_millis(&self) -> u16 {
(self.subsec_nanos() / 1_000_000) as u16
@@ -564,6 +603,7 @@ impl CucTime {
)
}
/// Packed length from the raw p-field.
#[inline]
pub fn len_packed_from_pfield(pfield: u8) -> usize {
let mut base_len: usize = 1;

View File

@@ -22,19 +22,29 @@ pub mod ascii;
pub mod cds;
pub mod cuc;
/// Conversion constant for converting CCSDS days to UNIX days.
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
/// Seconds per day.
pub const SECONDS_PER_DAY: u32 = 86400;
/// Milliseconds per day.
pub const MS_PER_DAY: u32 = SECONDS_PER_DAY * 1000;
/// Nanoseconds per second.
pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
/// CCSDS time code identifiers.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum CcsdsTimeCode {
/// CUC with a CCSDS epoch (1958-01-01T00:00:00+00:00).
CucCcsdsEpoch = 0b001,
/// CUC with a custom agency epoch.
CucAgencyEpoch = 0b010,
/// CDS time code.
Cds = 0b100,
/// CCS time code.
Ccs = 0b101,
/// Agency defined time code.
AgencyDefined = 0b110,
}
@@ -60,44 +70,61 @@ pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result<CcsdsTimeCode, u8> {
CcsdsTimeCode::try_from(raw_bits).map_err(|_| raw_bits)
}
/// Date is before the CCSDS epoch.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[error("date before ccsds epoch: {0:?}")]
pub struct DateBeforeCcsdsEpochError(UnixTime);
/// Generic timestamp error.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum TimestampError {
/// Invalid time code.
#[error("invalid time code, expected {expected:?}, found {found}")]
InvalidTimeCode { expected: CcsdsTimeCode, found: u8 },
InvalidTimeCode {
/// Expected time code.
expected: CcsdsTimeCode,
/// Found raw time code.
found: u8,
},
/// Byte conversion error.
#[error("time stamp: byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError),
/// CDS timestamp error.
#[error("CDS error: {0}")]
Cds(#[from] cds::CdsError),
/// CUC timestamp error.
#[error("CUC error: {0}")]
Cuc(#[from] cuc::CucError),
/// Custom epoch is not supported.
#[error("custom epoch not supported")]
CustomEpochNotSupported,
}
/// [std] module.
#[cfg(feature = "std")]
pub mod std_mod {
use crate::time::TimestampError;
use std::time::SystemTimeError;
use thiserror::Error;
/// [std] timestamp error.
#[derive(Debug, Clone, Error)]
pub enum StdTimestampError {
/// System time error.
#[error("system time error: {0:?}")]
SystemTime(#[from] SystemTimeError),
/// Generic timestamp error.
#[error("timestamp error: {0}")]
Timestamp(#[from] TimestampError),
}
}
/// Seconds since epoch for the current system time.
#[cfg(feature = "std")]
pub fn seconds_since_epoch() -> f64 {
SystemTime::now()
@@ -131,16 +158,19 @@ pub const fn unix_epoch_to_ccsds_epoch(unix_epoch: i64) -> i64 {
unix_epoch - (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
}
/// Convert CCSDS epoch to UNIX epoch.
#[inline]
pub const fn ccsds_epoch_to_unix_epoch(ccsds_epoch: i64) -> i64 {
ccsds_epoch + (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
}
/// Milliseconds of day for the current system time.
#[cfg(feature = "std")]
pub fn ms_of_day_using_sysclock() -> u32 {
ms_of_day(seconds_since_epoch())
}
/// Milliseconds for the given seconds since epoch.
pub fn ms_of_day(seconds_since_epoch: f64) -> u32 {
let fraction_ms = seconds_since_epoch - seconds_since_epoch.floor();
let ms_of_day: u32 = (((seconds_since_epoch.floor() as u32 % SECONDS_PER_DAY) * 1000) as f64
@@ -149,13 +179,16 @@ pub fn ms_of_day(seconds_since_epoch: f64) -> u32 {
ms_of_day
}
/// Generic writable timestamp trait.
pub trait TimeWriter {
/// Written length.
fn len_written(&self) -> usize;
/// Generic function to convert write a timestamp into a raw buffer.
/// Returns the number of written bytes on success.
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError>;
/// Convert to a owned [alloc::vec::Vec].
#[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<alloc::vec::Vec<u8>, TimestampError> {
let mut vec = alloc::vec![0; self.len_written()];
@@ -164,7 +197,9 @@ pub trait TimeWriter {
}
}
/// Genmeric readable timestamp trait.
pub trait TimeReader: Sized {
/// Create a timestamp from a raw byte buffer.
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>;
}
@@ -174,6 +209,7 @@ pub trait TimeReader: Sized {
/// practical because they are a very common and simple exchange format for time information.
/// Therefore, it was decided to keep them in this trait as well.
pub trait CcsdsTimeProvider {
/// Length when written to bytes.
fn len_as_bytes(&self) -> usize;
/// Returns the pfield of the time provider. The pfield can have one or two bytes depending
@@ -181,29 +217,37 @@ pub trait CcsdsTimeProvider {
/// entry denotes the length of the pfield and the second entry is the value of the pfield
/// in big endian format.
fn p_field(&self) -> (usize, [u8; 2]);
/// CCSDS time code field.
fn ccdsd_time_code(&self) -> CcsdsTimeCode;
/// UNIX time as seconds.
fn unix_secs(&self) -> i64 {
self.unix_time().secs
}
/// Subsecond nanoseconds.
fn subsec_nanos(&self) -> u32 {
self.unix_time().subsec_nanos
}
/// Subsecond milliseconds.
fn subsec_millis(&self) -> u16 {
(self.subsec_nanos() / 1_000_000) as u16
}
/// UNIX time.
fn unix_time(&self) -> UnixTime {
UnixTime::new(self.unix_secs(), self.subsec_nanos())
}
/// [chrono] date time.
#[cfg(feature = "chrono")]
fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
chrono::Utc.timestamp_opt(self.unix_secs(), self.subsec_nanos())
}
/// [time] library date] library date time.
#[cfg(feature = "timelib")]
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
Ok(time::OffsetDateTime::from_unix_timestamp(self.unix_secs())?
@@ -285,6 +329,7 @@ impl UnixTime {
}
}
/// New UNIX time with only seconds, subseconds set to zero.
pub fn new_only_secs(unix_seconds: i64) -> Self {
Self {
secs: unix_seconds,
@@ -292,15 +337,18 @@ impl UnixTime {
}
}
/// Sub-second milliseconds.
#[inline]
pub fn subsec_millis(&self) -> u16 {
(self.subsec_nanos / 1_000_000) as u16
}
/// Sub-second nanoseconds.
pub fn subsec_nanos(&self) -> u32 {
self.subsec_nanos
}
/// Create a UNIX timestamp from the current system time.
#[cfg(feature = "std")]
pub fn now() -> Result<Self, SystemTimeError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
@@ -308,27 +356,31 @@ impl UnixTime {
Ok(Self::new(epoch as i64, now.subsec_nanos()))
}
/// UNIX timestamp as a floating point number in seconds.
#[inline]
pub fn unix_secs_f64(&self) -> f64 {
self.secs as f64 + (self.subsec_nanos as f64 / 1_000_000_000.0)
}
/// UNIX timestamp as seconds, discards the sub-second part.
pub fn as_secs(&self) -> i64 {
self.secs
}
/// UNIX timestamp as [chrono] date time.
#[cfg(feature = "chrono")]
pub fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
Utc.timestamp_opt(self.secs, self.subsec_nanos)
}
/// UNIX timestamp as [time] library date time.
#[cfg(feature = "timelib")]
pub fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
Ok(time::OffsetDateTime::from_unix_timestamp(self.as_secs())?
+ time::Duration::nanoseconds(self.subsec_nanos().into()))
}
// Calculate the difference in milliseconds between two UnixTimestamps
/// Calculate the difference in milliseconds between two UnixTimestamps
pub fn diff_in_millis(&self, other: &UnixTime) -> Option<i64> {
let seconds_difference = self.secs.checked_sub(other.secs)?;
// Convert seconds difference to milliseconds
@@ -398,7 +450,9 @@ impl Ord for UnixTime {
/// so the sign information is supplied separately.
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct StampDiff {
/// Positive duration flag.
pub positive_duration: bool,
/// Absolute duration.
pub duration_absolute: Duration,
}

View File

@@ -1,12 +1,16 @@
//! # Utility module.
use crate::ByteConversionError;
use core::fmt::Debug;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Helper traits for types which can be converted to a byte array.
pub trait ToBeBytes {
/// Concrete byte array type.
type ByteArray: AsRef<[u8]>;
/// Length when written to big endian bytes.
fn written_len(&self) -> usize;
/// Convert to big endian byte array.
fn to_be_bytes(&self) -> Self::ByteArray;
}
@@ -80,14 +84,17 @@ impl ToBeBytes for u64 {
}
}
/// Helper trait for unsigned enumerations.
pub trait UnsignedEnum {
/// Size of the unsigned enumeration in bytes.
fn size(&self) -> usize;
/// Write the unsigned enumeration to a raw buffer. Returns the written size on success.
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
fn value(&self) -> u64;
/// Type-erased raw value.
fn value_raw(&self) -> u64;
/// Convert to a [alloc::vec::Vec].
#[cfg(feature = "alloc")]
fn to_vec(&self) -> alloc::vec::Vec<u8> {
let mut buf = alloc::vec![0; self.size()];
@@ -96,22 +103,32 @@ pub trait UnsignedEnum {
}
}
/// Extension trait for unsigned enumerations.
pub trait UnsignedEnumExt: UnsignedEnum + Debug + Copy + Clone + PartialEq + Eq {}
/// Unsigned byte field errors.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UnsignedByteFieldError {
/// Value is too large for specified width of byte field.
#[error("value {value} too large for width {width}")]
ValueTooLargeForWidth { width: usize, value: u64 },
ValueTooLargeForWidth {
/// Width in bytes.
width: usize,
/// Value.
value: u64,
},
/// Only 1, 2, 4 and 8 are allow width values. Optionally contains the expected width if
/// applicable, for example for conversions.
#[error("invalid width {found}, expected {expected:?}")]
InvalidWidth {
/// Found width.
found: usize,
/// Expected width.
expected: Option<usize>,
},
/// Error during byte conversion.
#[error("byte conversion error: {0}")]
ByteConversionError(#[from] ByteConversionError),
}
@@ -126,16 +143,19 @@ pub struct UnsignedByteField {
}
impl UnsignedByteField {
/// Generic constructor.
#[inline]
pub const fn new(width: usize, value: u64) -> Self {
Self { width, value }
}
/// Type-erased raw value.
#[inline]
pub const fn value_const(&self) -> u64 {
pub const fn value(&self) -> u64 {
self.value
}
/// Construct from raw bytes, assuming big-endian byte order.
#[inline]
pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result<Self, UnsignedByteFieldError> {
if width > buf.len() {
@@ -175,8 +195,8 @@ impl UnsignedEnum for UnsignedByteField {
}
#[inline]
fn value(&self) -> u64 {
self.value_const()
fn value_raw(&self) -> u64 {
self.value()
}
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
@@ -212,6 +232,7 @@ impl UnsignedEnum for UnsignedByteField {
}
}
/// Generic type erased unsigned byte field.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -220,11 +241,13 @@ pub struct GenericUnsignedByteField<TYPE: Copy + Into<u64>> {
}
impl<TYPE: Copy + Into<u64>> GenericUnsignedByteField<TYPE> {
/// Generic constructor.
pub const fn new(val: TYPE) -> Self {
Self { value: val }
}
pub const fn value_typed(&self) -> TYPE {
/// Raw value.
pub const fn value(&self) -> TYPE {
self.value
}
}
@@ -247,20 +270,29 @@ impl<TYPE: Copy + ToBeBytes + Into<u64>> UnsignedEnum for GenericUnsignedByteFie
}
#[inline]
fn value(&self) -> u64 {
self.value_typed().into()
fn value_raw(&self) -> u64 {
self.value().into()
}
}
/// Alias for [GenericUnsignedByteField] with [()] generic.
pub type UnsignedByteFieldEmpty = GenericUnsignedByteField<()>;
/// Alias for [GenericUnsignedByteField] with [u8] generic.
pub type UnsignedByteFieldU8 = GenericUnsignedByteField<u8>;
/// Alias for [GenericUnsignedByteField] with [u16] generic.
pub type UnsignedByteFieldU16 = GenericUnsignedByteField<u16>;
/// Alias for [GenericUnsignedByteField] with [u32] generic.
pub type UnsignedByteFieldU32 = GenericUnsignedByteField<u32>;
/// Alias for [GenericUnsignedByteField] with [u64] generic.
pub type UnsignedByteFieldU64 = GenericUnsignedByteField<u64>;
/// Alias for [UnsignedByteFieldU8]
pub type UbfU8 = UnsignedByteFieldU8;
/// Alias for [UnsignedByteFieldU16]
pub type UbfU16 = UnsignedByteFieldU16;
/// Alias for [UnsignedByteFieldU32]
pub type UbfU32 = UnsignedByteFieldU32;
/// Alias for [UnsignedByteFieldU64]
pub type UbfU64 = UnsignedByteFieldU64;
impl From<UnsignedByteFieldU8> for UnsignedByteField {
@@ -372,7 +404,7 @@ pub mod tests {
for val in buf.iter().skip(1) {
assert_eq!(*val, 0);
}
assert_eq!(u8.value_typed(), 5);
assert_eq!(u8.value_raw(), 5);
assert_eq!(u8.value(), 5);
}
@@ -390,7 +422,7 @@ pub mod tests {
for val in buf.iter().skip(2) {
assert_eq!(*val, 0);
}
assert_eq!(u16.value_typed(), 3823);
assert_eq!(u16.value_raw(), 3823);
assert_eq!(u16.value(), 3823);
}
@@ -408,7 +440,7 @@ pub mod tests {
(4..8).for_each(|i| {
assert_eq!(buf[i], 0);
});
assert_eq!(u32.value_typed(), 80932);
assert_eq!(u32.value_raw(), 80932);
assert_eq!(u32.value(), 80932);
}
@@ -423,7 +455,7 @@ pub mod tests {
assert_eq!(len, 8);
let raw_val = u64::from_be_bytes(buf[0..8].try_into().unwrap());
assert_eq!(raw_val, 5999999);
assert_eq!(u64.value_typed(), 5999999);
assert_eq!(u64.value_raw(), 5999999);
assert_eq!(u64.value(), 5999999);
}