#[derive(Clone, Copy, Debug)]
pub struct Tpx3Packet(u64);
impl Tpx3Packet {
pub const TPX3_HEADER_MAGIC: u64 = 0x3358_5054;
#[inline]
#[must_use]
pub const fn new(raw: u64) -> Self {
Self(raw)
}
#[inline]
#[must_use]
pub const fn raw(&self) -> u64 {
self.0
}
#[inline]
#[must_use]
pub const fn is_header(&self) -> bool {
(self.0 & 0xFFFF_FFFF) == Self::TPX3_HEADER_MAGIC
}
#[inline]
#[must_use]
pub const fn is_tdc(&self) -> bool {
(self.0 >> 56) & 0xFF == 0x6F
}
#[inline]
#[must_use]
pub const fn is_hit(&self) -> bool {
(self.0 >> 60) & 0xF == 0xB
}
#[inline]
#[must_use]
pub const fn packet_type(&self) -> u8 {
((self.0 >> 56) & 0xFF) as u8
}
#[inline]
#[must_use]
pub const fn chip_id(&self) -> u8 {
((self.0 >> 32) & 0xFF) as u8
}
#[inline]
#[must_use]
pub const fn pixel_address(&self) -> u16 {
((self.0 >> 44) & 0xFFFF) as u16
}
#[inline]
#[must_use]
pub const fn toa(&self) -> u16 {
((self.0 >> 30) & 0x3FFF) as u16
}
#[inline]
#[must_use]
pub const fn tot(&self) -> u16 {
((self.0 >> 20) & 0x3FF) as u16
}
#[inline]
#[must_use]
pub const fn fine_toa(&self) -> u8 {
((self.0 >> 16) & 0xF) as u8
}
#[inline]
#[must_use]
pub const fn spidr_time(&self) -> u16 {
(self.0 & 0xFFFF) as u16
}
#[inline]
#[must_use]
pub const fn tdc_timestamp(&self) -> u32 {
((self.0 >> 12) & 0x3FFF_FFFF) as u32
}
#[inline]
#[must_use]
pub const fn pixel_coordinates(&self) -> (u16, u16) {
let addr = self.pixel_address();
let dcol = (addr & 0xFE00) >> 8;
let spix = (addr & 0x1F8) >> 1;
let pix = addr & 0x7;
let x = dcol + (pix >> 2);
let y = spix + (pix & 0x3);
(x, y)
}
}
impl From<u64> for Tpx3Packet {
fn from(raw: u64) -> Self {
Self::new(raw)
}
}
impl Tpx3Packet {
#[inline]
#[must_use]
pub fn from_bytes(bytes: [u8; 8]) -> Self {
Self::new(u64::from_le_bytes(bytes))
}
#[inline]
#[must_use]
pub const fn is_pixel_data(&self) -> bool {
self.is_hit()
}
#[inline]
#[must_use]
pub fn timestamp_coarse(&self) -> u32 {
let spidr = u32::from(self.spidr_time());
let toa = u32::from(self.toa());
(spidr << 14) | toa
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_header_detection() {
let header = Tpx3Packet::new(0x3358_5054);
assert!(header.is_header());
let non_header = Tpx3Packet::new(0x1234_5678);
assert!(!non_header.is_header());
}
#[test]
fn test_tdc_detection() {
let tdc = Tpx3Packet::new(0x6F00_0000_0000_0000);
assert!(tdc.is_tdc());
assert!(!tdc.is_hit());
}
#[test]
fn test_hit_detection() {
let hit = Tpx3Packet::new(0xB000_0000_0000_0000);
assert!(hit.is_hit());
assert!(!hit.is_tdc());
}
#[test]
fn test_pixel_coordinate_decode() {
let packet = Tpx3Packet::new(0xB000_0000_0000_0000);
let (x, y) = packet.pixel_coordinates();
assert_eq!(x, 0);
assert_eq!(y, 0);
}
#[test]
fn test_tdc_timestamp_extraction() {
let tdc = Tpx3Packet::new(0x6F00_0001_2345_6000);
let ts = tdc.tdc_timestamp();
assert!(ts > 0);
}
}