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(std::make_unique<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
86
87//=============================================================================================================
88
89void FilterDesignView::setMaxAllowedFilterTaps(int iMaxNumberFilterTaps)
90{
91 if (iMaxNumberFilterTaps % 2 != 0) {
92 iMaxNumberFilterTaps--;
93 }
94
95 m_pUi->m_spinBox_filterTaps->setMaximum(iMaxNumberFilterTaps);
96 m_pUi->m_spinBox_filterTaps->setMinimum(16);
97
98 //Update filter depending on new window size
100}
101
102//=============================================================================================================
103
105{
106 return m_pUi->m_spinBox_filterTaps->value();
107}
108
109//=============================================================================================================
110
111void FilterDesignView::setSamplingRate(double dSamplingRate)
112{
113 if (dSamplingRate <= 0) {
114 qWarning() << "[FilterDesignView::setSamplingRate] Sampling frequency is <= 0. Returning.";
115 }
116
117 m_dSFreq = dSamplingRate;
118
119 //Update min max of spin boxes to nyquist
120 double nyquistFrequency = m_dSFreq / 2;
121
122 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
123 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
124
125 if (m_pUi->m_doubleSpinBox_to->value() > m_dSFreq / 2) {
126 m_pUi->m_doubleSpinBox_to->setValue(m_dSFreq / 2);
127 }
128
129 if (m_pUi->m_doubleSpinBox_from->value() > m_dSFreq / 2) {
130 m_pUi->m_doubleSpinBox_from->setValue(m_dSFreq / 2);
131 }
132
134
136}
137
138//=============================================================================================================
139
141{
142 m_pUi->m_doubleSpinBox_from->setValue(dFrom);
144}
145
146//=============================================================================================================
147
149{
150 m_pUi->m_doubleSpinBox_to->setValue(dTo);
152}
153
154//=============================================================================================================
155
160
161//=============================================================================================================
162
164{
165 return m_pUi->m_comboBox_filterApplyTo->currentText();
166}
167
168//=============================================================================================================
169
170void FilterDesignView::setChannelType(const QString& sType)
171{
172 m_pUi->m_comboBox_filterApplyTo->setCurrentText(sType);
173 saveSettings();
174}
175
176//=============================================================================================================
177
179{
180 if (m_sSettingsPath.isEmpty()) {
181 return;
182 }
183
184 QSettings settings("MNECPP");
185
186 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), m_filterKernel.getHighpassFreq());
187 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTo"), m_filterKernel.getLowpassFreq());
188 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), m_filterKernel.getFilterOrder());
189 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(m_filterKernel.getDesignMethod()));
190 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), m_filterKernel.getParksWidth() * (m_filterKernel.getSamplingFrequency() / 2));
191 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), getChannelType());
192 settings.setValue(m_sSettingsPath + QString("/FilterDesignView/Position"), this->pos());
193}
194
195//=============================================================================================================
196
198{
199 if (m_sSettingsPath.isEmpty()) {
200 return;
201 }
202
203 QSettings settings("MNECPP");
204
205 //Set stored filter settings from last session
206 m_pUi->m_doubleSpinBox_to->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTo"), 40.0).toDouble());
207 m_pUi->m_doubleSpinBox_from->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterFrom"), 1.0).toDouble());
208 m_pUi->m_spinBox_filterTaps->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterOrder"), 128).toInt());
209 m_pUi->m_comboBox_designMethod->setCurrentIndex(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterDesignMethod"), FilterKernel::m_designMethods.indexOf(FilterParameter("Cosine"))).toInt());
210 m_pUi->m_doubleSpinBox_transitionband->setValue(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterTransition"), 0.1).toDouble());
211 m_pUi->m_comboBox_filterApplyTo->setCurrentText(settings.value(m_sSettingsPath + QString("/FilterDesignView/filterChannelType"), "All").toString());
212
213 QPoint pos = settings.value(m_sSettingsPath + QString("/FilterDesignView/Position"), QPoint(100, 100)).toPoint();
214
215 QRect screenRect = QApplication::primaryScreen()->geometry();
216 if (!screenRect.contains(pos) && QGuiApplication::screens().size() == 1) {
217 move(QPoint(100, 100));
218 } else {
219 move(pos);
220 }
221}
222
223//=============================================================================================================
224
226{
227 switch (mode) {
229 break;
230 default: // default is research mode
231 break;
232 }
233}
234
235//=============================================================================================================
236
238{
239 switch (mode) {
241 break;
242 default: // default is realtime mode
243 break;
244 }
245}
246
247//=============================================================================================================
248
250{
251 connect(m_pUi->m_doubleSpinBox_from, &QDoubleSpinBox::editingFinished,
253
254 connect(m_pUi->m_doubleSpinBox_to, &QDoubleSpinBox::editingFinished,
256
257 connect(m_pUi->m_doubleSpinBox_transitionband, &QDoubleSpinBox::editingFinished,
259
260 connect(m_pUi->m_spinBox_filterTaps, &QSpinBox::editingFinished,
262
263 //Intercept events from the spin boxes to get control over key events
264 m_pUi->m_doubleSpinBox_from->installEventFilter(this);
265 m_pUi->m_doubleSpinBox_to->installEventFilter(this);
266 m_pUi->m_doubleSpinBox_transitionband->installEventFilter(this);
267}
268
269//=============================================================================================================
270
272{
273 connect(m_pUi->m_pushButton_exportPlot, &QPushButton::released,
275
276 connect(m_pUi->m_pushButton_exportFilter, &QPushButton::released,
278
279 connect(m_pUi->m_pushButton_loadFilter, &QPushButton::released,
281}
282
283//=============================================================================================================
284
286{
288 m_pUi->m_comboBox_designMethod->addItem(filterMethod.getName());
289 }
290
291 connect(m_pUi->m_comboBox_designMethod, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
293
294 //Initial selection is a bandpass and Cosine design method
295 m_pUi->m_doubleSpinBox_from->setVisible(true);
296 m_pUi->m_label_lowpass->setVisible(true);
297
298 m_pUi->m_doubleSpinBox_to->setVisible(true);
299 m_pUi->m_label_highpass->setVisible(true);
300
301 m_pUi->m_spinBox_filterTaps->setVisible(true);
302 m_pUi->m_label_filterTaps->setVisible(true);
303
304 connect(m_pUi->m_comboBox_filterApplyTo, &QComboBox::currentTextChanged,
306}
307
308//=============================================================================================================
309
311{
312 m_pFilterPlotScene = new FilterPlotScene(m_pUi->m_graphicsView_filterPlot, this);
313
314 m_pUi->m_graphicsView_filterPlot->setScene(m_pFilterPlotScene);
315 m_pUi->m_graphicsView_filterPlot->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
316 m_pUi->m_graphicsView_filterPlot->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
317
319}
320
321//=============================================================================================================
322
323void FilterDesignView::resizeEvent(QResizeEvent* event)
324{
325 Q_UNUSED(event);
326 m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
327}
328
329//=============================================================================================================
330
332{
333 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
334 emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
335 }
336
337 if ((event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_Z) || event->key() == Qt::Key_Delete) {
338 emit filterChannelTypeChanged(m_pUi->m_comboBox_filterApplyTo->currentText());
339 }
340}
341
342//=============================================================================================================
343
345{
346 //Update the filter of the scene
348 m_filterKernel.getSamplingFrequency(), //Pass the filters sampling frequency, not the one from the fiff info. Reason: sFreq from a loaded filter could be different
349 m_pUi->m_doubleSpinBox_from->value(),
350 m_pUi->m_doubleSpinBox_to->value());
351
352 m_pUi->m_graphicsView_filterPlot->fitInView(m_pFilterPlotScene->itemsBoundingRect(), Qt::KeepAspectRatio);
353}
354
355//=============================================================================================================
356
358{
359 Q_UNUSED(currentIndex);
360
361 //Change visibility of filter tap spin boxes depending on filter design method
362 switch (m_pUi->m_comboBox_designMethod->currentIndex()) {
363 case 0: //Cosine
364 m_pUi->m_spinBox_filterTaps->setVisible(true);
365 m_pUi->m_label_filterTaps->setVisible(true);
366 break;
367
368 case 1: //Tschebyscheff
369 m_pUi->m_spinBox_filterTaps->setVisible(true);
370 m_pUi->m_label_filterTaps->setVisible(true);
371 break;
372 }
373
375}
376
377//=============================================================================================================
378
380{
381 emit updateFilterFrom(m_pUi->m_doubleSpinBox_from->value());
382 emit updateFilterTo(m_pUi->m_doubleSpinBox_to->value());
383
384 //User defined filter parameters
385 double from = m_pUi->m_doubleSpinBox_from->value();
386 double to = m_pUi->m_doubleSpinBox_to->value();
387
388 double trans_width = m_pUi->m_doubleSpinBox_transitionband->value();
389
390 double bw = to - from;
391 double center = from + bw / 2;
392
393 double nyquistFrequency = m_dSFreq / 2;
394
395 //Calculate the needed fft length
396 m_iFilterTaps = m_pUi->m_spinBox_filterTaps->value();
397 if (m_pUi->m_spinBox_filterTaps->value() % 2 != 0) {
399 }
400
401 //set maximum and minimum for cut off frequency spin boxes
402 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
403 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
404 m_pUi->m_doubleSpinBox_to->setMinimum(0);
405 m_pUi->m_doubleSpinBox_from->setMinimum(0);
406
407 if ((m_pUi->m_doubleSpinBox_to->value() < m_pUi->m_doubleSpinBox_from->value())) {
408 m_pUi->m_doubleSpinBox_to->setValue(m_pUi->m_doubleSpinBox_from->value() + 1);
409 }
410
411 m_pUi->m_doubleSpinBox_to->setMinimum(m_pUi->m_doubleSpinBox_from->value());
412 m_pUi->m_doubleSpinBox_from->setMaximum(m_pUi->m_doubleSpinBox_to->value());
413
414 int iMethod = FilterKernel::m_designMethods.indexOf(FilterParameter(m_pUi->m_comboBox_designMethod->currentText()));
415
416 //Generate filters
417 m_filterKernel = FilterKernel("Designed Filter",
420 center / nyquistFrequency,
421 bw / nyquistFrequency,
422 trans_width / nyquistFrequency,
423 m_dSFreq,
424 iMethod);
425
427
428 //update filter plot
430
431 saveSettings();
432}
433
434//=============================================================================================================
435
436void FilterDesignView::onSpinBoxFilterChannelType(const QString& channelType)
437{
438 emit filterChannelTypeChanged(channelType);
439}
440
441//=============================================================================================================
442
444{
445 // Open file dialog
446 QDate date;
447 QString fileName = QFileDialog::getSaveFileName(this,
448 "Save filter plot",
449 QString("%1/%2_%3_%4_FilterPlot").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
450 tr("Vector graphic(*.svg);;Images (*.png)"));
451
452 if (!fileName.isEmpty()) {
453 // Generate screenshot
454 if (fileName.contains(".svg")) {
455 QSvgGenerator svgGen;
456
457 svgGen.setFileName(fileName);
458 QRectF rect = m_pFilterPlotScene->itemsBoundingRect();
459 svgGen.setSize(QSize(rect.width(), rect.height()));
460 //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
461
462 QPainter painter(&svgGen);
463 m_pFilterPlotScene->render(&painter);
464 }
465
466 if (fileName.contains(".png")) {
467 m_pFilterPlotScene->setSceneRect(m_pFilterPlotScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
468 QImage image(m_pFilterPlotScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
469 image.fill(Qt::transparent); // Start all pixels transparent
470
471 QPainter painter(&image);
472 m_pFilterPlotScene->render(&painter);
473 image.save(fileName);
474 }
475 }
476}
477
478//=============================================================================================================
479
481{
482 //Generate appropriate name for the filter to be saved
483 QString filtername;
484
485 filtername = QString("%1_%2_%3_Fs%4").arg(m_filterKernel.getFilterType().getName()).arg(static_cast<int>(m_filterKernel.getHighpassFreq())).arg(static_cast<int>(m_filterKernel.getLowpassFreq())).arg(static_cast<int>(m_filterKernel.getSamplingFrequency()));
486
487 //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.
488 FilterKernel filterWriteTemp = m_filterKernel;
489 filterWriteTemp.setName(filtername);
490
491 QString fileName = QFileDialog::getSaveFileName(this,
492 "Save filter coefficients",
493 QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(filtername),
494 tr("Text file(*.txt)"));
495
496 FilterIO::writeFilter(fileName, filterWriteTemp);
497}
498
499//=============================================================================================================
500
502{
503 QString path = QFileDialog::getOpenFileName(this,
504 QString("Load filter"),
505 QString("./"),
506 tr("txt files (*.txt)"));
507
508 if (!path.isEmpty()) {
509 //Replace old with new filter operator
510 FilterKernel filterLoadTemp;
511
512 if (!FilterIO::readFilter(path, filterLoadTemp)) {
513 return;
514 }
515 updateGuiFromFilter(filterLoadTemp);
516
518
520
521 } else {
522 qDebug() << "Could not load filter.";
523 }
524}
525
526//=============================================================================================================
527
531
532//=============================================================================================================
533
535{
536 return m_pUi->m_doubleSpinBox_from->value();
537}
538
539//=============================================================================================================
540
542{
543 return m_pUi->m_doubleSpinBox_to->value();
544}
545
546//=============================================================================================================
547
549{
550 m_pUi->m_doubleSpinBox_from->setValue(filter.getHighpassFreq());
551 m_pUi->m_doubleSpinBox_to->setValue(filter.getLowpassFreq());
552 m_pUi->m_spinBox_filterTaps->setValue(filter.getFilterOrder());
553 m_pUi->m_doubleSpinBox_transitionband->setValue(filter.getParksWidth() * (filter.getSamplingFrequency() / 2));
554
555 m_pUi->m_comboBox_designMethod->setCurrentIndex(FilterKernel::m_designMethods.indexOf(filter.getDesignMethod()));
556}
557
558//=============================================================================================================
559
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
QGraphicsScene that draws the magnitude (and optional phase) response of a designed FIR / IIR filter.
Interactive FIR / IIR filter design GUI with a live magnitude / phase preview.
Plain-text serialisation of UTILSLIB::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)
std::unique_ptr< Ui::FilterDesignViewWidget > m_pUi
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)
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:132
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
static QVector< FilterParameter > m_designMethods
static QVector< FilterParameter > m_filterTypes
void setName(const QString &sFilterName)
double getLowpassFreq() const
double getHighpassFreq() const
FilterParameter getDesignMethod() const