split up crate and create workspace
This commit is contained in:
14
spacepackets/Cargo.toml
Normal file
14
spacepackets/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "spacepackets"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
num = "0.4"
|
||||
postcard = { version = "0.7.3", features = ["use-std"] }
|
||||
serde = "1.0.137"
|
||||
deku = "0.13"
|
||||
zerocopy = "0.6.1"
|
||||
crc = "3.0.0"
|
13
spacepackets/src/ecss.rs
Normal file
13
spacepackets/src/ecss.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use crate::CcsdsPacket;
|
||||
|
||||
pub trait PusPacket: CcsdsPacket {
|
||||
fn service(&self) -> u8;
|
||||
fn subservice(&self) -> u8;
|
||||
fn source_id(&self) -> u16;
|
||||
fn ack_flags(&self) -> u8;
|
||||
|
||||
fn user_data(&self) -> Option<&[u8]>;
|
||||
fn crc16(&self) -> Option<u16>;
|
||||
/// Verify that the packet is valid. PUS packets have a CRC16 checksum to do this
|
||||
fn verify(&mut self) -> bool;
|
||||
}
|
539
spacepackets/src/lib.rs
Normal file
539
spacepackets/src/lib.rs
Normal file
@ -0,0 +1,539 @@
|
||||
//! # Space related components including CCSDS and ECSS packet standards
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub mod ecss;
|
||||
pub mod tc;
|
||||
pub mod tm;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum PacketError {
|
||||
/// The passed slice is too small. Returns the required size of the failed size chgeck
|
||||
ToBytesSliceTooSmall(usize),
|
||||
/// The [zerocopy] library failed to write to bytes
|
||||
ToBytesZeroCopyError,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
pub enum PacketType {
|
||||
Tm = 0,
|
||||
Tc = 1,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for PacketType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == PacketType::Tm as u8 => Ok(PacketType::Tm),
|
||||
x if x == PacketType::Tc as u8 => Ok(PacketType::Tc),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_from_packet_id(packet_id: u16) -> PacketType {
|
||||
PacketType::try_from((packet_id >> 12) as u8 & 0b1).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
pub enum SequenceFlags {
|
||||
ContinuationSegment = 0b00,
|
||||
FirstSegment = 0b01,
|
||||
LastSegment = 0b10,
|
||||
Unsegmented = 0b11,
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for SequenceFlags {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == SequenceFlags::ContinuationSegment as u8 => {
|
||||
Ok(SequenceFlags::ContinuationSegment)
|
||||
}
|
||||
x if x == SequenceFlags::FirstSegment as u8 => Ok(SequenceFlags::FirstSegment),
|
||||
x if x == SequenceFlags::LastSegment as u8 => Ok(SequenceFlags::LastSegment),
|
||||
x if x == SequenceFlags::Unsegmented as u8 => Ok(SequenceFlags::Unsegmented),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
pub struct PacketId {
|
||||
pub ptype: PacketType,
|
||||
pub sec_header_flag: bool,
|
||||
apid: u16,
|
||||
}
|
||||
|
||||
impl PacketId {
|
||||
pub fn new(ptype: PacketType, sec_header_flag: bool, apid: u16) -> Option<PacketId> {
|
||||
if apid > num::pow(2, 11) - 1 {
|
||||
return None;
|
||||
}
|
||||
Some(PacketId {
|
||||
ptype,
|
||||
sec_header_flag,
|
||||
apid,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_apid(&mut self, apid: u16) -> bool {
|
||||
if apid > num::pow(2, 11) {
|
||||
return false;
|
||||
}
|
||||
self.apid = apid;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn apid(&self) -> u16 {
|
||||
self.apid
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> u16 {
|
||||
((self.ptype as u16) << 12) | ((self.sec_header_flag as u16) << 11) | self.apid
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for PacketId {
|
||||
fn from(raw_id: u16) -> Self {
|
||||
PacketId {
|
||||
ptype: PacketType::try_from(((raw_id >> 12) & 0b1) as u8).unwrap(),
|
||||
sec_header_flag: ((raw_id >> 11) & 0b1) != 0,
|
||||
apid: raw_id & 0x7FF,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
pub struct PacketSequenceCtrl {
|
||||
pub seq_flags: SequenceFlags,
|
||||
ssc: u16,
|
||||
}
|
||||
|
||||
impl PacketSequenceCtrl {
|
||||
pub fn new(seq_flags: SequenceFlags, ssc: u16) -> Option<PacketSequenceCtrl> {
|
||||
if ssc > num::pow(2, 14) - 1 {
|
||||
return None;
|
||||
}
|
||||
Some(PacketSequenceCtrl { seq_flags, ssc })
|
||||
}
|
||||
pub fn raw(&self) -> u16 {
|
||||
((self.seq_flags as u16) << 14) | self.ssc
|
||||
}
|
||||
pub fn set_ssc(&mut self, ssc: u16) -> bool {
|
||||
if ssc > num::pow(2, 14) - 1 {
|
||||
return false;
|
||||
}
|
||||
self.ssc = ssc;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn ssc(&self) -> u16 {
|
||||
self.ssc
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u16> for PacketSequenceCtrl {
|
||||
fn from(raw_id: u16) -> Self {
|
||||
PacketSequenceCtrl {
|
||||
seq_flags: SequenceFlags::try_from(((raw_id >> 14) & 0b11) as u8).unwrap(),
|
||||
ssc: raw_id & SSC_MASK,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! sph_from_other {
|
||||
($Self: path, $other: path) => {
|
||||
impl From<$other> for $Self {
|
||||
fn from(other: $other) -> Self {
|
||||
Self::from_composite_fields(
|
||||
other.packet_id(),
|
||||
other.psc(),
|
||||
other.data_len(),
|
||||
Some(other.version()),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const SSC_MASK: u16 = 0x3FFF;
|
||||
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 {
|
||||
fn version(&self) -> u8;
|
||||
fn packet_id(&self) -> PacketId;
|
||||
fn psc(&self) -> PacketSequenceCtrl;
|
||||
|
||||
/// Retrieve data length field
|
||||
fn data_len(&self) -> u16;
|
||||
|
||||
/// Retrieve 13 bit Packet Identification field. Can usually be retrieved with a bitwise AND
|
||||
/// of the first 2 bytes with 0x1FFF
|
||||
#[inline]
|
||||
fn packet_id_raw(&self) -> u16 {
|
||||
self.packet_id().raw()
|
||||
}
|
||||
/// Retrieve Packet Sequence Count
|
||||
#[inline]
|
||||
fn psc_raw(&self) -> u16 {
|
||||
self.psc().raw()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Retrieve Packet Type (TM: 0, TC: 1)
|
||||
fn ptype(&self) -> PacketType {
|
||||
// This call should never fail because only 0 and 1 can be passed to the try_from call
|
||||
self.packet_id().ptype
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_tm(&self) -> bool {
|
||||
self.ptype() == PacketType::Tm
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_tc(&self) -> bool {
|
||||
self.ptype() == PacketType::Tc
|
||||
}
|
||||
|
||||
/// Retrieve the 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
|
||||
#[inline]
|
||||
fn apid(&self) -> u16 {
|
||||
self.packet_id().apid
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ssc(&self) -> u16 {
|
||||
self.psc().ssc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sequence_flags(&self) -> SequenceFlags {
|
||||
// This call should never fail because the mask ensures that only valid values are passed
|
||||
// into the try_from function
|
||||
self.psc().seq_flags
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CcsdsPrimaryHeader {
|
||||
fn from_composite_fields(
|
||||
packet_id: PacketId,
|
||||
psc: PacketSequenceCtrl,
|
||||
data_len: u16,
|
||||
version: Option<u8>,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
pub mod srd {
|
||||
use crate::{
|
||||
CcsdsPacket, CcsdsPrimaryHeader, PacketId, PacketSequenceCtrl, PacketType,
|
||||
SequenceFlags,
|
||||
};
|
||||
|
||||
/// Space Packet Primary Header according to CCSDS 133.0-B-2
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
pub struct SpHeader {
|
||||
pub version: u8,
|
||||
pub packet_id: PacketId,
|
||||
pub psc: PacketSequenceCtrl,
|
||||
pub data_len: u16,
|
||||
}
|
||||
impl Default for SpHeader {
|
||||
fn default() -> Self {
|
||||
SpHeader {
|
||||
version: 0,
|
||||
packet_id: PacketId {
|
||||
ptype: PacketType::Tm,
|
||||
apid: 0,
|
||||
sec_header_flag: true,
|
||||
},
|
||||
psc: PacketSequenceCtrl {
|
||||
seq_flags: SequenceFlags::Unsegmented,
|
||||
ssc: 0,
|
||||
},
|
||||
data_len: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SpHeader {
|
||||
pub fn new(apid: u16, ptype: PacketType, ssc: u16) -> Option<Self> {
|
||||
if ssc > num::pow(2, 14) || apid > num::pow(2, 11) {
|
||||
return None;
|
||||
}
|
||||
let mut header = SpHeader::default();
|
||||
header.packet_id.apid = apid;
|
||||
header.packet_id.ptype = ptype;
|
||||
header.psc.ssc = ssc;
|
||||
Some(header)
|
||||
}
|
||||
|
||||
pub fn tm(apid: u16, ssc: u16) -> Option<Self> {
|
||||
Self::new(apid, PacketType::Tm, ssc)
|
||||
}
|
||||
|
||||
pub fn tc(apid: u16, ssc: u16) -> Option<Self> {
|
||||
Self::new(apid, PacketType::Tc, ssc)
|
||||
}
|
||||
}
|
||||
|
||||
impl CcsdsPacket for SpHeader {
|
||||
#[inline]
|
||||
fn version(&self) -> u8 {
|
||||
self.version
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn packet_id(&self) -> PacketId {
|
||||
self.packet_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn psc(&self) -> PacketSequenceCtrl {
|
||||
self.psc
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn data_len(&self) -> u16 {
|
||||
self.data_len
|
||||
}
|
||||
}
|
||||
|
||||
impl CcsdsPrimaryHeader for SpHeader {
|
||||
fn from_composite_fields(
|
||||
packet_id: PacketId,
|
||||
psc: PacketSequenceCtrl,
|
||||
data_len: u16,
|
||||
version: Option<u8>,
|
||||
) -> Self {
|
||||
let mut version_to_set = 0b000;
|
||||
if let Some(version) = version {
|
||||
version_to_set = version;
|
||||
}
|
||||
SpHeader {
|
||||
version: version_to_set,
|
||||
packet_id,
|
||||
psc,
|
||||
data_len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sph_from_other!(SpHeader, crate::zc::SpHeader);
|
||||
}
|
||||
|
||||
pub mod zc {
|
||||
use crate::{
|
||||
CcsdsPacket, CcsdsPrimaryHeader, PacketId, PacketSequenceCtrl, VERSION_MASK,
|
||||
};
|
||||
use zerocopy::byteorder::NetworkEndian;
|
||||
use zerocopy::{AsBytes, FromBytes, Unaligned, U16};
|
||||
|
||||
#[derive(FromBytes, AsBytes, Unaligned, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct SpHeader {
|
||||
version_packet_id: U16<NetworkEndian>,
|
||||
psc: U16<NetworkEndian>,
|
||||
data_len: U16<NetworkEndian>,
|
||||
}
|
||||
|
||||
impl SpHeader {
|
||||
pub fn new(
|
||||
packet_id: PacketId,
|
||||
psc: PacketSequenceCtrl,
|
||||
data_len: u16,
|
||||
version: Option<u8>,
|
||||
) -> Self {
|
||||
let mut version_packet_id = packet_id.raw();
|
||||
if let Some(version) = version {
|
||||
version_packet_id = ((version as u16) << 13) | packet_id.raw()
|
||||
}
|
||||
SpHeader {
|
||||
version_packet_id: U16::from(version_packet_id),
|
||||
psc: U16::from(psc.raw()),
|
||||
data_len: U16::from(data_len),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bytes(slice: impl AsRef<[u8]>) -> Option<Self> {
|
||||
SpHeader::read_from(slice.as_ref())
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self, mut slice: impl AsMut<[u8]>) -> Option<()> {
|
||||
self.write_to(slice.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl CcsdsPacket for SpHeader {
|
||||
#[inline]
|
||||
fn version(&self) -> u8 {
|
||||
((self.version_packet_id.get() >> 13) as u8) & 0b111
|
||||
}
|
||||
|
||||
fn packet_id_raw(&self) -> u16 {
|
||||
self.version_packet_id.get() & (!VERSION_MASK)
|
||||
}
|
||||
fn packet_id(&self) -> PacketId {
|
||||
PacketId::from(self.packet_id_raw())
|
||||
}
|
||||
|
||||
fn psc_raw(&self) -> u16 {
|
||||
self.psc.get()
|
||||
}
|
||||
|
||||
fn psc(&self) -> PacketSequenceCtrl {
|
||||
PacketSequenceCtrl::from(self.psc_raw())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn data_len(&self) -> u16 {
|
||||
self.data_len.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl CcsdsPrimaryHeader for SpHeader {
|
||||
fn from_composite_fields(
|
||||
packet_id: PacketId,
|
||||
psc: PacketSequenceCtrl,
|
||||
data_len: u16,
|
||||
version: Option<u8>,
|
||||
) -> Self {
|
||||
SpHeader::new(packet_id, psc, data_len, version)
|
||||
}
|
||||
}
|
||||
|
||||
sph_from_other!(SpHeader, crate::srd::SpHeader);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::srd::SpHeader;
|
||||
use crate::{zc, CcsdsPacket, PacketId, PacketSequenceCtrl, PacketType, SequenceFlags};
|
||||
use postcard::{from_bytes, to_stdvec};
|
||||
|
||||
#[test]
|
||||
fn test_helpers() {
|
||||
let packet_id = PacketId {
|
||||
ptype: PacketType::Tm,
|
||||
sec_header_flag: false,
|
||||
apid: 0x42,
|
||||
};
|
||||
assert_eq!(packet_id.raw(), 0x42);
|
||||
let packet_id_from_raw = PacketId::from(packet_id.raw());
|
||||
assert_eq!(packet_id_from_raw, packet_id);
|
||||
|
||||
let packet_id_invalid = PacketId::new(PacketType::Tc, true, 0xFFFF);
|
||||
assert!(packet_id_invalid.is_none());
|
||||
let packet_id_from_new = PacketId::new(PacketType::Tm, false, 0x42).unwrap();
|
||||
assert_eq!(packet_id_from_new, packet_id);
|
||||
let psc = PacketSequenceCtrl {
|
||||
seq_flags: SequenceFlags::ContinuationSegment,
|
||||
ssc: 77,
|
||||
};
|
||||
assert_eq!(psc.raw(), 77);
|
||||
let psc_from_raw = PacketSequenceCtrl::from(psc.raw());
|
||||
assert_eq!(psc_from_raw, psc);
|
||||
|
||||
let psc_invalid = PacketSequenceCtrl::new(SequenceFlags::FirstSegment, 0xFFFF);
|
||||
assert!(psc_invalid.is_none());
|
||||
let psc_from_new = PacketSequenceCtrl::new(SequenceFlags::ContinuationSegment, 77).unwrap();
|
||||
assert_eq!(psc_from_new, psc);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deser_internally() {
|
||||
let sp_header = SpHeader::tc(0x42, 12).expect("Error creating SP header");
|
||||
assert_eq!(sp_header.version(), 0b000);
|
||||
assert!(sp_header.is_tc());
|
||||
assert!(sp_header.sec_header_flag());
|
||||
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
||||
assert_eq!(sp_header.ssc(), 12);
|
||||
assert_eq!(sp_header.apid(), 0x42);
|
||||
assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
|
||||
assert_eq!(sp_header.data_len(), 0);
|
||||
let output = to_stdvec(&sp_header).unwrap();
|
||||
let sp_header: SpHeader = from_bytes(&output).unwrap();
|
||||
assert_eq!(sp_header.version, 0b000);
|
||||
assert!(sp_header.packet_id.sec_header_flag);
|
||||
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
||||
assert_eq!(sp_header.ssc(), 12);
|
||||
assert_eq!(sp_header.apid(), 0x42);
|
||||
assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
|
||||
assert_eq!(sp_header.packet_id_raw(), 0x1842);
|
||||
assert_eq!(sp_header.psc_raw(), 0xC00C);
|
||||
assert_eq!(sp_header.version(), 0b000);
|
||||
assert_eq!(sp_header.data_len, 0);
|
||||
|
||||
let mut sp_header = SpHeader::tm(0x7, 22).expect("Error creating SP header");
|
||||
sp_header.data_len = 36;
|
||||
assert_eq!(sp_header.version(), 0b000);
|
||||
assert!(sp_header.is_tm());
|
||||
assert!(sp_header.sec_header_flag());
|
||||
assert_eq!(sp_header.ptype(), PacketType::Tm);
|
||||
assert_eq!(sp_header.ssc(), 22);
|
||||
assert_eq!(sp_header.apid(), 0x07);
|
||||
assert_eq!(sp_header.sequence_flags(), SequenceFlags::Unsegmented);
|
||||
assert_eq!(sp_header.packet_id_raw(), 0x0807);
|
||||
assert_eq!(sp_header.psc_raw(), 0xC016);
|
||||
assert_eq!(sp_header.data_len(), 36);
|
||||
assert_eq!(sp_header.version(), 0b000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deser_zerocopy() {
|
||||
use zerocopy::AsBytes;
|
||||
|
||||
let sp_header = SpHeader::tc(0x7FF, num::pow(2, 14) - 1).expect("Error creating SP header");
|
||||
assert_eq!(sp_header.packet_id.ptype, PacketType::Tc);
|
||||
assert!(sp_header.is_tc());
|
||||
let sp_header_zc = zc::SpHeader::from(sp_header);
|
||||
let slice = sp_header_zc.as_bytes();
|
||||
assert_eq!(slice.len(), 6);
|
||||
assert_eq!(slice[0], 0x1F);
|
||||
assert_eq!(slice[1], 0xFF);
|
||||
assert_eq!(slice[2], 0xFF);
|
||||
assert_eq!(slice[3], 0xFF);
|
||||
assert_eq!(slice[4], 0x00);
|
||||
assert_eq!(slice[5], 0x00);
|
||||
|
||||
let mut slice = [0; 6];
|
||||
sp_header_zc.write_to(slice.as_mut_slice());
|
||||
assert_eq!(slice.len(), 6);
|
||||
assert_eq!(slice[0], 0x1F);
|
||||
assert_eq!(slice[1], 0xFF);
|
||||
assert_eq!(slice[2], 0xFF);
|
||||
assert_eq!(slice[3], 0xFF);
|
||||
assert_eq!(slice[4], 0x00);
|
||||
assert_eq!(slice[5], 0x00);
|
||||
|
||||
let mut test_vec = vec![0_u8; 6];
|
||||
let slice = test_vec.as_mut_slice();
|
||||
sp_header_zc.write_to(slice);
|
||||
let slice = test_vec.as_slice();
|
||||
assert_eq!(slice.len(), 6);
|
||||
assert_eq!(slice[0], 0x1F);
|
||||
assert_eq!(slice[1], 0xFF);
|
||||
assert_eq!(slice[2], 0xFF);
|
||||
assert_eq!(slice[3], 0xFF);
|
||||
assert_eq!(slice[4], 0x00);
|
||||
assert_eq!(slice[5], 0x00);
|
||||
|
||||
let sp_header = zc::SpHeader::from_bytes(slice);
|
||||
assert!(sp_header.is_some());
|
||||
let sp_header = sp_header.unwrap();
|
||||
println!("Header: {:?}", sp_header);
|
||||
assert_eq!(sp_header.version(), 0b000);
|
||||
assert_eq!(sp_header.packet_id_raw(), 0x1FFF);
|
||||
assert_eq!(sp_header.apid(), 0x7FF);
|
||||
assert_eq!(sp_header.ptype(), PacketType::Tc);
|
||||
assert_eq!(sp_header.data_len(), 0);
|
||||
}
|
||||
}
|
43
spacepackets/src/main.rs
Normal file
43
spacepackets/src/main.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use postcard::to_stdvec;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zerocopy::byteorder::{I32, U16};
|
||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned};
|
||||
|
||||
#[derive(AsBytes, FromBytes, Unaligned, Debug, Eq, PartialEq)]
|
||||
#[repr(C, packed)]
|
||||
struct ZeroCopyTest {
|
||||
some_bool: u8,
|
||||
some_u16: U16<NetworkEndian>,
|
||||
some_i32: I32<NetworkEndian>,
|
||||
some_float: [u8; 4],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
struct PostcardTest {
|
||||
some_bool: u8,
|
||||
some_u16: u16,
|
||||
some_i32: i32,
|
||||
some_float: f32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let pc_test = PostcardTest {
|
||||
some_bool: true as u8,
|
||||
some_u16: 0x42,
|
||||
some_i32: -200,
|
||||
some_float: 7.7_f32,
|
||||
};
|
||||
|
||||
let out = to_stdvec(&pc_test).unwrap();
|
||||
println!("{:#04x?}", out);
|
||||
|
||||
let sample_hk = ZeroCopyTest {
|
||||
some_bool: true as u8,
|
||||
some_u16: U16::from(0x42),
|
||||
some_i32: I32::from(-200),
|
||||
some_float: 7.7_f32.to_ne_bytes(),
|
||||
};
|
||||
let mut slice = [0; 11];
|
||||
sample_hk.write_to(slice.as_mut_slice());
|
||||
println!("{:#04x?}", slice);
|
||||
}
|
163
spacepackets/src/tc.rs
Normal file
163
spacepackets/src/tc.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use crc::{Crc, CRC_16_IBM_3740};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// All PUS versions. Only PUS C is supported by this library
|
||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum PusVersion {
|
||||
EsaPus = 0,
|
||||
PusA = 1,
|
||||
PusC = 2,
|
||||
}
|
||||
|
||||
pub const CRC_CCITT_FALSE: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
||||
|
||||
pub mod zc {
|
||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned, U16};
|
||||
|
||||
#[derive(FromBytes, AsBytes, Unaligned)]
|
||||
#[repr(C)]
|
||||
pub struct PusTcDataFieldHeader {
|
||||
version_ack: u8,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
source_id: U16<NetworkEndian>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod srd {
|
||||
use crate::ecss::PusPacket;
|
||||
use crate::srd::SpHeader;
|
||||
use crate::tc::{PusVersion, CRC_CCITT_FALSE};
|
||||
use crate::{CcsdsPacket, PacketError, PacketId, PacketSequenceCtrl, PacketType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct PusTcDataFieldHeader {
|
||||
pub service: u8,
|
||||
pub subservice: u8,
|
||||
pub source_id: u16,
|
||||
pub ack: u8,
|
||||
pub version: PusVersion,
|
||||
}
|
||||
|
||||
impl PusTcDataFieldHeader {
|
||||
pub fn new(service: u8, subservice: u8, ack: u8) -> Self {
|
||||
PusTcDataFieldHeader {
|
||||
service,
|
||||
subservice,
|
||||
ack,
|
||||
source_id: 0,
|
||||
version: PusVersion::PusC,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
||||
pub struct PusTc<'slice> {
|
||||
pub sph: SpHeader,
|
||||
pub data_field_header: PusTcDataFieldHeader,
|
||||
raw_data: Option<&'slice [u8]>,
|
||||
app_data: Option<&'slice [u8]>,
|
||||
crc16: Option<u16>,
|
||||
}
|
||||
|
||||
impl<'slice> PusTc<'slice> {
|
||||
pub fn new(
|
||||
sph: &mut SpHeader,
|
||||
service: u8,
|
||||
subservice: u8,
|
||||
app_data: Option<&'slice [u8]>,
|
||||
) -> Self {
|
||||
sph.packet_id.ptype = PacketType::Tc;
|
||||
PusTc {
|
||||
sph: *sph,
|
||||
raw_data: None,
|
||||
app_data,
|
||||
data_field_header: PusTcDataFieldHeader::new(service, subservice, 0b1111),
|
||||
crc16: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, mut slice: impl AsMut<[u8]>) -> Result<(), PacketError> {
|
||||
let sph_zc = crate::zc::SpHeader::from(self.sph);
|
||||
if slice.as_mut().len() < 6 {
|
||||
return Err(PacketError::ToBytesSliceTooSmall(6));
|
||||
}
|
||||
sph_zc
|
||||
.to_bytes(slice)
|
||||
.ok_or(PacketError::ToBytesZeroCopyError)?;
|
||||
// TODO: Finish impl
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl CcsdsPacket for PusTc<'_> {
|
||||
fn version(&self) -> u8 {
|
||||
self.sph.version
|
||||
}
|
||||
|
||||
fn packet_id(&self) -> PacketId {
|
||||
self.sph.packet_id
|
||||
}
|
||||
|
||||
fn psc(&self) -> PacketSequenceCtrl {
|
||||
self.sph.psc
|
||||
}
|
||||
|
||||
fn data_len(&self) -> u16 {
|
||||
self.sph.data_len
|
||||
}
|
||||
}
|
||||
|
||||
impl PusPacket for PusTc<'_> {
|
||||
fn service(&self) -> u8 {
|
||||
self.data_field_header.service
|
||||
}
|
||||
|
||||
fn subservice(&self) -> u8 {
|
||||
self.data_field_header.subservice
|
||||
}
|
||||
|
||||
fn source_id(&self) -> u16 {
|
||||
self.data_field_header.source_id
|
||||
}
|
||||
|
||||
fn ack_flags(&self) -> u8 {
|
||||
self.data_field_header.ack
|
||||
}
|
||||
|
||||
fn user_data(&self) -> Option<&[u8]> {
|
||||
self.app_data
|
||||
}
|
||||
|
||||
fn crc16(&self) -> Option<u16> {
|
||||
self.crc16
|
||||
}
|
||||
|
||||
fn verify(&mut self) -> bool {
|
||||
let mut digest = CRC_CCITT_FALSE.digest();
|
||||
if self.raw_data.is_none() {
|
||||
return false;
|
||||
}
|
||||
digest.update(self.raw_data.unwrap().as_ref());
|
||||
if digest.finalize() == 0 {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::srd::SpHeader;
|
||||
use crate::tc::srd::PusTc;
|
||||
use postcard::to_stdvec;
|
||||
|
||||
#[test]
|
||||
fn test_tc() {
|
||||
let mut sph = SpHeader::tc(0x42, 12).unwrap();
|
||||
let pus_tc = PusTc::new(&mut sph, 1, 1, None);
|
||||
let out = to_stdvec(&pus_tc).unwrap();
|
||||
println!("Vector {:#04x?} with length {}", out, out.len());
|
||||
}
|
||||
}
|
1
spacepackets/src/tm.rs
Normal file
1
spacepackets/src/tm.rs
Normal file
@ -0,0 +1 @@
|
||||
|
Reference in New Issue
Block a user