1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
use crate::error::{Error, Result};
use crate::buffer::Window;
use crate::dotprod::DotProd;
use crate::filter;
use num_complex::ComplexFloat;
/// Finite impulse response (FIR) polyphase filter bank (PFB)
#[derive(Clone, Debug)]
pub struct FirPfbFilter<T, Coeff = T> {
num_filters: usize,
w: Window<T>,
filters: Vec<Vec<Coeff>>,
scale: Coeff,
}
impl<T, Coeff> FirPfbFilter<T, Coeff>
where
Coeff: Clone + Copy + ComplexFloat<Real = f32> + From<f32>,
T: Clone + Copy + ComplexFloat<Real = f32> + std::ops::Mul<Coeff, Output = T> + Default,
[Coeff]: DotProd<T, Output = T>,
{
/// Create a new FIR PFB filter bank
///
/// # Arguments
///
/// * `num_filters` - number of filters in the bank
/// * `h` - filter coefficients
/// * `h_len` - filter length
///
/// # Returns
///
/// A new FIR PFB filter bank
pub fn new(num_filters: usize, h: &[Coeff], h_len: usize) -> Result<Self> {
if num_filters == 0 {
return Err(Error::Config("number of filters must be greater than zero".into()));
}
if h_len == 0 {
return Err(Error::Config("filter length must be greater than zero".into()));
}
let h_sub_len = h_len / num_filters;
let mut filters = Vec::with_capacity(num_filters);
for i in 0..num_filters {
let mut h_sub = vec![Coeff::zero(); h_sub_len];
for n in 0..h_sub_len {
// load filter in reverse order
h_sub[h_sub_len - n - 1] = h[i + n * num_filters];
}
filters.push(h_sub);
}
let w = Window::new(h_sub_len)?;
let mut q = Self {
num_filters,
w,
filters,
scale: Coeff::one(),
};
q.reset();
Ok(q)
}
/// Create a new FIR PFB filter bank with default parameters
///
/// This is equivalent to FirPfbFilter::new_kaiser(num_filters, m, 0.5, 60.0)
///
/// # Arguments
///
/// * `num_filters` - number of filters in the bank
/// * `m` - filter delay
///
/// # Returns
///
/// A new FIR PFB filter bank
pub fn default(num_filters: usize, m: usize) -> Result<Self> {
Self::new_kaiser(num_filters, m, 0.5, 60.0)
}
/// Create a new FIR PFB filter bank using Kaiser-Bessel windowed sinc filter design
///
/// # Arguments
///
/// * `num_filters` - number of filters in the bank
/// * `m` - filter delay
/// * `fc` - filter normalized cut-off frequency
/// * `as_` - filter stop-band suppression [dB]
///
/// # Returns
///
/// A new FIR PFB filter bank
pub fn new_kaiser(num_filters: usize, m: usize, fc: f32, as_: f32) -> Result<Self> {
if num_filters == 0 {
return Err(Error::Config("number of filters must be greater than zero".into()));
}
if m == 0 {
return Err(Error::Config("filter delay must be greater than 0".into()));
}
if fc <= 0.0 || fc > 0.5 {
return Err(Error::Config("filter cut-off frequency must be in (0,0.5)".into()));
}
if as_ < 0.0 {
return Err(Error::Config("filter excess bandwidth factor must be in [0,1]".into()));
}
let h_len = 2 * num_filters * m + 1;
let hf = filter::fir_design_kaiser(h_len, fc / num_filters as f32, as_, 0.0)?;
let hc: Vec<Coeff> = hf.iter().map(|&x| x.into()).collect();
Self::new(num_filters, &hc, h_len)
}
/// Create a new FIR PFB filter bank using square-root Nyquist prototype filter design
///
/// # Arguments
///
/// * `filter_type` - filter type
/// * `num_filters` - number of filters in the bank
/// * `k` - samples/symbol
/// * `m` - filter delay
/// * `beta` - excess bandwidth factor
///
/// # Returns
///
/// A new FIR PFB filter bank
pub fn new_rnyquist(filter_type: filter::FirFilterShape, num_filters: usize, k: usize, m: usize, beta: f32) -> Result<Self> {
if num_filters == 0 {
return Err(Error::Config("number of filters must be greater than zero".into()));
}
if k < 2 {
return Err(Error::Config("filter samples/symbol must be greater than 1".into()));
}
if m == 0 {
return Err(Error::Config("filter delay must be greater than 0".into()));
}
if beta < 0.0 || beta > 1.0 {
return Err(Error::Config("filter excess bandwidth factor must be in [0,1]".into()));
}
let h_len = 2 * num_filters * k * m + 1;
let hf = filter::fir_design_prototype(filter_type, num_filters * k, m, beta, 0.0)?;
let hc: Vec<Coeff> = hf.iter().map(|&x| x.into()).collect();
Self::new(num_filters, &hc, h_len)
}
/// Create a new FIR PFB filter bank using square-root derivative Nyquist prototype filter design
///
/// # Arguments
///
/// * `filter_type` - filter type
/// * `num_filters` - number of filters in the bank
/// * `k` - samples/symbol
/// * `m` - filter delay
/// * `beta` - excess bandwidth factor
///
/// # Returns
///
/// A new FIR PFB filter bank
pub fn new_drnyquist(filter_type: filter::FirFilterShape, num_filters: usize, k: usize, m: usize, beta: f32) -> Result<Self> {
if num_filters == 0 {
return Err(Error::Config("number of filters must be greater than zero".into()));
}
if k < 2 {
return Err(Error::Config("filter samples/symbol must be greater than 1".into()));
}
if m == 0 {
return Err(Error::Config("filter delay must be greater than 0".into()));
}
if beta < 0.0 || beta > 1.0 {
return Err(Error::Config("filter excess bandwidth factor must be in [0,1]".into()));
}
let h_len = 2 * num_filters * k * m + 1;
let hf = filter::fir_design_prototype(filter_type, num_filters * k, m, beta, 0.0)?;
let mut dhf = vec![0.0; h_len];
let mut hdh_max: f32 = 0.0;
for i in 0..h_len {
dhf[i] = if i == 0 {
hf[i + 1] - hf[h_len - 1]
} else if i == h_len - 1 {
hf[0] - hf[i - 1]
} else {
hf[i + 1] - hf[i - 1]
};
hdh_max = hdh_max.max((hf[i] * dhf[i]).abs());
}
let hc: Vec<Coeff> = dhf.iter().map(|&x| (x * 0.06 / hdh_max).into()).collect();
Self::new(num_filters, &hc, h_len)
}
// pub fn set_coefficients(&mut self, num_filters: usize, h: &[Coeff], h_len: usize) -> Result<()> {
// if num_filters == 0 {
// return Err(Error::Config("number of filters must be greater than zero".into()));
// }
// if h_len == 0 {
// return Err(Error::Config("filter length must be greater than zero".into()));
// }
// let h_sub_len = h_len / num_filters;
// let mut filters = Vec::with_capacity(num_filters);
// for i in 0..num_filters {
// let mut h_sub = vec![Coeff::zero(); h_sub_len];
// for n in 0..h_sub_len {
// // load filter in reverse order
// h_sub[h_sub_len - n - 1] = h[i + n * num_filters];
// }
// filters.push(h_sub);
// }
// let w = Window::new(h_sub_len)?;
// self.num_filters = num_filters;
// self.filters = filters;
// self.w = w;
// self.reset()?;
// Ok(())
// }
/// Reset the filter bank
pub fn reset(&mut self) -> () {
self.w.reset();
}
/// Set the output scaling for the filter bank
///
/// # Arguments
///
/// * `scale` - scaling factor to apply to each output sample
pub fn set_scale(&mut self, scale: Coeff) {
self.scale = scale;
}
/// Get the output scaling for the filter bank
///
/// # Returns
///
/// The scaling factor applied to each output sample
pub fn get_scale(&self) -> Coeff {
self.scale
}
/// Push a sample into the filter bank
///
/// # Arguments
///
/// * `x` - input sample
pub fn push(&mut self, x: T) -> () {
self.w.push(x)
}
/// Write a block of samples into the filter bank
///
/// # Arguments
///
/// * `x` - input samples
pub fn write(&mut self, x: &[T]) -> () {
self.w.write(x)
}
/// Execute the filter bank on a single input sample
///
/// # Arguments
///
/// * `i` - index of filter to use
///
/// # Returns
///
/// The output sample
pub fn execute(&mut self, i: usize) -> Result<T> {
if i >= self.num_filters {
return Err(Error::Config(format!("filterbank index ({}) exceeds maximum ({})", i, self.num_filters)));
}
let r = self.w.read();
let mut y = self.filters[i].dotprod(r);
y = y * self.scale;
Ok(y)
}
/// Execute the filter bank on a block of input samples
///
/// # Arguments
///
/// * `i` - index of filter to use
/// * `x` - input samples
/// * `y` - output samples
pub fn execute_block(&mut self, i: usize, x: &[T], y: &mut [T]) -> Result<()> {
for (&xi, yi) in x.iter().zip(y.iter_mut()) {
self.push(xi);
*yi = self.execute(i)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_macro::autotest_annotate;
use approx::assert_relative_eq;
#[test]
#[autotest_annotate(autotest_firpfb_impulse_response)]
fn test_firpfb_impulse_response() {
// Initialize variables
let tol = 1e-4f32;
// k=2, m=3, beta=0.3, npfb=4;
// h=rrcos(k*npfb,m,beta);
let h: [f32; 48] = [
-0.033116, -0.024181, -0.006284, 0.018261,
0.045016, 0.068033, 0.080919, 0.078177,
0.056597, 0.016403, -0.038106, -0.098610,
-0.153600, -0.189940, -0.194900, -0.158390,
-0.075002, 0.054511, 0.222690, 0.415800,
0.615340, 0.800390, 0.950380, 1.048100,
1.082000, 1.048100, 0.950380, 0.800390,
0.615340, 0.415800, 0.222690, 0.054511,
-0.075002, -0.158390, -0.194900, -0.189940,
-0.153600, -0.098610, -0.038106, 0.016403,
0.056597, 0.078177, 0.080919, 0.068033,
0.045016, 0.018261, -0.006284, -0.024181
];
// filter input
let noise: [f32; 12] = [
0.438310, 1.001900, 0.200600, 0.790040,
1.134200, 1.592200, -0.702980, -0.937560,
-0.511270, -1.684700, 0.328940, -0.387780
];
// expected filter outputs
let test: [f32; 4] = [
2.05558467194397,
1.56922189602661,
0.998479744645138,
0.386125857849177
];
// Load filter coefficients externally
let mut f = FirPfbFilter::<f32, f32>::new(4, &h, 48).unwrap();
for &n in noise.iter() {
f.push(n);
}
for (i, &expected) in test.iter().enumerate() {
let y = f.execute(i).unwrap();
assert_relative_eq!(expected, y, epsilon = tol);
}
}
#[test]
#[autotest_annotate(autotest_firpfb_crcf_copy)]
fn test_firpfb_crcf_copy() {
use num_complex::Complex32;
// create base object with irregular parameters
let m = 13;
let h = 7;
let mut q0 = FirPfbFilter::<Complex32, f32>::default(m, h).unwrap();
// run random samples through filter
let num_samples = 80;
for _ in 0..num_samples {
let v = Complex32::new(crate::random::randnf(), crate::random::randnf());
q0.push(v);
}
// copy object
let mut q1 = q0.clone();
// run random samples through filter
for _ in 0..num_samples {
// random input channel and index
let v = Complex32::new(crate::random::randnf(), crate::random::randnf());
let idx = rand::random::<usize>() % m;
// push sample through each filter
q0.push(v);
q1.push(v);
// compare outputs
let y0 = q0.execute(idx).unwrap();
let y1 = q1.execute(idx).unwrap();
assert_eq!(y0, y1);
}
}
}