MNE-CPP  0.1.9
A Framework for Electrophysiology
filterkernel.cpp
Go to the documentation of this file.
1 //=============================================================================================================
37 //=============================================================================================================
38 // INCLUDES
39 //=============================================================================================================
40 
41 #include "filterkernel.h"
42 
43 #include <utils/mnemath.h>
44 
45 #include "parksmcclellan.h"
46 #include "cosinefilter.h"
47 
48 #include <iostream>
49 
50 //=============================================================================================================
51 // QT INCLUDES
52 //=============================================================================================================
53 
54 #include <QDebug>
55 
56 //=============================================================================================================
57 // EIGEN INCLUDES
58 //=============================================================================================================
59 
60 #include <Eigen/SparseCore>
61 //#ifndef EIGEN_FFTW_DEFAULT
62 //#define EIGEN_FFTW_DEFAULT
63 //#endif
64 #include <unsupported/Eigen/FFT>
65 
66 //=============================================================================================================
67 // USED NAMESPACES
68 //=============================================================================================================
69 
70 using namespace RTPROCESSINGLIB;
71 using namespace Eigen;
72 using namespace UTILSLIB;
73 
74 //=============================================================================================================
75 // INIT STATIC MEMBERS
76 //=============================================================================================================
77 
78 QVector<RTPROCESSINGLIB::FilterParameter> FilterKernel::m_designMethods ({
79  FilterParameter(QString("Cosine"), QString("A cosine filter")),
80  FilterParameter(QString("Tschebyscheff"), QString("A tschebyscheff filter"))
81 // FilterParameter(QString("External"), QString("An external filter"))
82 });
83 QVector<RTPROCESSINGLIB::FilterParameter> FilterKernel::m_filterTypes ({
84  FilterParameter(QString("LPF"), QString("An LPF filter")),
85  FilterParameter(QString("HPF"), QString("An HPF filter")),
86  FilterParameter(QString("BPF"), QString("A BPF filter")),
87  FilterParameter(QString("NOTCH"), QString("A NOTCH filter")),
88  FilterParameter(QString("UNKNOWN"), QString("An UNKNOWN filter"))
89 });
90 
91 //=============================================================================================================
92 // DEFINE MEMBER METHODS
93 //=============================================================================================================
94 
96 : m_sFreq(1000)
97 , m_dCenterFreq(0.5)
98 , m_dBandwidth(0.1)
99 , m_dParksWidth(0.1)
100 , m_dLowpassFreq(40)
101 , m_dHighpassFreq(4)
102 , m_iFilterOrder(80)
103 , m_iDesignMethod(m_designMethods.indexOf(FilterParameter("Cosine")))
104 , m_iFilterType(m_filterTypes.indexOf(FilterParameter("BPF")))
105 , m_sFilterName("Unknown")
106 , m_sFilterShortDescription("")
107 {
108  designFilter();
109 }
110 
111 //=============================================================================================================
112 
113 FilterKernel::FilterKernel(const QString& sFilterName,
114  int iFilterType,
115  int iOrder,
116  double dCenterfreq,
117  double dBandwidth,
118  double dParkswidth,
119  double dSFreq,
120  int iDesignMethod)
121 : m_sFreq(dSFreq)
122 , m_dCenterFreq(dCenterfreq)
123 , m_dBandwidth(dBandwidth)
124 , m_dParksWidth(dParkswidth)
125 , m_iFilterOrder(iOrder)
126 , m_iDesignMethod(iDesignMethod)
127 , m_iFilterType(iFilterType)
128 , m_sFilterName(sFilterName)
129 , m_sFilterShortDescription()
130 {
131  if(iOrder < 9) {
132  qWarning() << "[FilterKernel::FilterKernel] Less than 9 taps were provided. Setting number of taps to 9.";
133  }
134 
135  designFilter();
136 }
137 
138 //=============================================================================================================
139 
140 void FilterKernel::prepareFilter(int iDataSize)
141 {
142  int iFftLength, exp;
143 
144  iFftLength = iDataSize + m_vecCoeff.cols();
145  exp = ceil(MNEMath::log2(iFftLength));
146  iFftLength = pow(2, exp);
147 
148  // Transform coefficients anew if needed
149  if(m_vecCoeff.cols() != (iFftLength/2+1)) {
150  fftTransformCoeffs(iFftLength);
151  }
152 }
153 
154 //=============================================================================================================
155 
156 RowVectorXd FilterKernel::applyConvFilter(const RowVectorXd& vecData,
157  bool bKeepOverhead) const
158 {
159  //Do zero padding or mirroring depending on user input
160  RowVectorXd vecDataZeroPad = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
161  RowVectorXd vecFilteredTime = RowVectorXd::Zero(2*m_vecCoeff.cols() + vecData.cols());
162 
163  vecDataZeroPad.segment(m_vecCoeff.cols(), vecData.cols()) = vecData;
164 
165  //Do the convolution
166  for(int i = m_vecCoeff.cols(); i < vecFilteredTime.cols(); i++) {
167  vecFilteredTime(i-m_vecCoeff.cols()) = vecDataZeroPad.segment(i-m_vecCoeff.cols(),m_vecCoeff.cols()) * m_vecCoeff.transpose();
168  }
169 
170  //Return filtered data
171  if(!bKeepOverhead) {
172  return vecFilteredTime.segment(m_vecCoeff.cols()/2, vecData.cols());
173  }
174 
175  return vecFilteredTime.head(vecData.cols()+m_vecCoeff.cols());
176 }
177 
178 //=============================================================================================================
179 
180 void FilterKernel::applyFftFilter(RowVectorXd& vecData,
181  bool bKeepOverhead)
182 {
183  #ifdef EIGEN_FFTW_DEFAULT
184  fftw_make_planner_thread_safe();
185  #endif
186 
187  // Make sure we always have the correct FFT length for the given input data and filter overlap
188  int iFftLength = vecData.cols() + m_vecCoeff.cols();
189  int exp = ceil(MNEMath::log2(iFftLength));
190  iFftLength = pow(2, exp);
191 
192  // Transform coefficients anew if needed
193  if(m_vecFftCoeff.cols() != (iFftLength/2+1)) {
194  fftTransformCoeffs(iFftLength);
195  }
196 
197  //generate fft object
198  Eigen::FFT<double> fft;
199  fft.SetFlag(fft.HalfSpectrum);
200 
201  // Zero padd if necessary. Please note: The zero padding in Eigen's FFT is only working for column vectors -> We have to zero pad manually here
202  int iOriginalSize = vecData.cols();
203  if (vecData.cols() < iFftLength) {
204  int iResidual = iFftLength - vecData.cols();
205  vecData.conservativeResize(iFftLength);
206  vecData.tail(iResidual).setZero();
207  }
208 
209  //fft-transform data sequence
210  RowVectorXcd vecFreqData;
211  fft.fwd(vecFreqData, vecData, iFftLength);
212 
213  //perform frequency-domain filtering
214  vecFreqData = m_vecFftCoeff.array() * vecFreqData.array();
215 
216  //inverse-FFT
217  fft.inv(vecData, vecFreqData);
218 
219  //Return filtered data
220  if(!bKeepOverhead) {
221  vecData = vecData.segment(m_vecCoeff.cols()/2, iOriginalSize).eval();
222  } else {
223  vecData = vecData.head(iOriginalSize + m_vecCoeff.cols()).eval();
224  }
225 }
226 
227 //=============================================================================================================
228 
229 QString FilterKernel::getName() const
230 {
231  return m_sFilterName;
232 }
233 
234 //=============================================================================================================
235 
236 void FilterKernel::setName(const QString& sFilterName)
237 {
238  m_sFilterName = sFilterName;
239 }
240 
241 //=============================================================================================================
242 
243 double FilterKernel::getSamplingFrequency() const
244 {
245  return m_sFreq;
246 }
247 
248 //=============================================================================================================
249 
250 void FilterKernel::setSamplingFrequency(double dSFreq)
251 {
252  m_sFreq = dSFreq;
253 }
254 
255 //=============================================================================================================
256 
257 int FilterKernel::getFilterOrder() const
258 {
259  return m_iFilterOrder;
260 }
261 
262 //=============================================================================================================
263 
264 void FilterKernel::setFilterOrder(int iOrder)
265 {
266  m_iFilterOrder = iOrder;
267 }
268 
269 //=============================================================================================================
270 
271 double FilterKernel::getCenterFrequency() const
272 {
273  return m_dCenterFreq;
274 }
275 
276 //=============================================================================================================
277 
278 void FilterKernel::setCenterFrequency(double dCenterFreq)
279 {
280  m_dCenterFreq = dCenterFreq;
281 }
282 
283 //=============================================================================================================
284 
285 double FilterKernel::getBandwidth() const
286 {
287  return m_dBandwidth;
288 }
289 
290 //=============================================================================================================
291 
292 void FilterKernel::setBandwidth(double dBandwidth)
293 {
294  m_dBandwidth = dBandwidth;
295 }
296 
297 //=============================================================================================================
298 
299 double FilterKernel::getParksWidth() const
300 {
301  return m_dParksWidth;
302 }
303 
304 //=============================================================================================================
305 
306 void FilterKernel::setParksWidth(double dParksWidth)
307 {
308  m_dParksWidth = dParksWidth;
309 }
310 
311 //=============================================================================================================
312 
313 double FilterKernel::getHighpassFreq() const
314 {
315  return m_dHighpassFreq;
316 }
317 
318 //=============================================================================================================
319 
320 void FilterKernel::setHighpassFreq(double dHighpassFreq)
321 {
322  m_dHighpassFreq = dHighpassFreq;
323 }
324 
325 //=============================================================================================================
326 
327 double FilterKernel::getLowpassFreq() const
328 {
329  return m_dLowpassFreq;
330 }
331 
332 //=============================================================================================================
333 
334 void FilterKernel::setLowpassFreq(double dLowpassFreq)
335 {
336  m_dLowpassFreq = dLowpassFreq;
337 }
338 
339 //=============================================================================================================
340 
341 Eigen::RowVectorXd FilterKernel::getCoefficients() const
342 {
343  return m_vecCoeff;
344 }
345 
346 //=============================================================================================================
347 
348 void FilterKernel::setCoefficients(const Eigen::RowVectorXd& vecCoeff)
349 {
350  m_vecCoeff = vecCoeff;
351 }
352 
353 //=============================================================================================================
354 
355 Eigen::RowVectorXcd FilterKernel::getFftCoefficients() const
356 {
357  return m_vecFftCoeff;
358 }
359 
360 //=============================================================================================================
361 
362 void FilterKernel::setFftCoefficients(const Eigen::RowVectorXcd& vecFftCoeff)
363 {
364  m_vecFftCoeff = vecFftCoeff;
365 }
366 
367 //=============================================================================================================
368 
369 bool FilterKernel::fftTransformCoeffs(int iFftLength)
370 {
371  #ifdef EIGEN_FFTW_DEFAULT
372  fftw_make_planner_thread_safe();
373  #endif
374 
375  if(m_vecCoeff.cols() > iFftLength) {
376  std::cout <<"[FilterKernel::fftTransformCoeffs] The number of filter taps is bigger than the FFT length."<< std::endl;
377  return false;
378  }
379 
380  //generate fft object
381  Eigen::FFT<double> fft;
382  fft.SetFlag(fft.HalfSpectrum);
383 
384  // Zero padd if necessary. Please note: The zero padding in Eigen's FFT is only working for column vectors -> We have to zero pad manually here
385  RowVectorXd vecInputFft;
386  if (m_vecCoeff.cols() < iFftLength) {
387  vecInputFft.setZero(iFftLength);
388  vecInputFft.block(0,0,1,m_vecCoeff.cols()) = m_vecCoeff;
389  } else {
390  vecInputFft = m_vecCoeff;
391  }
392 
393  //fft-transform filter coeffs
394  RowVectorXcd vecFreqData;
395  fft.fwd(vecFreqData, vecInputFft, iFftLength);
396  m_vecFftCoeff = vecFreqData;;
397 
398  return true;
399 }
400 
401 //=============================================================================================================
402 
403 void FilterKernel::designFilter()
404 {
405  // Make sure we only use a minimum needed FFT size
406  int iFftLength = m_iFilterOrder;
407  int exp = ceil(MNEMath::log2(iFftLength));
408  iFftLength = pow(2, exp);
409 
410  switch(m_iDesignMethod) {
411  case 1: {
412  ParksMcClellan filter(m_iFilterOrder,
413  m_dCenterFreq,
414  m_dBandwidth,
415  m_dParksWidth,
416  static_cast<ParksMcClellan::TPassType>(m_iFilterType));
417  m_vecCoeff = filter.FirCoeff;
418 
419  //fft-transform m_vecCoeff in order to be able to perform frequency-domain filtering
420  fftTransformCoeffs(iFftLength);
421 
422  break;
423  }
424 
425  case 0: {
426  CosineFilter filtercos;
427 
428  switch(m_iFilterType) {
429  case 0:
430  filtercos = CosineFilter(iFftLength,
431  (m_dCenterFreq)*(m_sFreq/2.),
432  m_dParksWidth*(m_sFreq/2),
433  (m_dCenterFreq)*(m_sFreq/2),
434  m_dParksWidth*(m_sFreq/2),
435  m_sFreq,
436  static_cast<CosineFilter::TPassType>(m_iFilterType));
437 
438  break;
439 
440  case 1:
441  filtercos = CosineFilter(iFftLength,
442  (m_dCenterFreq)*(m_sFreq/2),
443  m_dParksWidth*(m_sFreq/2),
444  (m_dCenterFreq)*(m_sFreq/2),
445  m_dParksWidth*(m_sFreq/2),
446  m_sFreq,
447  static_cast<CosineFilter::TPassType>(m_iFilterType));
448 
449  break;
450 
451  case 2:
452  filtercos = CosineFilter(iFftLength,
453  (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2),
454  m_dParksWidth*(m_sFreq/2),
455  (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2),
456  m_dParksWidth*(m_sFreq/2),
457  m_sFreq,
458  static_cast<CosineFilter::TPassType>(m_iFilterType));
459 
460  break;
461  }
462 
463  //This filter is designed in the frequency domain, hence the time domain impulse response need to be shortend by the users dependent number of taps
464  m_vecCoeff.resize(m_iFilterOrder);
465 
466  m_vecCoeff.head(m_iFilterOrder/2) = filtercos.m_vecCoeff.tail(m_iFilterOrder/2);
467  m_vecCoeff.tail(m_iFilterOrder/2) = filtercos.m_vecCoeff.head(m_iFilterOrder/2);
468 
469  //Now generate the fft version of the shortened impulse response
470  fftTransformCoeffs(iFftLength);
471 
472  break;
473  }
474  }
475 
476  switch(m_iFilterType) {
477  case 0:
478  m_dLowpassFreq = 0;
479  m_dHighpassFreq = m_dCenterFreq*(m_sFreq/2);
480  break;
481 
482  case 1:
483  m_dLowpassFreq = m_dCenterFreq*(m_sFreq/2);
484  m_dHighpassFreq = 0;
485  break;
486 
487  case 2:
488  m_dLowpassFreq = (m_dCenterFreq + m_dBandwidth/2)*(m_sFreq/2);
489  m_dHighpassFreq = (m_dCenterFreq - m_dBandwidth/2)*(m_sFreq/2);
490  break;
491  }
492  getShortDescription();
493 }
494 
495 //=============================================================================================================
496 
497 QString FilterKernel::getShortDescription() const
498 {
499  QString description(m_designMethods.at(m_iDesignMethod).getName() + " - " + \
500  QString::number(m_dHighpassFreq,'g',4) + "Hz to " + QString::number(m_dLowpassFreq,'g',4) + "Hz - " \
501  "Ord: " + QString::number(m_iFilterOrder));
502  return description;
503 }
504 
505 //=============================================================================================================
506 
507 RTPROCESSINGLIB::FilterParameter FilterKernel::getDesignMethod() const
508 {
509  if(m_iDesignMethod < 0){
510  return m_designMethods.at(0);
511  }
512  return m_designMethods.at(m_iDesignMethod);
513 }
514 
515 //=============================================================================================================
516 
517 RTPROCESSINGLIB::FilterParameter FilterKernel::getFilterType() const
518 {
519  if(m_iFilterType < 0){
520  return m_filterTypes.at(0);
521  }
522  return m_filterTypes.at(m_iFilterType);
523 }
524 
525 //=============================================================================================================
526 
527 void FilterKernel::setDesignMethod(int iDesignMethod)
528 {
529  if(iDesignMethod < 0){
530  m_iDesignMethod = 0;
531  } else {
532  m_iDesignMethod = iDesignMethod;
533  }
534 }
535 
536 //=============================================================================================================
537 
538 void FilterKernel::setFilterType(int iFilterType)
539 {
540  if(iFilterType < 0){
541  m_iFilterType = 0;
542  } else {
543  m_iFilterType = iFilterType;
544  }
545 }
546 
547 //=============================================================================================================
548 
550 :FilterParameter("Unknown", "")
551 {
552 }
553 
554 //=============================================================================================================
555 
557 :FilterParameter(sName,"")
558 {
559 }
560 
561 //=============================================================================================================
562 
564  QString sDescription)
565 : m_sName(sName)
566 , m_sDescription(sDescription)
567 {
568 
569 }
570 
571 //=============================================================================================================
572 
574 {
575  return m_sName;
576 }
577 
578 //=============================================================================================================
RTPROCESSINGLIB::CosineFilter::m_vecCoeff
Eigen::RowVectorXd m_vecCoeff
Definition: cosinefilter.h:101
cosinefilter.h
Declaration of the CosineFilter class.
RTPROCESSINGLIB::FilterKernel::FilterKernel
FilterKernel()
FilterKernel creates a default FilterKernel object.
Definition: filterkernel.cpp:95
RTPROCESSINGLIB::FilterParameter
The FilterParameter class.
Definition: filterkernel.h:83
RTPROCESSINGLIB::FilterParameter::FilterParameter
FilterParameter()
Definition: filterkernel.cpp:549
parksmcclellan.h
RTPROCESSINGLIB::FilterKernel::applyFftFilter
void applyFftFilter(Eigen::RowVectorXd &vecData, bool bKeepOverhead=false)
Definition: filterkernel.cpp:180
RTPROCESSINGLIB::CosineFilter
Creates a cosine filter response in the frequency domain.
Definition: cosinefilter.h:68
RTPROCESSINGLIB::FilterKernel::m_filterTypes
static QVector< FilterParameter > m_filterTypes
Definition: filterkernel.h:241
RTPROCESSINGLIB::ParksMcClellan
The ParksMcClellan class provides the ParksMcClellan filter desing algorithm.
Definition: parksmcclellan.h:95
RTPROCESSINGLIB::FilterParameter::m_sName
QString m_sName
Definition: filterkernel.h:122
RTPROCESSINGLIB::FilterKernel::prepareFilter
void prepareFilter(int iDataSize)
Definition: filterkernel.cpp:140
RTPROCESSINGLIB::FilterKernel::m_designMethods
static QVector< FilterParameter > m_designMethods
Definition: filterkernel.h:240
RTPROCESSINGLIB::FilterKernel::applyConvFilter
Eigen::RowVectorXd applyConvFilter(const Eigen::RowVectorXd &vecData, bool bKeepOverhead=false) const
Definition: filterkernel.cpp:156
mnemath.h
MNEMath class declaration.
filterkernel.h
The FilterKernel class represents a filter object that generates the FIR filter coefficients using Pa...
RTPROCESSINGLIB::FilterParameter::getName
QString getName() const
Definition: filterkernel.cpp:573