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 telemetry_event;
26pub use telemetry_event::TelemetryEvent;
27
28use std::fmt;
29
30use strum_macros::{
31  AsRefStr,
32  FromRepr,
33  EnumIter
34};
35// needed for enum macro
36// FIXME 
37#[cfg(feature="random")]
38use strum::IntoEnumIterator;
39use crate::expand_and_test_enum;
40
41#[cfg(feature="pybindings")]
42use pyo3::prelude::*;
43
44#[cfg(feature="random")]
45use crate::random::FromRandom;
46#[cfg(feature="random")]
47use rand::Rng;
48
49/// mask to decode LTB hit masks
50pub const LTB_CH0 : u16 = 0x3   ;
51/// mask to decode LTB hit masks
52pub const LTB_CH1 : u16 = 0xc   ;
53/// mask to decode LTB hit masks
54pub const LTB_CH2 : u16 = 0x30  ; 
55/// mask to decode LTB hit masks
56pub const LTB_CH3 : u16 = 0xc0  ;
57/// mask to decode LTB hit masks
58pub const LTB_CH4 : u16 = 0x300 ;
59/// mask to decode LTB hit masks
60pub const LTB_CH5 : u16 = 0xc00 ;
61/// mask to decode LTB hit masks
62pub const LTB_CH6 : u16 = 0x3000;
63/// mask to decode LTB hit masks
64pub const LTB_CH7 : u16 = 0xc000;
65/// mask to decode LTB channels from bitmask
66pub const LTB_CHANNELS : [u16;8] = [
67  LTB_CH0,
68  LTB_CH1,
69  LTB_CH2,
70  LTB_CH3,
71  LTB_CH4,
72  LTB_CH5,
73  LTB_CH6,
74  LTB_CH7
75];
76
77/// An array of the channel numbers as they come in pairs on the LTB
78pub const PHYSICAL_CHANNELS : [(u8, u8); 8] = [(1u8,  2u8), (3u8,4u8), (5u8, 6u8), (7u8, 8u8),
79                                               (9u8, 10u8), (11u8,12u8), (13u8, 14u8), (15u8, 16u8)];
80
81
82/// Calculate an unique identifier for 
83/// tracker strips from the position in 
84/// the tracker stack
85///
86/// # Arguments:
87///   * layer   : tracker layer (0-9)
88///   * row     : row in layer  (0-6)
89///   * module  : module in row (0-6)
90///   * channel : channel in module (0-32) 
91///
92#[cfg_attr(feature="pybindings", pyfunction)]
93pub fn strip_id(layer : u8, row :u8, module : u8, channel : u8) -> u32 {
94  channel as u32 + (module as u32)*100 + (row as u32)*10000 + (layer as u32)*100000
95}
96  
97/// Get absolute timestamp as sent by the GPS and 
98/// as seen by the MTB
99#[cfg_attr(feature="pybindings", pyfunction)]
100pub fn mt_event_get_timestamp_abs48(mtb_timestamp : u32, gps_timestamp : u32, tiu_timestamp : u32) -> u64 {
101  let gps = gps_timestamp as u64;
102  let mut timestamp = mtb_timestamp as u64;
103  if timestamp < tiu_timestamp as u64 {
104    // it has wrapped
105    timestamp += u32::MAX as u64 + 1;
106  }
107  let gps_mult = match 100_000_000u64.checked_mul(gps) {
108  //let gps_mult = match 100_000u64.checked_mul(gps) {
109    Some(result) => result,
110    None => {
111        // Handle overflow case here
112        // Example: log an error, return a default value, etc.
113        0 // Example fallback value
114    }
115  };
116
117  let ts = gps_mult + (timestamp - tiu_timestamp as u64);
118  ts
119}
120
121#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter)]
122#[repr(u8)]
123#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
124pub enum EventQuality {
125  Unknown        =  0u8,
126  Silver         = 10u8,
127  Gold           = 20u8,
128  Diamond        = 30u8,
129  FourLeafClover = 40u8,
130}
131
132expand_and_test_enum!(EventQuality, test_eventquality_repr);
133
134//--------------------------------------------
135
136// Need serde here, so that we can add it to the liftof configs
137#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter, serde::Serialize, serde::Deserialize)]
138#[repr(u8)]
139#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
140pub enum TriggerType {
141  Unknown         = 0u8,
142  /// -> 1-10 "pysics" triggers
143  Any             = 1u8,
144  Track           = 2u8,
145  TrackCentral    = 3u8,
146  Gaps            = 4u8,
147  Gaps633         = 5u8, 
148  Gaps422         = 6u8,
149  Gaps211         = 7u8,
150  TrackUmbCentral = 8u8,
151  Gaps1044        = 9u8,
152  /// -> 20+ "Philip's triggers"
153  /// Any paddle HIT in UMB  + any paddle HIT in CUB
154  UmbCube         = 21u8,
155  /// Any paddle HIT in UMB + any paddle HIT in CUB top
156  UmbCubeZ        = 22u8,
157  /// Any paddle HIT in UMB + any paddle hit in COR + any paddle hit in CUB 
158  UmbCorCube      = 23u8,
159  /// Any paddle HIT in COR + any paddle HIT in CUB SIDES
160  CorCubeSide     = 24u8,
161  /// Any paddle hit in UMB + any three paddles HIT in CUB
162  Umb3Cube        = 25u8,
163  /// > 100 -> Debug triggers
164  Poisson         = 100u8,
165  Forced          = 101u8,
166  FixedRate       = 102u8,
167  /// > 200 -> These triggers can not be set, they are merely
168  /// the result of what we read out from the trigger mask of 
169  /// the ltb
170  ConfigurableTrigger = 200u8,
171}
172
173impl TriggerType {
174
175  /// In the serialized data, trigger sources are represented by 2bytes. 
176  /// This will regenerate a vector of trigger sources from these bytes
177  pub fn transcode_trigger_sources(trigger_sources : u16) -> Vec<Self> {
178    let mut t_types    = Vec::<Self>::new();
179    let gaps_trigger   = trigger_sources >> 5 & 0x1 == 1;
180    if gaps_trigger {
181      t_types.push(TriggerType::Gaps);
182    }
183    let any_trigger    = trigger_sources >> 6 & 0x1 == 1;
184    if any_trigger {
185      t_types.push(TriggerType::Any);
186    }
187    let forced_trigger = trigger_sources >> 7 & 0x1 == 1;
188    if forced_trigger {
189      t_types.push(TriggerType::Forced);
190    }
191    let track_trigger  = trigger_sources >> 8 & 0x1 == 1;
192    if track_trigger {
193      t_types.push(TriggerType::Track);
194    }
195    let central_track_trigger
196                       = trigger_sources >> 9 & 0x1 == 1;
197    if central_track_trigger {
198      t_types.push(TriggerType::TrackCentral);
199    }
200    t_types
201  }
202}
203
204#[cfg(feature="pybindings")]
205#[pymethods]
206impl TriggerType {
207  #[staticmethod]
208  #[pyo3(name="transcode_trigger_sources")]
209  fn transcode_trigger_sources_py(trigger_sources : u16) -> Vec<Self> {
210    TriggerType::transcode_trigger_sources(trigger_sources)
211  }
212}
213
214expand_and_test_enum!(TriggerType, test_triggertype_repr);
215
216//--------------------------------------------
217
218/// LTB Thresholds as passed on by the MTB
219/// [See also](https://gaps1.astro.ucla.edu/wiki/gaps/images/gaps/5/52/LTB_Data_Format.pdf)
220#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter)]
221#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
222#[repr(u8)]
223pub enum LTBThreshold {
224  NoHit = 0u8,
225  /// First threshold, 40mV, about 0.75 minI
226  Hit   = 1u8,
227  /// Second threshold, 32mV (? error in doc ?, about 2.5 minI
228  Beta  = 2u8,
229  /// Third threshold, 375mV about 30 minI
230  Veto  = 3u8,
231  /// Use u8::MAX for Unknown, since 0 is pre-determined for 
232  /// "NoHit, 
233  Unknown = 255u8
234}
235
236expand_and_test_enum!(LTBThreshold, test_ltbthreshold_repr);
237
238//--------------------------------------------
239
240#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter)]
241#[repr(u8)]
242#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
243pub enum EventStatus {
244  Unknown                = 0u8,
245  CRC32Wrong             = 10u8,
246  TailWrong              = 11u8,
247  ChannelIDWrong         = 12u8,
248  /// one of the channels cells CellSyncError bits 
249  /// has been set (RB)
250  CellSyncErrors         = 13u8,
251  /// one of the channels ChannelSyncError bits 
252  /// has been set (RB)
253  ChnSyncErrors          = 14u8,
254  /// Both of the bits (at least one for the cell sync errors)
255  /// have been set
256  CellAndChnSyncErrors   = 15u8,
257  /// If any of the RBEvents have Sync erros, we flag the tof 
258  /// event summary to indicate there were issues
259  AnyDataMangling        = 16u8,
260  IncompleteReadout      = 21u8,
261  /// This can be used if there is a version
262  /// missmatch and we have to hack something
263  IncompatibleData       = 22u8,
264  /// The TofEvent timed out while waiting for more Readoutboards
265  EventTimeOut           = 23u8,
266  /// A RB misses Ch9 data
267  NoChannel9             = 24u8,
268  GoodNoCRCOrErrBitCheck = 39u8,
269  /// The event status is good, but we did not 
270  /// perform any CRC32 check
271  GoodNoCRCCheck         = 40u8,
272  /// The event is good, but we did not perform
273  /// error checks
274  GoodNoErrBitCheck      = 41u8,
275  Perfect                = 42u8
276}
277
278// in case we have pybindings for this type, 
279// expand it so that it can be used as keys
280// in dictionaries
281#[cfg(feature = "pybindings")]
282#[pymethods]
283impl EventStatus {
284
285  #[getter]
286  fn __hash__(&self) -> usize {
287    (*self as u8) as usize
288  } 
289}
290
291
292expand_and_test_enum!(EventStatus, test_eventstatus_repr);
293
294//--------------------------------------------
295
296/// A generic data type
297///
298/// Describe the purpose of the data. This
299/// is the semantics behind it.
300#[derive(Debug, Copy, Clone, PartialEq,FromRepr, AsRefStr, EnumIter, serde::Deserialize, serde::Serialize)]
301#[cfg_attr(feature = "pybindings", pyclass(eq, eq_int))]
302#[repr(u8)]
303pub enum DataType {
304  Unknown            = 0u8,
305  VoltageCalibration = 10u8,
306  TimingCalibration  = 20u8,
307  Noi                = 30u8,
308  Physics            = 40u8,
309  RBTriggerPeriodic  = 50u8,
310  RBTriggerPoisson   = 60u8,
311  MTBTriggerPoisson  = 70u8,
312  // future extension for different trigger settings!
313}
314
315expand_and_test_enum!(DataType, test_datatype_repr);
316
317//--------------------------------------------
318