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)]
638#[cfg_attr(feature="pybindings", pyclass)]
639pub struct RBCalibrations {
640 pub rb_id : u8,
641 pub d_v : f32, pub timestamp : u32, pub serialize_event_data : bool,
645 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>,
653 pub tcal_data : Vec::<RBEvent>,
654 pub noi_data : Vec::<RBEvent>,
655}
656
657impl RBCalibrations {
658 pub const NSKIP : usize = 2;
662 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,
670 fcal_v : RBCalibrationFlightV)
671 -> Result<Self, CalibrationError> {
672 if (fcal_t.timestamp != fcal_v.timestamp) || (fcal_t.rb_id != fcal_v.rb_id) {
673 error!("These calibrations do not match! {} , {}", fcal_t, fcal_v);
674 return Err(CalibrationError::IncompatibleFlightCalibrations);
675 }
676 let mut cal = Self::new(fcal_t.rb_id);
677 cal.rb_id = fcal_t.rb_id;
678 cal.timestamp = fcal_t.timestamp;
679 cal.d_v = fcal_v.d_v;
680 cal.serialize_event_data = false;
681 for ch in 0..NCHN {
682 for k in 0..NWORDS {
683 cal.tbin [ch][k] = f16::to_f32(fcal_t.tbins[ch][k]);
684 cal.v_offsets[ch][k] = f16::to_f32(fcal_v.v_offsets[ch][k]);
685 cal.v_dips [ch][k] = f16::to_f32(fcal_v.v_dips[ch][k]);
686 cal.v_inc [ch][k] = f16::to_f32(fcal_v.v_inc[ch][k]);
687 }
688 }
689 Ok(cal)
690 }
691
692 pub fn emit_flighttcal(&self) -> RBCalibrationFlightT {
697 let mut cal = RBCalibrationFlightT::new();
698 cal.rb_id = self.rb_id;
699 cal.timestamp = self.timestamp;
700 for ch in 0..NCHN {
701 for n in 0..NWORDS {
702 cal.tbins [ch][n] = f16::from_f32(self.tbin[ch][n]);
703 }
704 }
705 cal
706 }
707
708 pub fn emit_flightvcal(&self) -> RBCalibrationFlightV {
713 let mut cal = RBCalibrationFlightV::new();
714 cal.rb_id = self.rb_id;
715 cal.timestamp = self.timestamp;
716 cal.d_v = self.d_v;
717 for ch in 0..NCHN {
718 for n in 0..NWORDS {
719 cal.v_offsets [ch][n] = f16::from_f32(self.v_offsets[ch][n]);
720 cal.v_dips [ch][n] = f16::from_f32(self.v_dips[ch][n]);
721 cal.v_inc [ch][n] = f16::from_f32(self.v_inc[ch][n]);
722 }
723 }
724 cal
725 }
726
727 pub fn clean_input_data(&mut self) {
729 self.vcal_data.retain(|x| !x.header.drs_lost_trigger()
730 && !x.header.is_event_fragment()
731 && x.trace_check());
732 self.tcal_data.retain(|x| !x.header.drs_lost_trigger()
733 && !x.header.is_event_fragment()
734 && x.trace_check());
735 self.noi_data.retain(|x| !x.header.drs_lost_trigger()
736 && !x.header.is_event_fragment()
737 && x.trace_check());
738 self.noi_data.sort_by(|a, b| a.header.event_id.cmp(&b.header.event_id));
739 self.vcal_data.sort_by(|a, b| a.header.event_id.cmp(&b.header.event_id));
740 self.tcal_data.sort_by(|a, b| a.header.event_id.cmp(&b.header.event_id));
741 }
742
743 fn apply_vcal(&self,
746 data : &Vec<RBEvent>)
747 -> (Vec<Vec<Vec<f32>>>,Vec<isize>) {
748 let nevents = data.len();
749 let mut traces = Vec::<Vec::<Vec::<f32>>>::new();
750 let mut trace = Vec::<f32>::with_capacity(NWORDS);
751 let mut stop_cells = Vec::<isize>::new();
752 let mut empty_events = Vec::<Vec::<f32>>::new();
753 for _ in 0..nevents {
754 empty_events.push(trace.clone());
755 }
756 for ch in 0..NCHN {
757 traces.push(empty_events.clone());
758 for (k,ev) in data.iter().enumerate() {
759 trace.clear();
760 stop_cells.push(ev.header.stop_cell as isize);
761 for k in 0..NWORDS {
762 trace.push(ev.adc[ch][k] as f32);
763 }
764 self.voltages(ch + 1, ev.header.stop_cell as usize,
765 &ev.adc[ch], &mut trace);
766 traces[ch][k] = trace.clone();
767 }
768 }
769 (traces, stop_cells)
770 }
771
772 pub fn apply_vcal_constants(&self,
774 adc : &Vec<f32>,
775 channel : usize,
776 stop_cell : usize) -> Vec<f32> {
777 let mut waveform = Vec::<f32>::with_capacity(adc.len());
778 let mut value : f32;
779 for k in 0..adc.len() {
780 value = adc[k] as f32;
781 value -= self.v_offsets[channel][(k + (stop_cell)) %NWORDS];
782 value -= self.v_dips [channel][k];
783 value *= self.v_inc [channel][(k + (stop_cell)) %NWORDS];
784 waveform.push(value);
785 }
786 waveform
787 }
788
789 fn voltage_offset_and_dips(input_vcal_data : &Vec<RBEvent>)
796 -> Result<(Vec<Vec<f32>>, Vec<Vec<f32>>), CalibrationError> {
797 if input_vcal_data.len() == 0 {
798 return Err(CalibrationError::EmptyInputData);
799 }
800 let mut all_v_offsets = Vec::<Vec::<f32>>::new();
801 let mut all_v_dips = Vec::<Vec::<f32>>::new();
802 let nchn = input_vcal_data[0].header.get_nchan();
803
804 debug!("Found {nchn} channels!");
805 for _ in 0..nchn {
806 let empty_vec_off : Vec<f32> = vec![0.0;NWORDS];
807 all_v_offsets.push(empty_vec_off);
808 let empty_vec_dip : Vec<f32> = vec![0.0;NWORDS];
809 all_v_dips.push(empty_vec_dip);
810 }
811
812 let mut traces = unpack_traces(&input_vcal_data);
815 let mut rolled_traces = traces.clone();
816 for ch in 0..nchn {
817 for n in 0..input_vcal_data.len() {
818 for k in 0..Self::NSKIP {
819 traces[ch][n][k] = f32::NAN;
820 rolled_traces[ch][n][k] = f32::NAN;
821 }
822 roll(&mut rolled_traces[ch][n],
826 input_vcal_data[n].header.stop_cell as isize);
827 }all_v_offsets[ch] = calculate_column_stat(&rolled_traces[ch], mean);
829 debug!("We calculated {} voltage offset values for ch {}", all_v_offsets[ch].len(), ch);
831 for (n, ev) in input_vcal_data.iter().enumerate() {
836 let mut v_offsets_rolled = all_v_offsets[ch].clone();
838 roll(&mut v_offsets_rolled, -1*ev.header.stop_cell as isize);
839 for k in 0..traces[ch][n].len() {
840 traces[ch][n][k] -= v_offsets_rolled[k];
841 }
842 }
843 let v_dips = calculate_column_stat(&traces[ch], median);
844 for k in 0..v_dips.len() {
845 if k < Self::NSKIP {
846 all_v_dips[ch][k] = 0.0;
847 } else {
848 all_v_dips[ch][k] = v_dips[k];
849 }
850 }
851 }
852 Ok((all_v_offsets, all_v_dips))
853 }
854
855
856 pub fn timing_calibration(&self,
863 edge : &Edge,
864 apply_vcal : bool)
865 -> Result<Vec<Vec<f32>>, CalibrationError> {
866 if self.tcal_data.len() == 0 {
867 error!("Input data for timing calibration is empty!");
868 return Err(CalibrationError::EmptyInputData);
869 }
870 let mut all_tcal = Vec::<Vec::<f32>>::new();
872 for _ in 0..NCHN {
873 all_tcal.push(Vec::<f32>::new());
874 }
875 let mut traces : Vec<Vec<Vec<f32>>>;
877 let mut stop_cells = Vec::<isize>::new();
878 if apply_vcal {
879 let result = self.apply_vcal(&self.tcal_data);
880 traces = result.0;
881 stop_cells = result.1;
882 } else {
883 warn!("Not applying voltage calibration to tcal data. This most likely makes no sense!");
884 traces = unpack_traces(&self.tcal_data);
885 for ev in self.tcal_data.iter() {
886 stop_cells.push(ev.header.event_id as isize);
887 }
888 }
889 let do_spike_cleaning = true;
890 if do_spike_cleaning {
891 for k_ch in 0..traces.len() {
892 for k_ev in 0..traces[k_ch].len() {
893 clean_spikes(&mut traces[k_ch][k_ev], true);
894 }
895 }
896 }
897 let nwords = traces[0][0].len();
898 for ch in 0..NCHN {
899 for ev in 0..traces[ch].len() {
900 for k in 0..nwords {
901 if k < Self::NSKIP {
902 traces[ch][ev][k] = f32::NAN;
903 }
904 if f32::abs(traces[ch][ev][k]) > Self::SINMAX as f32 {
905 traces[ch][ev][k] = f32::NAN;
906 }}
908 }
909 }
910
911 let mut rolled_traces = traces.clone();
912 let mut drolled_traces = traces.clone();
913 for ch in 0..NCHN {
914 for ev in 0..traces[ch].len() {
915 roll(&mut rolled_traces[ch][ev],
916 stop_cells[ev]);
917 }
918 }
919 for ch in 0..NCHN {
920 for ev in 0..traces[ch].len() {
921 for n in 0..traces[ch][ev].len() {
922 let mut dval : f32;
923 if n == traces[ch][ev].len() - 1 {
924 dval = rolled_traces[ch][ev][0] - rolled_traces[ch][ev][traces[ch][ev].len() -1];
925 } else {
926 dval = rolled_traces[ch][ev][n + 1] - rolled_traces[ch][ev][n];
927 }
928 match edge {
929 Edge::Rising | Edge::Average => {
930 if dval < 0.0 {
931 dval = f32::NAN;
932 }
933 },
934 Edge::Falling => {
935 if dval > 0.0 {
936 dval = f32::NAN;
937 }
938 },
939 _ => {
940 error!("Only average, rising or falling edge supported!");
942 }
943 } dval = f32::abs(dval);
945 if f32::abs(dval - 15.0) > Self::DVCUT {
946 dval = f32::NAN;
947 }
948 drolled_traces[ch][ev][n] = dval;
949 } } let col_means = calculate_column_stat(&drolled_traces[ch], mean);
952 let nfreq_vec : Vec<f32> = vec![1.0/Self::NOMINALFREQ;NWORDS];
953 all_tcal[ch] = nfreq_vec;
954 let ch_mean = mean(&col_means);
955 for n in 0..all_tcal[ch].len() {
956 all_tcal[ch][n] *= col_means[n]/ch_mean;
957 }
958 } Ok(all_tcal)
960 }
961
962 pub fn calibrate(&mut self) -> Result<(), CalibrationError>{
965 if self.vcal_data.len() == 0
966 || self.tcal_data.len() == 0
967 || self.noi_data.len() == 0 {
968 return Err(CalibrationError::EmptyInputData);
969 }
970 info!("Starting calculating voltage calibration constants!");
971 let (v_offsets_high, _v_dips_high)
972 = Self::voltage_offset_and_dips(&self.vcal_data)?;
973 let (v_offsets_low, v_dips_low)
974 = Self::voltage_offset_and_dips(&self.noi_data)?;
975 for ch in 0..NCHN {
977 for k in 0..NWORDS {
978 self.v_offsets[ch][k] = v_offsets_low[ch][k];
979 self.v_dips[ch][k] = v_dips_low[ch][k];
980 self.v_inc[ch][k] = self.d_v/(v_offsets_high[ch][k] - v_offsets_low[ch][k]);
981 }
982 }
983 info!("Filnished calculating voltage calibration constants!");
985 info!("Starting calculating timing calibration constants!");
986 warn!("Calibration only supported for Edge::Average!");
987 let mut edge = Edge::None;
991 if matches!(edge, Edge::None) {
992 edge = Edge::Average;
993 }
994
995 let mut tcal_av = self.timing_calibration( &edge, true)?;
996 if matches!(edge, Edge::Average) {
997 edge = Edge::Falling;
998 let tcal_falling = self.timing_calibration(&edge, true)?;
999 for ch in 0..NCHN {
1000 for k in 0..tcal_av.len() {
1001 tcal_av[ch][k] += tcal_falling[ch][k];
1002 tcal_av[ch][k] /= 2.0;
1003 }
1004 }
1005 edge = Edge::Rising;
1007 } else {
1008 error!("This is not implemented for any other case yet!");
1009 todo!();
1010 }
1011
1012 let mut damping : f32 = 0.1;
1015 let corr_limit : f32 = 0.05;
1016 let nperiod = Self::NOMINALFREQ/Self::CALFREQ;
1019 let global = true;
1020 if global {
1021
1022 let result = self.apply_vcal(&self.tcal_data);
1026 let tcal_traces = result.0;
1027 let stop_cells = result.1;
1028
1029 for ch in 0..NCHN {
1031 for ev in 0..tcal_traces[ch].len() {
1032 let tracelen = tcal_traces[ch][ev].len();
1033 let stop_cell = stop_cells[ev];
1034 let mut tcal_av_cp = tcal_av.clone();
1035 roll(&mut tcal_av_cp[ch], -1* (stop_cell as isize));
1036
1037 let (zcs, periods) = get_periods(&tcal_traces[ch][ev],
1038 &tcal_av_cp[ch],
1039 nperiod,
1040 Self::NSKIP as f32,
1041 &edge);
1042 debug!("Will iterate over {} periods!", periods.len());
1043 for (n_p,period) in periods.iter().enumerate() {
1044 if *period == 0.0 {
1045 warn!("period is 0 {:?}", periods);
1046 }
1047 if period.is_nan() {
1048 warn!("period is nan! {:?}", periods);
1049 }
1050 let zcs_a = zcs[n_p] + stop_cell as usize;
1051 let zcs_b = zcs[n_p + 1] + stop_cell as usize;
1052 let mut corr = (1.0/Self::CALFREQ)/period;
1053 if matches!(edge, Edge::None) {
1054 corr *= 0.5;
1055 }
1056 if f32::abs(corr - 1.0) > corr_limit {
1057 continue;
1058 }
1059 corr = (corr-1.0)*damping + 1.0;
1060 let zcs_a_tl = zcs_a%tracelen;
1061 let zcs_b_tl = zcs_b%tracelen;
1062 if zcs_a < tracelen && zcs_b > tracelen {
1063 for m in zcs_a..tcal_av[ch].len() {
1064 tcal_av[ch][m] *= corr;
1065 }
1066 for m in 0..zcs_b_tl {
1067 tcal_av[ch][m] *= corr;
1068 }
1069 } else {
1070 for m in zcs_a_tl..zcs_b_tl {
1071 tcal_av[ch][m] *= corr;
1072 }
1073 }
1074 }
1075 } damping *= 0.99;
1078 } } for ch in 0..NCHN {
1082 for k in 0..NWORDS {
1083 self.tbin[ch][k] = tcal_av[ch][k];
1084 }
1085 }
1086 Ok(())
1087 }
1088
1089 pub fn spike_cleaning(voltages : &mut Vec<Vec<f32>>,
1091 stop_cell : u16)
1092 -> Result<(), WaveformError> {
1093
1094 let mut spikes : [i32;10] = [0;10];
1095 let mut filter : f32;
1096 let mut dfilter : f32;
1097 let mut n_neighbor : usize;
1098 let mut n_rsp = 0usize;
1099 let mut rsp : [i32;10] = [-1;10];
1100 let mut sp : [[usize;10];NCHN] = [[0;10];NCHN];
1103 let mut n_sp : [usize;10] = [0;10];
1104
1105 for j in 0..NWORDS as usize {
1106 for i in 0..NCHN as usize {
1107 filter = -voltages[i][j] + voltages[i][(j + 1) % NWORDS] + voltages[i][(j + 2) % NWORDS] - voltages[i][(j + 3) % NWORDS];
1108 dfilter = filter + 2.0 * voltages[i][(j + 3) % NWORDS] + voltages[i][(j + 4) % NWORDS] - voltages[i][(j + 5) % NWORDS];
1109 if filter > 20.0 && filter < 100.0 {
1110 if n_sp[i] < 10 { sp[i][n_sp[i] as usize] = (j + 1) % NWORDS ;
1112 n_sp[i] += 1;
1113 } else {
1114 return Err(WaveformError::TooSpiky);
1115 } }else if dfilter > 40.0 && dfilter < 100.0 && filter > 10.0 {
1118 if n_sp[i] < 9 { sp[i][n_sp[i] as usize] = (j + 1) % NWORDS ;
1120 sp[i][(n_sp[i] + 1) as usize] = (j + 3) % NWORDS ;
1121 n_sp[i] += 2;
1122 } else {
1123 return Err(WaveformError::TooSpiky);
1124 } } }} for i in 0..NCHN {
1132 for j in 0..n_sp[i] as usize {
1133 n_neighbor = 0;
1135 for k in 0..NCHN {
1136 for l in 0..n_sp[k] as usize {
1137 if (sp[i][j] as i32 + sp[k][l] as i32 - 2 * stop_cell as i32) as i32 % NWORDS as i32 == 1022 {
1139 break;
1141 }
1142 }
1143 } for k in 0..NCHN {
1147 if i != k {
1148 for l in 0..n_sp[k] {
1149 if sp[i][j] == sp[k][l] {
1150 n_neighbor += 1;
1151 break;
1152 }
1153 } } } if n_neighbor >= 2 {
1158 for k in 0..n_rsp {
1159 if rsp[k] == sp[i][j] as i32 {break;} if n_rsp < 10 && k == n_rsp {
1161 rsp[n_rsp] = sp[i][j] as i32;
1162 n_rsp += 1;
1163 }
1164 }
1165 }
1166
1167 } } let magic_value : f32 = 14.8;
1173 let mut x : f32;
1174 let mut y : f32;
1175
1176 let mut skip_next : bool = false;
1177 for k in 0..n_rsp {
1178 if skip_next {
1179 skip_next = false;
1180 continue;
1181 }
1182 spikes[k] = rsp[k];
1183 for i in 0..NCHN {
1185 if k < n_rsp && i32::abs(rsp[k] as i32 - rsp[k + 1] as i32 % NWORDS as i32) == 2
1186 {
1187 let j = if rsp[k] > rsp[k + 1] {rsp[k + 1] as usize} else {rsp[k] as usize};
1189 x = voltages[i][(j - 1) % NWORDS];
1190 y = voltages[i][(j + 4) % NWORDS];
1191 if f32::abs(x - y) < 15.0 {
1192 voltages[i][j % NWORDS] = x + 1.0 * (y - x) / 5.0;
1193 voltages[i][(j + 1) % NWORDS] = x + 2.0 * (y - x) / 5.0;
1194 voltages[i][(j + 2) % NWORDS] = x + 3.0 * (y - x) / 5.0;
1195 voltages[i][(j + 3) % NWORDS] = x + 4.0 * (y - x) / 5.0;
1196 } else {
1197 voltages[i][j % NWORDS] -= magic_value;
1198 voltages[i][(j + 1) % NWORDS] -= magic_value;
1199 voltages[i][(j + 2) % NWORDS] -= magic_value;
1200 voltages[i][(j + 3) % NWORDS] -= magic_value;
1201 }
1202 } else {
1203 x = voltages[i][((rsp[k] - 1) % NWORDS as i32) as usize];
1205 y = voltages[i][(rsp[k] + 2) as usize % NWORDS];
1206 if f32::abs(x - y) < 15.0 {
1207 voltages[i][rsp[k] as usize] = x + 1.0 * (y - x) / 3.0;
1208 voltages[i][(rsp[k] + 1) as usize % NWORDS] = x + 2.0 * (y - x) / 3.0;
1209 } else {
1210 voltages[i][rsp[k] as usize] -= magic_value;
1211 voltages[i][(rsp[k] + 1) as usize % NWORDS] -= magic_value;
1212 }
1213 } } if k < n_rsp && i32::abs(rsp[k] - rsp[k + 1] % NWORDS as i32) == 2
1216 {skip_next = true;} } Ok(())
1219 }
1220
1221 pub fn voltages(&self,
1234 channel : usize,
1235 stop_cell : usize,
1236 adc : &Vec<u16>,
1237 waveform : &mut Vec<f32>) {
1238 if channel > 9 || channel == 0 {
1240 error!("There is no channel larger than 9 and no channel 0! Channel {channel} was requested. Can not perform voltage calibration!");
1241 return;
1242 }
1243 if adc.len() != waveform.len() {
1244 error!("Ch{} has {} adc values, however we are expecting {}!", channel, adc.len(), waveform.len());
1245 return;
1246 }
1247
1248 let mut value : f32;
1249 for k in 0..NWORDS {
1250 value = adc[k] as f32;
1251 value -= self.v_offsets[channel -1][(k + (stop_cell)) %NWORDS];
1252 value -= self.v_dips [channel -1][k];
1253 value *= self.v_inc [channel -1][(k + (stop_cell)) %NWORDS];
1254 waveform[k] = value;
1255 }
1256 }
1257
1258 pub fn nanoseconds(&self,
1268 channel : usize,
1269 stop_cell : usize,
1270 times : &mut Vec<f32>) {
1271 if channel > 9 || channel == 0 {
1272 error!("There is no channel larger than 9 and no channel 0! Channel {channel} was requested. Can not perform timing calibration!");
1273 }
1274 for k in 1..NWORDS {
1275 times[k] = times[k-1] + self.tbin[channel -1][(k-1+(stop_cell))%NWORDS];
1276 }
1277 }
1278
1279 pub fn new(rb_id : u8) -> Self {
1280 let timestamp = Utc::now().timestamp() as u32;
1281 Self {
1282 rb_id : rb_id,
1283 d_v : 182.0, timestamp : timestamp,
1285 serialize_event_data : false, v_offsets : [[0.0;NWORDS];NCHN],
1287 v_dips : [[0.0;NWORDS];NCHN],
1288 v_inc : [[0.0;NWORDS];NCHN],
1289 tbin : [[0.0;NWORDS];NCHN],
1290 vcal_data : Vec::<RBEvent>::new(),
1291 tcal_data : Vec::<RBEvent>::new(),
1292 noi_data : Vec::<RBEvent>::new()
1293 }
1294 }
1295
1296 pub fn discard_data(&mut self) {
1298 self.vcal_data = Vec::<RBEvent>::new();
1299 self.tcal_data = Vec::<RBEvent>::new();
1300 self.noi_data = Vec::<RBEvent>::new();
1301 }
1302
1303 pub fn from_file(filename : String, discard_data : bool) -> Result<Self, SerializationError> {
1309 let mut reader = TofPacketReader::new(&filename);
1310 loop {
1311 match reader.next() {
1312 None => {
1313 error!("Can't load calibration!");
1314 break;
1315 },
1316 Some(pack) => {
1317 if pack.packet_type == TofPacketType::RBCalibration {
1318 let mut cali = RBCalibrations::from_bytestream(&pack.payload, &mut 0)?;
1319 if discard_data {
1320 cali.discard_data();
1321 }
1322 return Ok(cali);
1323 } else {
1324 continue;
1325 }
1326 }
1327 }
1328 }
1329 Err(SerializationError::StreamTooShort)
1330 }
1331
1332
1333 pub fn get_id_from_filename(&mut self, filename : &Path) -> u8 {
1338 let rb_id : u8;
1339 match filename.file_name() {
1340 None => {
1341 error!("Path {} seems non-sensical!", filename.display());
1342 self.rb_id = 0;
1343 return 0;
1344 }
1345 Some(name) => {
1346 let fname = name.to_os_string().into_string().unwrap();
1348 let id = &fname[2..4];
1349 rb_id = id.parse::<u8>().unwrap();
1351 debug!("Extracted RB ID {} from filename {}", rb_id, fname);
1352 }
1353 }
1354 self.rb_id = rb_id;
1355 rb_id
1356 }
1357
1358 pub fn passes_timing_checks(&self) -> bool {
1360 for ch in 0..8 {
1361 let mut mean = 0.0;
1362 for k in 0..NWORDS {
1363 mean += self.tbin[ch][k];
1364 }
1365 mean /= NWORDS as f32;
1366 if mean < 0.48824 || mean > 0.48834 {
1367 error!("Timing calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1368 return false;
1369 }
1370 let tbin_ch = self.tbin[ch].to_vec();
1371 let var = standard_deviation(&tbin_ch).unwrap_or(0.0);
1372 if var < 0.08 || var > 0.15 {
1373 error!("Timing calibration for ch {}/ RB {} failed. Got st dev of {}", ch + 1, self.rb_id, var);
1374 return false;
1375 }
1376 }
1377 debug!("Passed timing calibration sanity checks for RB {}!", self.rb_id);
1378 true
1379 }
1380
1381 pub fn passes_voltage_checks(&self) -> bool {
1383 for ch in 0..8 {
1384 let mut mean = 0.0;
1385 for k in 0..NWORDS {
1386 mean += self.v_offsets[ch][k];
1387 }
1388 mean /= NWORDS as f32;
1389 if mean < 4200.0 || mean > 5200.0 {
1390 error!("Voltage offset calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1391 return false;
1392 }
1393 let v_off = self.tbin[ch].to_vec();
1394 let var = standard_deviation(&v_off).unwrap_or(0.0);
1395 if var > 150.0 {
1396 error!("Voltage offset calibration for ch {} / RB {} failed. Got st dev of {}", ch + 1, self.rb_id, var);
1397 return false;
1398 }
1399 mean = 0.0;
1400 for k in 0..NWORDS {
1401 mean += self.v_dips[ch][k];
1402 }
1403 mean /= NWORDS as f32;
1404 if mean < -0.5 || mean > 0.5 {
1405 error!("Voltage droop calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1406 return false;
1407 }
1408 mean = 0.0;
1409 for k in 0..NWORDS {
1410 mean += self.v_inc[ch][k];
1411 }
1412 mean /= NWORDS as f32;
1413 if mean < 0.06 || mean > 0.07 {
1414 error!("Voltage gain calibration for ch {} / RB {} failed. Got mean of {}", ch + 1, self.rb_id, mean);
1415 return false;
1416 }
1417 let v_inc = self.v_inc[ch].to_vec();
1418 let var = standard_deviation(&v_inc).unwrap_or(0.0);
1419 if var > 0.00025 {
1420 error!("Voltage gain calibration for ch {} / RB {} failed. Got st dev of {}", ch + 1, self.rb_id, var);
1421 }
1423 }
1424 debug!("Passed voltage calibration sanity checks for RB {}!", self.rb_id);
1425 true
1426 }
1427
1428
1429 pub fn check(&self) -> bool {
1452 self.passes_timing_checks() && self.passes_voltage_checks()
1453 }
1454}
1455
1456impl TofPackable for RBCalibrations {
1457 const TOF_PACKET_TYPE : TofPacketType = TofPacketType::RBCalibration;
1458}
1459
1460impl Serialization for RBCalibrations {
1461 const SIZE : usize = NCHN*NWORDS*4*8 + 4 + 1 + 1;
1462 const HEAD : u16 = 0xAAAA; const TAIL : u16 = 0x5555; fn from_bytestream(bytestream : &Vec<u8>,
1466 pos : &mut usize)
1467 -> Result<Self, SerializationError> {
1468 let mut rb_cal = Self::new(0);
1469 if parse_u16(bytestream, pos) != Self::HEAD {
1470 return Err(SerializationError::HeadInvalid {});
1471 }
1472 rb_cal.rb_id = parse_u8(bytestream, pos);
1473 rb_cal.d_v = parse_f32(bytestream, pos);
1474 rb_cal.timestamp = parse_u32(bytestream, pos);
1475 rb_cal.serialize_event_data = parse_bool(bytestream, pos);
1476 for ch in 0..NCHN {
1477 for k in 0..NWORDS {
1478 let mut value = parse_f32(bytestream, pos);
1479 rb_cal.v_offsets[ch][k] = value;
1480 value = parse_f32(bytestream, pos);
1481 rb_cal.v_dips[ch][k] = value;
1482 value = parse_f32(bytestream, pos);
1483 rb_cal.v_inc[ch][k] = value;
1484 value = parse_f32(bytestream, pos);
1485 rb_cal.tbin[ch][k] = value;
1486 }
1487 }
1488 if rb_cal.serialize_event_data {
1489 let broken_event = RBEvent::new();
1490 let n_noi = parse_u16(bytestream, pos);
1491 debug!("Found {n_noi} no input data events!");
1492 for _ in 0..n_noi {
1493 match RBEvent::from_bytestream(bytestream, pos) {
1494 Ok(ev) => {
1495 rb_cal.noi_data.push(ev);
1496 }
1497 Err(err) => {
1498 error!("Unable to read RBEvent! {err}");
1499 }
1500 }
1501 }
1503 let n_vcal = parse_u16(bytestream, pos);
1504 debug!("Found {n_vcal} VCal data events!");
1505 for _ in 0..n_vcal {
1506 match RBEvent::from_bytestream(bytestream, pos) {
1507 Err(err) => {
1508 error!("Found broken event {err}");
1509 },
1510 Ok(good_ev) => {
1511 rb_cal.vcal_data.push(good_ev);
1512 }
1513 }
1514 }
1515 let n_tcal = parse_u16(bytestream, pos);
1516 debug!("Found {n_tcal} TCal data events!");
1517 for _ in 0..n_tcal {
1518 rb_cal.tcal_data.push(RBEvent::from_bytestream(bytestream, pos).unwrap_or(broken_event.clone()));
1519 }
1520 } else {
1521 *pos += 6;
1524 }
1525 if parse_u16(bytestream, pos) != Self::TAIL {
1526 return Err(SerializationError::TailInvalid {});
1527 }
1528 Ok(rb_cal)
1529 }
1530
1531 fn to_bytestream(&self) -> Vec<u8> {
1532 let mut bs = Vec::<u8>::with_capacity(Self::SIZE);
1533 bs.extend_from_slice(&Self::HEAD.to_le_bytes());
1534 bs.extend_from_slice(&self.rb_id.to_le_bytes());
1535 bs.extend_from_slice(&self.d_v.to_le_bytes());
1536 bs.extend_from_slice(&self.timestamp.to_le_bytes());
1537 let serialize_event_data = self.serialize_event_data as u8;
1538 bs.push(serialize_event_data);
1539 for ch in 0..NCHN {
1540 for k in 0..NWORDS {
1541 bs.extend_from_slice(&self.v_offsets[ch][k].to_le_bytes());
1542 bs.extend_from_slice(&self.v_dips[ch][k] .to_le_bytes());
1543 bs.extend_from_slice(&self.v_inc[ch][k] .to_le_bytes());
1544 bs.extend_from_slice(&self.tbin[ch][k] .to_le_bytes());
1545 }
1546 }
1547 if self.serialize_event_data {
1548 info!("Serializing calibration event data!");
1549 let n_noi = self.noi_data.len() as u16;
1550 let n_vcal = self.vcal_data.len() as u16;
1551 let n_tcal = self.tcal_data.len() as u16;
1552 bs.extend_from_slice(&n_noi.to_le_bytes());
1553 for ev in &self.noi_data {
1554 bs.extend_from_slice(&ev.to_bytestream());
1555 }
1556 bs.extend_from_slice(&n_vcal.to_le_bytes());
1557 for ev in &self.vcal_data {
1558 bs.extend_from_slice(&ev.to_bytestream());
1559 }
1560 bs.extend_from_slice(&n_tcal.to_le_bytes());
1561 for ev in &self.tcal_data {
1562 bs.extend_from_slice(&ev.to_bytestream());
1563 }
1564 } else { for _ in 0..6 {
1568 bs.push(0);
1569 }
1570 }
1574 bs.extend_from_slice(&Self::TAIL.to_le_bytes());
1575 bs
1576 }
1577}
1578
1579impl Default for RBCalibrations {
1580 fn default() -> Self {
1581 Self::new(0)
1582 }
1583}
1584
1585impl fmt::Display for RBCalibrations {
1586 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1587 let mut timestamp_str = String::from("?");
1588 match Utc.timestamp_opt(self.timestamp.into(), 0) {
1589 LocalResult::Single(datetime_utc) => {
1590 timestamp_str = datetime_utc.format("%Y/%m/%d %H:%M:%S").to_string();
1591 },
1592 LocalResult::Ambiguous(_, _) => {
1593 println!("The given timestamp is ambiguous.");
1594 },
1595 LocalResult::None => {
1596 println!("The given timestamp is not valid.");
1597 },
1598 }
1599
1600 if !self.check() {
1602 return write!(f, "<RBCalibrations [{} UTC] for board {}:
1603 -- fails self checks!>", timestamp_str, self.rb_id);
1604 }
1605 write!(f,
1606 "<RBCalibrations [{} UTC]:
1607 ** all self checks passed! **
1608 RB : {}
1609 VCalData : {} (events)
1610 TCalData : {} (events)
1611 NoInputData : {} (events)
1612 V Offsets [ch0]: .. {:?} {:?} ..
1613 V Incrmts [ch0]: .. {:?} {:?} ..
1614 V Dips [ch0]: .. {:?} {:?} ..
1615 T Bins [ch0]: .. {:?} {:?} ..>",
1616 timestamp_str,
1617 self.rb_id,
1618 self.vcal_data.len(),
1619 self.tcal_data.len(),
1620 self.noi_data.len(),
1621 self.v_offsets[0][98],
1622 self.v_offsets[0][99],
1623 self.v_inc[0][98],
1624 self.v_inc[0][99],
1625 self.v_dips[0][98],
1626 self.v_dips[0][99],
1627 self.tbin[0][98],
1628 self.tbin[0][99])
1629 }
1630}
1631
1632#[cfg(feature = "pybindings")]
1633#[pymethods]
1634impl RBCalibrations {
1635 #[getter]
1636 fn rb_id(&self) -> u8 {
1637 self.rb_id
1638 }
1639
1640 #[getter]
1641 fn d_v(&self) -> f32 {
1642 self.d_v
1643 }
1644
1645 #[getter]
1646 fn vcal_data(&self) -> Vec<RBEvent> {
1647 self.vcal_data.clone()
1648 }
1649
1650 #[getter]
1651 fn tcal_data(&self) -> Vec<RBEvent> {
1652 self.tcal_data.clone()
1653 }
1654
1655 #[getter]
1656 fn noi_data(&self) -> Vec<RBEvent> {
1657 self.noi_data.clone()
1658 }
1659
1660 #[getter]
1661 fn v_offsets<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1662 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1663 for ch in 0..9 {
1664 data.push(self.v_offsets[ch].to_vec());
1665 }
1666 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1667 Ok(pyarray)
1668 }
1669
1670 #[getter]
1671 fn v_dips<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1672 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1673 for ch in 0..9 {
1674 data.push(self.v_dips[ch].to_vec());
1675 }
1676 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1677 Ok(pyarray)
1678 }
1679
1680 #[getter]
1681 fn v_inc<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1682 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1683 for ch in 0..9 {
1684 data.push(self.v_inc[ch].to_vec());
1685 }
1686 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1687 Ok(pyarray)
1688 }
1689
1690 #[getter]
1691 fn tbin<'_py>(&self, py: Python<'_py>) -> PyResult<Bound<'_py, PyArray2<f32>>> {
1692 let mut data = Vec::<Vec<f32>>::with_capacity(9);
1693 for ch in 0..9 {
1694 data.push(self.tbin[ch].to_vec());
1695 }
1696 let pyarray = PyArray2::from_vec2(py, &data).unwrap();
1697 Ok(pyarray)
1698 }
1699
1700 #[pyo3(name="voltages")]
1713 pub fn voltages_py<'_py>(&self,
1714 py : Python<'_py>,
1715 channel : usize,
1716 stop_cell : usize,
1717 adc : Bound<'_py, PyArray1<u16>>)
1718 -> PyResult<Bound<'_py, PyArray1<f32>>>{
1719 let mut voltages = vec![0.0f32; 1024];
1721 let adc_data = adc.to_vec().unwrap();
1722 self.voltages(channel, stop_cell, &adc_data, &mut voltages);
1723 let pyarray = PyArray1::from_vec(py, voltages);
1724 Ok(pyarray)
1725 }
1726
1727 #[pyo3(name="nanoseconds")]
1737 pub fn nanoseconds_py<'_py>(&self,
1738 py : Python<'_py>,
1739 channel : usize,
1740 stop_cell : usize)
1741 -> PyResult<Bound<'_py, PyArray1<f32>>> {
1742 let mut times = vec![0.0f32; 1024];
1743 self.nanoseconds(channel, stop_cell, &mut times);
1744 let pyarray = PyArray1::from_vec(py, times);
1745 Ok(pyarray)
1746 }
1747
1748 #[pyo3(name = "from_file", signature = (filename, discard_data = true))]
1756 #[staticmethod]
1757 fn from_file_py(filename : String, discard_data : bool) -> PyResult<Self> {
1758 let cali = RBCalibrations::from_file(filename, discard_data);
1759 match cali {
1760 Ok(c) => {
1761 return Ok(c);
1762 },
1763 Err(err) => {
1764 return Err(PyValueError::new_err(err.to_string()));
1765 }
1766 }
1767 }
1768
1769 #[pyo3(name="passes_voltage_checks")]
1770 fn passes_voltage_checks_py(&self) -> bool {
1771 self.passes_voltage_checks()
1772 }
1773
1774 #[pyo3(name="passes_timing_checks")]
1775 fn passes_timing_checks_py(&self) -> bool {
1776 self.passes_timing_checks()
1777 }
1778
1779 #[pyo3(name="check")]
1780 fn check_py(&self) -> bool {
1781 self.check()
1782 }
1783}
1784
1785#[cfg(feature = "pybindings")]
1786pythonize_packable_no_new!(RBCalibrations);
1787
1788#[cfg(feature = "random")]
1789impl FromRandom for RBCalibrations {
1790
1791 fn from_random() -> Self {
1792 let mut cali = Self::new(0);
1793 let mut rng = rand::rng();
1794 cali.rb_id = rng.random::<u8>();
1795 cali.d_v = rng.random::<f32>();
1796 cali.serialize_event_data = rng.random::<bool>();
1797 for ch in 0..NCHN {
1798 for n in 0..NWORDS {
1799 cali.v_offsets[ch][n] = rng.random::<f32>();
1800 cali.v_dips [ch][n] = rng.random::<f32>();
1801 cali.v_inc [ch][n] = rng.random::<f32>();
1802 cali.tbin [ch][n] = rng.random::<f32>();
1803 }
1804 }
1805 if cali.serialize_event_data {
1806 for _ in 0..1000 {
1807 let mut ev = RBEvent::from_random();
1808 cali.vcal_data.push(ev);
1809 ev = RBEvent::from_random();
1810 cali.noi_data.push(ev);
1811 ev = RBEvent::from_random();
1812 cali.tcal_data.push(ev);
1813 }
1814 }
1815 cali
1816 }
1817}
1818
1819#[cfg(feature = "random")]
1822#[test]
1823fn serialization_rbcalibration_noeventpayload() {
1824 let mut calis = Vec::<RBCalibrations>::new();
1825 for _ in 0..100 {
1826 let cali = RBCalibrations::from_random();
1827 if cali.serialize_event_data {
1828 continue;
1829 }
1830 calis.push(cali);
1831 break;
1832 }
1833 let test = RBCalibrations::from_bytestream(&calis[0].to_bytestream(), &mut 0).unwrap();
1834 assert_eq!(calis[0], test);
1835}
1836
1837#[cfg(feature = "random")]
1838#[test]
1839fn serialization_rbcalibration_witheventpayload() {
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 pack_rbcalibrationfilghtt() {
1856 for _ in 0..100 {
1857 let cfg = RBCalibrationFlightT::from_random();
1858 let test : RBCalibrationFlightT = cfg.pack().unpack().unwrap();
1859 assert_eq!(cfg, test);
1860 }
1861}
1862
1863#[cfg(feature = "random")]
1864#[test]
1865fn pack_rbcalibfilghtv() {
1866 for _ in 0..100 {
1867 let cfg = RBCalibrationFlightV::from_random();
1868 let test : RBCalibrationFlightV = cfg.pack().unpack().unwrap();
1869 assert_eq!(cfg, test);
1870 }
1871}
1872
1873#[cfg(feature = "random")]
1874#[test]
1875fn assemble_flightcal() {
1876 for _ in 0..10 {
1877 let cal = RBCalibrations::from_random();
1878 let fct = cal.emit_flighttcal();
1879 let fcv = cal.emit_flightvcal();
1880 let test = RBCalibrations::assemble_from_flightcal(fct, fcv).unwrap();
1881 assert_eq!(cal.rb_id, test.rb_id);
1882 assert_eq!(cal.d_v , test.d_v);
1883 assert_eq!(cal.timestamp, test.timestamp);
1884 assert_eq!(test.serialize_event_data, false);
1885 for ch in 0..NCHN {
1886 for k in 0..NWORDS {
1887 assert_eq!(f16::from_f32(cal.tbin[ch][k]), f16::from_f32(test.tbin[ch][k]));
1888 assert_eq!(f16::from_f32(cal.v_offsets[ch][k]),f16::from_f32(test.v_offsets[ch][k]));
1889 assert_eq!(f16::from_f32(cal.v_dips[ch][k]), f16::from_f32(test.v_dips[ch][k]));
1890 assert_eq!(f16::from_f32(cal.v_inc[ch][k]), f16::from_f32(test.v_inc[ch][k]));
1891 }
1892 }
1893 }
1894}
1895
1896