v2.0.0
Loading...
Searching...
No Matches
filterdesignview.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
21#include "filterdesignview.h"
22#include "ui_filterdesignview.h"
23
25
26#include <dsp/filterio.h>
27
28#include <fiff/fiff_info.h>
29
30//=============================================================================================================
31// QT INCLUDES
32//=============================================================================================================
33
34#include <QDate>
35#include <QFileDialog>
36#include <QStandardPaths>
37#include <QSvgGenerator>
38#include <QCheckBox>
39#include <QSettings>
40#include <QApplication>
41#include <QKeyEvent>
42#include <QScreen>
43
44//=============================================================================================================
45// EIGEN INCLUDES
46//=============================================================================================================
47
48//=============================================================================================================
49// USED NAMESPACES
50//=============================================================================================================
51
52using namespace DISPLIB;
53using namespace FIFFLIB;
54using namespace UTILSLIB;
55
56//=============================================================================================================
57// DEFINE MEMBER METHODS
58//=============================================================================================================
59
60FilterDesignView::FilterDesignView(const QString& sSettingsPath,
61 QWidget *parent,
62 Qt::WindowFlags f)
63: AbstractView(parent, f)
64, m_pUi(new Ui::FilterDesignViewWidget)
65, m_iFilterTaps(512)
66, m_dSFreq(600)
67{
68 m_sSettingsPath = sSettingsPath;
69 m_pUi->setupUi(this);
70
75
78}
79
80//=============================================================================================================
81
88
89//=============================================================================================================
90
91void FilterDesignView::setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
92{
93 if(iMaxNumberFilterTaps%2 != 0) {
94 iMaxNumberFilterTaps--;
95 }
96
97 m_pUi->m_spinBox_filterTaps->setMaximum(iMaxNumberFilterTaps);
98 m_pUi->m_spinBox_filterTaps->setMinimum(16);
99
100 //Update filter depending on new window size
102}
103
104//=============================================================================================================
105
107{
108 return m_pUi->m_spinBox_filterTaps->value();
109}
110
111//=============================================================================================================
112
113void FilterDesignView::setSamplingRate(double dSamplingRate)
114{
115 if(dSamplingRate <= 0) {
116 qWarning() << "[FilterDesignView::setSamplingRate] Sampling frequency is <= 0. Returning.";
117 }
118
119 m_dSFreq = dSamplingRate;
120
121 //Update min max of spin boxes to nyquist
122 double nyquistFrequency = m_dSFreq/2;
123
124 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
125 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
126
127 if(m_pUi->m_doubleSpinBox_to->value()>m_dSFreq/2) {
128 m_pUi->m_doubleSpinBox_to->setValue(m_dSFreq/2);
129 }
130
131 if(m_pUi->m_doubleSpinBox_from->value()>m_dSFreq/2) {
132 m_pUi->m_doubleSpinBox_from->setValue(m_dSFreq/2);
133 }
134
136
138}
139
140//=============================================================================================================
141
143{
144 m_pUi->m_doubleSpinBox_from->setValue(dFrom);
146}
147
148//=============================================================================================================
149
151{
152 m_pUi->m_doubleSpinBox_to->setValue(dTo);
154}
155
156//=============================================================================================================
157
162
163//=============================================================================================================
164
166{
167 return m_pUi->m_comboBox_filterApplyTo->currentText();
168}
169
170//=============================================================================================================
171
172void FilterDesignView::setChannelType(const QString& sType)
173{
174 m_pUi->m_comboBox_filterApplyTo->setCurrentText(sType);
175 saveSettings();
176}
177
178//=============================================================================================================
179
181{
182 if(m_sSettingsPath.isEmpty()) {
183 return;
184 }
185
186 QSettings settings("MNECPP");
187
188 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), m_filterKernel.getHighpassFreq());
189 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTo"), m_filterKernel.getLowpassFreq());
190 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), m_filterKernel.getFilterOrder());
191 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(m_filterKernel.getDesignMethod()));
192 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), m_filterKernel.getParksWidth()*(m_filterKernel.getSamplingFrequency()/2));
193 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), getChannelType());
194 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/Position"), this->pos());
195}
196
197//=============================================================================================================
198
200{
201 if(m_sSettingsPath.isEmpty()) {
202 return;
203 }
204
205 QSettings settings("MNECPP");
206
207 //Set stored filter settings from last session
208 m_pUi->m_doubleSpinBox_to->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTo"), 40.0).toDouble());
209 m_pUi->m_doubleSpinBox_from->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), 1.0).toDouble());
210 m_pUi->m_spinBox_filterTaps->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), 128).toInt());
211 m_pUi->m_comboBox_designMethod->setCurrentIndex(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(FilterParameter("Cosine"))).toInt());
212 m_pUi->m_doubleSpinBox_transitionband->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), 0.1).toDouble());
213 m_pUi->m_comboBox_filterApplyTo->setCurrentText(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), "All").toString());
214
215 QPoint pos = settings.value(m_sSettingsPath + QString("/FilterDesignView/Position"), QPoint(100,100)).toPoint();
216
217 QRect screenRect = QApplication::primaryScreen()->geometry();
218 if(!screenRect.contains(pos) && QGuiApplication::screens().size() == 1) {
219 move(QPoint(100,100));
220 } else {
221 move(pos);
222 }
223}
224
225//=============================================================================================================
226
228{
229 switch(mode) {
231 break;
232 default: // default is research mode
233 break;
234 }
235}
236
237//=============================================================================================================
238
240{
241 switch(mode) {
243 break;
244 default: // default is realtime mode
245 break;
246 }
247}
248
249//=============================================================================================================
250
252{
253 connect(m_pUi->m_doubleSpinBox_from, &QDoubleSpinBox::editingFinished,
255
256 connect(m_pUi->m_doubleSpinBox_to, &QDoubleSpinBox::editingFinished,
258
259 connect(m_pUi->m_doubleSpinBox_transitionband, &QDoubleSpinBox::editingFinished,
261
262 connect(m_pUi->m_spinBox_filterTaps, &QSpinBox::editingFinished,
264
265 //Intercept events from the spin boxes to get control over key events
266 m_pUi->m_doubleSpinBox_from->installEventFilter(this);
267 m_pUi->m_doubleSpinBox_to->installEventFilter(this);
268 m_pUi->m_doubleSpinBox_transitionband->installEventFilter(this);
269}
270
271//=============================================================================================================
272
274{
275 connect(m_pUi->m_pushButton_exportPlot,&QPushButton::released,
277
278 connect(m_pUi->m_pushButton_exportFilter,&QPushButton::released,
280
281 connect(m_pUi->m_pushButton_loadFilter,&QPushButton::released,
283}
284
285//=============================================================================================================
286
288{
290 m_pUi->m_comboBox_designMethod->addItem(filterMethod.getName());
291 }
292
293 connect(m_pUi->m_comboBox_designMethod,static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
295
296 //Initial selection is a bandpass and Cosine design method
297 m_pUi->m_doubleSpinBox_from->setVisible(true);
298 m_pUi->m_label_lowpass->setVisible(true);
299
300 m_pUi->m_doubleSpinBox_to->setVisible(true);
301 m_pUi->m_label_highpass->setVisible(true);
302
303 m_pUi->m_spinBox_filterTaps->setVisible(true);
304 m_pUi->m_label_filterTaps->setVisible(true);
305
306 connect(m_pUi->m_comboBox_filterApplyTo, &QComboBox::currentTextChanged,
308}
309
310//=============================================================================================================
311
313{
314 m_pFilterPlotScene = new FilterPlotScene(m_pUi->m_graphicsView_filterPlot, this);
315
316 m_pUi->m_graphicsView_filterPlot->setScene(m_pFilterPlotScene);
317 m_pUi->m_graphicsView_filterPlot->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
318 m_pUi->m_graphicsView_filterPlot->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
319
321}
322
323//=============================================================================================================
324
325void FilterDesignView::resizeEvent(QResizeEvent* event)
326{
327 Q_UNUSED(event);
328 m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
329}
330
331//=============================================================================================================
332
333void FilterDesignView::keyPressEvent(QKeyEvent * event)
334{
335 if(event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
336 emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
337 }
338
339 if((event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_Z) || event->key() == Qt::Key_Delete) {
340 emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
341 }
342}
343
344//=============================================================================================================
345
347{
348 //Update the filter of the scene
350 m_filterKernel.getSamplingFrequency(), //Pass the filters sampling frequency, not the one from the fiff info. Reason: sFreq from a loaded filter could be different
351 m_pUi->m_doubleSpinBox_from->value(),
352 m_pUi->m_doubleSpinBox_to->value());
353
354 m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
355}
356
357//=============================================================================================================
358
360{
361 Q_UNUSED(currentIndex);
362
363 //Change visibility of filter tap spin boxes depending on filter design method
364 switch(m_pUi->m_comboBox_designMethod->currentIndex()) {
365 case 0: //Cosine
366// m_pUi->m_spinBox_filterTaps->setVisible(false);
367// m_pUi->m_label_filterTaps->setVisible(false);
368 m_pUi->m_spinBox_filterTaps->setVisible(true);
369 m_pUi->m_label_filterTaps->setVisible(true);
370 break;
371
372 case 1: //Tschebyscheff
373 m_pUi->m_spinBox_filterTaps->setVisible(true);
374 m_pUi->m_label_filterTaps->setVisible(true);
375 break;
376 }
377
379}
380
381//=============================================================================================================
382
384{
385 emit updateFilterFrom(m_pUi->m_doubleSpinBox_from->value());
386 emit updateFilterTo(m_pUi->m_doubleSpinBox_to->value());
387
388 //User defined filter parameters
389 double from = m_pUi->m_doubleSpinBox_from->value();
390 double to = m_pUi->m_doubleSpinBox_to->value();
391
392 double trans_width = m_pUi->m_doubleSpinBox_transitionband->value();
393
394 double bw = to-from;
395 double center = from+bw/2;
396
397 double nyquistFrequency = m_dSFreq/2;
398
399 //Calculate the needed fft length
400 m_iFilterTaps = m_pUi->m_spinBox_filterTaps->value();
401 if(m_pUi->m_spinBox_filterTaps->value()%2 != 0) {
403 }
404
405 //set maximum and minimum for cut off frequency spin boxes
406 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
407 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
408 m_pUi->m_doubleSpinBox_to->setMinimum(0);
409 m_pUi->m_doubleSpinBox_from->setMinimum(0);
410
411 if((m_pUi->m_doubleSpinBox_to->value() < m_pUi->m_doubleSpinBox_from->value())) {
412 m_pUi->m_doubleSpinBox_to->setValue(m_pUi->m_doubleSpinBox_from->value() + 1);
413 }
414
415 m_pUi->m_doubleSpinBox_to->setMinimum(m_pUi->m_doubleSpinBox_from->value());
416 m_pUi->m_doubleSpinBox_from->setMaximum(m_pUi->m_doubleSpinBox_to->value());
417
418 int iMethod = FilterKernel::m_designMethods.indexOf(FilterParameter(m_pUi->m_comboBox_designMethod->currentText()));
419
420 //Generate filters
421 m_filterKernel = FilterKernel("Designed Filter",
424 (double)center/nyquistFrequency,
425 (double)bw/nyquistFrequency,
426 (double)trans_width/nyquistFrequency,
427 m_dSFreq,
428 iMethod);
429
431
432 //update filter plot
434
435 saveSettings();
436}
437
438//=============================================================================================================
439
440void FilterDesignView::onSpinBoxFilterChannelType(const QString& channelType)
441{
442 emit filterChannelTypeChanged(channelType);
443}
444
445//=============================================================================================================
446
448{
449 // Open file dialog
450 QDate date;
451 QString fileName = QFileDialog::getSaveFileName(this,
452 "Save filter plot",
453 QString("%1/%2_%3_%4_FilterPlot").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
454 tr("Vector graphic(*.svg);;Images (*.png)"));
455
456 if(!fileName.isEmpty()) {
457 // Generate screenshot
458 if(fileName.contains(".svg")) {
459 QSvgGenerator svgGen;
460
461 svgGen.setFileName(fileName);
462 QRectF rect = m_pFilterPlotScene->itemsBoundingRect();
463 svgGen.setSize(QSize(rect.width(), rect.height()));
464 //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
465
466 QPainter painter(&svgGen);
467 m_pFilterPlotScene->render(&painter);
468 }
469
470 if(fileName.contains(".png")) {
471 m_pFilterPlotScene->setSceneRect(m_pFilterPlotScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
472 QImage image(m_pFilterPlotScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
473 image.fill(Qt::transparent); // Start all pixels transparent
474
475 QPainter painter(&image);
476 m_pFilterPlotScene->render(&painter);
477 image.save(fileName);
478 }
479 }
480}
481
482//=============================================================================================================
483
485{
486 //Generate appropriate name for the filter to be saved
487 QString filtername;
488
489 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());
490
491 //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.
492 FilterKernel filterWriteTemp = m_filterKernel;
493 filterWriteTemp.getName() = filtername;
494
495 QString fileName = QFileDialog::getSaveFileName(this,
496 "Save filter coefficients",
497 QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(filtername),
498 tr("Text file(*.txt)"));
499
500 FilterIO::writeFilter(fileName, filterWriteTemp);
501}
502
503//=============================================================================================================
504
506{
507 QString path = QFileDialog::getOpenFileName(this,
508 QString("Load filter"),
509 QString("./"),
510 tr("txt files (*.txt)"));
511
512 if(!path.isEmpty()) {
513 //Replace old with new filter operator
514 FilterKernel filterLoadTemp;
515
516 if(!FilterIO::readFilter(path, filterLoadTemp)) {
517 return;
518 }
519 updateGuiFromFilter(filterLoadTemp);
520
522
524
525 } else {
526 qDebug()<<"Could not load filter.";
527 }
528}
529
530//=============================================================================================================
531
533{
534
535}
536
537//=============================================================================================================
538
540{
541 return m_pUi->m_doubleSpinBox_from->value();
542}
543
544//=============================================================================================================
545
547{
548 return m_pUi->m_doubleSpinBox_to->value();
549}
550
551//=============================================================================================================
552
554{
555 m_pUi->m_doubleSpinBox_from->setValue(filter.getHighpassFreq());
556 m_pUi->m_doubleSpinBox_to->setValue(filter.getLowpassFreq());
557 m_pUi->m_spinBox_filterTaps->setValue(filter.getFilterOrder());
558 m_pUi->m_doubleSpinBox_transitionband->setValue(filter.getParksWidth()*(filter.getSamplingFrequency()/2));
559
560 m_pUi->m_comboBox_designMethod->setCurrentIndex(FilterKernel::m_designMethods.indexOf(filter.getDesignMethod()));
561}
562
563//=============================================================================================================
564
Interactive FIR / IIR filter design GUI with a live magnitude / phase preview.
QGraphicsScene that draws the magnitude (and optional phase) response of a designed FIR / IIR filter.
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
Plain-text serialisation of FilterKernel coefficient sets.
FIFF file I/O, in-memory data structures and high-level readers/writers.
2-D display widgets and visualisation helpers (charts, topography, colour maps).
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
AbstractView(QWidget *parent=0, Qt::WindowFlags f=Qt::Widget)
void setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
void changeStateSpinBoxes(int currentIndex)
void updateFilterFrom(double dFrom)
void updateGuiMode(GuiMode mode)
void setSamplingRate(double dSamplingRate)
void updateFilterTo(double dTo)
QPointer< FilterPlotScene > m_pFilterPlotScene
void resizeEvent(QResizeEvent *event)
virtual void keyPressEvent(QKeyEvent *event)
UTILSLIB::FilterKernel m_filterKernel
void guiStyleChanged(DISPLIB::AbstractView::StyleMode style)
UTILSLIB::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)
void updateGuiFromFilter(const UTILSLIB::FilterKernel &filter)
FilterDesignView(const QString &sSettingsPath, QWidget *parent=0, Qt::WindowFlags f=Qt::Widget)
void filterChanged(const UTILSLIB::FilterKernel &activeFilter)
QGraphicsScene rendering the magnitude (and optional phase) response of a designed FIR / IIR filter.
static bool readFilter(QString path, FilterKernel &filter)
Definition filterio.cpp:45
static bool writeFilter(const QString &path, const FilterKernel &filter)
Definition filterio.cpp:130
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.
double getParksWidth() const
double getSamplingFrequency() const
QString getName() const
static QVector< FilterParameter > m_designMethods
static QVector< FilterParameter > m_filterTypes
double getLowpassFreq() const
double getHighpassFreq() const
FilterParameter getDesignMethod() const