use deku::prelude::*;
use crate::VitaError;
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DekuRead, DekuWrite,
)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PacketHeader {
hword_1: u16,
packet_size: u16,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PacketType {
#[deku(id = 0x0)]
SignalDataWithoutStreamId,
#[deku(id = 0x1)]
SignalData,
#[deku(id = 0x2)]
ExtensionDataWithoutStreamId,
#[deku(id = 0x3)]
ExtensionData,
#[deku(id = 0x4)]
Context,
#[deku(id = 0x5)]
ExtensionContext,
#[deku(id = 0x6)]
Command,
#[deku(id = 0x7)]
ExtensionCommand,
}
impl PacketType {
pub fn has_signal_data_payload(&self) -> bool {
!matches!(
&self,
PacketType::SignalData
| PacketType::ExtensionData
| PacketType::SignalDataWithoutStreamId
| PacketType::ExtensionDataWithoutStreamId
)
}
pub fn has_context_payload(&self) -> bool {
!matches!(&self, PacketType::Context | PacketType::ExtensionContext)
}
pub fn has_command_payload(&self) -> bool {
!matches!(&self, PacketType::Command | PacketType::ExtensionCommand)
}
}
impl TryFrom<u8> for PacketType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == PacketType::SignalDataWithoutStreamId as u8 => {
Ok(PacketType::SignalDataWithoutStreamId)
}
x if x == PacketType::SignalData as u8 => Ok(PacketType::SignalData),
x if x == PacketType::ExtensionDataWithoutStreamId as u8 => {
Ok(PacketType::ExtensionDataWithoutStreamId)
}
x if x == PacketType::ExtensionData as u8 => Ok(PacketType::ExtensionData),
x if x == PacketType::Context as u8 => Ok(PacketType::Context),
x if x == PacketType::ExtensionContext as u8 => Ok(PacketType::ExtensionContext),
x if x == PacketType::Command as u8 => Ok(PacketType::Command),
x if x == PacketType::ExtensionCommand as u8 => Ok(PacketType::ExtensionCommand),
_ => Err(()),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, DekuRead, DekuWrite)]
#[deku(
endian = "endian",
ctx = "endian: deku::ctx::Endian, packet_type: PacketType",
id = "packet_type"
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Indicators {
#[deku(id = "PacketType::SignalData")]
SignalData(SignalDataIndicators),
#[deku(id = "PacketType::Context")]
Context(ContextIndicators),
#[deku(id = "PacketType::Command")]
Command(CommandIndicators),
}
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DekuRead, DekuWrite,
)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SignalDataIndicators {
pub trailer_included: bool,
pub not_a_vita490_packet: bool,
pub signal_spectral_data: bool,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TimestampMode {
#[deku(id = 0x0)]
PreciseTiming,
#[deku(id = 0x1)]
GeneralTiming,
}
impl TryFrom<bool> for TimestampMode {
type Error = ();
fn try_from(value: bool) -> Result<Self, Self::Error> {
if value {
Ok(TimestampMode::GeneralTiming)
} else {
Ok(TimestampMode::PreciseTiming)
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, DekuRead, DekuWrite)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ContextIndicators {
pub not_a_vita490_packet: bool,
pub timestamp_mode: TimestampMode,
}
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, DekuRead, DekuWrite,
)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CommandIndicators {
pub ack_packet: bool,
pub cancellation_packet: bool,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Tsi {
#[deku(id = 0x0)]
Null,
#[deku(id = 0x1)]
Utc,
#[deku(id = 0x2)]
Gps,
#[deku(id = 0x3)]
Other,
}
impl TryFrom<u8> for Tsi {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == Tsi::Null as u8 => Ok(Tsi::Null),
x if x == Tsi::Utc as u8 => Ok(Tsi::Utc),
x if x == Tsi::Gps as u8 => Ok(Tsi::Gps),
x if x == Tsi::Other as u8 => Ok(Tsi::Other),
_ => Err(()),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u8", endian = "endian", ctx = "endian: deku::ctx::Endian")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Tsf {
#[deku(id = 0x0)]
Null,
#[deku(id = 0x1)]
SampleCount,
#[deku(id = 0x2)]
RealTimePs,
#[deku(id = 0x3)]
FreeRunningCount,
}
impl TryFrom<u8> for Tsf {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == Tsf::Null as u8 => Ok(Tsf::Null),
x if x == Tsf::SampleCount as u8 => Ok(Tsf::SampleCount),
x if x == Tsf::RealTimePs as u8 => Ok(Tsf::RealTimePs),
x if x == Tsf::FreeRunningCount as u8 => Ok(Tsf::FreeRunningCount),
_ => Err(()),
}
}
}
impl PacketHeader {
pub fn as_u32(&self) -> u32 {
((self.hword_1 as u32) << 16) | ((self.packet_size as u32) & 0xFFFF)
}
pub fn packet_type(&self) -> PacketType {
(((self.hword_1 >> 12) & 0b1111) as u8).try_into().unwrap()
}
pub fn set_packet_type(&mut self, packet_type: PacketType) {
self.hword_1 &= !(0b1111 << 12);
self.hword_1 |= (packet_type as u16) << 12
}
pub fn class_id_included(&self) -> bool {
self.hword_1 & (1 << 11) > 0
}
pub(crate) fn set_class_id_included(&mut self, included: bool) {
self.hword_1 = (self.hword_1 & !(1 << 11)) | ((included as u16) << 11);
}
pub fn indicators(&self) -> Indicators {
let i1 = self.hword_1 & (1 << 10) > 1;
let i2 = self.hword_1 & (1 << 9) > 1;
let i3 = self.hword_1 & (1 << 8) > 1;
match self.packet_type() {
PacketType::SignalData
| PacketType::SignalDataWithoutStreamId
| PacketType::ExtensionData
| PacketType::ExtensionDataWithoutStreamId => {
Indicators::SignalData(SignalDataIndicators {
trailer_included: i1,
not_a_vita490_packet: i2,
signal_spectral_data: i3,
})
}
PacketType::Context | PacketType::ExtensionContext => {
Indicators::Context(ContextIndicators {
not_a_vita490_packet: i2,
timestamp_mode: i3.try_into().unwrap(),
})
}
PacketType::Command | PacketType::ExtensionCommand => {
Indicators::Command(CommandIndicators {
ack_packet: i1,
cancellation_packet: i3,
})
}
}
}
pub fn set_indicators(&mut self, indicators: Indicators) {
match indicators {
Indicators::SignalData(i) => {
self.hword_1 |= (i.trailer_included as u16) << 10;
self.hword_1 |= (i.not_a_vita490_packet as u16) << 9;
self.hword_1 |= (i.signal_spectral_data as u16) << 8;
}
Indicators::Context(i) => {
self.hword_1 |= (i.not_a_vita490_packet as u16) << 9;
self.hword_1 |= (i.timestamp_mode as u16) << 8;
}
Indicators::Command(i) => {
self.hword_1 |= (i.ack_packet as u16) << 10;
self.hword_1 |= (i.cancellation_packet as u16) << 8;
}
}
}
pub fn is_ack_packet(&self) -> Result<bool, VitaError> {
match self.indicators() {
Indicators::Command(i) => Ok(i.ack_packet),
_ => Err(VitaError::CommandOnly),
}
}
pub fn is_cancellation_packet(&self) -> Result<bool, VitaError> {
match self.indicators() {
Indicators::Command(i) => Ok(i.cancellation_packet),
_ => Err(VitaError::CommandOnly),
}
}
pub fn tsi(&self) -> Tsi {
(((self.hword_1 >> 6) & 0b11) as u8).try_into().unwrap()
}
pub(crate) fn set_tsi(&mut self, tsi: Tsi) {
self.hword_1 = (self.hword_1 & !(0b11 << 6)) | ((tsi as u16) << 6);
}
pub fn tsf(&self) -> Tsf {
(((self.hword_1 >> 4) & 0b11) as u8).try_into().unwrap()
}
pub(crate) fn set_tsf(&mut self, tsf: Tsf) {
self.hword_1 = (self.hword_1 & !(0b11 << 4)) | ((tsf as u16) << 4);
}
pub fn packet_count(&self) -> u8 {
(self.hword_1 & 0b1111) as u8
}
pub fn set_packet_count(&mut self, count: u8) {
let masked_count = (count & 0b1111) as u16;
self.hword_1 = (self.hword_1 & (!0b1111)) | masked_count;
}
pub fn inc_packet_count(&mut self) {
self.set_packet_count((self.packet_count() + 1) % 16);
}
pub fn packet_size(&self) -> u16 {
self.packet_size
}
pub fn set_packet_size(&mut self, n_words: u16) {
self.packet_size = n_words;
}
pub fn stream_id_included(&self) -> bool {
!matches!(
&self.packet_type(),
PacketType::SignalDataWithoutStreamId | PacketType::ExtensionDataWithoutStreamId
)
}
pub fn integer_timestamp_included(&self) -> bool {
self.tsi() != Tsi::Null
}
pub fn fractional_timestamp_included(&self) -> bool {
self.tsf() != Tsf::Null
}
pub fn trailer_included(&self) -> bool {
match &self.indicators() {
Indicators::SignalData(i) => i.trailer_included,
_ => false,
}
}
pub fn payload_size_words(&self) -> usize {
let mut ret = self.packet_size as usize - 1;
if self.stream_id_included() {
ret -= 1;
}
if self.class_id_included() {
ret -= 2;
}
if self.integer_timestamp_included() {
ret -= 1;
}
if self.fractional_timestamp_included() {
ret -= 2;
}
if self.trailer_included() {
ret -= 1;
}
ret
}
pub fn new_signal_data_header() -> PacketHeader {
let mut ret = PacketHeader {
hword_1: 0,
packet_size: 0,
};
ret.set_packet_type(PacketType::SignalData);
ret.set_indicators(Indicators::SignalData(SignalDataIndicators {
trailer_included: false,
not_a_vita490_packet: false,
signal_spectral_data: false,
}));
ret
}
pub fn new_context_header() -> PacketHeader {
let mut ret = PacketHeader {
hword_1: 0,
packet_size: 0,
};
ret.set_packet_type(PacketType::Context);
ret.set_indicators(Indicators::Context(ContextIndicators {
not_a_vita490_packet: false,
timestamp_mode: TimestampMode::GeneralTiming,
}));
ret
}
pub fn new_control_header() -> PacketHeader {
let mut ret = PacketHeader::default();
ret.set_packet_type(PacketType::Command);
ret.set_indicators(Indicators::Command(CommandIndicators {
ack_packet: false,
cancellation_packet: false,
}));
ret
}
pub fn new_cancellation_header() -> PacketHeader {
let mut ret = PacketHeader::default();
ret.set_packet_type(PacketType::Command);
ret.set_indicators(Indicators::Command(CommandIndicators {
ack_packet: false,
cancellation_packet: true,
}));
ret
}
pub fn new_ack_header() -> PacketHeader {
let mut ret = PacketHeader::default();
ret.set_packet_type(PacketType::Command);
ret.set_indicators(Indicators::Command(CommandIndicators {
ack_packet: true,
cancellation_packet: false,
}));
ret
}
}
#[cfg(test)]
mod tests {
#[test]
fn packet_header() {
use crate::prelude::*;
let packet = Vrt::new_control_packet();
assert_eq!(packet.header().packet_type(), PacketType::Command);
assert_eq!(packet.header().as_u32() >> 28, 0b0110);
}
#[test]
fn set_class_id_sets_class_id_included_bit() {
use crate::prelude::*;
let mut packet = Vrt::new_signal_data_packet();
assert!(!packet.header().class_id_included());
let class_id = Some(ClassIdentifier::default());
packet.set_class_id(class_id);
assert!(packet.header().class_id_included());
}
}