use crate::error::{Error, Result};
use num_traits::{Float, FloatConst};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Band<T> {
begin: T,
end: T,
}
impl<T: Float> Band<T> {
pub fn new(begin: T, end: T) -> Result<Band<T>> {
if !begin.is_finite() || !end.is_finite() {
return Err(Error::BandLimitsOutOfBounds);
}
if begin > end {
return Err(Error::BandLimitsWrongOrder);
}
if begin < T::zero() || end > T::from(0.5).unwrap() {
return Err(Error::BandLimitsOutOfBounds);
}
Ok(Band { begin, end })
}
}
impl<T: Copy> Band<T> {
pub fn begin(&self) -> T {
self.begin
}
pub fn end(&self) -> T {
self.end
}
}
impl<T: Float> Band<T> {
pub fn len(&self) -> T {
self.end - self.begin
}
pub fn contains(&self, element: T) -> bool {
(self.begin()..=self.end()).contains(&element)
}
pub fn overlaps(&self, other: &Band<T>) -> bool {
self.end() > other.begin() && other.end() > self.begin()
}
pub fn distance(&self, element: T) -> T {
if self.contains(element) {
T::zero()
} else {
(element - self.begin())
.abs()
.min((element - self.end()).abs())
}
}
}
impl<T: Float + FloatConst> Band<T> {
pub(super) fn convert_to_radians(&mut self) {
let pi = T::PI();
let two_pi = T::TAU();
self.begin = (self.begin * two_pi).min(pi);
self.end = (self.end * two_pi).min(pi);
}
}
#[derive(Debug, Clone)]
pub struct PMDesign<T> {
pub impulse_response: Vec<T>,
pub weighted_error: T,
pub extremal_freqs: Vec<T>,
pub num_iterations: usize,
pub flatness: T,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Symmetry {
Even,
Odd,
}
pub struct PMParameters<T, D, W> {
num_taps: usize,
bands: Vec<Band<T>>,
symmetry: Symmetry,
desired_response: D,
weights: W,
cheby_proxy_m: usize,
max_iterations: usize,
flatness_threshold: T,
}
pub trait DesignParameters<T> {
fn num_taps(&self) -> usize;
fn bands(&self) -> &[Band<T>];
fn symmetry(&self) -> Symmetry;
fn desired_response(&self) -> impl Fn(T) -> T;
fn weights(&self) -> impl Fn(T) -> T;
fn chebyshev_proxy_degree(&self) -> usize;
fn max_iterations(&self) -> usize;
fn flatness_threshold(&self) -> T;
}
impl<T, D, W> DesignParameters<T> for PMParameters<T, D, W>
where
T: Copy,
D: Fn(T) -> T,
W: Fn(T) -> T,
{
fn num_taps(&self) -> usize {
self.num_taps
}
fn bands(&self) -> &[Band<T>] {
&self.bands
}
fn symmetry(&self) -> Symmetry {
self.symmetry
}
fn desired_response(&self) -> impl Fn(T) -> T {
&self.desired_response
}
fn weights(&self) -> impl Fn(T) -> T {
&self.weights
}
fn chebyshev_proxy_degree(&self) -> usize {
self.cheby_proxy_m
}
fn max_iterations(&self) -> usize {
self.max_iterations
}
fn flatness_threshold(&self) -> T {
self.flatness_threshold
}
}
pub trait ParametersBuilder<T>: DesignParameters<T> {
fn set_symmetry(&mut self, symmetry: Symmetry) -> &mut Self;
fn set_chebyshev_proxy_degree(&mut self, degree: usize) -> &mut Self;
fn set_max_iterations(&mut self, max_iterations: usize) -> &mut Self;
fn set_flatness_threshold(&mut self, flatness_threshold: T) -> &mut Self;
}
impl<T: Float, D, W> PMParameters<T, D, W> {
pub fn new(
num_taps: usize,
bands: Vec<Band<T>>,
desired_response: D,
weights: W,
) -> Result<Self> {
Ok(PMParameters {
num_taps,
bands,
symmetry: Symmetry::Even,
desired_response,
weights,
cheby_proxy_m: 8,
max_iterations: 100,
flatness_threshold: T::from(1e-3).unwrap(),
})
}
}
impl<T, D, W> ParametersBuilder<T> for PMParameters<T, D, W>
where
T: Copy,
D: Fn(T) -> T,
W: Fn(T) -> T,
{
fn set_symmetry(&mut self, symmetry: Symmetry) -> &mut Self {
self.symmetry = symmetry;
self
}
fn set_chebyshev_proxy_degree(&mut self, degree: usize) -> &mut Self {
self.cheby_proxy_m = degree;
self
}
fn set_max_iterations(&mut self, max_iterations: usize) -> &mut Self {
self.max_iterations = max_iterations;
self
}
fn set_flatness_threshold(&mut self, flatness_threshold: T) -> &mut Self {
self.flatness_threshold = flatness_threshold;
self
}
}