add docs and minor changes #178
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
126
src/ecss/mod.rs
126
src/ecss/mod.rs
@@ -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);
|
||||
|
||||
134
src/lib.rs
134
src/lib.rs
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
56
src/util.rs
56
src/util.rs
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user