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 self.hits.len() == 0 {
466      for rbev  in &mut self.rb_events {
467        for h in &mut rbev.hits {
468          match paddles.get(&h.paddle_id) {
469            None => {
470              error!("Got paddle id {} which is not in given map!", h.paddle_id);
471              nerror += 1;
472              continue;
473            }
474            Some(pdl) => {
475              h.set_paddle(pdl);
476            }
477          }
478        }
479      }
480    } else {
481      for h in &mut self.hits {
482        match paddles.get(&h.paddle_id) {
483          None => {
484            error!("Got paddle id {} which is not in given map!", h.paddle_id);
485            nerror += 1;
486            continue;
487          }
488          Some(pdl) => {
489            h.set_paddle(pdl);
490          }
491        }
492      }
493    }
494    if nerror == 0 {
495      self.paddles_set = true;
496      //self.normalize_hit_times();
497    }
498  }
499
500  /// Get the pointcloud of this event, sorted by time
501  /// 
502  /// # Returns
503  ///   (f32, f32, f32, f32, f32) : (x,y,z,t,edep)
504  pub fn get_pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
505    let mut pc = Vec::<(f32,f32,f32,f32,f32)>::new();
506    if !self.paddles_set {
507      error!("Before getting the pointcloud, paddle information needs to be set for this event. Call TofEvent;:set_paddle");
508      return None;
509    }
510    for h in &self.hits {
511      let result = (h.x, h.y, h.z, h.get_t0(), h.get_edep());
512      pc.push(result);
513    }
514    Some(pc)
515  }
516
517  /// Compare the MasterTriggerEvent::trigger_hits with 
518  /// the actual hits to determine from which paddles
519  /// we should have received HG hits (from waveforms)
520  /// but we did not get them
521  ///
522  /// WARNING: The current implementation of this is 
523  /// rather slow and not fit for production use
524  /// FIXME - rewrite as a closure
525  #[cfg(feature="database")]
526  pub fn get_missing_paddles_hg(&self, pid_map :   &DsiJChPidMapping) -> Vec<u8> {
527    let mut missing = Vec::<u8>::new();
528    for th in self.get_trigger_hits() {
529      if !pid_map.contains_key(&th.0) {
530        error!("Can't find {:?} in paddlemap!",th);
531        continue;
532      }
533      if !pid_map.get(&th.0).unwrap().contains_key(&th.1) {
534        error!("Can't find {:?} in paddlemap!",th);
535        continue;
536      }
537      if !pid_map.get(&th.0).unwrap().get(&th.1).unwrap().contains_key(&th.2.0) {
538        error!("Can't find {:?} in paddlemap!",th);
539        continue;
540      }
541      let pid = pid_map.get(&th.0).unwrap().get(&th.1).unwrap().get(&th.2.0).unwrap().0;
542      let mut found = false;
543      for h in &self.hits {
544        if h.paddle_id == pid {
545          found = true;
546          break
547        }
548      }
549      if !found {
550        missing.push(pid);
551      }
552    }
553    missing
554  }
555  
556  /// Get the triggered paddle ids
557  ///
558  /// Warning, this might be a bit slow
559  #[cfg(feature="database")]
560  pub fn get_triggered_paddles(&self, pid_map :   &DsiJChPidMapping) -> Vec<u8> {
561    let mut paddles = Vec::<u8>::with_capacity(3);
562    for th in &self.get_trigger_hits() {
563      let pid = pid_map.get(&th.0).unwrap().get(&th.1).unwrap().get(&th.2.0).unwrap().0;
564      paddles.push(pid);
565    }
566    paddles
567  }
568
569  /// Get the RB link IDs according to the mask
570  pub fn get_rb_link_ids(&self) -> Vec<u8> {
571    let mut links = Vec::<u8>::new();
572    for k in 0..64 {
573      if (self.mtb_link_mask >> k) as u64 & 0x1 == 1 {
574        links.push(k as u8);
575      }
576    }
577    links
578  }
579
580  /// Get the combination of triggered DSI/J/CH on 
581  /// the MTB which formed the trigger. This does 
582  /// not include further hits which fall into the 
583  /// integration window. For those, se rb_link_mask
584  ///
585  /// The returned values follow the TOF convention
586  /// to start with 1, so that we can use them to 
587  /// look up LTB ids in the db.
588  ///
589  /// # Returns
590  ///
591  ///   Vec<(hit)> where hit is (DSI, J, CH) 
592  pub fn get_trigger_hits(&self) -> Vec<(u8, u8, (u8, u8), LTBThreshold)> {
593    let mut hits = Vec::<(u8,u8,(u8,u8),LTBThreshold)>::with_capacity(5); 
594    //let n_masks_needed = self.dsi_j_mask.count_ones() / 2 + self.dsi_j_mask.count_ones() % 2;
595    let n_masks_needed = self.dsi_j_mask.count_ones();
596    if self.channel_mask.len() < n_masks_needed as usize {
597      error!("We need {} hit masks, but only have {}! This is bad!", n_masks_needed, self.channel_mask.len());
598      return hits;
599    }
600    let mut n_mask = 0;
601    trace!("Expecting {} hit masks", n_masks_needed);
602    trace!("ltb channels {:?}", self.dsi_j_mask);
603    trace!("hit masks {:?}", self.channel_mask); 
604    //println!("We see LTB Channels {:?} with Hit masks {:?} for {} masks requested by us!", self.dsi_j_mask, self.channel_mask, n_masks_needed);
605    
606    // one k here is for one ltb
607    for k in 0..32 {
608      if (self.dsi_j_mask >> k) as u32 & 0x1 == 1 {
609        let mut dsi = 0u8;
610        let mut j   = 0u8;
611        if k < 5 {
612          dsi = 1;
613          j   = k as u8 + 1;
614        } else if k < 10 {
615          dsi = 2;
616          j   = k as u8 - 5 + 1;
617        } else if k < 15 {
618          dsi = 3;
619          j   = k as u8- 10 + 1;
620        } else if k < 20 {
621          dsi = 4;
622          j   = k as u8- 15 + 1;
623        } else if k < 25 {
624          dsi = 5;
625          j   = k as u8 - 20 + 1;
626        } 
627        //let dsi = (k as f32 / 4.0).floor() as u8 + 1;       
628        //let j   = (k % 5) as u8 + 1;
629        //println!("n_mask {n_mask}");
630        let channels = self.channel_mask[n_mask]; 
631        for (i,ch) in LTB_CHANNELS.iter().enumerate() {
632          //let chn = *ch as u8 + 1;
633          let ph_chn = PHYSICAL_CHANNELS[i];
634          //let chn = i as u8 + 1;
635          //println!("i,ch {}, {}", i, ch);
636          let thresh_bits = ((channels & ch) >> (i*2)) as u8;
637          //println!("thresh_bits {}", thresh_bits);
638          if thresh_bits > 0 { // hit over threshold
639            hits.push((dsi, j, ph_chn, LTBThreshold::from(thresh_bits)));
640          }
641        }
642        n_mask += 1;
643      } // next ltb
644    }
645    hits
646  }
647  
648  /// Get the trigger sources from trigger source byte
649  pub fn get_trigger_sources(&self) -> Vec<TriggerType> {
650    TriggerType::transcode_trigger_sources(self.trigger_sources)
651  }
652  
653  pub fn get_timestamp48(&self) -> u64 {
654    0x273000000000000 | (((self.timestamp16 as u64) << 32) | self.timestamp32 as u64)
655  }
656  
657  /// Ttotal energy depostion in the TOF - Umbrella
658  ///
659  /// Utilizes Philip's formula based on 
660  /// peak height
661  pub fn get_edep_umbrella(&self) -> f32 {
662    let mut tot_edep = 0.0f32;
663    for h in &self.hits {
664      if h.paddle_id < 61 || h.paddle_id > 108 {
665        continue;
666      }
667      tot_edep += h.get_edep();
668    }
669    tot_edep
670  }
671  
672  /// Ttotal energy depostion in the TOF - Umbrella
673  ///
674  /// Utilizes Philip's formula based on 
675  /// peak height
676  pub fn get_edep_cube(&self) -> f32 {
677    let mut tot_edep = 0.0f32;
678    for h in &self.hits {
679      if h.paddle_id > 60 {
680        continue;
681      }
682      tot_edep += h.get_edep();
683    }
684    tot_edep
685  }
686  
687  /// Ttotal energy depostion in the Cortina
688  ///
689  /// Utilizes Philip's formula based on 
690  /// peak height
691  pub fn get_edep_cortina(&self) -> f32 {
692    let mut tot_edep = 0.0f32;
693    for h in &self.hits {
694      if h.paddle_id < 109 {
695        continue;
696      }
697      tot_edep += h.get_edep();
698    }
699    tot_edep
700  }
701  
702  /// Ttotal energy depostion in the complete TOF
703  ///
704  /// Utilizes Philip's formula based on 
705  /// peak height
706  pub fn get_edep(&self) -> f32 {
707    let mut tot_edep = 0.0f32;
708    for h in &self.hits {
709      tot_edep += h.get_edep();
710    }
711    tot_edep
712  }
713
714  pub fn get_nhits_umb(&self) -> usize {
715    let mut nhit = 0;
716    for h in &self.hits {
717      if h.paddle_id > 60 && h.paddle_id < 109 {
718        nhit += 1;
719      }
720    }
721    nhit
722  }
723  
724  pub fn get_nhits_cbe(&self) -> usize {
725    let mut nhit = 0;
726    for h in &self.hits {
727      if h.paddle_id < 61  {
728        nhit += 1;
729      }
730    }
731    nhit
732  }
733  
734  pub fn get_nhits_cor(&self) -> usize {
735    let mut nhit = 0;
736    for h in &self.hits {
737      if h.paddle_id > 108  {
738        nhit += 1;
739      }
740    }
741    nhit
742  }
743
744  pub fn get_nhits(&self) -> usize {
745    self.hits.len()
746  }
747  
748  /// Check if th eassociated RBEvents have any of their
749  /// mangling stati set
750  pub fn has_any_mangling(&self) -> bool {
751    for rbev in &self.rb_events {
752      if rbev.status == EventStatus::CellAndChnSyncErrors 
753      || rbev.status == EventStatus::CellSyncErrors 
754      || rbev.status == EventStatus::ChnSyncErrors {
755        return true;
756      }
757    }
758    false
759  }
760  
761  /// Get all waveforms of all RBEvents in this event
762  /// ISSUE - Performance, Memory
763  /// FIXME - reimplement this things where this
764  ///         returns only a reference
765  pub fn get_waveforms(&self) -> Vec<RBWaveform> {
766    let mut wfs = Vec::<RBWaveform>::new();
767    for ev in &self.rb_events {
768      for wf in &ev.get_rbwaveforms() {
769        wfs.push(wf.clone());
770      }
771    }
772    wfs
773  }
774
775  /// Change the status version when the event is already 
776  /// packed. The status version is encoded in byte 2 
777  /// (starting from 0) in the payload of the TofPacket 
778  pub fn set_packed_status_version(pack : &mut TofPacket, version : ProtocolVersion) 
779    -> Result<(), SerializationError> {
780    if pack.packet_type != TofPacketType::TofEvent {
781      return Err(SerializationError::IncorrectPacketType);
782    }
783    let mut status_version = pack.payload[2];
784    // null the bytes relevant for the version 
785    status_version  = status_version & 0x3f;
786    // now or the bytes for the new version 
787    status_version  = status_version | version.to_u8();
788    pack.payload[2] = status_version; 
789    Ok(()) 
790  }
791
792  /// For events with ProtocolVersion == V3, 
793  /// we have the rbevents at the end and the 
794  /// packet contains the GCU variables.
795  ///
796  /// This can remove the rbevents from a packed bytestrem,
797  /// and will reset the ProtocolVersion to V2.
798  /// The result is an event which should be ready 
799  /// to be sent to the GCU.
800  pub fn strip_packed_rbevents_for_pv3(pack : &mut TofPacket)
801    -> Result<(), SerializationError> {
802    if pack.packet_type != TofPacketType::TofEvent {
803      return Err(SerializationError::IncorrectPacketType);
804    }
805    let status_version = pack.payload[2];
806    let mut version    = ProtocolVersion::from(status_version & 0xc0);
807    if version != ProtocolVersion::V3 {
808      error!("This operation can only be executed on {}, however, this is version {}!", ProtocolVersion::V3, version);
809      return Err(SerializationError::WrongProtocolVersion);
810    }
811    // jump to the start of RBEvents 
812    let mut pos = 0usize;
813    pos += 10; // header 
814    pos += 15; // gcu variables (protocolversion V1 & V3) 
815    pos += 15;
816    if pack.payload.len() >= pos {
817      return Err(SerializationError::StreamTooShort);
818    }
819    let nmasks = parse_u8(&pack.payload, &mut pos);
820    for _ in 0..nmasks {
821      pos += 2;
822    }
823    pos += 8;
824    let nhits  = parse_u16(&pack.payload,&mut pos);
825    // set back the version
826    for _ in 0..nhits {
827      pos += TofHit::SIZE;
828      // FIXME - if we don't be able to manage 
829      //         to have a consistent size for 
830      //         TofHit, we have to deserialize them 
831      //         here (or write a minimal deserializer
832    }
833    // the next byte is finally the number of RBEvents. 
834    // So we set that to 0, strip the rest of the paylod 
835    // and re-attach the TAIL
836    pack.payload.truncate(pack.payload.len() - pos);
837    pack.payload.extend_from_slice(&Self::TAIL.to_le_bytes());
838    version = ProtocolVersion::V2;
839    Self::set_packed_status_version(pack, version)?;
840    Ok(())
841  }
842}
843
844impl TofPackable for TofEvent {
845  // v0.11 TofPacketType::TofEventSummary -> TofPacketType::TofEvent
846  const TOF_PACKET_TYPE        : TofPacketType = TofPacketType::TofEvent;
847  const TOF_PACKET_TYPE_ALT    : TofPacketType = TofPacketType::TofEventDeprecated;
848}
849
850impl Serialization for TofEvent {
851  
852  const HEAD               : u16   = 43690; //0xAAAA
853  const TAIL               : u16   = 21845; //0x5555
854  
855  fn to_bytestream(&self) -> Vec<u8> {
856    let mut stream = Vec::<u8>::new();
857    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
858    let status_version = self.status as u8 | self.version.to_u8();
859    stream.push(status_version);
860    stream.extend_from_slice(&self.trigger_sources.to_le_bytes());
861    stream.extend_from_slice(&self.n_trigger_paddles.to_le_bytes());
862    stream.extend_from_slice(&self.event_id.to_le_bytes());
863    // depending on the version, we send the fc event packet
864    if self.version == ProtocolVersion::V1 
865      || self.version == ProtocolVersion::V3 {
866      stream.extend_from_slice(&self.n_hits_umb  .to_le_bytes()); 
867      stream.extend_from_slice(&self.n_hits_cbe  .to_le_bytes()); 
868      stream.extend_from_slice(&self.n_hits_cor  .to_le_bytes()); 
869      stream.extend_from_slice(&self.tot_edep_umb.to_le_bytes()); 
870      stream.extend_from_slice(&self.tot_edep_cbe.to_le_bytes()); 
871      stream.extend_from_slice(&self.tot_edep_cor.to_le_bytes()); 
872    }
873    stream.extend_from_slice(&(self.quality as u8).to_le_bytes());
874    stream.extend_from_slice(&self.timestamp32.to_le_bytes());
875    stream.extend_from_slice(&self.timestamp16.to_le_bytes());
876    stream.extend_from_slice(&self.run_id.to_le_bytes());
877    stream.extend_from_slice(&self.drs_dead_lost_hits.to_le_bytes());
878    stream.extend_from_slice(&self.dsi_j_mask.to_le_bytes());
879    let n_channel_masks = self.channel_mask.len();
880    stream.push(n_channel_masks as u8);
881    for k in 0..n_channel_masks {
882      stream.extend_from_slice(&self.channel_mask[k].to_le_bytes());
883    }
884    stream.extend_from_slice(&self.mtb_link_mask.to_le_bytes());
885    let nhits = self.hits.len() as u16;
886    stream.extend_from_slice(&nhits.to_le_bytes());
887    for k in 0..self.hits.len() {
888      stream.extend_from_slice(&self.hits[k].to_bytestream());
889    }
890    // for the new (>=v0.11) event, we will always write 
891    // the rb events
892    if self.version == ProtocolVersion::V2 
893      || self.version == ProtocolVersion::V3 {
894      stream.push(self.rb_events.len() as u8);
895      for rbev in &self.rb_events {
896        stream.extend_from_slice(&rbev.to_bytestream());
897      }
898    }
899    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
900    stream
901  }
902  
903  fn from_bytestream(stream    : &Vec<u8>, 
904                     pos       : &mut usize) 
905    -> Result<Self, SerializationError>{
906    let mut event = Self::new();
907    let head      = parse_u16(stream, pos);
908    if head != Self::HEAD {
909      error!("Decoding of HEAD failed! Got {} instead!", head);
910      return Err(SerializationError::HeadInvalid);
911    }
912    
913    let status_version_u8   = parse_u8(stream, pos);
914    let status              = EventStatus::from(status_version_u8 & 0x3f);
915    let version             = ProtocolVersion::from(status_version_u8 & 0xc0); 
916    event.status            = status;
917    event.version           = version;
918    event.trigger_sources   = parse_u16(stream, pos);
919    event.n_trigger_paddles = parse_u8(stream, pos);
920    event.event_id          = parse_u32(stream, pos);
921    if event.version == ProtocolVersion::V1
922      || event.version == ProtocolVersion::V3 {
923      event.n_hits_umb      = parse_u8(stream, pos); 
924      event.n_hits_cbe      = parse_u8(stream, pos); 
925      event.n_hits_cor      = parse_u8(stream, pos); 
926      event.tot_edep_umb    = parse_f32(stream, pos); 
927      event.tot_edep_cbe    = parse_f32(stream, pos); 
928      event.tot_edep_cor    = parse_f32(stream, pos); 
929    }
930    event.quality            = EventQuality::from(parse_u8(stream, pos));
931    event.timestamp32        = parse_u32(stream, pos);
932    event.timestamp16        = parse_u16(stream, pos);
933    event.run_id             = parse_u16(stream, pos);
934    event.drs_dead_lost_hits = parse_u16(stream, pos);
935    event.dsi_j_mask         = parse_u32(stream, pos);
936    let n_channel_masks        = parse_u8(stream, pos);
937    for _ in 0..n_channel_masks {
938      event.channel_mask.push(parse_u16(stream, pos));
939    }
940    event.mtb_link_mask      = parse_u64(stream, pos);
941    let nhits                = parse_u16(stream, pos);
942    //println!("{}", event);
943    if nhits > 160 {
944      error!("There are an abnormous amount of hits in this event!");
945      return Err(SerializationError::StreamTooLong);
946    } 
947    for _ in 0..nhits {
948      event.hits.push(TofHit::from_bytestream(stream, pos)?);
949    }
950    if event.version == ProtocolVersion::V2 
951      || event.version == ProtocolVersion::V3 {
952      let n_rb_events = parse_u8(stream, pos);
953      if n_rb_events > 0 {
954        for _ in 0..n_rb_events {
955          event.rb_events.push(RBEvent::from_bytestream(stream, pos)?);
956        }
957      }
958    }
959
960    let tail = parse_u16(stream, pos);
961    if tail != Self::TAIL {
962      error!("Decoding of TAIL failed for version {}! Got {} instead!", version, tail);
963      return Err(SerializationError::TailInvalid);
964    }
965    Ok(event)
966  }
967  
968  /// Allows to get TofEvent from a packet 
969  /// of the deprecate packet type TofEventDeprecated.
970  /// This packet type was formerly known as TofEvent
971  ///
972  /// This will produce an event with rbevents & hits.
973  fn from_bytestream_alt(stream    : &Vec<u8>, 
974                         pos       : &mut usize) 
975    -> Result<Self, SerializationError> {
976    let head = parse_u16(stream, pos);
977    if head != TofEvent::HEAD {
978      return Err(SerializationError::HeadInvalid);
979    }
980    let mut te            = Self::new();
981    // the compression level will always be 0 for old data
982    let _compression_level = parse_u8(stream, pos);
983
984    te.quality            = EventQuality::from(parse_u8(stream, pos));
985    // at this position is the serialized TofEventHeader. We don't have that anymore (>v0.11). 
986    // However, the only information we need from it is the run id, the other fields are anyway
987    // empty
988    *pos += 2; // for TofEventHeader::HEAD
989    // FIXME - potentially dangerous for u16 overflow!
990    te.run_id             = parse_u32(stream, pos) as u16;
991    *pos += 43 - 6;// rest of TofEventHeader 
992    //let header         = TofEventHeader::from_bytestream(stream, &mut pos)?;
993    // now parse the "old" MasterTriggerEvent
994    *pos += 2; // MasterTriggerEvent::HEAD
995    let event_status   = parse_u8 (stream, pos);
996    te.status              = EventStatus::from(event_status);
997    if te.has_any_mangling() {
998      te.status = EventStatus::AnyDataMangling;
999    }
1000    te.event_id        = parse_u32(stream, pos);
1001    let mtb_timestamp  = parse_u32(stream, pos);
1002    let tiu_timestamp  = parse_u32(stream, pos);
1003    let tiu_gps32      = parse_u32(stream, pos);
1004    let _tiu_gps16     = parse_u16(stream,pos);
1005    let _crc           = parse_u32(stream, pos);
1006    let mt_timestamp   = (mt_event_get_timestamp_abs48(mtb_timestamp, tiu_gps32, tiu_timestamp ) as f64/1000.0).floor()  as u64; 
1007    te.timestamp32      = (mt_timestamp  & 0x00000000ffffffff ) as u32;
1008    te.timestamp16      = ((mt_timestamp & 0x0000ffff00000000 ) >> 32) as u16;
1009    te.trigger_sources  = parse_u16(stream, pos);
1010    te.dsi_j_mask       = parse_u32(stream, pos);
1011    let n_channel_masks = parse_u8(stream, pos);
1012    for _ in 0..n_channel_masks {
1013      te.channel_mask.push(parse_u16(stream, pos));
1014    }
1015
1016    te.mtb_link_mask      = parse_u64(stream, pos);
1017    let mt_event_tail     = parse_u16(stream, pos);
1018    if mt_event_tail != Self::TAIL {
1019      // (tail for mt event was the same)
1020      error!("Parsed TAIL from MT event is incorrect! Got {} instead of {} at pos {}", mt_event_tail, Self::TAIL, pos);
1021    }
1022    //let mt_event      = MasterTriggerEvent::from_bytestream(stream, &mut pos)?;
1023    let v_sizes           = Self::decode_depr_tofevent_size_header(&parse_u32(stream, pos));
1024    //println!("TofEvent - rbevents,  {:?}", v_sizes);
1025    for _ in 0..v_sizes.0 {
1026      // we are getting all waveforms for now, but we can 
1027      // discard them later
1028      let next_rb_event = RBEvent::from_bytestream(stream, pos)?;
1029      //println!("{}", next_rb_event);
1030      te.rb_events.push(next_rb_event);
1031    }
1032    //println!("{}",te);
1033    
1034    // FIXME - this is slow, use Arc<> instead. However, then make 
1035    // sure to copy them in case we get rid of the rb evvnts 
1036    // (or does Arc take care of it) 
1037    for rbev in &te.rb_events {
1038      for h in &rbev.hits {
1039        te.hits.push(*h);
1040      }
1041    }
1042    let tail = parse_u16(stream, pos);
1043    if tail != Self::TAIL {
1044      error!("Decoding of TAIL failed! Got {} instead!", tail);
1045      return Err(SerializationError::TailInvalid);
1046    }
1047    return Ok(te);
1048  }
1049}
1050    
1051impl Default for TofEvent {
1052  fn default() -> Self {
1053    Self::new()
1054  }
1055}
1056
1057impl fmt::Display for TofEvent {
1058  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1059    let mut repr = format!("<TofEvent (version {})", self.version);
1060    repr += &(format!("\n  EventID          : {}", self.event_id));
1061    repr += &(format!("\n  RunID            : {}", self.run_id));
1062    repr += &(format!("\n  EventStatus      : {}", self.status));
1063    repr += &(format!("\n  TriggerSources   : {:?}", self.get_trigger_sources()));
1064    repr += &(format!("\n  NTrigPaddles     : {}", self.n_trigger_paddles));
1065    repr += &(format!("\n  DRS dead hits    : {}", self.drs_dead_lost_hits));
1066    repr += &(format!("\n  timestamp32      : {}", self.timestamp32)); 
1067    repr += &(format!("\n  timestamp16      : {}", self.timestamp16)); 
1068    repr += &(format!("\n   |-> timestamp48 : {}", self.get_timestamp48())); 
1069    //repr += &(format!("\n  mt_tiu_gps16     : {}", self.mt_tiu_gps16));
1070    //repr += &(format!("\n  mt_tiu_gps32     : {}", self.mt_tiu_gps32)); 
1071    //repr += &(format!("\n  mt_timestamp     : {}", self.mt_timestamp));
1072    //repr += &(format!("\n  mt_tiu_timestamp : {}", self.mt_tiu_timestamp));
1073    //repr += &(format!("\n  gps timestamp    : {}", self.get_mt_timestamp_abs()));
1074    //repr += &(format!("\n  PrimaryBeta      : {}", self.get_beta())); 
1075    //repr += &(format!("\n  PrimaryCharge    : {}", self.primary_charge));
1076    if self.version == ProtocolVersion::V1 {
1077      repr += "\n ---- V1 variables ----";
1078      repr += &(format!("\n n_hits_umb   : {}", self.n_hits_umb  )); 
1079      repr += &(format!("\n n_hits_cbe   : {}", self.n_hits_cbe  )); 
1080      repr += &(format!("\n n_hits_cor   : {}", self.n_hits_cor  )); 
1081      repr += &(format!("\n tot_edep_umb : {}", self.tot_edep_umb)); 
1082      repr += &(format!("\n tot_edep_cbe : {}", self.tot_edep_cbe)); 
1083      repr += &(format!("\n tot_edep_cor : {}", self.tot_edep_cor)); 
1084    }
1085    repr += &(format!("\n  ** ** TRIGGER HITS (DSI/J/CH) [{} LTBS] ** **", self.dsi_j_mask.count_ones()));
1086    for k in self.get_trigger_hits() {
1087      repr += &(format!("\n  => {}/{}/({},{}) ({}) ", k.0, k.1, k.2.0, k.2.1, k.3));
1088    }
1089    repr += "\n  ** ** MTB LINK IDs ** **";
1090    let mut mtblink_str = String::from("\n  => ");
1091    for k in self.get_rb_link_ids() {
1092      mtblink_str += &(format!("{} ", k))
1093    }
1094    repr += &mtblink_str;
1095    repr += &(format!("\n  == Trigger hits {}, expected RBEvents {}",
1096            self.get_trigger_hits().len(),
1097            self.get_rb_link_ids().len()));
1098    repr += &String::from("\n  ** ** ** HITS ** ** **");
1099    for h in &self.hits {
1100      repr += &(format!("\n  {}", h));
1101    }
1102    if self.rb_events.len() > 0 {
1103      repr += &format!("\n -- has {} RBEvents with waveforms!", self.rb_events.len());
1104      repr += "\n -- -- boards: ";
1105      for b in &self.rb_events {
1106        repr += &format!("{} ", b.header.rb_id);
1107      }
1108    }
1109    repr += ">";
1110    write!(f, "{}", repr)
1111  }
1112}
1113
1114#[cfg(feature="random")]
1115impl FromRandom for TofEvent {
1116
1117  fn from_random() -> Self {
1118    let mut event             = Self::new();
1119    let mut rng               = rand::rng();
1120    let status                = EventStatus::from_random();
1121    let version               = ProtocolVersion::from_random();
1122    if version == ProtocolVersion::V1 {
1123      event.n_hits_umb        = rng.random::<u8>();
1124      event.n_hits_cbe        = rng.random::<u8>();
1125      event.n_hits_cor        = rng.random::<u8>();
1126      event.tot_edep_umb      = rng.random::<f32>();
1127      event.tot_edep_cbe      = rng.random::<f32>();
1128      event.tot_edep_cor      = rng.random::<f32>();
1129      event.quality           = EventQuality::from_random();
1130    }
1131    event.status             = status;
1132    event.version            = version;
1133    // variable packet for the FC
1134    event.trigger_sources    = rng.random::<u16>();
1135    event.n_trigger_paddles  = rng.random::<u8>();
1136    event.event_id           = rng.random::<u32>();
1137    event.timestamp32        = rng.random::<u32>();
1138    event.timestamp16        = rng.random::<u16>();
1139    event.drs_dead_lost_hits = rng.random::<u16>();
1140    event.dsi_j_mask         = rng.random::<u32>();
1141    let n_channel_masks        = rng.random::<u8>();
1142    for _ in 0..n_channel_masks {
1143      event.channel_mask.push(rng.random::<u16>());
1144    }
1145    event.mtb_link_mask      = rng.random::<u64>();
1146    //let nhits                  = rng.random::<u8>();
1147    let nhits: u16 = rng.random_range(0..5);
1148    for _ in 0..nhits {
1149      event.hits.push(TofHit::from_random());
1150    }
1151    if event.version == ProtocolVersion::V2 {
1152      let n_rb_events = rng.random_range(0..4);
1153      for _ in 0..n_rb_events {
1154        event.rb_events.push(RBEvent::from_random());
1155      }
1156    }
1157    event
1158  }
1159}
1160
1161//---------------------------------------------------
1162
1163#[cfg(feature="pybindings")]
1164#[pymethods]
1165impl TofEvent {
1166   
1167  #[pyo3(name="strip_rbevents")]
1168  fn strip_rbevents_py(&mut self) {
1169    self.strip_rbevents()
1170  }
1171  
1172  /// Calculate the TOF part of the interesting events mechanism, whcih is
1173  /// NHIT (CBE, COR, UMB) and EDEP (CBE, COR, UMB)
1174  #[pyo3(name="calc_gcu_variables")]
1175  fn calc_gcu_variables_py(&mut self) {
1176    self.calc_gcu_variables()
1177  }
1178
1179  /// Emit a copy of self
1180  fn copy(&self) -> Self {
1181    self.clone()
1182  }
1183
1184  #[pyo3(name="set_timing_offsets")]
1185  pub fn set_timing_offsets_py(&mut self, timing_offsets : HashMap<u8, f32>) {
1186    self.set_timing_offsets(&timing_offsets);
1187  }
1188  
1189  #[pyo3(name="normalize_hit_times")]
1190  pub fn normalize_hit_times_py(&mut self) {
1191    self.normalize_hit_times();
1192  }
1193
1194  /// Remove hits from the hitseries which can not 
1195  /// be caused by the same particle, which means 
1196  /// that for these two specific hits beta with 
1197  /// respect to the first hit in the event is 
1198  /// larger than one
1199  /// That this works, first hits need to be 
1200  /// "normalized" by calling normalize_hit_times
1201  #[pyo3(name="lightspeed_cleaning")]
1202  pub fn lightspeed_cleaning_py(&mut self, t_err : f32) -> (Vec<u16>, Vec<f32>) {
1203    // return Vec<u16> here so that python does not 
1204    // interpret it as a byte
1205    let mut pids = Vec::<u16>::new();
1206    let (pids_rm, twindows) = self.lightspeed_cleaning(t_err);
1207    for pid in pids_rm {
1208      pids.push(pid as u16);
1209    }
1210    (pids, twindows)
1211  }
1212 
1213  /// The run id 
1214  #[getter]
1215  fn get_run_id(&self) -> u16 { 
1216    self.run_id
1217  }
1218
1219  /// Remove all hits from the event's hit series which 
1220  /// do NOT obey causality. that is where the timings
1221  /// measured at ends A and B can not be correlated
1222  /// by the assumed speed of light in the paddle
1223  #[pyo3(name="remove_non_causal_hits")]
1224  fn remove_non_causal_hits_py(&mut self) -> Vec<u16> {
1225    // return Vec<u16> here so that python does not 
1226    // interpret it as a byte
1227    let mut pids = Vec::<u16>::new();
1228    for pid in self.remove_non_causal_hits() {
1229      pids.push(pid as u16);
1230    }
1231    pids
1232  }
1233  
1234  #[getter]
1235  fn pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
1236    self.get_pointcloud()
1237  }
1238
1239  #[getter]
1240  #[pyo3(name="has_any_mangling")]
1241  fn has_any_mangling_py(&self) -> bool {
1242    self.has_any_mangling() 
1243  }
1244
1245  #[getter]
1246  fn get_event_id(&self) -> u32 {
1247    self.event_id
1248  }
1249  
1250  #[getter]
1251  fn get_event_status(&self) -> EventStatus {
1252    self.status
1253  }
1254  
1255  /// Compare the hg hits of the event with the triggered paddles and 
1256  /// return the paddles which have at least a missing HG hit
1257  #[pyo3(name="get_missing_paddles_hg")]
1258  fn get_missing_paddles_hg_py(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
1259    self.get_missing_paddles_hg(&mapping)
1260  }
1261
1262  /// Get all the paddle ids which have been triggered
1263  #[pyo3(name="get_triggered_paddles")]
1264  fn get_triggered_paddles_py(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
1265    self.get_triggered_paddles(&mapping)
1266  }
1267
1268  /// The hits we were not able to read out because the DRS4 chip
1269  /// on the RBs was busy
1270  #[getter]
1271  fn lost_hits(&self) -> u16 {
1272    self.drs_dead_lost_hits
1273  }
1274
1275  /// RB Link IDS (not RB ids) which fall into the 
1276  /// trigger window
1277  #[getter]
1278  fn rb_link_ids(&self) -> Vec<u32> {
1279    self.get_rb_link_ids().into_iter().map(|byte| byte as u32).collect()
1280  }
1281
1282  /// The event might have RBEvents associated with it
1283  #[getter]
1284  fn get_rb_events(&self) -> Vec<RBEvent> {
1285    self.rb_events.clone()
1286  }
1287
1288  /// Hits which formed a trigger
1289  #[getter]
1290  pub fn trigger_hits(&self) -> PyResult<Vec<(u8, u8, (u8, u8), LTBThreshold)>> {
1291    Ok(self.get_trigger_hits())
1292  }
1293  
1294  /// The active triggers in this event. This can be more than one, 
1295  /// if multiple trigger conditions are satisfied.
1296  #[getter]
1297  pub fn trigger_sources(&self) -> Vec<TriggerType> {
1298    self.get_trigger_sources()
1299  } 
1300
1301  #[pyo3(name="move_hits")]
1302  pub fn move_hits_py(&mut self) {
1303    self.move_hits()
1304  }
1305
1306  #[getter]
1307  #[pyo3(name="hits")]
1308  pub fn hits_py<'_py>(&self) -> Vec<TofHit> {
1309  //pub fn hits_py<'_py>(&self) -> PyResult<Bound<'_,Vec<TofHit>>> {
1310    //Bound::new(py, self.hits)
1311    //FIXMEFIXMEFIXME
1312    self.hits.clone()
1313  }
1314  
1315  #[getter]
1316  #[pyo3(name="hitmap")]
1317  pub fn hitmap<'_py>(&self) -> HashMap<u8,TofHit> {
1318  //pub fn hits_py<'_py>(&self) -> PyResult<Bound<'_,Vec<TofHit>>> {
1319    //Bound::new(py, self.hits)
1320    //FIXMEFIXMEFIXME
1321    let mut hitmap = HashMap::<u8, TofHit>::new();
1322    for h in &self.hits {
1323      hitmap.insert(h.paddle_id, *h);
1324    }
1325    hitmap
1326  }
1327  
1328  /// Total energy depostion in the Umbrella
1329  ///
1330  /// Utilizes Philip's formula based on 
1331  /// peak height
1332  #[getter]
1333  #[pyo3(name="edep_umb")]
1334  pub fn get_edep_umbrella_py(&self) -> f32 {
1335    self.get_edep_umbrella()
1336  }
1337  
1338  /// Total energy depostion in the Cube
1339  ///
1340  /// Utilizes Philip's formula based on 
1341  /// peak height
1342  #[getter]
1343  #[pyo3(name="edep_cbe")]
1344  pub fn get_edep_cube_py(&self) -> f32 {
1345    self.get_edep_cube()
1346  }
1347  
1348  /// Total energy depostion in the Cortina
1349  ///
1350  /// Utilizes Philip's formula based on 
1351  /// peak height
1352  #[getter]
1353  #[pyo3(name="edep_cor")]
1354  pub fn get_edep_cortina_py(&self) -> f32 {
1355    self.get_edep_cortina()
1356  }
1357
1358  /// Total energy depostion in the complete TOF
1359  ///
1360  /// Utilizes Philip's formula based on 
1361  /// peak height
1362  #[getter]
1363  #[pyo3(name="edep")]
1364  pub fn get_edep_py(&self) -> f32 {
1365    self.get_edep()
1366  }
1367
1368  #[getter]
1369  #[pyo3(name="nhits")]
1370  pub fn nhits_py(&self) -> usize {
1371    self.get_nhits()
1372  }
1373
1374  #[getter]
1375  #[pyo3(name="nhits_umb")]
1376  pub fn nhits_umb_py(&self) -> usize {
1377    self.get_nhits_umb()
1378  }
1379
1380  #[getter]
1381  #[pyo3(name="nhits_cbe")]
1382  fn get_nhits_cbe_py(&self) -> usize {
1383    self.get_nhits_cbe()
1384  }
1385  
1386  #[getter]
1387  #[pyo3(name="nhits_cor")]
1388  fn get_nhits_cor_py(&self) -> usize {
1389    self.get_nhits_cor()
1390  }
1391
1392  #[getter]
1393  fn get_timestamp16(&self) -> u16 {
1394    self.timestamp16
1395  }
1396  
1397  #[getter]
1398  fn get_timestamp32(&self) -> u32 {
1399    self.timestamp32
1400  }
1401  
1402  #[getter]
1403  fn timestamp48(&self) -> u64 {
1404    self.get_timestamp48()
1405  }
1406  
1407  #[getter]
1408  fn get_status(&self) -> EventStatus {
1409    self.status
1410  }
1411
1412  #[getter]
1413  #[pyo3(name="waveforms")]
1414  fn get_waveforms_py(&self) -> Vec<RBWaveform> {
1415    self.get_waveforms()
1416  }
1417
1418  #[staticmethod]
1419  #[pyo3(name = "set_packed_status_version")]
1420  fn set_packed_status_version_py(pack : &mut TofPacket, version : ProtocolVersion) 
1421    -> PyResult<()> {
1422    match Self::set_packed_status_version(pack, version) {
1423      Err(err) => {
1424        let err_mesg = format!("Unable to set status version! {}", err);
1425        return Err(PyValueError::new_err(err_mesg));
1426      } 
1427      Ok(_) => {
1428        return Ok(());
1429      }
1430    }
1431  }
1432
1433  #[staticmethod]
1434  #[pyo3(name = "strip_packed_rbevents_for_pv3")]
1435  fn strip_packed_rbevents_for_pv3_py(pack : &mut TofPacket) 
1436    -> PyResult<()> {
1437    match Self::strip_packed_rbevents_for_pv3(pack) {
1438      Err(err) => {
1439        let err_msg = format!("Unable to strip packed rbevents{}", err);
1440        return Err(PyValueError::new_err(err_msg));
1441      } 
1442      Ok(_) => {
1443        return Ok(());
1444      }
1445    }
1446  }
1447  
1448  #[cfg(feature="database")]
1449  #[staticmethod]
1450  fn unpack(pack : &TofPacket) -> PyResult<Self> {
1451    if pack.packet_type != Self::TOF_PACKET_TYPE {
1452      let err_msg = format!("This is a packet of type {}, but we need type {}", pack.packet_type, Self::TOF_PACKET_TYPE);
1453      return Err(PyValueError::new_err(err_msg));
1454    }
1455    let mut pos = 0;
1456    let mut ev = Self::from_bytestream(&pack.payload,&mut pos)?; 
1457    ev.set_paddles(&pack.tof_paddles);
1458    Ok(ev)
1459  }
1460}
1461
1462#[cfg(feature="pybindings")]
1463pythonize_packable!(TofEvent);
1464
1465//---------------------------------------------------
1466
1467#[test]
1468#[cfg(feature="random")]
1469fn packable_tofeventv0() {
1470  for _ in 0..500 {
1471    let mut data = TofEvent::from_random();
1472    if data.version != ProtocolVersion::Unknown {
1473      continue;
1474    }
1475    let mut test : TofEvent = data.pack().unpack().unwrap();
1476    //println!("{}", data.hits[0]);
1477    //println!("{}", test.hits[0]);
1478    // Manually zero these fields, since comparison with nan will fail and 
1479    // from_random did not touch these
1480    //println!("{}", data);
1481    //println!("{}", test);
1482    let fix_time = Instant::now();
1483    test.creation_time = fix_time;
1484    data.creation_time = fix_time;
1485    for k in &mut data.rb_events {
1486      k.creation_time = None;
1487    }
1488    for k in &mut test.rb_events {
1489      k.creation_time = None;
1490    }
1491    for h in &mut test.hits {
1492      h.paddle_len       = 0.0; 
1493      h.coax_cable_time  = 0.0; 
1494      h.hart_cable_time  = 0.0; 
1495      h.x                = 0.0; 
1496      h.y                = 0.0; 
1497      h.z                = 0.0; 
1498      h.event_t0         = 0.0;
1499    }
1500    assert_eq!(data, test);
1501  }
1502}  
1503
1504#[test]
1505#[cfg(feature="random")]
1506fn packable_tofeventv1() {
1507  for _ in 0..500 {
1508    let mut data = TofEvent::from_random();
1509    if data.version != ProtocolVersion::V1 {
1510      continue;
1511    }
1512    let mut test : TofEvent = data.pack().unpack().unwrap();
1513    //println!("{}", data.hits[0]);
1514    //println!("{}", test.hits[0]);
1515    // Manually zero these fields, since comparison with nan will fail and 
1516    // from_random did not touch these
1517    let fix_time = Instant::now();
1518    test.creation_time = fix_time;
1519    data.creation_time = fix_time;
1520    for k in &mut data.rb_events {
1521      k.creation_time = None;
1522    }
1523    for k in &mut test.rb_events {
1524      k.creation_time = None;
1525    }
1526    for h in &mut test.hits {
1527      h.paddle_len       = 0.0; 
1528      h.coax_cable_time  = 0.0; 
1529      h.hart_cable_time  = 0.0; 
1530      h.x                = 0.0; 
1531      h.y                = 0.0; 
1532      h.z                = 0.0; 
1533      h.event_t0         = 0.0;
1534    }
1535    assert_eq!(data, test);
1536  }
1537}  
1538
1539#[test]
1540#[cfg(feature="random")]
1541fn packable_tofeventv2() {
1542  for _ in 0..500 {
1543    let mut data = TofEvent::from_random();
1544    if data.version != ProtocolVersion::V2 {
1545      continue;
1546    }
1547    let mut test : TofEvent = data.pack().unpack().unwrap();
1548    //println!("{}", data.hits[0]);
1549    //println!("{}", test.hits[0]);
1550    // Manually zero these fields, since comparison with nan will fail and 
1551    // from_random did not touch these
1552    let fix_time = Instant::now();
1553    test.creation_time = fix_time;
1554    data.creation_time = fix_time;
1555    for k in &mut data.rb_events {
1556      k.creation_time = None;
1557    }
1558    for k in &mut test.rb_events {
1559      k.creation_time = None;
1560    }
1561    for h in &mut test.hits {
1562      h.paddle_len       = 0.0; 
1563      h.coax_cable_time  = 0.0; 
1564      h.hart_cable_time  = 0.0; 
1565      h.x                = 0.0; 
1566      h.y                = 0.0; 
1567      h.z                = 0.0; 
1568      h.event_t0         = 0.0;
1569    }
1570    assert_eq!(data, test);
1571  }
1572}  
1573
1574#[test]
1575#[cfg(feature="random")]
1576fn packable_tofeventv3() {
1577  for _ in 0..500 {
1578    let mut data = TofEvent::from_random();
1579    if data.version != ProtocolVersion::V3 {
1580      continue;
1581    }
1582    let mut test : TofEvent = data.pack().unpack().unwrap();
1583    //println!("{}", data.hits[0]);
1584    //println!("{}", test.hits[0]);
1585    // Manually zero these fields, since comparison with nan will fail and 
1586    // from_random did not touch these
1587    let fix_time = Instant::now();
1588    test.creation_time = fix_time;
1589    data.creation_time = fix_time;
1590    for h in &mut test.hits {
1591      h.paddle_len       = 0.0; 
1592      h.coax_cable_time  = 0.0; 
1593      h.hart_cable_time  = 0.0; 
1594      h.x                = 0.0; 
1595      h.y                = 0.0; 
1596      h.z                = 0.0; 
1597      h.event_t0         = 0.0;
1598    }
1599    assert_eq!(data, test);
1600  }
1601}  
1602
1603#[test]
1604#[cfg(feature="random")]
1605fn tofevent_move_hits() {
1606  let mut event = TofEvent::from_random();
1607  let mut n_hits_exp = 0usize;
1608  for rb in &event.rb_events {
1609    n_hits_exp += rb.hits.len();
1610  }
1611  event.hits.clear();
1612  event.move_hits();
1613  for rb in &event.rb_events {
1614    assert_eq!(rb.hits.len(),0);
1615  }
1616  assert_eq!(n_hits_exp, event.hits.len());
1617
1618}
1619
1620#[test]
1621#[cfg(feature="random")] 
1622fn tofevent_striprbevents() {
1623  let mut event      = TofEvent::from_random();
1624  let mut n_hits_exp = 0usize;
1625  for rb in &event.rb_events {
1626    n_hits_exp += rb.hits.len();
1627  }
1628  event.hits.clear();
1629  event.strip_rbevents();
1630  assert_eq!(event.rb_events.len(),0);
1631  assert_eq!(n_hits_exp, event.hits.len());
1632}
1633
1634//#[test]
1635//#[cfg(feature = "random")]
1636//fn tofevent_sizes_header() {
1637//  for _ in 0..100 {
1638//    let data = TofEvent::from_random();
1639//    let mask = data.construct_sizes_header();
1640//    let size = TofEvent::decode_size_header(&mask);
1641//    assert_eq!(size.0, data.rb_events.len());
1642//    //assert_eq!(size.1, data.missing_hits.len());
1643//  }
1644//}
1645
1646//#[test]
1647//#[cfg(feature = "random")]
1648//fn packable_tofevent() {
1649//  for _ in 0..5 {
1650//    let data = TofEvent::from_random();
1651//    let test : TofEvent = data.pack().unpack().unwrap();
1652//    assert_eq!(data.header, test.header);
1653//    assert_eq!(data.compression_level, test.compression_level);
1654//    assert_eq!(data.quality, test.quality);
1655//    assert_eq!(data.mt_event, test.mt_event);
1656//    assert_eq!(data.rb_events.len(), test.rb_events.len());
1657//    //assert_eq!(data.missing_hits.len(), test.missing_hits.len());
1658//    //assert_eq!(data.missing_hits, test.missing_hits);
1659//    assert_eq!(data.rb_events, test.rb_events);
1660//    //assert_eq!(data, test);
1661//    //println!("{}", data);
1662//  }
1663//}
1664
1665
1666