API update
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
This commit is contained in:
parent
f406957752
commit
c85177ece9
@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- `UnsignedByteField` as a type-erased helper.
|
- `UnsignedByteField` as a type-erased helper.
|
||||||
- Added `SerializablePusPacket` as a generic abstraction for PUS packets which are
|
- Added `SerializablePusPacket` as a generic abstraction for PUS packets which are
|
||||||
writable.
|
writable.
|
||||||
|
- Added new `PusTmZeroCopyWriter` class which allows to set fields on a raw TM packet,
|
||||||
|
which might be more efficient that modification and re-writing a packet with the
|
||||||
|
`PusTm` object.
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
|
@ -598,12 +598,12 @@ mod tests {
|
|||||||
// No record boundary preservation
|
// No record boundary preservation
|
||||||
assert_eq!((buf[3] >> 7) & 1, pdu_conf.seg_ctrl as u8);
|
assert_eq!((buf[3] >> 7) & 1, pdu_conf.seg_ctrl as u8);
|
||||||
// Entity ID length raw value is actual number of octets - 1 => 0
|
// Entity ID length raw value is actual number of octets - 1 => 0
|
||||||
let entity_id_len = pdu_conf.pdu_conf.source_entity_id.len();
|
let entity_id_len = pdu_conf.pdu_conf.source_entity_id.size();
|
||||||
assert_eq!((buf[3] >> 4) & 0b111, entity_id_len as u8 - 1);
|
assert_eq!((buf[3] >> 4) & 0b111, entity_id_len as u8 - 1);
|
||||||
// No segment metadata
|
// No segment metadata
|
||||||
assert_eq!((buf[3] >> 3) & 0b1, pdu_conf.seg_metadata_flag as u8);
|
assert_eq!((buf[3] >> 3) & 0b1, pdu_conf.seg_metadata_flag as u8);
|
||||||
// Transaction Sequence ID length raw value is actual number of octets - 1 => 0
|
// Transaction Sequence ID length raw value is actual number of octets - 1 => 0
|
||||||
let seq_num_len = pdu_conf.pdu_conf.transaction_seq_num.len();
|
let seq_num_len = pdu_conf.pdu_conf.transaction_seq_num.size();
|
||||||
assert_eq!(buf[3] & 0b111, seq_num_len as u8 - 1);
|
assert_eq!(buf[3] & 0b111, seq_num_len as u8 - 1);
|
||||||
let mut current_idx = 4;
|
let mut current_idx = 4;
|
||||||
let mut byte_field_check = |field_len: usize, ubf: &UnsignedByteField| {
|
let mut byte_field_check = |field_len: usize, ubf: &UnsignedByteField| {
|
||||||
|
@ -257,16 +257,22 @@ pub(crate) fn user_data_from_raw(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify_crc16_ccitt_false_from_raw(
|
pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error(
|
||||||
raw_data: &[u8],
|
raw_data: &[u8],
|
||||||
crc16: u16,
|
crc16: u16,
|
||||||
) -> Result<(), PusError> {
|
) -> Result<(), PusError> {
|
||||||
|
verify_crc16_ccitt_false_from_raw(raw_data)
|
||||||
|
.then(|| ())
|
||||||
|
.ok_or(PusError::IncorrectCrc(crc16))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
|
||||||
let mut digest = CRC_CCITT_FALSE.digest();
|
let mut digest = CRC_CCITT_FALSE.digest();
|
||||||
digest.update(raw_data);
|
digest.update(raw_data);
|
||||||
if digest.finalize() == 0 {
|
if digest.finalize() == 0 {
|
||||||
return Ok(());
|
return true;
|
||||||
}
|
}
|
||||||
Err(PusError::IncorrectCrc(crc16))
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! ccsds_impl {
|
macro_rules! ccsds_impl {
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
//! ```
|
//! ```
|
||||||
use crate::ecss::{
|
use crate::ecss::{
|
||||||
ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls, user_data_from_raw,
|
ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls, user_data_from_raw,
|
||||||
verify_crc16_ccitt_false_from_raw, CrcType, PusError, PusPacket, PusVersion,
|
verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError, PusPacket, PusVersion,
|
||||||
SerializablePusPacket,
|
SerializablePusPacket,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -394,7 +394,10 @@ impl<'raw_data> PusTc<'raw_data> {
|
|||||||
calc_crc_on_serialization: false,
|
calc_crc_on_serialization: false,
|
||||||
crc16: Some(crc_from_raw_data(raw_data)?),
|
crc16: Some(crc_from_raw_data(raw_data)?),
|
||||||
};
|
};
|
||||||
verify_crc16_ccitt_false_from_raw(raw_data, pus_tc.crc16.expect("CRC16 invalid"))?;
|
verify_crc16_ccitt_false_from_raw_to_pus_error(
|
||||||
|
raw_data,
|
||||||
|
pus_tc.crc16.expect("CRC16 invalid"),
|
||||||
|
)?;
|
||||||
Ok((pus_tc, total_len))
|
Ok((pus_tc, total_len))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +503,7 @@ impl GenericPusTcSecondaryHeader for PusTc<'_> {
|
|||||||
#[cfg(all(test, feature = "std"))]
|
#[cfg(all(test, feature = "std"))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::ecss::PusVersion::PusC;
|
use crate::ecss::PusVersion::PusC;
|
||||||
use crate::ecss::{PusError, PusPacket};
|
use crate::ecss::{PusError, PusPacket, SerializablePusPacket};
|
||||||
use crate::tc::ACK_ALL;
|
use crate::tc::ACK_ALL;
|
||||||
use crate::tc::{GenericPusTcSecondaryHeader, PusTc, PusTcSecondaryHeader};
|
use crate::tc::{GenericPusTcSecondaryHeader, PusTc, PusTcSecondaryHeader};
|
||||||
use crate::{ByteConversionError, SpHeader};
|
use crate::{ByteConversionError, SpHeader};
|
||||||
|
91
src/tm.rs
91
src/tm.rs
@ -1,13 +1,13 @@
|
|||||||
//! This module contains all components required to create a ECSS PUS C telemetry packets according
|
//! This module contains all components required to create a ECSS PUS C telemetry packets according
|
||||||
//! to [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
//! to [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/).
|
||||||
use crate::ecss::{
|
use crate::ecss::{
|
||||||
ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls, user_data_from_raw,
|
calc_pus_crc16, ccsds_impl, crc_from_raw_data, crc_procedure, sp_header_impls,
|
||||||
verify_crc16_ccitt_false_from_raw, CrcType, PusError, PusPacket, PusVersion,
|
user_data_from_raw, verify_crc16_ccitt_false_from_raw_to_pus_error, CrcType, PusError,
|
||||||
SerializablePusPacket,
|
PusPacket, PusVersion, SerializablePusPacket,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, SpHeader,
|
ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, SpHeader,
|
||||||
CCSDS_HEADER_LEN, CRC_CCITT_FALSE,
|
CCSDS_HEADER_LEN, CRC_CCITT_FALSE, MAX_SEQ_COUNT,
|
||||||
};
|
};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
@ -388,7 +388,10 @@ impl<'raw_data> PusTm<'raw_data> {
|
|||||||
calc_crc_on_serialization: false,
|
calc_crc_on_serialization: false,
|
||||||
crc16: Some(crc_from_raw_data(raw_data)?),
|
crc16: Some(crc_from_raw_data(raw_data)?),
|
||||||
};
|
};
|
||||||
verify_crc16_ccitt_false_from_raw(raw_data, pus_tm.crc16.expect("CRC16 invalid"))?;
|
verify_crc16_ccitt_false_from_raw_to_pus_error(
|
||||||
|
raw_data,
|
||||||
|
pus_tm.crc16.expect("CRC16 invalid"),
|
||||||
|
)?;
|
||||||
Ok((pus_tm, total_len))
|
Ok((pus_tm, total_len))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +402,62 @@ impl<'raw_data> PusTm<'raw_data> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is a helper class to update certain fields in a raw PUS telemetry packet directly in place.
|
||||||
|
/// This can be more efficient than creating a full [PusTm], modifying the fields and then writing
|
||||||
|
/// it back to another buffer.
|
||||||
|
///
|
||||||
|
/// Please note that the [Self::finish] method has to be called for the PUS TM CRC16 to be valid
|
||||||
|
/// after changing fields of the TM packet. Furthermore, the constructor of this class will not
|
||||||
|
/// do any checks except a length check to ensure that all relevant fields can be updated without
|
||||||
|
/// a panic. If a full validity check of the PUS TM packet is required, it is recommended
|
||||||
|
/// to construct a full [PusTm] object from the raw bytestream first.
|
||||||
|
pub struct PusTmZeroCopyWriter<'raw> {
|
||||||
|
raw_tm: &'raw mut [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'raw> PusTmZeroCopyWriter<'raw> {
|
||||||
|
/// This function will not do any other checks on the raw data other than a length check
|
||||||
|
/// for all internal fields which can be updated. It is the responsibility of the user to ensure
|
||||||
|
/// the raw slice contains a valid telemetry packet. The slice should have the exact length
|
||||||
|
/// of the telemetry packet for this class to work properly.
|
||||||
|
pub fn new(raw_tm: &'raw mut [u8]) -> Option<Self> {
|
||||||
|
if raw_tm.len() < 13 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self { raw_tm })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function sets the message counter in the PUS TM secondary header.
|
||||||
|
pub fn set_msg_count(&mut self, msg_count: u16) {
|
||||||
|
self.raw_tm[9..11].copy_from_slice(&msg_count.to_be_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function sets the destination ID in the PUS TM secondary header.
|
||||||
|
pub fn set_destination_id(&mut self, dest_id: u16) {
|
||||||
|
self.raw_tm[11..13].copy_from_slice(&dest_id.to_be_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the sequence count. Returns false and does not update the value if the passed value
|
||||||
|
/// exceeds [MAX_SEQ_COUNT].
|
||||||
|
pub fn set_seq_count_in_place(&mut self, seq_count: u16) -> bool {
|
||||||
|
if seq_count > MAX_SEQ_COUNT {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let new_psc =
|
||||||
|
(u16::from_be_bytes(self.raw_tm[2..4].try_into().unwrap()) & 0xC000) | seq_count;
|
||||||
|
self.raw_tm[2..4].copy_from_slice(&new_psc.to_be_bytes());
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This method has to be called after modifying fields to ensure the CRC16 of the telemetry
|
||||||
|
/// packet remains valid.
|
||||||
|
pub fn finish(self) {
|
||||||
|
let slice_len = self.raw_tm.len();
|
||||||
|
let crc16 = calc_pus_crc16(&self.raw_tm[..slice_len - 2]);
|
||||||
|
self.raw_tm[slice_len - 2..].copy_from_slice(&crc16.to_be_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SerializablePusPacket for PusTm<'_> {
|
impl SerializablePusPacket for PusTm<'_> {
|
||||||
fn len_packed(&self) -> usize {
|
fn len_packed(&self) -> usize {
|
||||||
let mut length = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA;
|
let mut length = PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA;
|
||||||
@ -714,4 +773,26 @@ mod tests {
|
|||||||
pus_tm.write_to_bytes(&mut buf).unwrap();
|
pus_tm.write_to_bytes(&mut buf).unwrap();
|
||||||
assert_eq!(pus_tm, PusTm::from_bytes(&buf, timestamp.len()).unwrap().0);
|
assert_eq!(pus_tm, PusTm::from_bytes(&buf, timestamp.len()).unwrap().0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_zero_copy_writer() {
|
||||||
|
let ping_tm = base_ping_reply_full_ctor(dummy_timestamp());
|
||||||
|
let mut buf: [u8; 64] = [0; 64];
|
||||||
|
let tm_size = ping_tm
|
||||||
|
.write_to_bytes(&mut buf)
|
||||||
|
.expect("writing PUS ping TM failed");
|
||||||
|
let mut writer = PusTmZeroCopyWriter::new(&mut buf[..tm_size])
|
||||||
|
.expect("Creating zero copy writer failed");
|
||||||
|
writer.set_destination_id(55);
|
||||||
|
writer.set_msg_count(100);
|
||||||
|
writer.set_seq_count_in_place(MAX_SEQ_COUNT - 1);
|
||||||
|
writer.finalize();
|
||||||
|
// This performs all necessary checks, including the CRC check.
|
||||||
|
let (tm_read_back, tm_size_read_back) =
|
||||||
|
PusTm::from_bytes(&buf, 7).expect("Re-creating PUS TM failed");
|
||||||
|
assert_eq!(tm_size_read_back, tm_size);
|
||||||
|
assert_eq!(tm_read_back.msg_counter(), 100);
|
||||||
|
assert_eq!(tm_read_back.dest_id(), 55);
|
||||||
|
assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
16
src/util.rs
16
src/util.rs
@ -317,7 +317,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_simple_u8() {
|
fn test_simple_u8() {
|
||||||
let u8 = UnsignedByteFieldU8::new(5);
|
let u8 = UnsignedByteFieldU8::new(5);
|
||||||
assert_eq!(u8.len(), 1);
|
assert_eq!(u8.size(), 1);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
let len = u8
|
let len = u8
|
||||||
.write_to_be_bytes(&mut buf)
|
.write_to_be_bytes(&mut buf)
|
||||||
@ -332,7 +332,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_simple_u16() {
|
fn test_simple_u16() {
|
||||||
let u16 = UnsignedByteFieldU16::new(3823);
|
let u16 = UnsignedByteFieldU16::new(3823);
|
||||||
assert_eq!(u16.len(), 2);
|
assert_eq!(u16.size(), 2);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
let len = u16
|
let len = u16
|
||||||
.write_to_be_bytes(&mut buf)
|
.write_to_be_bytes(&mut buf)
|
||||||
@ -348,7 +348,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_simple_u32() {
|
fn test_simple_u32() {
|
||||||
let u32 = UnsignedByteFieldU32::new(80932);
|
let u32 = UnsignedByteFieldU32::new(80932);
|
||||||
assert_eq!(u32.len(), 4);
|
assert_eq!(u32.size(), 4);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
let len = u32
|
let len = u32
|
||||||
.write_to_be_bytes(&mut buf)
|
.write_to_be_bytes(&mut buf)
|
||||||
@ -364,7 +364,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_simple_u64() {
|
fn test_simple_u64() {
|
||||||
let u64 = UnsignedByteFieldU64::new(5999999);
|
let u64 = UnsignedByteFieldU64::new(5999999);
|
||||||
assert_eq!(u64.len(), 8);
|
assert_eq!(u64.size(), 8);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
let len = u64
|
let len = u64
|
||||||
.write_to_be_bytes(&mut buf)
|
.write_to_be_bytes(&mut buf)
|
||||||
@ -493,7 +493,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn type_erased_u8_write() {
|
fn type_erased_u8_write() {
|
||||||
let u8 = UnsignedByteField::new(1, 5);
|
let u8 = UnsignedByteField::new(1, 5);
|
||||||
assert_eq!(u8.len(), 1);
|
assert_eq!(u8.size(), 1);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
u8.write_to_be_bytes(&mut buf)
|
u8.write_to_be_bytes(&mut buf)
|
||||||
.expect("writing to raw buffer failed");
|
.expect("writing to raw buffer failed");
|
||||||
@ -506,7 +506,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn type_erased_u16_write() {
|
fn type_erased_u16_write() {
|
||||||
let u16 = UnsignedByteField::new(2, 3823);
|
let u16 = UnsignedByteField::new(2, 3823);
|
||||||
assert_eq!(u16.len(), 2);
|
assert_eq!(u16.size(), 2);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
u16.write_to_be_bytes(&mut buf)
|
u16.write_to_be_bytes(&mut buf)
|
||||||
.expect("writing to raw buffer failed");
|
.expect("writing to raw buffer failed");
|
||||||
@ -520,7 +520,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn type_erased_u32_write() {
|
fn type_erased_u32_write() {
|
||||||
let u32 = UnsignedByteField::new(4, 80932);
|
let u32 = UnsignedByteField::new(4, 80932);
|
||||||
assert_eq!(u32.len(), 4);
|
assert_eq!(u32.size(), 4);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
u32.write_to_be_bytes(&mut buf)
|
u32.write_to_be_bytes(&mut buf)
|
||||||
.expect("writing to raw buffer failed");
|
.expect("writing to raw buffer failed");
|
||||||
@ -534,7 +534,7 @@ pub mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn type_erased_u64_write() {
|
fn type_erased_u64_write() {
|
||||||
let u64 = UnsignedByteField::new(8, 5999999);
|
let u64 = UnsignedByteField::new(8, 5999999);
|
||||||
assert_eq!(u64.len(), 8);
|
assert_eq!(u64.size(), 8);
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
u64.write_to_be_bytes(&mut buf)
|
u64.write_to_be_bytes(&mut buf)
|
||||||
.expect("writing to raw buffer failed");
|
.expect("writing to raw buffer failed");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user