1use secs_compute::{Context, MemoryError};
4use secs_dsp::resample::{IrResamplePlan, MAX_RESAMPLE_RATE, MIN_RESAMPLE_RATE};
5use secs_types::calibration::SampleRatePolicy;
6
7use crate::{EngineError, MeasurementSet, Samples, grid::SR};
8
9fn check(ctx: &Context) -> Result<(), EngineError> {
10 ctx.cancel().check().map_err(|_| EngineError::Cancelled)
11}
12
13pub fn normalize_measurements(
20 irs: &MeasurementSet,
21 policy: SampleRatePolicy,
22 ctx: &Context,
23) -> Result<MeasurementSet, EngineError> {
24 check(ctx)?;
25 if policy == SampleRatePolicy::Reject {
26 return Ok(irs.clone());
27 }
28 let mut output_bytes = 0_u64;
29 let mut largest_output = 0_u64;
30 let mut largest_kernel = 0_u64;
31 let mut count = 0_u64;
32 for (position, roles) in &irs.positions {
33 for (role, measurement) in roles {
34 check(ctx)?;
35 if measurement.sample_rate == SR {
36 continue;
37 }
38 if !(MIN_RESAMPLE_RATE..=MAX_RESAMPLE_RATE).contains(&measurement.sample_rate) {
39 return Err(EngineError::UnsupportedResamplingRate {
40 position: *position,
41 role: role.clone(),
42 actual_hz: measurement.sample_rate,
43 });
44 }
45 if measurement.samples.is_empty() || !measurement.t0.is_finite() {
46 return Err(EngineError::InvalidMeasurement {
47 position: *position,
48 role: role.clone(),
49 });
50 }
51 for n in 0..measurement.samples.len() {
52 if n % 4096 == 0 {
53 check(ctx)?;
54 }
55 if !measurement.samples.at(n).is_finite() {
56 return Err(EngineError::InvalidMeasurement {
57 position: *position,
58 role: role.clone(),
59 });
60 }
61 }
62 let plan = IrResamplePlan::new(measurement.samples.len(), measurement.sample_rate, SR)?;
63 let bytes = (plan.output_len() * size_of::<f64>()) as u64;
64 output_bytes = output_bytes.checked_add(bytes).ok_or_else(|| {
65 EngineError::Input("IR resampling memory estimate overflow".into())
66 })?;
67 largest_output = largest_output.max(bytes);
68 largest_kernel = largest_kernel.max(plan.kernel_bytes() as u64);
69 count += 1;
70 }
71 }
72 if output_bytes == 0 {
73 return Ok(irs.clone());
74 }
75 let bytes = output_bytes
76 .checked_add(largest_output)
77 .and_then(|n| n.checked_add(largest_kernel))
78 .ok_or_else(|| EngineError::Input("IR resampling memory estimate overflow".into()))?;
79 let _permit = ctx
80 .budget()
81 .acquire(bytes, ctx.cancel())
82 .map_err(|error| match error {
83 MemoryError::Cancelled => EngineError::Cancelled,
84 other => EngineError::Input(other.to_string()),
85 })?;
86 let progress = ctx.stage("engine.resample", count);
87 let mut normalized = irs.clone();
88 for (_, roles) in &mut normalized.positions {
89 for (_, measurement) in roles {
90 check(ctx)?;
91 if measurement.sample_rate == SR {
92 continue;
93 }
94 let plan = IrResamplePlan::new(measurement.samples.len(), measurement.sample_rate, SR)?;
95 let samples = plan.process(|n| measurement.samples.at(n), || check(ctx))?;
96 measurement.t0 -= plan.leading_output_samples() as f64 / f64::from(SR);
97 measurement.sample_rate = SR;
98 measurement.samples = Samples::F64(samples.into());
99 progress.advance(1);
100 }
101 }
102 Ok(normalized)
103}