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
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(true);
367 m_pUi->m_label_filterTaps->setVisible(true);
368 break;
369
370 case 1: //Tschebyscheff
371 m_pUi->m_spinBox_filterTaps->setVisible(true);
372 m_pUi->m_label_filterTaps->setVisible(true);
373 break;
374 }
375
377}
378
379//=============================================================================================================
380
382{
383 emit updateFilterFrom(m_pUi->m_doubleSpinBox_from->value());
384 emit updateFilterTo(m_pUi->m_doubleSpinBox_to->value());
385
386 //User defined filter parameters
387 double from = m_pUi->m_doubleSpinBox_from->value();
388 double to = m_pUi->m_doubleSpinBox_to->value();
389
390 double trans_width = m_pUi->m_doubleSpinBox_transitionband->value();
391
392 double bw = to - from;
393 double center = from + bw / 2;
394
395 double nyquistFrequency = m_dSFreq / 2;
396
397 //Calculate the needed fft length
398 m_iFilterTaps = m_pUi->m_spinBox_filterTaps->value();
399 if (m_pUi->m_spinBox_filterTaps->value() % 2 != 0) {
401 }
402
403 //set maximum and minimum for cut off frequency spin boxes
404 m_pUi->m_doubleSpinBox_to->setMaximum(nyquistFrequency);
405 m_pUi->m_doubleSpinBox_from->setMaximum(nyquistFrequency);
406 m_pUi->m_doubleSpinBox_to->setMinimum(0);
407 m_pUi->m_doubleSpinBox_from->setMinimum(0);
408
409 if ((m_pUi->m_doubleSpinBox_to->value() < m_pUi->m_doubleSpinBox_from->value())) {
410 m_pUi->m_doubleSpinBox_to->setValue(m_pUi->m_doubleSpinBox_from->value() + 1);
411 }
412
413 m_pUi->m_doubleSpinBox_to->setMinimum(m_pUi->m_doubleSpinBox_from->value());
414 m_pUi->m_doubleSpinBox_from->setMaximum(m_pUi->m_doubleSpinBox_to->value());
415
416 int iMethod = FilterKernel::m_designMethods.indexOf(FilterParameter(m_pUi->m_comboBox_designMethod->currentText()));
417
418 //Generate filters
419 m_filterKernel = FilterKernel("Designed Filter",
422 (double)center / nyquistFrequency,
423 (double)bw / nyquistFrequency,
424 (double)trans_width / nyquistFrequency,
425 m_dSFreq,
426 iMethod);
427
429
430 //update filter plot
432
433 saveSettings();
434}
435
436//=============================================================================================================
437
438void FilterDesignView::onSpinBoxFilterChannelType(const QString& channelType)
439{
440 emit filterChannelTypeChanged(channelType);
441}
442
443//=============================================================================================================
444
446{
447 // Open file dialog
448 QDate date;
449 QString fileName = QFileDialog::getSaveFileName(this,
450 "Save filter plot",
451 QString("%1/%2_%3_%4_FilterPlot").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
452 tr("Vector graphic(*.svg);;Images (*.png)"));
453
454 if (!fileName.isEmpty()) {
455 // Generate screenshot
456 if (fileName.contains(".svg")) {
457 QSvgGenerator svgGen;
458
459 svgGen.setFileName(fileName);
460 QRectF rect = m_pFilterPlotScene->itemsBoundingRect();
461 svgGen.setSize(QSize(rect.width(), rect.height()));
462 //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
463
464 QPainter painter(&svgGen);
465 m_pFilterPlotScene->render(&painter);
466 }
467
468 if (fileName.contains(".png")) {
469 m_pFilterPlotScene->setSceneRect(m_pFilterPlotScene->itemsBoundingRect()); // Re-shrink the scene to it's bounding contents
470 QImage image(m_pFilterPlotScene->sceneRect().size().toSize(), QImage::Format_ARGB32); // Create the image with the exact size of the shrunk scene
471 image.fill(Qt::transparent); // Start all pixels transparent
472
473 QPainter painter(&image);
474 m_pFilterPlotScene->render(&painter);
475 image.save(fileName);
476 }
477 }
478}
479
480//=============================================================================================================
481
483{
484 //Generate appropriate name for the filter to be saved
485 QString filtername;
486
487 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());
488
489 //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.
490 FilterKernel filterWriteTemp = m_filterKernel;
491 filterWriteTemp.setName(filtername);
492
493 QString fileName = QFileDialog::getSaveFileName(this,
494 "Save filter coefficients",
495 QString("%1/%2").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(filtername),
496 tr("Text file(*.txt)"));
497
498 FilterIO::writeFilter(fileName, filterWriteTemp);
499}
500
501//=============================================================================================================
502
504{
505 QString path = QFileDialog::getOpenFileName(this,
506 QString("Load filter"),
507 QString("./"),
508 tr("txt files (*.txt)"));
509
510 if (!path.isEmpty()) {
511 //Replace old with new filter operator
512 FilterKernel filterLoadTemp;
513
514 if (!FilterIO::readFilter(path, filterLoadTemp)) {
515 return;
516 }
517 updateGuiFromFilter(filterLoadTemp);
518
520
522
523 } else {
524 qDebug() << "Could not load filter.";
525 }
526}
527
528//=============================================================================================================
529
533
534//=============================================================================================================
535
537{
538 return m_pUi->m_doubleSpinBox_from->value();
539}
540
541//=============================================================================================================
542
544{
545 return m_pUi->m_doubleSpinBox_to->value();
546}
547
548//=============================================================================================================
549
551{
552 m_pUi->m_doubleSpinBox_from->setValue(filter.getHighpassFreq());
553 m_pUi->m_doubleSpinBox_to->setValue(filter.getLowpassFreq());
554 m_pUi->m_spinBox_filterTaps->setValue(filter.getFilterOrder());
555 m_pUi->m_doubleSpinBox_transitionband->setValue(filter.getParksWidth() * (filter.getSamplingFrequency() / 2));
556
557 m_pUi->m_comboBox_designMethod->setCurrentIndex(FilterKernel::m_designMethods.indexOf(filter.getDesignMethod()));
558}
559
560//=============================================================================================================
561
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)
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: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