v2.0.0
Loading...
Searching...
No Matches
filterdesignview.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "filterdesignview.h"
42#include "ui_filterdesignview.h"
43
45
46#include "utils/mnemath.h"
47
49
50#include <fiff/fiff_info.h>
51
52//=============================================================================================================
53// QT INCLUDES
54//=============================================================================================================
55
56#include <QDate>
57#include <QFileDialog>
58#include <QStandardPaths>
59#include <QSvgGenerator>
60#include <QCheckBox>
61#include <QSettings>
62#include <QApplication>
63#include <QKeyEvent>
64#include <QScreen>
65
66//=============================================================================================================
67// EIGEN INCLUDES
68//=============================================================================================================
69
70//=============================================================================================================
71// USED NAMESPACES
72//=============================================================================================================
73
74using namespace DISPLIB;
75using namespace FIFFLIB;
76using namespace UTILSLIB;
77using namespace RTPROCESSINGLIB;
78
79//=============================================================================================================
80// DEFINE MEMBER METHODS
81//=============================================================================================================
82
83FilterDesignView::FilterDesignView(const QString& sSettingsPath,
84 QWidget *parent,
85 Qt::WindowFlags f)
86: AbstractView(parent, f)
87, m_pUi(new Ui::FilterDesignViewWidget)
88, m_iFilterTaps(512)
89, m_dSFreq(600)
90{
91 m_sSettingsPath = sSettingsPath;
92 m_pUi->setupUi(this);
93
98
101}
102
103//=============================================================================================================
104
106{
107 saveSettings();
108
109 delete m_pUi;
110}
111
112//=============================================================================================================
113
114void FilterDesignView::setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
115{
116 if(iMaxNumberFilterTaps%2 != 0) {
117 iMaxNumberFilterTaps--;
118 }
119
120 m_pUi->m_spinBox_filterTaps->setMaximum(iMaxNumberFilterTaps);
121 m_pUi->m_spinBox_filterTaps->setMinimum(16);
122
123 //Update filter depending on new window size
125}
126
127//=============================================================================================================
128
130{
131 return m_pUi->m_spinBox_filterTaps->value();
132}
133
134//=============================================================================================================
135
136void FilterDesignView::setSamplingRate(double dSamplingRate)
137{
138 if(dSamplingRate <= 0) {
139 qWarning() << "[FilterDesignView::setSamplingRate] Sampling frequency is <= 0. Returning.";
140 }
141
142 m_dSFreq = dSamplingRate;
143
144 //Update min max of spin boxes to nyquist
145 double nyquistFrequency = m_dSFreq/2;
146
147 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
148 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
149
150 if(m_pUi->m_doubleSpinBox_to->value()>m_dSFreq/2) {
151 m_pUi->m_doubleSpinBox_to->setValue(m_dSFreq/2);
152 }
153
154 if(m_pUi->m_doubleSpinBox_from->value()>m_dSFreq/2) {
155 m_pUi->m_doubleSpinBox_from->setValue(m_dSFreq/2);
156 }
157
159
161}
162
163//=============================================================================================================
164
166{
167 m_pUi->m_doubleSpinBox_from->setValue(dFrom);
169}
170
171//=============================================================================================================
172
174{
175 m_pUi->m_doubleSpinBox_to->setValue(dTo);
177}
178
179//=============================================================================================================
180
185
186//=============================================================================================================
187
189{
190 return m_pUi->m_comboBox_filterApplyTo->currentText();
191}
192
193//=============================================================================================================
194
195void FilterDesignView::setChannelType(const QString& sType)
196{
197 m_pUi->m_comboBox_filterApplyTo->setCurrentText(sType);
198 saveSettings();
199}
200
201//=============================================================================================================
202
204{
205 if(m_sSettingsPath.isEmpty()) {
206 return;
207 }
208
209 QSettings settings("MNECPP");
210
211 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), m_filterKernel.getHighpassFreq());
212 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTo"), m_filterKernel.getLowpassFreq());
213 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), m_filterKernel.getFilterOrder());
214 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(m_filterKernel.getDesignMethod()));
215 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), m_filterKernel.getParksWidth()*(m_filterKernel.getSamplingFrequency()/2));
216 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), getChannelType());
217 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/Position"), this->pos());
218}
219
220//=============================================================================================================
221
223{
224 if(m_sSettingsPath.isEmpty()) {
225 return;
226 }
227
228 QSettings settings("MNECPP");
229
230 //Set stored filter settings from last session
231 m_pUi->m_doubleSpinBox_to->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTo"), 40.0).toDouble());
232 m_pUi->m_doubleSpinBox_from->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), 1.0).toDouble());
233 m_pUi->m_spinBox_filterTaps->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), 128).toInt());
234 m_pUi->m_comboBox_designMethod->setCurrentIndex(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(FilterParameter("Cosine"))).toInt());
235 m_pUi->m_doubleSpinBox_transitionband->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), 0.1).toDouble());
236 m_pUi->m_comboBox_filterApplyTo->setCurrentText(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), "All").toString());
237
238 QPoint pos = settings.value(m_sSettingsPath + QString("/FilterDesignView/Position"), QPoint(100,100)).toPoint();
239
240 QRect screenRect = QApplication::primaryScreen()->geometry();
241 if(!screenRect.contains(pos) && QGuiApplication::screens().size() == 1) {
242 move(QPoint(100,100));
243 } else {
244 move(pos);
245 }
246}
247
248//=============================================================================================================
249
251{
252 switch(mode) {
254 break;
255 default: // default is research mode
256 break;
257 }
258}
259
260//=============================================================================================================
261
263{
264 switch(mode) {
266 break;
267 default: // default is realtime mode
268 break;
269 }
270}
271
272//=============================================================================================================
273
275{
276 connect(m_pUi->m_doubleSpinBox_from, &QDoubleSpinBox::editingFinished,
278
279 connect(m_pUi->m_doubleSpinBox_to, &QDoubleSpinBox::editingFinished,
281
282 connect(m_pUi->m_doubleSpinBox_transitionband, &QDoubleSpinBox::editingFinished,
284
285 connect(m_pUi->m_spinBox_filterTaps, &QSpinBox::editingFinished,
287
288 //Intercept events from the spin boxes to get control over key events
289 m_pUi->m_doubleSpinBox_from->installEventFilter(this);
290 m_pUi->m_doubleSpinBox_to->installEventFilter(this);
291 m_pUi->m_doubleSpinBox_transitionband->installEventFilter(this);
292}
293
294//=============================================================================================================
295
297{
298 connect(m_pUi->m_pushButton_exportPlot,&QPushButton::released,
300
301 connect(m_pUi->m_pushButton_exportFilter,&QPushButton::released,
303
304 connect(m_pUi->m_pushButton_loadFilter,&QPushButton::released,
306}
307
308//=============================================================================================================
309
311{
313 m_pUi->m_comboBox_designMethod->addItem(filterMethod.getName());
314 }
315
316 connect(m_pUi->m_comboBox_designMethod,static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
318
319 //Initial selection is a bandpass and Cosine design method
320 m_pUi->m_doubleSpinBox_from->setVisible(true);
321 m_pUi->m_label_lowpass->setVisible(true);
322
323 m_pUi->m_doubleSpinBox_to->setVisible(true);
324 m_pUi->m_label_highpass->setVisible(true);
325
326 m_pUi->m_spinBox_filterTaps->setVisible(true);
327 m_pUi->m_label_filterTaps->setVisible(true);
328
329 connect(m_pUi->m_comboBox_filterApplyTo, &QComboBox::currentTextChanged,
331}
332
333//=============================================================================================================
334
336{
337 m_pFilterPlotScene = new FilterPlotScene(m_pUi->m_graphicsView_filterPlot, this);
338
339 m_pUi->m_graphicsView_filterPlot->setScene(m_pFilterPlotScene);
340 m_pUi->m_graphicsView_filterPlot->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
341 m_pUi->m_graphicsView_filterPlot->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
342
344}
345
346//=============================================================================================================
347
348void FilterDesignView::resizeEvent(QResizeEvent* event)
349{
350 Q_UNUSED(event);
351 m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
352}
353
354//=============================================================================================================
355
356void FilterDesignView::keyPressEvent(QKeyEvent * event)
357{
358 if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
359 emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
360 }
361
362 if((event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_Z) || event->key() == Qt::Key_Delete) {
363 emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
364 }
365}
366
367//=============================================================================================================
368
370{
371 //Update the filter of the scene
373 m_filterKernel.getSamplingFrequency(), //Pass the filters sampling frequency, not the one from the fiff info. Reason: sFreq from a loaded filter could be different
374 m_pUi->m_doubleSpinBox_from->value(),
375 m_pUi->m_doubleSpinBox_to->value());
376
377 m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
378}
379
380//=============================================================================================================
381
383{
384 Q_UNUSED(currentIndex);
385
386 //Change visibility of filter tap spin boxes depending on filter design method
387 switch(m_pUi->m_comboBox_designMethod->currentIndex()) {
388 case 0: //Cosine
389// m_pUi->m_spinBox_filterTaps->setVisible(false);
390// m_pUi->m_label_filterTaps->setVisible(false);
391 m_pUi->m_spinBox_filterTaps->setVisible(true);
392 m_pUi->m_label_filterTaps->setVisible(true);
393 break;
394
395 case 1: //Tschebyscheff
396 m_pUi->m_spinBox_filterTaps->setVisible(true);
397 m_pUi->m_label_filterTaps->setVisible(true);
398 break;
399 }
400
402}
403
404//=============================================================================================================
405
407{
408 emit updateFilterFrom(m_pUi->m_doubleSpinBox_from->value());
409 emit updateFilterTo(m_pUi->m_doubleSpinBox_to->value());
410
411 //User defined filter parameters
412 double from = m_pUi->m_doubleSpinBox_from->value();
413 double to = m_pUi->m_doubleSpinBox_to->value();
414
415 double trans_width = m_pUi->m_doubleSpinBox_transitionband->value();
416
417 double bw = to-from;
418 double center = from+bw/2;
419
420 double nyquistFrequency = m_dSFreq/2;
421
422 //Calculate the needed fft length
423 m_iFilterTaps = m_pUi->m_spinBox_filterTaps->value();
424 if(m_pUi->m_spinBox_filterTaps->value()%2 != 0) {
426 }
427
428 //set maximum and minimum for cut off frequency spin boxes
429 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
430 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
431 m_pUi->m_doubleSpinBox_to->setMinimum(0);
432 m_pUi->m_doubleSpinBox_from->setMinimum(0);
433
434 if((m_pUi->m_doubleSpinBox_to->value() < m_pUi->m_doubleSpinBox_from->value())) {
435 m_pUi->m_doubleSpinBox_to->setValue(m_pUi->m_doubleSpinBox_from->value() + 1);
436 }
437
438 m_pUi->m_doubleSpinBox_to->setMinimum(m_pUi->m_doubleSpinBox_from->value());
439 m_pUi->m_doubleSpinBox_from->setMaximum(m_pUi->m_doubleSpinBox_to->value());
440
441 int iMethod = FilterKernel::m_designMethods.indexOf(FilterParameter(m_pUi->m_comboBox_designMethod->currentText()));
442
443 //Generate filters
444 m_filterKernel = FilterKernel("Designed Filter",
447 (double)center/nyquistFrequency,
448 (double)bw/nyquistFrequency,
449 (double)trans_width/nyquistFrequency,
450 m_dSFreq,
451 iMethod);
452
454
455 //update filter plot
457
458 saveSettings();
459}
460
461//=============================================================================================================
462
463void FilterDesignView::onSpinBoxFilterChannelType(const QString& channelType)
464{
465 emit filterChannelTypeChanged(channelType);
466}
467
468//=============================================================================================================
469
471{
472 // Open file dialog
473 QDate date;
474 QString fileName = QFileDialog::getSaveFileName(this,
475 "Save filter plot",
476 QString("%1/%2_%3_%4_FilterPlot").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
477 tr("Vector graphic(*.svg);;Images (*.png)"));
478
479 if(!fileName.isEmpty()) {
480 // Generate screenshot
481 if(fileName.contains(".svg")) {
482 QSvgGenerator svgGen;
483
484 svgGen.setFileName(fileName);
485 QRectF rect = m_pFilterPlotScene->itemsBoundingRect();
486 svgGen.setSize(QSize(rect.width(), rect.height()));
487 //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
488
489 QPainter painter(&svgGen);
490 m_pFilterPlotScene->render(&painter);
491 }
492
493 if(fileName.contains(".png")) {
494 m_pFilterPlotScene->setSceneRect(m_pFilterPlotScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
495 QImage image(m_pFilterPlotScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
496 image.fill(Qt::transparent); // Start all pixels transparent
497
498 QPainter painter(&image);
499 m_pFilterPlotScene->render(&painter);
500 image.save(fileName);
501 }
502 }
503}
504
505//=============================================================================================================
506
508{
509 //Generate appropriate name for the filter to be saved
510 QString filtername;
511
512 filtername = QString("%1_%2_%3_Fs%4").arg(m_filterKernel.getFilterType().getName()).arg((int)m_filterKernel.getHighpassFreq()).arg((int)m_filterKernel.getLowpassFreq()).arg((int)m_filterKernel.getSamplingFrequency());
513
514 //Do not pass m_filterKernel because this is most likely the User Defined filter which name should not change due to the filter model implementation. Hence use temporal copy of m_filterKernel.
515 FilterKernel filterWriteTemp = m_filterKernel;
516 filterWriteTemp.getName() = filtername;
517
518 QString fileName = QFileDialog::getSaveFileName(this,
519 "Save filter coefficients",
520 QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(filtername),
521 tr("Text file(*.txt)"));
522
523 FilterIO::writeFilter(fileName, filterWriteTemp);
524}
525
526//=============================================================================================================
527
529{
530 QString path = QFileDialog::getOpenFileName(this,
531 QString("Load filter"),
532 QString("./"),
533 tr("txt files (*.txt)"));
534
535 if(!path.isEmpty()) {
536 //Replace old with new filter operator
537 FilterKernel filterLoadTemp;
538
539 if(!FilterIO::readFilter(path, filterLoadTemp)) {
540 return;
541 }
542 updateGuiFromFilter(filterLoadTemp);
543
545
547
548 } else {
549 qDebug()<<"Could not load filter.";
550 }
551}
552
553//=============================================================================================================
554
556{
557
558}
559
560//=============================================================================================================
561
563{
564 return m_pUi->m_doubleSpinBox_from->value();
565}
566
567//=============================================================================================================
568
570{
571 return m_pUi->m_doubleSpinBox_to->value();
572}
573
574//=============================================================================================================
575
577{
578 m_pUi->m_doubleSpinBox_from->setValue(filter.getHighpassFreq());
579 m_pUi->m_doubleSpinBox_to->setValue(filter.getLowpassFreq());
580 m_pUi->m_spinBox_filterTaps->setValue(filter.getFilterOrder());
581 m_pUi->m_doubleSpinBox_transitionband->setValue(filter.getParksWidth()*(filter.getSamplingFrequency()/2));
582
583 m_pUi->m_comboBox_designMethod->setCurrentIndex(FilterKernel::m_designMethods.indexOf(filter.getDesignMethod()));
584}
585
586//=============================================================================================================
587
FilterIO class declaration.
Contains the declaration of the FilterDesignView class.
Contains the declaration of the FilterPlotScene class.
FiffInfo class declaration.
MNEMath class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
2-D display widgets and visualisation helpers (charts, topography, colour maps).
Real-time signal processing (filtering, averaging, HPI fitting, noise reduction).
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Definition buildinfo.h:45
AbstractView(QWidget *parent=0, Qt::WindowFlags f=Qt::Widget)
void setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
void changeStateSpinBoxes(int currentIndex)
void filterChanged(const RTPROCESSINGLIB::FilterKernel &activeFilter)
void updateFilterFrom(double dFrom)
void updateGuiMode(GuiMode mode)
void setSamplingRate(double dSamplingRate)
void updateFilterTo(double dTo)
QPointer< FilterPlotScene > m_pFilterPlotScene
void updateGuiFromFilter(const RTPROCESSINGLIB::FilterKernel &filter)
void resizeEvent(QResizeEvent *event)
virtual void keyPressEvent(QKeyEvent *event)
void guiStyleChanged(DISPLIB::AbstractView::StyleMode style)
RTPROCESSINGLIB::FilterKernel getCurrentFilter()
void setChannelType(const QString &sType)
Ui::FilterDesignViewWidget * m_pUi
void updateProcessingMode(ProcessingMode mode)
void onSpinBoxFilterChannelType(const QString &channelType)
void filterChannelTypeChanged(const QString &channelType)
RTPROCESSINGLIB::FilterKernel m_filterKernel
FilterDesignView(const QString &sSettingsPath, QWidget *parent=0, Qt::WindowFlags f=Qt::Widget)
The FilterPlotScene class provides the scene where a filter response can be plotted.
static bool readFilter(QString path, FilterKernel &filter)
Definition filterio.cpp:68
static bool writeFilter(const QString &path, const FilterKernel &filter)
Definition filterio.cpp:153
Named filter-design parameter descriptor holding a human-readable name and description (e....
The FilterKernel class provides methods to create/design a FIR filter kernel.
FilterParameter getDesignMethod() const
static QVector< FilterParameter > m_designMethods
static QVector< FilterParameter > m_filterTypes