Skip to main content

gondola_core/
events.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4pub mod tof_hit;
5pub use tof_hit::TofHit;
6
7pub mod rb_waveform;
8pub use rb_waveform::RBWaveform;
9
10pub mod rb_event_header;
11pub use rb_event_header::RBEventHeader;
12
13pub mod tof_event;
14pub use tof_event::TofEvent;
15
16pub mod rb_event;
17pub use rb_event::{
18  RBEvent,
19  unpack_traces
20};
21
22pub mod tracker_hit;
23pub use tracker_hit::TrackerHit;
24
25pub mod tracker_daq_event;
26pub use tracker_daq_event::TrackerDAQEvent;
27
28pub mod tracker_daq_event_packet;
29pub use tracker_daq_event_packet::TrackerDAQEventPacket;
30
31pub mod telemetry_event;
32pub use telemetry_event::TelemetryEvent;
33
34pub mod mc_hit;
35pub use mc_hit::McHit;
36
37pub mod mc_track;
38pub use mc_track::McTrack;
39
40pub mod mc_event;
41pub use mc_event::McEvent;
42
43pub mod mc_tree;
44pub use mc_tree::McTree;
45
46use std::fmt;
47
48use strum_macros::{
49  AsRefStr,
50  FromRepr,
51  EnumIter
52};
53// needed for enum macro
54// FIXME 
55#[cfg(feature="random")]
56use strum::IntoEnumIterator;
57use crate::expand_and_test_enum;
58
59#[cfg(feature="pybindings")]
60use pyo3::prelude::*;
61
62#[cfg(feature="random")]
63use crate::random::FromRandom;
64#[cfg(feature="random")]
65use rand::Rng;
66
67/// mask to decode LTB hit masks
68pub const LTB_CH0 : u16 = 0x3   ;
69/// mask to decode LTB hit masks
70pub const LTB_CH1 : u16 = 0xc   ;
71/// mask to decode LTB hit masks
72pub const LTB_CH2 : u16 = 0x30  ; 
73/// mask to decode LTB hit masks
74pub const LTB_CH3 : u16 = 0xc0  ;
75/// mask to decode LTB hit masks
76pub const LTB_CH4 : u16 = 0x300 ;
77/// mask to decode LTB hit masks
78pub const LTB_CH5 : u16 = 0xc00 ;
79/// mask to decode LTB hit masks
80pub const LTB_CH6 : u16 = 0x3000;
81/// mask to decode LTB hit masks
82pub const LTB_CH7 : u16 = 0xc000;
83/// mask to decode LTB channels from bitmask
84pub const LTB_CHANNELS : [u16;8] = [
85  LTB_CH0,
86  LTB_CH1,
87  LTB_CH2,
88  LTB_CH3,
89  LTB_CH4,
90  LTB_CH5,
91  LTB_CH6,
92  LTB_CH7
93];
94
95/// An array of the channel numbers as they come in pairs on the LTB
96pub const PHYSICAL_CHANNELS : [(u8, u8); 8] = [(1u8,  2u8), (3u8,4u8), (5u8, 6u8), (7u8, 8u8),
97                                               (9u8, 10u8), (11u8,12u8), (13u8, 14u8), (15u8, 16u8)];
98
99
100/// Calculate an unique identifier for 
101/// tracker strips from the position in 
102/// the tracker stack
103///
104/// # Arguments:
105///   * layer   : tracker layer (0-9)
106///   * row     : row in layer  (0-6)
107///   * module  : module in row (0-6)
108///   * channel : channel in module (0-32) 
109///
110#[cfg_attr(feature="pybindings", pyfunction)]
111pub fn strip_id(layer : u8, row :u8, module : u8, channel : u8) -> u32 {
112  channel as u32 + (module as u32)*100 + (row as u32)*10000 + (layer as u32)*100000
113}
114  
115/// Get absolute timestamp as sent by the GPS and 
116/// as seen by the MTB
117#[cfg_attr(feature="pybindings", pyfunction)]
118pub fn mt_event_get_timestamp_abs48(mtb_timestamp : u32, gps_timestamp : u32, tiu_timestamp : u32) -> u64 {
119  let gps = gps_timestamp as u64;
120  let mut timestamp = mtb_timestamp as u64;
121  if timestamp < tiu_timestamp as u64 {
122    // it has wrapped
123    timestamp += u32::MAX as u64 + 1;
124  }
125  let gps_mult = match 100_000_000u64.checked_mul(gps) {
126  //let gps_mult = match 100_000u64.checked_mul(gps) {
127    Some(result) => result,
128    None => {
129        // Handle overflow case here
130        // Example: log an error, return a default value, etc.
131        0 // Example fallback value
132    }
133  };
134
135  let ts = gps_mult + (timestamp - tiu_timestamp as u64);
136  ts
137}
138
139#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter)]
140#[repr(u8)]
141#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
142pub enum EventQuality {
143  Unknown        =  0u8,
144  Silver         = 10u8,
145  Gold           = 20u8,
146  Diamond        = 30u8,
147  FourLeafClover = 40u8,
148}
149
150expand_and_test_enum!(EventQuality, test_eventquality_repr);
151
152//--------------------------------------------
153
154/// The type of trigger which has fired at time of 
155/// recoring a specific event. 
156///
157/// The GAPS main trigger system allows to record 
158/// events based on different conditions
159/// simulataneously. 
160#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter, serde::Serialize, serde::Deserialize)]
161#[repr(u8)]
162#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
163pub enum TriggerType {
164  Unknown         = 0u8,
165  /// -> 1-10 "pysics" triggers
166  Any             = 1u8,
167  /// 1 hit on outer (UMB + COR) and inner (CBE) TOF
168  Track           = 2u8,
169  TrackCentral    = 3u8,
170  /// The "default" antiparticle trigger. It requires 
171  /// 8 hits in total, 4 on the inner, 4 on the outer TOF.
172  /// 2 of the hits have to cross
173  /// the second LTB threshold "beta"
174  Gaps            = 4u8,
175  Gaps633         = 5u8, 
176  Gaps422         = 6u8,
177  Gaps211         = 7u8,
178  TrackUmbCentral = 8u8,
179  Gaps1044        = 9u8,
180  /// -> 20+ "Philip's triggers"
181  /// Any paddle HIT in UMB  + any paddle HIT in CUB
182  UmbCube         = 21u8,
183  /// Any paddle HIT in UMB + any paddle HIT in CUB top
184  UmbCubeZ        = 22u8,
185  /// Any paddle HIT in UMB + any paddle hit in COR + any paddle hit in CUB 
186  UmbCorCube      = 23u8,
187  /// Any paddle HIT in COR + any paddle HIT in CUB SIDES
188  CorCubeSide     = 24u8,
189  /// Any paddle hit in UMB + any three paddles HIT in CUB
190  Umb3Cube        = 25u8,
191  /// > 100 -> Debug triggers
192  Poisson         = 100u8,
193  Forced          = 101u8,
194  FixedRate       = 102u8,
195  /// > 200 -> These triggers can not be set, they are merely
196  /// the result of what we read out from the trigger mask of 
197  /// the ltb
198  ConfigurableTrigger = 200u8,
199}
200
201impl TriggerType {
202
203  /// In the serialized data, trigger sources are represented by 2bytes. 
204  /// This will regenerate a vector of trigger sources from these bytes
205  pub fn transcode_trigger_sources(trigger_sources : u16) -> Vec<Self> {
206    let mut t_types    = Vec::<Self>::new();
207    let gaps_trigger   = trigger_sources >> 5 & 0x1 == 1;
208    if gaps_trigger {
209      t_types.push(TriggerType::Gaps);
210    }
211    let any_trigger    = trigger_sources >> 6 & 0x1 == 1;
212    if any_trigger {
213      t_types.push(TriggerType::Any);
214    }
215    let forced_trigger = trigger_sources >> 7 & 0x1 == 1;
216    if forced_trigger {
217      t_types.push(TriggerType::Forced);
218    }
219    let track_trigger  = trigger_sources >> 8 & 0x1 == 1;
220    if track_trigger {
221      t_types.push(TriggerType::Track);
222    }
223    let central_track_trigger
224                       = trigger_sources >> 9 & 0x1 == 1;
225    if central_track_trigger {
226      t_types.push(TriggerType::TrackCentral);
227    }
228    t_types
229  }
230 
231  pub fn to_u8(&self) -> u8 {
232    match self {
233      TriggerType::Unknown => {
234        return 0;
235      }
236      TriggerType::Poisson => {
237        return 100;
238      }
239      TriggerType::Forced => {
240        return 101;
241      }
242      TriggerType::FixedRate => {
243        return 102;
244      }
245      TriggerType::Any => {
246        return 1;
247      }
248      TriggerType::Track => {
249        return 2;
250      }
251      TriggerType::TrackCentral => {
252        return 3;
253      }
254      TriggerType::Gaps => {
255        return 4;
256      }
257      TriggerType::Gaps633 => {
258        return 5;
259      }
260      TriggerType::Gaps422 => {
261        return 6;
262      }
263      TriggerType::Gaps211 => {
264        return 7;
265      }
266      TriggerType::TrackUmbCentral => {
267        return 8;
268      }
269      TriggerType::Gaps1044 => {
270        return 9;
271      }
272      TriggerType::UmbCube => {
273        return 21;
274      }
275      TriggerType::UmbCubeZ => {
276        return 22; 
277      }
278      TriggerType::UmbCorCube => {
279        return 23;
280      }
281      TriggerType::CorCubeSide => {
282        return 24;
283      }
284      TriggerType::Umb3Cube => {
285        return 25;
286      }
287      TriggerType::ConfigurableTrigger => {
288        return 200;  
289      }
290    }
291  }
292}
293
294#[cfg(feature="pybindings")]
295#[pymethods]
296impl TriggerType {
297  #[staticmethod]
298  #[pyo3(name="transcode_trigger_sources")]
299  fn transcode_trigger_sources_py(trigger_sources : u16) -> Vec<Self> {
300    TriggerType::transcode_trigger_sources(trigger_sources)
301  }
302}
303
304expand_and_test_enum!(TriggerType, test_triggertype_repr);
305
306//--------------------------------------------
307
308/// LTB Thresholds as passed on by the MTB
309/// [See also](https://gaps1.astro.ucla.edu/wiki/gaps/images/gaps/5/52/LTB_Data_Format.pdf)
310#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter)]
311#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
312#[repr(u8)]
313pub enum LTBThreshold {
314  NoHit = 0u8,
315  /// First threshold, 40mV, about 0.75 minI
316  Hit   = 1u8,
317  /// Second threshold, 32mV (? error in doc ?, about 2.5 minI
318  Beta  = 2u8,
319  /// Third threshold, 375mV about 30 minI
320  Veto  = 3u8,
321  /// Use u8::MAX for Unknown, since 0 is pre-determined for 
322  /// "NoHit, 
323  Unknown = 255u8
324}
325
326expand_and_test_enum!(LTBThreshold, test_ltbthreshold_repr);
327
328//--------------------------------------------
329
330#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, FromRepr, AsRefStr, EnumIter)]
331#[repr(u8)]
332#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
333pub enum EventStatus {
334  Unknown                = 0u8,
335  CRC32Wrong             = 10u8,
336  TailWrong              = 11u8,
337  ChannelIDWrong         = 12u8,
338  /// one of the channels cells CellSyncError bits 
339  /// has been set (RB)
340  CellSyncErrors         = 13u8,
341  /// one of the channels ChannelSyncError bits 
342  /// has been set (RB)
343  ChnSyncErrors          = 14u8,
344  /// Both of the bits (at least one for the cell sync errors)
345  /// have been set
346  CellAndChnSyncErrors   = 15u8,
347  /// If any of the RBEvents have Sync erros, we flag the tof 
348  /// event summary to indicate there were issues
349  AnyDataMangling        = 16u8,
350  /// RB is missing, but it is expected that is missing
351  /// when we compare the trigger information with the 
352  /// list of known dead rbs
353  KnownDeadRB            = 17u8,
354  IncompleteReadout      = 21u8,
355  /// This can be used if there is a version
356  /// missmatch and we have to hack something
357  IncompatibleData       = 22u8,
358  /// The TofEvent timed out while waiting for more Readoutboards
359  EventTimeOut           = 23u8,
360  /// A RB misses Ch9 data
361  NoChannel9             = 24u8,
362  /// A RBReceives a strange event id 
363  RBEventWacky           = 25u8,
364  GoodNoCRCOrErrBitCheck = 39u8,
365  /// The event status is good, but we did not 
366  /// perform any CRC32 check
367  GoodNoCRCCheck         = 40u8,
368  /// The event is good, but we did not perform
369  /// error checks
370  GoodNoErrBitCheck      = 41u8,
371  Perfect                = 42u8
372}
373
374// in case we have pybindings for this type, 
375// expand it so that it can be used as keys
376// in dictionaries
377#[cfg(feature = "pybindings")]
378#[pymethods]
379impl EventStatus {
380
381  #[getter]
382  fn __hash__(&self) -> usize {
383    (*self as u8) as usize
384  } 
385}
386
387
388expand_and_test_enum!(EventStatus, test_eventstatus_repr);
389
390//--------------------------------------------
391
392/// A generic data type
393///
394/// Describe the purpose of the data. This
395/// is the semantics behind it.
396#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter, serde::Deserialize, serde::Serialize)]
397#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
398#[repr(u8)]
399pub enum DataType {
400  Unknown            = 0u8,
401  VoltageCalibration = 10u8,
402  TimingCalibration  = 20u8,
403  Noi                = 30u8,
404  Physics            = 40u8,
405  RBTriggerPeriodic  = 50u8,
406  RBTriggerPoisson   = 60u8,
407  MTBTriggerPoisson  = 70u8,
408  // future extension for different trigger settings!
409}
410
411expand_and_test_enum!(DataType, test_datatype_repr);
412
413//--------------------------------------------
414