gondola_core/events/
rb_event_header.rs

1// This file is part of gaps-online-software and published 
2// under the GPLv3 license
3
4use crate::prelude::*;
5use colored::Colorize;
6
7/// The RBEvent header gets generated once per event
8/// per RB. 
9/// Contains information about event id, timestamps,
10/// etc.
11#[cfg_attr(feature="pybindings",pyclass)]
12#[derive(Debug, Copy, Clone, PartialEq)]
13pub struct RBEventHeader { 
14  /// Readoutboard ID - should be in the range 1-50
15  /// not consecutive, there are some missing.
16  /// In general, we have 40 boards
17  pub rb_id                : u8   ,
18  /// The event ID as sent from the MTB or self-generated
19  /// if not latched to the MTB
20  pub event_id             : u32  ,
21  /// The DRS stop cell. This is vital information which is
22  /// needed for the calibration
23  pub stop_cell            : u16  , 
24  /// RBPaddleID - component 
25  pub pid_ch12             : u8,
26  /// RBPaddleID - component
27  pub pid_ch34             : u8,
28  /// RBPaddleID - component
29  pub pid_ch56             : u8,
30  /// RBPaddleID - component
31  pub pid_ch78             : u8,
32  /// RBPaddleID - component
33  pub pid_ch_order         : u8,
34  /// Reserved
35  pub rsvd1                : u8,
36  /// Reserved
37  pub rsvd2                : u8,
38  /// Reserved
39  pub rsvd3                : u8,
40  /// The adc value for the temperature
41  /// of the FPGA
42  pub fpga_temp             : u16, 
43  /// DRS deadtime as read out from the 
44  /// register
45  pub drs_deadtime          : u16,
46  pub timestamp32           : u32,
47  pub timestamp16           : u16,
48  /// Store the drs_deadtime instead 
49  /// of the fpga temperature
50  pub deadtime_instead_temp : bool,
51  /// The status byte contains information about lsos of lock
52  /// and event fragments and needs to be decoded
53  pub status_byte               : u8,
54  /// The channel mask is 9bit for the 9 channels.
55  /// This leaves 7 bits of space so we actually 
56  /// hijack that for the version information 
57  /// 
58  /// Bit 15 will be set 1 in case we are sending
59  /// the DRS_DEADTIME instead of FPGA TEMP
60  ///
61  /// FIXME - make this proper and use ProtocolVersion 
62  ///         instead
63  pub channel_mask             : u16, 
64}
65
66impl RBEventHeader {
67
68  pub fn new() -> Self {
69    Self {
70      rb_id                 : 0,  
71      status_byte           : 0, 
72      event_id              : 0,  
73      channel_mask          : 0,  
74      stop_cell             : 0,  
75      pid_ch12              : 0,
76      pid_ch34              : 0,
77      pid_ch56              : 0,
78      pid_ch78              : 0,
79      pid_ch_order          : 0,
80      rsvd1                 : 0,
81      rsvd2                 : 0,
82      rsvd3                 : 0,
83      fpga_temp             : 0,  
84      drs_deadtime          : 0,
85      timestamp32           : 0,
86      timestamp16           : 0,
87      deadtime_instead_temp : false,
88    }
89  }
90
91  /// Set the channel mask with the 9bit number
92  ///
93  /// Set bit 15 to either 1 or 0 depending on
94  /// deadtime_instead_temp
95  pub fn set_channel_mask(&mut self, channel_mask : u16) {
96    if self.deadtime_instead_temp {
97      self.channel_mask = 2u16.pow(15) | channel_mask;
98    } else {
99      self.channel_mask = channel_mask;
100    }
101  }
102
103  /// Just return the channel mask and strip of 
104  /// the part which contains the information about
105  /// drs deadtime or fpga temp
106  pub fn get_channel_mask(&self) -> u16 {
107    self.channel_mask & 0x1ff 
108  }
109
110  /// Get the channel mask from a bytestream.
111  /// 
112  /// This takes into acount that bit 15 is 
113  /// used to convey information about if we
114  /// stored the drs temperature or deadtime
115  pub fn parse_channel_mask(ch_mask : u16) -> (bool, u16) {
116    let channel_mask          : u16;
117    let deadtime_instead_temp : bool 
118      = ch_mask >> 15 == 1;
119    channel_mask = ch_mask & 0x1ff;
120    (deadtime_instead_temp, channel_mask)
121  }
122
123  /// Only get the eventid from a binary stream
124  pub fn extract_eventid_from_rbheader(stream :&Vec<u8>) -> u32 {
125    // event id is 18 bytes in (including HEAD bytes)
126    // event id is 3 bytes in (including HEAD bytes)
127    let event_id = parse_u32(stream, &mut 3); // or should it be 5?
128    event_id
129  }
130  
131  pub fn is_event_fragment(&self) -> bool {
132    self.status_byte & 1 > 0
133  }
134  
135  pub fn drs_lost_trigger(&self) -> bool {
136    (self.status_byte >> 1) & 1 > 0
137  }
138
139  pub fn lost_lock(&self) -> bool {
140    (self.status_byte >> 2) & 1 > 0
141  }
142
143  pub fn lost_lock_last_sec(&self) -> bool {
144    (self.status_byte >> 3) & 1 > 0
145  }
146
147  pub fn is_locked(&self) -> bool {
148    !self.lost_lock()
149  }
150  
151  pub fn is_locked_last_sec(&self) -> bool {
152    !self.lost_lock_last_sec()
153  }
154  
155  /// extract lock, drs busy and fpga temp from status field
156  pub fn parse_status(&mut self, status_bytes : u16) {
157    // status byte is only 4bit really
158    self.status_byte        = (status_bytes & 0xf) as u8;
159    self.fpga_temp = status_bytes >> 4;
160  }
161
162  /// Get the temperature value (Celsius) from the fpga_temp adc.
163  pub fn get_fpga_temp(&self) -> f32 {
164    let zynq_temp = (((self.fpga_temp & 4095) as f32 * 503.975) / 4096.0) - 273.15;
165    zynq_temp
166  }
167
168  /// Check if the channel 9 is present in the 
169  /// channel mask
170  pub fn has_ch9(&self) -> bool {
171    self.channel_mask & 256 > 0
172  }
173
174  pub fn get_rbpaddleid(&self) -> RBPaddleID {
175    let mut pid = RBPaddleID::new();
176    pid.paddle_12     = self.pid_ch12;
177    pid.paddle_34     = self.pid_ch34;
178    pid.paddle_56     = self.pid_ch56;
179    pid.paddle_78     = self.pid_ch78;
180    pid.channel_order = self.pid_ch_order;
181    pid                    
182  }
183  
184  pub fn set_rbpaddleid(&mut self, pid : &RBPaddleID) {
185    self.pid_ch12     = pid.paddle_12;
186    self.pid_ch34     = pid.paddle_34;
187    self.pid_ch56     = pid.paddle_56;
188    self.pid_ch78     = pid.paddle_78;
189    self.pid_ch_order = pid.channel_order;
190  }
191
192  /// Decode the channel mask into channel ids.
193  ///
194  /// The channel ids inside the memory representation
195  /// of the RB Event data ("blob") are from 0-7
196  ///
197  /// We keep ch9 seperate.
198  pub fn get_channels(&self) -> Vec<u8> {
199    let mut channels = Vec::<u8>::with_capacity(8);
200    for k in 0..9 {
201      if self.channel_mask & (1 << k) > 0 {
202        channels.push(k);
203      }
204    }
205    channels
206  }
207
208  /// Get the active paddles
209  pub fn get_active_paddles(&self) -> Vec<(u8,bool)> {
210    // FIXME - help. Make this nicer. My brain is fried 
211    // at this point. Please. I'll be thankful.
212    let mut active_paddles = Vec::<(u8,bool)>::new();
213    let active_channels = self.get_channels();
214    let pid             = self.get_rbpaddleid();
215    let mut ch0_pair_done = false;
216    let mut ch2_pair_done = false;
217    let mut ch4_pair_done = false;
218    let mut ch6_pair_done = false;
219    for ch in active_channels {
220      if (ch == 0 || ch == 1) && !ch0_pair_done {
221        active_paddles.push(pid.get_paddle_id(ch));
222        ch0_pair_done = true;
223      }
224      if (ch == 2 || ch == 3) && !ch2_pair_done {
225        active_paddles.push(pid.get_paddle_id(ch));
226        ch2_pair_done = true;
227      }
228      if (ch == 4 || ch == 5) && !ch4_pair_done {
229        active_paddles.push(pid.get_paddle_id(ch));
230        ch4_pair_done = true;
231      }
232      if (ch == 6 || ch == 7) && !ch6_pair_done {
233        active_paddles.push(pid.get_paddle_id(ch));
234        ch6_pair_done = true;
235      }
236    }
237    active_paddles
238  }
239
240  /// Get the number of data channels + 1 for ch9
241  pub fn get_nchan(&self) -> usize {
242    self.get_channels().len()
243  }
244  
245  pub fn get_timestamp48(&self) -> u64 {
246    ((self.timestamp16 as u64) << 32) | self.timestamp32 as u64
247  }
248}
249
250impl Default for RBEventHeader {
251  fn default() -> Self {
252    Self::new()
253  }
254}
255
256
257impl fmt::Display for RBEventHeader {
258  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259    let mut repr = String::from("<RBEventHeader:");
260    repr += &(format!("\n  RB ID            {}",self.rb_id               )); 
261    repr += &(format!("\n  event id         {}",self.event_id            ));  
262    repr += &(format!("\n  ch mask          {}",self.channel_mask        ));  
263    repr += &(format!("\n  has ch9          {}",self.has_ch9()           )); 
264    repr += &(format!("\n  ch mapping       {}",self.get_rbpaddleid()    ));
265    if self.deadtime_instead_temp {
266      repr += &(format!("\n  DRS deadtime          : {:.2}", self.drs_deadtime));
267    } else {
268      repr += &(format!("\n  FPGA T [\u{00B0}C]    : {:.2}", self.get_fpga_temp()));
269    }
270    repr += &(format!("\n  timestamp32      {}", self.timestamp32            )); 
271    repr += &(format!("\n  timestamp16      {}", self.timestamp16            )); 
272    repr += &(format!("\n   |-> timestamp48 {}", self.get_timestamp48()      )); 
273    repr += &(format!("\n  stop cell        {}", self.stop_cell              )); 
274    let mut perfect = true;
275    if self.drs_lost_trigger() {
276      repr += &"\n  !! DRS4 REPORTS LOST TRIGGER!".red().bold();
277      perfect = false;
278    }
279    if self.is_event_fragment() {
280      repr += &"\n  !! EVENT FRAGMENT!".red().bold();
281      perfect = false;
282    }
283    if self.lost_lock() {
284      repr += &"\n  !! RB CLOCK IS NOT LOCKED!".yellow().bold();
285      perfect = false;
286    }
287    if self.lost_lock_last_sec() {
288      repr += &"\n  !! RB CLOCK HAS LOST ITS LOCK WITHIN THE LAST SECOND!".yellow().bold();
289      perfect = false;
290    }
291    if perfect {
292      repr += &"\n  -- locked: YES, locked last second; YES, no event fragemnet, and no lost trigger!".green();
293    }
294    repr += ">";
295    write!(f, "{}", repr)
296  }
297}
298
299impl Serialization for RBEventHeader {
300  
301  const HEAD : u16   = 0xAAAA;
302  const TAIL : u16   = 0x5555;
303  const SIZE : usize = 30; // size in bytes with HEAD and TAIL
304
305  fn from_bytestream(stream : &Vec<u8>, pos : &mut usize)
306    -> Result<Self, SerializationError> {
307    let mut header  = Self::new();
308    Self::verify_fixed(stream, pos)?;
309    header.rb_id                 = parse_u8 (stream, pos);  
310    header.event_id              = parse_u32(stream, pos);  
311    let ch_mask                  = parse_u16(stream, pos);
312    let (deadtime_instead_temp, channel_mask)  
313      = Self::parse_channel_mask(ch_mask);
314    header.deadtime_instead_temp = deadtime_instead_temp;
315    header.set_channel_mask(channel_mask);
316    header.status_byte         = parse_u8 (stream, pos);
317    header.stop_cell             = parse_u16(stream, pos);  
318    header.pid_ch12              = parse_u8(stream, pos);
319    header.pid_ch34              = parse_u8(stream, pos);
320    header.pid_ch56              = parse_u8(stream, pos);
321    header.pid_ch78              = parse_u8(stream, pos);
322    header.pid_ch_order          = parse_u8(stream, pos);
323    header.rsvd1                 = parse_u8(stream, pos);
324    header.rsvd2                 = parse_u8(stream, pos);
325    header.rsvd3                 = parse_u8(stream, pos);
326    if deadtime_instead_temp {
327      header.drs_deadtime        = parse_u16(stream, pos);
328    } else {
329      header.fpga_temp           = parse_u16(stream, pos);
330    }
331    header.timestamp32           = parse_u32(stream, pos);
332    header.timestamp16           = parse_u16(stream, pos);
333    *pos += 2; // account for tail earlier 
334    Ok(header) 
335  }
336  
337
338  fn to_bytestream(&self) -> Vec<u8> {
339    let mut stream = Vec::<u8>::with_capacity(Self::SIZE);
340    stream.extend_from_slice(&Self::HEAD.to_le_bytes());
341    stream.extend_from_slice(&self.rb_id             .to_le_bytes());
342    stream.extend_from_slice(&self.event_id          .to_le_bytes());
343    let ch_mask = ((self.deadtime_instead_temp as u16) << 15) | self.get_channel_mask();
344    stream.extend_from_slice(&ch_mask                .to_le_bytes());
345    stream.extend_from_slice(&self.status_byte       .to_le_bytes());
346    stream.extend_from_slice(&self.stop_cell         .to_le_bytes());
347    stream.push(self.pid_ch12    );
348    stream.push(self.pid_ch34    );
349    stream.push(self.pid_ch56    );
350    stream.push(self.pid_ch78    );
351    stream.push(self.pid_ch_order);
352    stream.push(self.rsvd1       );
353    stream.push(self.rsvd2       );
354    stream.push(self.rsvd3       );
355    if self.deadtime_instead_temp {
356      stream.extend_from_slice(&self.drs_deadtime    .to_le_bytes());
357    } else {
358      stream.extend_from_slice(&self.fpga_temp       .to_le_bytes());
359    }
360    stream.extend_from_slice(&self.timestamp32       .to_le_bytes());
361    stream.extend_from_slice(&self.timestamp16       .to_le_bytes());
362    stream.extend_from_slice(&RBEventHeader::TAIL.to_le_bytes());
363    stream
364  }
365}
366
367#[cfg(feature = "random")]
368impl FromRandom for RBEventHeader {
369    
370  fn from_random() -> Self {
371    let mut header = RBEventHeader::new();
372    let mut rng = rand::rng();
373
374    header.rb_id                 = rng.random::<u8>();    
375    header.event_id              = rng.random::<u32>();   
376    header.status_byte           = rng.random::<u8>();    
377    header.stop_cell             = rng.random::<u16>();   
378    header.pid_ch12              = rng.random::<u8>();
379    header.pid_ch34              = rng.random::<u8>();
380    header.pid_ch56              = rng.random::<u8>();
381    header.pid_ch78              = rng.random::<u8>();
382    header.pid_ch_order          = rng.random::<u8>();
383    header.rsvd1                 = rng.random::<u8>();
384    header.rsvd2                 = rng.random::<u8>();
385    header.rsvd3                 = rng.random::<u8>();
386    header.deadtime_instead_temp = rng.random::<bool>();
387    if header.deadtime_instead_temp {
388      header.drs_deadtime          = rng.random::<u16>();
389    } else {
390      header.fpga_temp             = rng.random::<u16>();  
391    }
392    // make sure the generated channel mask is valid!
393    let ch_mask                  = rng.random::<u16>() & 0x1ff;
394    header.set_channel_mask(ch_mask);
395    header.timestamp32           = rng.random::<u32>();
396    header.timestamp16           = rng.random::<u16>();
397    header
398  }
399}
400
401//------------------------------------------
402
403#[cfg(feature="pybindings")]
404#[pymethods]
405impl RBEventHeader {
406  
407  #[getter]
408  #[pyo3(name="rb_id")]
409  fn rb_id_py(&self) -> u8 {
410    self.rb_id
411  }
412  
413  #[getter]
414  #[pyo3(name="event_id")]
415  fn event_id_py(&self) -> u32 {
416    self.event_id
417  }
418  
419  //#[getter]
420  //fn status_byte(&self) -> u8 {
421  //  self.status_byte
422  //}
423  
424  #[getter]
425  #[pyo3(name="channel_mask")]
426  fn channel_mask_py(&self) -> u16 {
427    self.get_channel_mask()
428  }
429  
430  #[getter]
431  #[pyo3(name="stop_cell")]
432  fn stop_cell_py(&self) -> u16 {
433    self.stop_cell
434  }
435  
436  #[getter]
437  #[pyo3(name="fpga_temp")]
438  fn fpga_temp_py(&self) -> f32 {
439    self.get_fpga_temp()
440  }
441  
442  #[getter]
443  #[pyo3(name="drs_deadtime")]
444  fn drs_deadtime_py(&self) -> u16 {
445    self.drs_deadtime 
446  }
447
448  #[getter]
449  #[pyo3(name="timestamp32")]
450  fn timestamp32_py(&self) -> u32 {
451    self.timestamp32
452  }
453  
454  #[getter]
455  #[pyo3(name="timestamp16")]
456  fn timestamp16_py(&self) -> u16 {
457    self.timestamp16
458  }
459
460  //  pub ch9_amp: u16,
461  //  pub ch9_freq: u16,
462  //  pub ch9_phase: u32,
463  #[pyo3(name="get_channels")]
464  fn get_channels_py(&self) -> Vec<u8> {
465    self.get_channels()
466  }
467
468  #[getter]
469  #[pyo3(name="is_event_fragment")]
470  pub fn is_event_fragment_py(&self) -> bool {
471    self.is_event_fragment()
472  }
473
474  #[getter]
475  #[pyo3(name="drs_lost_trigger")]
476  pub fn drs_lost_trigger_py(&self) -> bool {
477    self.drs_lost_trigger()
478  }
479
480  #[getter]
481  #[pyo3(name="lost_lock")]
482  fn lost_lock_py(&self) -> bool {
483    self.lost_lock()
484  }
485
486  #[getter]
487  #[pyo3(name="lost_lock_last_sec")]
488  fn lost_lock_last_sec_py(&self) -> bool {
489    self.lost_lock_last_sec()
490  }
491
492  #[getter]
493  #[pyo3(name="is_locked")]
494  fn is_locked_py(&self) -> bool {
495    self.is_locked()
496  }
497
498  #[getter]
499  #[pyo3(name="is_locked_last_sec")]
500  fn is_locked_last_sec_py(&self) -> bool {
501    self.is_locked_last_sec()
502  }
503}
504
505#[cfg(feature="pybindings")]
506pythonize!(RBEventHeader);
507
508//------------------------------------------
509
510#[test]
511fn serialization_rbeventheader() {
512  for _ in 0..100 {
513    let mut pos = 0usize;
514    let head = RBEventHeader::from_random();
515    //println!("{}",  head);
516    let stream = head.to_bytestream();
517    assert_eq!(stream.len(), RBEventHeader::SIZE);
518    let test = RBEventHeader::from_bytestream(&stream, &mut pos).unwrap();
519    println!("{}", test);
520    assert_eq!(pos, RBEventHeader::SIZE);
521    assert_eq!(head, test);
522    assert_eq!(head.lost_lock()         , test.lost_lock());
523    assert_eq!(head.lost_lock_last_sec(), test.lost_lock_last_sec());
524    assert_eq!(head.drs_lost_trigger()  , test.drs_lost_trigger());
525    assert_eq!(head, test);
526  }
527}
528