use crate::traits::FloatScalar;
use super::biquad::{
bilinear_hp_pair, bilinear_hp_real, bilinear_lp_pair, bilinear_lp_real, BiquadCascade,
};
use super::{validate_design_params, ControlError};
pub fn chebyshev1_lowpass<T: FloatScalar, const N: usize>(
order: usize,
ripple_db: T,
cutoff: T,
sample_rate: T,
) -> Result<BiquadCascade<T, N>, ControlError> {
validate_design_params::<T, N>(order, cutoff, sample_rate)?;
if ripple_db <= T::zero() || !ripple_db.is_finite() {
return Err(ControlError::InvalidRipple);
}
let two = T::one() + T::one();
let ten = T::from(10.0).unwrap();
let pi = T::from(core::f64::consts::PI).unwrap();
let wa = two * sample_rate * (pi * cutoff / sample_rate).tan();
let c = two * sample_rate;
let n = order;
let nf = T::from(n).unwrap();
let epsilon = (ten.powf(ripple_db / ten) - T::one()).sqrt();
let a = (T::one() / epsilon).asinh() / nf;
let sinh_a = a.sinh();
let cosh_a = a.cosh();
let mut sections = [super::biquad::Biquad::passthrough(); N];
let mut idx = 0;
let num_pairs = n / 2;
for k in 0..num_pairs {
let kf = T::from(k).unwrap();
let phi = pi * (two * kf + T::one()) / (two * nf);
let sigma = -wa * sinh_a * phi.sin();
let omega = wa * cosh_a * phi.cos();
sections[idx] = bilinear_lp_pair(sigma, omega, wa, c);
idx += 1;
}
if n % 2 == 1 {
let sigma = -wa * sinh_a;
let wa_real = wa * sinh_a;
sections[idx] = bilinear_lp_real(sigma, wa_real, c);
}
let target_dc = if n % 2 == 0 {
T::one() / (T::one() + epsilon * epsilon).sqrt()
} else {
T::one()
};
let mut current_dc = T::one();
for s in sections.iter() {
let (b, a) = s.coefficients();
let num_dc = b[0] + b[1] + b[2];
let den_dc = a[0] + a[1] + a[2];
current_dc = current_dc * num_dc / den_dc;
}
if current_dc.abs() > T::epsilon() {
let scale = target_dc / current_dc;
let (b, a) = sections[0].coefficients();
sections[0] = super::biquad::Biquad::new(
[b[0] * scale, b[1] * scale, b[2] * scale],
a,
);
}
Ok(BiquadCascade { sections })
}
pub fn chebyshev1_highpass<T: FloatScalar, const N: usize>(
order: usize,
ripple_db: T,
cutoff: T,
sample_rate: T,
) -> Result<BiquadCascade<T, N>, ControlError> {
validate_design_params::<T, N>(order, cutoff, sample_rate)?;
if ripple_db <= T::zero() || !ripple_db.is_finite() {
return Err(ControlError::InvalidRipple);
}
let two = T::one() + T::one();
let ten = T::from(10.0).unwrap();
let pi = T::from(core::f64::consts::PI).unwrap();
let wa = two * sample_rate * (pi * cutoff / sample_rate).tan();
let c = two * sample_rate;
let n = order;
let nf = T::from(n).unwrap();
let epsilon = (ten.powf(ripple_db / ten) - T::one()).sqrt();
let a = (T::one() / epsilon).asinh() / nf;
let sinh_a = a.sinh();
let cosh_a = a.cosh();
let mut sections = [super::biquad::Biquad::passthrough(); N];
let mut idx = 0;
let wa2 = wa * wa;
let num_pairs = n / 2;
for k in 0..num_pairs {
let kf = T::from(k).unwrap();
let phi = pi * (two * kf + T::one()) / (two * nf);
let sigma_lp = -wa * sinh_a * phi.sin();
let omega_lp = wa * cosh_a * phi.cos();
let mag2 = sigma_lp * sigma_lp + omega_lp * omega_lp;
let sigma_hp = wa2 * sigma_lp / mag2;
let omega_hp = -wa2 * omega_lp / mag2;
sections[idx] = bilinear_hp_pair(sigma_hp, omega_hp, wa, c);
idx += 1;
}
if n % 2 == 1 {
let sigma_lp = -wa * sinh_a;
let sigma_hp = wa2 / sigma_lp; sections[idx] = bilinear_hp_real(sigma_hp, wa, c);
}
let target_nyq = if n % 2 == 0 {
T::one() / (T::one() + epsilon * epsilon).sqrt()
} else {
T::one()
};
let mut current_nyq = T::one();
for s in sections.iter() {
let (b, a) = s.coefficients();
let num_nyq = b[0] - b[1] + b[2];
let den_nyq = a[0] - a[1] + a[2];
current_nyq = current_nyq * num_nyq / den_nyq;
}
if current_nyq.abs() > T::epsilon() {
let scale = target_nyq / current_nyq.abs();
let (b, a) = sections[0].coefficients();
sections[0] = super::biquad::Biquad::new(
[b[0] * scale, b[1] * scale, b[2] * scale],
a,
);
}
Ok(BiquadCascade { sections })
}