pub fn lpc(y: &[f64], order: usize) -> Vec<f64> {
let n = y.len();
assert!(order >= 1, "lpc: order must be >= 1, got {order}");
assert!(
n > order,
"lpc: signal length ({n}) must be > order ({order})"
);
let epsilon: f64 = f64::MIN_POSITIVE;
let mut ar_coeffs = vec![0.0; order + 1];
let mut ar_coeffs_prev = vec![0.0; order + 1];
ar_coeffs[0] = 1.0;
let mut forward_error: Vec<f64> = y[1..].to_vec();
let mut backward_error: Vec<f64> = y[..n - 1].to_vec();
let mut den: f64 = forward_error.iter().map(|x| x * x).sum::<f64>()
+ backward_error.iter().map(|x| x * x).sum::<f64>();
for i in 0..order {
let reflect_numer: f64 = forward_error
.iter()
.zip(backward_error.iter())
.map(|(f, b)| b * f)
.sum::<f64>();
let reflect = -2.0 * reflect_numer / (den + epsilon);
std::mem::swap(&mut ar_coeffs, &mut ar_coeffs_prev);
for j in 1..=i + 1 {
ar_coeffs[j] = ar_coeffs_prev[j] + reflect * ar_coeffs_prev[i + 1 - j];
}
ar_coeffs[0] = 1.0;
let fe_len = forward_error.len();
let mut new_forward = vec![0.0; fe_len];
let mut new_backward = vec![0.0; fe_len];
for k in 0..fe_len {
new_forward[k] = forward_error[k] + reflect * backward_error[k];
new_backward[k] = backward_error[k] + reflect * forward_error[k];
}
let q = 1.0 - reflect * reflect;
den = q * den
- new_backward[fe_len - 1] * new_backward[fe_len - 1]
- new_forward[0] * new_forward[0];
if fe_len > 1 {
forward_error = new_forward[1..].to_vec();
backward_error = new_backward[..fe_len - 1].to_vec();
} else {
break;
}
}
ar_coeffs[0] = 1.0;
ar_coeffs
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lpc_order_1() {
let y: Vec<f64> = vec![1.0; 100];
let coeffs = lpc(&y, 1);
assert_eq!(coeffs.len(), 2);
assert!((coeffs[0] - 1.0).abs() < 1e-15);
assert!((coeffs[1] - (-1.0)).abs() < 1e-12);
}
#[test]
fn test_lpc_returns_correct_length() {
let y: Vec<f64> = (0..100).map(|i| (i as f64 * 0.1).sin()).collect();
let coeffs = lpc(&y, 4);
assert_eq!(coeffs.len(), 5);
assert!((coeffs[0] - 1.0).abs() < 1e-15);
}
#[test]
#[should_panic]
fn test_lpc_panics_if_order_zero() {
let y = vec![1.0; 10];
lpc(&y, 0);
}
#[test]
#[should_panic]
fn test_lpc_panics_if_signal_too_short() {
let y = vec![1.0; 3];
lpc(&y, 5);
}
}