tof_dataclasses/events/
tof_hit.rs

1use std::fmt;
2use std::f32::consts::PI;
3
4use half::f16;
5
6use crate::errors::SerializationError;
7use crate::serialization::{
8  parse_u8,
9  parse_u16,
10  parse_f16,
11  Serialization
12};
13use crate::ProtocolVersion;
14
15use crate::constants::{
16  C_LIGHT_PADDLE,
17};
18
19#[cfg(feature="random")]
20use rand::Rng;
21
22#[cfg(feature="database")]
23use crate::database::Paddle;
24
25///// We will save the values for the peak heigth, time and charge
26///// as u16. The calculations yield f32 though. We need to convert
27///// them using MIN/MAX and a range
28//const MAX_PEAK_HEIGHT      : f32 = 150.0; //mV
29////const MIN_PEAK_HEIGHT      : f32 = 0.0;
30//const U16TOF32_PEAK_HEIGHT : f32 = MAX_PEAK_HEIGHT/(u16::MAX as f32);
31//const F32TOU16_PEAK_HEIGHT : u16 = ((u16::MAX as f32)/MAX_PEAK_HEIGHT) as u16;
32//const MAX_PEAK_CHARGE      : f32 = 100.0; 
33////const MIN_PEAK_CHARGE      : f32 = 0.0;
34//const U16TOF32_PEAK_CHARGE : f32 = MAX_PEAK_CHARGE/(u16::MAX as f32);
35//const F32TOU16_PEAK_CHARGE : u16 = ((u16::MAX as f32)/MAX_PEAK_CHARGE) as u16;
36//const MAX_PEAK_TIME        : f32 = 500.0;
37////const MIN_PEAK_TIME        : f32 = 0.0;
38//const U16TOF32_PEAK_TIME   : f32 = MAX_PEAK_TIME/(u16::MAX as f32);
39//const F32TOU16_PEAK_TIME   : u16 = ((u16::MAX as f32)/MAX_PEAK_TIME) as u16;
40//const U16TOF32_T0          : f32 = MAX_PEAK_TIME/(u16::MAX as f32);
41//const F32TOU16_T0          : u16 = ((u16::MAX as f32)/MAX_PEAK_TIME) as u16;
42//const U16TOF32_POS_ACROSS  : f32 = 1800.0/(u16::MAX as f32);
43//const F32TOU16_POS_ACROSS  : u16 = ((u16::MAX as f32)/1800.0) as u16;
44//const U16TOF32_EDEP        : f32 = 180.0/(u16::MAX as f32);
45//const F32TOU16_EDEP        : u16 = ((u16::MAX as f32)/100.0) as u16;
46
47
48/// Waveform peak
49///
50/// Helper to form TofHits
51#[derive(Debug,Copy,Clone,PartialEq)]
52pub struct Peak {
53  pub paddle_end_id : u16,
54  pub time          : f32,
55  pub charge        : f32,
56  pub height        : f32
57}
58
59impl Peak {
60  pub fn new() -> Self {
61    Self {
62      paddle_end_id : 40,
63      time          : 0.0,
64      charge        : 0.0,
65      height        : 0.0,
66    }
67  }
68}
69
70impl Default for Peak {
71  fn default() -> Self {
72    Self::new()
73  }
74}
75
76impl fmt::Display for Peak {
77  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78    write!(f, "<Peak:
79  p_end_id : {}
80  time     : {}
81  charge   : {}
82  height   : {}>",
83            self.paddle_end_id,
84            self.time,
85            self.charge,
86            self.height)
87  }
88}
89
90/// Comprehensive paddle information
91///
92/// Results of the (online) waveform analysis
93///
94/// A and B are the different ends of the paddle
95///
96#[derive(Debug,Copy,Clone,PartialEq)]
97pub struct TofHit {
98  
99  /// The ID of the paddle in TOF notation
100  /// (1-160)
101  pub paddle_id      : u8,
102  pub time_a         : f16,
103  pub time_b         : f16,
104  pub peak_a         : f16,
105  pub peak_b         : f16,
106  pub charge_a       : f16,
107  pub charge_b       : f16,
108  
109  /// The paddle length will not get serialized
110  /// and has to be set after the hit has been 
111  /// created
112  pub paddle_len     : f32,
113  // These will not get serialized
114  /// The Harting cable length to the RB will not get
115  /// serialized and has to be set after the hit has been 
116  /// created
117  pub cable_len      : f32,
118  /// Another calibration constant - a non-understood
119  /// timing offsets
120  pub timing_offset  : f32,
121  /// The coordinates will not get serialized
122  /// and has to be set after the hit has been 
123  /// created
124  pub x              : f32,
125  pub y              : f32,
126  pub z              : f32,
127  /// cable times will get populated from the db
128  pub coax_cable_time: f32,
129  pub hart_cable_time: f32,
130  /// normalized t0, where we have the phase difference
131  /// limited to -pi/2 -> pi/2
132  pub event_t0       : f32,
133
134  // deprecated values (prior to V1 version)
135  pub timestamp32    : u32,
136  pub timestamp16    : u16,
137  pub ctr_etx        : u8,
138  pub charge_min_i   : u16,
139  
140  /// DEPRECATED
141  /// Reconstructed particle interaction position
142  /// across the paddle
143  pub pos_across     : u16,
144  /// DEPRECATED
145  /// Reconstructed particle interaction time
146  pub t0             : u16,
147  
148  // new values
149  pub reserved       : u8,
150  // only 2 bytes of version
151  // are used
152  pub version        : ProtocolVersion,
153  // for now, but we want to use half instead
154  pub baseline_a     : f16,
155  pub baseline_a_rms : f16,
156  pub baseline_b     : f16,
157  pub baseline_b_rms : f16,
158  // phase of the sine fit
159  pub phase          : f16,
160  // fields which won't get 
161  // serialized
162  pub valid          : bool,
163  // for debugging purposes
164  pub ftime_a        : f32,
165  pub ftime_b        : f32,
166  pub fpeak_a        : f32,
167  pub fpeak_b        : f32,
168}
169
170impl Default for TofHit {
171  fn default() -> Self {
172    Self::new()
173  }
174}
175
176impl fmt::Display for TofHit {
177  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178    let mut paddle_info = String::from("");
179    if self.paddle_len == 0.0 {
180      paddle_info = String::from("NOT SET!");
181    }
182    write!(f, "<TofHit (version : {}):
183  Paddle ID       {}
184  Peak:
185    LE Time A/B   {:.2} {:.2}   
186    Height  A/B   {:.2} {:.2}
187    Charge  A/B   {:.2} {:.2}
188  ** paddle {} ** 
189    Length        {:.2}
190    Harting cable length {:.2}
191    Timing offset {:.2} (ns)
192    Coax cbl time {:.2}
193    Hart cbl time {:.2}
194  ** reconstructed interaction
195    energy_dep    {:.2}   
196    pos_across    {:.2}   
197    t0            {:.2}  
198    x, y, z       {:.2} {:.2} {:.2}
199  ** V1 variables
200    phase (ch9)   {:.4}
201      n phs ro    {}
202    baseline A/B  {:.2} {:.2}
203    bl. RMS  A/B  {:.2} {:.2}>",
204            self.version,
205            self.paddle_id,
206            self.get_time_a(),
207            self.get_time_b(),
208            self.get_peak_a(),
209            self.get_peak_b(),
210            self.get_charge_a(),
211            self.get_charge_b(),
212            paddle_info,
213            self.paddle_len,
214            self.cable_len,
215            self.timing_offset,
216            self.coax_cable_time,
217            self.hart_cable_time,
218            self.get_edep(),
219            self.get_pos(),
220            self.get_t0(),
221            self.x,
222            self.y,
223            self.z,
224            self.phase,
225            self.get_phase_rollovers(),
226            self.baseline_a,
227            self.baseline_b,
228            self.baseline_a_rms,
229            self.baseline_b_rms,
230            )
231  }
232}
233
234impl Serialization for TofHit {
235  
236  const HEAD          : u16   = 61680; //0xF0F0)
237  const TAIL          : u16   = 3855;
238  const SIZE          : usize = 30; // size in bytes with HEAD and TAIL
239
240  /// Serialize the packet
241  ///
242  /// Not all fields will get serialized, 
243  /// only the relevant data for the 
244  /// flight computer
245  //
246  /// **A note about protocol versions **
247  /// When we serialize (to_bytestream) we will
248  /// always write the latest version.
249  /// Deserialization can also read previous versions
250  fn to_bytestream(&self) -> Vec<u8> {
251
252    let mut bytestream = Vec::<u8>::with_capacity(Self::SIZE);
253    bytestream.extend_from_slice(&Self::HEAD.to_le_bytes());
254    bytestream.push(self.paddle_id); 
255    bytestream.extend_from_slice(&self.time_a      .to_le_bytes()); 
256    bytestream.extend_from_slice(&self.time_b      .to_le_bytes()); 
257    bytestream.extend_from_slice(&self.peak_a      .to_le_bytes()); 
258    bytestream.extend_from_slice(&self.peak_b      .to_le_bytes()); 
259    bytestream.extend_from_slice(&self.charge_a    .to_le_bytes()); 
260    bytestream.extend_from_slice(&self.charge_b    .to_le_bytes()); 
261    bytestream.extend_from_slice(&self.charge_min_i.to_le_bytes()); 
262    //bytestream.extend_from_slice(&self.pos_across  .to_le_bytes()); 
263    //bytestream.extend_from_slice(&self.t0          .to_le_bytes()); 
264    bytestream.extend_from_slice(&self.baseline_a   .to_le_bytes());
265    bytestream.extend_from_slice(&self.baseline_a_rms.to_le_bytes());
266    // instead of ctr_etx and reserved, we now have phase in V1
267    bytestream.extend_from_slice(&self.phase       .to_le_bytes());
268    //bytestream.push(self.ctr_etx); 
269    //bytestream.extend_from_slice(&self.timestamp32 .to_le_bytes());
270    //bytestream.extend_from_slice(&self.timestamp16 .to_le_bytes());
271    //bytestream.push(self.reserved);
272    bytestream.push(self.version.to_u8());
273    bytestream.extend_from_slice(&self.baseline_b.to_le_bytes());
274    bytestream.extend_from_slice(&self.baseline_b_rms.to_le_bytes());
275    bytestream.extend_from_slice(&Self::TAIL       .to_le_bytes()); 
276    bytestream
277  }
278
279
280  /// Deserialization
281  ///
282  ///
283  /// # Arguments:
284  ///
285  /// * bytestream : 
286  fn from_bytestream(stream : &Vec<u8>, pos : &mut usize) 
287    -> Result<Self, SerializationError> {
288    let mut pp  = Self::new();
289    Self::verify_fixed(stream, pos)?;
290    // since we passed the above test, the packet
291    // is valid
292    pp.valid          = true;
293    pp.paddle_id      = parse_u8(stream, pos);
294    pp.time_a         = parse_f16(stream, pos);
295    pp.time_b         = parse_f16(stream, pos);
296    pp.peak_a         = parse_f16(stream, pos);
297    pp.peak_b         = parse_f16(stream, pos);
298    pp.charge_a       = parse_f16(stream, pos);
299    pp.charge_b       = parse_f16(stream, pos);
300    pp.charge_min_i   = parse_u16(stream, pos);
301    pp.baseline_a     = parse_f16(stream, pos);
302    pp.baseline_a_rms = parse_f16(stream, pos);
303    //pp.time_a        = parse_u16(stream, pos);
304    //pp.time_b        = parse_u16(stream, pos);
305    //pp.peak_a        = parse_u16(stream, pos);
306    //pp.peak_b        = parse_u16(stream, pos);
307    //pp.charge_a      = parse_u16(stream, pos);
308    //pp.charge_b      = parse_u16(stream, pos);
309    //pp.charge_min_i  = parse_u16(stream, pos);
310    //pp.pos_across    = parse_u16(stream, pos);
311    //pp.t0            = parse_u16(stream, pos);
312    let mut phase_vec = Vec::<u8>::new();
313    phase_vec.push(parse_u8(stream, pos));
314    phase_vec.push(parse_u8(stream, pos));
315    pp.phase    = parse_f16(&phase_vec, &mut 0);
316    //pp.ctr_etx       = parse_u8(stream, pos);
317    //pp.reserved      = parse_u8(stream, pos);
318    let version      = ProtocolVersion::from(parse_u8(stream, pos));
319    pp.version       = version;
320    match pp.version {
321      ProtocolVersion::V1 => {
322        // in this version we do have phase instead of
323        // ctr_etx and reserved
324        //let mut phase_vec = Vec::<u8>::new();
325        //phase_vec.push(pp.ctr_etx);
326        //phase_vec.push(pp.reserved);
327        //pp.phase    = parse_f16(&phase_vec, &mut 0);
328      }
329      _ => ()
330    }
331    pp.baseline_b      = parse_f16(stream, pos);
332    pp.baseline_b_rms  = parse_f16(stream, pos);
333
334    //pp.timestamp32   = parse_u32(stream, pos);
335    //pp.timestamp16   = parse_u16(stream, pos);
336    *pos += 2; // always have to do this when using verify fixed
337    Ok(pp)
338  }
339}
340
341impl TofHit {
342
343  pub fn new() -> Self {
344    Self{
345      paddle_id       : 0,
346      time_a          : f16::from_f32(0.0),
347      time_b          : f16::from_f32(0.0),
348      peak_a          : f16::from_f32(0.0),
349      peak_b          : f16::from_f32(0.0),
350      charge_a        : f16::from_f32(0.0),
351      charge_b        : f16::from_f32(0.0),
352      paddle_len      : f32::NAN,
353      timing_offset   : 0.0,
354      cable_len       : f32::NAN,
355      coax_cable_time : f32::NAN,
356      hart_cable_time : f32::NAN,
357      event_t0        : f32::NAN,
358      x               : f32::NAN,
359      y               : f32::NAN,
360      z               : f32::NAN,
361      
362      charge_min_i    : 0,
363      // deprecated   
364      pos_across      : 0,
365      t0              : 0,
366      ctr_etx         : 0,
367      timestamp32     : 0,
368      timestamp16     : 0,
369      valid           : true,
370      // v1 variables 
371      version         : ProtocolVersion::V1,
372      reserved        : 0,
373      baseline_a      : f16::from_f32(0.0),
374      baseline_a_rms  : f16::from_f32(0.0),
375      baseline_b      : f16::from_f32(0.0),
376      baseline_b_rms  : f16::from_f32(0.0),
377      phase           : f16::from_f32(0.0),
378      // non-serialize fields
379      ftime_a         : 0.0,
380      ftime_b         : 0.0,
381      fpeak_a        : 0.0,
382      fpeak_b        : 0.0,
383    }
384  }
385
386  /// Calculate the distance to another hit. For this 
387  /// to work, the hit coordinates have had to be 
388  /// determined, so this will only return a 
389  /// propper result after the paddle information 
390  /// is added
391  pub fn distance(&self, other : &TofHit) -> f32 {
392    ((self.x - other.x).powi(2) + (self.y - other.y).powi(2) + (self.z - other.z).powi(2)).sqrt()
393  }
394
395  #[cfg(feature="database")]
396  pub fn set_paddle(&mut self, paddle : &Paddle) {
397    self.cable_len  = paddle.cable_len;
398    self.coax_cable_time = paddle.coax_cable_time;
399    self.hart_cable_time = paddle.harting_cable_time;
400    self.paddle_len = paddle.length * 10.0; // stupid units!
401    let pr          = paddle.principal();
402    //println!("Principal {:?}", pr);
403    let rel_pos     = self.get_pos();
404    let pos         = (paddle.global_pos_x_l0_A*10.0 + pr.0*rel_pos,
405                       paddle.global_pos_y_l0_A*10.0 + pr.1*rel_pos,
406                       paddle.global_pos_z_l0_A*10.0 + pr.2*rel_pos);
407    self.x          = pos.0;
408    self.y          = pos.1;
409    self.z          = pos.2;
410  }
411
412  /// Get the (official) paddle id
413  ///
414  /// Convert the paddle end id following 
415  /// the convention
416  ///
417  /// A-side : paddle id + 1000
418  /// B-side : paddle id + 2000
419  ///
420  /// FIXME - maybe return Result?
421  pub fn get_pid(paddle_end_id : u16) -> u8 {
422    if paddle_end_id < 1000 {
423      return 0;
424    }
425    if paddle_end_id > 2000 {
426      return (paddle_end_id - 2000) as u8;
427    }
428    if paddle_end_id < 2000 {
429      return (paddle_end_id - 1000) as u8;
430    }
431    return 0;
432  }
433
434  pub fn add_peak(&mut self, peak : &Peak)  {
435    if self.paddle_id != TofHit::get_pid(peak.paddle_end_id) {
436      //error!("Can't add peak to 
437    }
438    if peak.paddle_end_id < 1000 {
439      error!("Invalide paddle end id {}", peak.paddle_end_id);
440    }
441    if peak.paddle_end_id > 2000 {
442      self.set_time_b  (peak.time);
443      self.set_peak_b  (peak.height);
444      self.set_charge_b(peak.charge);
445    } else if peak.paddle_end_id < 2000 {
446      self.set_time_a  (peak.time);
447      self.set_peak_a  (peak.height);
448      self.set_charge_a(peak.charge);
449    }
450  }
451
452
453  // rework the whole getter/setter cluster, since 
454  // we switched to f16 instead of our custom 
455  // conversion
456  
457  /// Calculate the position across the paddle from
458  /// the two times at the paddle ends
459  ///
460  /// **This will be measured from the A side**
461  ///
462  /// Just to be extra clear, this assumes the two 
463  /// sets of cables for each paddle end have the
464  /// same length
465  pub fn get_pos(&self) -> f32 {
466    let t0 = self.get_t0_uncorrected();
467    let clean_t_a = self.time_a.to_f32() - t0;
468    return clean_t_a*C_LIGHT_PADDLE*10.0; 
469  }
470
471  /// If the two reconstructed pulse times are not related to each other by the paddle length,
472  /// meaning that they can't be caused by the same event, we dub this hit as "not following
473  /// causality"
474  pub fn obeys_causality(&self) -> bool {
475    (self.paddle_len/(10.0*C_LIGHT_PADDLE)) - f32::abs(self.time_a.to_f32() - self.time_b.to_f32()) > 0.0
476    && self.get_t0_uncorrected() > 0.0
477  }
478
479  /// Get the cable correction time
480  pub fn get_cable_delay(&self) -> f32 {
481    self.hart_cable_time - self.coax_cable_time 
482  }
483
484  /// Get the delay relative to other readoutboards based 
485  /// on the channel9 sine wave
486  pub fn get_phase_delay(&self) -> f32 { 
487    let freq : f32 = 20.0e6;
488    let phase = self.phase.to_f32();
489    // fit allows for negative phase shift.
490    // that means to distinguish 2 points, we
491    // only have HALF of the sine wave
492    // FIXME - implement warning?
493    //while phase < -PI {
494    //  phase += 2.0*PI;
495    //}
496    //while phase > PI {
497    //  phase -= 2.0*PI;
498    //}
499    (phase/(2.0*PI*freq))*1.0e9f32 
500  }
501
502  pub fn get_phase_rollovers(&self) -> i16 {
503    let mut phase = self.phase.to_f32();
504    let mut ro = 0i16;
505    while phase < PI/2.0 {
506      phase += PI/2.0;
507      ro += 1;
508    }
509    while phase > PI/2.0 {
510      phase -= PI/2.0;
511      ro -= 1;
512    }
513    ro
514  }
515  
516  /// That this works, the length of the paddle has to 
517  /// be set before (in mm).
518  /// This assumes that the cable on both sides of the paddle are 
519  /// the same length
520  pub fn get_t0(&self) -> f32 {
521    //self.get_t0_uncorrected() + self.get_phase_delay() + self.get_cable_delay()
522    self.event_t0 + self.timing_offset
523  }
524
525  /// Calculate the interaction time based on the peak timings measured 
526  /// at the paddle ends A and B
527  ///
528  /// This does not correct for any cable length
529  /// or ch9 phase shift
530  pub fn get_t0_uncorrected(&self) -> f32 {
531    0.5*(self.time_a.to_f32() + self.time_b.to_f32() - (self.paddle_len/(10.0*C_LIGHT_PADDLE)))// - ((self.cable_len*2.0)/(10.0*C_LIGHT_CABLE)))
532  }
533
534  /// Philip's energy deposition based on peak height
535  pub fn get_edep(&self) -> f32 {
536    (1.29/34.3)*(self.peak_a.to_f32() + self.peak_b.to_f32()) / 2.0
537  }
538
539  pub fn get_time_a(&self) -> f32 {
540    self.time_a.to_f32()
541  }
542
543  pub fn set_time_a(&mut self, t : f32) {
544    self.time_a = f16::from_f32(t);
545  }
546
547  pub fn get_time_b(&self) -> f32 {
548    self.time_b.to_f32()
549  }
550
551  pub fn set_time_b(&mut self, t : f32) {
552    self.time_b = f16::from_f32(t)
553  }
554
555  pub fn get_peak_a(&self) -> f32 {
556    self.peak_a.to_f32()
557  }
558
559  pub fn set_peak_a(&mut self, p : f32) {
560    self.peak_a = f16::from_f32(p)
561  }
562
563  pub fn get_peak_b(&self) -> f32 {
564    self.peak_b.to_f32()
565  }
566
567  pub fn set_peak_b(&mut self, p : f32) {
568    self.peak_b = f16::from_f32(p)
569  }
570
571  pub fn get_charge_a(&self) -> f32 {
572    self.charge_a.to_f32()
573  }
574
575  pub fn set_charge_a(&mut self, c : f32) {
576    self.charge_a = f16::from_f32(c)
577  }
578
579  pub fn get_charge_b(&self) -> f32 {
580    self.charge_b.to_f32()
581  }
582
583  pub fn set_charge_b(&mut self, c : f32) {
584    self.charge_b = f16::from_f32(c)
585  }
586
587  pub fn get_bl_a(&self) -> f32 {
588    self.baseline_a.to_f32()
589  }
590  
591  pub fn get_bl_b(&self) -> f32 {
592    self.baseline_b.to_f32()
593  }
594  
595  pub fn get_bl_a_rms(&self) -> f32 {
596    self.baseline_a_rms.to_f32()
597  }
598  
599  pub fn get_bl_b_rms(&self) -> f32 {
600    self.baseline_b_rms.to_f32()
601  }
602
603  #[cfg(feature="random")]
604  pub fn from_random() -> TofHit {
605    let mut pp  = TofHit::new();
606    let mut rng = rand::thread_rng();
607
608    pp.paddle_id      = rng.gen::<u8> ();
609    pp.time_a         = f16::from_f32(rng.gen::<f32>());
610    pp.time_b         = f16::from_f32(rng.gen::<f32>());
611    pp.peak_a         = f16::from_f32(rng.gen::<f32>());
612    pp.peak_b         = f16::from_f32(rng.gen::<f32>());
613    pp.charge_a       = f16::from_f32(rng.gen::<f32>());
614    pp.charge_b       = f16::from_f32(rng.gen::<f32>());
615    pp.version        = ProtocolVersion::from(rng.gen::<u8>());
616    pp.baseline_a     = f16::from_f32(rng.gen::<f32>());
617    pp.baseline_a_rms = f16::from_f32(rng.gen::<f32>());
618    pp.baseline_b     = f16::from_f32(rng.gen::<f32>());
619    pp.baseline_b_rms = f16::from_f32(rng.gen::<f32>());
620    pp.phase          = f16::from_f32(rng.gen::<f32>());
621    
622    pp.paddle_len       = 0.0; 
623    pp.cable_len        = 0.0; 
624    pp.coax_cable_time  = 0.0; 
625    pp.hart_cable_time  = 0.0; 
626    pp.x                = 0.0; 
627    pp.y                = 0.0; 
628    pp.z                = 0.0; 
629    pp.event_t0         = 0.0; 
630    //charge_min_i   : 0,
631    //// deprecated  
632    //pos_across     : 0,
633    //t0             : 0,
634    //ctr_etx        : 0,
635    //timestamp32    : 0,
636    //timestamp16    : 0,
637    //valid          : true,
638    //// v1 variables
639    //version        : ProtocolVersion::V1,
640    //reserved       : 0,
641    //baseline_a     : f16::from_f32(0.0),
642    //baseline_a_rms : f16::from_f32(0.0),
643    //baseline_b     : f16::from_f32(0.0),
644    //baseline_b_rms : f16::from_f32(0.0),
645    //phase          : f16::from_f32(0.0),
646    //// non-serialize fields
647    //ftime_a        : 0.0,
648    //ftime_b        : 0.0,
649    //fpeak_a        : 0.0,
650    //fpeak_b        : 0.0,
651    
652
653
654    pp
655  }
656}
657
658#[cfg(feature = "random")]
659#[test]
660fn serialization_tofhit() {
661  for _ in 0..100 {
662    let mut pos = 0;
663    let data = TofHit::from_random();
664    let mut test = TofHit::from_bytestream(&data.to_bytestream(),&mut pos).unwrap();
665    // Manually zero these fields, since comparison with nan will fail and 
666    // from_random did not touch these
667    test.paddle_len       = 0.0; 
668    test.cable_len        = 0.0; 
669    test.coax_cable_time  = 0.0; 
670    test.hart_cable_time  = 0.0; 
671    test.x                = 0.0; 
672    test.y                = 0.0; 
673    test.z                = 0.0; 
674    test.event_t0         = 0.0;
675    assert_eq!(pos, TofHit::SIZE);
676    assert_eq!(data, test);
677  }
678}