1use crate::prelude::*;
7
8pub fn get_max_value_idx<T : std::cmp::PartialOrd + std::fmt::Display + Copy>(values : &[T],
18 start_idx : usize,
19 n_idx : usize) -> Result<usize, WaveformError> {
20 if start_idx >= values.len() {
21 error!("Invalid value for start index {}", start_idx);
22 return Err(WaveformError::OutOfRangeLowerBound);
23 }
24 if start_idx + n_idx >= values.len() {
25 error!("Start index {} + n steps of {} is larger tan array size!", start_idx, n_idx);
26 return Err(WaveformError::OutOfRangeUpperBound);
27 }
28 let mut maxval = values[start_idx];
29 let mut maxbin = start_idx;
30 for n in start_idx..start_idx + n_idx {
31 if values[n] > maxval {
32 maxval = values[n];
33 maxbin = n;
34 }
35 } trace!("Got index {} for a max value of {}", maxbin, maxval);
37 Ok(maxbin)
38} #[cfg(feature="pybindings")]
43#[pyfunction]
44#[pyo3(name="get_max_value_idx")]
45pub fn get_max_value_idx_py<'_py>(value : Bound<'_py,PyArray1<f32>>,
46 start_idx :usize,
47 n_idx : usize) -> PyResult<usize> {
48 unsafe {
49 match get_max_value_idx::<f32>(value.as_slice().unwrap(), start_idx, n_idx) {
50 Err(err) => {
51 return Err(PyValueError::new_err(err.to_string()));
52 }
53 Ok(max_val) => {
54 return Ok(max_val);
55 }
56 }
57 }
58}
59
60pub fn interpolate_time<T : AsRef<[f32]>> (volts : &T,
74 times : &T,
75 mut threshold : f32,
76 mut idx : usize,
77 size : usize) -> Result<f32, WaveformError> {
78 let voltages = volts.as_ref();
79 let nanoseconds = times.as_ref();
80 if idx + 1 > nanoseconds.len() {
81 return Err(WaveformError::OutOfRangeUpperBound);
82 }
83 threshold = threshold.abs();
84 let mut lval = (voltages[idx]).abs();
85 let mut hval : f32 = 0.0;
86 if size == 1 {
87 hval = (voltages[idx+1]).abs();
88 } else {
89 for n in idx+1..idx+size {
90 hval = voltages[n].abs();
91 if (hval>=threshold) && (threshold<=lval) { idx = n-1; break;
94 }
95 lval = hval;
96 }
97 }
98 if ((lval > threshold) && (size != 1)) || lval == hval {
99 return Ok(nanoseconds[idx]);
100 } else {
101 return Ok(nanoseconds[idx]
102 + (threshold-lval)/(hval-lval) * (nanoseconds[idx+1]
103 - nanoseconds[idx]));
104 }
105}
106
107
108#[cfg(feature = "pybindings")]
109#[pyfunction]
110#[pyo3(name="interpolate_time")]
111pub fn interpolate_time_py(voltages : PyReadonlyArray1<f32>,
125 nanoseconds : PyReadonlyArray1<f32>,
126 threshold : f32,
127 idx : usize,
128 size : usize) -> PyResult<f32> {
129 let i = idx;
130 match interpolate_time(&voltages.readonly().as_slice().unwrap(),
131 &nanoseconds.readonly().as_slice().unwrap(),
132 threshold, i, size) {
133 Ok(time) => {
134 return Ok(time);
135 }
136 Err(err) => {
137 return Err(PyValueError::new_err(err.to_string()));
138 }
139 }
140}
141
142
143pub fn integrate(voltages : &Vec<f32>,
153 nanoseconds : &Vec<f32>,
154 lo_bin : usize,
155 upper_bin : usize,
156 impedance : f32) -> Result<f32, WaveformError> {
157 if upper_bin > voltages.len() {
158 return Err(WaveformError::OutOfRangeUpperBound);
159 }
160 if lo_bin < 1 {
161 return Err(WaveformError::OutOfRangeLowerBound);
162 }
163 let mut sum = 0f32;
164 for n in lo_bin..upper_bin {
165 sum += voltages[n] * (nanoseconds[n] - nanoseconds[n-1]) ;
166 }
167 sum /= impedance;
168 Ok(sum)
169}
170
171pub fn time2bin(nanoseconds : &Vec<f32>,
175 t_ns : f32) -> Result<usize, WaveformError> {
176 for n in 0..nanoseconds.len() {
177 if nanoseconds[n] > t_ns {
178 return Ok(n-1);
179 }
180 }
181 debug!("Did not find a bin corresponding to the given time {}!", t_ns);
182 return Err(WaveformError::TimesTooSmall);
183}
184
185pub fn calculate_pedestal(voltages : &Vec<f32>,
200 threshold : f32,
201 ped_begin_bin : usize,
202 ped_range_bin : usize) -> (f32,f32) {
203 let mut sum = 0f32;
204 let mut sum2 = 0f32;
205 for k in ped_begin_bin..ped_begin_bin + ped_range_bin {
206 if f32::abs(voltages[k]) < threshold {
207 sum += voltages[k];
208 sum2 += voltages[k]*voltages[k];
209 }
210 }
211 let average = sum/(ped_range_bin as f32);
212 let sigma = f32::sqrt(sum2/(ped_range_bin as f32 - (average*average)));
213 (average, sigma)
214}
215
216pub fn cfd_simple(voltages : &Vec<f32>,
222 nanoseconds : &Vec<f32>,
223 cfd_frac : f32,
224 start_peak : usize,
225 end_peak : usize) -> Result<f32, WaveformError> {
226
227 let idx = get_max_value_idx(&voltages, start_peak, end_peak-start_peak)?;
228 let mut sum : f32 = 0.0;
229 for n in idx-1..idx+1{
230 sum += voltages[n];
231 }
232 let tmp_thresh : f32 = f32::abs(cfd_frac * (sum / 3.0));
233 trace!("Calculated tmp threshold of {}", tmp_thresh);
234 let mut lo_bin : usize = voltages.len();
239 let mut n = idx;
240 if idx < start_peak {
241 error!("The index {} is smaller than the beginning of the peak {}!", idx, start_peak);
242 return Err(WaveformError::OutOfRangeLowerBound);
243 }
244 if start_peak >= 10 {
245 while n > start_peak - 10 {
246 if f32::abs(voltages[n]) < tmp_thresh {
247 lo_bin = n;
248 break;
249 }
250 n -= 1;
251 }
252 } else {
253 debug!("We require that the peak is at least 10 bins away from the start!");
254 return Err(WaveformError::OutOfRangeLowerBound);
255 }
256
257 trace!("Lo bin {} , start peak {}", lo_bin, start_peak);
258 let cfd_time : f32;
259 if lo_bin < nanoseconds.len() -1 {
260 cfd_time = interpolate_time(voltages, nanoseconds, tmp_thresh, lo_bin, 1)?;
261 } else {
262 cfd_time = nanoseconds[nanoseconds.len() - 1];
263 }
264 Ok(cfd_time)
265}
266
267pub fn find_peaks(voltages : &Vec<f32>,
288 nanoseconds : &Vec<f32>,
289 start_time : f32,
290 window_size : f32,
291 min_peak_width : usize,
292 threshold : f32,
293 max_peaks : usize)
294-> Result<Vec<(usize,usize)>, WaveformError> {
295 let mut peaks = Vec::<(usize,usize)>::new();
296 let mut start_bin = time2bin(nanoseconds, start_time)?;
297 if start_bin <= 10 {
298 debug!("We deliberatly do not search for peaks within the first 10 bins! Correcting..");
299 start_bin = 10;
300 }
301 let window_bin = time2bin(nanoseconds, start_time + window_size)? - start_bin;
302 if start_bin + window_bin > voltages.len () {
303 return Err(WaveformError::OutOfRangeUpperBound);
304 }
305
306 let mut pos = 0usize;
307 for k in start_bin..start_bin + window_bin {
310 if voltages[k] >= threshold {
311 pos = k;
312 break;
313 }
314 }
315 if pos == 0 && start_bin == 0 && voltages[pos] < threshold {
316 return Err(WaveformError::DidNotCrossThreshold)
318 }
319 let mut nbins_peak = 0usize;
321 let mut begin_peak = pos;
322 let mut end_peak : usize;
323 if (pos + window_bin) > voltages.len() {
324 return Err(WaveformError::OutOfRangeUpperBound);
325 }
326 for k in pos..(pos + window_bin) {
327 if voltages[k] >= threshold {
328 nbins_peak += 1;
329 let mut slope = 0i16; if nbins_peak == min_peak_width {
336 begin_peak = k - min_peak_width -1;
338 } else if nbins_peak > min_peak_width {
339 for j in 0..min_peak_width {
340 if voltages[k -j] > voltages[k-j-1] {
341 slope = 1; }
343 }
344 if slope == 1 {
345 continue;
347 }
348 if slope == 0 {
349 end_peak = k;
351 nbins_peak = 0; peaks.push((begin_peak, end_peak));
353 if peaks.len() == max_peaks {
354 break;
355 }
356 }
357 } } else {
361 if nbins_peak > min_peak_width {
362 end_peak = k;
363 peaks.push((begin_peak, end_peak));
364 if peaks.len() == max_peaks {
365 break;
366 }
367 }
368 nbins_peak = 0;
369 }
370 }
371 let len_pks_dirty = peaks.len();
373 peaks.retain(|&x| {(x.0 < NWORDS - 1) & (x.1 <= NWORDS - 1)});
374 let len_pks_clean = peaks.len();
375 if len_pks_clean != len_pks_dirty {
376 debug!("We removed {} pks because they had values outside of 0-{}!", len_pks_dirty - len_pks_clean, NWORDS);
377 }
378 Ok(peaks)
379}
380
381
382#[cfg(feature = "advanced-algorithms")]
383fn find_sequence_ranges(vec: Vec<usize>) -> Vec<(usize, usize)> {
384 let mut ranges = Vec::new();
385 let mut start = vec[0];
386 let mut end = vec[0];
387
388 for &value in vec.iter().skip(1) {
389 if value == end + 1 {
390 end = value;
392 } else {
393 ranges.push((start, end));
395 start = value;
396 end = value;
397 }
398 }
399
400 ranges.push((start, end));
402 ranges
403}
404
405#[cfg(feature = "advanced-algorithms")]
406pub fn find_peaks_zscore(nanoseconds : &Vec<f32>,
459 voltages : &Vec<f32>,
460 start_time : f32,
461 window_size : f32,
462 lag : usize,
463 threshold : f64,
464 influence : f64)
465-> Result<Vec<(usize,usize)>, WaveformError> {
466 let mut peaks = Vec::<(usize, usize)>::new();
467 let start_bin = time2bin(nanoseconds, start_time)?;
468 let end_bin = time2bin(nanoseconds, start_time + window_size)?;
469 let mut ranged_voltage = Vec::<f32>::with_capacity(end_bin - start_bin);
470 ranged_voltage.extend_from_slice(&voltages[start_bin..=end_bin]);
471 let output: Vec<_> = voltages
474 .into_iter()
475 .enumerate()
476 .peaks(PeaksDetector::new(lag, threshold, influence), |e| *e.1 as f64)
477 .map(|((i, _), p)| (i, p))
478 .collect();
479 if output.len() == 0 {
481 return Ok(peaks);
482 }
483 let mut peak_high = Vec::<usize>::new();
484 for k in output.iter() {
485 if matches!(k.1, Peak::High) {
486 peak_high.push(k.0);
487 }
488 }
489 if peaks.len() > 0 {
490 peaks = find_sequence_ranges(peak_high);
491 }
492 Ok(peaks)
493}
494
495pub fn fit_sine_simple<T>(volts: &[T], times: &[T]) -> (f32, f32, f32)
499 where T: Float + NumAssign + NumAssignOps + NumOps + Copy + NumCast + FloatConst {
500 let start_bin = 20;
501 let size_bin = 900;
502 let mut data_size = T::zero();
503
504 let mut xi_yi = T::zero();
505 let mut xi_zi = T::zero();
506 let mut yi_zi = T::zero();
507 let mut xi_xi = T::zero();
508 let mut yi_yi = T::zero();
509 let mut xi_sum = T::zero();
510 let mut yi_sum = T::zero();
511 let mut zi_sum = T::zero();
512
513 let c1 = T::from(2).unwrap();
514 let c2 = T::from(0.02f32).unwrap();
515 for i in start_bin..(start_bin + size_bin) {
516 let xi = (c1 * T::PI() * c2 * times[i]).cos();
517 let yi = (c1 * T::PI() * c2 * times[i]).sin();
518 let zi = volts[i];
519
520 xi_yi += xi * yi;
521 xi_zi += xi * zi;
522 yi_zi += yi * zi;
523 xi_xi += xi * xi;
524 yi_yi += yi * yi;
525 xi_sum += xi;
526 yi_sum += yi;
527 zi_sum += zi;
528
529 data_size += T::one();
530 }
531
532 let mut a_matrix = [[T::zero(); 3]; 3];
533 a_matrix[0][0] = xi_xi;
534 a_matrix[0][1] = xi_yi;
535 a_matrix[0][2] = xi_sum;
536 a_matrix[1][0] = xi_yi;
537 a_matrix[1][1] = yi_yi;
538 a_matrix[1][2] = yi_sum;
539 a_matrix[2][0] = xi_sum;
540 a_matrix[2][1] = yi_sum;
541 a_matrix[2][2] = data_size;
542
543 let determinant = a_matrix[0][0] * a_matrix[1][1] * a_matrix[2][2]
544 + a_matrix[0][1] * a_matrix[1][2] * a_matrix[2][0]
545 + a_matrix[0][2] * a_matrix[1][0] * a_matrix[2][1]
546 - a_matrix[0][0] * a_matrix[1][2] * a_matrix[2][1]
547 - a_matrix[0][1] * a_matrix[1][0] * a_matrix[2][2]
548 - a_matrix[0][2] * a_matrix[1][1] * a_matrix[2][0];
549
550 let inverse_factor = T::one() / determinant;
551
552 let mut cofactor_matrix = [[T::zero(); 3]; 3];
553 cofactor_matrix[0][0] = a_matrix[1][1] * a_matrix[2][2] - a_matrix[2][1] * a_matrix[1][2];
554 cofactor_matrix[0][1] = (a_matrix[1][0] * a_matrix[2][2] - a_matrix[2][0] * a_matrix[1][2]) * -T::one();
555 cofactor_matrix[0][2] = a_matrix[1][0] * a_matrix[2][1] - a_matrix[2][0] * a_matrix[1][1];
556 cofactor_matrix[1][0] = (a_matrix[0][1] * a_matrix[2][2] - a_matrix[2][1] * a_matrix[0][2]) * -T::one();
557 cofactor_matrix[1][1] = a_matrix[0][0] * a_matrix[2][2] - a_matrix[2][0] * a_matrix[0][2];
558 cofactor_matrix[1][2] = (a_matrix[0][0] * a_matrix[2][1] - a_matrix[2][0] * a_matrix[0][1]) * -T::one();
559 cofactor_matrix[2][0] = a_matrix[0][1] * a_matrix[1][2] - a_matrix[1][1] * a_matrix[0][2];
560 cofactor_matrix[2][1] = (a_matrix[0][0] * a_matrix[1][2] - a_matrix[1][0] * a_matrix[0][2]) * -T::one();
561 cofactor_matrix[2][2] = a_matrix[0][0] * a_matrix[1][1] - a_matrix[1][0] * a_matrix[0][1];
562
563 let mut inverse_matrix = [[T::zero(); 3]; 3];
564 for i in 0..3 {
565 for j in 0..3 {
566 inverse_matrix[i][j] = cofactor_matrix[j][i] * inverse_factor;
567 }
568 }
569
570 let p = [xi_zi, yi_zi, zi_sum];
571 let a = inverse_matrix[0][0] * p[0] + inverse_matrix[1][0] * p[1] + inverse_matrix[2][0] * p[2];
572 let b = inverse_matrix[0][1] * p[0] + inverse_matrix[1][1] * p[1] + inverse_matrix[2][1] * p[2];
573
574 let phi = <f32 as NumCast>::from(a.atan2(b)).unwrap();
575 let amp = <f32 as NumCast>::from((a*a + b*b).sqrt()).unwrap();
576 let freq = 0.02 as f32;
577
578 (amp, freq, phi)
579}
580
581#[cfg(feature="pybindings")]
582#[pyfunction]
583#[pyo3(name="fit_sine_simple")]
584pub fn fit_sine_simple_py<'_py>(xs : Bound<'_py,PyArray1<f32>>, ys: Bound<'_py, PyArray1<f32>>) -> (f32,f32,f32) {
585 unsafe {
586 fit_sine_simple::<f32>(ys.as_slice().unwrap(), xs.as_slice().unwrap())
587 }
588}
589