1use crate::prelude::*;
7
8pub fn roll<T: Clone>(vec: &mut Vec<T>, offset: isize) {
20 let len = vec.len() as isize;
21 if len <= 1 {
22 return;
23 }
24 let offset = offset % len;
25 if offset == 0 {
26 return;
27 }
28 let split_point = if offset > 0 {
29 len - offset
30 } else {
31 -offset
32 } as usize;
33
34 let mut temp = Vec::with_capacity(len as usize);
35 temp.extend_from_slice(&vec[split_point..]);
36 temp.extend_from_slice(&vec[..split_point]);
37
38 vec.clear();
39 vec.extend_from_slice(&temp);
40}
41
42pub fn clean_spikes(trace : &mut Vec<f32>, vcaldone : bool) {
48 let mut thresh : f32 = 360.0;
50 if vcaldone {
51 thresh = 16.0;
52 }
53
54 let mut spf_allch = vec![0usize;1023];
55 let mut spf_sum = vec![0f32;1024];
56 let tracelen = trace.len();
57 let spikefilter0 = &trace[0..tracelen-3];
58 let spikefilter1 = &trace[1..tracelen-2];
59 let spikefilter2 = &trace[2..tracelen-1];
60 let spikefilter3 = &trace[3..tracelen];
61 let spf_len = spikefilter0.len();
62 for k in 0..spf_len {
63 spf_sum[k] += spikefilter1[k] - spikefilter0[k] + spikefilter2[k] - spikefilter3[k];
64 }
65 for k in 0..spf_len {
66 if spf_sum[k] > thresh {
67 spf_allch[k] += 1;
68 }
69 }
70 let mut spikes = Vec::<usize>::new();
71 for k in 0..spf_allch.len() {
72 if spf_allch[k] >= 2 {
73 spikes.push(k);
74 }
75 }
76 for spike in spikes.iter() {
77 let d_v : f32 = (trace[spike+3] - trace[*spike])/3.0;
78 trace[spike+1] = trace[*spike] + d_v;
79 trace[spike+2] = trace[*spike] + 2.0*d_v;
80 }
81}
82
83#[derive(Debug, Copy, Clone)]
105#[repr(u8)]
106pub enum Edge {
107 Unknown = 0u8,
108 Rising = 10u8,
109 Falling = 20u8,
110 Average = 30u8,
111 None = 40u8
112}
113
114impl fmt::Display for Edge {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 let r : &str;
117 match self {
118 Edge::Unknown => {r = "Unknown"}
119 Edge::Rising => {r = "Rising"},
120 Edge::Falling => {r = "Falling"},
121 Edge::Average => {r = "Average"},
122 Edge::None => {r = "None"}
123 }
124 write!(f, "<Edge: {}>", r)
125 }
126}
127
128impl From<u8> for Edge {
129 fn from(value: u8) -> Self {
130 match value {
131 0u8 => Edge::Unknown,
132 10u8 => Edge::Rising,
133 20u8 => Edge::Falling,
134 30u8 => Edge::Average,
135 40u8 => Edge::None,
136 _ => Edge::Unknown
137 }
138 }
139}
140
141#[cfg(feature = "random")]
142impl FromRandom for Edge {
143
144 fn from_random() -> Self {
145 let choices = [
146 Edge::Rising,
147 Edge::Falling,
148 Edge::Average,
149 Edge::None,
150 ];
151 let mut rng = rand::rng();
152 let idx = rng.random_range(0..choices.len());
153 choices[idx]
154 }
155}
156
157
158#[derive(Debug, Clone, PartialEq)]
160#[cfg_attr(feature="pybindings", pyclass)]
161pub struct RBCalibrationFlightT {
162 pub rb_id : u8,
163 pub timestamp : u32, pub tbins : [[f16;NWORDS];NCHN], }
166
167pub fn get_periods(trace : &Vec<f32>,
174 dts : &Vec<f32>,
175 nperiod : f32,
176 nskip : f32,
177 edge : &Edge) -> (Vec<usize>, Vec<f32>) {
178 let mut trace_c = trace.clone();
179 let mut periods = Vec::<f32>::new();
180 if trace_c.len() == 0 {
181 let foo = Vec::<usize>::new();
182 return (foo, periods);
183 }
184 let firstbin : usize = 20;
185 let lastbin = firstbin + (nperiod * (900.0/nperiod).floor()).floor() as usize;
186 let mut vec_for_median = Vec::<f32>::new();
188 for bin in firstbin..lastbin {
189 if trace[bin].is_nan() {
190 continue;
191 }
192 vec_for_median.push(trace[bin]);
193 }
194 let median_val = median(&vec_for_median);
195 debug!("median val {median_val}");
196 for k in 0..trace_c.len() {
197 trace_c[k] -= median_val;
198 }
199 let mut zcs = find_zero_crossings(&trace_c);
200 let mut zcs_nskip = Vec::<usize>::with_capacity(zcs.len());
202 for k in 0..zcs.len() {
203 if zcs[k] > nskip as usize {
204 zcs_nskip.push(zcs[k]);
205 }
206 }
207 zcs = zcs_nskip;
208 let mut zcs_filter = Vec::<usize>::with_capacity(zcs.len());
209 for k in 0..zcs.len() {
210 match edge {
211 Edge::Rising => {
212 if trace_c[zcs[k]] < 0.0 {
213 zcs_filter.push(zcs[k]);
214 }
215 }
216 Edge::Falling => {
217 if trace_c[zcs[k]] > 0.0 {
219 zcs_filter.push(zcs[k]);
220 }
221 },
222 Edge::None => {
223 warn!("Unsure what to do for Edge::None");
224 },
225 Edge::Average => {
226 warn!("Unsure what to do for Edge::Average");
227 },
228 Edge::Unknown => {
229 warn!("Unsure what to do for Edge::Unknown");
230 }
231 }
232 }
233 debug!("Found {} zero crossings!", zcs_filter.len());
234 zcs = zcs_filter;
235 if zcs.len() < 3 {
236 return (zcs, periods);
237 }
238
239 for k in 0..zcs.len() -1 {
240 let zcs_a = &zcs[k];
241 let zcs_b = &zcs[k+1];
242 if zcs_a + 2 > trace_c.len() || zcs_b + 2 > trace_c.len() {
245 continue;
246 }
247 let tr_a = &trace_c[*zcs_a..*zcs_a+2];
248 let tr_b = &trace_c[*zcs_b..*zcs_b+2];
249 let mut period : f32 = 0.0;
250 for n in zcs_a+1..*zcs_b {
251 period += dts[n];
252 }
253 period += dts[*zcs_a]*f32::abs(tr_a[1]/(tr_a[1] - tr_a[0])); period += dts[*zcs_b]*f32::abs(tr_b[0]/(tr_b[1] - tr_b[0])); if period.is_nan() {
256 error!("NAN in period found!");
257 continue;
258 }
259
260 if f32::abs(*zcs_b as f32 - *zcs_a as f32 - nperiod) > 5.0 {
261 let mut zcs_tmp = Vec::<usize>::new();
262 zcs_tmp.extend_from_slice(&zcs[0..k+1]);
263 zcs = zcs_tmp;
264 break;
265 }
266 trace!("zcs_a, zcs_b, period, nperiod {} {} {} {}", zcs_a, zcs_b, period, nperiod);
267 periods.push(period);
268 }
269 debug!("Calculated {} zero-crossings and {} periods!", zcs.len(), periods.len());
270 (zcs, periods)
271}
272
273pub fn find_zero_crossings(trace: &Vec<f32>) -> Vec<usize> {
278 let mut zero_crossings = Vec::new();
279 for i in 1..trace.len() {
280 if (trace[i - 1] > 0.0 && trace[i] < 0.0) || (trace[i - 1] < 0.0 && trace[i] > 0.0) {
282 zero_crossings.push(i - 1);
283 }
284 if i < trace.len() - 1 {
285 if trace[i - 1] > 0.0 && trace[i] == 0.0 && trace[i+1] < 0.0 {
286 zero_crossings.push(1);
287 }
288 if trace[i - 1] < 0.0 && trace[i] == 0.0 && trace[i+1] > 0.0 {
289 zero_crossings.push(i);
290 }
291 }
292 }
293 zero_crossings
294}
295
296impl RBCalibrationFlightT {
299 pub fn new() -> Self {
300 Self {
301 rb_id : 0,
302 timestamp : 0,
303 tbins : [[f16::from_f32(0.0);NWORDS];NCHN],
304 }
305 }
306}
307
308impl Serialization for RBCalibrationFlightT {
309 const SIZE : usize = NCHN*NWORDS*2 + 4 + 1;
310 const HEAD : u16 = 0xAAAA; const TAIL : u16 = 0x5555; fn from_bytestream(bytestream : &Vec<u8>,
314 pos : &mut usize)
315 -> Result<Self, SerializationError> {
316 let mut rb_cal = Self::new();
317 if parse_u16(bytestream, pos) != Self::HEAD {
318 return Err(SerializationError::HeadInvalid {});
319 }
320 rb_cal.rb_id = parse_u8(bytestream, pos);
321 rb_cal.timestamp = parse_u32(bytestream, pos);
322 for ch in 0..NCHN {
323 for k in 0..NWORDS {
324 let value = parse_f16(bytestream, pos);
325 rb_cal.tbins[ch][k] = value;
326 }
327 }
328 *pos += 2;
329 Ok(rb_cal)
330 }
331
332 fn to_bytestream(&self) -> Vec<u8> {
333
334 let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
335 bs.extend_from_slice(&Self::HEAD.to_le_bytes());
336 bs.extend_from_slice(&self.rb_id.to_le_bytes());
337 bs.extend_from_slice(&self.timestamp.to_le_bytes());
338 for ch in 0..NCHN {
339 for k in 0..NWORDS {
340 bs.extend_from_slice(&self.tbins[ch][k] .to_le_bytes());
341 }
342 }
343 bs.extend_from_slice(&Self::TAIL.to_le_bytes());
344 bs
345 }
346}
347
348impl TofPackable for RBCalibrationFlightT {
349 const TOF_PACKET_TYPE : TofPacketType = TofPacketType::RBCalibrationFlightT;
350}
351
352impl fmt::Display for RBCalibrationFlightT {
353 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354 let mut timestamp_str = String::from("?");
355 match Utc.timestamp_opt(self.timestamp.into(), 0) {
356 LocalResult::Single(datetime_utc) => {
357 timestamp_str = datetime_utc.format("%Y/%m/%d %H:%M:%S").to_string();
358 },
359 LocalResult::Ambiguous(_, _) => {
360 error!("The given timestamp is ambiguous.");
361 },
362 LocalResult::None => {
363 error!("The given timestamp is not valid.");
364 },
365 }
366
367 write!(f,
369 "<RBCalibrationFlightT [{} UTC]:
370 RB : {}
371 T Bins [ch0]: .. {:?} {:?} ..>",
372 timestamp_str,
373 self.rb_id,
374 self.tbins[0][98],
375 self.tbins[0][99]
376 )
377 }
378}
379
380#[cfg(feature = "random")]
381impl FromRandom for RBCalibrationFlightT {
382
383 fn from_random() -> Self {
384 let mut cali = Self::new();
385 let mut rng = rand::rng();
386 cali.rb_id = rng.random::<u8>();
387 cali.timestamp = rng.random::<u32>();
388 for ch in 0..NCHN {
389 for n in 0..NWORDS {
390 cali.tbins [ch][n] = f16::from_f32(rng.random::<f32>());
391 }
392 }
393 cali
394 }
395}
396
397#[cfg(feature="pybindings")]
400#[pymethods]
401impl RBCalibrationFlightT {
402 #[getter]
403 fn get_rb_id(&self) -> u8 {
404 self.rb_id
405 }
406
407 #[getter]
408 fn get_timestamp(&self) -> u32 {
409 self.timestamp
410 }
411
412 fn get_tbins<'_py>(&self, py: Python<'_py>, channel : u8 ) -> PyResult<Bound<'_py, PyArray1<f32>>> {
413 if channel < 1 || channel > 9 {
414 return Err(PyValueError::new_err("Channel must be > 0 and < 9"));
415 }
416 let mut py_tbins = [0.0f32;NWORDS];
417 for k in 0..NWORDS {
418 py_tbins[k] = self.tbins[channel as usize][k].to_f32();
419 };
420 let py_array = py_tbins.to_pyarray(py);
421 Ok(py_array)
422 }
423}
424
425#[cfg(feature="pybindings")]
426pythonize_packable!(RBCalibrationFlightT);
427
428#[derive(Debug, Clone, PartialEq)]
431#[cfg_attr(feature="pybindings", pyclass)]
432pub struct RBCalibrationFlightV {
433 pub rb_id : u8,
434 pub d_v : f32, pub timestamp : u32, pub v_offsets : [[f16;NWORDS];NCHN], pub v_dips : [[f16;NWORDS];NCHN], pub v_inc : [[f16;NWORDS];NCHN], }
441
442impl RBCalibrationFlightV {
443 pub fn new() -> Self {
444 Self {
445 rb_id : 0,
446 d_v : 0.9,
447 timestamp : 0,
448 v_offsets : [[f16::from_f32(0.0);NWORDS];NCHN],
449 v_dips : [[f16::from_f32(0.0);NWORDS];NCHN],
450 v_inc : [[f16::from_f32(0.0);NWORDS];NCHN],
451 }
452 }
453}
454
455impl Serialization for RBCalibrationFlightV {
456 const SIZE : usize = NCHN*NWORDS*2*3 + 4 + 4 + 1;
457 const HEAD : u16 = 0xAAAA; const TAIL : u16 = 0x5555; fn from_bytestream(bytestream : &Vec<u8>,
461 pos : &mut usize)
462 -> Result<Self, SerializationError> {
463 let mut rb_cal = Self::new();
464 if parse_u16(bytestream, pos) != Self::HEAD {
465 return Err(SerializationError::HeadInvalid {});
466 }
467 rb_cal.rb_id = parse_u8(bytestream, pos);
468 rb_cal.d_v = parse_f32(bytestream, pos);
469 rb_cal.timestamp = parse_u32(bytestream, pos);
470 for ch in 0..NCHN {
471 for k in 0..NWORDS {
472 let mut value = parse_f16(bytestream, pos);
473 rb_cal.v_offsets[ch][k] = value;
474 value = parse_f16(bytestream, pos);
475 rb_cal.v_dips[ch][k] = value;
476 value = parse_f16(bytestream, pos);
477 rb_cal.v_inc[ch][k] = value;
478 }
479 }
480 *pos += 2;
481 Ok(rb_cal)
482 }
483
484 fn to_bytestream(&self) -> Vec<u8> {
485 let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
486 bs.extend_from_slice(&Self::HEAD.to_le_bytes());
487 bs.extend_from_slice(&self.rb_id.to_le_bytes());
488 bs.extend_from_slice(&self.d_v.to_le_bytes());
489 bs.extend_from_slice(&self.timestamp.to_le_bytes());
490 for ch in 0..NCHN {
491 for k in 0..NWORDS {
492 bs.extend_from_slice(&self.v_offsets[ch][k].to_le_bytes());
493 bs.extend_from_slice(&self.v_dips[ch][k] .to_le_bytes());
494 bs.extend_from_slice(&self.v_inc[ch][k] .to_le_bytes());
495 }
496 }
497 bs.extend_from_slice(&Self::TAIL.to_le_bytes());
498 bs
499 }
500}
501
502impl TofPackable for RBCalibrationFlightV {
503 const TOF_PACKET_TYPE : TofPacketType = TofPacketType::RBCalibrationFlightV;
504}
505
506impl fmt::Display for RBCalibrationFlightV {
507 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
508 let mut timestamp_str = String::from("?");
509 match Utc.timestamp_opt(self.timestamp.into(), 0) {
510 LocalResult::Single(datetime_utc) => {
511 timestamp_str = datetime_utc.format("%Y/%m/%d %H:%M:%S").to_string();
512 },
513 LocalResult::Ambiguous(_, _) => {
514 error!("The given timestamp is ambiguous.");
515 },
516 LocalResult::None => {
517 error!("The given timestamp is not valid.");
518 },
519 }
520 write!(f,
521 "<RBCalibrationFlightV [{} UTC]:
522 RB : {}
523 V Offsets [ch0]: .. {:?} {:?} ..
524 V Incrmts [ch0]: .. {:?} {:?} ..
525 V Dips [ch0]: .. {:?} {:?} ..>",
526 timestamp_str,
527 self.rb_id,
528 self.v_offsets[0][98],
529 self.v_offsets[0][99],
530 self.v_inc[0][98],
531 self.v_inc[0][99],
532 self.v_dips[0][98],
533 self.v_dips[0][99]
534 )
535 }
536}
537
538#[cfg(feature = "random")]
539impl FromRandom for RBCalibrationFlightV {
540
541 fn from_random() -> Self {
542 let mut cali = Self::new();
543 let mut rng = rand::rng();
544 cali.rb_id = rng.random::<u8>();
545 cali.d_v = rng.random::<f32>();
546 cali.timestamp = rng.random::<u32>();
547 for ch in 0..NCHN {
548 for n in 0..NWORDS {
549 cali.v_offsets [ch][n] = f16::from_f32(rng.random::<f32>());
550 cali.v_dips [ch][n] = f16::from_f32(rng.random::<f32>());
551 cali.v_inc [ch][n] = f16::from_f32(rng.random::<f32>());
552 }
553 }
554 cali
555 }
556}
557
558#[cfg(feature="pybindings")]
561#[pymethods]
562impl RBCalibrationFlightV {
563 #[getter]
564 fn get_rb_id(&self) -> u8 {
565 self.rb_id
566 }
567
568 #[getter]
569 fn get_d_v(&self) -> f32 {
570 self.d_v
571 }
572
573 #[getter]
574 fn get_timestamp(&self) -> u32 {
575 self.timestamp
576 }
577
578 fn get_v_offsets<'_py>(&self, py: Python<'_py>, channel : u8 ) -> PyResult<Bound<'_py, PyArray1<f32>>> {
579 if channel < 1 || channel > 9 {
580 return Err(PyValueError::new_err("Channel must be > 0 and < 9"));
581 }
582 let mut py_voffsets = [0.0f32;NWORDS];
583 for k in 0..NWORDS {
584 py_voffsets[k] = self.v_offsets[channel as usize][k].to_f32();
585 };
586 let py_array = py_voffsets.to_pyarray(py);
587 Ok(py_array)
588 }
589
590 fn get_v_dips<'_py>(&self, py: Python<'_py>, channel : u8 ) -> PyResult<Bound<'_py, PyArray1<f32>>> {
591 if channel < 1 || channel > 9 {
592 return Err(PyValueError::new_err("Channel must be > 0 and < 9"));
593 }
594 let mut py_vdips = [0.0f32;NWORDS];
595 for k in 0..NWORDS {
596 py_vdips[k] = self.v_dips[channel as usize][k].to_f32();
597 };
598 let py_array = py_vdips.to_pyarray(py);
599 Ok(py_array)
600 }
601
602 fn get_v_inc<'_py>(&self, py: Python<'_py>, channel : u8 ) -> PyResult<Bound<'_py, PyArray1<f32>>> {
603 if channel < 1 || channel > 9 {
604 return Err(PyValueError::new_err("Channel must be > 0 and < 9"));
605 }
606 let mut py_vinc = [0.0f32;NWORDS];
607 for k in 0..NWORDS {
608 py_vinc[k] = self.v_inc[channel as usize][k].to_f32();
609 };
610 let py_array = py_vinc.to_pyarray(py);
611 Ok(py_array)
612 }
613}
614
615#[cfg(feature="pybindings")]
616pythonize_packable!(RBCalibrationFlightV);
617
618#[test]
622fn prop_roll_then_unroll_gives_original() {
623 fn prop(mut vec: Vec<u8>, offset: i8) -> bool {
624 let original = vec.clone();
625 let offset = offset as isize;
626
627 roll(&mut vec, offset);
628 roll(&mut vec, -offset);
629
630 vec == original
631 }
632 quickcheck::QuickCheck::new().tests(100).quickcheck(prop as fn(Vec<u8>, i8) -> bool);
633}
634
635#[derive(Debug, Clone, PartialEq)]
646#[cfg_attr(feature="pybindings", pyclass)]
647pub struct RBCalibrations {
648 pub rb_id : u8,
649 pub d_v : f32, pub timestamp : u32, pub serialize_event_data : bool,
653 pub v_offsets : [[f32;NWORDS];NCHN], pub v_dips : [[f32;NWORDS];NCHN], pub v_inc : [[f32;NWORDS];NCHN], pub tbin : [[f32;NWORDS];NCHN], pub vcal_data : Vec::<RBEvent>,
661 pub tcal_data : Vec::<RBEvent>,
662 pub noi_data : Vec::<RBEvent>,
663}
664
665impl RBCalibrations {
666 pub const NSKIP : usize = 2;
670 pub const SINMAX : usize = 60; pub const DVCUT : f32 = 15.0; pub const NOMINALFREQ : f32 = 2.0; pub const CALFREQ : f32 = 0.025; pub fn assemble_from_flightcal(fcal_t : RBCalibrationFlightT,
678 fcal_v : RBCalibrationFlightV)
679 -> Result<Self, CalibrationError> {
680 if (fcal_t.timestamp != fcal_v.timestamp) || (fcal_t.rb_id != fcal_v.rb_id) {
681 error!("These calibrations do not match! {} , {}", fcal_t, fcal_v);
682 return Err(CalibrationError::IncompatibleFlightCalibrations);
683 }
684 let mut cal = Self::new(fcal_t.rb_id);
685 cal.rb_id = fcal_t.rb_id;
686 cal.timestamp = fcal_t.timestamp;
687 cal.d_v = fcal_v.d_v;
688 cal.serialize_event_data = false;
689 for ch in 0..NCHN {
690 for k in 0..NWORDS {
691 cal.tbin [ch][k] = f16::to_f32(fcal_t.tbins[ch][k]);
692 cal.v_offsets[ch][k] = f16::to_f32(fcal_v.v_offsets[ch][k]);
693 cal.v_dips [ch][k] = f16::to_f32(fcal_v.v_dips[ch][k]);
694 cal.v_inc [ch][k] = f16::to_f32(fcal_v.v_inc[ch][k]);
695 }
696 }
697 Ok(cal)
698 }
699
700 pub fn emit_flighttcal(&self) -> RBCalibrationFlightT {
705 let mut cal = RBCalibrationFlightT::new();
706 cal.rb_id = self.rb_id;
707 cal.timestamp = self.timestamp;
708 for ch in 0..NCHN {
709 for n in 0..NWORDS {
710 cal.tbins [ch][n] = f16::from_f32(self.tbin[ch][n]);
711 }
712 }
713 cal
714 }
715
716 pub fn emit_flightvcal(&self) -> RBCalibrationFlightV {
721 let mut cal = RBCalibrationFlightV::new();
722 cal.rb_id = self.rb_id;
723 cal.timestamp = self.timestamp;
724 cal.d_v = self.d_v;
725 for ch in 0..NCHN {
726 for n in 0..NWORDS {
727 cal.v_offsets [ch][n] = f16::from_f32(self.v_offsets[ch][n]);
728 cal.v_dips [ch][n] = f16::from_f32(self.v_dips[ch][n]);
729 cal.v_inc [ch][n] = f16::from_f32(self.v_inc[ch][n]);
730 }
731 }
732 cal
733 }
734
735 pub fn clean_input_data(&mut self) {
737 self.vcal_data.retain(|x| !x.header.drs_lost_trigger()
738 && !x.header.is_event_fragment()
739 && x.trace_check());
740 self.tcal_data.retain(|x| !x.header.drs_lost_trigger()
741 && !x.header.is_event_fragment()
742 && x.trace_check());
743 self.noi_data.retain(|x| !x.header.drs_lost_trigger()
744 && !x.header.is_event_fragment()
745 && x.trace_check());
746 self.noi_data.sort_by(|a, b| a.header.event_id.cmp(&b.header.event_id));
747 self.vcal_data.sort_by(|a, b| a.header.event_id.cmp(&b.header.event_id));
748 self.tcal_data.sort_by(|a, b| a.header.event_id.cmp(&b.header.event_id));
749 }
750
751 fn apply_vcal(&self,
754 data : &Vec<RBEvent>)
755 -> (Vec<Vec<Vec<f32>>>,Vec<isize>) {
756 let nevents = data.len();
757 let mut traces = Vec::<Vec::<Vec::<f32>>>::new();
758 let mut trace = Vec::<f32>::with_capacity(NWORDS);
759 let mut stop_cells = Vec::<isize>::new();
760 let mut empty_events = Vec::<Vec::<f32>>::new();
761 for _ in 0..nevents {
762 empty_events.push(trace.clone());
763 }
764 for ch in 0..NCHN {
765 traces.push(empty_events.clone());
766 for (k,ev) in data.iter().enumerate() {
767 trace.clear();
768 stop_cells.push(ev.header.stop_cell as isize);
769 for k in 0..NWORDS {
770 trace.push(ev.adc[ch][k] as f32);
771 }
772 self.voltages(ch + 1, ev.header.stop_cell as usize,
773 &ev.adc[ch], &mut trace);
774 traces[ch][k] = trace.clone();
775 }
776 }
777 (traces, stop_cells)
778 }
779
780 pub fn apply_vcal_constants(&self,
782 adc : &Vec<f32>,
783 channel : usize,
784 stop_cell : usize) -> Vec<f32> {
785 let mut waveform = Vec::<f32>::with_capacity(adc.len());
786 let mut value : f32;
787 for k in 0..adc.len() {
788 value = adc[k] as f32;
789 value -= self.v_offsets[channel][(k + (stop_cell)) %NWORDS];
790 value -= self.v_dips [channel][k];
791 value *= self.v_inc [channel][(k + (stop_cell)) %NWORDS];
792 waveform.push(value);
793 }
794 waveform
795 }
796
797 fn voltage_offset_and_dips(input_vcal_data : &Vec<RBEvent>)
804 -> Result<(Vec<Vec<f32>>, Vec<Vec<f32>>), CalibrationError> {
805 if input_vcal_data.len() == 0 {
806 return Err(CalibrationError::EmptyInputData);
807 }
808 let mut all_v_offsets = Vec::<Vec::<f32>>::new();
809 let mut all_v_dips = Vec::<Vec::<f32>>::new();
810 let nchn = input_vcal_data[0].header.get_nchan();
811
812 debug!("Found {nchn} channels!");
813 for _ in 0..nchn {
814 let empty_vec_off : Vec<f32> = vec![0.0;NWORDS];
815 all_v_offsets.push(empty_vec_off);
816 let empty_vec_dip : Vec<f32> = vec![0.0;NWORDS];
817 all_v_dips.push(empty_vec_dip);
818 }
819
820 let mut traces = unpack_traces(&input_vcal_data);
823 let mut rolled_traces = traces.clone();
824 for ch in 0..nchn {
825 for n in 0..input_vcal_data.len() {
826 for k in 0..Self::NSKIP {
827 traces[ch][n][k] = f32::NAN;
828 rolled_traces[ch][n][k] = f32::NAN;
829 }
830 roll(&mut rolled_traces[ch][n],
834 input_vcal_data[n].header.stop_cell as isize);
835 }all_v_offsets[ch] = calculate_column_stat(&rolled_traces[ch], mean);
837 debug!("We calculated {} voltage offset values for ch {}", all_v_offsets[ch].len(), ch);
839 for (n, ev) in input_vcal_data.iter().enumerate() {
844 let mut v_offsets_rolled = all_v_offsets[ch].clone();
846 roll(&mut v_offsets_rolled, -1*ev.header.stop_cell as isize);
847 for k in 0..traces[ch][n].len() {
848 traces[ch][n][k] -= v_offsets_rolled[k];
849 }
850 }
851 let v_dips = calculate_column_stat(&traces[ch], median);
852 for k in 0..v_dips.len() {
853 if k < Self::NSKIP {
854 all_v_dips[ch][k] = 0.0;
855 } else {
856 all_v_dips[ch][k] = v_dips[k];
857 }
858 }
859 }
860 Ok((all_v_offsets, all_v_dips))
861 }
862
863
864 pub fn timing_calibration(&self,
871 edge : &Edge,
872 apply_vcal : bool)
873 -> Result<Vec<Vec<f32>>, CalibrationError> {
874 if self.tcal_data.len() == 0 {
875 error!("Input data for timing calibration is empty!");
876 return Err(CalibrationError::EmptyInputData);
877 }
878 let mut all_tcal = Vec::<Vec::<f32>>::new();
880 for _ in 0..NCHN {
881 all_tcal.push(Vec::<f32>::new());
882 }
883 let mut traces : Vec<Vec<Vec<f32>>>;
885 let mut stop_cells = Vec::<isize>::new();
886 if apply_vcal {
887 let result = self.apply_vcal(&self.tcal_data);
888 traces = result.0;
889 stop_cells = result.1;
890 } else {
891 warn!("Not applying voltage calibration to tcal data. This most likely makes no sense!");
892 traces = unpack_traces(&self.tcal_data);
893 for ev in self.tcal_data.iter() {
894 stop_cells.push(ev.header.event_id as isize);
895 }
896 }
897 let do_spike_cleaning = true;
898 if do_spike_cleaning {
899 for k_ch in 0..traces.len() {
900 for k_ev in 0..traces[k_ch].len() {
901 clean_spikes(&mut traces[k_ch][k_ev], true);
902 }
903 }
904 }
905 let nwords = traces[0][0].len();
906 for ch in 0..NCHN {
907 for ev in 0..traces[ch].len() {
908 for k in 0..nwords {
909 if k < Self::NSKIP {
910 traces[ch][ev][k] = f32::NAN;
911 }
912 if f32::abs(traces[ch][ev][k]) > Self::SINMAX as f32 {
913 traces[ch][ev][k] = f32::NAN;
914 }}
916 }
917 }
918
919 let mut rolled_traces = traces.clone();
920 let mut drolled_traces = traces.clone();
921 for ch in 0..NCHN {
922 for ev in 0..traces[ch].len() {
923 roll(&mut rolled_traces[ch][ev],
924 stop_cells[ev]);
925 }
926 }
927 for ch in 0..NCHN {
928 for ev in 0..traces[ch].len() {
929 for n in 0..traces[ch][ev].len() {
930 let mut dval : f32;
931 if n == traces[ch][ev].len() - 1 {
932 dval = rolled_traces[ch][ev][0] - rolled_traces[ch][ev][traces[ch][ev].len() -1];
933 } else {
934 dval = rolled_traces[ch][ev][n + 1] - rolled_traces[ch][ev][n];
935 }
936 match edge {
937 Edge::Rising | Edge::Average => {
938 if dval < 0.0 {
939 dval = f32::NAN;
940 }
941 },
942 Edge::Falling => {
943 if dval > 0.0 {
944 dval = f32::NAN;
945 }
946 },
947 _ => {
948 error!("Only average, rising or falling edge supported!");
950 }
951 } dval = f32::abs(dval);
953 if f32::abs(dval - 15.0) > Self::DVCUT {
954 dval = f32::NAN;
955 }
956 drolled_traces[ch][ev][n] = dval;
957 } } let col_means = calculate_column_stat(&drolled_traces[ch], mean);
960 let nfreq_vec : Vec<f32> = vec![1.0/Self::NOMINALFREQ;NWORDS];
961 all_tcal[ch] = nfreq_vec;
962 let ch_mean = mean(&col_means);
963 for n in 0..all_tcal[ch].len() {
964 all_tcal[ch][n] *= col_means[n]/ch_mean;
965 }
966 } Ok(all_tcal)
968 }
969
970 pub fn calibrate(&mut self) -> Result<(), CalibrationError>{
973 if self.vcal_data.len() == 0
974 || self.tcal_data.len() == 0
975 || self.noi_data.len() == 0 {
976 return Err(CalibrationError::EmptyInputData);
977 }
978 info!("Starting calculating voltage calibration constants!");
979 let (v_offsets_high, _v_dips_high)
980 = Self::voltage_offset_and_dips(&self.vcal_data)?;
981 let (v_offsets_low, v_dips_low)
982 = Self::voltage_offset_and_dips(&self.noi_data)?;
983 for ch in 0..NCHN {
985 for k in 0..NWORDS {
986 self.v_offsets[ch][k] = v_offsets_low[ch][k];
987 self.v_dips[ch][k] = v_dips_low[ch][k];
988 self.v_inc[ch][k] = self.d_v/(v_offsets_high[ch][k] - v_offsets_low[ch][k]);
989 }
990 }
991 info!("Filnished calculating voltage calibration constants!");
993 info!("Starting calculating timing calibration constants!");
994 warn!("Calibration only supported for Edge::Average!");
995 let mut edge = Edge::None;
999 if matches!(edge, Edge::None) {
1000 edge = Edge::Average;
1001 }
1002
1003 let mut tcal_av = self.timing_calibration( &edge, true)?;
1004 if matches!(edge, Edge::Average) {
1005 edge = Edge::Falling;
1006 let tcal_falling = self.timing_calibration(&edge, true)?;
1007 for ch in 0..NCHN {
1008 for k in 0..tcal_av.len() {
1009 tcal_av[ch][k] += tcal_falling[ch][k];
1010 tcal_av[ch][k] /= 2.0;
1011 }
1012 }
1013 edge = Edge::Rising;
1015 } else {
1016 error!("This is not implemented for any other case yet!");
1017 todo!();
1018 }
1019
1020 let mut damping : f32 = 0.1;
1023 let corr_limit : f32 = 0.05;
1024 let nperiod = Self::NOMINALFREQ/Self::CALFREQ;
1027 let global = true;
1028 if global {
1029
1030 let result = self.apply_vcal(&self.tcal_data);
1034 let tcal_traces = result.0;
1035 let stop_cells = result.1;
1036
1037 for ch in 0..NCHN {
1039 for ev in 0..tcal_traces[ch].len() {
1040 let tracelen = tcal_traces[ch][ev].len();
1041 let stop_cell = stop_cells[ev];
1042 let mut tcal_av_cp = tcal_av.clone();
1043 roll(&mut tcal_av_cp[ch], -1* (stop_cell as isize));
1044
1045 let (zcs, periods) = get_periods(&tcal_traces[ch][ev],
1046 &tcal_av_cp[ch],
1047 nperiod,
1048 Self::NSKIP as f32,
1049 &edge);
1050 debug!("Will iterate over {} periods!", periods.len());
1051 for (n_p,period) in periods.iter().enumerate() {
1052 if *period == 0.0 {
1053 warn!("period is 0 {:?}", periods);
1054 }
1055 if period.is_nan() {
1056 warn!("period is nan! {:?}", periods);
1057 }
1058 let zcs_a = zcs[n_p] + stop_cell as usize;
1059 let zcs_b = zcs[n_p + 1] + stop_cell as usize;
1060 let mut corr = (1.0/Self::CALFREQ)/period;
1061 if matches!(edge, Edge::None) {
1062 corr *= 0.5;
1063 }
1064 if f32::abs(corr - 1.0) > corr_limit {
1065 continue;
1066 }
1067 corr = (corr-1.0)*damping + 1.0;
1068 let zcs_a_tl = zcs_a%tracelen;
1069 let zcs_b_tl = zcs_b%tracelen;
1070 if zcs_a < tracelen && zcs_b > tracelen {
1071 for m in zcs_a..tcal_av[ch].len() {
1072 tcal_av[ch][m] *= corr;
1073 }
1074 for m in 0..zcs_b_tl {
1075 tcal_av[ch][m] *= corr;
1076 }
1077 } else {
1078 for m in zcs_a_tl..zcs_b_tl {
1079 tcal_av[ch][m] *= corr;
1080 }
1081 }
1082 }
1083 } damping *= 0.99;
1086 } } for ch in 0..NCHN {
1090 for k in 0..NWORDS {
1091 self.tbin[ch][k] = tcal_av[ch][k];
1092 }
1093 }
1094 Ok(())
1095 }
1096
1097 pub fn spike_cleaning(voltages : &mut Vec<Vec<f32>>,
1099 stop_cell : u16)
1100 -> Result<(), WaveformError> {
1101
1102 let mut spikes : [i32;10] = [0;10];
1103 let mut filter : f32;
1104 let mut dfilter : f32;
1105 let mut n_neighbor : usize;
1106 let mut n_rsp = 0usize;
1107 let mut rsp : [i32;10] = [-1;10];
1108 let mut sp : [[usize;10];NCHN] = [[0;10];NCHN];
1111 let mut n_sp : [usize;10] = [0;10];
1112
1113 for j in 0..NWORDS as usize {
1114 for i in 0..NCHN as usize {
1115 filter = -voltages[i][j] + voltages[i][(j + 1) % NWORDS] + voltages[i][(j + 2) % NWORDS] - voltages[i][(j + 3) % NWORDS];
1116 dfilter = filter + 2.0 * voltages[i][(j + 3) % NWORDS] + voltages[i][(j + 4) % NWORDS] - voltages[i][(j + 5) % NWORDS];
1117 if filter > 20.0 && filter < 100.0 {
1118 if n_sp[i] < 10 { sp[i][n_sp[i] as usize] = (j + 1) % NWORDS ;
1120 n_sp[i] += 1;
1121 } else {
1122 return Err(WaveformError::TooSpiky);
1123 } }else if dfilter > 40.0 && dfilter < 100.0 && filter > 10.0 {
1126 if n_sp[i] < 9 { sp[i][n_sp[i] as usize] = (j + 1) % NWORDS ;
1128 sp[i][(n_sp[i] + 1) as usize] = (j + 3) % NWORDS ;
1129 n_sp[i] += 2;
1130 } else {
1131 return Err(WaveformError::TooSpiky);
1132 } } }} for i in 0..NCHN {
1140 for j in 0..n_sp[i] as usize {
1141 n_neighbor = 0;
1143 for k in 0..NCHN {
1144 for l in 0..n_sp[k] as usize {
1145 if (sp[i][j] as i32 + sp[k][l] as i32 - 2 * stop_cell as i32) as i32 % NWORDS as i32 == 1022 {
1147 break;
1149 }
1150 }
1151 } for k in 0..NCHN {
1155 if i != k {
1156 for l in 0..n_sp[k] {
1157 if sp[i][j] == sp[k][l] {
1158 n_neighbor += 1;
1159 break;
1160 }
1161 } } } if n_neighbor >= 2 {
1166 for k in 0..n_rsp {
1167 if rsp[k] == sp[i][j] as i32 {break;} if n_rsp < 10 && k == n_rsp {
1169 rsp[n_rsp] = sp[i][j] as i32;
1170 n_rsp += 1;
1171 }
1172 }
1173 }
1174
1175 } } let magic_value : f32 = 14.8;
1181 let mut x : f32;
1182 let mut y : f32;
1183
1184 let mut skip_next : bool = false;
1185 for k in 0..n_rsp {
1186 if skip_next {
1187 skip_next = false;
1188 continue;
1189 }
1190 spikes[k] = rsp[k];
1191 for i in 0..NCHN {
1193 if k < n_rsp && i32::abs(rsp[k] as i32 - rsp[k + 1] as i32 % NWORDS as i32) == 2
1194 {
1195 let j = if rsp[k] > rsp[k + 1] {rsp[k + 1] as usize} else {rsp[k] as usize};
1197 x = voltages[i][(j - 1) % NWORDS];
1198 y = voltages[i][(j + 4) % NWORDS];
1199 if f32::abs(x - y) < 15.0 {
1200 voltages[i][j % NWORDS] = x + 1.0 * (y - x) / 5.0;
1201 voltages[i][(j + 1) % NWORDS] = x + 2.0 * (y - x) / 5.0;
1202 voltages[i][(j + 2) % NWORDS] = x + 3.0 * (y - x) / 5.0;
1203 voltages[i][(j + 3) % NWORDS] = x + 4.0 * (y - x) / 5.0;
1204 } else {
1205 voltages[i][j % NWORDS] -= magic_value;
1206 voltages[i][(j + 1) % NWORDS] -= magic_value;
1207 voltages[i][(j + 2) % NWORDS] -= magic_value;
1208 voltages[i][(j + 3) % NWORDS] -= magic_value;
1209 }
1210 } else {
1211 x = voltages[i][((rsp[k] - 1) % NWORDS as i32) as usize];
1213 y = voltages[i][(rsp[k] + 2) as usize % NWORDS];
1214 if f32::abs(x - y) < 15.0 {
1215 voltages[i][rsp[k] as usize] = x + 1.0 * (y - x) / 3.0;
1216 voltages[i][(rsp[k] + 1) as usize % NWORDS] = x + 2.0 * (y - x) / 3.0;
1217 } else {
1218 voltages[i][rsp[k] as usize] -= magic_value;
1219 voltages[i][(rsp[k] + 1) as usize % NWORDS] -= magic_value;
1220 }
1221 } } if k < n_rsp && i32::abs(rsp[k] - rsp[k + 1] % NWORDS as i32) == 2
1224 {skip_next = true;} } Ok(())
1227 }
1228
1229 pub fn voltages(&self,
1242 channel : usize,
1243 stop_cell : usize,
1244 adc : &Vec<u16>,
1245 waveform : &mut Vec<f32>) {
1246 if channel > 9 || channel == 0 {
1248 error!("There is no channel larger than 9 and no channel 0! Channel {channel} was requested. Can not perform voltage calibration!");
1249 return;
1250 }
1251 if adc.len() != waveform.len() {
1252 error!("Ch{} has {} adc values, however we are expecting {}!", channel, adc.len(), waveform.len());
1253 return;
1254 }
1255
1256 let mut value : f32;
1257 for k in 0..NWORDS {
1258 value = adc[k] as f32;
1259 value -= self.v_offsets[channel -1][(k + (stop_cell)) %NWORDS];
1260 value -= self.v_dips [channel -1][k];
1261 value *= self.v_inc [channel -1][(k + (stop_cell)) %NWORDS];
1262 waveform[k] = value;
1263 }
1264 }
1265
1266 pub fn nanoseconds(&self,
1276 channel : usize,
1277 stop_cell : usize,
1278 times : &mut Vec<f32>) {
1279 if channel > 9 || channel == 0 {
1280 error!("There is no channel larger than 9 and no channel 0! Channel {channel} was requested. Can not perform timing calibration!");
1281 }
1282 for k in 1..NWORDS {
1283 times[k] = times[k-1] + self.tbin[channel -1][(k-1+(stop_cell))%NWORDS];
1284 }
1285 }
1286
1287 pub fn new(rb_id : u8) -> Self {
1288 let timestamp = Utc::now().timestamp() as u32;
1289 Self {
1290 rb_id : rb_id,
1291 d_v : 182.0, timestamp : timestamp,
1293 serialize_event_data : false, v_offsets : [[0.0;NWORDS];NCHN],
1295 v_dips : [[0.0;NWORDS];NCHN],
1296 v_inc : [[0.0;NWORDS];NCHN],
1297 tbin : [[0.0;NWORDS];NCHN],
1298 vcal_data : Vec::<RBEvent>::new(),
1299 tcal_data : Vec::<RBEvent>::new(),
1300 noi_data : Vec::<RBEvent>::new()
1301 }
1302 }
1303
1304 pub fn discard_data(&mut self) {
1306 self.vcal_data = Vec::<RBEvent>::new();
1307 self.tcal_data = Vec::<RBEvent>::new();
1308 self.noi_data = Vec::<RBEvent>::new();
1309 }
1310
1311 pub fn from_file(filename : String, discard_data : bool) -> Result<Self, SerializationError> {
1317 let mut reader = TofPacketReader::new(&filename);
1318 loop {
1319 match reader.next() {
1320 None => {
1321 error!("Can't load calibration!");
1322 break;
1323 },
1324 Some(pack) => {
1325 if pack.packet_type == TofPacketType::RBCalibration {
1326 let mut cali = RBCalibrations::from_bytestream(&pack.payload, &mut 0)?;
1327 if discard_data {
1328 cali.discard_data();
1329 }
1330 return Ok(cali);
1331 } else {
1332 continue;
1333 }
1334 }
1335 }
1336 }
1337 Err(SerializationError::StreamTooShort)
1338 }
1339
1340
1341 pub fn get_id_from_filename(&mut self, filename : &Path) -> u8 {
1346 let rb_id : u8;
1347 match filename.file_name() {
1348 None => {
1349 error!("Path {} seems non-sensical!", filename.display());
1350 self.rb_id = 0;
1351 return 0;
1352 }
1353 Some(name) => {
1354 let fname = name.to_os_string().into_string().unwrap();
1356 let id = &fname[2..4];
1357 rb_id = id.parse::<u8>().unwrap();
1359 debug!("Extracted RB ID {} from filename {}", rb_id, fname);
1360 }
1361 }
1362 self.rb_id = rb_id;
1363 rb_id
1364 }
1365
1366 pub fn passes_timing_checks(&self) -> bool {
1368 for ch in 0..9 {
1369 let mut mean = 0.0;
1370 for k in 0..NWORDS {
1371 mean += self.tbin[ch][k];
1372 }
1373 mean /= NWORDS as f32;
1374 if mean < 0.48824 || mean > 0.48834 {
1375 error!("Timing calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1376 return false;
1377 }
1378 let tbin_ch = self.tbin[ch].to_vec();
1379 let var = standard_deviation(&tbin_ch).unwrap_or(0.0);
1380 if var < 0.08 || var > 0.15 {
1381 error!("Timing calibration for ch {}/ RB {} failed. Got st dev of {}", ch + 1, self.rb_id, var);
1382 return false;
1383 }
1384 }
1385 debug!("Passed timing calibration sanity checks for RB {}!", self.rb_id);
1386 true
1387 }
1388
1389 pub fn passes_voltage_checks(&self) -> bool {
1391 for ch in 0..9 {
1392 let mut mean = 0.0;
1393 for k in 0..NWORDS {
1394 mean += self.v_offsets[ch][k];
1395 }
1396 mean /= NWORDS as f32;
1397 if mean < 4200.0 || mean > 5200.0 {
1398 error!("Voltage offset calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1399 return false;
1400 }
1401 let v_off = self.tbin[ch].to_vec();
1402 let var = standard_deviation(&v_off).unwrap_or(0.0);
1403 if var > 150.0 {
1404 error!("Voltage offset calibration for ch {} / RB {} failed. Got st dev of {}", ch + 1, self.rb_id, var);
1405 return false;
1406 }
1407 mean = 0.0;
1408 for k in 0..NWORDS {
1409 mean += self.v_dips[ch][k];
1410 }
1411 mean /= NWORDS as f32;
1412 if mean < -0.5 || mean > 0.5 {
1413 error!("Voltage droop calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1414 return false;
1415 }
1416 mean = 0.0;
1417 for k in 0..NWORDS {
1418 mean += self.v_inc[ch][k];
1419 }
1420 mean /= NWORDS as f32;
1421 if mean < 0.06 || mean > 0.07 {
1422 error!("Voltage gain calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1423 return false;
1424 }
1425 let v_inc = self.v_inc[ch].to_vec();
1426 let var = standard_deviation(&v_inc).unwrap_or(0.0);
1427 if var > 0.00025 {
1428 error!("Voltage gain calibration for ch {} / RB {} failed. Got st dev of {}", ch + 1, self.rb_id, var);
1429 }
1431 }
1432 debug!("Passed voltage calibration sanity checks for RB {}!", self.rb_id);
1433 true
1434 }
1435
1436
1437 pub fn check(&self) -> bool {
1460 self.passes_timing_checks() && self.passes_voltage_checks()
1461 }
1462}
1463
1464impl TofPackable for RBCalibrations {
1465 const TOF_PACKET_TYPE : TofPacketType = TofPacketType::RBCalibration;
1466}
1467
1468impl Serialization for RBCalibrations {
1469 const SIZE : usize = NCHN*NWORDS*4*8 + 4 + 1 + 1;
1470 const HEAD : u16 = 0xAAAA; const TAIL : u16 = 0x5555; fn from_bytestream(bytestream : &Vec<u8>,
1474 pos : &mut usize)
1475 -> Result<Self, SerializationError> {
1476 let mut rb_cal = Self::new(0);
1477 if parse_u16(bytestream, pos) != Self::HEAD {
1478 return Err(SerializationError::HeadInvalid {});
1479 }
1480 rb_cal.rb_id = parse_u8(bytestream, pos);
1481 rb_cal.d_v = parse_f32(bytestream, pos);
1482 rb_cal.timestamp = parse_u32(bytestream, pos);
1483 rb_cal.serialize_event_data = parse_bool(bytestream, pos);
1484 for ch in 0..NCHN {
1485 for k in 0..NWORDS {
1486 let mut value = parse_f32(bytestream, pos);
1487 rb_cal.v_offsets[ch][k] = value;
1488 value = parse_f32(bytestream, pos);
1489 rb_cal.v_dips[ch][k] = value;
1490 value = parse_f32(bytestream, pos);
1491 rb_cal.v_inc[ch][k] = value;
1492 value = parse_f32(bytestream, pos);
1493 rb_cal.tbin[ch][k] = value;
1494 }
1495 }
1496 if rb_cal.serialize_event_data {
1497 let broken_event = RBEvent::new();
1498 let n_noi = parse_u16(bytestream, pos);
1499 debug!("Found {n_noi} no input data events!");
1500 for _ in 0..n_noi {
1501 match RBEvent::from_bytestream(bytestream, pos) {
1502 Ok(ev) => {
1503 rb_cal.noi_data.push(ev);
1504 }
1505 Err(err) => {
1506 error!("Unable to read RBEvent! {err}");
1507 }
1508 }
1509 }
1511 let n_vcal = parse_u16(bytestream, pos);
1512 debug!("Found {n_vcal} VCal data events!");
1513 for _ in 0..n_vcal {
1514 match RBEvent::from_bytestream(bytestream, pos) {
1515 Err(err) => {
1516 error!("Found broken event {err}");
1517 },
1518 Ok(good_ev) => {
1519 rb_cal.vcal_data.push(good_ev);
1520 }
1521 }
1522 }
1523 let n_tcal = parse_u16(bytestream, pos);
1524 debug!("Found {n_tcal} TCal data events!");
1525 for _ in 0..n_tcal {
1526 rb_cal.tcal_data.push(RBEvent::from_bytestream(bytestream, pos).unwrap_or(broken_event.clone()));
1527 }
1528 } else {
1529 *pos += 6;
1532 }
1533 if parse_u16(bytestream, pos) != Self::TAIL {
1534 return Err(SerializationError::TailInvalid {});
1535 }
1536 Ok(rb_cal)
1537 }
1538
1539 fn to_bytestream(&self) -> Vec<u8> {
1540 let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1541 bs.extend_from_slice(&Self::HEAD.to_le_bytes());
1542 bs.extend_from_slice(&self.rb_id.to_le_bytes());
1543 bs.extend_from_slice(&self.d_v.to_le_bytes());
1544 bs.extend_from_slice(&self.timestamp.to_le_bytes());
1545 let serialize_event_data = self.serialize_event_data as u8;
1546 bs.push(serialize_event_data);
1547 for ch in 0..NCHN {
1548 for k in 0..NWORDS {
1549 bs.extend_from_slice(&self.v_offsets[ch][k].to_le_bytes());
1550 bs.extend_from_slice(&self.v_dips[ch][k] .to_le_bytes());
1551 bs.extend_from_slice(&self.v_inc[ch][k] .to_le_bytes());
1552 bs.extend_from_slice(&self.tbin[ch][k] .to_le_bytes());
1553 }
1554 }
1555 if self.serialize_event_data {
1556 info!("Serializing calibration event data!");
1557 let n_noi = self.noi_data.len() as u16;
1558 let n_vcal = self.vcal_data.len() as u16;
1559 let n_tcal = self.tcal_data.len() as u16;
1560 bs.extend_from_slice(&n_noi.to_le_bytes());
1561 for ev in &self.noi_data {
1562 bs.extend_from_slice(&ev.to_bytestream());
1563 }
1564 bs.extend_from_slice(&n_vcal.to_le_bytes());
1565 for ev in &self.vcal_data {
1566 bs.extend_from_slice(&ev.to_bytestream());
1567 }
1568 bs.extend_from_slice(&n_tcal.to_le_bytes());
1569 for ev in &self.tcal_data {
1570 bs.extend_from_slice(&ev.to_bytestream());
1571 }
1572 } else { for _ in 0..6 {
1576 bs.push(0);
1577 }
1578 }
1582 bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1583 bs
1584 }
1585}
1586
1587impl Default for RBCalibrations {
1588 fn default() -> Self {
1589 Self::new(0)
1590 }
1591}
1592
1593impl fmt::Display for RBCalibrations {
1594 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1595 let mut timestamp_str = String::from("?");
1596 match Utc.timestamp_opt(self.timestamp.into(), 0) {
1597 LocalResult::Single(datetime_utc) => {
1598 timestamp_str = datetime_utc.format("%Y/%m/%d %H:%M:%S").to_string();
1599 },
1600 LocalResult::Ambiguous(_, _) => {
1601 println!("The given timestamp is ambiguous.");
1602 },
1603 LocalResult::None => {
1604 println!("The given timestamp is not valid.");
1605 },
1606 }
1607
1608 if !self.check() {
1610 return write!(f, "<RBCalibrations [{} UTC] for board {}:
1611 -- fails self checks!>", timestamp_str, self.rb_id);
1612 }
1613 write!(f,
1614 "<RBCalibrations [{} UTC]:
1615 ** all self checks passed! **
1616 RB : {}
1617 VCalData : {} (events)
1618 TCalData : {} (events)
1619 NoInputData : {} (events)
1620 V Offsets [ch0]: .. {:?} {:?} ..
1621 V Incrmts [ch0]: .. {:?} {:?} ..
1622 V Dips [ch0]: .. {:?} {:?} ..
1623 T Bins [ch0]: .. {:?} {:?} ..>",
1624 timestamp_str,
1625 self.rb_id,
1626 self.vcal_data.len(),
1627 self.tcal_data.len(),
1628 self.noi_data.len(),
1629 self.v_offsets[0][98],
1630 self.v_offsets[0][99],
1631 self.v_inc[0][98],
1632 self.v_inc[0][99],
1633 self.v_dips[0][98],
1634 self.v_dips[0][99],
1635 self.tbin[0][98],
1636 self.tbin[0][99])
1637 }
1638}
1639
1640#[cfg(feature = "pybindings")]
1641#[pymethods]
1642impl RBCalibrations {
1643 #[getter]
1644 fn rb_id(&self) -> u8 {
1645 self.rb_id
1646 }
1647
1648 #[getter]
1649 fn d_v(&self) -> f32 {
1650 self.d_v
1651 }
1652
1653 #[getter]
1654 fn vcal_data(&self) -> Vec<RBEvent> {
1655 self.vcal_data.clone()
1656 }
1657
1658 #[getter]
1659 fn tcal_data(&self) -> Vec<RBEvent> {
1660 self.tcal_data.clone()
1661 }
1662
1663 #[getter]
1664 fn noi_data(&self) -> Vec<RBEvent> {
1665 self.noi_data.clone()
1666 }
1667
1668 #[getter]
1669 fn v_offsets<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1670 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1671 for ch in 0..9 {
1672 data.push(self.v_offsets[ch].to_vec());
1673 }
1674 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1675 Ok(pyarray)
1676 }
1677
1678 #[getter]
1679 fn v_dips<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1680 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1681 for ch in 0..9 {
1682 data.push(self.v_dips[ch].to_vec());
1683 }
1684 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1685 Ok(pyarray)
1686 }
1687
1688 #[getter]
1689 fn v_inc<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1690 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1691 for ch in 0..9 {
1692 data.push(self.v_inc[ch].to_vec());
1693 }
1694 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1695 Ok(pyarray)
1696 }
1697
1698 #[getter]
1699 fn tbin<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1700 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1701 for ch in 0..9 {
1702 data.push(self.tbin[ch].to_vec());
1703 }
1704 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1705 Ok(pyarray)
1706 }
1707
1708 #[pyo3(name="voltages")]
1721 pub fn voltages_py<'_py>(&self,
1722 py : Python<'_py>,
1723 channel : usize,
1724 stop_cell : usize,
1725 adc : Bound<'_py, PyArray1<u16>>)
1726 -> PyResult<Bound<'_py, PyArray1<f32>>>{
1727 let mut voltages = vec![0.0f32; 1024];
1729 let adc_data = adc.to_vec().unwrap();
1730 self.voltages(channel, stop_cell, &adc_data, &mut voltages);
1731 let pyarray = PyArray1::from_vec(py, voltages);
1732 Ok(pyarray)
1733 }
1734
1735 #[pyo3(name="nanoseconds")]
1745 pub fn nanoseconds_py<'_py>(&self,
1746 py : Python<'_py>,
1747 channel : usize,
1748 stop_cell : usize)
1749 -> PyResult<Bound<'_py, PyArray1<f32>>> {
1750 let mut times = vec![0.0f32; 1024];
1751 self.nanoseconds(channel, stop_cell, &mut times);
1752 let pyarray = PyArray1::from_vec(py, times);
1753 Ok(pyarray)
1754 }
1755
1756 #[pyo3(name = "from_file", signature = (filename, discard_data = true))]
1764 #[staticmethod]
1765 fn from_file_py(filename : String, discard_data : bool) -> PyResult<Self> {
1766 let cali = RBCalibrations::from_file(filename, discard_data);
1767 match cali {
1768 Ok(c) => {
1769 return Ok(c);
1770 },
1771 Err(err) => {
1772 return Err(PyValueError::new_err(err.to_string()));
1773 }
1774 }
1775 }
1776
1777 #[pyo3(name="passes_voltage_checks")]
1778 fn passes_voltage_checks_py(&self) -> bool {
1779 self.passes_voltage_checks()
1780 }
1781
1782 #[pyo3(name="passes_timing_checks")]
1783 fn passes_timing_checks_py(&self) -> bool {
1784 self.passes_timing_checks()
1785 }
1786
1787 #[pyo3(name="check")]
1788 fn check_py(&self) -> bool {
1789 self.check()
1790 }
1791
1792 #[pyo3(name="assemble_from_flightcal")]
1793 #[staticmethod]
1794 pub fn assemble_from_flightcal_py(fcal_t : RBCalibrationFlightT,
1796 fcal_v : RBCalibrationFlightV) -> Self {
1797 Self::assemble_from_flightcal(fcal_t, fcal_v).unwrap()
1798 }
1799}
1800
1801#[cfg(feature = "pybindings")]
1802pythonize_packable_no_new!(RBCalibrations);
1803
1804#[cfg(feature = "random")]
1805impl FromRandom for RBCalibrations {
1806
1807 fn from_random() -> Self {
1808 let mut cali = Self::new(0);
1809 let mut rng = rand::rng();
1810 cali.rb_id = rng.random::<u8>();
1811 cali.d_v = rng.random::<f32>();
1812 cali.serialize_event_data = rng.random::<bool>();
1813 for ch in 0..NCHN {
1814 for n in 0..NWORDS {
1815 cali.v_offsets[ch][n] = rng.random::<f32>();
1816 cali.v_dips [ch][n] = rng.random::<f32>();
1817 cali.v_inc [ch][n] = rng.random::<f32>();
1818 cali.tbin [ch][n] = rng.random::<f32>();
1819 }
1820 }
1821 if cali.serialize_event_data {
1822 for _ in 0..1000 {
1823 let mut ev = RBEvent::from_random();
1824 cali.vcal_data.push(ev);
1825 ev = RBEvent::from_random();
1826 cali.noi_data.push(ev);
1827 ev = RBEvent::from_random();
1828 cali.tcal_data.push(ev);
1829 }
1830 }
1831 cali
1832 }
1833}
1834
1835#[cfg(feature = "random")]
1838#[test]
1839fn serialization_rbcalibration_noeventpayload() {
1840 let mut calis = Vec::<RBCalibrations>::new();
1841 for _ in 0..100 {
1842 let cali = RBCalibrations::from_random();
1843 if cali.serialize_event_data {
1844 continue;
1845 }
1846 calis.push(cali);
1847 break;
1848 }
1849 let test = RBCalibrations::from_bytestream(&calis[0].to_bytestream(), &mut 0).unwrap();
1850 assert_eq!(calis[0], test);
1851}
1852
1853#[cfg(feature = "random")]
1854#[test]
1855fn serialization_rbcalibration_witheventpayload() {
1856 loop {
1857 let cali = RBCalibrations::from_random();
1858 if !cali.serialize_event_data {
1859 continue;
1860 }
1861 let mut test = RBCalibrations::from_bytestream(&cali.to_bytestream(), &mut 0).unwrap();
1862 for k in &mut test.vcal_data {
1863 k.creation_time = None;
1864 }
1865 for k in &mut test.tcal_data {
1866 k.creation_time = None;
1867 }
1868 for k in &mut test.noi_data {
1869 k.creation_time = None;
1870 }
1871 assert_eq!(cali, test);
1872 break;
1873 }
1874}
1875
1876#[cfg(feature = "random")]
1877#[test]
1878fn pack_rbcalibrationfilghtt() {
1879 for _ in 0..100 {
1880 let cfg = RBCalibrationFlightT::from_random();
1881 let test : RBCalibrationFlightT = cfg.pack().unpack().unwrap();
1882 assert_eq!(cfg, test);
1883 }
1884}
1885
1886#[cfg(feature = "random")]
1887#[test]
1888fn pack_rbcalibfilghtv() {
1889 for _ in 0..100 {
1890 let cfg = RBCalibrationFlightV::from_random();
1891 let test : RBCalibrationFlightV = cfg.pack().unpack().unwrap();
1892 assert_eq!(cfg, test);
1893 }
1894}
1895
1896#[cfg(feature = "random")]
1897#[test]
1898fn assemble_flightcal() {
1899 for _ in 0..10 {
1900 let cal = RBCalibrations::from_random();
1901 let fct = cal.emit_flighttcal();
1902 let fcv = cal.emit_flightvcal();
1903 let test = RBCalibrations::assemble_from_flightcal(fct, fcv).unwrap();
1904 assert_eq!(cal.rb_id, test.rb_id);
1905 assert_eq!(cal.d_v , test.d_v);
1906 assert_eq!(cal.timestamp, test.timestamp);
1907 assert_eq!(test.serialize_event_data, false);
1908 for ch in 0..NCHN {
1909 for k in 0..NWORDS {
1910 assert_eq!(f16::from_f32(cal.tbin[ch][k]), f16::from_f32(test.tbin[ch][k]));
1911 assert_eq!(f16::from_f32(cal.v_offsets[ch][k]),f16::from_f32(test.v_offsets[ch][k]));
1912 assert_eq!(f16::from_f32(cal.v_dips[ch][k]), f16::from_f32(test.v_dips[ch][k]));
1913 assert_eq!(f16::from_f32(cal.v_inc[ch][k]), f16::from_f32(test.v_inc[ch][k]));
1914 }
1915 }
1916 }
1917}
1918
1919