1use crate::prelude::*;
7
8use std::collections::HashSet;
9
10#[cfg_attr(feature="pybindings", pyclass)]
23#[derive(Debug, Clone, PartialEq)]
24pub struct TelemetryEvent {
25 pub header : TelemetryPacketHeader,
26 pub creation_time : u64,
27 pub event_id : u32,
28 pub tracker_hits : Vec<TrackerHit>,
29 pub tracker_oscillators : Vec<u64>,
30 pub tof_event : TofEvent,
31 pub raw_data : Vec<u8>,
36 pub flags0 : u8,
37 pub flags1 : u8,
38 pub version : u8,
39 pub osc_flags : u8,
40 pub oscillator_idx : Vec<u8>,
41 pub expected_tr_hits : u8,
43}
44
45impl TelemetryEvent {
46
47 pub fn new() -> Self {
48 let mut tracker_oscillators = Vec::<u64>::new();
49 for _ in 0..10 {
50 tracker_oscillators.push(0);
51 }
52 Self {
53 header : TelemetryPacketHeader::new(),
54 creation_time : 0,
55 event_id : 0,
56 tracker_hits : Vec::<TrackerHit>::new(),
57 tracker_oscillators : tracker_oscillators,
58 tof_event : TofEvent::new(),
59 raw_data : Vec::<u8>::new(),
60 flags0 : 0,
61 flags1 : 1,
62 version : 0,
63 osc_flags : 0,
64 oscillator_idx : Vec::<u8>::new(),
65 expected_tr_hits : 0,
66 }
67 }
68
69 pub fn has_only_one_hit_per_tlayer(&self) -> bool {
71 let mut layers = HashSet::<u8>::new();
72 for h in &self.tracker_hits {
73 if !layers.insert(h.layer) {
74 return false; }
76 }
77 return true;
78 }
79
80 pub fn pack(&self) -> TelemetryPacket {
86 let mut tp = TelemetryPacket::new();
87 tp.header = self.header.clone();
90 tp.payload = self.to_bytestream();
91 tp
92 }
93
94 pub fn calibrate_trk_hits(&mut self, cali : &TrackerOfflineCalibration)
95 -> Result<(), CalibrationError> {
96 cali.mask_hits(&mut self.tracker_hits);
97 cali.calibrate(&mut self.tracker_hits)?;
98 Ok(())
99 }
100
101 #[cfg(feature="database")]
103 pub fn hydrate(&mut self, tof_paddles : &HashMap<u8,TofPaddle>, trk_strips : &HashMap<u32, TrackerStrip>) {
104 self.tof_event.set_paddles(tof_paddles);
105 for h in &mut self.tracker_hits {
106 h.set_coordinates(trk_strips);
107 }
108 }
109
110 pub fn delete_all_tracker_hits(&mut self) {
113 self.tracker_hits.clear();
114 }
115
116 pub fn add_tracker_hits(&mut self, hits: &Vec<TrackerHit>) {
118 self.tracker_hits.extend_from_slice(hits);
119 self.header.length += (4*hits.len()) as u16;
121 }
122
123 #[cfg(feature="database")]
124 pub fn mask_strips(&mut self, masks : &HashMap<u32, TrackerStripMask>) {
125 let mut clean_hits = Vec::<TrackerHit>::with_capacity(self.tracker_hits.len());
126 for h in &self.tracker_hits {
127 if !masks.contains_key(&h.get_stripid()) {
128 warn!("We don't have a mask information for strip id {}", h.get_stripid());
129 continue;
130 }
131 if masks[&h.get_stripid()].active {
132 clean_hits.push(h.clone());
133 }
134 }
135 self.tracker_hits = clean_hits;
136 }
137
138
139 }
236
237
238impl TelemetryPackable for TelemetryEvent {
239 }
242
243impl Serialization for TelemetryEvent {
244
245 fn from_bytestream(stream : &Vec<u8>,
246 pos : &mut usize)
247 -> Result<Self, SerializationError> {
248 let mut me = Self::new();
249 let version = parse_u8(stream, pos);
250 me.version = version;
251 me.flags0 = parse_u8(stream, pos);
252 if version == 0 {
255 me.flags1 = parse_u8(stream, pos);
256 } else {
257 *pos += 8;
258 }
259
260 me.event_id = parse_u32(stream, pos);
261 let _tof_delim = parse_u8(stream, pos);
263 if stream.len() <= *pos + 2 {
265 error!("Not able to parse merged event!");
266 return Err(SerializationError::StreamTooShort);
267 }
268 let num_tof_bytes = parse_u16(stream, pos) as usize;
269 if stream.len() < *pos+num_tof_bytes {
271 error!("Not enough bytes for TOF packet with {} bytes! Only {} bytes remaining in input", num_tof_bytes, stream.len() - *pos);
272 return Err(SerializationError::StreamTooShort);
273 }
274 let pos_before = *pos;
275 if num_tof_bytes != 0 {
276 let tof_pack = TofPacket::from_bytestream(stream, pos)?;
277 let ts = tof_pack.unpack::<TofEvent>()?;
278 me.tof_event = ts;
280 }
281 if pos_before + num_tof_bytes != *pos {
282 error!("Byte misalignment. Expected {num_tof_bytes}, got {pos} - {pos_before}");
283 return Err(SerializationError::WrongByteSize);
284 }
285 let trk_delim = parse_u8(stream, pos);
286
287 if trk_delim != 0xbb {
289 return Err(SerializationError::HeadInvalid);
290 }
291 if version == 1 {
292 let num_trk_hits = parse_u16(stream, pos);
293 if (*pos + (num_trk_hits as usize)*4 ) > stream.len() {
294 let expected_s = *pos + (num_trk_hits as usize)*4 - stream.len();
295 let actual = stream.len() - *pos;
296 error!("We expect {} more bytes, but see only {}", expected_s, actual);
297 error!("Not enough bytes ({}) for {} tracker hits!", stream.len(), num_trk_hits);
298 return Err(SerializationError::StreamTooShort);
300 }
301 for _ in 0..num_trk_hits {
302 let mut hit = TrackerHit::new();
303 let strip_id = parse_u16(stream, pos);
304 let adc = parse_u16(stream, pos);
305 hit.channel = (strip_id & 0b11111) as u8;
306 hit.module = ((strip_id >> 5) & 0b111) as u8;
307 hit.row = ((strip_id >> 8) & 0b111) as u8;
308 hit.layer = ((strip_id >> 11) & 0b1111) as u8;
309 hit.adc = adc;
310 me.tracker_hits.push(hit);
311 }
312 let oscillators_delimiter = parse_u8(stream, pos);
314 if oscillators_delimiter != 0xcc {
315 return Err(SerializationError::HeadInvalid);
316 }
317 me.osc_flags = parse_u8(stream, pos);
318 let mut oscillator_idx = Vec::<u8>::new();
319 for j in 0..8 {
320 if (me.osc_flags >> j & 0b1) > 0 {
321 oscillator_idx.push(j)
322 }
323 }
324 if (*pos + oscillator_idx.len()*6) > stream.len() {
325 error!("Not enough bytes to parse tracker oscillators!");
326 return Err(SerializationError::StreamTooShort);
327 }
328 for idx in oscillator_idx.iter() {
329 let lower = parse_u32(stream, pos);
330 let upper = parse_u16(stream, pos);
331 let osc : u64 = (upper as u64) << 32 | (lower as u64);
332 me.tracker_oscillators[*idx as usize] = osc;
334 }
335 me.oscillator_idx = oscillator_idx;
336 } else if version == 0 {
337 error!("Unsupported {version}!");
338 return Err(SerializationError::UnsupportedVersion);
339 } else {
340 error!("Unsuported version {version}!");
341 return Err(SerializationError::UnsupportedVersion);
342 }
343 me.expected_tr_hits = me.tracker_hits.len() as u8;
344 Ok(me)
345 }
346
347 fn to_bytestream(&self) -> Vec<u8> {
348 let mut stream = Vec::<u8>::new();
349 stream.push(self.version);
350 stream.push(self.flags0);
351 if self.version == 0 {
352 stream.push(self.flags1);
353 } else {
354 stream.extend_from_slice(&[0u8;8]);
355 }
356 stream.extend_from_slice(&self.event_id.to_le_bytes());
357 stream.push(0xaa); let tof = self.tof_event.pack();
359 let tof_bytes = tof.to_bytestream();
360 let num_tof_bytes = tof_bytes.len() as u16;
361 debug!("Will write {} bytes for tef event to stream!", num_tof_bytes);
362 stream.extend_from_slice(&num_tof_bytes.to_le_bytes());
363 stream.extend_from_slice(&tof_bytes);
364 stream.push(0xbb);
365 stream.extend_from_slice(&(self.tracker_hits.len() as u16).to_le_bytes());
366 for h in &self.tracker_hits {
368 let mut strip_id: u16 = 0;
369 strip_id |= (h.channel as u16) & 0b11111; strip_id |= ((h.module as u16) & 0b111) << 5; strip_id |= ((h.row as u16) & 0b111) << 8; strip_id |= ((h.layer as u16) & 0b1111) << 11;
374 stream.extend_from_slice(&strip_id.to_le_bytes());
375 stream.extend_from_slice(&h.adc.to_le_bytes());
376 }
377 stream.push(0xcc); stream.push(self.osc_flags);
379 for idx in &self.oscillator_idx {
380 let osc = self.tracker_oscillators[*idx as usize];
383 let upper = ((osc >> 32) & (u16::MAX as u64)) as u16;
384 let lower = (osc & (u32::MAX as u64)) as u32;
385 stream.extend_from_slice(&lower.to_le_bytes());
387 stream.extend_from_slice(&upper.to_le_bytes());
388 }
389 return stream;
390 }
391}
392
393impl fmt::Display for TelemetryEvent {
394 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
395 let mut repr = String::from("<TelemetryEvent:");
396 let mut te = self.tof_event.clone();
397 te.calc_gcu_variables();
398 let tof_str = format!("\n {}", self.tof_event);
399 let mut good_hits = 0;
400
401 if self.version == 0 {
402 repr += "\n VERSION 0 NOT SUPPORTED!!";
403 } else if self.version == 1 {
404 for _ in &self.tracker_hits {
405 good_hits += 1;
406 }
407 }
408 repr += &(format!(" {}", self.header));
409 repr += "\n ** ** ** MERGED ** ** **";
410 repr += &(format!("\n version {}", self.version));
411 repr += &(format!("\n event ID {}", self.event_id));
412 repr += "\n ** ** ** TOF GCU VARIABLES ** ** **";
413 repr += &(format!("\n n_hits_umb {}", te.n_hits_umb));
414 repr += &(format!("\n n_hits_cbe {}", te.n_hits_cbe));
415 repr += &(format!("\n n_hits_cor {}", te.n_hits_cor));
416 repr += &(format!("\n tot_edep_umb {}", te.tot_edep_umb));
417 repr += &(format!("\n tot_edep_cbe {}", te.tot_edep_cbe));
418 repr += &(format!("\n tot_edep_cor {}", te.tot_edep_cor));
419 if self.version == 0 {
420 repr += "\n VERSION 0 NOT SUPPORTED!!";
421 }
422 repr += "\n ** ** ** TRACKER ** ** **";
423 if self.version == 0 {
424 repr += "\n VERSION 0 NOT SUPPORTED!!";
425 } else if self.version == 1 {
426 repr += &(format!("\n Trk oscillators {:?}", self.tracker_oscillators));
427 }
428 repr += &(format!("\n N Good Trk Hits {}", good_hits));
429 repr += &tof_str;
430 write!(f,"{}", repr)
431 }
432}
433
434#[cfg(feature="pybindings")]
437#[pymethods]
438impl TelemetryEvent {
439
440 #[getter]
441 #[pyo3(name="exptected_tracker_hits")]
442 fn expected_trk_hits_py(&self) -> u8 {
443 self.expected_tr_hits
444 }
445
446 #[getter]
447 #[pyo3(name="has_only_one_hit_per_tlayer")]
448 fn has_only_one_hit_per_tlayer_py(&self) -> bool {
449 self.has_only_one_hit_per_tlayer()
450 }
451
452 #[getter]
453 #[pyo3(name="has_at_least_expected_trk_hits")]
454 fn has_at_least_expected_trk_hits(&self) -> bool {
455 return self.tracker_hits.len() as u8 >= self.expected_tr_hits
456 }
457
458 #[getter]
459 #[pyo3(name="version")]
460 fn version_py(&self) -> u8 {
461 self.version
462 }
463
464 #[getter]
465 #[pyo3(name="flags0")]
466 fn flags0_py(&self) -> u8 {
467 self.flags0
468 }
469
470 #[getter]
471 #[pyo3(name="flags1")]
472 fn flags1_py(&self) -> u8 {
473 self.flags1
474 }
475
476 #[getter]
483 fn get_header(&self) -> TelemetryPacketHeader {
484 self.header
485 }
486
487 #[getter]
488 fn tracker(&self) -> PyResult<Vec<TrackerHit>> {
489 Ok(self.tracker_hits.clone())
490 }
491
492 #[getter]
493 fn get_event_id(&self) -> u32 {
494 self.event_id
495 }
496
497 #[getter]
503 fn get_run_id(&self) -> Option<u16> {
504 if self.tof_event.run_id == 0 {
505 return None;
506 }
507 Some(self.tof_event.run_id)
508 }
509
510 #[pyo3(name="tof_remove_non_causal_hits")]
515 fn tof_remove_non_causal_hits_py(&mut self) -> Vec<u16> {
516 let mut pids = Vec::<u16>::new();
519 for pid in self.tof_event.remove_non_causal_hits() {
520 pids.push(pid as u16);
521 }
522 pids
523 }
524
525 pub fn tof_remove_elena_hits(&mut self, no_return : bool) -> Option<Vec<TofHit>> {
534 return self.tof_event.remove_elena_hits(no_return);
535 }
536
537 #[pyo3(name="tof_lightspeed_cleaning")]
545 pub fn tof_lightspeed_cleaning_py(&mut self, t_err : f32) -> (Vec<u16>, Vec<f32>) {
546 let mut pids = Vec::<u16>::new();
549 let (pids_rm, twindows) = self.tof_event.lightspeed_cleaning(t_err);
550 for pid in pids_rm {
551 pids.push(pid as u16);
552 }
553 (pids, twindows)
554 }
555
556 #[getter]
566 fn get_tof_time_of_flight(&mut self) -> Option<(f32,f32,f32,f32,f32)> {
567 self.tof_event.get_tof()
568 }
569
570 #[pyo3(name="set_tof_timing_offsets")]
577 pub fn set_tof_timing_offsets_py(&mut self, timing_offsets : HashMap<u8, f32>) {
578 self.tof_event.set_timing_offsets(&timing_offsets);
579 }
580
581 #[pyo3(name="tof_normalize_hit_times")]
585 fn tof_normalize_hit_times_py(&mut self) {
586 self.tof_event.normalize_hit_times();
587 }
588
589 fn tof_set_timing_constants(&mut self, constants: HashMap<u8, f32>) {
594 for h in &mut self.tof_event.hits {
595 h.event_t0 -= constants[&h.paddle_id];
596 }
597 }
598
599
600 #[getter]
603 fn get_tof(&self) -> PyResult<TofEvent> {
604 Ok(self.tof_event.clone())
605 }
606 #[getter]
632 fn tracker_pointcloud(&self) -> Vec<(f32, f32, f32, f32, f32)> {
633 let mut pts = Vec::<(f32,f32,f32,f32,f32)>::new();
634 for h in &self.tracker_hits {
635 let pt = (10.0*h.x, 10.0*h.y, 10.0*h.z, f32::NAN, h.adc as f32);
638 pts.push(pt);
639 }
640 pts
641 }
642
643 #[pyo3(name="add_tracker_hits")]
645 pub fn add_tracker_hits_py(&mut self, mut hits: Vec<TrackerHit>, dedup : bool) {
646 if dedup {
647 hits.sort();
648 hits.dedup();
649 }
650 let n_hits_before = self.tracker_hits.len();
651 self.tracker_hits.extend_from_slice(&hits);
652 self.tracker_hits.sort();
653 self.tracker_hits.dedup();
654 let delta_hits = self.tracker_hits.len() - n_hits_before;
655 self.header.length += (4*delta_hits) as u16;
657 }
658
659 #[pyo3(name="delete_all_tracker_hits")]
662 pub fn delete_all_tracker_hits_py(&mut self) {
663 self.tracker_hits.clear();
664 }
665
666 #[staticmethod]
670 fn from_telemetrypacket(packet : TelemetryPacket) -> PyResult<Self> {
671 match Self::from_bytestream(&packet.payload, &mut 0) {
672 Ok(mut event) => {
673 event.header = packet.header.clone();
674 #[cfg(feature="database")]
675 event.hydrate(&packet.tof_paddles, &packet.trk_strips);
676 return Ok(event);
677 }
678 Err(err) => {
679 return Err(PyValueError::new_err(err.to_string()));
680 }
681 }
682 }
683
684 #[pyo3(name="pack")]
685 fn pack_py(&self) -> TelemetryPacket {
686 self.pack()
687 }
688
689 #[pyo3(name="calibrate_trk_hits")]
690 fn calibrate_trk_hits_py(&mut self, cali : Bound<'_, TrackerOfflineCalibration> ) -> PyResult<()> {
691 let cali_ref = cali.borrow();
692 self.calibrate_trk_hits(&*cali_ref)?;
693 Ok(())
694 }
695
696}
713
714#[cfg(feature="random")]
715impl FromRandom for TelemetryEvent {
716
717 fn from_random() -> Self {
718 let mut rng = rand::rng();
719 let mut ev = Self::new();
720 ev.header = TelemetryPacketHeader::from_random();
721 ev.tof_event = TofEvent::from_random();
722 let n_trk_hits = 1u8;
724 for _ in 0..n_trk_hits {
725 let mut h = TrackerHit::from_random();
726 h.oscillator = 0;
727 h.asic_event_code = 0;
728 ev.tracker_hits.push(h);
729 }
730 ev.creation_time = 0;
732 ev.event_id = rng.random::<u32>();
733 ev.flags0 = 3;
734 ev.flags1 = 1;
735 ev.version = 1;
736 for idx in 0..10usize {
739 let active = rng.random::<bool>();
740 if idx < 8 && active {
741 ev.osc_flags = ev.osc_flags | (0b1 << idx);
742 let osc = rng.random::<u64>() & 0x0000FFFFFFFFFFFF;
743 ev.tracker_oscillators[idx] = osc;
744 ev.oscillator_idx.push(idx as u8);
745 }
747 }
748 return ev;
749 }
750}
751
752#[test]
753#[cfg(feature="random")]
754fn serialize_deserialize_telemetryevent() {
755 for _ in 0..10 {
756 let mut ev = TelemetryEvent::from_random();
757 let bytes = ev.to_bytestream();
758 let mut test = TelemetryEvent::from_bytestream(&bytes, &mut 0).unwrap();
759 let creation_time = Instant::now();
760 ev.tof_event.creation_time = creation_time;
761 test.tof_event.creation_time = creation_time;
762 ev.tof_event.rb_events.clear();
763 test.tof_event.rb_events.clear();
764 for h in &mut ev.tof_event.hits {
765 h.coax_cable_time = 0.0;
766 h.hart_cable_time = 0.0;
767 h.event_t0 = 0.0;
768 h.paddle_len = 0.0;
769 h.x = 0.0;
770 h.y = 0.0;
771 h.z = 0.0;
772 }
773 for h in &mut test.tof_event.hits {
774 h.coax_cable_time = 0.0;
775 h.hart_cable_time = 0.0;
776 h.event_t0 = 0.0;
777 h.paddle_len = 0.0;
778 h.x = 0.0;
779 h.y = 0.0;
780 h.z = 0.0;
781 }
782 assert_eq!(ev.creation_time , test.creation_time);
785 assert_eq!(ev.event_id , test.event_id);
786 assert_eq!(ev.osc_flags , test.osc_flags);
787 assert_eq!(ev.tracker_oscillators, test.tracker_oscillators);
788 assert_eq!(ev.raw_data , test.raw_data);
789 assert_eq!(ev.flags0 , test.flags0);
790 assert_eq!(ev.flags1 , test.flags1);
791 assert_eq!(ev.version , test.version);
792 assert_eq!(ev.oscillator_idx , test.oscillator_idx);
793 assert_eq!(ev.tracker_hits , test.tracker_hits);
794 assert_eq!(ev.tof_event , test.tof_event);
795 }
797}
798
799#[cfg(feature="pybindings")]
800pythonize!(TelemetryEvent);
801