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, remove_pulsed : bool)
95 -> Result<(), CalibrationError> {
96 cali.mask_hits(&mut self.tracker_hits);
97 cali.calibrate(&mut self.tracker_hits, remove_pulsed)?;
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!("Version {version} found, which is currently not supported!");
338 return Err(SerializationError::UnsupportedVersion);
339 } else {
340 error!("Unsuported and unknown 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 #[cfg(feature="database")]
640 #[pyo3(name="tof_set_paddles")]
641 fn tof_set_paddles_py(&mut self, paddles : HashMap<u8, TofPaddle>) {
642 self.tof_event.set_paddles(&paddles);
643 }
644
645 #[getter]
646 fn tracker_pointcloud(&self) -> Vec<(f32, f32, f32, f32, f32)> {
647 let mut pts = Vec::<(f32,f32,f32,f32,f32)>::new();
648 for h in &self.tracker_hits {
649 let pt = (10.0*h.x, 10.0*h.y, 10.0*h.z, f32::NAN, h.adc as f32);
652 pts.push(pt);
653 }
654 pts
655 }
656
657 #[pyo3(name="add_tracker_hits")]
659 pub fn add_tracker_hits_py(&mut self, mut hits: Vec<TrackerHit>, dedup : bool) {
660 if dedup {
661 hits.sort();
662 hits.dedup();
663 }
664 let n_hits_before = self.tracker_hits.len();
665 self.tracker_hits.extend_from_slice(&hits);
666 self.tracker_hits.sort();
667 self.tracker_hits.dedup();
668 let delta_hits = self.tracker_hits.len() - n_hits_before;
669 self.header.length += (4*delta_hits) as u16;
671 }
672
673 #[pyo3(name="delete_all_tracker_hits")]
676 pub fn delete_all_tracker_hits_py(&mut self) {
677 self.tracker_hits.clear();
678 }
679
680 #[staticmethod]
684 fn from_telemetrypacket(packet : TelemetryPacket) -> PyResult<Self> {
685 match Self::from_bytestream(&packet.payload, &mut 0) {
686 Ok(mut event) => {
687 event.header = packet.header.clone();
688 #[cfg(feature="database")]
689 event.hydrate(&packet.tof_paddles, &packet.trk_strips);
690 return Ok(event);
691 }
692 Err(err) => {
693 return Err(PyValueError::new_err(err.to_string()));
694 }
695 }
696 }
697
698 #[pyo3(name="pack")]
699 fn pack_py(&self) -> TelemetryPacket {
700 self.pack()
701 }
702
703 #[pyo3(name="calibrate_trk_hits")]
704 fn calibrate_trk_hits_py(&mut self, cali : Bound<'_, TrackerOfflineCalibration> , remove_pulsed : bool) -> PyResult<()> {
705 let cali_ref = cali.borrow();
706 self.calibrate_trk_hits(&*cali_ref, remove_pulsed)?;
707 Ok(())
708 }
709}
710
711#[cfg(feature="random")]
712impl FromRandom for TelemetryEvent {
713
714 fn from_random() -> Self {
715 let mut rng = rand::rng();
716 let mut ev = Self::new();
717 ev.header = TelemetryPacketHeader::from_random();
718 ev.tof_event = TofEvent::from_random();
719 let n_trk_hits = 1u8;
721 for _ in 0..n_trk_hits {
722 let mut h = TrackerHit::from_random();
723 h.oscillator = 0;
724 h.asic_event_code = 0;
725 ev.tracker_hits.push(h);
726 }
727 ev.creation_time = 0;
729 ev.event_id = rng.random::<u32>();
730 ev.flags0 = 3;
731 ev.flags1 = 1;
732 ev.version = 1;
733 for idx in 0..10usize {
736 let active = rng.random::<bool>();
737 if idx < 8 && active {
738 ev.osc_flags = ev.osc_flags | (0b1 << idx);
739 let osc = rng.random::<u64>() & 0x0000FFFFFFFFFFFF;
740 ev.tracker_oscillators[idx] = osc;
741 ev.oscillator_idx.push(idx as u8);
742 }
744 }
745 return ev;
746 }
747}
748
749#[test]
750#[cfg(feature="random")]
751fn serialize_deserialize_telemetryevent() {
752 for _ in 0..10 {
753 let mut ev = TelemetryEvent::from_random();
754 let bytes = ev.to_bytestream();
755 let mut test = TelemetryEvent::from_bytestream(&bytes, &mut 0).unwrap();
756 let creation_time = Instant::now();
757 ev.tof_event.creation_time = creation_time;
758 test.tof_event.creation_time = creation_time;
759 ev.tof_event.rb_events.clear();
760 test.tof_event.rb_events.clear();
761 for h in &mut ev.tof_event.hits {
762 h.coax_cable_time = 0.0;
763 h.hart_cable_time = 0.0;
764 h.event_t0 = 0.0;
765 h.paddle_len = 0.0;
766 h.x = 0.0;
767 h.y = 0.0;
768 h.z = 0.0;
769 }
770 for h in &mut test.tof_event.hits {
771 h.coax_cable_time = 0.0;
772 h.hart_cable_time = 0.0;
773 h.event_t0 = 0.0;
774 h.paddle_len = 0.0;
775 h.x = 0.0;
776 h.y = 0.0;
777 h.z = 0.0;
778 }
779 assert_eq!(ev.creation_time , test.creation_time);
782 assert_eq!(ev.event_id , test.event_id);
783 assert_eq!(ev.osc_flags , test.osc_flags);
784 assert_eq!(ev.tracker_oscillators, test.tracker_oscillators);
785 assert_eq!(ev.raw_data , test.raw_data);
786 assert_eq!(ev.flags0 , test.flags0);
787 assert_eq!(ev.flags1 , test.flags1);
788 assert_eq!(ev.version , test.version);
789 assert_eq!(ev.oscillator_idx , test.oscillator_idx);
790 assert_eq!(ev.tracker_hits , test.tracker_hits);
791 assert_eq!(ev.tof_event , test.tof_event);
792 }
794}
795
796#[cfg(feature="pybindings")]
797pythonize!(TelemetryEvent);
798