use std::io::{Error, ErrorKind, Read};
use byteorder::{BigEndian, ReadBytesExt};
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BypassFlag {
TypeA = 0,
TypeB = 1,
}
impl BypassFlag {
pub fn from_u8(val: u8) -> Result<Self, Error> {
match val {
0 => Ok(Self::TypeA),
1 => Ok(Self::TypeB),
val => Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid BypassFlag value {val:}. Can only be 1 bit."),
)),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ControlFlag {
TypeD = 0,
TypeC = 1,
}
impl ControlFlag {
pub fn from_u8(val: u8) -> Result<Self, Error> {
match val {
0 => Ok(Self::TypeD),
1 => Ok(Self::TypeC),
val => Err(Error::new(
ErrorKind::InvalidData,
format!("Invalid ControlFlag value {val:}. Can only be 1 bit."),
)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TCPrimaryHeader {
pub tfvn: u8,
pub bypass_flag: BypassFlag,
pub control_flag: ControlFlag,
pub scid: u16,
pub vcid: u8,
pub sequence_number: u8,
}
impl TCPrimaryHeader {
pub fn validate(&self) -> Result<(), Error> {
if self.tfvn > 3 {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"Transfer frame version number must be <=3 but found {}",
self.tfvn
),
));
}
if self.scid > 1023 {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Spacecraft ID must be <=1023 but found {}", self.scid),
));
}
if self.vcid > 63 {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Virtual Channel ID must be <=63 but found {}", self.vcid),
));
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TCTransferFrame {
header: TCPrimaryHeader,
payload: Vec<u8>,
}
impl TCTransferFrame {
pub fn new(header: TCPrimaryHeader, payload: Vec<u8>) -> Result<Self, Error> {
header.validate()?;
if payload.len() > 1019 {
return Err(Error::new(
ErrorKind::InvalidData,
format!(
"Payload length must be <=1019 bytes but supplied payload has length {}",
payload.len()
),
));
}
Ok(Self { header, payload })
}
pub fn header(&self) -> TCPrimaryHeader {
self.header
}
pub fn payload(&self) -> &[u8] {
self.payload.as_slice()
}
pub fn encode(mut self) -> Vec<u8> {
let TCPrimaryHeader {
tfvn,
bypass_flag,
control_flag,
scid,
vcid,
sequence_number,
} = self.header;
let first_word = {
( (tfvn as u16 & 0x3_u16) << 14)
| ((bypass_flag as u16 & 0x1_u16) << 13)
| ((control_flag as u16 & 0x1_u16) << 12)
| (scid & 0x3ff_u16)
};
let encoded_len = (self.payload.len() - 1 + 5) as u16;
let second_word = { ((vcid as u16 & 0x3f_u16) << 10) | (encoded_len & 0x3ff_u16) };
let mut message = first_word.to_be_bytes().to_vec();
message.extend_from_slice(&second_word.to_be_bytes());
message.push(sequence_number);
message.append(&mut self.payload);
message
}
pub fn decode<R: Read>(buffer: &mut R) -> Result<Self, Error> {
let first_word = buffer.read_u16::<BigEndian>()?;
let second_word = buffer.read_u16::<BigEndian>()?;
let payload_len = (second_word & 0x3ff_u16) + 1 - 5;
let header = TCPrimaryHeader {
tfvn: ((first_word >> 14) & 0x3_u16) as u8,
bypass_flag: BypassFlag::from_u8(((first_word >> 13) & 0x1_u16) as u8)?,
control_flag: ControlFlag::from_u8(((first_word >> 12) & 0x1_u16) as u8)?,
scid: first_word & 0x3ff_u16,
vcid: ((second_word >> 10) & 0x3f_u16) as u8,
sequence_number: buffer.read_u8()?,
};
let mut payload = vec![0_u8; payload_len as usize];
buffer.read_exact(&mut payload)?;
Self::new(header, payload)
}
}
#[cfg(test)]
mod test {
use super::*;
use rstest::rstest;
#[rstest]
#[case(0, 5, 2)]
#[should_panic]
#[case(4, 5, 33)]
#[should_panic]
#[case(0, 1024, 62)]
#[should_panic]
#[case(0, 7, 65)]
fn header_validation(#[case] tfvn: u8, #[case] scid: u16, #[case] vcid: u8) {
let header = TCPrimaryHeader {
tfvn,
bypass_flag: BypassFlag::TypeA,
control_flag: ControlFlag::TypeC,
scid,
vcid,
sequence_number: 23,
};
assert!(header.validate().is_ok())
}
#[rstest]
#[case(b"some bytes foo bar baz".to_vec())]
#[should_panic]
#[case(vec![0_u8; 2048])]
fn frame_roundtrip(
#[values(BypassFlag::TypeA, BypassFlag::TypeB)] bypass_flag: BypassFlag,
#[values(ControlFlag::TypeD, ControlFlag::TypeD)] control_flag: ControlFlag,
#[case] payload: Vec<u8>,
#[values(0, 33, 1023)] scid: u16,
#[values(0, 3, 7)] vcid: u8,
) {
let expected = TCTransferFrame::new(
TCPrimaryHeader {
tfvn: 0,
bypass_flag,
control_flag,
scid,
vcid,
sequence_number: 23,
},
payload,
)
.unwrap();
let buffer = expected.clone().encode();
let recovered = TCTransferFrame::decode(&mut buffer.as_slice())
.expect("Should be able to roundtrip TCTransferFrame");
assert_eq!(expected, recovered)
}
#[test]
fn tc_compare_spacepy() {
let mut input_bytes: &[u8] = &[
0x22, 0xF6, 0x00, 0x23, 0x00, 0x82, 0x00, 0x0F, 0x00, 0x1D, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x0F, 0x00, 0x1E, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x1F, 0xFF, 0x00,
0x00, 0x00, 0x00, 0x0F, 0xAC, 0x8F, 0x00, 0x68,
];
let expected = TCTransferFrame::new(
TCPrimaryHeader {
tfvn: 0,
bypass_flag: BypassFlag::TypeB,
control_flag: ControlFlag::TypeD,
scid: 758,
vcid: 0,
sequence_number: 0,
},
vec![
0x82, 0x00, 0x0F, 0x00, 0x1D, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x1E, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xAC,
0x8F, 0x00, 0x68,
],
)
.expect("Unable to make test transfer frame.");
let parsed_tc =
TCTransferFrame::decode(&mut input_bytes).expect("unable to parse input bytes.");
assert_eq!(expected, parsed_tc)
}
}