SigPack - the C++ signal processing library
filter.h
Go to the documentation of this file.
1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 #ifndef SP_FILTER_H
5 #define SP_FILTER_H
6 namespace sp
7 {
12 
20 template <class T1, class T2, class T3> class FIR_filt
21 {
22 private:
23  // Ordinary FIR filter
24  arma::uword M;
25  arma::uword cur_p;
26  arma::Mat<T1> buf;
27  arma::Mat<T2> b;
28  // Adaptive LMS FIR filter
29  double mu;
30  arma::uword L;
31  arma::uword blk_ctr;
32  T2 c;
33  arma::Mat<T1> P;
34  arma::Mat<T1> Q;
35  arma::Mat<T1> R;
36  arma::Mat<T1> K;
37  double lmd;
38  arma::Mat<T1> X_tpz;
39  arma::uword do_adapt;
40 public:
44  FIR_filt() {}
45 
49  ~FIR_filt() {}
50 
54  void clear(void)
55  {
56  buf.zeros();
57  cur_p = 0;
58  }
59 
65  void set_coeffs(const arma::Mat<T2> &_b)
66  {
67  M = _b.n_elem;
68  buf.set_size(M, 1);
69  this->clear();
70  b.set_size(M, 1);
71  b = _b;
72  }
73 
79  void set_coeffs(const arma::Col<T2> &_b_col)
80  {
81  arma::Mat<T2> b_mat = arma::conv_to<arma::Mat<T2>>::from(_b_col);
82  set_coeffs(b_mat);
83  }
84 
89  arma::Col<T2> get_coeffs() { return arma::conv_to<arma::Col<T2>>::from(b); }
90 
96  void update_coeffs(const arma::Mat<T2> &_b) { b = _b; }
97 
103  T3 operator()(const T1 &in)
104  {
105  T3 out = 0;
106  arma::uword p = 0;
107  buf[cur_p] = in; // Insert new sample
108  for (arma::uword m = cur_p; m < M; m++)
109  out += b[p++] * buf[m]; // Calc upper part
110  for (arma::uword m = 0; m < cur_p; m++)
111  out += b[p++] * buf[m]; // ... and lower
112 
113  // Move insertion point
114  if (cur_p == 0)
115  cur_p = M - 1;
116  else
117  cur_p--;
118 
119  return out;
120  }
121 
127  arma::Mat<T3> filter(const arma::Mat<T1> &in)
128  {
129  arma::uword sz = in.n_elem;
130  arma::Mat<T3> out(sz, 1);
131  for (arma::uword n = 0; n < sz; n++)
132  out[n] = this->operator()(in[n]);
133  return out;
134  }
135  arma::Col<T3> filter(const arma::Col<T1> &in)
136  {
137  arma::Mat<T1> in_col = arma::conv_to<arma::Mat<T1>>::from(in);
138  return arma::conv_to<arma::Col<T3>>::from(filter(in_col));
139  }
140 
147  void setup_lms(const arma::uword _N, const double _mu,
148  const arma::uword _L = 1)
149  {
150  M = _N;
151  mu = _mu;
152  L = _L;
153  buf.set_size(M, 1);
154  buf.zeros();
155  b.set_size(M, 1);
156  b.zeros();
157  K.set_size(M, 1);
158  K.zeros();
159  cur_p = 0;
160  blk_ctr = 0;
161  do_adapt = 1;
162  }
163 
172  void lms_adapt(const T3 _err)
173  {
174  if (do_adapt)
175  {
176  // Reshape buf
177  arma::Mat<T1> buf_tmp(M, 1);
178  for (arma::uword m = 0; m < M; m++)
179  {
180  buf_tmp(m) = buf((cur_p + m + 1) % M);
181  }
182 
183  // Accumulate
184  K += _err * buf_tmp;
185 
186  // Block update
187  if (blk_ctr++ % L == 0)
188  {
189  b += 2 * mu * K / double(L);
190  K.zeros();
191  }
192  }
193  }
194 
202  void setup_nlms(const arma::uword _N, const double _mu, const T2 _c,
203  const arma::uword _L = 1)
204  {
205  M = _N;
206  mu = _mu;
207  L = _L;
208  c = _c;
209  buf.set_size(M, 1);
210  buf.zeros();
211  b.set_size(M, 1);
212  b.zeros();
213  K.set_size(M, 1);
214  K.zeros();
215  cur_p = 0;
216  blk_ctr = 0;
217  do_adapt = 1;
218  }
219 
229  void nlms_adapt(const T3 _err)
230  {
231  if (do_adapt)
232  {
233  // Reshape buf
234  arma::Mat<T1> buf_tmp(M, 1);
235  for (arma::uword m = 0; m < M; m++)
236  {
237  buf_tmp(m) = buf((cur_p + m + 1) % M);
238  }
239 
240  // Accumulate
241  T1 S = c + arma::as_scalar(buf_tmp.t() * buf_tmp);
242  K += _err * buf_tmp / S;
243 
244  // Block update
245  if (blk_ctr++ % L == 0)
246  {
247  b += 2 * mu * K / double(L);
248  K.zeros();
249  }
250  }
251  }
252 
260  void setup_newt(const arma::uword _N, const double _mu, const T2 _c,
261  const arma::uword _L = 1)
262  {
263  M = _N;
264  mu = _mu;
265  L = _L;
266  c = _c;
267  buf.set_size(M, 1);
268  buf.zeros();
269  b.set_size(M, 1);
270  b.zeros();
271  K.set_size(M, 1);
272  K.zeros();
273  X_tpz.set_size(M, L);
274  X_tpz.zeros();
275  cur_p = 0;
276  blk_ctr = 0;
277  do_adapt = 1;
278  }
279 
290  void newt_adapt(const T3 _err)
291  {
292  if (do_adapt)
293  {
294  // Reshape buf
295  arma::Mat<T1> buf_tmp(M, 1);
296  for (arma::uword m = 0; m < M; m++)
297  {
298  buf_tmp(m) = buf((cur_p + m + 1) % M);
299  }
300 
301  // Accumulate in buf
302  K += _err * buf_tmp;
303  X_tpz.col(blk_ctr % L) = buf_tmp;
304 
305  // Block update
306  if (blk_ctr++ % L == 0)
307  {
308  // Correlation matrix estimate
309  arma::Mat<T1> Rxx = X_tpz * X_tpz.t() / L;
310  arma::Mat<T1> I;
311  I.eye(M, M);
312  b += mu * pinv(Rxx + c * I) * K / L;
313  K.zeros();
314  }
315  }
316  }
317 
324  void setup_rls(const arma::uword _N, const double _lmd, const double _P0)
325  {
326  M = _N;
327  lmd = _lmd;
328  L = 1;
329  P.eye(M, M);
330  P = _P0 * P;
331  K.set_size(M, 1);
332  K.zeros();
333  buf.set_size(M, 1);
334  buf.zeros();
335  b.set_size(M, 1);
336  b.zeros();
337  cur_p = 0;
338  do_adapt = 1;
339  }
340 
351  void rls_adapt(const T3 _err)
352  {
353  if (do_adapt)
354  {
355  // Reshape buf
356  arma::Mat<T1> buf_tmp(M, 1);
357  for (arma::uword m = 0; m < M; m++)
358  {
359  buf_tmp(m) = buf((cur_p + m + 1) % M);
360  }
361 
362  // Update P
363  T1 S = lmd + arma::as_scalar(buf_tmp.t() * P * buf_tmp);
364  K = P * buf_tmp / S;
365  P = (P - K * buf_tmp.t() * P) / lmd;
366 
367  // Update coeffs
368  b = b + K * _err;
369  }
370  }
371 
379  void setup_kalman(const arma::uword _N, const double _P0, const double _Q0,
380  const double _R0)
381  {
382  M = _N;
383  L = 1;
384  P.eye(M, M);
385  P = _P0 * P;
386  Q.eye(M, M);
387  Q = _Q0 * Q;
388  R.ones(1, 1);
389  R = _R0 * R;
390  K.set_size(M, 1);
391  K.zeros();
392  buf.set_size(M, 1);
393  buf.zeros();
394  b.set_size(M, 1);
395  b.zeros();
396  cur_p = 0;
397  do_adapt = 1;
398  }
399 
409  void kalman_adapt(const T3 _err)
410  {
411  if (do_adapt)
412  {
413  // Reshape buf
414  arma::Mat<T1> buf_tmp(M, 1);
415  for (arma::uword m = 0; m < M; m++)
416  {
417  buf_tmp(m) = buf((cur_p + m + 1) % M);
418  }
419 
420  // Innovation/error covariance
421  T1 S = arma::as_scalar(R + buf_tmp.t() * P * buf_tmp);
422 
423  // Kalman gain
424  K = P * buf_tmp / S;
425 
426  // Update coeffs/state
427  b = b + K * _err;
428 
429  // Update estimate covariance
430  P = P - K * buf_tmp.t() * P + Q;
431  }
432  }
433 
438  double get_step_size(void) { return mu; }
439 
444  arma::Mat<T1> get_P(void) { return P; }
445 
450  arma::Mat<T1> get_K(void) { return K; }
451 
456  void set_step_size(const double _mu) { mu = _mu; }
457 
461  void adapt_enable(void) { do_adapt = 1; }
462 
466  void adapt_disble(void) { do_adapt = 0; }
467 };
468 
477 template <class T1, class T2, class T3> class IIR_filt
478 {
479 private:
480  arma::uword M;
481  arma::uword N;
482  arma::uword b_cur_p;
483  arma::uword a_cur_p;
484  arma::Col<T2> b;
485  arma::Col<T2> a;
486  arma::Col<T1> b_buf;
487  arma::Col<T1> a_buf;
488 public:
492  IIR_filt() {}
493 
498 
502  void clear(void)
503  {
504  b_buf.zeros();
505  a_buf.zeros();
506  b_cur_p = 0;
507  a_cur_p = 0;
508  }
509 
516  void set_coeffs(const arma::Col<T2> &_b, const arma::Col<T2> &_a)
517  {
518  M = _b.size();
519  N = _a.size();
520  b_buf.set_size(M);
521  a_buf.set_size(N);
522  this->clear();
523  b = _b / _a[0];
524  a = _a / _a[0];
525  }
526 
533  void update_coeffs(const arma::Col<T2> &_b, const arma::Col<T2> &_a)
534  {
535  b = _b / _a[0];
536  a = _a / _a[0];
537  }
538 
544  T3 operator()(const T1 &in)
545  {
546  T3 out = 0;
547  arma::uword p = 0;
548 
549  // MA part
550  b_buf[b_cur_p] = in; // Insert new sample
551  for (arma::uword m = b_cur_p; m < M; m++)
552  out += b[p++] * b_buf[m]; // Calc upper part
553  for (arma::uword m = 0; m < b_cur_p; m++)
554  out += b[p++] * b_buf[m]; // ... and lower
555 
556  // Move insertion point
557  if (b_cur_p == 0)
558  b_cur_p = M - 1;
559  else
560  b_cur_p--;
561 
562  // AR part
563  p = 1;
564  for (arma::uword n = a_cur_p + 1; n < N; n++)
565  out -= a[p++] * a_buf[n]; // Calc upper part
566  for (arma::uword n = 0; n < a_cur_p; n++)
567  out -= a[p++] * a_buf[n]; // ... and lower
568 
569  a_buf[a_cur_p] = out; // Insert output
570 
571  // Move insertion point
572  if (a_cur_p == 0)
573  a_cur_p = N - 1;
574  else
575  a_cur_p--;
576 
577  return out;
578  }
579 
585  arma::Col<T3> filter(const arma::Col<T1> &in)
586  {
587  arma::uword sz = in.size();
588  arma::Col<T3> out(sz);
589  for (arma::uword n = 0; n < sz; n++)
590  out[n] = this->operator()(in[n]);
591  return out;
592  }
593 };
594 
598 
607 arma_inline arma::vec fir1(const arma::uword M, const double f0)
608 {
609  arma::vec b(M + 1), h(M + 1);
610  h = hamming(M + 1);
611  for (arma::uword m = 0; m < M + 1; m++)
612  {
613  b[m] = f0 * h[m] * sinc(f0 * (m - M / 2.0));
614  }
615  return b / arma::sum(b);
616 }
617 
626 arma_inline arma::vec fir1_hp(const arma::uword M, const double f0)
627 {
628  if (M % 2 != 0)
629  err_handler("Filter order must be even");
630  arma::vec b(M + 1), h(M + 1);
631  h = hamming(M + 1);
632  for (arma::uword m = 0; m < M + 1; m++)
633  {
634  b[m] = h[m] * (sinc(m - M / 2.0) - f0 * sinc(f0 * (m - M / 2.0)));
635  }
636 
637  // Scale
638  std::complex<double> i(0, 1);
639  double nrm;
640  arma::vec fv = arma::regspace(0, double(M));
641  nrm = abs(arma::sum(exp(-i * fv * PI) % b));
642 
643  return b / nrm;
644 }
645 
655 arma_inline arma::vec fir1_bp(const arma::uword M, const double f0,
656  const double f1)
657 {
658  if (f1 <= f0)
659  err_handler("Frequencies must be [0 < f0 < f1 < 1]");
660 
661  arma::vec b(M + 1), h(M + 1);
662  h = hamming(M + 1);
663  for (arma::uword m = 0; m < M + 1; m++)
664  {
665  b[m] =
666  h[m] * (f1 * sinc(f1 * (m - M / 2.0)) - f0 * sinc(f0 * (m - M / 2.0)));
667  }
668 
669  // Scale
670  double fc = (f0 + f1) / 2;
671  std::complex<double> i(0, 1);
672  double nrm;
673  arma::vec fv = arma::regspace(0, double(M));
674  nrm = abs(arma::sum(exp(-i * fv * PI * fc) % b));
675 
676  return b / nrm;
677 }
678 
688 arma_inline arma::vec fir1_bs(const arma::uword M, const double f0,
689  const double f1)
690 {
691  if (M % 2 != 0)
692  err_handler("Filter order must be even");
693  if (f1 <= f0)
694  err_handler("Frequencies must be [0 < f0 < f1 < 1]");
695 
696  arma::vec b(M + 1), h(M + 1);
697  h = hamming(M + 1);
698  for (arma::uword m = 0; m < M + 1; m++)
699  {
700  b[m] = h[m] * (sinc(m - M / 2.0) - f1 * sinc(f1 * (m - M / 2.0)) +
701  f0 * sinc(f0 * (m - M / 2.0)));
702  }
703 
704  return b / arma::sum(b);
705 }
706 
716 arma_inline arma::vec fd_filter(const arma::uword M, double fd)
717 {
718  arma::vec h(M);
719  arma::vec w = blackmanharris(M);
720  if (M % 2 == 1)
721  fd = fd - 0.5; // Offset for odd nr of taps
722  for (arma::uword m = 0; m < M; m++)
723  {
724  h(m) = w(m) * sinc(m - M / 2.0 - fd);
725  }
726  h = h / arma::sum(h); // Normalize gain
727 
728  return h;
729 }
730 
738 arma_inline arma::cx_vec freq(const arma::vec b, const arma::vec a,
739  const arma::uword K = 512)
740 {
741  arma::cx_vec h(K);
742  arma::uword M = b.size();
743  arma::uword N = a.size();
744  std::complex<double> b_tmp, a_tmp, i(0, 1);
745  for (arma::uword k = 0; k < K; k++)
746  {
747  b_tmp = std::complex<double>(b(0), 0);
748  for (arma::uword m = 1; m < M; m++)
749  b_tmp += b(m) * (cos(m * PI * k / K) - i * sin(m * PI * k / K));
750  a_tmp = std::complex<double>(a(0), 0);
751  for (arma::uword n = 1; n < N; n++)
752  a_tmp += a(n) * (cos(n * PI * k / K) - i * sin(n * PI * k / K));
753  h(k) = b_tmp / a_tmp;
754  }
755  return h;
756 }
757 
765 arma_inline arma::vec freqz(const arma::vec b, const arma::vec a,
766  const arma::uword K = 512)
767 {
768  arma::cx_vec f = freq(b, a, K);
769  return abs(f);
770 }
771 
779 arma_inline arma::vec phasez(const arma::vec b, const arma::vec a,
780  const arma::uword K = 512)
781 {
782  arma::cx_vec f = freq(b, a, K);
783  return angle(f);
784 }
786 
787 } // namespace sp
788 #endif
IIR/ARMA filter class.
Definition: filter.h:477
arma::Mat< T1 > R
Adaptive filter Measurement noise.
Definition: filter.h:35
double mu
Adaptive filter step size.
Definition: filter.h:29
void lms_adapt(const T3 _err)
LMS Filter update function.
Definition: filter.h:172
double lmd
Adaptive filter RLS forgetting factor.
Definition: filter.h:37
void set_coeffs(const arma::Col< T2 > &_b_col)
Sets coefficients in FIR filter (col format) The internal state and pointers are cleared.
Definition: filter.h:79
void update_coeffs(const arma::Mat< T2 > &_b)
Updates coefficients in FIR filter without clearing the internal states.
Definition: filter.h:96
arma::Mat< T3 > filter(const arma::Mat< T1 > &in)
Filter function.
Definition: filter.h:127
arma::uword cur_p
Pointer to current sample in buffer.
Definition: filter.h:25
arma::uword N
Nr of AR filter taps.
Definition: filter.h:481
arma_inline double sinc(double x)
A sinc, sin(x)/x, function.
Definition: base.h:21
double angle(const std::complex< T > &x)
Calculates angle in radians for complex input.
Definition: base.h:67
arma::Mat< T1 > P
Adaptive filter Inverse corr matrix (estimated accuracy)
Definition: filter.h:33
void rls_adapt(const T3 _err)
RLS Filter update function.
Definition: filter.h:351
arma_inline arma::vec blackmanharris(const arma::uword N)
Blackman-Harris window. Symmetric BH4 window.
Definition: window.h:89
#define err_handler(msg)
Definition: base.h:213
arma_inline arma::vec fd_filter(const arma::uword M, double fd)
Fractional delay function. Fractional delay filter design using windowed sinc method. Actual delay is M/2+fd samples for even nr of taps and (M-1)/2+fd for odd nr of taps Best performance if -1 < fd < 1.
Definition: filter.h:716
FIR_filt()
Constructor.
Definition: filter.h:44
arma_inline arma::vec phasez(const arma::vec b, const arma::vec a, const arma::uword K=512)
Frequency phase response function. Calculates the frequency phase response.
Definition: filter.h:779
arma::Col< T2 > get_coeffs()
Get coefficients from FIR filter.
Definition: filter.h:89
arma_inline arma::vec fir1_hp(const arma::uword M, const double f0)
FIR highpass design function. FIR design using windows method (hamming window). NB! Returns size M+1...
Definition: filter.h:626
arma_inline arma::vec fir1_bs(const arma::uword M, const double f0, const double f1)
FIR bandstop design function. FIR design using windows method (hamming window). NB! Returns size M+1...
Definition: filter.h:688
Definition: base.h:7
void adapt_disble(void)
Stop adapt.
Definition: filter.h:466
void setup_newt(const arma::uword _N, const double _mu, const T2 _c, const arma::uword _L=1)
LMS-Newton Filter function setup. (Affine Projection Algorithm)
Definition: filter.h:260
arma_inline arma::vec hamming(const arma::uword N)
Hamming window.
Definition: window.h:44
arma::uword blk_ctr
Adaptive filter block length counter.
Definition: filter.h:31
arma::uword a_cur_p
Pointer to current sample in AR buffer.
Definition: filter.h:483
void clear(void)
Clears the internal states and pointer.
Definition: filter.h:54
arma_inline arma::vec fir1_bp(const arma::uword M, const double f0, const double f1)
FIR bandpass design function. FIR design using windows method (hamming window). NB! Returns size M+1...
Definition: filter.h:655
arma::Mat< T1 > get_P(void)
Get P.
Definition: filter.h:444
arma::uword b_cur_p
Pointer to current sample in MA buffer.
Definition: filter.h:482
T2 c
Adaptive filter NLMS regulation const.
Definition: filter.h:32
arma::Mat< T1 > Q
Adaptive filter Process noise.
Definition: filter.h:34
void setup_lms(const arma::uword _N, const double _mu, const arma::uword _L=1)
LMS Filter function setup.
Definition: filter.h:147
arma::Mat< T1 > K
Adaptive filter gain vector.
Definition: filter.h:36
T3 operator()(const T1 &in)
Filter operator.
Definition: filter.h:103
void setup_rls(const arma::uword _N, const double _lmd, const double _P0)
RLS Filter function setup.
Definition: filter.h:324
arma::Mat< T1 > get_K(void)
Get K.
Definition: filter.h:450
void set_step_size(const double _mu)
Set step size.
Definition: filter.h:456
FIR/MA filter class.
Definition: filter.h:20
arma::Mat< T1 > buf
Signal buffer.
Definition: filter.h:26
arma::Col< T2 > b
MA Filter coefficients.
Definition: filter.h:484
arma::Col< T2 > a
AR Filter coefficients.
Definition: filter.h:485
arma::Mat< T2 > b
Filter coefficients.
Definition: filter.h:27
arma::Col< T1 > b_buf
MA Signal buffer.
Definition: filter.h:486
arma_inline arma::cx_vec freq(const arma::vec b, const arma::vec a, const arma::uword K=512)
Frequency response function. Calculates the frequency response.
Definition: filter.h:738
void clear(void)
Clears the internal states and pointers.
Definition: filter.h:502
void nlms_adapt(const T3 _err)
NLMS Filter update function.
Definition: filter.h:229
arma_inline arma::vec freqz(const arma::vec b, const arma::vec a, const arma::uword K=512)
Frequency magnitude response function. Calculates the frequency magnitude response.
Definition: filter.h:765
arma::uword L
Adaptive filter block length.
Definition: filter.h:30
~IIR_filt()
Destructor.
Definition: filter.h:497
double get_step_size(void)
Get step size.
Definition: filter.h:438
arma::Col< T3 > filter(const arma::Col< T1 > &in)
Definition: filter.h:135
arma_inline arma::vec fir1(const arma::uword M, const double f0)
FIR lowpass design function. FIR lowpassdesign using windows method (hamming window). NB! Returns size M+1.
Definition: filter.h:607
T3 operator()(const T1 &in)
Filter operator.
Definition: filter.h:544
const double PI
... or use arma::datum::pi
Definition: base.h:14
~FIR_filt()
Destructor.
Definition: filter.h:49
arma::uword M
Nr of MA filter taps.
Definition: filter.h:480
void adapt_enable(void)
Start adapt.
Definition: filter.h:461
arma::uword M
Nr of filter taps.
Definition: filter.h:24
void update_coeffs(const arma::Col< T2 > &_b, const arma::Col< T2 > &_a)
Updates coefficients in filter without clearing the internal states.
Definition: filter.h:533
void newt_adapt(const T3 _err)
LMS-Newton Filter update function.
Definition: filter.h:290
void setup_nlms(const arma::uword _N, const double _mu, const T2 _c, const arma::uword _L=1)
NLMS Filter function setup.
Definition: filter.h:202
void set_coeffs(const arma::Mat< T2 > &_b)
Sets coefficients in FIR filter. The internal state and pointers are cleared.
Definition: filter.h:65
void set_coeffs(const arma::Col< T2 > &_b, const arma::Col< T2 > &_a)
Sets coefficients in IIR filter. The internal state and pointers are cleared.
Definition: filter.h:516
arma::Col< T1 > a_buf
AR Signal buffer.
Definition: filter.h:487
arma::Mat< T1 > X_tpz
Adaptive filter Toeplitz for Corr matrix calc.
Definition: filter.h:38
void kalman_adapt(const T3 _err)
Kalman Filter update function.
Definition: filter.h:409
void setup_kalman(const arma::uword _N, const double _P0, const double _Q0, const double _R0)
Kalman Filter function setup.
Definition: filter.h:379
arma::Col< T3 > filter(const arma::Col< T1 > &in)
Filter function.
Definition: filter.h:585
arma::uword do_adapt
Adaptive filter enable flag.
Definition: filter.h:39
IIR_filt()
Constructor.
Definition: filter.h:492