Skip to main content

gondola_core/events/
tof_event.rs

1//! Basic event structure for all TOF systems
2// This file is part of gaps-online-software and published 
3// under the GPLv3 license
4
5use crate::prelude::*;
6
7#[cfg(feature="database")]
8use std::f32::consts::PI;
9use std::cmp::Ordering;
10
11/// Main event class for the TOF. This will be sent over telemetry and be 
12/// written to disk
13///
14/// CHANGELOG: v0.11 (gondola-core): Merges TofEvent, TofEventSummary and 
15/// MasterTriggerEvent all into a single TofEvent, based on former TofEventSummary.
16/// The new TofEvent has the ability to cary RBEvents and thus mimick the "old" TofEvent
17/// We are using the version flag to indicate:
18/// * ProtocolVersion::Unknown - The "old" TofEventSummary. (now "TofEvent"). No extra 
19///   variables for the GCU, no RBEvents
20/// * ProtocolVersion::V1      - The version crafted for Antarctica '24/'25 containing 
21///   a bunch of summary variables for the GCU (e.g. nhits(umbrella)). This version will 
22///   add these variables to the bytestream and also if ProtocolVersion::V1 is read out 
23///   from the bytestream, the variables are expected to be in it. This is to keep compatibility
24///   with the gcu
25/// * ProtocolVersion::V2     - v0.11 (gondola-core) version of TofEvent(Summary).
26///   This version will not write out GCU variables and does not expect them to be in the 
27///   bytestream. If desired, this version can read/write RBEvents. 
28/// * ProtocolVersion::V3     - the "latest and greatest". This version has gcuvariables 
29///                             AND rbevents. RBEvents can be stripped off later on.
30#[derive(Debug, Clone, PartialEq)]
31#[cfg_attr(feature="pybindings", pyclass)]
32pub struct TofEvent {
33  pub status            : EventStatus,
34  /// The version of this event. Prior to tof-dataclasses v0.11,
35  /// the default was `ProtocolVersion::Unknown`. 
36  /// Then for the Antarctic campaign we added more variables, 
37  /// so that the gcu doesn't have to compute them. These variables
38  /// will be included in the bytestream for `ProtocolVersion::V1`.
39  /// However, we don't want to write them to disk in the new (gondola-core > v0.11)
40  /// verion, so we will use ProtocolVersionV2 for the version to be
41  /// written to disk. ProtocolVersionV2 can (but not must) have RBEvents
42  pub version           : ProtocolVersion,
43  pub quality           : EventQuality,
44  pub trigger_sources   : u16,
45
46  /// the number of triggered paddles coming
47  /// from the MTB directly. This might NOT be
48  /// the same as the number of hits!
49  pub n_trigger_paddles  : u8,
50  pub event_id           : u32,
51  pub run_id             : u16,
52  pub timestamp32        : u32,
53  pub timestamp16        : u16,
54  /// scalar number of hits missed in
55  /// this event due to DRS on the RB
56  /// being busy
57  pub drs_dead_lost_hits : u16, 
58  pub dsi_j_mask         : u32,
59  pub channel_mask       : Vec<u16>,
60  pub mtb_link_mask      : u64,
61  pub hits               : Vec<TofHit>,
62  // a number of variables which are directly 
63  // read out from the MTB packet and won't get 
64  // serialized in this form but then used
65  // later to calculate other variables
66  pub mt_trigger_sources : u16,
67  pub mt_tiu_gps16       : u16,
68  pub mt_tiu_gps32       : u32, 
69  pub mt_timestamp       : u32,
70  pub mt_tiu_timestamp   : u32,
71  // a bunch of calculated variablels, used 
72  // for online interesting event search
73  // these will be only available in ProtocolVersion 1
74  pub n_hits_umb         : u8,
75  pub n_hits_cbe         : u8,
76  pub n_hits_cor         : u8,
77  pub tot_edep_umb       : f32,
78  pub tot_edep_cbe       : f32,
79  pub tot_edep_cor       : f32,
80  pub paddles_set        : bool,
81  // this is new (v0.11) -> We are expanding TofEvent(Summary) 
82  // so that it is the same as TofEvent in v0.10 and is able 
83  // to carry waveforms. These then can be stripped off
84  pub rb_events          : Vec<RBEvent>,
85  /// Start time for a time to wait for incoming RBEvents
86  pub creation_time      : Instant,
87  pub write_to_disk      : bool, 
88}
89
90impl TofEvent {
91
92  pub fn new() -> Self {
93    Self {
94      status             : EventStatus::Unknown,
95      version            : ProtocolVersion::Unknown,
96      n_hits_umb         : 0,
97      n_hits_cbe         : 0,
98      n_hits_cor         : 0,
99      tot_edep_umb       : 0.0,
100      tot_edep_cbe       : 0.0,
101      tot_edep_cor       : 0.0,
102      quality            : EventQuality::Unknown,
103      trigger_sources    : 0,
104      n_trigger_paddles  : 0,
105      event_id           : 0,
106      run_id             : 0,
107      timestamp32        : 0,
108      timestamp16        : 0,
109      drs_dead_lost_hits : 0,
110      dsi_j_mask         : 0,
111      channel_mask       : Vec::<u16>::new(),
112      mtb_link_mask      : 0,
113      hits               : Vec::<TofHit>::new(),
114      mt_trigger_sources : 0,
115      mt_tiu_gps16       : 0,
116      mt_tiu_gps32       : 0, 
117      mt_timestamp       : 0,
118      mt_tiu_timestamp   : 0,
119      paddles_set        : false,
120      rb_events          : Vec::<RBEvent>::new(),
121      creation_time      : Instant::now(),
122      write_to_disk      : true,
123    }
124  }
125
126  /// Calculate the timestamp from the MTB inlcuding GPS and all
127  ///
128  /// This will be the most precise timestamp in GAPS, based on 
129  /// a 100MHz oscillator and if GPS is active, it will fix itself
130  /// with the GPS 1PPS pulse
131  pub fn get_mt_timestamp_abs(&self) -> u64 {
132    let gps = self.mt_tiu_gps32 as u64;
133    let mut timestamp = self.mt_timestamp as u64;
134    if timestamp < self.mt_tiu_timestamp as u64 {
135      // it has wrapped
136      timestamp += u32::MAX as u64 + 1;
137    }
138    let gps_mult = match 100_000_000u64.checked_mul(gps) {
139    //let gps_mult = match 100_000u64.checked_mul(gps) {
140      Some(result) => result,
141      None => {
142          // Handle overflow case here
143          // Example: log an error, return a default value, etc.
144          0 // Example fallback value
145      }
146    };
147    let ts = gps_mult + (timestamp - self.mt_tiu_timestamp as u64);
148    ts
149  }
150
151  /// Move hits out of RBEvents and in the general 
152  /// hitvector. 
153  ///
154  /// This should be done once an event is complete.
155  /// The RBEvents keep the associated waveforms, but 
156  /// the hits all move into a single vector
157  pub fn move_hits(&mut self) {
158    let mut all_hits = Vec::<TofHit>::with_capacity(5);
159    for rbev in &mut self.rb_events {
160       all_hits.append(&mut rbev.hits);
161    }
162    self.hits = all_hits;
163  }
164
165  /// Remove any RBEvents from the event. 
166  ///
167  /// This will move the hits out of the 
168  /// RBEvents and put them in the hit vector.
169  pub fn strip_rbevents(&mut self) {
170    if self.hits.len() == 0 {
171      self.move_hits();
172    }
173    self.rb_events.clear();
174  }
175  
176  pub fn age(&self) -> u64 {
177    self.creation_time.elapsed().as_secs()
178  }
179 
180  /// The expectedd RBs participating in this event as 
181  /// infered from the RB link ids coming from the MTB
182  pub fn get_expected_rbs(&self, mapping : &HashMap<u8,u8>) -> Vec<u8> {
183    let mut expected_rbs = Vec::<u8>::new();
184    for k in self.get_rb_link_ids() {
185      match mapping.get(&k) {
186        None => {
187          error!("Seeing unassociated link id {k}");
188        }
189        Some(rb_id) => {
190          expected_rbs.push(*rb_id);
191        }
192      }
193    }
194    expected_rbs 
195  }
196
197  /// Simple check if the event contains as much RBEvents 
198  /// as expected from the provided boards masks by the MTB
199  pub fn is_complete(&mut self, exclude_rbs : Option<(&Vec<u8>,&DsiJChRbMapping)>) -> bool {
200    if exclude_rbs.is_none() {
201      return self.get_rb_link_ids().len() == self.rb_events.len();
202    } else {
203      let dead_rbs = exclude_rbs.unwrap();
204      let mut n_known_dead = 0usize;
205      let t_hits = self.get_trigger_hits();
206      for h in t_hits {
207        match dead_rbs.1.get(&h.0) {
208          None => {
209            continue;
210          }
211          Some(dsi) => {
212            match dsi.get(&h.1) {
213              None => {
214                continue;
215              }
216              Some(j) => {
217                match j.get(&h.2.0) {
218                  None => {
219                    continue;
220                  }
221                  Some(rb) => {
222                    if dead_rbs.0.contains(&rb) {
223                      n_known_dead += 1
224                    }
225                  }
226                }
227              }
228            }
229          }
230        }
231      } // end loop over trigger hits
232        //  we use <= here, because sometimes, 
233        //  the link ids are wrong, so n_known_dead 
234        //  might be false positive
235      let n_rb_link_ids = self.get_rb_link_ids().len();
236      //if n_rb_link_ids <= self.rb_events.len() + n_known_dead {
237      //  self.status = EventStatus::KnownDeadRB; 
238      //}
239      return n_rb_link_ids <= self.rb_events.len() + n_known_dead;
240    }
241  }
242  
243  /// The number of hits we did not get 
244  /// becaue of the DRS busy
245  pub fn get_lost_hits(&self) -> u16 {
246    let mut lost_hits = 0u16;
247    for rbev in &self.rb_events {
248      if rbev.header.drs_lost_trigger() {
249        let mut nhits = rbev.header.get_nchan() as u16;
250        // FIXME - I don't understand this - that would only work if the RB 
251        // sees 2 channels, that is 1 hit (?) Potential bug
252        if nhits > 0 {
253          nhits -= 1;
254        }
255        lost_hits += nhits;
256      }
257    }
258    lost_hits
259  }
260
261
262  /// Calculate extra variables for the GCU, 
263  /// set the protocol version to V1 and 
264  /// strip the waveforms if desired
265  /// 
266  /// # Arguments:
267  ///   * strip_rbevents : remove the rbevents from the TofEvent so they
268  ///                      won't bother the poor gcu
269  pub fn prepare_for_gcu(&mut self, strip_rbevents : bool) {
270    if strip_rbevents {
271      self.strip_rbevents();
272    }
273    self.version = ProtocolVersion::V1;
274    if self.n_hits_cbe == 0 && self.n_hits_umb == 0 && self.n_hits_cor == 0 {
275      self.calc_gcu_variables();
276    }
277  }
278 
279  /// Calculate the TOF part of the interesting events mechanism, whcih is
280  /// NHIT (CBE, COR, UMB) and EDEP (CBE, COR, UMB)
281  pub fn calc_gcu_variables(&mut self) {
282    if self.hits.len() == 0 {
283      for rbev in &self.rb_events {
284        for h in &rbev.hits {
285          if h.paddle_id <= 60 {
286            self.n_hits_cbe += 1;
287            self.tot_edep_cbe += h.get_edep();
288          }
289          else if h.paddle_id <= 108 && h.paddle_id > 60 {
290            self.n_hits_umb += 1;
291            self.tot_edep_umb += h.get_edep();
292          }
293          else {
294            self.n_hits_cor += 1;
295            self.tot_edep_cor += h.get_edep();
296          }
297        }
298      }
299    } else { 
300      for h in &self.hits {
301        if h.paddle_id <= 60 {
302          self.n_hits_cbe += 1;
303          self.tot_edep_cbe += h.get_edep();
304        }
305        else if h.paddle_id <= 108 && h.paddle_id > 60 {
306          self.n_hits_umb += 1;
307          self.tot_edep_umb += h.get_edep();
308        }
309        else {
310          self.n_hits_cor += 1;
311          self.tot_edep_cor += h.get_edep();
312        }
313      }
314    }
315  }
316
317  /// Ensure compatibility with older data, which 
318  /// contained a different type of TofEvent
319  pub fn decode_depr_tofevent_size_header(mask : &u32) 
320    -> (usize, usize) {
321    let rb_event_len = (mask & 0xFF)        as usize;
322    let miss_len     = ((mask & 0xFF00)     >> 8)  as usize;
323    (rb_event_len, miss_len)
324  }
325
326  /// Set timing offsets to the event's hits
327  ///
328  /// # Arguments:
329  ///   * offsets : a hashmap paddle id -> timing offset
330  #[cfg(feature="database")]
331  pub fn set_timing_offsets(&mut self, offsets : &HashMap<u8, f32>) {
332    for h in self.hits.iter_mut() {
333      if offsets.contains_key(&h.paddle_id) {
334        h.timing_offset = offsets[&h.paddle_id]; 
335      }
336    }
337  }
338
339  /// Remove hits from the hitseries which can not 
340  /// be caused by the same particle, which means 
341  /// that for these two specific hits beta with 
342  /// respect to the first hit in the event is 
343  /// larger than one
344  /// That this works, first hits need to be 
345  /// "normalized" by calling normalize_hit_times
346  ///
347  /// # Return:
348  ///
349  ///   * removed paddle ids, twindows
350  pub fn lightspeed_cleaning(&mut self, t_err : f32) -> (Vec<u8>, Vec<f32>) {
351    // first sort the hits in time
352    if self.hits.len() == 0 {
353      return (Vec::<u8>::new(), Vec::<f32>::new());
354    }
355    let mut twindows = Vec::<f32>::new();
356    self.hits.sort_by(|a,b| (a.event_t0).partial_cmp(&b.event_t0).unwrap_or(Ordering::Greater));
357    let first_hit = self.hits[0].clone(); // the clone here is a bit unfortunate, 
358                                           // this can be done better with walking 
359                                           // over the list and updating references
360    let mut clean_hits = Vec::<TofHit>::new(); 
361    let mut rm_hits    = Vec::<u8>::new();
362    // per definition, we can't remove the first hit, ever
363    clean_hits.push(first_hit.clone());
364    //error!("-----");
365    let mut prior_hit = first_hit;
366    //println!("TO FIRST {}",first_hit.event_t0);
367    for h in self.hits.iter().skip(1) {
368      let min_tdiff_cvac = 1e9*1e-3*prior_hit.distance(h)/299792458.0;
369      let twindow            = prior_hit.event_t0 + min_tdiff_cvac;
370
371      // FIXME - implement different strategies
372      // this is the "default" strategy
373      //if h.event_t0 < twindow {
374      //  rm_hits.push(h.paddle_id);
375      //  twindows.push(twindow);
376      //  continue;
377      //}
378      // this is the "lenient" strategy
379      if h.event_t0 + 2.0*t_err < twindow {
380        rm_hits.push(h.paddle_id);
381        twindows.push(twindow);
382        continue;
383      }
384      // this is the "aggressive" strategy
385      //if h.event_t0 - 2.0*t_err < twindow {
386      //  rm_hits.push(h.paddle_id);
387      //  continue;
388      //}
389      
390      // should we remove negative beta hits?
391      //if beta < 0.0 {
392      //  rm_hits.push(h.paddle_id);
393      //  continue;
394      //}
395      // update the prior hit only 
396      // when it is a good one. If it 
397      // is bad we continue to compare
398      // to the first hit
399      prior_hit = h.clone();
400      clean_hits.push(*h);
401    }
402    self.hits = clean_hits;
403    (rm_hits, twindows)
404  }
405
406
407  /// Non causal hits have hit times in ends A and be which 
408  /// are not compatible with the speed of light in the paddle, 
409  /// that is, the hit gets registered too early. If we look 
410  /// at a plot of the reconstructed position, these hits would 
411  /// correspond to positions outside of the paddle.
412  ///
413  /// #Returns:
414  ///   A vector of paddle ids with removed hits
415  ///
416  pub fn remove_non_causal_hits(&mut self) -> Vec<u8> {
417    let mut clean_hits = Vec::<TofHit>::new();
418    let mut removed_pids = Vec::<u8>::new();
419    for h in &self.hits {
420      if h.obeys_causality() {
421        clean_hits.push(*h);
422      } else {
423        removed_pids.push(h.paddle_id);
424      }
425    }
426    self.hits = clean_hits;
427    removed_pids
428  }
429  
430  #[cfg(feature="database")]
431  pub fn normalize_hit_times(&mut self) {
432    if self.hits.len() == 0 {
433      return;
434    }
435    // check if hit times have already been normalized
436    if self.hits[0].event_t0 == 0.0 {
437      return;
438    }
439
440    let phase0 = self.hits[0].phase.to_f32();
441    for h in &mut self.hits {
442      let t0 = h.get_t0_uncorrected() + h.get_cable_delay();
443      let mut phase_diff = h.phase.to_f32() - phase0;
444      while phase_diff < - PI/2.0 {
445        phase_diff += 2.0*PI;
446      }
447      while phase_diff > PI/2.0 {
448        phase_diff -= 2.0*PI;
449      }
450      let t_shift = 50.0*phase_diff/(2.0*PI);
451      h.event_t0 = t0 + t_shift;
452    }
453    // start the first hit at 0
454    //self.hits.sort_by(|a,b| (a.event_t0).partial_cmp(&b.event_t0).unwrap_or(Ordering::Greater));
455    self.hits.sort_by(|a,b| (a.event_t0).total_cmp(&b.event_t0));
456    let t0_first_hit = self.hits[0].event_t0;
457    for h in self.hits.iter_mut() {
458      h.event_t0 -= t0_first_hit;
459    }
460  }
461 
462  #[cfg(feature="database")]
463  pub fn set_paddles(&mut self, paddles : &HashMap<u8, TofPaddle>) {
464    let mut nerror = 0u8;
465    if paddles.is_empty() {
466      debug!("Unable to set paddles, empty map given!");
467      return;
468    }
469    if self.hits.len() == 0 {
470      for rbev  in &mut self.rb_events {
471        for h in &mut rbev.hits {
472          match paddles.get(&h.paddle_id) {
473            None => {
474              error!("Got paddle id {} which is not in given map!", h.paddle_id);
475              nerror += 1;
476              continue;
477            }
478            Some(pdl) => {
479              h.set_paddle(pdl);
480            }
481          }
482        }
483      }
484    } else {
485      for h in &mut self.hits {
486        match paddles.get(&h.paddle_id) {
487          None => {
488            error!("Got paddle id {} which is not in given map!", h.paddle_id);
489            nerror += 1;
490            continue;
491          }
492          Some(pdl) => {
493            h.set_paddle(pdl);
494          }
495        }
496      }
497    }
498    if nerror == 0 {
499      self.paddles_set = true;
500      //self.normalize_hit_times();
501    }
502  }
503
504  /// Get the pointcloud of this event, sorted by time
505  /// 
506  /// # Returns
507  ///   (f32, f32, f32, f32, f32) : (x,y,z,t,edep)
508  pub fn get_pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
509    let mut pc = Vec::<(f32,f32,f32,f32,f32)>::new();
510    if !self.paddles_set {
511      error!("Before getting the pointcloud, paddle information needs to be set for this event. Call TofEvent;:set_paddle");
512      return None;
513    }
514    for h in &self.hits {
515      let result = (h.x, h.y, h.z, h.get_t0(), h.get_edep());
516      pc.push(result);
517    }
518    Some(pc)
519  }
520
521  /// Compare the MasterTriggerEvent::trigger_hits with 
522  /// the actual hits to determine from which paddles
523  /// we should have received HG hits (from waveforms)
524  /// but we did not get them
525  ///
526  /// WARNING: The current implementation of this is 
527  /// rather slow and not fit for production use
528  /// FIXME - rewrite as a closure
529  #[cfg(feature="database")]
530  pub fn get_missing_paddles_hg(&self, pid_map :   &DsiJChPidMapping) -> Vec<u8> {
531    let mut missing = Vec::<u8>::new();
532    for th in self.get_trigger_hits() {
533      if !pid_map.contains_key(&th.0) {
534        error!("Can't find {:?} in paddlemap!",th);
535        continue;
536      }
537      if !pid_map.get(&th.0).unwrap().contains_key(&th.1) {
538        error!("Can't find {:?} in paddlemap!",th);
539        continue;
540      }
541      if !pid_map.get(&th.0).unwrap().get(&th.1).unwrap().contains_key(&th.2.0) {
542        error!("Can't find {:?} in paddlemap!",th);
543        continue;
544      }
545      let pid = pid_map.get(&th.0).unwrap().get(&th.1).unwrap().get(&th.2.0).unwrap().0;
546      let mut found = false;
547      for h in &self.hits {
548        if h.paddle_id == pid {
549          found = true;
550          break
551        }
552      }
553      if !found {
554        missing.push(pid);
555      }
556    }
557    missing
558  }
559  
560  /// Get the triggered paddle ids
561  ///
562  /// Warning, this might be a bit slow
563  #[cfg(feature="database")]
564  pub fn get_triggered_paddles(&self, pid_map :   &DsiJChPidMapping) -> Vec<u8> {
565    let mut paddles = Vec::<u8>::with_capacity(3);
566    for th in &self.get_trigger_hits() {
567      let pid = pid_map.get(&th.0).unwrap().get(&th.1).unwrap().get(&th.2.0).unwrap().0;
568      paddles.push(pid);
569    }
570    paddles
571  }
572
573  /// Get the RB link IDs according to the mask
574  pub fn get_rb_link_ids(&self) -> Vec<u8> {
575    let mut links = Vec::<u8>::new();
576    for k in 0..64 {
577      if (self.mtb_link_mask >> k) as u64 & 0x1 == 1 {
578        links.push(k as u8);
579      }
580    }
581    links
582  }
583
584  /// Get the combination of triggered DSI/J/CH on 
585  /// the MTB which formed the trigger. This does 
586  /// not include further hits which fall into the 
587  /// integration window. For those, se rb_link_mask
588  ///
589  /// The returned values follow the TOF convention
590  /// to start with 1, so that we can use them to 
591  /// look up LTB ids in the db.
592  ///
593  /// # Returns
594  ///
595  ///   Vec<(hit)> where hit is (DSI, J, CH) 
596  pub fn get_trigger_hits(&self) -> Vec<(u8, u8, (u8, u8), LTBThreshold)> {
597    let mut hits = Vec::<(u8,u8,(u8,u8),LTBThreshold)>::with_capacity(5); 
598    //let n_masks_needed = self.dsi_j_mask.count_ones() / 2 + self.dsi_j_mask.count_ones() % 2;
599    let n_masks_needed = self.dsi_j_mask.count_ones();
600    if self.channel_mask.len() < n_masks_needed as usize {
601      error!("We need {} hit masks, but only have {}! This is bad!", n_masks_needed, self.channel_mask.len());
602      return hits;
603    }
604    let mut n_mask = 0;
605    trace!("Expecting {} hit masks", n_masks_needed);
606    trace!("ltb channels {:?}", self.dsi_j_mask);
607    trace!("hit masks {:?}", self.channel_mask); 
608    //println!("We see LTB Channels {:?} with Hit masks {:?} for {} masks requested by us!", self.dsi_j_mask, self.channel_mask, n_masks_needed);
609    
610    // one k here is for one ltb
611    for k in 0..32 {
612      if (self.dsi_j_mask >> k) as u32 & 0x1 == 1 {
613        let mut dsi = 0u8;
614        let mut j   = 0u8;
615        if k < 5 {
616          dsi = 1;
617          j   = k as u8 + 1;
618        } else if k < 10 {
619          dsi = 2;
620          j   = k as u8 - 5 + 1;
621        } else if k < 15 {
622          dsi = 3;
623          j   = k as u8- 10 + 1;
624        } else if k < 20 {
625          dsi = 4;
626          j   = k as u8- 15 + 1;
627        } else if k < 25 {
628          dsi = 5;
629          j   = k as u8 - 20 + 1;
630        } 
631        //let dsi = (k as f32 / 4.0).floor() as u8 + 1;       
632        //let j   = (k % 5) as u8 + 1;
633        //println!("n_mask {n_mask}");
634        let channels = self.channel_mask[n_mask]; 
635        for (i,ch) in LTB_CHANNELS.iter().enumerate() {
636          //let chn = *ch as u8 + 1;
637          let ph_chn = PHYSICAL_CHANNELS[i];
638          //let chn = i as u8 + 1;
639          //println!("i,ch {}, {}", i, ch);
640          let thresh_bits = ((channels & ch) >> (i*2)) as u8;
641          //println!("thresh_bits {}", thresh_bits);
642          if thresh_bits > 0 { // hit over threshold
643            hits.push((dsi, j, ph_chn, LTBThreshold::from(thresh_bits)));
644          }
645        }
646        n_mask += 1;
647      } // next ltb
648    }
649    hits
650  }
651  
652  /// Get the trigger sources from trigger source byte
653  pub fn get_trigger_sources(&self) -> Vec<TriggerType> {
654    TriggerType::transcode_trigger_sources(self.trigger_sources)
655  }
656  
657  pub fn get_timestamp48(&self) -> u64 {
658    // the first number here is a constant, which was not sent, it is basically 
659    // a time offset (redefinition of epoch) and gets added through the "|" operation
660    0x273000000000000 | (((self.timestamp16 as u64) << 32) | self.timestamp32 as u64)
661  }
662  
663  /// Ttotal energy depostion in the TOF - Umbrella
664  ///
665  /// Utilizes Philip's formula based on 
666  /// peak height
667  pub fn get_edep_umbrella(&self) -> f32 {
668    let mut tot_edep = 0.0f32;
669    for h in &self.hits {
670      if h.paddle_id < 61 || h.paddle_id > 108 {
671        continue;
672      }
673      tot_edep += h.get_edep();
674    }
675    tot_edep
676  }
677  
678  /// Ttotal energy depostion in the TOF - Umbrella
679  ///
680  /// Utilizes Philip's formula based on 
681  /// peak height
682  pub fn get_edep_cube(&self) -> f32 {
683    let mut tot_edep = 0.0f32;
684    for h in &self.hits {
685      if h.paddle_id > 60 {
686        continue;
687      }
688      tot_edep += h.get_edep();
689    }
690    tot_edep
691  }
692  
693  /// Ttotal energy depostion in the Cortina
694  ///
695  /// Utilizes Philip's formula based on 
696  /// peak height
697  pub fn get_edep_cortina(&self) -> f32 {
698    let mut tot_edep = 0.0f32;
699    for h in &self.hits {
700      if h.paddle_id < 109 {
701        continue;
702      }
703      tot_edep += h.get_edep();
704    }
705    tot_edep
706  }
707  
708  /// Ttotal energy depostion in the complete TOF
709  ///
710  /// Utilizes Philip's formula based on 
711  /// peak height
712  pub fn get_edep(&self) -> f32 {
713    let mut tot_edep = 0.0f32;
714    for h in &self.hits {
715      tot_edep += h.get_edep();
716    }
717    tot_edep
718  }
719
720  pub fn get_nhits_umb(&self) -> usize {
721    let mut nhit = 0;
722    for h in &self.hits {
723      if h.paddle_id > 60 && h.paddle_id < 109 {
724        nhit += 1;
725      }
726    }
727    nhit
728  }
729  
730  pub fn get_nhits_cbe(&self) -> usize {
731    let mut nhit = 0;
732    for h in &self.hits {
733      if h.paddle_id < 61  {
734        nhit += 1;
735      }
736    }
737    nhit
738  }
739  
740  pub fn get_nhits_cor(&self) -> usize {
741    let mut nhit = 0;
742    for h in &self.hits {
743      if h.paddle_id > 108  {
744        nhit += 1;
745      }
746    }
747    nhit
748  }
749
750  pub fn get_nhits(&self) -> usize {
751    self.hits.len()
752  }
753  
754  /// Check if th eassociated RBEvents have any of their
755  /// mangling stati set
756  pub fn has_any_mangling(&self) -> bool {
757    for rbev in &self.rb_events {
758      if rbev.status == EventStatus::CellAndChnSyncErrors 
759      || rbev.status == EventStatus::CellSyncErrors 
760      || rbev.status == EventStatus::ChnSyncErrors {
761        return true;
762      }
763    }
764    false
765  }
766  
767  /// Get all waveforms of all RBEvents in this event
768  /// ISSUE - Performance, Memory
769  /// FIXME - reimplement this things where this
770  ///         returns only a reference
771  pub fn get_waveforms(&self) -> Vec<RBWaveform> {
772    let mut wfs = Vec::<RBWaveform>::new();
773    for ev in &self.rb_events {
774      for wf in &ev.get_rbwaveforms() {
775        wfs.push(wf.clone());
776      }
777    }
778    wfs
779  }
780
781  /// Change the status version when the event is already 
782  /// packed. The status version is encoded in byte 2 
783  /// (starting from 0) in the payload of the TofPacket 
784  pub fn set_packed_status_version(pack : &mut TofPacket, version : ProtocolVersion) 
785    -> Result<(), SerializationError> {
786    if pack.packet_type != TofPacketType::TofEvent {
787      return Err(SerializationError::IncorrectPacketType);
788    }
789    let mut status_version = pack.payload[2];
790    // null the bytes relevant for the version 
791    status_version  = status_version & 0x3f;
792    // now or the bytes for the new version 
793    status_version  = status_version | version.to_u8();
794    pack.payload[2] = status_version; 
795    Ok(()) 
796  }
797
798  /// For events with ProtocolVersion == V3, 
799  /// we have the rbevents at the end and the 
800  /// packet contains the GCU variables.
801  ///
802  /// This can remove the rbevents from a packed bytestrem,
803  /// and will reset the ProtocolVersion to V2.
804  /// The result is an event which should be ready 
805  /// to be sent to the GCU.
806  pub fn strip_packed_rbevents_for_pv3(pack : &mut TofPacket)
807    -> Result<(), SerializationError> {
808    if pack.packet_type != TofPacketType::TofEvent {
809      return Err(SerializationError::IncorrectPacketType);
810    }
811    let status_version = pack.payload[2];
812    let mut version    = ProtocolVersion::from(status_version & 0xc0);
813    if version != ProtocolVersion::V3 {
814      error!("This operation can only be executed on {}, however, this is version {}!", ProtocolVersion::V3, version);
815      return Err(SerializationError::WrongProtocolVersion);
816    }
817    // jump to the start of RBEvents 
818    let mut pos = 0usize;
819    pos += 10; // header 
820    pos += 15; // gcu variables (protocolversion V1 & V3) 
821    pos += 15;
822    if pack.payload.len() >= pos {
823      return Err(SerializationError::StreamTooShort);
824    }
825    let nmasks = parse_u8(&pack.payload, &mut pos);
826    for _ in 0..nmasks {
827      pos += 2;
828    }
829    pos += 8;
830    let nhits  = parse_u16(&pack.payload,&mut pos);
831    // set back the version
832    for _ in 0..nhits {
833      pos += TofHit::SIZE;
834      // FIXME - if we don't be able to manage 
835      //         to have a consistent size for 
836      //         TofHit, we have to deserialize them 
837      //         here (or write a minimal deserializer
838    }
839    // the next byte is finally the number of RBEvents. 
840    // So we set that to 0, strip the rest of the paylod 
841    // and re-attach the TAIL
842    pack.payload.truncate(pack.payload.len() - pos);
843    pack.payload.extend_from_slice(&Self::TAIL.to_le_bytes());
844    version = ProtocolVersion::V2;
845    Self::set_packed_status_version(pack, version)?;
846    Ok(())
847  }
848}
849
850impl TofPackable for TofEvent {
851  // v0.11 TofPacketType::TofEventSummary -> TofPacketType::TofEvent
852  const TOF_PACKET_TYPE        : TofPacketType = TofPacketType::TofEvent;
853  const TOF_PACKET_TYPE_ALT    : TofPacketType = TofPacketType::TofEventDeprecated;
854}
855
856impl Serialization for TofEvent {
857  
858  const HEAD               : u16   = 43690; //0xAAAA
859  const TAIL               : u16   = 21845; //0x5555
860  
861  fn to_bytestream(&self) -> Vec<u8> {
862    let mut stream = Vec::<u8>::new();
863    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
864    let status_version = self.status as u8 | self.version.to_u8();
865    stream.push(status_version);
866    stream.extend_from_slice(&self.trigger_sources.to_le_bytes());
867    stream.extend_from_slice(&self.n_trigger_paddles.to_le_bytes());
868    stream.extend_from_slice(&self.event_id.to_le_bytes());
869    // depending on the version, we send the fc event packet
870    if self.version == ProtocolVersion::V1 
871      || self.version == ProtocolVersion::V3 {
872      stream.extend_from_slice(&self.n_hits_umb  .to_le_bytes()); 
873      stream.extend_from_slice(&self.n_hits_cbe  .to_le_bytes()); 
874      stream.extend_from_slice(&self.n_hits_cor  .to_le_bytes()); 
875      stream.extend_from_slice(&self.tot_edep_umb.to_le_bytes()); 
876      stream.extend_from_slice(&self.tot_edep_cbe.to_le_bytes()); 
877      stream.extend_from_slice(&self.tot_edep_cor.to_le_bytes()); 
878    }
879    stream.extend_from_slice(&(self.quality as u8).to_le_bytes());
880    stream.extend_from_slice(&self.timestamp32.to_le_bytes());
881    stream.extend_from_slice(&self.timestamp16.to_le_bytes());
882    stream.extend_from_slice(&self.run_id.to_le_bytes());
883    stream.extend_from_slice(&self.drs_dead_lost_hits.to_le_bytes());
884    stream.extend_from_slice(&self.dsi_j_mask.to_le_bytes());
885    let n_channel_masks = self.channel_mask.len();
886    stream.push(n_channel_masks as u8);
887    for k in 0..n_channel_masks {
888      stream.extend_from_slice(&self.channel_mask[k].to_le_bytes());
889    }
890    stream.extend_from_slice(&self.mtb_link_mask.to_le_bytes());
891    let nhits = self.hits.len() as u16;
892    stream.extend_from_slice(&nhits.to_le_bytes());
893    for k in 0..self.hits.len() {
894      stream.extend_from_slice(&self.hits[k].to_bytestream());
895    }
896    // for the new (>=v0.11) event, we will always write 
897    // the rb events
898    if self.version == ProtocolVersion::V2 
899      || self.version == ProtocolVersion::V3 {
900      stream.push(self.rb_events.len() as u8);
901      for rbev in &self.rb_events {
902        stream.extend_from_slice(&rbev.to_bytestream());
903      }
904    }
905    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
906    stream
907  }
908  
909  fn from_bytestream(stream    : &Vec<u8>, 
910                     pos       : &mut usize) 
911    -> Result<Self, SerializationError>{
912    let mut event = Self::new();
913    let head      = parse_u16(stream, pos);
914    if head != Self::HEAD {
915      error!("Decoding of HEAD failed! Got {} instead!", head);
916      return Err(SerializationError::HeadInvalid);
917    }
918    
919    let status_version_u8   = parse_u8(stream, pos);
920    let status              = EventStatus::from(status_version_u8 & 0x3f);
921    let version             = ProtocolVersion::from(status_version_u8 & 0xc0); 
922    event.status            = status;
923    event.version           = version;
924    event.trigger_sources   = parse_u16(stream, pos);
925    event.n_trigger_paddles = parse_u8(stream, pos);
926    event.event_id          = parse_u32(stream, pos);
927    if event.version == ProtocolVersion::V1
928      || event.version == ProtocolVersion::V3 {
929      event.n_hits_umb      = parse_u8(stream, pos); 
930      event.n_hits_cbe      = parse_u8(stream, pos); 
931      event.n_hits_cor      = parse_u8(stream, pos); 
932      event.tot_edep_umb    = parse_f32(stream, pos); 
933      event.tot_edep_cbe    = parse_f32(stream, pos); 
934      event.tot_edep_cor    = parse_f32(stream, pos); 
935    }
936    event.quality            = EventQuality::from(parse_u8(stream, pos));
937    event.timestamp32        = parse_u32(stream, pos);
938    event.timestamp16        = parse_u16(stream, pos);
939    event.run_id             = parse_u16(stream, pos);
940    event.drs_dead_lost_hits = parse_u16(stream, pos);
941    event.dsi_j_mask         = parse_u32(stream, pos);
942    let n_channel_masks        = parse_u8(stream, pos);
943    for _ in 0..n_channel_masks {
944      event.channel_mask.push(parse_u16(stream, pos));
945    }
946    event.mtb_link_mask      = parse_u64(stream, pos);
947    let nhits                = parse_u16(stream, pos);
948    //println!("{}", event);
949    if nhits > 160 {
950      error!("There are an abnormous amount of hits in this event!");
951      return Err(SerializationError::StreamTooLong);
952    } 
953    for _ in 0..nhits {
954      event.hits.push(TofHit::from_bytestream(stream, pos)?);
955    }
956    if event.version == ProtocolVersion::V2 
957      || event.version == ProtocolVersion::V3 {
958      let n_rb_events = parse_u8(stream, pos);
959      if n_rb_events > 0 {
960        for _ in 0..n_rb_events {
961          event.rb_events.push(RBEvent::from_bytestream(stream, pos)?);
962        }
963      }
964    }
965
966    let tail = parse_u16(stream, pos);
967    if tail != Self::TAIL {
968      error!("Decoding of TAIL failed for version {}! Got {} instead!", version, tail);
969      return Err(SerializationError::TailInvalid);
970    }
971    Ok(event)
972  }
973  
974  /// Allows to get TofEvent from a packet 
975  /// of the deprecate packet type TofEventDeprecated.
976  /// This packet type was formerly known as TofEvent
977  ///
978  /// This will produce an event with rbevents & hits.
979  fn from_bytestream_alt(stream    : &Vec<u8>, 
980                         pos       : &mut usize) 
981    -> Result<Self, SerializationError> {
982    let head = parse_u16(stream, pos);
983    if head != TofEvent::HEAD {
984      return Err(SerializationError::HeadInvalid);
985    }
986    let mut te            = Self::new();
987    // the compression level will always be 0 for old data
988    let _compression_level = parse_u8(stream, pos);
989
990    te.quality            = EventQuality::from(parse_u8(stream, pos));
991    // at this position is the serialized TofEventHeader. We don't have that anymore (>v0.11). 
992    // However, the only information we need from it is the run id, the other fields are anyway
993    // empty
994    *pos += 2; // for TofEventHeader::HEAD
995    // FIXME - potentially dangerous for u16 overflow!
996    te.run_id             = parse_u32(stream, pos) as u16;
997    *pos += 43 - 6;// rest of TofEventHeader 
998    //let header         = TofEventHeader::from_bytestream(stream, &mut pos)?;
999    // now parse the "old" MasterTriggerEvent
1000    *pos += 2; // MasterTriggerEvent::HEAD
1001    let event_status   = parse_u8 (stream, pos);
1002    te.status              = EventStatus::from(event_status);
1003    if te.has_any_mangling() {
1004      te.status = EventStatus::AnyDataMangling;
1005    }
1006    te.event_id        = parse_u32(stream, pos);
1007    let mtb_timestamp  = parse_u32(stream, pos);
1008    let tiu_timestamp  = parse_u32(stream, pos);
1009    let tiu_gps32      = parse_u32(stream, pos);
1010    let _tiu_gps16     = parse_u16(stream,pos);
1011    let _crc           = parse_u32(stream, pos);
1012    let mt_timestamp   = (mt_event_get_timestamp_abs48(mtb_timestamp, tiu_gps32, tiu_timestamp ) as f64/1000.0).floor()  as u64; 
1013    te.timestamp32      = (mt_timestamp  & 0x00000000ffffffff ) as u32;
1014    te.timestamp16      = ((mt_timestamp & 0x0000ffff00000000 ) >> 32) as u16;
1015    te.trigger_sources  = parse_u16(stream, pos);
1016    te.dsi_j_mask       = parse_u32(stream, pos);
1017    let n_channel_masks = parse_u8(stream, pos);
1018    for _ in 0..n_channel_masks {
1019      te.channel_mask.push(parse_u16(stream, pos));
1020    }
1021
1022    te.mtb_link_mask      = parse_u64(stream, pos);
1023    let mt_event_tail     = parse_u16(stream, pos);
1024    if mt_event_tail != Self::TAIL {
1025      // (tail for mt event was the same)
1026      error!("Parsed TAIL from MT event is incorrect! Got {} instead of {} at pos {}", mt_event_tail, Self::TAIL, pos);
1027    }
1028    //let mt_event      = MasterTriggerEvent::from_bytestream(stream, &mut pos)?;
1029    let v_sizes           = Self::decode_depr_tofevent_size_header(&parse_u32(stream, pos));
1030    //println!("TofEvent - rbevents,  {:?}", v_sizes);
1031    for _ in 0..v_sizes.0 {
1032      // we are getting all waveforms for now, but we can 
1033      // discard them later
1034      let next_rb_event = RBEvent::from_bytestream(stream, pos)?;
1035      //println!("{}", next_rb_event);
1036      te.rb_events.push(next_rb_event);
1037    }
1038    //println!("{}",te);
1039    
1040    // FIXME - this is slow, use Arc<> instead. However, then make 
1041    // sure to copy them in case we get rid of the rb evvnts 
1042    // (or does Arc take care of it) 
1043    for rbev in &te.rb_events {
1044      for h in &rbev.hits {
1045        te.hits.push(*h);
1046      }
1047    }
1048    let tail = parse_u16(stream, pos);
1049    if tail != Self::TAIL {
1050      error!("Decoding of TAIL failed! Got {} instead!", tail);
1051      return Err(SerializationError::TailInvalid);
1052    }
1053    return Ok(te);
1054  }
1055}
1056    
1057impl Default for TofEvent {
1058  fn default() -> Self {
1059    Self::new()
1060  }
1061}
1062
1063impl fmt::Display for TofEvent {
1064  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1065    let mut repr = format!("<TofEvent (version {})", self.version);
1066    repr += &(format!("\n  EventID          : {}", self.event_id));
1067    repr += &(format!("\n  RunID            : {}", self.run_id));
1068    repr += &(format!("\n  EventStatus      : {}", self.status));
1069    repr += &(format!("\n  TriggerSources   : {:?}", self.get_trigger_sources()));
1070    repr += &(format!("\n  NTrigPaddles     : {}", self.n_trigger_paddles));
1071    repr += &(format!("\n  DRS dead hits    : {}", self.drs_dead_lost_hits));
1072    repr += &(format!("\n  timestamp32      : {}", self.timestamp32)); 
1073    repr += &(format!("\n  timestamp16      : {}", self.timestamp16)); 
1074    repr += &(format!("\n   |-> timestamp48 : {}", self.get_timestamp48())); 
1075    repr += &(format!("\n   |->       UNIX  : {}", (1e-8) * self.get_timestamp48() as f64)); 
1076    repr += &(format!("\n   |->        UTC  : {}", get_utc_timestamp_from_unix((1e-8) * self.get_timestamp48() as f64).unwrap_or(String::from("INVALID")))); 
1077    //repr += &(format!("\n  mt_tiu_gps16     : {}", self.mt_tiu_gps16));
1078    //repr += &(format!("\n  mt_tiu_gps32     : {}", self.mt_tiu_gps32)); 
1079    //repr += &(format!("\n  mt_timestamp     : {}", self.mt_timestamp));
1080    //repr += &(format!("\n  mt_tiu_timestamp : {}", self.mt_tiu_timestamp));
1081    //repr += &(format!("\n  gps timestamp    : {}", self.get_mt_timestamp_abs()));
1082    //repr += &(format!("\n  PrimaryBeta      : {}", self.get_beta())); 
1083    //repr += &(format!("\n  PrimaryCharge    : {}", self.primary_charge));
1084    if self.version == ProtocolVersion::V1 {
1085      repr += "\n ---- V1 variables ----";
1086      repr += &(format!("\n n_hits_umb   : {}", self.n_hits_umb  )); 
1087      repr += &(format!("\n n_hits_cbe   : {}", self.n_hits_cbe  )); 
1088      repr += &(format!("\n n_hits_cor   : {}", self.n_hits_cor  )); 
1089      repr += &(format!("\n tot_edep_umb : {}", self.tot_edep_umb)); 
1090      repr += &(format!("\n tot_edep_cbe : {}", self.tot_edep_cbe)); 
1091      repr += &(format!("\n tot_edep_cor : {}", self.tot_edep_cor)); 
1092    }
1093    repr += &(format!("\n  ** ** TRIGGER HITS (DSI/J/CH) [{} LTBS] ** **", self.dsi_j_mask.count_ones()));
1094    for k in self.get_trigger_hits() {
1095      repr += &(format!("\n  => {}/{}/({},{}) ({}) ", k.0, k.1, k.2.0, k.2.1, k.3));
1096    }
1097    repr += "\n  ** ** MTB LINK IDs ** **";
1098    let mut mtblink_str = String::from("\n  => ");
1099    for k in self.get_rb_link_ids() {
1100      mtblink_str += &(format!("{} ", k))
1101    }
1102    repr += &mtblink_str;
1103    repr += &(format!("\n  == Trigger hits {}, expected RBEvents {}",
1104            self.get_trigger_hits().len(),
1105            self.get_rb_link_ids().len()));
1106    repr += &String::from("\n  ** ** ** HITS ** ** **");
1107    for h in &self.hits {
1108      repr += &(format!("\n  {}", h));
1109    }
1110    if self.rb_events.len() > 0 {
1111      repr += &format!("\n -- has {} RBEvents with waveforms!", self.rb_events.len());
1112      repr += "\n -- -- boards: ";
1113      for b in &self.rb_events {
1114        repr += &format!("{} ", b.header.rb_id);
1115      }
1116    }
1117    repr += ">";
1118    write!(f, "{}", repr)
1119  }
1120}
1121
1122#[cfg(feature="random")]
1123impl FromRandom for TofEvent {
1124
1125  fn from_random() -> Self {
1126    let mut event             = Self::new();
1127    let mut rng               = rand::rng();
1128    let status                = EventStatus::from_random();
1129    let version               = ProtocolVersion::from_random();
1130    if version == ProtocolVersion::V1 {
1131      event.n_hits_umb        = rng.random::<u8>();
1132      event.n_hits_cbe        = rng.random::<u8>();
1133      event.n_hits_cor        = rng.random::<u8>();
1134      event.tot_edep_umb      = rng.random::<f32>();
1135      event.tot_edep_cbe      = rng.random::<f32>();
1136      event.tot_edep_cor      = rng.random::<f32>();
1137      event.quality           = EventQuality::from_random();
1138    }
1139    event.status             = status;
1140    event.version            = version;
1141    // variable packet for the FC
1142    event.trigger_sources    = rng.random::<u16>();
1143    event.n_trigger_paddles  = rng.random::<u8>();
1144    event.event_id           = rng.random::<u32>();
1145    event.timestamp32        = rng.random::<u32>();
1146    event.timestamp16        = rng.random::<u16>();
1147    event.drs_dead_lost_hits = rng.random::<u16>();
1148    event.dsi_j_mask         = rng.random::<u32>();
1149    let n_channel_masks        = rng.random::<u8>();
1150    for _ in 0..n_channel_masks {
1151      event.channel_mask.push(rng.random::<u16>());
1152    }
1153    event.mtb_link_mask      = rng.random::<u64>();
1154    //let nhits                  = rng.random::<u8>();
1155    let nhits: u16 = rng.random_range(0..5);
1156    for _ in 0..nhits {
1157      event.hits.push(TofHit::from_random());
1158    }
1159    if event.version == ProtocolVersion::V2 {
1160      let n_rb_events = rng.random_range(0..4);
1161      for _ in 0..n_rb_events {
1162        event.rb_events.push(RBEvent::from_random());
1163      }
1164    }
1165    event
1166  }
1167}
1168
1169//---------------------------------------------------
1170
1171#[cfg(feature="pybindings")]
1172#[pymethods]
1173impl TofEvent {
1174   
1175  #[pyo3(name="strip_rbevents")]
1176  fn strip_rbevents_py(&mut self) {
1177    self.strip_rbevents()
1178  }
1179  
1180  /// Calculate the TOF part of the interesting events mechanism, whcih is
1181  /// NHIT (CBE, COR, UMB) and EDEP (CBE, COR, UMB)
1182  #[pyo3(name="calc_gcu_variables")]
1183  fn calc_gcu_variables_py(&mut self) {
1184    self.calc_gcu_variables()
1185  }
1186
1187  /// Emit a copy of self
1188  fn copy(&self) -> Self {
1189    self.clone()
1190  }
1191
1192  #[pyo3(name="set_timing_offsets")]
1193  pub fn set_timing_offsets_py(&mut self, timing_offsets : HashMap<u8, f32>) {
1194    self.set_timing_offsets(&timing_offsets);
1195  }
1196  
1197  #[pyo3(name="normalize_hit_times")]
1198  pub fn normalize_hit_times_py(&mut self) {
1199    self.normalize_hit_times();
1200  }
1201
1202  /// Remove hits from the hitseries which can not 
1203  /// be caused by the same particle, which means 
1204  /// that for these two specific hits beta with 
1205  /// respect to the first hit in the event is 
1206  /// larger than one
1207  /// That this works, first hits need to be 
1208  /// "normalized" by calling normalize_hit_times
1209  #[pyo3(name="lightspeed_cleaning")]
1210  pub fn lightspeed_cleaning_py(&mut self, t_err : f32) -> (Vec<u16>, Vec<f32>) {
1211    // return Vec<u16> here so that python does not 
1212    // interpret it as a byte
1213    let mut pids = Vec::<u16>::new();
1214    let (pids_rm, twindows) = self.lightspeed_cleaning(t_err);
1215    for pid in pids_rm {
1216      pids.push(pid as u16);
1217    }
1218    (pids, twindows)
1219  }
1220 
1221  /// The run id 
1222  #[getter]
1223  fn get_run_id(&self) -> u16 { 
1224    self.run_id
1225  }
1226
1227  /// Remove all hits from the event's hit series which 
1228  /// do NOT obey causality. that is where the timings
1229  /// measured at ends A and B can not be correlated
1230  /// by the assumed speed of light in the paddle
1231  #[pyo3(name="remove_non_causal_hits")]
1232  fn remove_non_causal_hits_py(&mut self) -> Vec<u16> {
1233    // return Vec<u16> here so that python does not 
1234    // interpret it as a byte
1235    let mut pids = Vec::<u16>::new();
1236    for pid in self.remove_non_causal_hits() {
1237      pids.push(pid as u16);
1238    }
1239    pids
1240  }
1241  
1242  #[getter]
1243  fn pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
1244    self.get_pointcloud()
1245  }
1246
1247  #[getter]
1248  #[pyo3(name="has_any_mangling")]
1249  fn has_any_mangling_py(&self) -> bool {
1250    self.has_any_mangling() 
1251  }
1252
1253  #[getter]
1254  fn get_event_id(&self) -> u32 {
1255    self.event_id
1256  }
1257  
1258  #[getter]
1259  fn get_event_status(&self) -> EventStatus {
1260    self.status
1261  }
1262  
1263  /// Compare the hg hits of the event with the triggered paddles and 
1264  /// return the paddles which have at least a missing HG hit
1265  #[pyo3(name="get_missing_paddles_hg")]
1266  fn get_missing_paddles_hg_py(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
1267    self.get_missing_paddles_hg(&mapping)
1268  }
1269
1270  /// Get all the paddle ids which have been triggered
1271  #[pyo3(name="get_triggered_paddles")]
1272  fn get_triggered_paddles_py(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
1273    self.get_triggered_paddles(&mapping)
1274  }
1275
1276  /// The hits we were not able to read out because the DRS4 chip
1277  /// on the RBs was busy
1278  #[getter]
1279  fn lost_hits(&self) -> u16 {
1280    self.drs_dead_lost_hits
1281  }
1282
1283  /// RB Link IDS (not RB ids) which fall into the 
1284  /// trigger window
1285  #[getter]
1286  fn rb_link_ids(&self) -> Vec<u32> {
1287    self.get_rb_link_ids().into_iter().map(|byte| byte as u32).collect()
1288  }
1289
1290  /// The event might have RBEvents associated with it
1291  #[getter]
1292  fn get_rb_events(&self) -> Vec<RBEvent> {
1293    self.rb_events.clone()
1294  }
1295
1296  /// Hits which formed a trigger
1297  #[getter]
1298  pub fn trigger_hits(&self) -> PyResult<Vec<(u8, u8, (u8, u8), LTBThreshold)>> {
1299    Ok(self.get_trigger_hits())
1300  }
1301  
1302  /// The active triggers in this event. This can be more than one, 
1303  /// if multiple trigger conditions are satisfied.
1304  #[getter]
1305  pub fn trigger_sources(&self) -> Vec<TriggerType> {
1306    self.get_trigger_sources()
1307  } 
1308  
1309  /// The active triggers in this event. This can be more than one, 
1310  /// if multiple trigger conditions are satisfied.
1311  #[getter]
1312  #[pyo3(name="trigger_sources_bytes")]
1313  pub fn get_trigger_sources_bytes_py(&self) -> u16 {
1314    self.trigger_sources
1315  } 
1316
1317  #[pyo3(name="move_hits")]
1318  pub fn move_hits_py(&mut self) {
1319    self.move_hits()
1320  }
1321
1322  #[getter]
1323  #[pyo3(name="hits")]
1324  pub fn hits_py<'_py>(&self) -> Vec<TofHit> {
1325  //pub fn hits_py<'_py>(&self) -> PyResult<Bound<'_,Vec<TofHit>>> {
1326    //Bound::new(py, self.hits)
1327    //FIXMEFIXMEFIXME
1328    self.hits.clone()
1329  }
1330  
1331  #[getter]
1332  #[pyo3(name="hitmap")]
1333  pub fn hitmap<'_py>(&self) -> HashMap<u8,TofHit> {
1334  //pub fn hits_py<'_py>(&self) -> PyResult<Bound<'_,Vec<TofHit>>> {
1335    //Bound::new(py, self.hits)
1336    //FIXMEFIXMEFIXME
1337    let mut hitmap = HashMap::<u8, TofHit>::new();
1338    for h in &self.hits {
1339      hitmap.insert(h.paddle_id, *h);
1340    }
1341    hitmap
1342  }
1343  
1344  /// Total energy depostion in the Umbrella
1345  ///
1346  /// Utilizes Philip's formula based on 
1347  /// peak height
1348  #[getter]
1349  #[pyo3(name="edep_umb")]
1350  pub fn get_edep_umbrella_py(&self) -> f32 {
1351    self.get_edep_umbrella()
1352  }
1353  
1354  /// Total energy depostion in the Cube
1355  ///
1356  /// Utilizes Philip's formula based on 
1357  /// peak height
1358  #[getter]
1359  #[pyo3(name="edep_cbe")]
1360  pub fn get_edep_cube_py(&self) -> f32 {
1361    self.get_edep_cube()
1362  }
1363  
1364  /// Total energy depostion in the Cortina
1365  ///
1366  /// Utilizes Philip's formula based on 
1367  /// peak height
1368  #[getter]
1369  #[pyo3(name="edep_cor")]
1370  pub fn get_edep_cortina_py(&self) -> f32 {
1371    self.get_edep_cortina()
1372  }
1373
1374  /// Total energy depostion in the complete TOF
1375  ///
1376  /// Utilizes Philip's formula based on 
1377  /// peak height
1378  #[getter]
1379  #[pyo3(name="edep")]
1380  pub fn get_edep_py(&self) -> f32 {
1381    self.get_edep()
1382  }
1383
1384  #[getter]
1385  #[pyo3(name="nhits")]
1386  pub fn nhits_py(&self) -> usize {
1387    self.get_nhits()
1388  }
1389
1390  #[getter]
1391  #[pyo3(name="dsi_j_mask")]
1392  pub fn get_dsi_j_mask_py(&self) -> u32 {
1393    self.dsi_j_mask
1394  }
1395
1396  #[getter]
1397  #[pyo3(name="channel_masks")]
1398  pub fn get_channel_masks_py(&self) -> Vec<u16> {
1399    self.channel_mask.clone()
1400  }
1401
1402  #[getter]
1403  #[pyo3(name="nhits_umb")]
1404  pub fn nhits_umb_py(&self) -> usize {
1405    self.get_nhits_umb()
1406  }
1407
1408  #[getter]
1409  #[pyo3(name="nhits_cbe")]
1410  fn get_nhits_cbe_py(&self) -> usize {
1411    self.get_nhits_cbe()
1412  }
1413  
1414  #[getter]
1415  #[pyo3(name="nhits_cor")]
1416  fn get_nhits_cor_py(&self) -> usize {
1417    self.get_nhits_cor()
1418  }
1419
1420  #[getter]
1421  fn get_timestamp16(&self) -> u16 {
1422    self.timestamp16
1423  }
1424  
1425  #[getter]
1426  fn get_timestamp32(&self) -> u32 {
1427    self.timestamp32
1428  }
1429  
1430  #[getter]
1431  fn timestamp48(&self) -> u64 {
1432    self.get_timestamp48()
1433  }
1434  
1435  #[getter]
1436  fn get_status(&self) -> EventStatus {
1437    self.status
1438  }
1439
1440  #[getter]
1441  #[pyo3(name="waveforms")]
1442  fn get_waveforms_py(&self) -> Vec<RBWaveform> {
1443    self.get_waveforms()
1444  }
1445
1446  #[staticmethod]
1447  #[pyo3(name = "set_packed_status_version")]
1448  fn set_packed_status_version_py(pack : &mut TofPacket, version : ProtocolVersion) 
1449    -> PyResult<()> {
1450    match Self::set_packed_status_version(pack, version) {
1451      Err(err) => {
1452        let err_mesg = format!("Unable to set status version! {}", err);
1453        return Err(PyValueError::new_err(err_mesg));
1454      } 
1455      Ok(_) => {
1456        return Ok(());
1457      }
1458    }
1459  }
1460
1461  #[staticmethod]
1462  #[pyo3(name = "strip_packed_rbevents_for_pv3")]
1463  fn strip_packed_rbevents_for_pv3_py(pack : &mut TofPacket) 
1464    -> PyResult<()> {
1465    match Self::strip_packed_rbevents_for_pv3(pack) {
1466      Err(err) => {
1467        let err_msg = format!("Unable to strip packed rbevents{}", err);
1468        return Err(PyValueError::new_err(err_msg));
1469      } 
1470      Ok(_) => {
1471        return Ok(());
1472      }
1473    }
1474  }
1475  
1476  #[cfg(feature="database")]
1477  #[staticmethod]
1478  fn unpack(pack : &TofPacket) -> PyResult<Self> {
1479    if pack.packet_type != Self::TOF_PACKET_TYPE {
1480      let err_msg = format!("This is a packet of type {}, but we need type {}", pack.packet_type, Self::TOF_PACKET_TYPE);
1481      return Err(PyValueError::new_err(err_msg));
1482    }
1483    let mut pos = 0;
1484    let mut ev = Self::from_bytestream(&pack.payload,&mut pos)?; 
1485    ev.set_paddles(&pack.tof_paddles);
1486    Ok(ev)
1487  }
1488}
1489
1490#[cfg(feature="pybindings")]
1491pythonize_packable!(TofEvent);
1492
1493//---------------------------------------------------
1494
1495#[test]
1496#[cfg(feature="random")]
1497fn packable_tofeventv0() {
1498  for _ in 0..500 {
1499    let mut data = TofEvent::from_random();
1500    if data.version != ProtocolVersion::Unknown {
1501      continue;
1502    }
1503    let mut test : TofEvent = data.pack().unpack().unwrap();
1504    //println!("{}", data.hits[0]);
1505    //println!("{}", test.hits[0]);
1506    // Manually zero these fields, since comparison with nan will fail and 
1507    // from_random did not touch these
1508    //println!("{}", data);
1509    //println!("{}", test);
1510    let fix_time = Instant::now();
1511    test.creation_time = fix_time;
1512    data.creation_time = fix_time;
1513    for k in &mut data.rb_events {
1514      k.creation_time = None;
1515    }
1516    for k in &mut test.rb_events {
1517      k.creation_time = None;
1518    }
1519    for h in &mut test.hits {
1520      h.paddle_len       = 0.0; 
1521      h.coax_cable_time  = 0.0; 
1522      h.hart_cable_time  = 0.0; 
1523      h.x                = 0.0; 
1524      h.y                = 0.0; 
1525      h.z                = 0.0; 
1526      h.event_t0         = 0.0;
1527    }
1528    assert_eq!(data, test);
1529  }
1530}  
1531
1532#[test]
1533#[cfg(feature="random")]
1534fn packable_tofeventv1() {
1535  for _ in 0..500 {
1536    let mut data = TofEvent::from_random();
1537    if data.version != ProtocolVersion::V1 {
1538      continue;
1539    }
1540    let mut test : TofEvent = data.pack().unpack().unwrap();
1541    //println!("{}", data.hits[0]);
1542    //println!("{}", test.hits[0]);
1543    // Manually zero these fields, since comparison with nan will fail and 
1544    // from_random did not touch these
1545    let fix_time = Instant::now();
1546    test.creation_time = fix_time;
1547    data.creation_time = fix_time;
1548    for k in &mut data.rb_events {
1549      k.creation_time = None;
1550    }
1551    for k in &mut test.rb_events {
1552      k.creation_time = None;
1553    }
1554    for h in &mut test.hits {
1555      h.paddle_len       = 0.0; 
1556      h.coax_cable_time  = 0.0; 
1557      h.hart_cable_time  = 0.0; 
1558      h.x                = 0.0; 
1559      h.y                = 0.0; 
1560      h.z                = 0.0; 
1561      h.event_t0         = 0.0;
1562    }
1563    assert_eq!(data, test);
1564  }
1565}  
1566
1567#[test]
1568#[cfg(feature="random")]
1569fn packable_tofeventv2() {
1570  for _ in 0..500 {
1571    let mut data = TofEvent::from_random();
1572    if data.version != ProtocolVersion::V2 {
1573      continue;
1574    }
1575    let mut test : TofEvent = data.pack().unpack().unwrap();
1576    //println!("{}", data.hits[0]);
1577    //println!("{}", test.hits[0]);
1578    // Manually zero these fields, since comparison with nan will fail and 
1579    // from_random did not touch these
1580    let fix_time = Instant::now();
1581    test.creation_time = fix_time;
1582    data.creation_time = fix_time;
1583    for k in &mut data.rb_events {
1584      k.creation_time = None;
1585    }
1586    for k in &mut test.rb_events {
1587      k.creation_time = None;
1588    }
1589    for h in &mut test.hits {
1590      h.paddle_len       = 0.0; 
1591      h.coax_cable_time  = 0.0; 
1592      h.hart_cable_time  = 0.0; 
1593      h.x                = 0.0; 
1594      h.y                = 0.0; 
1595      h.z                = 0.0; 
1596      h.event_t0         = 0.0;
1597    }
1598    assert_eq!(data, test);
1599  }
1600}  
1601
1602#[test]
1603#[cfg(feature="random")]
1604fn packable_tofeventv3() {
1605  for _ in 0..500 {
1606    let mut data = TofEvent::from_random();
1607    if data.version != ProtocolVersion::V3 {
1608      continue;
1609    }
1610    let mut test : TofEvent = data.pack().unpack().unwrap();
1611    //println!("{}", data.hits[0]);
1612    //println!("{}", test.hits[0]);
1613    // Manually zero these fields, since comparison with nan will fail and 
1614    // from_random did not touch these
1615    let fix_time = Instant::now();
1616    test.creation_time = fix_time;
1617    data.creation_time = fix_time;
1618    for h in &mut test.hits {
1619      h.paddle_len       = 0.0; 
1620      h.coax_cable_time  = 0.0; 
1621      h.hart_cable_time  = 0.0; 
1622      h.x                = 0.0; 
1623      h.y                = 0.0; 
1624      h.z                = 0.0; 
1625      h.event_t0         = 0.0;
1626    }
1627    assert_eq!(data, test);
1628  }
1629}  
1630
1631#[test]
1632#[cfg(feature="random")]
1633fn tofevent_move_hits() {
1634  let mut event = TofEvent::from_random();
1635  let mut n_hits_exp = 0usize;
1636  for rb in &event.rb_events {
1637    n_hits_exp += rb.hits.len();
1638  }
1639  event.hits.clear();
1640  event.move_hits();
1641  for rb in &event.rb_events {
1642    assert_eq!(rb.hits.len(),0);
1643  }
1644  assert_eq!(n_hits_exp, event.hits.len());
1645
1646}
1647
1648#[test]
1649#[cfg(feature="random")] 
1650fn tofevent_striprbevents() {
1651  let mut event      = TofEvent::from_random();
1652  let mut n_hits_exp = 0usize;
1653  for rb in &event.rb_events {
1654    n_hits_exp += rb.hits.len();
1655  }
1656  event.hits.clear();
1657  event.strip_rbevents();
1658  assert_eq!(event.rb_events.len(),0);
1659  assert_eq!(n_hits_exp, event.hits.len());
1660}
1661
1662//#[test]
1663//#[cfg(feature = "random")]
1664//fn tofevent_sizes_header() {
1665//  for _ in 0..100 {
1666//    let data = TofEvent::from_random();
1667//    let mask = data.construct_sizes_header();
1668//    let size = TofEvent::decode_size_header(&mask);
1669//    assert_eq!(size.0, data.rb_events.len());
1670//    //assert_eq!(size.1, data.missing_hits.len());
1671//  }
1672//}
1673
1674//#[test]
1675//#[cfg(feature = "random")]
1676//fn packable_tofevent() {
1677//  for _ in 0..5 {
1678//    let data = TofEvent::from_random();
1679//    let test : TofEvent = data.pack().unpack().unwrap();
1680//    assert_eq!(data.header, test.header);
1681//    assert_eq!(data.compression_level, test.compression_level);
1682//    assert_eq!(data.quality, test.quality);
1683//    assert_eq!(data.mt_event, test.mt_event);
1684//    assert_eq!(data.rb_events.len(), test.rb_events.len());
1685//    //assert_eq!(data.missing_hits.len(), test.missing_hits.len());
1686//    //assert_eq!(data.missing_hits, test.missing_hits);
1687//    assert_eq!(data.rb_events, test.rb_events);
1688//    //assert_eq!(data, test);
1689//    //println!("{}", data);
1690//  }
1691//}
1692
1693
1694