Skip to main content

gondola_core/events/
tracker_hit.rs

1//! Per-strip event information for the GAPS tracker
2// This file is part of gaps-online-software and published 
3// under the GPLv3 license
4
5use crate::prelude::*;
6
7#[cfg(feature="pybindings")]
8use pyo3::basic::CompareOp;
9
10/// Hit on a tracker strip
11#[derive(Debug, Copy, Clone)]
12#[cfg_attr(feature="pybindings", pyclass)]
13pub struct TrackerHit {
14  pub layer           : u8,
15  pub row             : u8,
16  pub module          : u8,
17  pub channel         : u8,
18  pub adc             : u16,
19  pub oscillator      : u64,
20  /// In BFSW, there are two versions of the tracker hit, 
21  /// tracker_hit and tracker::hit. The latter has 
22  /// an extra ASIC event code field. Let's unify those here
23  pub asic_event_code : u8,
24
25  // not getting serialized
26  /// calibrated energy
27  pub energy          : f32, 
28  pub x               : f32,
29  pub y               : f32,
30  pub z               : f32,
31  pub has_coordinates : bool,
32  pub adc_pedestal    : u16,
33}
34
35impl TrackerHit {
36  //const SIZE : usize = 18;
37  
38  pub fn new() -> Self {
39    Self {
40      layer           : 0,
41      row             : 0,
42      module          : 0,
43      channel         : 0,
44      adc             : 0,
45      oscillator      : 0,
46      asic_event_code : 0,
47      energy          : 0.0,
48      x               : 0.0,
49      y               : 0.0,
50      z               : 0.0,
51      has_coordinates : false,
52      adc_pedestal    : 0,
53    }
54  }
55 
56  /// Calculate the strip id from layer, module, row and channel
57  pub fn get_stripid(&self) -> u32 {
58    crate::events::strip_id(self.layer  , 
59                            self.row    ,
60                            self.module ,
61                            self.channel)
62  }
63
64 #[cfg(feature="database")]
65 pub fn set_coordinates(&mut self, strip_map : &HashMap<u32, TrackerStrip>) {
66   match strip_map.get(&self.get_stripid()) {
67     None  => debug!("Can not get strip for strip id {}" , self.get_stripid()),
68     Some(strip) => { 
69       self.x = strip.global_pos_x_l0;
70       self.y = strip.global_pos_y_l0;
71       self.z = strip.global_pos_z_l0;
72       self.has_coordinates = true
73     }
74   }
75 }
76}
77
78impl PartialEq for TrackerHit {
79  fn eq(&self, other: &TrackerHit) -> bool {
80    // we can only compare fields here which 
81    // are always set, merged events do 
82    // not have the asic event code 
83    // populated
84    self.layer              ==  other.layer           
85    && self.row             ==  other.row            
86    && self.module          ==  other.module         
87    && self.channel         ==  other.channel        
88    && self.adc             ==  other.adc            
89    && self.oscillator      ==  other.oscillator     
90    //&& self.asic_event_code ==  other.asic_event_code
91  }
92}
93
94
95impl fmt::Display for TrackerHit {
96  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97    let mut repr = String::from("<TrackerHit:");
98    repr += &(format!("\n  Layer, Row, Module, Channel : {} {} {} {}" ,self.layer, self.row, self.module, self.channel));
99    repr += &(format!("\n  ADC           : {}" ,self.adc));
100    repr += &(format!("\n  Oscillator    : {}" ,self.oscillator));
101    repr += &(format!("\n  ASIC Evt code : {}" ,self.asic_event_code)); 
102    if self.has_coordinates {
103      repr += &(format!("\n -- coordinates x : {} , y : {} , z {}", self.x, self.y, self.z));
104    } else {
105      repr += "\n -- [no coordinates set]";
106    }
107    repr += &(format!("\n  Cali. energy  : {}>", self.energy));
108    write!(f, "{}", repr)
109  }
110}
111
112#[cfg(feature="pybindings")]
113#[pymethods]
114impl TrackerHit {
115
116  /// Change the ADC value, e.g. if the 
117  /// pedestal should be subtracted
118  fn subtract_pedestal(&mut self, pedestal : u16) {
119    self.adc -= pedestal;
120  }
121
122  #[getter]
123  fn get_strip_id(&self) -> u32 {
124    self.get_stripid()
125  }
126
127  #[getter]
128  fn get_layer(&self) -> u8 {
129    self.layer
130  }
131
132  #[getter]
133  fn get_row(&self) -> u8 {
134    self.row
135  }
136
137  #[getter]
138  fn get_module(&self) -> u8 {
139    self.module
140  }
141
142  #[getter]
143  fn get_channel(&self) -> u8 {
144    self.channel
145  }
146
147  #[getter]
148  fn get_adc(&self) -> u16 {
149    self.adc
150  }
151
152  #[getter]
153  fn get_oscillator(&self) -> u64 {
154    self.oscillator
155  }
156 
157  #[getter] 
158  fn get_asic_event_code(&self) -> u8 {
159    self.asic_event_code
160  }
161
162  #[getter]
163  fn get_energy(&self) -> f32 {
164    self.energy
165  }
166
167  #[getter]
168  fn get_x(&self) -> f32 {
169    self.x
170  }
171  
172  #[getter]
173  fn get_y(&self) -> f32 {
174    self.y
175  }
176  
177  #[getter]
178  fn get_z(&self) -> f32 {
179    self.z
180  }
181    
182  // This handles Python's == and != (and others if you wish)
183  fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
184    match op {
185      CompareOp::Eq => Ok(self == other),
186      CompareOp::Ne => Ok(self != other),
187      _ => Ok(false), // Or return an error for unsupported ops like < or >
188    }
189  }
190}
191
192#[cfg(feature="random")]
193impl FromRandom for TrackerHit {
194  fn from_random() -> Self {
195    let mut rng       = rand::rng();
196    Self {
197      layer           : rng.random_range(0..9),
198      row             : rng.random_range(0..6),
199      module          : rng.random_range(0..6),
200      channel         : rng.random_range(0..32),
201      adc             : rng.random::<u16>() & 0x7ff,
202      oscillator      : rng.random::<u64>(),
203      asic_event_code : rng.random_range(0..4),
204      energy          : 0.0, 
205      x               : 0.0,
206      y               : 0.0,
207      z               : 0.0,
208      has_coordinates : false,
209      adc_pedestal    : 0,
210    }
211  }
212}
213
214
215#[cfg(feature="pybindings")]
216pythonize!(TrackerHit);
217