use crate::Curve;
use ndarray::Array1;
fn interpolate_log_space_vals(
log_freq_out: &[f64],
log_freq_in: &[f64],
vals_in: &Array1<f64>,
) -> Array1<f64> {
let n_out = log_freq_out.len();
let n_in = log_freq_in.len();
let mut vals_out = Array1::zeros(n_out);
for i in 0..n_out {
let target_log_freq = log_freq_out[i];
if target_log_freq <= log_freq_in[0] {
if n_in >= 2 {
let denom = log_freq_in[1] - log_freq_in[0];
if denom.abs() < 1e-10 {
vals_out[i] = vals_in[0];
} else {
let slope = (vals_in[1] - vals_in[0]) / denom;
vals_out[i] = vals_in[0] + slope * (target_log_freq - log_freq_in[0]);
}
} else {
vals_out[i] = vals_in[0];
}
} else if target_log_freq >= log_freq_in[n_in - 1] {
if n_in >= 2 {
let denom = log_freq_in[n_in - 1] - log_freq_in[n_in - 2];
if denom.abs() < 1e-10 {
vals_out[i] = vals_in[n_in - 1];
} else {
let slope = (vals_in[n_in - 1] - vals_in[n_in - 2]) / denom;
vals_out[i] =
vals_in[n_in - 1] + slope * (target_log_freq - log_freq_in[n_in - 1]);
}
} else {
vals_out[i] = vals_in[n_in - 1];
}
} else {
let mut j = 0;
while j < n_in - 1 && log_freq_in[j + 1] < target_log_freq {
j += 1;
}
let denom = log_freq_in[j + 1] - log_freq_in[j];
if denom.abs() < 1e-10 {
vals_out[i] = vals_in[j];
} else {
let t = (target_log_freq - log_freq_in[j]) / denom;
vals_out[i] = vals_in[j] * (1.0 - t) + vals_in[j + 1] * t;
}
}
}
vals_out
}
pub fn interpolate_log_space(freq_out: &Array1<f64>, curve: &Curve) -> Curve {
let freq_in = &curve.freq;
let log_freq_in: Vec<f64> = freq_in.iter().map(|&f| f.max(1e-6).ln()).collect();
let log_freq_out: Vec<f64> = freq_out.iter().map(|&f| f.max(1e-6).ln()).collect();
let spl_out = interpolate_log_space_vals(&log_freq_out, &log_freq_in, &curve.spl);
let phase_out = curve
.phase
.as_ref()
.map(|p| interpolate_log_space_vals(&log_freq_out, &log_freq_in, p));
Curve {
freq: freq_out.clone(),
spl: spl_out,
phase: phase_out,
}
}
pub fn create_log_frequency_grid(n_points: usize, f_min: f64, f_max: f64) -> Array1<f64> {
Array1::logspace(10.0, f_min.log10(), f_max.log10(), n_points)
}
pub fn interpolate(freqs: &Array1<f64>, curve: &Curve) -> Curve {
debug_assert!(
curve
.freq
.as_slice()
.unwrap()
.windows(2)
.all(|w| w[0] <= w[1]),
"interpolate() requires sorted frequencies"
);
let mut result_spl = Array1::zeros(freqs.len());
let mut result_phase = curve.phase.as_ref().map(|_| Array1::zeros(freqs.len()));
for (i, &target_freq) in freqs.iter().enumerate() {
let mut left_idx = 0;
let mut right_idx = curve.freq.len() - 1;
if target_freq <= curve.freq[0] {
result_spl[i] = curve.spl[0];
if let (Some(res_p), Some(src_p)) = (result_phase.as_mut(), &curve.phase) {
res_p[i] = src_p[0];
}
} else if target_freq >= curve.freq[curve.freq.len() - 1] {
result_spl[i] = curve.spl[curve.freq.len() - 1];
if let (Some(res_p), Some(src_p)) = (result_phase.as_mut(), &curve.phase) {
res_p[i] = src_p[curve.freq.len() - 1];
}
} else {
for j in 1..curve.freq.len() {
if curve.freq[j] >= target_freq {
left_idx = j - 1;
right_idx = j;
break;
}
}
let freq_left = curve.freq[left_idx];
let freq_right = curve.freq[right_idx];
let t = (target_freq - freq_left) / (freq_right - freq_left);
let spl_left = curve.spl[left_idx];
let spl_right = curve.spl[right_idx];
result_spl[i] = spl_left + t * (spl_right - spl_left);
if let (Some(res_p), Some(src_p)) = (result_phase.as_mut(), &curve.phase) {
let p_left = src_p[left_idx];
let p_right = src_p[right_idx];
res_p[i] = p_left + t * (p_right - p_left);
}
}
}
Curve {
freq: freqs.clone(),
spl: result_spl,
phase: result_phase,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interpolate_log_space_zero_freq() {
let curve = Curve {
freq: Array1::from_vec(vec![0.0, 100.0, 1000.0, 10000.0]),
spl: Array1::from_vec(vec![80.0, 85.0, 90.0, 88.0]),
phase: None,
};
let freq_out = Array1::from_vec(vec![50.0, 500.0, 5000.0]);
let result = interpolate_log_space(&freq_out, &curve);
for &v in result.spl.iter() {
assert!(
v.is_finite(),
"interpolate_log_space produced non-finite value: {}",
v
);
}
}
}