v2.0.0
Loading...
Searching...
No Matches
rtfiffrawviewmodel.cpp
Go to the documentation of this file.
1//=============================================================================================================
15
16//=============================================================================================================
17// INCLUDES
18//=============================================================================================================
19
20#include "rtfiffrawviewmodel.h"
21
22#include <fiff/fiff_types.h>
23#include <fiff/fiff_info.h>
24
25#include <math/numerics.h>
26#include <utils/ioutils.h>
27
28#include <dsp/sphara.h>
30
31//=============================================================================================================
32// QT INCLUDES
33//=============================================================================================================
34
35#include <QBrush>
36#include <QCoreApplication>
37#include <QtConcurrent>
38#include <QFuture>
39#include <QDebug>
40
41//=============================================================================================================
42// EIGEN INCLUDES
43//=============================================================================================================
44
45//=============================================================================================================
46// STL INCLUDES
47//=============================================================================================================
48
49#include <iostream>
50
51//=============================================================================================================
52// USED NAMESPACES
53//=============================================================================================================
54
55using namespace DISPLIB;
56using namespace UTILSLIB;
57using namespace FIFFLIB;
58using namespace Eigen;
59using namespace RTPROCESSINGLIB;
60
61//=============================================================================================================
62// DEFINE GLOBAL METHODS
63//=============================================================================================================
64
65namespace
66{
67
69template<typename Fn>
70void forWrappedSpan(int iStart, int iLength, int iSize, Fn fn)
71{
72 iStart = ((iStart % iSize) + iSize) % iSize;
73 for (int iDone = 0; iDone < iLength;) {
74 const int iPiece = std::min(iLength - iDone, iSize - iStart);
75 fn(iStart, iDone, iPiece);
76 iDone += iPiece;
77 iStart = 0;
78 }
79}
80
81} // namespace
82
83//=============================================================================================================
84// DEFINE MEMBER METHODS
85//=============================================================================================================
86
88: QAbstractTableModel(parent)
89, m_bProjActivated(false)
90, m_bCompActivated(false)
91, m_bSpharaActivated(false)
92, m_bIsFreezed(false)
93, m_bDrawFilterFront(true)
94, m_bPerformFiltering(false)
95, m_bTriggerDetectionActive(false)
96, m_fSps(1024.0f)
97, m_dTriggerThreshold(0.01)
98, m_iT(10)
99, m_iDownsampling(10)
100, m_iMaxSamples(1024)
101, m_iCurrentSample(0)
102, m_iCurrentStartingSample(0)
103, m_iCurrentSampleFreeze(0)
104, m_iMaxFilterLength(128)
105, m_iCurrentBlockSize(0)
106, m_iCurrentTriggerChIndex(0)
107, m_iDistanceTimerSpacer(1000)
108, m_iDetectedTriggers(0)
109, m_sFilterChannelType("MEG")
110, m_pFiffInfo(FiffInfo::SPtr::create())
111, m_colBackground(Qt::white)
112{
113}
114
115//=============================================================================================================
116
120
121//=============================================================================================================
122//virtual functions
123int RtFiffRawViewModel::rowCount(const QModelIndex& /*parent*/) const
124{
125 if (!m_pFiffInfo->chs.isEmpty()) {
126 return m_pFiffInfo->chs.size();
127 } else {
128 return 0;
129 }
130}
131
132//=============================================================================================================
133
134int RtFiffRawViewModel::columnCount(const QModelIndex& /*parent*/) const
135{
136 return 3;
137}
138
139//=============================================================================================================
140
141QVariant RtFiffRawViewModel::data(const QModelIndex& index, int role) const
142{
143 if (role != Qt::DisplayRole && role != Qt::BackgroundRole) {
144 return QVariant();
145 }
146
147 if (role == Qt::BackgroundRole) {
148 return QVariant(QBrush(m_colBackground));
149 }
150
151 if (index.isValid()) {
152 qint32 row = m_qMapIdxRowSelection.value(index.row(), 0);
153
154 //******** first column (chname) ********
155 if (index.column() == 0 && role == Qt::DisplayRole)
156 return QVariant(m_pFiffInfo->ch_names[row]);
157
158 //******** second column (data plot) ********
159 if (index.column() == 1) {
160 QVariant v;
161 RowVectorPair rowVectorPair;
162
163 switch (role) {
164 case Qt::DisplayRole: {
165 if (m_bIsFreezed) {
166 // data freeze
167 if (!m_filterKernel.isEmpty() && m_bPerformFiltering) {
168 rowVectorPair.first = m_matDataFilteredFreeze.data() + row * m_matDataFilteredFreeze.cols();
169 rowVectorPair.second = m_matDataFilteredFreeze.cols();
170 v.setValue(rowVectorPair);
171 } else {
172 rowVectorPair.first = m_matDataRawFreeze.data() + row * m_matDataRawFreeze.cols();
173 rowVectorPair.second = m_matDataRawFreeze.cols();
174 v.setValue(rowVectorPair);
175 }
176 } else {
177 // data stream
178 if (!m_filterKernel.isEmpty() && m_bPerformFiltering) {
179 rowVectorPair.first = m_matDataFiltered.data() + row * m_matDataFiltered.cols();
180 rowVectorPair.second = m_matDataFiltered.cols();
181 v.setValue(rowVectorPair);
182 } else {
183 rowVectorPair.first = m_matDataRaw.data() + row * m_matDataRaw.cols();
184 rowVectorPair.second = m_matDataRaw.cols();
185 v.setValue(rowVectorPair);
186 }
187 }
188
189 return v;
190 }
191 } // end role switch
192 } // end column check
193
194 //******** third column (bad channel) ********
195 if (index.column() == 2 && role == Qt::DisplayRole) {
196 return QVariant(m_pFiffInfo->bads.contains(m_pFiffInfo->ch_names[row]));
197 } // end column check
198
199 } // end index.valid() check
200
201 return QVariant();
202}
203
204//=============================================================================================================
205
206QVariant RtFiffRawViewModel::headerData(int section, Qt::Orientation orientation, int role) const
207{
208 if (role != Qt::DisplayRole && role != Qt::TextAlignmentRole)
209 return QVariant();
210
211 if (orientation == Qt::Horizontal) {
212 switch (section) {
213 case 0: //chname column
214 return QVariant();
215 case 1: //data plot column
216 switch (role) {
217 case Qt::DisplayRole:
218 return QVariant("data plot");
219 case Qt::TextAlignmentRole:
220 return QVariant(Qt::AlignLeft);
221 }
222 return QVariant("data plot");
223 }
224 } else if (orientation == Qt::Vertical) {
225 QModelIndex chname = createIndex(section, 0);
226 switch (role) {
227 case Qt::DisplayRole:
228 return QVariant(data(chname).toString());
229 }
230 }
231
232 return QVariant();
233}
234
235//=============================================================================================================
236
237void RtFiffRawViewModel::initSphara()
238{
239 //Load SPHARA matrices for babymeg and vectorview
240 IOUtils::read_eigen_matrix(m_matSpharaVVGradLoaded, QCoreApplication::applicationDirPath() + QString("/../resources/mne_scan/plugins/noisereduction/SPHARA/Vectorview_SPHARA_InvEuclidean_Grad.txt"));
241 IOUtils::read_eigen_matrix(m_matSpharaVVMagLoaded, QCoreApplication::applicationDirPath() + QString("/../resources/mne_scan/plugins/noisereduction/SPHARA/Vectorview_SPHARA_InvEuclidean_Mag.txt"));
242
243 IOUtils::read_eigen_matrix(m_matSpharaBabyMEGInnerLoaded, QCoreApplication::applicationDirPath() + QString("/../resources/mne_scan/plugins/noisereduction/SPHARA/BabyMEG_SPHARA_InvEuclidean_Inner.txt"));
244 IOUtils::read_eigen_matrix(m_matSpharaBabyMEGOuterLoaded, QCoreApplication::applicationDirPath() + QString("/../resources/mne_scan/plugins/noisereduction/SPHARA/BabyMEG_SPHARA_InvEuclidean_Outer.txt"));
245
246 IOUtils::read_eigen_matrix(m_matSpharaEEGLoaded, QCoreApplication::applicationDirPath() + QString("/../resources/mne_scan/plugins/noisereduction/SPHARA/Current_SPHARA_EEG.txt"));
247
248 //Generate indices used to create the SPHARA operators for VectorView
249 m_vecIndicesFirstVV.resize(0);
250 m_vecIndicesSecondVV.resize(0);
251
252 for (int r = 0; r < m_pFiffInfo->chs.size(); ++r) {
253 //Find GRADIOMETERS
254 if (m_pFiffInfo->chs.at(r).chpos.coil_type == 3012) {
255 m_vecIndicesFirstVV.conservativeResize(m_vecIndicesFirstVV.rows() + 1);
256 m_vecIndicesFirstVV(m_vecIndicesFirstVV.rows() - 1) = r;
257 }
258
259 //Find Magnetometers
260 if (m_pFiffInfo->chs.at(r).chpos.coil_type == 3024) {
261 m_vecIndicesSecondVV.conservativeResize(m_vecIndicesSecondVV.rows() + 1);
262 m_vecIndicesSecondVV(m_vecIndicesSecondVV.rows() - 1) = r;
263 }
264 }
265
266 //Generate indices used to create the SPHARA operators for babyMEG
267 m_vecIndicesFirstBabyMEG.resize(0);
268 for (int r = 0; r < m_pFiffInfo->chs.size(); ++r) {
269 //Find INNER LAYER
270 if (m_pFiffInfo->chs.at(r).chpos.coil_type == 7002) {
271 m_vecIndicesFirstBabyMEG.conservativeResize(m_vecIndicesFirstBabyMEG.rows() + 1);
272 m_vecIndicesFirstBabyMEG(m_vecIndicesFirstBabyMEG.rows() - 1) = r;
273 }
274
275 //Find outer layer (FIFFV_COIL_BABY_REF_MAG)
276 if (m_pFiffInfo->chs.at(r).chpos.coil_type == 7003) {
277 m_vecIndicesSecondBabyMEG.conservativeResize(m_vecIndicesSecondBabyMEG.rows() + 1);
278 m_vecIndicesSecondBabyMEG(m_vecIndicesSecondBabyMEG.rows() - 1) = r;
279 }
280 }
281
282 //Generate indices used to create the SPHARA operators for EEG layouts
283 m_vecIndicesFirstEEG.resize(0);
284 for (int r = 0; r < m_pFiffInfo->chs.size(); ++r) {
285 //Find EEG
286 if (m_pFiffInfo->chs.at(r).kind == FIFFV_EEG_CH) {
287 m_vecIndicesFirstEEG.conservativeResize(m_vecIndicesFirstEEG.rows() + 1);
288 m_vecIndicesFirstEEG(m_vecIndicesFirstEEG.rows() - 1) = r;
289 }
290 }
291
292 //Create Sphara operator for the first time
293 updateSpharaOptions("BabyMEG", 270, 105);
294
295 qDebug() << "RtFiffRawViewModel::initSphara - Read VectorView mag matrix " << m_matSpharaVVMagLoaded.rows() << m_matSpharaVVMagLoaded.cols() << "and grad matrix" << m_matSpharaVVGradLoaded.rows() << m_matSpharaVVGradLoaded.cols();
296 qDebug() << "RtFiffRawViewModel::initSphara - Read BabyMEG inner layer matrix " << m_matSpharaBabyMEGInnerLoaded.rows() << m_matSpharaBabyMEGInnerLoaded.cols() << "and outer layer matrix" << m_matSpharaBabyMEGOuterLoaded.rows() << m_matSpharaBabyMEGOuterLoaded.cols();
297}
298
299//=============================================================================================================
300
301void RtFiffRawViewModel::setFiffInfo(QSharedPointer<FIFFLIB::FiffInfo>& p_pFiffInfo)
302{
303 if (p_pFiffInfo) {
304 RowVectorXi sel; // = RowVectorXi(0,0);
305 QStringList emptyExclude;
306
307 if (p_pFiffInfo->bads.size() > 0) {
308 sel = FiffInfoBase::pick_channels(p_pFiffInfo->ch_names, p_pFiffInfo->bads, emptyExclude);
309 }
310
311 m_vecBadIdcs = sel;
312
313 m_pFiffInfo = p_pFiffInfo;
314
316
317 //Resize data matrix without touching the stored values
318 m_matDataRaw.conservativeResize(m_pFiffInfo->chs.size(), m_iMaxSamples);
319 m_matDataRaw.setZero();
320
321 m_matDataFiltered.conservativeResize(m_pFiffInfo->chs.size(), m_iMaxSamples);
322 m_matDataFiltered.setZero();
323
324 m_vecLastBlockFirstValuesFiltered.conservativeResize(m_pFiffInfo->chs.size());
325 m_vecLastBlockFirstValuesFiltered.setZero();
326
327 m_vecLastBlockFirstValuesRaw.conservativeResize(m_pFiffInfo->chs.size());
328 m_vecLastBlockFirstValuesRaw.setZero();
329
330 m_matOverlap.conservativeResize(m_pFiffInfo->chs.size(), m_iMaxFilterLength);
331
332 m_matSparseProjMult = SparseMatrix<double>(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
333 m_matSparseCompMult = SparseMatrix<double>(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
334 m_matSparseSpharaMult = SparseMatrix<double>(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
335 m_matSparseProjCompMult = SparseMatrix<double>(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
336
337 m_matSparseProjMult.setIdentity();
338 m_matSparseCompMult.setIdentity();
339 m_matSparseSpharaMult.setIdentity();
340 m_matSparseProjCompMult.setIdentity();
341
342 //Create the initial Compensator projector
344
345 //Initialize filter channel names
346 int visibleInit = 20;
347 QStringList filterChannels;
348
349 if (visibleInit > m_pFiffInfo->chs.size()) {
350 while (visibleInit > m_pFiffInfo->chs.size()) {
351 visibleInit--;
352 }
353 }
354
355 for (qint32 b = 0; b < visibleInit; ++b) {
356 filterChannels.append(m_pFiffInfo->ch_names.at(b));
357 }
358
359 createFilterChannelList(filterChannels);
360
361 // //Look for trigger channels and initialise detected trigger map
362 // for(int i = 0; i<m_pFiffInfo->chs.size(); ++i) {
363 // if(m_pFiffInfo->chs[i].kind == FIFFV_STIM_CH/* && m_pFiffInfo->chs[i].ch_name == "STI 001"*/)
364 // m_lTriggerChannelIndices.append(i);
365 // }
366
367 //Init the sphara operators
368 initSphara();
369 } else {
370 m_vecBadIdcs = RowVectorXi(0, 0);
371 m_matProj = MatrixXd(0, 0);
372 m_matComp = MatrixXd(0, 0);
373 }
374}
375
376//=============================================================================================================
377
378void RtFiffRawViewModel::setSamplingInfo(float sps, int T, bool bSetZero)
379{
380 beginResetModel();
381
382 m_iT = T;
383
384 m_iMaxSamples = (qint32)ceil(static_cast<double>(sps) * T);
385
386 //Resize data matrix without touching the stored values
387 m_matDataRaw.conservativeResize(m_pFiffInfo->chs.size(), m_iMaxSamples);
388 m_matDataFiltered.conservativeResize(m_pFiffInfo->chs.size(), m_iMaxSamples);
389 m_vecLastBlockFirstValuesRaw.conservativeResize(m_pFiffInfo->chs.size());
390 m_vecLastBlockFirstValuesFiltered.conservativeResize(m_pFiffInfo->chs.size());
391
392 if (bSetZero) {
393 m_matDataRaw.setZero();
394 m_matDataFiltered.setZero();
395 m_vecLastBlockFirstValuesRaw.setZero();
396 m_vecLastBlockFirstValuesFiltered.setZero();
397 }
398
399 if (m_iCurrentSample > m_iMaxSamples) {
400 m_iCurrentStartingSample += m_iCurrentSample;
401 m_iCurrentSample = 0;
402 }
403
404 endResetModel();
405}
406
407//=============================================================================================================
408
410{
411 const MatrixXdR& matData = (!m_filterKernel.isEmpty() && m_bPerformFiltering) ? m_matDataFiltered : m_matDataRaw;
412
413 MatrixXd matBlock(matData.rows(), m_iCurrentBlockSize);
414 if (m_iCurrentBlockSize > 0) {
415 forWrappedSpan(m_iCurrentSample - m_iCurrentBlockSize, m_iCurrentBlockSize, static_cast<int>(matData.cols()), [&](int iCol, int iOffset, int iLength) {
416 matBlock.middleCols(iOffset, iLength) = matData.middleCols(iCol, iLength);
417 });
418 }
419
420 return matBlock;
421}
422
423//=============================================================================================================
424
425void RtFiffRawViewModel::addData(const QList<MatrixXd>& data)
426{
427 //SSP
428 bool doProj = m_bProjActivated && m_matDataRaw.cols() > 0 && m_matDataRaw.rows() == m_matProj.cols() ? true : false;
429
430 //Compensator
431 bool doComp = m_bCompActivated && m_matDataRaw.cols() > 0 && m_matDataRaw.rows() == m_matComp.cols() ? true : false;
432
433 //SPHARA
434 bool doSphara = m_bSpharaActivated && m_matSparseSpharaMult.cols() > 0 && m_matDataRaw.rows() == m_matSparseSpharaMult.cols() ? true : false;
435
436 const int iMaxCols = static_cast<int>(m_matDataRaw.cols());
437 if (iMaxCols == 0) {
438 return;
439 }
440
441 //Copy new data into the ring buffer: stream sample s goes to column s mod iMaxCols
442 for (qint32 b = 0; b < data.size(); ++b) {
443 int nRow = data.at(b).rows();
444
445 if (nRow != m_matDataRaw.rows()) {
446 qDebug() << "incoming data does not match internal data row size. Returning...";
447 return;
448 }
449
450 const MatrixXd& matBlock = data.at(b);
451 const int nCol = static_cast<int>(matBlock.cols());
452
453 MatrixXd matCorrected;
454 if (doComp) {
455 matCorrected = doProj ? MatrixXd(m_matSparseProjCompMult * matBlock) : MatrixXd(m_matSparseCompMult * matBlock);
456 } else {
457 matCorrected = doProj ? MatrixXd(m_matSparseProjMult * matBlock) : matBlock;
458 }
459
460 QList<QPair<int, double>> lTriggers;
461 if (m_bTriggerDetectionActive) {
462 lTriggers = RTPROCESSINGLIB::detectTriggerFlanksMax(matBlock, m_iCurrentTriggerChIndex, 0, m_dTriggerThreshold, true, 500);
463 }
464
465 forWrappedSpan(m_iCurrentSample, nCol, iMaxCols, [&](int iCol, int iOffset, int iLength) {
466 if (iCol == 0 && m_iCurrentSample == iMaxCols) {
467 startNewSweep();
468 }
469
470 m_matDataRaw.middleCols(iCol, iLength) = matCorrected.middleCols(iOffset, iLength);
471 if (m_filterKernel.isEmpty() || !m_bPerformFiltering) {
472 m_matDataFiltered.middleCols(iCol, iLength).setZero();
473 if (doSphara) {
474 m_matDataRaw.middleCols(iCol, iLength) = m_matSparseSpharaMult * m_matDataRaw.middleCols(iCol, iLength);
475 }
476 }
477
478 for (const QPair<int, double>& trigger : std::as_const(lTriggers)) {
479 if (trigger.first >= iOffset && trigger.first < iOffset + iLength) {
480 m_qMapDetectedTrigger[m_iCurrentTriggerChIndex].append({iCol + trigger.first - iOffset, trigger.second});
481 }
482 }
483
484 m_iCurrentSample = iCol + iLength;
485 });
486
487 if (!lTriggers.isEmpty()) {
488 m_iDetectedTriggers += lTriggers.size();
489 emit triggerDetected(m_iDetectedTriggers, m_qMapDetectedTrigger);
490 }
491
492 //Filter the whole block at once so the overlap-add stays continuous across the wrap
493 if (!m_filterKernel.isEmpty() && m_bPerformFiltering) {
494 const int iBlockStart = m_iCurrentSample - nCol;
495 filterDataBlock(matCorrected, iBlockStart);
496
497 //Perform SPHARA on filtered data after actual filtering - SPHARA should be applied on the best possible data
498 if (doSphara) {
499 forWrappedSpan(iBlockStart - m_iMaxFilterLength / 2, nCol, iMaxCols, [&](int iCol, int, int iLength) {
500 m_matDataFiltered.middleCols(iCol, iLength) = m_matSparseSpharaMult * m_matDataFiltered.middleCols(iCol, iLength);
501 });
502 }
503 }
504
505 m_iCurrentBlockSize = std::min(nCol, iMaxCols);
506 }
507
508 //Update data content
509 QModelIndex topLeft = this->index(0, 1);
510 QModelIndex bottomRight = this->index(m_pFiffInfo->ch_names.size() - 1, 1);
511 QVector<int> roles;
512 roles << Qt::DisplayRole;
513
514 emit dataChanged(topLeft, bottomRight, roles);
515}
516
517//=============================================================================================================
518
519void RtFiffRawViewModel::startNewSweep()
520{
521 m_iCurrentStartingSample += m_iCurrentSample;
522 m_iCurrentSample = 0;
523
524 if (!m_bIsFreezed) {
525 m_vecLastBlockFirstValuesFiltered = m_matDataFiltered.col(0);
526 m_vecLastBlockFirstValuesRaw = m_matDataRaw.col(0);
527 }
528
529 //Store old detected triggers
530 m_qMapDetectedTriggerOld = m_qMapDetectedTrigger;
531
532 //Clear detected triggers
533 if (m_bTriggerDetectionActive) {
534 QMutableMapIterator<int, QList<QPair<int, double>>> i(m_qMapDetectedTrigger);
535 while (i.hasNext()) {
536 i.next();
537 i.value().clear();
538 }
539 }
540}
541
542//=============================================================================================================
543
545{
546 if (row < m_qMapIdxRowSelection.size()) {
547 qint32 chRow = m_qMapIdxRowSelection[row];
548 return m_pFiffInfo->chs.at(chRow).kind;
549 }
550
551 return 0;
552}
553
554//=============================================================================================================
555
557{
558 if (row < m_qMapIdxRowSelection.size()) {
559 qint32 chRow = m_qMapIdxRowSelection[row];
560 return m_pFiffInfo->chs.at(chRow).unit;
561 }
562
563 return FIFF_UNIT_NONE;
564}
565
566//=============================================================================================================
567
569{
570 if (row < m_qMapIdxRowSelection.size()) {
571 qint32 chRow = m_qMapIdxRowSelection[row];
572 return m_pFiffInfo->chs.at(chRow).chpos.coil_type;
573 }
574
575 return FIFFV_COIL_NONE;
576}
577
578//=============================================================================================================
579
580void RtFiffRawViewModel::selectRows(const QList<qint32>& selection)
581{
582 beginResetModel();
583
584 m_qMapIdxRowSelection.clear();
585
586 qint32 count = 0;
587 for (qint32 i = 0; i < selection.size(); ++i) {
588 if (selection[i] < m_pFiffInfo->chs.size()) {
589 m_qMapIdxRowSelection.insert(count, selection[i]);
590 ++count;
591 }
592 }
593
594 emit newSelection(selection);
595
596 endResetModel();
597}
598
599//=============================================================================================================
600
601void RtFiffRawViewModel::hideRows(const QList<qint32>& selection)
602{
603 beginResetModel();
604
605 for (qint32 i = 0; i < selection.size(); ++i) {
606 if (m_qMapIdxRowSelection.contains(selection.at(i))) {
607 m_qMapIdxRowSelection.remove(selection.at(i));
608 }
609 }
610
611 emit newSelection(selection);
612
613 endResetModel();
614}
615
616//=============================================================================================================
617
619{
620 beginResetModel();
621
622 m_qMapIdxRowSelection.clear();
623
624 for (qint32 i = 0; i < m_pFiffInfo->chs.size(); ++i) {
625 m_qMapIdxRowSelection.insert(i, i);
626 }
627
628 endResetModel();
629}
630
631//=============================================================================================================
632
633void RtFiffRawViewModel::toggleFreeze(const QModelIndex&)
634{
635 m_bIsFreezed = !m_bIsFreezed;
636
637 if (m_bIsFreezed) {
638 m_matDataRawFreeze = m_matDataRaw;
639 m_matDataFilteredFreeze = m_matDataFiltered;
640 m_qMapDetectedTriggerFreeze = m_qMapDetectedTrigger;
641 m_qMapDetectedTriggerOldFreeze = m_qMapDetectedTriggerOld;
642
643 m_iCurrentSampleFreeze = m_iCurrentSample;
644 }
645
646 //Update data content
647 QModelIndex topLeft = this->index(0, 1);
648 QModelIndex bottomRight = this->index(m_pFiffInfo->chs.size() - 1, 1);
649 QVector<int> roles;
650 roles << Qt::DisplayRole;
651
652 emit dataChanged(topLeft, bottomRight, roles);
653}
654
655//=============================================================================================================
656
657void RtFiffRawViewModel::setScaling(const QMap<qint32, float>& p_qMapChScaling)
658{
659 beginResetModel();
660 m_qMapChScaling = p_qMapChScaling;
661 endResetModel();
662}
663
664//=============================================================================================================
665
666void RtFiffRawViewModel::updateProjection(const QList<FIFFLIB::FiffProj>& projs)
667{
668 // Update the SSP projector
669 if (m_pFiffInfo) {
670 //If a minimum of one projector is active set m_bProjActivated to true so that this model applies the ssp to the incoming data
671 m_bProjActivated = false;
672 m_pFiffInfo->projs = projs;
673
674 for (qint32 i = 0; i < this->m_pFiffInfo->projs.size(); ++i) {
675 if (this->m_pFiffInfo->projs[i].active) {
676 m_bProjActivated = true;
677 break;
678 }
679 }
680
681 this->m_pFiffInfo->make_projector(m_matProj);
682
683 qDebug() << "RtFiffRawViewModel::updateProjection - New projection calculated.";
684
685 //set columns of matrix to zero depending on bad channels indexes
686 for (qint32 j = 0; j < m_vecBadIdcs.cols(); ++j) {
687 m_matProj.col(m_vecBadIdcs[j]).setZero();
688 }
689
690 // std::cout << "Bads\n" << m_vecBadIdcs << std::endl;
691 // std::cout << "Proj\n";
692 // std::cout << m_matProj.block(0,0,10,10) << std::endl;
693
694 //
695 // Make proj sparse
696 //
697 qint32 nchan = this->m_pFiffInfo->nchan;
698 qint32 i, k;
699
700 typedef Eigen::Triplet<double> T;
701 std::vector<T> tripletList;
702 tripletList.reserve(nchan);
703
704 tripletList.clear();
705 tripletList.reserve(m_matProj.rows() * m_matProj.cols());
706 for (i = 0; i < m_matProj.rows(); ++i) {
707 for (k = 0; k < m_matProj.cols(); ++k) {
708 if (m_matProj(i, k) != 0) {
709 tripletList.push_back(T(i, k, m_matProj(i, k)));
710 }
711 }
712 }
713
714 m_matSparseProjMult = SparseMatrix<double>(m_matProj.rows(), m_matProj.cols());
715 if (tripletList.size() > 0) {
716 m_matSparseProjMult.setFromTriplets(tripletList.begin(), tripletList.end());
717 }
718
719 //Create full multiplication matrix
720 m_matSparseProjCompMult = m_matSparseProjMult * m_matSparseCompMult;
721 }
722}
723
724//=============================================================================================================
725
727{
728 // Update the compensator
729 if (m_pFiffInfo) {
730 if (to == 0) {
731 m_bCompActivated = false;
732 } else {
733 m_bCompActivated = true;
734 }
735
736 // qDebug()<<"to"<<to;
737 // qDebug()<<"from"<<from;
738 // qDebug()<<"m_bCompActivated"<<m_bCompActivated;
739
740 FiffCtfComp newComp;
741 this->m_pFiffInfo->make_compensator(0, to, newComp); //Do this always from 0 since we always read new raw data, we never actually perform a multiplication on already existing data
742
743 //We do not need to call this->m_pFiffInfo->set_current_comp(to);
744 //Because we will set the compensators to the coil in the same FiffInfo which is already used to write to file.
745 //Note that the data is written in raw form not in compensated form.
746 m_matComp = newComp.data->data;
747
748 //
749 // Make proj sparse
750 //
751 qint32 nchan = this->m_pFiffInfo->nchan;
752 qint32 i, k;
753
754 typedef Eigen::Triplet<double> T;
755 std::vector<T> tripletList;
756 tripletList.reserve(nchan);
757
758 tripletList.clear();
759 tripletList.reserve(m_matComp.rows() * m_matComp.cols());
760 for (i = 0; i < m_matComp.rows(); ++i) {
761 for (k = 0; k < m_matComp.cols(); ++k) {
762 if (m_matComp(i, k) != 0) {
763 tripletList.push_back(T(i, k, m_matComp(i, k)));
764 }
765 }
766 }
767
768 m_matSparseCompMult = SparseMatrix<double>(m_matComp.rows(), m_matComp.cols());
769 if (tripletList.size() > 0) {
770 m_matSparseCompMult.setFromTriplets(tripletList.begin(), tripletList.end());
771 }
772
773 //Create full multiplication matrix
774 m_matSparseProjCompMult = m_matSparseProjMult * m_matSparseCompMult;
775 }
776}
777
778//=============================================================================================================
779
781{
782 m_bSpharaActivated = state;
783}
784
785//=============================================================================================================
786
787void RtFiffRawViewModel::updateSpharaOptions(const QString& sSytemType, int nBaseFctsFirst, int nBaseFctsSecond)
788{
789 if (m_pFiffInfo) {
790 qDebug() << "RtFiffRawViewModel::updateSpharaOptions - Creating SPHARA operator for" << sSytemType;
791
792 MatrixXd matSpharaMultFirst = MatrixXd::Identity(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
793 MatrixXd matSpharaMultSecond = MatrixXd::Identity(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
794
795 if (sSytemType == "VectorView" && m_matSpharaVVGradLoaded.size() != 0 && m_matSpharaVVMagLoaded.size() != 0) {
796 matSpharaMultFirst = UTILSLIB::makeSpharaProjector(m_matSpharaVVGradLoaded, m_vecIndicesFirstVV, m_pFiffInfo->nchan, nBaseFctsFirst, 1); //GRADIOMETERS
797 matSpharaMultSecond = UTILSLIB::makeSpharaProjector(m_matSpharaVVMagLoaded, m_vecIndicesSecondVV, m_pFiffInfo->nchan, nBaseFctsSecond, 0); //Magnetometers
798 }
799
800 if (sSytemType == "BabyMEG" && m_matSpharaBabyMEGInnerLoaded.size() != 0) {
801 matSpharaMultFirst = UTILSLIB::makeSpharaProjector(m_matSpharaBabyMEGInnerLoaded, m_vecIndicesFirstBabyMEG, m_pFiffInfo->nchan, nBaseFctsFirst, 0); //InnerLayer
802 }
803
804 if (sSytemType == "EEG" && m_matSpharaEEGLoaded.size() != 0) {
805 matSpharaMultFirst = UTILSLIB::makeSpharaProjector(m_matSpharaEEGLoaded, m_vecIndicesFirstEEG, m_pFiffInfo->nchan, nBaseFctsFirst, 0); //InnerLayer
806 }
807
808 //Write final operator matrices to file
809 // IOUtils::write_eigen_matrix(matSpharaMultFirst, QString(QCoreApplication::applicationDirPath() + "/../resources/mne_scan/plugins/noisereduction/SPHARA/matSpharaMultFirst.txt"));
810 // IOUtils::write_eigen_matrix(matSpharaMultSecond, QString(QCoreApplication::applicationDirPath() + "/../resources/mne_scan/plugins/noisereduction/SPHARA/matSpharaMultSecond.txt"));
811 // IOUtils::write_eigen_matrix(m_matSpharaEEGLoaded, QString(QCoreApplication::applicationDirPath() + "/../resources/mne_scan/plugins/noisereduction/SPHARA/m_matSpharaEEGLoaded.txt"));
812
813 //
814 // Make operators sparse
815 //
816 qint32 nchan = this->m_pFiffInfo->nchan;
817 qint32 i, k;
818
819 typedef Eigen::Triplet<double> T;
820 std::vector<T> tripletList;
821 tripletList.reserve(nchan);
822
823 //First operator
824 tripletList.clear();
825 tripletList.reserve(matSpharaMultFirst.rows() * matSpharaMultFirst.cols());
826 for (i = 0; i < matSpharaMultFirst.rows(); ++i) {
827 for (k = 0; k < matSpharaMultFirst.cols(); ++k) {
828 if (matSpharaMultFirst(i, k) != 0) {
829 tripletList.push_back(T(i, k, matSpharaMultFirst(i, k)));
830 }
831 }
832 }
833
834 Eigen::SparseMatrix<double> matSparseSpharaMultFirst = SparseMatrix<double>(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
835
836 matSparseSpharaMultFirst = SparseMatrix<double>(matSpharaMultFirst.rows(), matSpharaMultFirst.cols());
837 if (tripletList.size() > 0) {
838 matSparseSpharaMultFirst.setFromTriplets(tripletList.begin(), tripletList.end());
839 }
840
841 //Second operator
842 tripletList.clear();
843 tripletList.reserve(matSpharaMultSecond.rows() * matSpharaMultSecond.cols());
844
845 for (i = 0; i < matSpharaMultSecond.rows(); ++i) {
846 for (k = 0; k < matSpharaMultSecond.cols(); ++k) {
847 if (matSpharaMultSecond(i, k) != 0) {
848 tripletList.push_back(T(i, k, matSpharaMultSecond(i, k)));
849 }
850 }
851 }
852
853 Eigen::SparseMatrix<double> matSparseSpharaMultSecond = SparseMatrix<double>(m_pFiffInfo->chs.size(), m_pFiffInfo->chs.size());
854
855 if (tripletList.size() > 0) {
856 matSparseSpharaMultSecond.setFromTriplets(tripletList.begin(), tripletList.end());
857 }
858
859 //Create full multiplication matrix
860 m_matSparseSpharaMult = matSparseSpharaMultFirst * matSparseSpharaMultSecond;
861 }
862}
863
864//=============================================================================================================
865
867{
868 m_filterKernel = filterData;
869
870 m_iMaxFilterLength = 1;
871 for (int i = 0; i < filterData.size(); ++i) {
872 if (m_iMaxFilterLength < filterData.at(i).getFilterOrder()) {
873 m_iMaxFilterLength = filterData.at(i).getFilterOrder();
874 }
875 }
876
877 m_matOverlap.conservativeResize(m_pFiffInfo->chs.size(), m_iMaxFilterLength);
878 m_matOverlap.setZero();
879
880 m_bDrawFilterFront = false;
881
882 //Filter all visible data channels at once
883 //filterDataBlock();
884}
885
886//=============================================================================================================
887
889{
890 m_bPerformFiltering = state;
891}
892
893//=============================================================================================================
894
896{
897 m_colBackground = color;
898}
899
900//=============================================================================================================
901
902void RtFiffRawViewModel::setFilterChannelType(const QString& channelType)
903{
904 m_sFilterChannelType = channelType;
905 m_filterChannelList = m_visibleChannelList;
906
907 //This version is for when all channels of a type are to be filtered (not only the visible ones).
908 //Create channel filter list independent from channelNames
909 m_filterChannelList.clear();
910
911 for (int i = 0; i < m_pFiffInfo->chs.size(); ++i) {
912 if ((m_pFiffInfo->chs.at(i).kind == FIFFV_MEG_CH || m_pFiffInfo->chs.at(i).kind == FIFFV_EEG_CH ||
913 m_pFiffInfo->chs.at(i).kind == FIFFV_EOG_CH || m_pFiffInfo->chs.at(i).kind == FIFFV_ECG_CH ||
914 m_pFiffInfo->chs.at(i).kind == FIFFV_EMG_CH) /* && !m_pFiffInfo->bads.contains(m_pFiffInfo->chs.at(i).ch_name)*/) {
915 if (m_sFilterChannelType == "All") {
916 m_filterChannelList << m_pFiffInfo->chs.at(i).ch_name;
917 } else if (m_pFiffInfo->chs.at(i).ch_name.contains(m_sFilterChannelType)) {
918 m_filterChannelList << m_pFiffInfo->chs.at(i).ch_name;
919 }
920 }
921 }
922
923 // if(channelType != "All") {
924 // QMutableListIterator<QString> i(m_filterChannelList);
925 // while(i.hasNext()) {
926 // QString val = i.next();
927 // if(!val.contains(channelType, Qt::CaseInsensitive)) {
928 // i.remove();
929 // }
930 // }
931 // }
932
933 // m_bDrawFilterFront = false;
934
935 //Filter all visible data channels at once
936 //filterDataBlock();
937}
938
939//=============================================================================================================
940
942{
943 m_filterChannelList.clear();
944 m_visibleChannelList = channelNames;
945
946 // //Create channel fiter list based on channelNames
947 // for(int i = 0; i<m_pFiffInfo->chs.size(); ++i) {
948 // if((m_pFiffInfo->chs.at(i).kind == FIFFV_MEG_CH || m_pFiffInfo->chs.at(i).kind == FIFFV_EEG_CH ||
949 // m_pFiffInfo->chs.at(i).kind == FIFFV_EOG_CH || m_pFiffInfo->chs.at(i).kind == FIFFV_ECG_CH ||
950 // m_pFiffInfo->chs.at(i).kind == FIFFV_EMG_CH) && !m_pFiffInfo->bads.contains(m_pFiffInfo->chs.at(i).ch_name)) {
951 // if(m_sFilterChannelType == "All" && channelNames.contains(m_pFiffInfo->chs.at(i).ch_name))
952 // m_filterChannelList << m_pFiffInfo->chs.at(i).ch_name;
953 // else if(m_pFiffInfo->chs.at(i).ch_name.contains(m_sFilterChannelType) && channelNames.contains(m_pFiffInfo->chs.at(i).ch_name))
954 // m_filterChannelList << m_pFiffInfo->chs.at(i).ch_name;
955 // }
956 // }
957
958 //Create channel filter list independent from channelNames
959 for (int i = 0; i < m_pFiffInfo->chs.size(); ++i) {
960 if ((m_pFiffInfo->chs.at(i).kind == FIFFV_MEG_CH || m_pFiffInfo->chs.at(i).kind == FIFFV_EEG_CH ||
961 m_pFiffInfo->chs.at(i).kind == FIFFV_EOG_CH || m_pFiffInfo->chs.at(i).kind == FIFFV_ECG_CH ||
962 m_pFiffInfo->chs.at(i).kind == FIFFV_EMG_CH) /* && !m_pFiffInfo->bads.contains(m_pFiffInfo->chs.at(i).ch_name)*/) {
963 if (m_sFilterChannelType == "All") {
964 m_filterChannelList << m_pFiffInfo->chs.at(i).ch_name;
965 } else if (m_pFiffInfo->chs.at(i).ch_name.contains(m_sFilterChannelType)) {
966 m_filterChannelList << m_pFiffInfo->chs.at(i).ch_name;
967 }
968 }
969 }
970
971 // m_bDrawFilterFront = false;
972
973 // for(int i = 0; i<m_filterChannelList.size(); ++i)
974 // std::cout<<m_filterChannelList.at(i).toStdString()<<std::endl;
975
976 //Filter all visible data channels at once
977 //filterDataBlock();
978}
979
980//=============================================================================================================
981
982void RtFiffRawViewModel::markChBad(QModelIndex ch, bool status)
983{
984 QList<FiffChInfo> chInfolist = m_pFiffInfo->chs;
985
986 if (status) {
987 if (!m_pFiffInfo->bads.contains(chInfolist[ch.row()].ch_name))
988 m_pFiffInfo->bads.append(chInfolist[ch.row()].ch_name);
989 qDebug() << "RawModel:" << chInfolist[ch.row()].ch_name << "marked as bad.";
990 } else if (m_pFiffInfo->bads.contains(chInfolist[ch.row()].ch_name)) {
991 int index = m_pFiffInfo->bads.indexOf(chInfolist[ch.row()].ch_name);
992 m_pFiffInfo->bads.removeAt(index);
993 qDebug() << "RawModel:" << chInfolist[ch.row()].ch_name << "marked as good.";
994 }
995
996 //Redefine channels which are to be filtered
997 QStringList channelNames;
998 createFilterChannelList(channelNames);
999
1000 //Update indeices of bad channels (this vector is needed when creating new ssp operators)
1001 QStringList emptyExclude;
1002 m_vecBadIdcs = FiffInfoBase::pick_channels(m_pFiffInfo->ch_names, m_pFiffInfo->bads, emptyExclude);
1003
1004 emit dataChanged(ch, ch);
1005}
1006
1007//=============================================================================================================
1008
1009void RtFiffRawViewModel::triggerInfoChanged(const QMap<double, QColor>& colorMap, bool active, QString triggerCh, double threshold)
1010{
1011 m_qMapTriggerColor = colorMap;
1012 m_bTriggerDetectionActive = active;
1013 m_dTriggerThreshold = threshold;
1014
1015 //Find channel index and initialise detected trigger map if channel name changed
1016 if (m_sCurrentTriggerCh != triggerCh) {
1017 m_sCurrentTriggerCh = triggerCh;
1018
1019 QList<QPair<int, double>> temp;
1020 m_qMapDetectedTrigger.clear();
1021
1022 for (int i = 0; i < m_pFiffInfo->chs.size(); ++i) {
1023 if (m_pFiffInfo->chs[i].ch_name == m_sCurrentTriggerCh) {
1024 m_iCurrentTriggerChIndex = i;
1025 m_qMapDetectedTrigger.insert(i, temp);
1026 break;
1027 }
1028 }
1029 }
1030
1031 m_sCurrentTriggerCh = triggerCh;
1032}
1033
1034//=============================================================================================================
1035
1037{
1038 if (value <= 0) {
1039 m_iDistanceTimerSpacer = 1000;
1040 } else {
1041 m_iDistanceTimerSpacer = value;
1042 }
1043}
1044
1045//=============================================================================================================
1046
1048{
1049 m_iDetectedTriggers = 0;
1050}
1051
1052//=============================================================================================================
1053
1054void RtFiffRawViewModel::markChBad(QModelIndexList chlist, bool status)
1055{
1056 QList<FiffChInfo> chInfolist = m_pFiffInfo->chs;
1057
1058 for (int i = 0; i < chlist.size(); ++i) {
1059 if (status) {
1060 if (!m_pFiffInfo->bads.contains(chInfolist[chlist[i].row()].ch_name))
1061 m_pFiffInfo->bads.append(chInfolist[chlist[i].row()].ch_name);
1062 } else {
1063 if (m_pFiffInfo->bads.contains(chInfolist[chlist[i].row()].ch_name)) {
1064 int index = m_pFiffInfo->bads.indexOf(chInfolist[chlist[i].row()].ch_name);
1065 m_pFiffInfo->bads.removeAt(index);
1066 }
1067 }
1068
1069 emit dataChanged(chlist[i], chlist[i]);
1070 }
1071
1072 //Update indeices of bad channels (this vector is needed when creating new ssp operators)
1073 QStringList emptyExclude;
1074 m_vecBadIdcs = FiffInfoBase::pick_channels(m_pFiffInfo->ch_names, m_pFiffInfo->bads, emptyExclude);
1075}
1076
1077//=============================================================================================================
1078
1079void RtFiffRawViewModel::doFilterPerChannelRTMSA(QPair<QList<FilterKernel>, QPair<int, RowVectorXd>>& channelDataTime)
1080{
1081 for (int i = 0; i < channelDataTime.first.size(); ++i) {
1082 //channelDataTime.second.second = channelDataTime.first.at(i).applyConvFilter(channelDataTime.second.second, true);
1083 channelDataTime.first[i].applyFftFilter(channelDataTime.second.second, true); //FFT Convolution for rt is not suitable. FFT make the signal filtering non causal.
1084 }
1085}
1086
1087//=============================================================================================================
1088
1089void RtFiffRawViewModel::filterDataBlock()
1090{
1091 //std::cout<<"START RtFiffRawViewModel::filterDataBlock"<<std::endl;
1092
1093 if (m_filterKernel.isEmpty() || !m_bPerformFiltering) {
1094 return;
1095 }
1096
1097 //Create temporary filters with higher fft length because we are going to filter all available data at once for one time
1098 QList<FilterKernel> tempFilterList;
1099
1100 int fftLength = m_matDataRaw.row(0).cols() + 4 * m_iMaxFilterLength;
1101 int exp = ceil(Numerics::log2(fftLength));
1102 fftLength = pow(2, exp) < 512 ? 512 : pow(2, exp);
1103
1104 for (int i = 0; i < m_filterKernel.size(); ++i) {
1105 FilterKernel tempFilter(m_filterKernel.at(i).getName(),
1106 FilterKernel::m_filterTypes.indexOf(m_filterKernel.at(i).getFilterType()),
1107 m_filterKernel.at(i).getFilterOrder(),
1108 m_filterKernel.at(i).getCenterFrequency(),
1109 m_filterKernel.at(i).getBandwidth(),
1110 m_filterKernel.at(i).getParksWidth(),
1111 m_filterKernel.at(i).getSamplingFrequency(),
1112 FilterKernel::m_designMethods.indexOf(m_filterKernel.at(i).getDesignMethod()));
1113
1114 tempFilterList.append(tempFilter);
1115 }
1116
1117 //Generate QList structure which can be handled by the QConcurrent framework
1118 QList<QPair<QList<FilterKernel>, QPair<int, RowVectorXd>>> timeData;
1119 QList<int> notFilterChannelIndex;
1120
1121 //Also append mirrored data in front and back to get rid of edge effects
1122 for (qint32 i = 0; i < m_matDataRaw.rows(); ++i) {
1123 if (m_filterChannelList.contains(m_pFiffInfo->chs.at(i).ch_name)) {
1124 RowVectorXd datTemp(m_matDataRaw.row(i).cols() + 2 * m_iMaxFilterLength);
1125 datTemp << m_matDataRaw.row(i).head(m_iMaxFilterLength).reverse(), m_matDataRaw.row(i), m_matDataRaw.row(i).tail(m_iMaxFilterLength).reverse();
1126 timeData.append(QPair<QList<FilterKernel>, QPair<int, RowVectorXd>>(tempFilterList, QPair<int, RowVectorXd>(i, datTemp)));
1127 } else {
1128 notFilterChannelIndex.append(i);
1129 }
1130 }
1131
1132 //Do the concurrent filtering
1133 if (!timeData.isEmpty()) {
1134 QFuture<void> future = QtConcurrent::map(timeData,
1135 doFilterPerChannelRTMSA);
1136
1137 future.waitForFinished();
1138
1139 for (int r = 0; r < timeData.size(); ++r) {
1140 m_matDataFiltered.row(timeData.at(r).second.first) = timeData.at(r).second.second.segment(m_iMaxFilterLength + m_iMaxFilterLength / 2, m_matDataRaw.cols());
1141 m_matOverlap.row(timeData.at(r).second.first) = timeData.at(r).second.second.tail(m_iMaxFilterLength);
1142 }
1143 }
1144
1145 //Fill filtered data with raw data if the channel was not filtered
1146 for (int i = 0; i < notFilterChannelIndex.size(); ++i) {
1147 m_matDataFiltered.row(notFilterChannelIndex.at(i)) = m_matDataRaw.row(notFilterChannelIndex.at(i));
1148 }
1149
1150 if (!m_bIsFreezed) {
1151 m_vecLastBlockFirstValuesFiltered = m_matDataFiltered.col(0);
1152 }
1153
1154 //std::cout<<"END RtFiffRawViewModel::filterDataBlock"<<std::endl;
1155}
1156
1157//=============================================================================================================
1158
1159void RtFiffRawViewModel::filterDataBlock(const MatrixXd& data, int iDataIndex)
1160{
1161 if (data.cols() < m_iMaxFilterLength) {
1162 return;
1163 }
1164
1165 //Generate QList structure which can be handled by the QConcurrent framework
1166 QList<QPair<QList<FilterKernel>, QPair<int, RowVectorXd>>> timeData;
1167 QList<int> notFilterChannelIndex;
1168
1169 for (qint32 i = 0; i < data.rows(); ++i) {
1170 if (m_filterChannelList.contains(m_pFiffInfo->chs.at(i).ch_name)) {
1171 timeData.append(QPair<QList<FilterKernel>, QPair<int, RowVectorXd>>(m_filterKernel, QPair<int, RowVectorXd>(i, data.row(i))));
1172 } else {
1173 notFilterChannelIndex.append(i);
1174 }
1175 }
1176
1177 const int iMaxCols = static_cast<int>(m_matDataFiltered.cols());
1178 const int nCol = static_cast<int>(data.cols());
1179 const int iFilterDelay = m_iMaxFilterLength / 2;
1180
1181 //Do the concurrent filtering
1182 if (!timeData.isEmpty()) {
1183 QFuture<void> future = QtConcurrent::map(timeData,
1184 doFilterPerChannelRTMSA);
1185
1186 future.waitForFinished();
1187
1188 //Overlap-add: output sample k of this block is convolution sample iDataIndex + k, drawn iFilterDelay earlier
1189 for (int r = 0; r < timeData.size(); ++r) {
1190 const int iRow = timeData.at(r).second.first;
1191 RowVectorXd tempData = timeData.at(r).second.second;
1192
1193 if (m_bDrawFilterFront) {
1194 tempData.head(m_iMaxFilterLength) += m_matOverlap.row(iRow);
1195 forWrappedSpan(iDataIndex - iFilterDelay, nCol, iMaxCols, [&](int iCol, int iOffset, int iLength) {
1196 m_matDataFiltered.row(iRow).segment(iCol, iLength) = tempData.segment(iOffset, iLength);
1197 });
1198 } else {
1199 //The overlap belongs to the previous filter, so skip the start-up transient of the new one
1200 forWrappedSpan(iDataIndex - iFilterDelay, m_iMaxFilterLength, iMaxCols, [&](int iCol, int, int iLength) {
1201 m_matDataFiltered.row(iRow).segment(iCol, iLength).setZero();
1202 });
1203 forWrappedSpan(iDataIndex - iFilterDelay + m_iMaxFilterLength, nCol - m_iMaxFilterLength, iMaxCols, [&](int iCol, int iOffset, int iLength) {
1204 m_matDataFiltered.row(iRow).segment(iCol, iLength) = tempData.segment(m_iMaxFilterLength + iOffset, iLength);
1205 });
1206 }
1207
1208 m_matOverlap.row(iRow) = tempData.tail(m_iMaxFilterLength);
1209 }
1210 }
1211
1212 m_bDrawFilterFront = true;
1213
1214 //Fill filtered data with raw data if the channel was not filtered
1215 forWrappedSpan(iDataIndex, nCol, iMaxCols, [&](int iCol, int iOffset, int iLength) {
1216 for (int i : std::as_const(notFilterChannelIndex)) {
1217 m_matDataFiltered.row(i).segment(iCol, iLength) = data.row(i).segment(iOffset, iLength);
1218 }
1219 });
1220}
1221
1222//=============================================================================================================
1223
1224void RtFiffRawViewModel::clearModel()
1225{
1226 beginResetModel();
1227
1228 m_matDataRaw.setZero();
1229 m_matDataFiltered.setZero();
1230 m_matDataRawFreeze.setZero();
1231 m_matDataFilteredFreeze.setZero();
1232 m_vecLastBlockFirstValuesFiltered.setZero();
1233 m_vecLastBlockFirstValuesRaw.setZero();
1234 m_matOverlap.setZero();
1235
1236 endResetModel();
1237}
1238
1239//=============================================================================================================
1240
1242{
1243 double dMaxValue;
1244 qint32 kind = getKind(row);
1245
1246 switch (kind) {
1247 case FIFFV_MEG_CH: {
1248 dMaxValue = 1e-11f;
1249 qint32 unit = getUnit(row);
1250 if (unit == FIFF_UNIT_T_M) { //gradiometers
1251 dMaxValue = 1e-10f;
1252 if (getScaling().contains(FIFF_UNIT_T_M))
1253 dMaxValue = getScaling()[FIFF_UNIT_T_M];
1254 } else if (unit == FIFF_UNIT_T) //magnetometers
1255 {
1256 dMaxValue = 1e-11f;
1257 if (getScaling().contains(FIFF_UNIT_T))
1258 dMaxValue = getScaling()[FIFF_UNIT_T];
1259 }
1260 break;
1261 }
1262
1263 case FIFFV_REF_MEG_CH: {
1264 dMaxValue = 1e-11f;
1265 if (getScaling().contains(FIFF_UNIT_T))
1266 dMaxValue = getScaling()[FIFF_UNIT_T];
1267 break;
1268 }
1269 case FIFFV_EEG_CH: {
1270 dMaxValue = 1e-4f;
1271 if (getScaling().contains(FIFFV_EEG_CH))
1272 dMaxValue = getScaling()[FIFFV_EEG_CH];
1273 break;
1274 }
1275 case FIFFV_EOG_CH: {
1276 dMaxValue = 1e-3f;
1277 if (getScaling().contains(FIFFV_EOG_CH))
1278 dMaxValue = getScaling()[FIFFV_EOG_CH];
1279 break;
1280 }
1281 case FIFFV_STIM_CH: {
1282 dMaxValue = 5;
1283 if (getScaling().contains(FIFFV_STIM_CH))
1284 dMaxValue = getScaling()[FIFFV_STIM_CH];
1285 break;
1286 }
1287 case FIFFV_MISC_CH: {
1288 dMaxValue = 1e-3f;
1289 if (getScaling().contains(FIFFV_MISC_CH))
1290 dMaxValue = getScaling()[FIFFV_MISC_CH];
1291 break;
1292 }
1293 default:
1294 dMaxValue = 1e-9f;
1295 break;
1296 }
1297
1298 return dMaxValue;
1299}
1300
1301//=============================================================================================================
1302
1304{
1305 if (m_fnAddEvent) {
1306 m_fnAddEvent(iSample);
1307 }
1308}
1309
1310//=============================================================================================================
1311
1313{
1314 if (iColumn < 0 || iColumn >= m_iMaxSamples) {
1315 return -1;
1316 }
1317
1318 // Left of the write position is the current sweep, from it on the previous one
1319 const int iWritePosition = m_bIsFreezed ? m_iCurrentSampleFreeze : m_iCurrentSample;
1320 const int iSample = getFirstSampleOffset() + iColumn - (iColumn < iWritePosition ? 0 : m_iMaxSamples);
1321
1322 return iSample >= 0 ? iSample : -1;
1323}
1324
1325//=============================================================================================================
1326
1327double RtFiffRawViewModel::getValueAtColumn(int iRow, int iColumn) const
1328{
1329 const RowVectorPair rowData = data(index(iRow, 1)).value<RowVectorPair>();
1330 if (!rowData.first || iColumn < 0 || iColumn >= rowData.second) {
1331 return 0.0;
1332 }
1333
1334 return rowData.first[iColumn];
1335}
1336
1337//=============================================================================================================
1338
1339std::vector<int> RtFiffRawViewModel::getEventsToDisplay(int iBegin, int iEnd) const
1340{
1341 if (m_fnGetEventSamples) {
1342 return m_fnGetEventSamples(iBegin, iEnd);
1343 }
1344 return {};
1345}
1346
1347//=============================================================================================================
1348
1349void RtFiffRawViewModel::setEventCallbacks(std::function<void(int)> addFn,
1350 std::function<std::vector<int>(int, int)> getFn)
1351{
1352 m_fnAddEvent = std::move(addFn);
1353 m_fnGetEventSamples = std::move(getFn);
1354}
#define FIFFV_EOG_CH
#define FIFFV_EEG_CH
#define FIFF_UNIT_NONE
#define FIFFV_REF_MEG_CH
#define FIFFV_MISC_CH
#define FIFFV_MEG_CH
#define FIFFV_STIM_CH
#define FIFF_UNIT_T
#define FIFFV_EMG_CH
#define FIFFV_ECG_CH
#define FIFF_UNIT_T_M
#define FIFFV_COIL_NONE
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
Primitive scalar typedefs and forward-compatible aliases backing the FIFF type system.
Header-only Eigen matrix text I/O — round-trips dense matrices to whitespace-separated ASCII for cros...
Circular-buffer QAbstractTableModel feeding the real-time FIFF raw browser.
SPatial HARmonic Analysis (SPHARA) spatial-filter projector assembly.
Threshold and edge-based trigger detection on streaming stim channels.
General numerical helpers: GCD, log2, histogram binning, baseline rescaling, sparsity tests.
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
2-D display widgets and visualisation helpers (charts, topography, colour maps).
Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > MatrixXdR
QPair< const double *, qint32 > RowVectorPair
DSPSHARED_EXPORT QMap< int, QList< QPair< int, double > > > detectTriggerFlanksMax(const Eigen::MatrixXd &data, const QList< int > &lTriggerChannels, int iOffsetIndex, double dThreshold, bool bRemoveOffset, int iBurstLengthSamp=100)
DSPSHARED_EXPORT Eigen::MatrixXd filterData(const Eigen::MatrixXd &matData, int type, double dCenterfreq, double dBandwidth, double dTransition, double dSFreq, int iOrder=1024, int designMethod=UTILSLIB::FilterKernel::m_designMethods.indexOf(UTILSLIB::FilterParameter("Cosine")), const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi(), bool bUseThreads=true, bool bKeepOverhead=false)
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
DSPSHARED_EXPORT Eigen::MatrixXd makeSpharaProjector(const Eigen::MatrixXd &matBaseFct, const Eigen::VectorXi &vecIndices, int iOperatorDim, int iNBaseFct, int iSkip=0)
const QMap< qint32, float > & getScaling() const
void setBackgroundColor(const QColor &color)
void setFilter(QList< UTILSLIB::FilterKernel > filterData)
void selectRows(const QList< qint32 > &selection)
void newSelection(const QList< qint32 > &selection)
FIFFLIB::fiff_int_t getKind(qint32 row) const
QSharedPointer< RtFiffRawViewModel > SPtr
void setSamplingInfo(float sps, int T, bool bSetZero=false)
double getValueAtColumn(int iRow, int iColumn) const
void markChBad(QModelIndex ch, bool status)
RtFiffRawViewModel(QObject *parent=0)
void createFilterChannelList(QStringList channelNames)
void setFilterChannelType(const QString &channelType)
void setFiffInfo(QSharedPointer< FIFFLIB::FiffInfo > &p_pFiffInfo)
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
void toggleFreeze(const QModelIndex &index)
void hideRows(const QList< qint32 > &selection)
void setEventCallbacks(std::function< void(int)> addFn, std::function< std::vector< int >(int, int)> getFn)
void updateSpharaOptions(const QString &sSytemType, int nBaseFctsFirst, int nBaseFctsSecond)
void setScaling(const QMap< qint32, float > &p_qMapChScaling)
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
FIFFLIB::fiff_int_t getCoil(qint32 row) const
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
void triggerDetected(int numberDetectedTriggers, const QMap< int, QList< QPair< int, double > > > &mapDetectedTriggers)
FIFFLIB::fiff_int_t getUnit(qint32 row) const
int getSampleAtColumn(int iColumn) const
std::vector< int > getEventsToDisplay(int iBegin, int iEnd) const
void triggerInfoChanged(const QMap< double, QColor > &colorMap, bool active, QString triggerCh, double threshold)
void updateProjection(const QList< FIFFLIB::FiffProj > &projs)
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
void addData(const QList< Eigen::MatrixXd > &data)
double getMaxValueFromRawViewModel(int row) const
static QVector< FilterParameter > m_designMethods
static QVector< FilterParameter > m_filterTypes
One CTF software-gradient compensation matrix: grade kind, calibration flag and the gradiometer × ref...
FiffNamedMatrix::SDPtr data
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Definition fiff_info.h:90
static Eigen::RowVectorXi pick_channels(const QStringList &ch_names, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList)
static double log2(const T d)
Definition numerics.h:217
static bool read_eigen_matrix(Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &out, const QString &path)
Definition ioutils.h:283