gondola_core/events/
telemetry_event.rs

1//! The TelemetryEvent or former "MergedEvent" is that what gets 
2//! sent over telemetry during flight
3// The following file is part of gaps-online-software and published 
4// under the GPLv3 license
5
6use crate::prelude::*;
7
8#[cfg_attr(feature="pybindings", pyclass)]
9pub struct TelemetryEvent {
10  pub header              : TelemetryPacketHeader,
11  pub creation_time       : u64,
12  pub event_id            : u32,
13  pub tracker_hits        : Vec<TrackerHit>,
14  pub tracker_oscillators : Vec<u64>,
15  pub tof_event           : TofEvent,
16  pub raw_data            : Vec<u8>,
17  pub flags0              : u8,
18  pub flags1              : u8,
19  pub version             : u8
20}
21
22impl TelemetryEvent {
23
24  pub fn new() -> Self {
25    let mut tracker_oscillators = Vec::<u64>::new();
26    for _ in 0..10 {
27      tracker_oscillators.push(0);
28    }
29    Self {
30      header              : TelemetryPacketHeader::new(),
31      creation_time       : 0,
32      event_id            : 0,
33      tracker_hits        : Vec::<TrackerHit>::new(),
34      tracker_oscillators : tracker_oscillators,
35      tof_event           : TofEvent::new(),
36      raw_data            : Vec::<u8>::new(),
37      flags0              : 0,
38      flags1              : 1,
39      version             : 0, 
40    }
41  }
42
43  /// Restore position information from database
44  #[cfg(feature="database")]
45  pub fn dehydrate(&mut self, tof_paddles : &HashMap<u8,TofPaddle>, trk_strips : &HashMap<u32, TrackerStrip>) {
46    self.tof_event.set_paddles(tof_paddles);
47    for h in &mut self.tracker_hits {  
48      h.set_coordinates(trk_strips);
49    }
50  }
51 
52  #[cfg(feature="database")]
53  pub fn mask_strips(&mut self, masks : &HashMap<u32, TrackerStripMask>) {
54    let mut clean_hits = Vec::<TrackerHit>::with_capacity(self.tracker_hits.len());
55    for h in &self.tracker_hits {
56      if !masks.contains_key(&h.get_stripid()) {
57        warn!("We don't have a mask information for strip id {}", h.get_stripid());
58        continue;
59      }
60      if masks[&h.get_stripid()].active {
61        clean_hits.push(h.clone());
62      }
63    }
64    self.tracker_hits = clean_hits;
65  }
66
67
68  /// Applies a cut based on adc values
69  #[cfg(feature="database")]
70  pub fn apply_signal_cut(&mut self, cut : f32, pedestals : &HashMap<u32, TrackerStripPedestal>) {
71    let mut clean_hits = Vec::<TrackerHit>::with_capacity(self.tracker_hits.len());
72    for h in &self.tracker_hits {
73      if !pedestals.contains_key(&h.get_stripid()) {
74        warn!("We don't have pedestal information for strip id {}", h.get_stripid());
75        continue;
76      }
77      let ped = &pedestals[&h.get_stripid()];
78      if (h.adc as f32) - ped.pedestal_mean > (cut * ped.pedestal_sigma){
79        clean_hits.push(h.clone());
80      }
81    }
82    self.tracker_hits = clean_hits;
83  }
84 
85  /// Calculate the absolute common noise (adc)
86  #[cfg(feature="database")]
87  pub fn cmn_noise(hit       : &TrackerHit, 
88                   pedestals : &HashMap<u32, TrackerStripPedestal>,
89                   cmn_noise : &HashMap<u32, TrackerStripCmnNoise>) -> Option<f32> {
90    let stripid = hit.get_stripid();
91    if !cmn_noise.contains_key(&stripid) {
92      warn!("We don't have pedestal information for strip id {}", stripid);
93      return None;
94    }
95    if !pedestals.contains_key(&stripid) {
96      warn!("We don't have pedestal information for strip id {}", stripid);
97      return None;
98    }
99    let mut cmn_level = 0.0f32;
100    let adc_ped_sub   = hit.adc as f32 - pedestals[&stripid].pedestal_mean; 
101    if adc_ped_sub < 400.0 {
102      cmn_level = cmn_noise[&stripid].common_level(hit.adc as f32);
103    }
104    return Some(adc_ped_sub - cmn_noise[&stripid].gain * cmn_level);
105  }
106
107  /// Calculate the energy deposition
108  #[cfg(feature="database")]
109  pub fn get_trk_energy(adc : f32, tf : &TrackerStripTransferFunction) -> f32 {
110    let mut energy = 0.0f32;
111    let mut adc_m  = adc; 
112    if adc_m <= 0.0 {
113      return energy;
114    }
115    if adc_m > 1600.0 {
116      adc_m = 1600.0;
117    }
118    #[allow(non_snake_case)]
119    let mV2keV  = 0.841f32;
120    // the max and min range are basically defined by the 
121    // polynominal [0-1600]
122    let voltage = tf.transfer_fn(adc_m);
123    println!("voltage {}", voltage);
124    energy  = voltage*mV2keV;
125    energy /= 1000.0;
126    println!("adc : {} , energy {}", adc_m, energy);
127    return energy;
128  }
129
130  #[cfg(feature="database")]
131  pub fn calibrate_tracker(&mut self, 
132                           remove_cmn_noise : bool,
133                           pedestals        : &HashMap<u32, TrackerStripPedestal>,
134                           transfer_fn      : &HashMap<u32, TrackerStripTransferFunction>,
135                           cmn_noise        : &HashMap<u32, TrackerStripCmnNoise>) {
136    //println!("Will remove CMN noise {}", remove_cmn_noise);
137    for h in &mut self.tracker_hits {
138      let stripid = h.get_stripid();
139      //println!("Running TRK calibration for strip {}", stripid);
140      if !pedestals.contains_key(&stripid) {
141        warn!("Pedestal map does not contain strip {}. Will not calculate energy!", stripid);
142        continue;
143      }
144      if !transfer_fn.contains_key(&stripid) {
145        warn!("Transfer fn for strip {} not available. Will not calculate energy!", stripid);
146        continue;
147      }
148      let adc_no_ped = h.adc as f32 - pedestals[&stripid].pedestal_mean;
149      //println!("ADC NO PED {}", adc_no_ped);
150      if remove_cmn_noise {
151        match Self::cmn_noise(&h, pedestals, cmn_noise) { 
152          None => {
153            h.energy = Self::get_trk_energy(adc_no_ped, &transfer_fn[&stripid]); 
154          }
155          Some(cmn) => {
156            h.energy = Self::get_trk_energy(cmn, &transfer_fn[&stripid]);
157          }
158        }    
159      } else {
160        h.energy = Self::get_trk_energy(adc_no_ped, &transfer_fn[&stripid]);
161      }
162    }
163  }
164}
165
166
167impl TelemetryPackable for TelemetryEvent {
168  // trivial for TelemetryEvent, since the default 
169  // already contains the packet types
170}
171
172impl Serialization for TelemetryEvent {
173  
174  fn from_bytestream(stream : &Vec<u8>,
175                     pos    : &mut usize)
176    -> Result<Self, SerializationError> {
177    let mut me       = Self::new();
178    let version      = parse_u8(stream, pos);
179    me.version       = version;
180    //println!("_version {}", _version);
181    me.flags0         = parse_u8(stream, pos);
182    // skip a bunch of Alex newly implemented things
183    // FIXME
184    if version == 0 {
185      me.flags1      = parse_u8(stream, pos);
186    } else {
187      *pos += 8;
188    }
189
190    me.event_id       = parse_u32(stream, pos);
191    //println!("EVENT ID {}", me.event_id);
192    let _tof_delim    = parse_u8(stream, pos);
193    //println!("TOF delim : {}", _tof_delim);
194    if stream.len() <= *pos + 2 {
195      error!("Not able to parse merged event!");
196      return Err(SerializationError::StreamTooShort);
197    }
198   let num_tof_bytes = parse_u16(stream, pos) as usize;
199    //println!("Num TOF bytes : {}", num_tof_bytes);
200    if stream.len() < *pos+num_tof_bytes {
201      error!("Not enough bytes for TOF packet! Expected {}, seen {}", *pos+num_tof_bytes as usize, stream.len());
202      return Err(SerializationError::StreamTooShort); 
203    }
204    let pos_before = *pos;
205    if num_tof_bytes != 0 {
206      let tof_pack   = TofPacket::from_bytestream(stream, pos)?;
207      let ts         = tof_pack.unpack::<TofEvent>()?;
208    // sanity check - is tofpacket as long as num_tof_bytes lets us believe?
209      me.tof_event = ts;
210    }
211    if pos_before + num_tof_bytes != *pos {
212      error!("Byte misalignment. Expected {num_tof_bytes}, got {pos} - {pos_before}"); 
213      return Err(SerializationError::WrongByteSize);
214    }
215    let trk_delim    = parse_u8(stream, pos);
216
217    //println!("TRK delim {}", trk_delim);
218    if trk_delim != 0xbb {
219      return Err(SerializationError::HeadInvalid);
220    }
221    if version == 1 {
222      let num_trk_hits = parse_u16(stream, pos);
223      if (*pos + (num_trk_hits as usize)*4 ) > stream.len() {
224        return Err(SerializationError::StreamTooShort);
225      }
226      for _ in 0..num_trk_hits { 
227        let mut hit  = TrackerHit::new();
228        let strip_id = parse_u16(stream, pos);
229        let adc      = parse_u16(stream, pos);
230        hit.channel  = strip_id & 0b11111;
231        hit.module   = (strip_id >> 5) & 0b111;
232        hit.row      = (strip_id >> 8) & 0b111;
233        hit.layer    = (strip_id >> 11) & 0b1111;
234        hit.adc      = adc;
235        me.tracker_hits.push(hit);
236      }
237      // oscillators
238      let oscillators_delimiter = parse_u8(stream, pos);
239      if oscillators_delimiter != 0xcc {
240        return Err(SerializationError::HeadInvalid);
241      }
242      let osc_flags = parse_u8(stream, pos);
243      let mut oscillator_idx = Vec::<u8>::new();
244      for j in 0..8 {
245        if (osc_flags >> j & 0b1) > 0 {
246          oscillator_idx.push(j)
247        }
248      }
249      if (*pos + oscillator_idx.len()*6) > stream.len() {
250        return Err(SerializationError::StreamTooShort);
251      }
252      for idx in oscillator_idx.iter() {
253        let lower = parse_u32(stream, pos);
254        let upper = parse_u16(stream, pos);
255        let osc : u64 = (upper as u64) << 32 | (lower as u64);
256        me.tracker_oscillators[*idx as usize] = osc;
257      }
258    } else if version == 0 {
259      error!("Unsupported {version}!");
260      return Err(SerializationError::UnsupportedVersion);
261    } else {
262      error!("Unsuported version {version}!");
263      return Err(SerializationError::UnsupportedVersion);
264    } 
265    Ok(me)
266  }
267}
268
269impl fmt::Display for TelemetryEvent {
270  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
271    let mut repr     = String::from("<TelemetryEvent:");
272    let tof_str  = format!("\n  {}", self.tof_event);
273    let mut good_hits = 0;
274    if self.version == 0 {
275      repr += "\n VERSION 0 NOT SUPPORTED!!";
276    } else if self.version == 1 {
277      for _ in &self.tracker_hits {
278        good_hits += 1;
279      }
280    }
281    repr += &(format!("  {}", self.header));
282    repr += "\n  ** ** ** MERGED  ** ** **";
283    repr += &(format!("\n  version         {}", self.version));
284    repr += &(format!("\n  event ID        {}", self.event_id));  
285    if self.version == 0 {
286      repr += "\n VERSION 0 NOT SUPPORTED!!"; 
287    }
288    repr += "\n  ** ** ** TRACKER ** ** **";
289    if self.version == 0 {
290      repr += "\n VERSION 0 NOT SUPPORTED!!"; 
291    } else if self.version == 1 {
292      repr += &(format!("\n  Trk oscillators {:?}", self.tracker_oscillators)); 
293    }
294    repr += &(format!("\n  N Good Trk Hits {}", good_hits));
295    repr += &tof_str;
296    write!(f,"{}", repr)
297  }
298}
299
300//----------------------------------------
301
302#[cfg(feature="pybindings")]
303#[pymethods]
304impl TelemetryEvent {
305
306  #[getter]
307  #[pyo3(name="version")]
308  fn version_py(&self) -> u8 {
309    self.version
310  }
311  
312  #[staticmethod]
313  #[pyo3(name = "get_trk_energy")]
314  fn get_trk_energy_py(adc : f32, tf : &TrackerStripTransferFunction) -> f32 {
315    Self::get_trk_energy(adc, tf)
316  }
317
318  #[getter]
319  fn get_header(&self) -> TelemetryPacketHeader {
320    self.header
321  }
322
323  #[getter]
324  fn tracker(&self) -> PyResult<Vec<TrackerHit>> {
325    Ok(self.tracker_hits.clone())
326  }
327
328  #[getter]
329  fn get_event_id(&self) -> u32 {
330    self.event_id
331  }
332
333  // FIXME - do this with bound
334  #[getter]
335  fn get_tof(&self) -> PyResult<TofEvent> {
336    Ok(self.tof_event.clone())
337  }
338
339  #[getter]
340  fn tracker_pointcloud(&self) -> Vec<(f32, f32, f32, f32, f32)> {
341    let mut pts = Vec::<(f32,f32,f32,f32,f32)>::new();
342    for h in &self.tracker_hits {
343      // uses adc
344      // FIXME - factor 10!
345      let pt = (10.0*h.x, 10.0*h.y, 10.0*h.z, f32::NAN, h.adc as f32);
346      pts.push(pt);
347    }
348    pts
349  }
350
351  /// Populate a merged event from a TelemetryPacket.
352  ///
353  /// Telemetry packet type should be 90 (MergedEvent)
354  #[staticmethod]
355  fn from_telemetrypacket(packet : TelemetryPacket) -> PyResult<Self> {
356    match Self::from_bytestream(&packet.payload, &mut 0) {
357      Ok(mut event) => {
358        event.header = packet.header.clone();
359        return Ok(event);
360      }
361      Err(err) => {
362        return Err(PyValueError::new_err(err.to_string()));
363      }  
364    }
365  }
366
367//  #[cfg(feature="database")]
368//  #[pyo3(name="mask_strips")]
369//  pub fn mask_strips_py(&mut self, masks : &HashMap<u32, TrackerStripMask>) {
370//  }
371//
372//  #[cfg(feature="database")]
373//  #[pyo3(name="remove_cmn_noise")]
374//  pub fn remove_cmn_noise_py(&mut self, cmn_noise : &HashMap<u32, TrackerStripCmnNoise>) {
375//  }
376//
377//  #[cfg(feature="database")]
378//  #[pyo3(name="calibrate_tracker")]
379//  pub fn calibrate_tracker_py(&mut self, 
380//                              pedestals   : &HashMap<u32, TrackerStripPedestal>,
381//                              transfer_fn : &HashMap<u32, TrackerStripTransferFunction>) {
382//  }
383
384
385}
386
387#[cfg(feature="pybindings")]
388pythonize!(TelemetryEvent);
389