Library of Assembled Shared Sources
filters.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * Distributed under the terms of the GPL (GNU Public License)
6 *
7 * The LASS License:
8 *
9 * Copyright 2004-2006 Bram de Greve and Tom De Muer
10 *
11 * LASS is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#ifndef LASS_GUARDIAN_OF_INCLUSION_NUM_FILTERS_INL
27#define LASS_GUARDIAN_OF_INCLUSION_NUM_FILTERS_INL
28
29#include "num_common.h"
30#include "filters.h"
31#include "polynomial.h"
33
34namespace lass
35{
36namespace num
37{
38namespace impl
39{
40 template <typename T, typename ForwardIterator>
41 num::Polynomial<T> laplaceToZHelper(ForwardIterator first, ForwardIterator last, const T& samplingFrequency)
42 {
43 typedef num::NumTraits<T> TNumTraits;
44 typedef num::Polynomial<T> TPolynomial;
45
46 TPolynomial oneMinZ = 2 * samplingFrequency * (TNumTraits::one - TPolynomial::x());
47 TPolynomial onePlusZ = TNumTraits::one + TPolynomial::x();
48 TPolynomial result;
49 const size_t n = static_cast<size_t>(std::distance(first, last));
50 for (size_t i = 0; i < n; ++i)
51 {
52 result += *first++ * oneMinZ.pow(i) * onePlusZ.pow(n - i - 1);
53 }
54 return result;
55 }
56
57 template <typename T, typename ForwardIterator1, typename ForwardIterator2>
58 std::pair< std::vector<T>, std::vector<T> >
59 laplaceToZ(ForwardIterator1 numeratorFirst, ForwardIterator1 numeratorLast,
60 ForwardIterator2 denominatorFirst, ForwardIterator2 denominatorLast,
61 const T& samplingFrequency)
62 {
63 typedef num::NumTraits<T> TNumTraits;
64 typedef num::Polynomial<T> TPolynomial;
65
66 TPolynomial num = laplaceToZHelper(numeratorFirst, numeratorLast, samplingFrequency);
67 TPolynomial den = laplaceToZHelper(denominatorFirst, denominatorLast, samplingFrequency);
68 TPolynomial onePlusZ = TNumTraits::one + TPolynomial::x();
69 const size_t m = num.size();
70 const size_t n = den.size();
71 if (n > m)
72 {
73 num *= onePlusZ.pow(static_cast<unsigned>(n - m));
74 }
75 else
76 {
77 den *= onePlusZ.pow(static_cast<unsigned>(m - n));
78 }
79 return std::make_pair(num.coefficients(), den.coefficients());
80 }
81
82 template <typename T>
83 std::pair< std::vector<T>, std::vector<T> >
84 laplaceButterworthLowPass(unsigned n, const T& cutoff, const T& gain)
85 {
86 typedef num::NumTraits<T> TNumTraits;
87 typedef num::Polynomial<T> TPolynomial;
88
89 const TPolynomial s = TPolynomial::x() / cutoff;
90 const TPolynomial s2 = s * s;
91
92 TPolynomial nom(gain);
93 TPolynomial den(TNumTraits::one);
94 if (n % 2 == 1)
95 den += s;
96 for (unsigned k = 0; k < n / 2; ++k)
97 {
98 const T theta = (TNumTraits::pi * (2 * k + n + 1)) / (2 * n);
99 den *= s2 - 2 * num::cos(theta) * s + TNumTraits::one;
100 }
101
102 return std::make_pair(nom.coefficients(), den.coefficients());
103 }
104
105 template <typename T>
106 std::pair< std::vector<T>, std::vector<T> >
107 laplaceButterworthHighPass(unsigned n, const T& cutoff, const T& gain)
108 {
109 typedef num::NumTraits<T> TNumTraits;
110 typedef std::pair< std::vector<T>, std::vector<T> > TValuesPair;
111
112 TValuesPair result = laplaceButterworthLowPass<T>(n, TNumTraits::one, TNumTraits::one);
113
114 std::reverse(result.second.begin(), result.second.end());
115 for (size_t k = 0; k < result.second.size(); ++k)
116 {
117 result.second[k] /= num::pow(cutoff, T(k));
118 }
119
120 result.first.resize(n + 1);
121 std::fill(result.first.begin(), result.first.end(), TNumTraits::zero);
122 result.first[n] = gain / num::pow(cutoff, T(n));
123
124 return result;
125 }
126}
127
128// --- FirFilter -----------------------------------------------------------------------------------
129
130template <typename T, typename InIt, typename OutIt>
131FirFilter<T, InIt, OutIt>::FirFilter(const TValues& impulseResponse):
132 taps_(impulseResponse),
133 buffer_(2 * impulseResponse.size()),
134 nextIndex_(impulseResponse.size()),
135 tapSize_(impulseResponse.size()),
136 bufferIndex_(0)
137{
138 if (impulseResponse.empty())
139 {
140 LASS_THROW("Cannot use an empty vector as impulse response");
141 }
142
143 for (size_t i = 0; i < tapSize_; ++i)
144 {
145 nextIndex_[i] = (i - 1 + tapSize_) % tapSize_;
146 }
147 this->reset();
148}
149
150
151
152template <typename T, typename InIt, typename OutIt>
153typename FirFilter<T, InIt, OutIt>::TOutputIterator
154FirFilter<T, InIt, OutIt>::doFilter(TInputIterator first, TInputIterator last, TOutputIterator output)
155{
156 const T* const taps = &taps_[0];
157 while (first != last)
158 {
159 buffer_[tapSize_ + bufferIndex_] = buffer_[bufferIndex_] = *first++;
160 const T* const buf = &buffer_[bufferIndex_];
161 TValue accumulator = TNumTraits::zero;
162 for (size_t i = 0; i < tapSize_; ++i)
163 {
164 accumulator += taps[i] * buf[i];
165 }
166 bufferIndex_ = nextIndex_[bufferIndex_];
167 *output++ = accumulator;
168 }
169 return output;
170}
171
172
173
174template <typename T, typename InIt, typename OutIt>
175void FirFilter<T, InIt, OutIt>::doReset()
176{
177 std::fill(buffer_.begin(), buffer_.end(), TNumTraits::zero);
178}
179
180
181
182// --- IirFilter -----------------------------------------------------------------------------------
183
184/** construct IIR filter
185 *
186 * @code
187 * a[0] + a[1] * z^-1 + a[2] * z^-2 + ... + a[m-1] * z^-(m-1)
188 * H(z) = ----------------------------------------------------------
189 * b[0] + b[1] * z^-1 + b[2] * z^-2 + ... + b[m-1] * z^-(m-1)
190 * @endcode
191 *
192 * @param numerator [in] coefficients a[i] of transfer function H(z).
193 * @param denominator [in] coefficients b[i] of transfer function H(z).
194 */
195template <typename T, typename InIt, typename OutIt>
196IirFilter<T, InIt, OutIt>::IirFilter(const TValues& numerator, const TValues& denominator)
197{
198 this->init(numerator, denominator);
199 this->reset();
200}
201
202
203
204/** construct IIR filter
205 *
206 * @param coefficients [in] numerator (=first) and denominator (=second) of transfer function H(z).
207 */
208template <typename T, typename InIt, typename OutIt>
209IirFilter<T, InIt, OutIt>::IirFilter(const TValuesPair& coefficients)
210{
211 this->init(coefficients.first, coefficients.second);
212 this->reset();
213}
214
215
216
217/* make an IIR filter from a transfer function H(s) in Laplace domain.
218 *
219 * @code
220 * a[0] + a[1] * s + a[2] * s^2 + ... + a[m-1] * s^m-1
221 * H(s) = ---------------------------------------------------
222 * b[0] + b[1] * s + b[2] * s^2 + ... + b[n-1] * s^n-1
223 * @endcode
224 *
225 * @param numerator [in] coefficients a[i] of Laplace transfer function H(s).
226 * @param denominator [in] coefficients b[i] of Laplace transfer function H(s).
227 * @param samplingFrequency [in] sampling frequency of the digital signal.
228 */
229template <typename T, typename InIt, typename OutIt>
230IirFilter<T, InIt, OutIt> IirFilter<T, InIt, OutIt>::makeLaplace(
231 const TValues& numerator, const TValues& denominator, TParam samplingFrequency)
232{
233 return doMakeLaplace(numerator.begin(), numerator.end(), denominator.begin(), denominator.end(), samplingFrequency);
234}
235
236
237
238/** make an IIR filter implementing a low-pass butterworth filter.
239 *
240 * @param order [in] order of filter. filter rolls of at (order * 6) dB per decade.
241 * @param cutoffAngularFrequency [in] cutoff frequency measured in radians per sec (w = 2 pi f).
242 * @param gain [in] DC gain
243 * @param samplingFrequency [in] sampling frequency of digital signal
244 */
245template <typename T, typename InIt, typename OutIt>
247 unsigned order, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
248{
249 return doMakeLaplace(impl::laplaceButterworthLowPass(order, cutoffAngularFrequency, gain), samplingFrequency);
250}
251
252
253
254/** make an IIR filter implementing a high-pass butterworth filter.
255 *
256 * @param order [in] order of filter. filter rolls of at (order * 6) dB per decade.
257 * @param cutoffAngularFrequency [in] cutoff frequency measured in radians per sec (w = 2 pi f).
258 * @param gain [in] DC gain
259 * @param samplingFrequency [in] sampling frequency of digital signal
260 */
261template <typename T, typename InIt, typename OutIt>
263 unsigned order, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
264{
265 return doMakeLaplace(impl::laplaceButterworthHighPass(order, cutoffAngularFrequency, gain), samplingFrequency);
266}
267
268
269
270/** make an IIR filter implementing an low-pass RLC circuit.
271 *
272 * @code
273 * o---[R]---[sL]---*-----o
274 * |
275 * Vin [1/sC] Vout
276 * |
277 * o----------------*-----o
278 * @endcode
279 *
280 * @param qFactor [in] quality factor Q = sqrt(L/C)/R (Q > 1 for resonance peak, Q = 1/sqrt(2) for 2nd order butterworth).
281 * @param cutoffAngularFrequency [in] cutoff frequency measured in radians per sec (w = 2 pi f = 1 / sqrt(LC))
282 * @param gain [in] DC gain
283 * @param samplingFrequency [in] sampling frequency of digital signal
284 */
285template <typename T, typename InIt, typename OutIt>
287 TParam qFactor, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
288{
289 const TValue num[] = { gain };
290 const TValue den[] = { TNumTraits::one, num::inv(cutoffAngularFrequency * qFactor), num::inv(num::sqr(cutoffAngularFrequency)) };
291 return doMakeLaplace(num, num + 1, den, den + 3, samplingFrequency);
292}
293
294
295
296/** make an IIR filter implementing an high-pass RLC circuit.
297 *
298 * @code
299 * o---[R]---[1/sC]---*----o
300 * |
301 * Vin [sL] Vout
302 * |
303 * o------------------*----o
304 * @endcode
305 *
306 * @param qFactor [in] quality factor Q = sqrt(L/C)/R (Q > 1 for resonance peak, Q = 1/sqrt(2) for 2nd order butterworth).
307 * @param cutoffAngularFrequency [in] cutoff frequency measured in radians per sec (w = 2 pi f = 1 / sqrt(LC))
308 * @param gain [in] DC gain
309 * @param samplingFrequency [in] sampling frequency of digital signal
310 */
311template <typename T, typename InIt, typename OutIt>
313 TParam qFactor, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
314{
315 const TValue num[] = { 0, 0, gain / num::sqr(cutoffAngularFrequency) };
316 const TValue den[] = { TNumTraits::one, num::inv(cutoffAngularFrequency * qFactor), num::inv(num::sqr(cutoffAngularFrequency)) };
317 return doMakeLaplace(num, num + 3, den, den + 3, samplingFrequency);
318}
319
320
321
322/** make an IIR filter implementing an band-pass RLC circuit.
323 *
324 * @code
325 * o---[sL]---[1/sC]---*----o
326 * |
327 * Vin [R] Vout
328 * |
329 * o-------------------*----o
330 * @endcode
331 *
332 * @param qFactor [in] quality factor Q = sqrt(L/C)/R (greater Q is more narrow stop band).
333 * @param centerAngularFrequency [in] frequency to be filter out, measured in radians per sec (w = 2 pi f = 1 / sqrt(LC))
334 * @param gain [in] DC gain
335 * @param samplingFrequency [in] sampling frequency of digital signal
336 */
337template <typename T, typename InIt, typename OutIt>
339 TParam qFactor, TParam centerAngularFrequency, TParam gain, TParam samplingFrequency)
340{
341 const TValue num[] = { TNumTraits::zero, gain / (centerAngularFrequency * qFactor) };
342 const TValue den[] = { TNumTraits::one, num::inv(centerAngularFrequency * qFactor), num::inv(num::sqr(centerAngularFrequency)) };
343 return doMakeLaplace(num, num + 2, den, den + 3, samplingFrequency);
344}
345
346
347
348/** make an IIR filter implementing an notch RLC circuit.
349 *
350 * @code
351 * o---[R]---*----o
352 * |
353 * [sL]
354 * Vin | Vout
355 * [1/sC]
356 * |
357 * o---------*----o
358 * @endcode
359 *
360 * @param qFactor [in] quality factor Q = sqrt(L/C)/R (greater Q is more narrow stop band).
361 * @param centerAngularFrequency [in] frequency to be filter out, measured in radians per sec (w = 2 pi f = 1 / sqrt(LC))
362 * @param gain [in] DC gain
363 * @param samplingFrequency [in] sampling frequency of digital signal
364 */
365template <typename T, typename InIt, typename OutIt>
367 TParam qFactor, TParam centerAngularFrequency, TParam gain, TParam samplingFrequency)
368{
369 const TValue num[] = { gain, TNumTraits::zero, gain / num::sqr(centerAngularFrequency) };
370 const TValue den[] = { TNumTraits::one, num::inv(centerAngularFrequency * qFactor), num::inv(num::sqr(centerAngularFrequency)) };
371 return doMakeLaplace(num, num + 3, den, den + 3, samplingFrequency);
372}
373
374
375
376/** make an IIR filter implementing a perfect integrator
377 *
378 * @param gain [in] DC gain
379 * @param samplingFrequency [in] sampling frequency of digital signal
380 */
381template <typename T, typename InIt, typename OutIt>
383{
384 TValue num[] = { gain };
385 TValue den[] = { TNumTraits::zero, TNumTraits::one, };
386 return doMakeLaplace(num, num + 1, den, den + 2, samplingFrequency);
387}
388
389
390
391/** make an IIR filter implementing a perfect differentiator
392 *
393 * @param gain [in] DC gain
394 * @param samplingFrequency [in] sampling frequency of digital signal
395 */
396template <typename T, typename InIt, typename OutIt>
398{
399 TValue num[] = { TNumTraits::zero, gain, };
400 TValue den[] = { TNumTraits::one, };
401 return doMakeLaplace(num, num + 2, den, den + 1, samplingFrequency);
402}
403
404
405
406/** make an A weighting filter
407 *
408 * @param samplingFrequency [in] sampling frequency of digital signal
409 */
410template <typename T, typename InIt, typename OutIt>
412{
413 TValue num[] = { 0, 0, 0, 0, 7.39705e9 };
414 TValue den[] = { .3086662405e21, .5301498365e19, .2674964995e17, .3343329206e14, 6734684542., 158881.5, 1 };
415 return doMakeLaplace(
416 num, num + sizeof(num)/sizeof(TValue),
417 den, den + sizeof(den)/sizeof(TValue),
418 samplingFrequency);
419}
420
421
422
423template <typename T, typename InIt, typename OutIt>
424typename IirFilter<T, InIt, OutIt>::TOutputIterator
425IirFilter<T, InIt, OutIt>::doFilter(TInputIterator first, TInputIterator last, TOutputIterator output)
426{
427 const T* const xTaps = &xTaps_[0];
428 const T* const yTaps = yTaps_.empty() ? 0 : &yTaps_[0];
429 while (first != last)
430 {
431 xBuffer_[xTapSize_ + xBufferIndex_] = xBuffer_[xBufferIndex_] = *first++;
432 const T* const xBuf = &xBuffer_[xBufferIndex_];
433 const T* const yBuf = &yBuffer_[yBufferIndex_];
434 TValue accumulator = TNumTraits::zero;
435 for (size_t i = 0; i < xTapSize_; ++i)
436 {
437 accumulator += xTaps[i] * xBuf[i];
438 }
439 for (size_t i = 0; i < yTapSize_; ++i)
440 {
441 accumulator -= yTaps[i] * yBuf[i];
442 }
443 xBufferIndex_ = xNextIndex_[xBufferIndex_];
444 yBufferIndex_ = yNextIndex_[yBufferIndex_];
445 yBuffer_[yTapSize_ + yBufferIndex_] = yBuffer_[yBufferIndex_] = accumulator;
446 *output++ = accumulator;
447 }
448 return output;
449}
450
451
452
453template <typename T, typename InIt, typename OutIt>
454void IirFilter<T, InIt, OutIt>::doReset()
455{
456 std::fill(xBuffer_.begin(), xBuffer_.end(), TNumTraits::zero);
457 std::fill(yBuffer_.begin(), yBuffer_.end(), TNumTraits::zero);
458}
459
460
461
462template <typename T, typename InIt, typename OutIt>
463void IirFilter<T, InIt, OutIt>::init(const TValues& numerator, const TValues& denominator)
464{
465 if (numerator.empty() || denominator.empty())
466 {
467 LASS_THROW("Cannot use an empty vector as numerator or denominator");
468 }
469 if (denominator[0] == TNumTraits::zero)
470 {
471 LASS_THROW("Cannot use a zero as first element in the denominator");
472 }
473
474 xTaps_ = numerator;
475 yTaps_.assign(stde::next(denominator.begin()), denominator.end());
476 xTapSize_ = xTaps_.size();
477 yTapSize_ = yTaps_.size();
478 const TValue scaler = num::inv(denominator[0]);
479 for (size_t i = 0; i < xTapSize_; ++i)
480 {
481 xTaps_[i] *= scaler;
482 }
483 for (size_t i = 0; i < yTapSize_; ++i)
484 {
485 yTaps_[i] *= scaler;
486 }
487
488 const size_t xBufferSize = xTapSize_;
489 const size_t yBufferSize = std::max<size_t>(yTapSize_, 1);
490 xBuffer_.resize(2 * xBufferSize);
491 yBuffer_.resize(2 * yBufferSize);
492 xNextIndex_.resize(xBufferSize);
493 yNextIndex_.resize(yBufferSize);
494 for (size_t i = 0; i < xBufferSize; ++i)
495 {
496 xNextIndex_[i] = (i + xBufferSize - 1) % xBufferSize;
497 }
498 for (size_t i = 0; i < yBufferSize; ++i)
499 {
500 yNextIndex_[i] = (i + yBufferSize - 1) % yBufferSize;
501 }
502
503 xBufferIndex_ = 0;
504 yBufferIndex_ = 0;
505}
506
507
508
509/** little private helper
510 */
511template <typename T, typename InIt, typename OutIt>
512template <typename FwdIt1, typename FwdIt2>
513IirFilter<T, InIt, OutIt> IirFilter<T, InIt, OutIt>::doMakeLaplace(
514 FwdIt1 numFirst, FwdIt1 numLast, FwdIt2 denFirst, FwdIt2 denLast, TParam samplingFrequency)
515{
516 return IirFilter<T, InIt, OutIt>(impl::laplaceToZ(numFirst, numLast, denFirst, denLast, samplingFrequency));
517}
518
519
520
521/** little private helper
522 */
523template <typename T, typename InIt, typename OutIt>
524IirFilter<T, InIt, OutIt> IirFilter<T, InIt, OutIt>::doMakeLaplace(
525 const TValuesPair& coefficients, TParam samplingFrequency)
526{
527 return doMakeLaplace(coefficients.first.begin(), coefficients.first.end(),
528 coefficients.second.begin(), coefficients.second.end(),
529 samplingFrequency);
530}
531
532}
533
534}
535
536#endif
537
538// EOF
Infinite Impulse Response filter.
Definition filters.h:120
static IirFilter makeButterworthHighPass(unsigned order, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
make an IIR filter implementing a high-pass butterworth filter.
Definition filters.inl:262
IirFilter(const TValues &numerator, const TValues &denominator)
construct IIR filter
Definition filters.inl:196
static IirFilter makeRlcNotch(TParam qFactor, TParam centerAngularFrequency, TParam gain, TParam samplingFrequency)
make an IIR filter implementing an notch RLC circuit.
Definition filters.inl:366
static IirFilter makeRlcHighPass(TParam qFactor, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
make an IIR filter implementing an high-pass RLC circuit.
Definition filters.inl:312
static IirFilter makeDifferentiator(TParam gain, TParam samplingFrequency)
make an IIR filter implementing a perfect differentiator
Definition filters.inl:397
static IirFilter makeButterworthLowPass(unsigned order, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
make an IIR filter implementing a low-pass butterworth filter.
Definition filters.inl:246
static IirFilter makeRlcBandPass(TParam qFactor, TParam centerAngularFrequency, TParam gain, TParam samplingFrequency)
make an IIR filter implementing an band-pass RLC circuit.
Definition filters.inl:338
static IirFilter makeIntegrator(TParam gain, TParam samplingFrequency)
make an IIR filter implementing a perfect integrator
Definition filters.inl:382
static IirFilter makeRlcLowPass(TParam qFactor, TParam cutoffAngularFrequency, TParam gain, TParam samplingFrequency)
make an IIR filter implementing an low-pass RLC circuit.
Definition filters.inl:286
static IirFilter makeAWeighting(TParam samplingFrequency)
make an A weighting filter
Definition filters.inl:411
T sqr(const T &x)
return x * x
Definition basic_ops.h:162
T inv(const T &x)
return x ^ -1
Definition basic_ops.h:178
T pow(const T &x, const T &p)
return exp(p * log(x));
Definition basic_ops.h:187
numeric types and traits.
Definition basic_ops.h:70
Library for Assembled Shared Sources.
Definition config.h:53