gondola_core/events/
tof_event.rs

1//! Basic event structure for all TOF systems
2// The following 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///
29#[derive(Debug, Clone, PartialEq)]
30#[cfg_attr(feature="pybindings", pyclass)]
31pub struct TofEvent {
32  pub status            : EventStatus,
33  /// The version of this event. Prior to tof-dataclasses v0.11,
34  /// the default was `ProtocolVersion::Unknown`. 
35  /// Then for the Antarctic campaign we added more variables, 
36  /// so that the gcu doesn't have to compute them. These variables
37  /// will be included in the bytestream for `ProtocolVersion::V1`.
38  /// However, we don't want to write them to disk in the new (gondola-core > v0.11)
39  /// verion, so we will use ProtocolVersionV2 for the version to be
40  /// written to disk. ProtocolVersionV2 can (but not must) have RBEvents
41  pub version           : ProtocolVersion,
42  pub quality           : EventQuality,
43  pub trigger_sources   : u16,
44
45  /// the number of triggered paddles coming
46  /// from the MTB directly. This might NOT be
47  /// the same as the number of hits!
48  pub n_trigger_paddles  : u8,
49  pub event_id           : u32,
50  pub run_id             : u16,
51  pub timestamp32        : u32,
52  pub timestamp16        : u16,
53  /// scalar number of hits missed in
54  /// this event due to DRS on the RB
55  /// being busy
56  pub drs_dead_lost_hits : u16, 
57  pub dsi_j_mask         : u32,
58  pub channel_mask       : Vec<u16>,
59  pub mtb_link_mask      : u64,
60  pub hits               : Vec<TofHit>,
61  // a number of variables which are directly 
62  // read out from the MTB packet and won't get 
63  // serialized in this form but then used
64  // later to calculate other variables
65  pub mt_trigger_sources : u16,
66  pub mt_tiu_gps16       : u16,
67  pub mt_tiu_gps32       : u32, 
68  pub mt_timestamp       : u32,
69  pub mt_tiu_timestamp   : u32,
70  // a bunch of calculated variablels, used 
71  // for online interesting event search
72  // these will be only available in ProtocolVersion 1
73  pub n_hits_umb         : u8,
74  pub n_hits_cbe         : u8,
75  pub n_hits_cor         : u8,
76  pub tot_edep_umb       : f32,
77  pub tot_edep_cbe       : f32,
78  pub tot_edep_cor       : f32,
79  pub paddles_set        : bool,
80  // this is new (v0.11) -> We are expanding TofEvent(Summary) 
81  // so that it is the same as TofEvent in v0.10 and is able 
82  // to carry waveforms. These then can be stripped off
83  pub rb_events          : Vec<RBEvent>,
84  /// Start time for a time to wait for incoming RBEvents
85  pub creation_time     : Instant,
86  pub write_to_disk     : bool, 
87}
88
89impl TofEvent {
90
91  pub fn new() -> Self {
92    Self {
93      status             : EventStatus::Unknown,
94      version            : ProtocolVersion::Unknown,
95      n_hits_umb         : 0,
96      n_hits_cbe         : 0,
97      n_hits_cor         : 0,
98      tot_edep_umb       : 0.0,
99      tot_edep_cbe       : 0.0,
100      tot_edep_cor       : 0.0,
101      quality            : EventQuality::Unknown,
102      trigger_sources    : 0,
103      n_trigger_paddles  : 0,
104      event_id           : 0,
105      run_id             : 0,
106      timestamp32        : 0,
107      timestamp16        : 0,
108      drs_dead_lost_hits : 0,
109      dsi_j_mask         : 0,
110      channel_mask       : Vec::<u16>::new(),
111      mtb_link_mask      : 0,
112      hits               : Vec::<TofHit>::new(),
113      mt_trigger_sources : 0,
114      mt_tiu_gps16       : 0,
115      mt_tiu_gps32       : 0, 
116      mt_timestamp       : 0,
117      mt_tiu_timestamp   : 0,
118      paddles_set        : false,
119      rb_events          : Vec::<RBEvent>::new(),
120      creation_time      : Instant::now(),
121      write_to_disk      : true,
122    }
123  }
124 
125  /// Remove any RBEvents from the event 
126  pub fn strip_rbevents(&mut self) {
127    self.rb_events.clear();
128  }
129  
130  pub fn age(&self) -> u64 {
131    self.creation_time.elapsed().as_secs()
132  }
133  
134  /// Simple check if the event contains as much RBEvents 
135  /// as expected from the provided boards masks by the MTB
136  pub fn is_complete(&self) -> bool {
137    self.get_rb_link_ids().len() == self.rb_events.len()
138  }
139  
140  /// The number of hits we did not get 
141  /// becaue of the DRS busy
142  pub fn get_lost_hits(&self) -> u16 {
143    let mut lost_hits = 0u16;
144    for rbev in &self.rb_events {
145      if rbev.header.drs_lost_trigger() {
146        let mut nhits = rbev.header.get_nchan() as u16;
147        if nhits > 0 {
148          nhits -= 1;
149        }
150        lost_hits += nhits;
151      }
152    }
153    lost_hits
154  }
155
156
157  /// Calculate extra variables for the GCU, 
158  /// set the protocol version to V1 and 
159  /// strip the waveforms if desired
160  /// 
161  /// # Arguments:
162  ///   * strip_rbevents : remove the rbevents from the TofEvent so they
163  ///                      won't bother the poor gcu
164  pub fn prepare_for_gcu(&mut self, strip_rbevents : bool) {
165    if strip_rbevents {
166      self.strip_rbevents();
167    }
168    self.version = ProtocolVersion::V1;
169    for h in &self.hits {
170      if h.paddle_id <= 60 {
171        self.n_hits_cbe += 1;
172        self.tot_edep_cbe += h.get_edep();
173      }
174      else if h.paddle_id <= 108 && h.paddle_id > 60 {
175        self.n_hits_umb += 1;
176        self.tot_edep_umb += h.get_edep();
177      }
178      else {
179        self.n_hits_cor += 1;
180        self.tot_edep_cor += h.get_edep();
181      }
182    }
183  }
184  
185  /// Ensure compatibility with older data, which 
186  /// contained a different type of TofEvent
187  pub fn decode_depr_tofevent_size_header(mask : &u32) 
188    -> (usize, usize) {
189    let rb_event_len = (mask & 0xFF)        as usize;
190    let miss_len     = ((mask & 0xFF00)     >> 8)  as usize;
191    (rb_event_len, miss_len)
192  }
193
194  /// Set timing offsets to the event's hits
195  ///
196  /// # Arguments:
197  ///   * offsets : a hashmap paddle id -> timing offset
198  #[cfg(feature="database")]
199  pub fn set_timing_offsets(&mut self, offsets : &HashMap<u8, f32>) {
200    for h in self.hits.iter_mut() {
201      if offsets.contains_key(&h.paddle_id) {
202        h.timing_offset = offsets[&h.paddle_id]; 
203      }
204    }
205  }
206
207  /// Remove hits from the hitseries which can not 
208  /// be caused by the same particle, which means 
209  /// that for these two specific hits beta with 
210  /// respect to the first hit in the event is 
211  /// larger than one
212  /// That this works, first hits need to be 
213  /// "normalized" by calling normalize_hit_times
214  ///
215  /// # Return:
216  ///
217  ///   * removed paddle ids, twindows
218  pub fn lightspeed_cleaning(&mut self, t_err : f32) -> (Vec<u8>, Vec<f32>) {
219    // first sort the hits in time
220    if self.hits.len() == 0 {
221      return (Vec::<u8>::new(), Vec::<f32>::new());
222    }
223    let mut twindows = Vec::<f32>::new();
224    self.hits.sort_by(|a,b| (a.event_t0).partial_cmp(&b.event_t0).unwrap_or(Ordering::Greater));
225    let first_hit = self.hits[0].clone(); // the clone here is a bit unfortunate, 
226                                           // this can be done better with walking 
227                                           // over the list and updating references
228    let mut clean_hits = Vec::<TofHit>::new(); 
229    let mut rm_hits    = Vec::<u8>::new();
230    // per definition, we can't remove the first hit, ever
231    clean_hits.push(first_hit.clone());
232    //error!("-----");
233    let mut prior_hit = first_hit;
234    //println!("TO FIRST {}",first_hit.event_t0);
235    for h in self.hits.iter().skip(1) {
236      let min_tdiff_cvac = 1e9*1e-3*prior_hit.distance(h)/299792458.0;
237      let twindow            = prior_hit.event_t0 + min_tdiff_cvac;
238
239      // FIXME - implement different strategies
240      // this is the "default" strategy
241      //if h.event_t0 < twindow {
242      //  rm_hits.push(h.paddle_id);
243      //  twindows.push(twindow);
244      //  continue;
245      //}
246      // this is the "lenient" strategy
247      if h.event_t0 + 2.0*t_err < twindow {
248        rm_hits.push(h.paddle_id);
249        twindows.push(twindow);
250        continue;
251      }
252      // this is the "aggressive" strategy
253      //if h.event_t0 - 2.0*t_err < twindow {
254      //  rm_hits.push(h.paddle_id);
255      //  continue;
256      //}
257      
258      // should we remove negative beta hits?
259      //if beta < 0.0 {
260      //  rm_hits.push(h.paddle_id);
261      //  continue;
262      //}
263      // update the prior hit only 
264      // when it is a good one. If it 
265      // is bad we continue to compare
266      // to the first hit
267      prior_hit = h.clone();
268      clean_hits.push(*h);
269    }
270    self.hits = clean_hits;
271    (rm_hits, twindows)
272  }
273
274
275  /// Non causal hits have hit times in ends A and be which 
276  /// are not compatible with the speed of light in the paddle, 
277  /// that is, the hit gets registered too early. If we look 
278  /// at a plot of the reconstructed position, these hits would 
279  /// correspond to positions outside of the paddle.
280  ///
281  /// #Returns:
282  ///   A vector of paddle ids with removed hits
283  ///
284  pub fn remove_non_causal_hits(&mut self) -> Vec<u8> {
285    let mut clean_hits = Vec::<TofHit>::new();
286    let mut removed_pids = Vec::<u8>::new();
287    for h in &self.hits {
288      if h.obeys_causality() {
289        clean_hits.push(*h);
290      } else {
291        removed_pids.push(h.paddle_id);
292      }
293    }
294    self.hits = clean_hits;
295    removed_pids
296  }
297  
298  #[cfg(feature="database")]
299  pub fn normalize_hit_times(&mut self) {
300    if self.hits.len() == 0 {
301      return;
302    }
303    // check if hit times have already been normalized
304    if self.hits[0].event_t0 == 0.0 {
305      return;
306    }
307
308    let phase0 = self.hits[0].phase.to_f32();
309    for h in &mut self.hits {
310      let t0 = h.get_t0_uncorrected() + h.get_cable_delay();
311      let mut phase_diff = h.phase.to_f32() - phase0;
312      while phase_diff < - PI/2.0 {
313        phase_diff += 2.0*PI;
314      }
315      while phase_diff > PI/2.0 {
316        phase_diff -= 2.0*PI;
317      }
318      let t_shift = 50.0*phase_diff/(2.0*PI);
319      h.event_t0 = t0 + t_shift;
320    }
321    // start the first hit at 0
322    self.hits.sort_by(|a,b| (a.event_t0).partial_cmp(&b.event_t0).unwrap_or(Ordering::Greater));
323    let t0_first_hit = self.hits[0].event_t0;
324    for h in self.hits.iter_mut() {
325      h.event_t0 -= t0_first_hit
326    }
327  }
328 
329  #[cfg(feature="database")]
330  pub fn set_paddles(&mut self, paddles : &HashMap<u8, TofPaddle>) {
331    let mut nerror = 0u8;
332    for h in &mut self.hits {
333      match paddles.get(&h.paddle_id) {
334        None => {
335          error!("Got paddle id {} which is not in given map!", h.paddle_id);
336          nerror += 1;
337          continue;
338        }
339        Some(pdl) => {
340          h.set_paddle(pdl);
341        }
342      }
343    }
344    if nerror == 0 {
345      self.paddles_set = true;
346    }
347  }
348
349  /// Get the pointcloud of this event, sorted by time
350  /// 
351  /// # Returns
352  ///   (f32, f32, f32, f32, f32) : (x,y,z,t,edep)
353  pub fn get_pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
354    let mut pc = Vec::<(f32,f32,f32,f32,f32)>::new();
355    if !self.paddles_set {
356      error!("Before getting the pointcloud, paddle information needs to be set for this event. Call TofEvent;:set_paddle");
357      return None;
358    }
359    for h in &self.hits {
360      let result = (h.x, h.y, h.z, h.get_t0(), h.get_edep());
361      pc.push(result);
362    }
363    Some(pc)
364  }
365
366  /// Compare the MasterTriggerEvent::trigger_hits with 
367  /// the actual hits to determine from which paddles
368  /// we should have received HG hits (from waveforms)
369  /// but we did not get them
370  ///
371  /// WARNING: The current implementation of this is 
372  /// rather slow and not fit for production use
373  /// FIXME - rewrite as a closure
374  #[cfg(feature="database")]
375  pub fn get_missing_paddles_hg(&self, pid_map :   &DsiJChPidMapping) -> Vec<u8> {
376    let mut missing = Vec::<u8>::new();
377    for th in self.get_trigger_hits() {
378      let pid = pid_map.get(&th.0).unwrap().get(&th.1).unwrap().get(&th.2.0).unwrap().0;
379      let mut found = false;
380      for h in &self.hits {
381        if h.paddle_id == pid {
382          found = true;
383          break
384        }
385      }
386      if !found {
387        missing.push(pid);
388      }
389    }
390    missing
391  }
392  
393  /// Get the triggered paddle ids
394  ///
395  /// Warning, this might be a bit slow
396  #[cfg(feature="database")]
397  pub fn get_triggered_paddles(&self, pid_map :   &DsiJChPidMapping) -> Vec<u8> {
398    let mut paddles = Vec::<u8>::with_capacity(3);
399    for th in &self.get_trigger_hits() {
400      let pid = pid_map.get(&th.0).unwrap().get(&th.1).unwrap().get(&th.2.0).unwrap().0;
401      paddles.push(pid);
402    }
403    paddles
404  }
405
406  /// Get the RB link IDs according to the mask
407  pub fn get_rb_link_ids(&self) -> Vec<u8> {
408    let mut links = Vec::<u8>::new();
409    for k in 0..64 {
410      if (self.mtb_link_mask >> k) as u64 & 0x1 == 1 {
411        links.push(k as u8);
412      }
413    }
414    links
415  }
416
417  /// Get the combination of triggered DSI/J/CH on 
418  /// the MTB which formed the trigger. This does 
419  /// not include further hits which fall into the 
420  /// integration window. For those, se rb_link_mask
421  ///
422  /// The returned values follow the TOF convention
423  /// to start with 1, so that we can use them to 
424  /// look up LTB ids in the db.
425  ///
426  /// # Returns
427  ///
428  ///   Vec<(hit)> where hit is (DSI, J, CH) 
429  pub fn get_trigger_hits(&self) -> Vec<(u8, u8, (u8, u8), LTBThreshold)> {
430    let mut hits = Vec::<(u8,u8,(u8,u8),LTBThreshold)>::with_capacity(5); 
431    //let n_masks_needed = self.dsi_j_mask.count_ones() / 2 + self.dsi_j_mask.count_ones() % 2;
432    let n_masks_needed = self.dsi_j_mask.count_ones();
433    if self.channel_mask.len() < n_masks_needed as usize {
434      error!("We need {} hit masks, but only have {}! This is bad!", n_masks_needed, self.channel_mask.len());
435      return hits;
436    }
437    let mut n_mask = 0;
438    trace!("Expecting {} hit masks", n_masks_needed);
439    trace!("ltb channels {:?}", self.dsi_j_mask);
440    trace!("hit masks {:?}", self.channel_mask); 
441    //println!("We see LTB Channels {:?} with Hit masks {:?} for {} masks requested by us!", self.dsi_j_mask, self.channel_mask, n_masks_needed);
442    
443    // one k here is for one ltb
444    for k in 0..32 {
445      if (self.dsi_j_mask >> k) as u32 & 0x1 == 1 {
446        let mut dsi = 0u8;
447        let mut j   = 0u8;
448        if k < 5 {
449          dsi = 1;
450          j   = k as u8 + 1;
451        } else if k < 10 {
452          dsi = 2;
453          j   = k as u8 - 5 + 1;
454        } else if k < 15 {
455          dsi = 3;
456          j   = k as u8- 10 + 1;
457        } else if k < 20 {
458          dsi = 4;
459          j   = k as u8- 15 + 1;
460        } else if k < 25 {
461          dsi = 5;
462          j   = k as u8 - 20 + 1;
463        } 
464        //let dsi = (k as f32 / 4.0).floor() as u8 + 1;       
465        //let j   = (k % 5) as u8 + 1;
466        //println!("n_mask {n_mask}");
467        let channels = self.channel_mask[n_mask]; 
468        for (i,ch) in LTB_CHANNELS.iter().enumerate() {
469          //let chn = *ch as u8 + 1;
470          let ph_chn = PHYSICAL_CHANNELS[i];
471          //let chn = i as u8 + 1;
472          //println!("i,ch {}, {}", i, ch);
473          let thresh_bits = ((channels & ch) >> (i*2)) as u8;
474          //println!("thresh_bits {}", thresh_bits);
475          if thresh_bits > 0 { // hit over threshold
476            hits.push((dsi, j, ph_chn, LTBThreshold::from(thresh_bits)));
477          }
478        }
479        n_mask += 1;
480      } // next ltb
481    }
482    hits
483  }
484  
485  /// Get the trigger sources from trigger source byte
486  pub fn get_trigger_sources(&self) -> Vec<TriggerType> {
487    TriggerType::transcode_trigger_sources(self.trigger_sources)
488  }
489  
490  pub fn get_timestamp48(&self) -> u64 {
491    ((self.timestamp16 as u64) << 32) | self.timestamp32 as u64
492  }
493  
494  /// Ttotal energy depostion in the TOF - Umbrella
495  ///
496  /// Utilizes Philip's formula based on 
497  /// peak height
498  pub fn get_edep_umbrella(&self) -> f32 {
499    let mut tot_edep = 0.0f32;
500    for h in &self.hits {
501      if h.paddle_id < 61 || h.paddle_id > 108 {
502        continue;
503      }
504      tot_edep += h.get_edep();
505    }
506    tot_edep
507  }
508  
509  /// Ttotal energy depostion in the TOF - Umbrella
510  ///
511  /// Utilizes Philip's formula based on 
512  /// peak height
513  pub fn get_edep_cube(&self) -> f32 {
514    let mut tot_edep = 0.0f32;
515    for h in &self.hits {
516      if h.paddle_id > 60 {
517        continue;
518      }
519      tot_edep += h.get_edep();
520    }
521    tot_edep
522  }
523  
524  /// Ttotal energy depostion in the Cortina
525  ///
526  /// Utilizes Philip's formula based on 
527  /// peak height
528  pub fn get_edep_cortina(&self) -> f32 {
529    let mut tot_edep = 0.0f32;
530    for h in &self.hits {
531      if h.paddle_id < 109 {
532        continue;
533      }
534      tot_edep += h.get_edep();
535    }
536    tot_edep
537  }
538  
539  /// Ttotal energy depostion in the complete TOF
540  ///
541  /// Utilizes Philip's formula based on 
542  /// peak height
543  pub fn get_edep(&self) -> f32 {
544    let mut tot_edep = 0.0f32;
545    for h in &self.hits {
546      tot_edep += h.get_edep();
547    }
548    tot_edep
549  }
550
551  pub fn get_nhits_umb(&self) -> usize {
552    let mut nhit = 0;
553    for h in &self.hits {
554      if h.paddle_id > 60 && h.paddle_id < 109 {
555        nhit += 1;
556      }
557    }
558    nhit
559  }
560  
561  pub fn get_nhits_cbe(&self) -> usize {
562    let mut nhit = 0;
563    for h in &self.hits {
564      if h.paddle_id < 61  {
565        nhit += 1;
566      }
567    }
568    nhit
569  }
570  
571  pub fn get_nhits_cor(&self) -> usize {
572    let mut nhit = 0;
573    for h in &self.hits {
574      if h.paddle_id > 108  {
575        nhit += 1;
576      }
577    }
578    nhit
579  }
580
581  pub fn get_nhits(&self) -> usize {
582    self.hits.len()
583  }
584  
585  /// Check if th eassociated RBEvents have any of their
586  /// mangling stati set
587  pub fn has_any_mangling(&self) -> bool {
588    for rbev in &self.rb_events {
589      if rbev.status == EventStatus::CellAndChnSyncErrors 
590      || rbev.status == EventStatus::CellSyncErrors 
591      || rbev.status == EventStatus::ChnSyncErrors {
592        return true;
593      }
594    }
595    false
596  }
597  
598  /// Get all waveforms of all RBEvents in this event
599  /// ISSUE - Performance, Memory
600  /// FIXME - reimplement this things where this
601  ///         returns only a reference
602  pub fn get_waveforms(&self) -> Vec<RBWaveform> {
603    let mut wfs = Vec::<RBWaveform>::new();
604    for ev in &self.rb_events {
605      for wf in &ev.get_rbwaveforms() {
606        wfs.push(wf.clone());
607      }
608    }
609    wfs
610  }
611}
612
613impl TofPackable for TofEvent {
614  // v0.11 TofPacketType::TofEventSummary -> TofPacketType::TofEvent
615  const TOF_PACKET_TYPE        : TofPacketType = TofPacketType::TofEvent;
616  const TOF_PACKET_TYPE_ALT    : TofPacketType = TofPacketType::TofEventDeprecated;
617}
618
619impl Serialization for TofEvent {
620  
621  const HEAD               : u16   = 43690; //0xAAAA
622  const TAIL               : u16   = 21845; //0x5555
623  
624  fn to_bytestream(&self) -> Vec<u8> {
625    let mut stream = Vec::<u8>::new();
626    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
627    let status_version = self.status as u8 | self.version.to_u8();
628    stream.push(status_version);
629    stream.extend_from_slice(&self.trigger_sources.to_le_bytes());
630    stream.extend_from_slice(&self.n_trigger_paddles.to_le_bytes());
631    stream.extend_from_slice(&self.event_id.to_le_bytes());
632    // depending on the version, we send the fc event packet
633    if self.version == ProtocolVersion::V1 {
634      stream.extend_from_slice(&self.n_hits_umb  .to_le_bytes()); 
635      stream.extend_from_slice(&self.n_hits_cbe  .to_le_bytes()); 
636      stream.extend_from_slice(&self.n_hits_cor  .to_le_bytes()); 
637      stream.extend_from_slice(&self.tot_edep_umb.to_le_bytes()); 
638      stream.extend_from_slice(&self.tot_edep_cbe.to_le_bytes()); 
639      stream.extend_from_slice(&self.tot_edep_cor.to_le_bytes()); 
640    }
641    stream.extend_from_slice(&(self.quality as u8).to_le_bytes());
642    stream.extend_from_slice(&self.timestamp32.to_le_bytes());
643    stream.extend_from_slice(&self.timestamp16.to_le_bytes());
644    stream.extend_from_slice(&self.run_id.to_le_bytes());
645    stream.extend_from_slice(&self.drs_dead_lost_hits.to_le_bytes());
646    stream.extend_from_slice(&self.dsi_j_mask.to_le_bytes());
647    let n_channel_masks = self.channel_mask.len();
648    stream.push(n_channel_masks as u8);
649    for k in 0..n_channel_masks {
650      stream.extend_from_slice(&self.channel_mask[k].to_le_bytes());
651    }
652    stream.extend_from_slice(&self.mtb_link_mask.to_le_bytes());
653    let nhits = self.hits.len() as u16;
654    stream.extend_from_slice(&nhits.to_le_bytes());
655    for k in 0..self.hits.len() {
656      stream.extend_from_slice(&self.hits[k].to_bytestream());
657    }
658    // for the new (>=v0.11) event, we will always write 
659    // the rb events
660    if self.version == ProtocolVersion::V2 {
661      stream.push(self.rb_events.len() as u8);
662      for rbev in &self.rb_events {
663        stream.extend_from_slice(&rbev.to_bytestream());
664      }
665    }
666    stream.extend_from_slice(&Self::TAIL.to_le_bytes());
667    stream
668  }
669  
670  fn from_bytestream(stream    : &Vec<u8>, 
671                     pos       : &mut usize) 
672    -> Result<Self, SerializationError>{
673    let mut event           = Self::new();
674    let head = parse_u16(stream, pos);
675    if head != Self::HEAD {
676      error!("Decoding of HEAD failed! Got {} instead!", head);
677      return Err(SerializationError::HeadInvalid);
678    }
679    let status_version_u8     = parse_u8(stream, pos);
680    let status                = EventStatus::from(status_version_u8 & 0x3f);
681    let version               = ProtocolVersion::from(status_version_u8 & 0xc0); 
682    event.status            = status;
683    event.version           = version;
684    event.trigger_sources   = parse_u16(stream, pos);
685    event.n_trigger_paddles = parse_u8(stream, pos);
686    event.event_id          = parse_u32(stream, pos);
687    if event.version == ProtocolVersion::V1 {
688      event.n_hits_umb      = parse_u8(stream, pos); 
689      event.n_hits_cbe      = parse_u8(stream, pos); 
690      event.n_hits_cor      = parse_u8(stream, pos); 
691      event.tot_edep_umb    = parse_f32(stream, pos); 
692      event.tot_edep_cbe    = parse_f32(stream, pos); 
693      event.tot_edep_cor    = parse_f32(stream, pos); 
694    }
695    event.quality            = EventQuality::from(parse_u8(stream, pos));
696    event.timestamp32        = parse_u32(stream, pos);
697    event.timestamp16        = parse_u16(stream, pos);
698    event.run_id             = parse_u16(stream, pos);
699    event.drs_dead_lost_hits = parse_u16(stream, pos);
700    event.dsi_j_mask         = parse_u32(stream, pos);
701    let n_channel_masks        = parse_u8(stream, pos);
702    for _ in 0..n_channel_masks {
703      event.channel_mask.push(parse_u16(stream, pos));
704    }
705    event.mtb_link_mask     = parse_u64(stream, pos);
706    let nhits                 = parse_u16(stream, pos);
707    for _ in 0..nhits {
708      event.hits.push(TofHit::from_bytestream(stream, pos)?);
709    }
710    if event.version == ProtocolVersion::V2 {
711      // for this version, we can have rb events 
712      let n_rb_events = parse_u8(stream, pos);
713      if n_rb_events > 0 {
714        for _ in 0..n_rb_events {
715          event.rb_events.push(RBEvent::from_bytestream(stream, pos)?);
716        }
717      }
718    }
719
720    let tail = parse_u16(stream, pos);
721    if tail != Self::TAIL {
722      error!("Decoding of TAIL failed for version {}! Got {} instead!", version, tail);
723      return Err(SerializationError::TailInvalid);
724    }
725    Ok(event)
726  }
727  
728  /// Allows to get TofEvent from a packet 
729  /// of the deprecate packet type TofEventDeprecated.
730  /// This packet type was formerly known as TofEvent
731  /// This will dismiss all the 
732  /// waveforms and RBEvents
733  fn from_bytestream_alt(stream    : &Vec<u8>, 
734                         pos       : &mut usize) 
735    -> Result<Self, SerializationError> {
736    let head    = parse_u16(stream, pos);
737    if head != TofEvent::HEAD {
738      return Err(SerializationError::HeadInvalid);
739    }
740    let mut te            = Self::new();
741    // the compression level will always be 0 for old data
742    let _compression_level = parse_u8(stream, pos);
743
744    te.quality            = EventQuality::from(parse_u8(stream, pos));
745    // at this position is the serialized TofEventHeader. We don't have that anymore (>v0.11). 
746    // However, the only information we need from it is the run id, the other fields are anyway
747    // empty
748    *pos += 2; // for TofEventHeader::HEAD
749    // FIXME - potentially dangerous for u16 overflow!
750    te.run_id             = parse_u32(stream, pos) as u16;
751    *pos += 43 - 6;// rest of TofEventHeader 
752    //let header         = TofEventHeader::from_bytestream(stream, &mut pos)?;
753    // now parse the "old" MasterTriggerEvent
754    *pos += 2; // MasterTriggerEvent::HEAD
755    let event_status   = parse_u8 (stream, pos);
756    te.status              = EventStatus::from(event_status);
757    if te.has_any_mangling() {
758      te.status = EventStatus::AnyDataMangling;
759    }
760    te.event_id           = parse_u32(stream, pos);
761    let mtb_timestamp      = parse_u32(stream, pos);
762    let tiu_timestamp      = parse_u32(stream, pos);
763    let tiu_gps32          = parse_u32(stream, pos);
764    let _tiu_gps16          = parse_u16(stream,pos);
765    let _crc           = parse_u32(stream, pos);
766    let mt_timestamp  = (mt_event_get_timestamp_abs48(mtb_timestamp, tiu_gps32, tiu_timestamp ) as f64/1000.0).floor()  as u64; 
767    te.timestamp32         = (mt_timestamp  & 0x00000000ffffffff ) as u32;
768    te.timestamp16         = ((mt_timestamp & 0x0000ffff00000000 ) >> 32) as u16;
769    te.trigger_sources     = parse_u16(stream, pos);
770    te.dsi_j_mask          = parse_u32(stream, pos);
771    let n_channel_masks   = parse_u8(stream, pos);
772    for _ in 0..n_channel_masks {
773      te.channel_mask.push(parse_u16(stream, pos));
774    }
775
776    te.mtb_link_mask      = parse_u64(stream, pos);
777    let mt_event_tail     = parse_u16(stream, pos);
778    if mt_event_tail != Self::TAIL {
779      // (tail for mt event was the same)
780      error!("Parsed TAIL from MT event is incorrect! Got {} instead of {} at pos {}", mt_event_tail, Self::TAIL, pos);
781    }
782    //let mt_event      = MasterTriggerEvent::from_bytestream(stream, &mut pos)?;
783    let v_sizes           = Self::decode_depr_tofevent_size_header(&parse_u32(stream, pos));
784    //println!("{:?}", v_sizes);
785    for _ in 0..v_sizes.0 {
786      // we are getting all waveforms for now, but we can 
787      // discard them later
788      let next_rb_event = RBEvent::from_bytestream(stream, pos)?;
789      //println!("{}", next_rb_event);
790      te.rb_events.push(next_rb_event);
791    }
792    let tail = parse_u16(stream, pos);
793    if tail != Self::TAIL {
794      error!("Decoding of TAIL failed! Got {} instead!", tail);
795      return Err(SerializationError::TailInvalid);
796    }
797    //println!("{}",te);
798    
799    // FIXME - this is slow, use Arc<> instead. However, then make 
800    // sure to copy them in case we get rid of the rb evvnts 
801    // (or does Arc take care of it) 
802    for rbev in &te.rb_events {
803      for h in &rbev.hits {
804        te.hits.push(*h);
805      }
806    }
807    return Ok(te);
808  }
809}
810    
811impl Default for TofEvent {
812  fn default() -> Self {
813    Self::new()
814  }
815}
816
817impl fmt::Display for TofEvent {
818  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
819    let mut repr = format!("<TofEvent (version {})", self.version);
820    repr += &(format!("\n  EventID          : {}", self.event_id));
821    repr += &(format!("\n  RunID            : {}", self.run_id));
822    repr += &(format!("\n  EventStatus      : {}", self.status));
823    repr += &(format!("\n  TriggerSources   : {:?}", self.get_trigger_sources()));
824    repr += &(format!("\n  NTrigPaddles     : {}", self.n_trigger_paddles));
825    repr += &(format!("\n  DRS dead hits    : {}", self.drs_dead_lost_hits));
826    repr += &(format!("\n  timestamp32      : {}", self.timestamp32)); 
827    repr += &(format!("\n  timestamp16      : {}", self.timestamp16)); 
828    repr += &(format!("\n   |-> timestamp48 : {}", self.get_timestamp48())); 
829    //repr += &(format!("\n  PrimaryBeta      : {}", self.get_beta())); 
830    //repr += &(format!("\n  PrimaryCharge    : {}", self.primary_charge));
831    repr += &(format!("\n  ** ** TRIGGER HITS (DSI/J/CH) [{} LTBS] ** **", self.dsi_j_mask.count_ones()));
832    for k in self.get_trigger_hits() {
833      repr += &(format!("\n  => {}/{}/({},{}) ({}) ", k.0, k.1, k.2.0, k.2.1, k.3));
834    }
835    repr += "\n  ** ** MTB LINK IDs ** **";
836    let mut mtblink_str = String::from("\n  => ");
837    for k in self.get_rb_link_ids() {
838      mtblink_str += &(format!("{} ", k))
839    }
840    repr += &mtblink_str;
841    repr += &(format!("\n  == Trigger hits {}, expected RBEvents {}",
842            self.get_trigger_hits().len(),
843            self.get_rb_link_ids().len()));
844    repr += &String::from("\n  ** ** ** HITS ** ** **");
845    for h in &self.hits {
846      repr += &(format!("\n  {}", h));
847    }
848    if self.rb_events.len() > 0 {
849      repr += &format!("\n -- has {} RBEvents with waveforms!", self.rb_events.len());
850      repr += "\n -- -- boards: ";
851      for b in &self.rb_events {
852        repr += &format!("{} ", b.header.rb_id);
853      }
854    }
855    repr += ">";
856    write!(f, "{}", repr)
857  }
858}
859
860#[cfg(feature="random")]
861impl FromRandom for TofEvent {
862
863  fn from_random() -> Self {
864    let mut event           = Self::new();
865    let mut rng               = rand::rng();
866    let status                = EventStatus::from_random();
867    let version               = ProtocolVersion::from_random();
868    if version == ProtocolVersion::V1 {
869      event.n_hits_umb        = rng.random::<u8>();
870      event.n_hits_cbe        = rng.random::<u8>();
871      event.n_hits_cor        = rng.random::<u8>();
872      event.tot_edep_umb      = rng.random::<f32>();
873      event.tot_edep_cbe      = rng.random::<f32>();
874      event.tot_edep_cor      = rng.random::<f32>();
875      event.quality           = EventQuality::from_random();
876    }
877    event.status             = status;
878    event.version            = version;
879    // variable packet for the FC
880    event.trigger_sources    = rng.random::<u16>();
881    event.n_trigger_paddles  = rng.random::<u8>();
882    event.event_id           = rng.random::<u32>();
883    event.timestamp32        = rng.random::<u32>();
884    event.timestamp16        = rng.random::<u16>();
885    event.drs_dead_lost_hits = rng.random::<u16>();
886    event.dsi_j_mask         = rng.random::<u32>();
887    let n_channel_masks        = rng.random::<u8>();
888    for _ in 0..n_channel_masks {
889      event.channel_mask.push(rng.random::<u16>());
890    }
891    event.mtb_link_mask      = rng.random::<u64>();
892    //let nhits                  = rng.random::<u8>();
893    let nhits: u16 = rng.random_range(0..5);
894    for _ in 0..nhits {
895      event.hits.push(TofHit::from_random());
896    }
897    if event.version == ProtocolVersion::V2 {
898      let n_rb_events = rng.random_range(0..4);
899      for _ in 0..n_rb_events {
900        event.rb_events.push(RBEvent::from_random());
901      }
902    }
903    event
904  }
905}
906
907//---------------------------------------------------
908
909#[cfg(feature="pybindings")]
910#[pymethods]
911impl TofEvent {
912  
913  /// Emit a copy of self
914  fn copy(&self) -> Self {
915    self.clone()
916  }
917
918  #[pyo3(name="set_timing_offsets")]
919  pub fn set_timing_offsets_py(&mut self, timing_offsets : HashMap<u8, f32>) {
920    self.set_timing_offsets(&timing_offsets);
921  }
922  
923  #[pyo3(name="normalize_hit_times")]
924  pub fn normalize_hit_times_py(&mut self) {
925    self.normalize_hit_times();
926  }
927
928  /// Remove hits from the hitseries which can not 
929  /// be caused by the same particle, which means 
930  /// that for these two specific hits beta with 
931  /// respect to the first hit in the event is 
932  /// larger than one
933  /// That this works, first hits need to be 
934  /// "normalized" by calling normalize_hit_times
935  #[pyo3(name="lightspeed_cleaning")]
936  pub fn lightspeed_cleaning_py(&mut self, t_err : f32) -> (Vec<u16>, Vec<f32>) {
937    // return Vec<u16> here so that python does not 
938    // interpret it as a byte
939    let mut pids = Vec::<u16>::new();
940    let (pids_rm, twindows) = self.lightspeed_cleaning(t_err);
941    for pid in pids_rm {
942      pids.push(pid as u16);
943    }
944    (pids, twindows)
945  }
946 
947
948  /// Remove all hits from the event's hit series which 
949  /// do NOT obey causality. that is where the timings
950  /// measured at ends A and B can not be correlated
951  /// by the assumed speed of light in the paddle
952  #[pyo3(name="remove_non_causal_hits")]
953  fn remove_non_causal_hits_py(&mut self) -> Vec<u16> {
954    // return Vec<u16> here so that python does not 
955    // interpret it as a byte
956    let mut pids = Vec::<u16>::new();
957    for pid in self.remove_non_causal_hits() {
958      pids.push(pid as u16);
959    }
960    pids
961  }
962  
963  #[getter]
964  fn pointcloud(&self) -> Option<Vec<(f32,f32,f32,f32,f32)>> {
965    self.get_pointcloud()
966  }
967
968  #[getter]
969  #[pyo3(name="has_any_mangling")]
970  fn has_any_mangling_py(&self) -> bool {
971    self.has_any_mangling() 
972  }
973
974  #[getter]
975  fn get_event_id(&self) -> u32 {
976    self.event_id
977  }
978  
979  #[getter]
980  fn get_event_status(&self) -> EventStatus {
981    self.status
982  }
983  
984  /// Compare the hg hits of the event with the triggered paddles and 
985  /// return the paddles which have at least a missing HG hit
986  #[pyo3(name="get_missing_paddles_hg")]
987  fn get_missing_paddles_hg_py(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
988    self.get_missing_paddles_hg(&mapping)
989  }
990
991  /// Get all the paddle ids which have been triggered
992  #[pyo3(name="get_triggered_paddles")]
993  fn get_triggered_paddles_py(&self, mapping : DsiJChPidMapping) -> Vec<u8> {
994    self.get_triggered_paddles(&mapping)
995  }
996
997  /// The hits we were not able to read out because the DRS4 chip
998  /// on the RBs was busy
999  #[getter]
1000  fn lost_hits(&self) -> u16 {
1001    self.drs_dead_lost_hits
1002  }
1003
1004  /// RB Link IDS (not RB ids) which fall into the 
1005  /// trigger window
1006  #[getter]
1007  fn rb_link_ids(&self) -> Vec<u32> {
1008    self.get_rb_link_ids().into_iter().map(|byte| byte as u32).collect()
1009  }
1010
1011  /// The event might have RBEvents associated with it
1012  #[getter]
1013  fn get_rb_events(&self) -> Vec<RBEvent> {
1014    self.rb_events.clone()
1015  }
1016
1017  /// Hits which formed a trigger
1018  #[getter]
1019  pub fn trigger_hits(&self) -> PyResult<Vec<(u8, u8, (u8, u8), LTBThreshold)>> {
1020    Ok(self.get_trigger_hits())
1021  }
1022  
1023  /// The active triggers in this event. This can be more than one, 
1024  /// if multiple trigger conditions are satisfied.
1025  #[getter]
1026  pub fn trigger_sources(&self) -> Vec<TriggerType> {
1027    self.get_trigger_sources()
1028  } 
1029
1030  #[getter]
1031  #[pyo3(name="hits")]
1032  pub fn hits_py<'_py>(&self) -> Vec<TofHit> {
1033  //pub fn hits_py<'_py>(&self) -> PyResult<Bound<'_,Vec<TofHit>>> {
1034    //Bound::new(py, self.hits)
1035    //FIXMEFIXMEFIXME
1036    self.hits.clone()
1037  }
1038  
1039  #[getter]
1040  #[pyo3(name="hitmap")]
1041  pub fn hitmap<'_py>(&self) -> HashMap<u8,TofHit> {
1042  //pub fn hits_py<'_py>(&self) -> PyResult<Bound<'_,Vec<TofHit>>> {
1043    //Bound::new(py, self.hits)
1044    //FIXMEFIXMEFIXME
1045    let mut hitmap = HashMap::<u8, TofHit>::new();
1046    for h in &self.hits {
1047      hitmap.insert(h.paddle_id, *h);
1048    }
1049    hitmap
1050  }
1051  
1052  /// Total energy depostion in the Umbrella
1053  ///
1054  /// Utilizes Philip's formula based on 
1055  /// peak height
1056  #[getter]
1057  #[pyo3(name="get_edep_umbrella")]
1058  pub fn get_edep_umbrella_py(&self) -> f32 {
1059    self.get_edep_umbrella()
1060  }
1061  
1062  /// Total energy depostion in the Cube
1063  ///
1064  /// Utilizes Philip's formula based on 
1065  /// peak height
1066  #[getter]
1067  #[pyo3(name="get_edep_cube")]
1068  pub fn get_edep_cube_py(&self) -> f32 {
1069    self.get_edep_cube()
1070  }
1071  
1072  /// Total energy depostion in the Cortina
1073  ///
1074  /// Utilizes Philip's formula based on 
1075  /// peak height
1076  #[getter]
1077  #[pyo3(name="get_edep_cortina")]
1078  pub fn get_edep_cortina_py(&self) -> f32 {
1079    self.get_edep_cortina()
1080  }
1081
1082  /// Total energy depostion in the complete TOF
1083  ///
1084  /// Utilizes Philip's formula based on 
1085  /// peak height
1086  #[getter]
1087  #[pyo3(name="get_edep")]
1088  pub fn get_edep_py(&self) -> f32 {
1089    self.get_edep()
1090  }
1091
1092  #[getter]
1093  #[pyo3(name="nhits")]
1094  pub fn nhits_py(&self) -> usize {
1095    self.get_nhits()
1096  }
1097
1098  #[getter]
1099  #[pyo3(name="nhits_umb")]
1100  pub fn nhits_umb_py(&self) -> usize {
1101    self.get_nhits_umb()
1102  }
1103
1104  #[getter]
1105  #[pyo3(name="nhits_cbe")]
1106  fn get_nhits_cbe_py(&self) -> usize {
1107    self.get_nhits_cbe()
1108  }
1109  
1110  #[getter]
1111  #[pyo3(name="nhits_cor")]
1112  fn get_nhits_cor_py(&self) -> usize {
1113    self.get_nhits_cor()
1114  }
1115
1116  #[getter]
1117  fn get_timestamp16(&self) -> u16 {
1118    self.timestamp16
1119  }
1120  
1121  #[getter]
1122  fn get_timestamp32(&self) -> u32 {
1123    self.timestamp32
1124  }
1125  
1126  #[getter]
1127  fn timestamp48(&self) -> u64 {
1128    self.get_timestamp48()
1129  }
1130  
1131  #[getter]
1132  fn get_status(&self) -> EventStatus {
1133    self.status
1134  }
1135
1136  #[getter]
1137  #[pyo3(name="waveforms")]
1138  fn get_waveforms_py(&self) -> Vec<RBWaveform> {
1139    self.get_waveforms()
1140  }
1141}
1142
1143#[cfg(feature="pybindings")]
1144pythonize_packable!(TofEvent);
1145
1146//---------------------------------------------------
1147
1148#[test]
1149fn packable_tofeventv0() {
1150  for _ in 0..500 {
1151    let mut data = TofEvent::from_random();
1152    if data.version != ProtocolVersion::Unknown {
1153      continue;
1154    }
1155    let mut test : TofEvent = data.pack().unpack().unwrap();
1156    //println!("{}", data.hits[0]);
1157    //println!("{}", test.hits[0]);
1158    // Manually zero these fields, since comparison with nan will fail and 
1159    // from_random did not touch these
1160    //println!("{}", data);
1161    //println!("{}", test);
1162    let fix_time = Instant::now();
1163    test.creation_time = fix_time;
1164    data.creation_time = fix_time;
1165    for h in &mut test.hits {
1166      h.paddle_len       = 0.0; 
1167      h.coax_cable_time  = 0.0; 
1168      h.hart_cable_time  = 0.0; 
1169      h.x                = 0.0; 
1170      h.y                = 0.0; 
1171      h.z                = 0.0; 
1172      h.event_t0         = 0.0;
1173    }
1174    assert_eq!(data, test);
1175  }
1176}  
1177
1178#[test]
1179fn packable_tofeventv1() {
1180  for _ in 0..500 {
1181    let mut data = TofEvent::from_random();
1182    if data.version != ProtocolVersion::V1 {
1183      continue;
1184    }
1185    let mut test : TofEvent = data.pack().unpack().unwrap();
1186    //println!("{}", data.hits[0]);
1187    //println!("{}", test.hits[0]);
1188    // Manually zero these fields, since comparison with nan will fail and 
1189    // from_random did not touch these
1190    let fix_time = Instant::now();
1191    test.creation_time = fix_time;
1192    data.creation_time = fix_time;
1193    for h in &mut test.hits {
1194      h.paddle_len       = 0.0; 
1195      h.coax_cable_time  = 0.0; 
1196      h.hart_cable_time  = 0.0; 
1197      h.x                = 0.0; 
1198      h.y                = 0.0; 
1199      h.z                = 0.0; 
1200      h.event_t0         = 0.0;
1201    }
1202    assert_eq!(data, test);
1203  }
1204}  
1205
1206#[test]
1207fn packable_tofeventv2() {
1208  for _ in 0..500 {
1209    let mut data = TofEvent::from_random();
1210    if data.version != ProtocolVersion::V2 {
1211      continue;
1212    }
1213    let mut test : TofEvent = data.pack().unpack().unwrap();
1214    //println!("{}", data.hits[0]);
1215    //println!("{}", test.hits[0]);
1216    // Manually zero these fields, since comparison with nan will fail and 
1217    // from_random did not touch these
1218    let fix_time = Instant::now();
1219    test.creation_time = fix_time;
1220    data.creation_time = fix_time;
1221    for h in &mut test.hits {
1222      h.paddle_len       = 0.0; 
1223      h.coax_cable_time  = 0.0; 
1224      h.hart_cable_time  = 0.0; 
1225      h.x                = 0.0; 
1226      h.y                = 0.0; 
1227      h.z                = 0.0; 
1228      h.event_t0         = 0.0;
1229    }
1230    assert_eq!(data, test);
1231  }
1232}  
1233
1234
1235//#[test]
1236//#[cfg(feature = "random")]
1237//fn tofevent_sizes_header() {
1238//  for _ in 0..100 {
1239//    let data = TofEvent::from_random();
1240//    let mask = data.construct_sizes_header();
1241//    let size = TofEvent::decode_size_header(&mask);
1242//    assert_eq!(size.0, data.rb_events.len());
1243//    //assert_eq!(size.1, data.missing_hits.len());
1244//  }
1245//}
1246
1247//#[test]
1248//#[cfg(feature = "random")]
1249//fn packable_tofevent() {
1250//  for _ in 0..5 {
1251//    let data = TofEvent::from_random();
1252//    let test : TofEvent = data.pack().unpack().unwrap();
1253//    assert_eq!(data.header, test.header);
1254//    assert_eq!(data.compression_level, test.compression_level);
1255//    assert_eq!(data.quality, test.quality);
1256//    assert_eq!(data.mt_event, test.mt_event);
1257//    assert_eq!(data.rb_events.len(), test.rb_events.len());
1258//    //assert_eq!(data.missing_hits.len(), test.missing_hits.len());
1259//    //assert_eq!(data.missing_hits, test.missing_hits);
1260//    assert_eq!(data.rb_events, test.rb_events);
1261//    //assert_eq!(data, test);
1262//    //println!("{}", data);
1263//  }
1264//}
1265
1266
1267