pm_remez/
requirements.rs

1use crate::{
2    error::{Error, Result},
3    types::{Band, PMParameters, ParametersBuilder},
4};
5use num_traits::Float;
6
7/// Creates parameters for the Parks-McClellan algorithm in terms of a list of
8/// [`BandSetting`]s.
9///
10/// This function is used by one of the two coding styles supported by this
11/// crate. It uses a list of `BandSetting`s to specify each of the bands and
12/// its desired function and weight used in the Parks-McClellan algorithm. The
13/// `num_taps` parameter indicates the number of taps of FIR filter to be
14/// designed.
15///
16/// As in [`PMParameters`], some of the parameters required for the
17/// Parks-McClellan algorith are given default values (the same defaults as in
18/// `PMParameters`, and in particular, an even symmetry for the FIR taps). The
19/// defaults can be changed by using the methods defined by the
20/// [`ParametersBuilder`] trait, which is implemented by the object returned by
21/// this function.
22///
23/// In technical terms, this function constructs and returns an appropriate
24/// `PMParameters` object. However, the type parameters `D` and `W` of that
25/// object cannot be named, because they are closures with anonymous
26/// types. Therefore, an `impl ParametersBuilder` is used as the return type of
27/// the function.
28pub fn pm_parameters<T: Float>(
29    num_taps: usize,
30    band_settings: &[BandSetting<T>],
31) -> Result<impl ParametersBuilder<T> + '_> {
32    if band_settings.is_empty() {
33        return Err(Error::BandsEmpty);
34    }
35    let bands = band_settings.iter().map(|setting| setting.band()).collect();
36    let desired = move |f| desired_response(band_settings, f);
37    let weights = move |f| weight(band_settings, f);
38    PMParameters::new(num_taps, bands, desired, weights)
39}
40
41fn desired_response<T: Float>(settings: &[BandSetting<T>], freq: T) -> T {
42    let setting = closest_setting(settings, freq);
43    setting.desired_response.evaluate(freq, &setting.band)
44}
45
46fn weight<T: Float>(settings: &[BandSetting<T>], freq: T) -> T {
47    let setting = closest_setting(settings, freq);
48    setting.weight.evaluate(freq, &setting.band)
49}
50
51fn closest_setting<T: Float>(settings: &[BandSetting<T>], freq: T) -> &BandSetting<T> {
52    settings
53        .iter()
54        .min_by(|a, b| {
55            a.band
56                .distance(freq)
57                .partial_cmp(&b.band.distance(freq))
58                .unwrap()
59        })
60        .unwrap()
61}
62
63/// Band with desired response and weight [`Setting`]s.
64///
65/// This struct defines a band (a closed subinterval of [0.0, 0.5] in which the
66/// Parks-McClellan algorithm tries to minimize the maximum weighted error) to
67/// which a desired response and a weight function in the form of [`Setting`]s
68/// are attached. The struct is used in one of the coding styles supported by
69/// this crate. In such coding style, the Parks-McClellan algorithm parameters
70/// are defined in terms of a list of `BandSetting`s by calling the
71/// [`pm_parameters`] function.
72#[derive(Debug)]
73pub struct BandSetting<T> {
74    band: Band<T>,
75    desired_response: Setting<T>,
76    weight: Setting<T>,
77}
78
79impl<T: Float> BandSetting<T> {
80    /// Creates a new `BandSetting` with default weight.
81    ///
82    /// The `band_begin` and `band_end` parameter indicate the being and the end
83    /// of the band respectively. The `desired_response` parameter gives the
84    /// desired response in this band. The weight when using this constructor is
85    /// set to `constant(T::one())`. A custom weight can be defined using the
86    /// constructor [`BandSetting::with_weight`] instead, or by calling
87    /// [`BandSetting::set_weight`].
88    pub fn new(band_begin: T, band_end: T, desired_response: Setting<T>) -> Result<BandSetting<T>> {
89        let weight = constant(T::one());
90        BandSetting::with_weight(band_begin, band_end, desired_response, weight)
91    }
92
93    /// Creates a new `BandSetting` with a custom weight.
94    ///
95    /// The `weight` parameter gives the weight function in this band. The
96    /// remaining parameters behave as in [`BandSetting::new`].
97    pub fn with_weight(
98        band_begin: T,
99        band_end: T,
100        desired_response: Setting<T>,
101        weight: Setting<T>,
102    ) -> Result<BandSetting<T>> {
103        let band = Band::new(band_begin, band_end)?;
104        Ok(BandSetting {
105            band,
106            desired_response,
107            weight,
108        })
109    }
110
111    /// Returns the [`Band`] associated to this [`BandSetting`].
112    pub fn band(&self) -> Band<T> {
113        self.band
114    }
115
116    /// Sets the [`Band`] associated to this [`BandSetting`].
117    pub fn set_band(&mut self, band: Band<T>) {
118        self.band = band;
119    }
120
121    /// Sets the desired response used by this [`BandSetting`].
122    pub fn set_desired_response(&mut self, desired_response: Setting<T>) {
123        self.desired_response = desired_response;
124    }
125
126    /// Sets the weight function used by this [`BandSetting`].
127    pub fn set_weight(&mut self, weight: Setting<T>) {
128        self.weight = weight
129    }
130}
131
132/// Desired response or weight setting.
133///
134/// This struct is used to indicate the desired response or the weigth function
135/// for a band through a [`BandSetting`] object when using the coding style that
136/// employs the [`pm_parameters`] function to indicate the Parks-McClellan
137/// algorithm parameters in terms of a list of [`BandSetting`]s.
138///
139/// Values of this object are constructed using the [`constant`], [`linear`],
140/// and [`function`] functions, which create a [`Setting`] that represents a
141/// constant function, a linear function, or a function defined by an arbitrary
142/// closure respectively.
143#[derive(Debug)]
144pub struct Setting<T>(SettingData<T>);
145
146impl<T: Float> Setting<T> {
147    fn evaluate(&self, x: T, band: &Band<T>) -> T {
148        match &self.0 {
149            SettingData::Constant { value } => *value,
150            SettingData::Linear { begin, end } => {
151                let u = (x - band.begin()) / (band.end() - band.begin());
152                *begin + u * (*end - *begin)
153            }
154            SettingData::Function { f } => (f)(x),
155        }
156    }
157}
158
159enum SettingData<T> {
160    Constant { value: T },
161    Linear { begin: T, end: T },
162    Function { f: Box<dyn Fn(T) -> T> },
163}
164
165/// Creates a [`Setting`] that represents a constant function.
166pub fn constant<T: Float>(value: T) -> Setting<T> {
167    Setting(SettingData::Constant { value })
168}
169
170/// Creates a [`Setting`] that represents a linear function.
171///
172/// The function has the values `begin` and `end` at the begin and end of the
173/// band to which the `Setting` is applied respectively, and it is linearly
174/// interpolated for the remaining points of the band.
175pub fn linear<T: Float>(begin: T, end: T) -> Setting<T> {
176    Setting(SettingData::Linear { begin, end })
177}
178
179/// Creates a [`Setting`] that represents an arbitrary function.
180///
181/// The arbitrary function is provided as a boxed closure trait object. The
182/// closure will only be evaluated at points belonging to the band to which the
183/// `Setting` is applied.
184pub fn function<T: Float>(f: Box<dyn Fn(T) -> T>) -> Setting<T> {
185    Setting(SettingData::Function { f })
186}
187
188impl<T: std::fmt::Debug> std::fmt::Debug for SettingData<T> {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        match &self {
191            SettingData::Constant { value } => {
192                f.debug_struct("Constant").field("value", value).finish()
193            }
194            SettingData::Linear { begin, end } => f
195                .debug_struct("Linear")
196                .field("begin", begin)
197                .field("end", end)
198                .finish(),
199            SettingData::Function { .. } => f.debug_struct("Function").finish(),
200        }
201    }
202}