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