1#[derive(Clone, Copy, Debug)]
19pub struct Tpx3Packet(u64);
20
21impl Tpx3Packet {
22 pub const TPX3_HEADER_MAGIC: u64 = 0x3358_5054;
24
25 #[inline]
27 #[must_use]
28 pub const fn new(raw: u64) -> Self {
29 Self(raw)
30 }
31
32 #[inline]
34 #[must_use]
35 pub const fn raw(&self) -> u64 {
36 self.0
37 }
38
39 #[inline]
41 #[must_use]
42 pub const fn is_header(&self) -> bool {
43 (self.0 & 0xFFFF_FFFF) == Self::TPX3_HEADER_MAGIC
44 }
45
46 #[inline]
48 #[must_use]
49 pub const fn is_tdc(&self) -> bool {
50 (self.0 >> 56) & 0xFF == 0x6F
51 }
52
53 #[inline]
55 #[must_use]
56 pub const fn is_hit(&self) -> bool {
57 (self.0 >> 60) & 0xF == 0xB
58 }
59
60 #[inline]
62 #[must_use]
63 pub const fn packet_type(&self) -> u8 {
64 ((self.0 >> 56) & 0xFF) as u8
65 }
66
67 #[inline]
69 #[must_use]
70 pub const fn chip_id(&self) -> u8 {
71 ((self.0 >> 32) & 0xFF) as u8
72 }
73
74 #[inline]
76 #[must_use]
77 pub const fn pixel_address(&self) -> u16 {
78 ((self.0 >> 44) & 0xFFFF) as u16
79 }
80
81 #[inline]
83 #[must_use]
84 pub const fn toa(&self) -> u16 {
85 ((self.0 >> 30) & 0x3FFF) as u16
86 }
87
88 #[inline]
90 #[must_use]
91 pub const fn tot(&self) -> u16 {
92 ((self.0 >> 20) & 0x3FF) as u16
93 }
94
95 #[inline]
97 #[must_use]
98 pub const fn fine_toa(&self) -> u8 {
99 ((self.0 >> 16) & 0xF) as u8
100 }
101
102 #[inline]
104 #[must_use]
105 pub const fn spidr_time(&self) -> u16 {
106 (self.0 & 0xFFFF) as u16
107 }
108
109 #[inline]
111 #[must_use]
112 pub const fn tdc_timestamp(&self) -> u32 {
113 ((self.0 >> 12) & 0x3FFF_FFFF) as u32
114 }
115
116 #[inline]
124 #[must_use]
125 pub const fn pixel_coordinates(&self) -> (u16, u16) {
126 let addr = self.pixel_address();
127 let dcol = (addr & 0xFE00) >> 8;
128 let spix = (addr & 0x1F8) >> 1;
129 let pix = addr & 0x7;
130 let x = dcol + (pix >> 2);
131 let y = spix + (pix & 0x3);
132 (x, y)
133 }
134}
135
136impl From<u64> for Tpx3Packet {
137 fn from(raw: u64) -> Self {
138 Self::new(raw)
139 }
140}
141
142impl Tpx3Packet {
143 #[inline]
145 #[must_use]
146 pub fn from_bytes(bytes: [u8; 8]) -> Self {
147 Self::new(u64::from_le_bytes(bytes))
148 }
149
150 #[inline]
152 #[must_use]
153 pub const fn is_pixel_data(&self) -> bool {
154 self.is_hit()
155 }
156
157 #[inline]
162 #[must_use]
163 pub fn timestamp_coarse(&self) -> u32 {
164 let spidr = u32::from(self.spidr_time());
165 let toa = u32::from(self.toa());
166
167 (spidr << 14) | toa
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_header_detection() {
178 let header = Tpx3Packet::new(0x3358_5054);
179 assert!(header.is_header());
180
181 let non_header = Tpx3Packet::new(0x1234_5678);
182 assert!(!non_header.is_header());
183 }
184
185 #[test]
186 fn test_tdc_detection() {
187 let tdc = Tpx3Packet::new(0x6F00_0000_0000_0000);
188 assert!(tdc.is_tdc());
189 assert!(!tdc.is_hit());
190 }
191
192 #[test]
193 fn test_hit_detection() {
194 let hit = Tpx3Packet::new(0xB000_0000_0000_0000);
195 assert!(hit.is_hit());
196 assert!(!hit.is_tdc());
197 }
198
199 #[test]
200 fn test_pixel_coordinate_decode() {
201 let packet = Tpx3Packet::new(0xB000_0000_0000_0000);
204 let (x, y) = packet.pixel_coordinates();
205 assert_eq!(x, 0);
206 assert_eq!(y, 0);
207 }
208
209 #[test]
210 fn test_tdc_timestamp_extraction() {
211 let tdc = Tpx3Packet::new(0x6F00_0001_2345_6000);
213 let ts = tdc.tdc_timestamp();
214 assert!(ts > 0);
216 }
217}