50 #include <QtCore/QtPlugin>
51 #include <QtCore/QTextStream>
70 : m_qStringResourcePath(qApp->applicationDirPath()+
"/mne_x_plugins/resources/bci/")
71 , m_bProcessData(false)
92 QSharedPointer<BCI> pTMSIClone(
new BCI());
101 m_bIsRunning =
false;
102 m_bTriggerActivated =
false;
106 connect(m_pRTSEInput.data(), &PluginInputConnector::notify,
this, &
BCI::updateSource, Qt::DirectConnection);
110 connect(m_pRTMSAInput.data(), &PluginInputConnector::notify,
this, &
BCI::updateSensor, Qt::DirectConnection);
115 m_pBCIOutputOne->data()->setArraySize(1);
116 m_pBCIOutputOne->data()->setName(
"Boundary");
120 m_pBCIOutputTwo->data()->setArraySize(1);
121 m_pBCIOutputTwo->data()->setName(
"Left electrode var");
125 m_pBCIOutputThree->data()->setArraySize(1);
126 m_pBCIOutputThree->data()->setName(
"Right electrode var");
130 m_pBCIOutputFour->data()->setArraySize(1);
131 m_pBCIOutputFour->data()->setName(
"Left electrode");
135 m_pBCIOutputFive->data()->setArraySize(1);
136 m_pBCIOutputFive->data()->setName(
"Right electrode");
147 m_bSubtractMean =
true;
149 m_bUseSensorData =
true;
150 m_bUseSourceData =
false;
151 m_bDisplayFeatures =
true;
152 m_bUseArtefactThresholdReduction =
false;
153 m_dSlidingWindowSize = 0.5;
154 m_dTimeBetweenWindows = 0.5;
155 m_dDisplayRangeBoundary = 15;
156 m_dDisplayRangeVariances = 5;
157 m_dDisplayRangeElectrodes = 7;
158 m_iNumberFeatures = 1;
159 m_dThresholdValue = 30;
160 m_iNumberFeaturesToDisplay = 30;
161 m_iFeatureCalculationType = 0;
164 m_slChosenFeatureSensor <<
"LA4" <<
"RA4";
167 m_bFillSensorWindowFirstTime =
true;
170 m_filterOperator = QSharedPointer<FilterData>(
new FilterData());
172 m_dFilterLowerBound = 7.0;
173 m_dFilterUpperBound = 14.0;
174 m_dParcksWidth = m_dFilterLowerBound-1;
175 m_iFilterOrder = 256;
178 m_BCIFeatureWindow = QSharedPointer<BCIFeatureWindow>(
new BCIFeatureWindow(
this));
195 if(m_slChosenFeatureSensor.size()%2 != 0)
197 if(!m_slChosenFeatureSensor.contains(
"TEST"))
200 msgBox.setText(
"The number of selected electrodes needs to be even.");
201 msgBox.setInformativeText(
"Please select feautres again");
202 msgBox.setStandardButtons(QMessageBox::Ok);
209 m_iTBWIndexSensor = 0;
210 m_iNumberOfCalculatedFeatures = 0;
213 if(m_bDisplayFeatures)
215 m_BCIFeatureWindow->initGui();
216 m_BCIFeatureWindow->show();
220 QString path(
"BCIDebugFile.txt");
221 path.prepend(m_qStringResourcePath);
222 m_outStreamDebug.open(path.toStdString(), ios::trunc);
226 m_bFillSensorWindowFirstTime =
true;
245 m_pBCIOutputOne->data()->setMaxValue(5);
246 m_pBCIOutputOne->data()->setMinValue(-5);
248 m_pBCIOutputTwo->data()->setMaxValue(10);
249 m_pBCIOutputTwo->data()->setMinValue(0);
251 m_pBCIOutputThree->data()->setMaxValue(10);
252 m_pBCIOutputThree->data()->setMinValue(0);
254 m_pBCIOutputFour->data()->setMaxValue(1e-05);
255 m_pBCIOutputFour->data()->setMinValue(-1e-05);
257 m_pBCIOutputFive->data()->setMaxValue(1e-05);
258 m_pBCIOutputFive->data()->setMinValue(-1e-05);
288 m_bIsRunning =
false;
302 m_bProcessData =
false;
305 m_bTriggerActivated =
false;
308 m_outStreamDebug.close();
309 m_outStreamDebug.clear();
312 if(m_bDisplayFeatures)
313 m_BCIFeatureWindow->hide();
360 if(!m_pBCIBuffer_Sensor)
364 if(!m_pFiffInfo_Sensor)
366 m_pFiffInfo_Sensor = pRTMSA->getFiffInfo();
369 int arraySize = pRTMSA->getMultiArraySize();
370 int modulo = int(m_pFiffInfo_Sensor->sfreq*m_dSlidingWindowSize) % arraySize;
371 int rows = m_slChosenFeatureSensor.size();
372 int cols = m_pFiffInfo_Sensor->sfreq*m_dSlidingWindowSize-modulo;
373 m_matSlidingWindowSensor.resize(rows, cols);
375 m_matStimChannelSensor.resize(1,cols);
377 modulo = int(m_pFiffInfo_Sensor->sfreq*m_dTimeBetweenWindows) % arraySize;
378 rows = m_slChosenFeatureSensor.size();
379 cols = m_pFiffInfo_Sensor->sfreq*m_dTimeBetweenWindows-modulo;
380 m_matTimeBetweenWindowsSensor.resize(rows, cols);
382 m_matTimeBetweenWindowsStimSensor.resize(1,cols);
385 double dCenterFreqNyq = (m_dFilterLowerBound+((m_dFilterUpperBound - m_dFilterLowerBound)/2))/(m_pFiffInfo_Sensor->sfreq/2);
386 double dBandwidthNyq = (m_dFilterUpperBound - m_dFilterLowerBound)/(m_pFiffInfo_Sensor->sfreq/2);
387 double dParksWidth = m_dParcksWidth/(m_pFiffInfo_Sensor->sfreq/2);
394 m_filterOperator = QSharedPointer<FilterData>(
new FilterData(QString(
"BPF"),FilterData::BPF,m_iFilterOrder,dCenterFreqNyq,dBandwidthNyq,dParksWidth,m_matSlidingWindowSensor.cols()+m_iFilterOrder));
397 for(
int i = 0; i<m_filterOperator->m_dCoeffA.cols(); i++)
398 m_outStreamDebug << m_filterOperator->m_dFFTCoeffA(0,i).real() <<
"+" << m_filterOperator->m_dFFTCoeffA(0,i).imag() <<
"i " << endl;
400 m_outStreamDebug << endl << endl;
402 for(
int i = 0; i<m_filterOperator->m_dCoeffA.cols(); i++)
403 m_outStreamDebug << m_filterOperator->m_dCoeffA(0,i) << endl;
405 m_outStreamDebug <<
"---------------------------------------------------------------------" << endl;
411 MatrixXd t_mat(pRTMSA->getNumChannels(), pRTMSA->getMultiArraySize());
413 for(
unsigned char i = 0; i < pRTMSA->getMultiArraySize(); ++i)
414 t_mat.col(i) = pRTMSA->getMultiSampleArray()[i];
416 m_pBCIBuffer_Sensor->
push(&t_mat);
426 cout<<
"update source"<<endl;
431 if(!m_pBCIBuffer_Source)
436 MatrixXd t_mat(pRTSE->getValue().size(), pRTSE->getArraySize());
438 for(
unsigned char i = 0; i < pRTSE->getArraySize(); ++i)
439 t_mat.col(i) = pRTSE->getStc().data.col(i);
441 m_pBCIBuffer_Source->
push(&t_mat);
451 chdata.second = chdata.second.array() - chdata.second.mean();
459 chdata.second = m_filterOperator->applyFFTFilter(chdata.second);
467 RowVectorXd data = chdata.second;
468 QList<double> features;
471 switch(m_iFeatureCalculationType)
474 features << data.squaredNorm();
477 features << abs(log10(data.squaredNorm()));
480 features << data.squaredNorm();
484 return QPair< int,QList<double> >(chdata.first, features);
500 double return_val = 0;
502 if(featData.size() == m_vLoadedSensorBoundary[1].size())
504 VectorXd feat_temp(featData.size());
505 for(
int i = 0; i<featData.size(); i++)
506 feat_temp(i) = featData.at(i);
508 return_val = m_vLoadedSensorBoundary[0](0) + m_vLoadedSensorBoundary[1].dot(feat_temp);
520 m_lFeaturesSensor.clear();
530 m_lClassResultsSensor.clear();
543 if(m_bUseArtefactThresholdReduction)
546 for(
int i = 0; i<data.size(); i++)
548 if(data.at(i).second.maxCoeff() > max)
549 max = data.at(i).second.maxCoeff();
551 if(data.at(i).second.minCoeff() < min)
552 min = data.at(i).second.minCoeff();
559 if(max<m_dThresholdValue*1e-06 && min>m_dThresholdValue*-1e-06)
574 for(
int i = 0; i<data.cols()-1; i++)
576 if(data(0,i) == 254 && data(0,i+1) == 254)
605 while(!m_pFiffInfo_Sensor)
609 m_bProcessData =
true;
612 if(m_bFillSensorWindowFirstTime)
614 if(m_iTBWIndexSensor < m_matSlidingWindowSensor.cols())
617 MatrixXd t_mat = m_pBCIBuffer_Sensor->
pop();
621 for(
int i = 0; i < m_matSlidingWindowSensor.rows(); i++)
622 m_matSlidingWindowSensor.block(i, m_iTBWIndexSensor, 1, t_mat.cols()) = t_mat.block(m_mapElectrodePinningScheme[m_slChosenFeatureSensor.at(i)], 0, 1, t_mat.cols());
624 m_matStimChannelSensor.block(0, m_iTBWIndexSensor, 1, t_mat.cols()) = t_mat.block(136, 0, 1, t_mat.cols());
626 m_iTBWIndexSensor = m_iTBWIndexSensor + t_mat.cols();
630 m_iTBWIndexSensor = 0;
631 m_bFillSensorWindowFirstTime =
false;
636 if(m_iTBWIndexSensor < m_matTimeBetweenWindowsSensor.cols())
639 MatrixXd t_mat = m_pBCIBuffer_Sensor->
pop();
643 for(
int i = 0; i < m_matTimeBetweenWindowsSensor.rows(); i++)
644 m_matTimeBetweenWindowsSensor.block(i, m_iTBWIndexSensor, 1, t_mat.cols()) = t_mat.block(m_mapElectrodePinningScheme[m_slChosenFeatureSensor.at(i)], 0, 1, t_mat.cols());
646 m_matTimeBetweenWindowsStimSensor.block(0, m_iTBWIndexSensor, 1, t_mat.cols()) = t_mat.block(136, 0, 1, t_mat.cols());
648 m_iTBWIndexSensor = m_iTBWIndexSensor + t_mat.cols();
655 m_matSlidingWindowSensor.block(0, 0, m_matSlidingWindowSensor.rows(), m_matSlidingWindowSensor.cols()-m_matTimeBetweenWindowsSensor.cols()) = m_matSlidingWindowSensor.block(0, m_matTimeBetweenWindowsSensor.cols(), m_matSlidingWindowSensor.rows(), m_matSlidingWindowSensor.cols()-m_matTimeBetweenWindowsSensor.cols()).eval();
656 m_matStimChannelSensor.block(0, 0, m_matStimChannelSensor.rows(), m_matStimChannelSensor.cols()-m_matTimeBetweenWindowsStimSensor.cols()) = m_matStimChannelSensor.block(0, m_matTimeBetweenWindowsStimSensor.cols(), m_matStimChannelSensor.rows(), m_matStimChannelSensor.cols()-m_matTimeBetweenWindowsStimSensor.cols()).eval();
659 m_matSlidingWindowSensor.block(0, m_matSlidingWindowSensor.cols()-m_matTimeBetweenWindowsSensor.cols(), m_matTimeBetweenWindowsSensor.rows(), m_matTimeBetweenWindowsSensor.cols()) = m_matTimeBetweenWindowsSensor;
660 m_matStimChannelSensor.block(0, m_matStimChannelSensor.cols()-m_matTimeBetweenWindowsStimSensor.cols(), m_matTimeBetweenWindowsStimSensor.rows(), m_matTimeBetweenWindowsStimSensor.cols()) = m_matTimeBetweenWindowsStimSensor;
665 if(m_slChosenFeatureSensor.contains(
"TEST"))
667 cout<<
"Recalculate matrix"<<endl;
669 for(
int i = 0; i<m_matSlidingWindowSensor.cols() ; i++)
670 cout << m_matSlidingWindowSensor(m_matSlidingWindowSensor.rows()-1,i) <<endl;
682 QList< QPair<int,RowVectorXd> > qlMatrixRows;
683 for(
int i = 0; i< m_matSlidingWindowSensor.rows(); i++)
684 qlMatrixRows << QPair<int,RowVectorXd>(i, m_matSlidingWindowSensor.row(i));
686 int iNumberOfFeatures = qlMatrixRows.size();
692 QFuture<void> futureMean = QtConcurrent::map(qlMatrixRows,[
this](QPair<int,RowVectorXd>& rowdata) {
696 futureMean.waitForFinished();
704 if(
lookForTrigger(m_matStimChannelSensor) && !m_bTriggerActivated)
708 m_bTriggerActivated =
true;
714 QList< QPair<int,RowVectorXd> > filteredRows = qlMatrixRows;
718 QFuture<void> futureFilter = QtConcurrent::map(filteredRows,[
this](QPair<int,RowVectorXd>& chdata) {
722 futureFilter.waitForFinished();
742 std::function< QPair<int,QList<double> > (QPair<int,RowVectorXd>&)> applyOpsFeatures = [
this](QPair<int,RowVectorXd>& chdata) -> QPair< int,QList<double> > {
746 QFuture< QPair< int,QList<double> > > futureCalculatedFeatures = QtConcurrent::mapped(filteredRows.begin(), filteredRows.end(), applyOpsFeatures);
748 futureCalculatedFeatures.waitForFinished();
750 m_iNumberOfCalculatedFeatures++;
754 m_lFeaturesSensor.append(futureCalculatedFeatures.results());
758 if(m_iNumberOfCalculatedFeatures == m_iNumberFeatures)
761 QList< QList<double> > lFeaturesSensor_new;
763 for(
int i = 0; i<m_lFeaturesSensor.size()-iNumberOfFeatures+1; i = i + iNumberOfFeatures)
764 for(
int z = 0; z<m_lFeaturesSensor.at(0).second.size(); z++)
767 for(
int t = 0; t<iNumberOfFeatures; t++)
768 temp.append(m_lFeaturesSensor.at(i+t).second.at(z));
769 lFeaturesSensor_new.append(temp);
778 if(m_bDisplayFeatures)
779 emit paintFeatures((MyQList)lFeaturesSensor_new, m_bTriggerActivated);
782 m_bTriggerActivated =
false;
787 std::function<double (QList<double>&)> applyOpsClassification = [
this](QList<double>& featData){
791 QFuture<double> futureClassificationResults = QtConcurrent::mapped(lFeaturesSensor_new.begin(), lFeaturesSensor_new.end(), applyOpsClassification);
793 futureClassificationResults.waitForFinished();
797 double dfinalResult = 0;
799 for(
int i = 0; i<futureClassificationResults.resultCount() ;i++)
800 dfinalResult += futureClassificationResults.resultAt(i);
802 dfinalResult = dfinalResult/futureClassificationResults.resultCount();
803 cout <<
"dfinalResult: " << dfinalResult << endl << endl;
807 m_lClassResultsSensor.append(dfinalResult);
811 VectorXd variances(iNumberOfFeatures);
814 for(
int i = 0; i<lFeaturesSensor_new.size(); i++)
815 for(
int t = 0; t<iNumberOfFeatures; t++)
816 variances(t) = variances(t) + lFeaturesSensor_new.at(i).at(t);
818 variances = variances/lFeaturesSensor_new.size();
820 m_pBCIOutputOne->data()->setValue(dfinalResult);
821 m_pBCIOutputTwo->data()->setValue(variances(0));
822 m_pBCIOutputThree->data()->setValue(variances(1));
824 for(
int i = 0; i<filteredRows.at(0).second.cols() ; i++)
826 m_pBCIOutputFour->data()->setValue(filteredRows.at(0).second(0,i));
827 m_pBCIOutputFive->data()->setValue(filteredRows.at(1).second(0,i));
834 m_iNumberOfCalculatedFeatures = 0;
840 m_pBCIOutputOne->data()->setValue(0);
841 m_pBCIOutputTwo->data()->setValue(0);
842 m_pBCIOutputThree->data()->setValue(0);
844 QList< QPair<int,RowVectorXd> > filteredRows = qlMatrixRows;
848 QFuture<void> futureFilter = QtConcurrent::map(filteredRows,[
this](QPair<int,RowVectorXd>& chdata) {
852 futureFilter.waitForFinished();
855 for(
int i = 0; i<filteredRows.at(0).second.cols() ; i++)
857 m_pBCIOutputFour->data()->setValue(filteredRows.at(0).second(0,i));
858 m_pBCIOutputFive->data()->setValue(filteredRows.at(1).second(0,i));
862 m_iTBWIndexSensor = 0;
double classificationBoundaryValue(const QList< double > &featData)
virtual IPlugin::PluginType getType() const
QPair< int, QList< double > > applyFeatureCalcConcurrentlyOnSensorLevel(const QPair< int, RowVectorXd > &chdata)
void applyFilterOperatorConcurrently(QPair< int, RowVectorXd > &chdata)
OutputConnectorList m_outputConnectors
The BCIFeatureWindow class provides a visualization tool for calculated features. ...
QSharedPointer< CircularMatrixBuffer > SPtr
void updateSource(XMEASLIB::NewMeasurement::SPtr pMeasurement)
QSharedPointer< FiffInfo > SPtr
double applyClassificationCalcConcurrentlyOnSensorLevel(QList< double > &featData)
void push(const Matrix< _Tp, Dynamic, Dynamic > *pMatrix)
bool lookForTrigger(const MatrixXd &data)
void applyMeanCorrectionConcurrently(QPair< int, RowVectorXd > &rowdata)
Matrix< _Tp, Dynamic, Dynamic > pop()
bool hasThresholdArtefact(const QList< QPair< int, RowVectorXd > > &data)
virtual QWidget * setupWidget()
Real-time source estimate measurement.
QSharedPointer< NewMeasurement > SPtr
virtual QString getName() const
void clearClassifications()
virtual QSharedPointer< IPlugin > clone() const
static QSharedPointer< PluginOutputData< T > > create(IPlugin *parent, const QString &name, const QString &descr)
Contains the declaration of the BCI class.
InputConnectorList m_inputConnectors
void updateSensor(XMEASLIB::NewMeasurement::SPtr pMeasurement)
The RealTimeMultiSampleArrayNew class is the base class of every RealTimeMultiSampleArrayNew Measurem...