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