MNE-CPP  beta 0.1
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
sourcelab.cpp
Go to the documentation of this file.
1 //=============================================================================================================
36 //*************************************************************************************************************
37 //=============================================================================================================
38 // INCLUDES
39 //=============================================================================================================
40 
41 #include "sourcelab.h"
42 
44 
45 
46 //*************************************************************************************************************
47 //=============================================================================================================
48 // QT INCLUDES
49 //=============================================================================================================
50 
51 #include <QtCore/QtPlugin>
52 #include <QtConcurrent>
53 #include <QDebug>
54 
55 
56 //*************************************************************************************************************
57 //=============================================================================================================
58 // USED NAMESPACES
59 //=============================================================================================================
60 
61 using namespace SourceLabPlugin;
62 using namespace FIFFLIB;
63 using namespace XMEASLIB;
64 
65 
66 //*************************************************************************************************************
67 //=============================================================================================================
68 // DEFINE MEMBER METHODS
69 //=============================================================================================================
70 
72 : m_bIsRunning(false)
73 , m_bReceiveData(false)
74 , m_bProcessData(false)
75 , m_bFinishedClustering(false)
76 , m_qFileFwdSolution("./MNE-sample-data/MEG/sample/sample_audvis-meg-eeg-oct-6-fwd.fif")
77 , m_sAtlasDir("./MNE-sample-data/subjects/sample/label")
78 , m_sSurfaceDir("./MNE-sample-data/subjects/sample/surf")
79 , m_iNumAverages(10)
80 , m_bSingleTrial(false)
81 , m_iStimChan(0)
82 , m_iDownSample(4)
83 {
84 
85 }
86 
87 
88 //*************************************************************************************************************
89 
91 {
92  if(this->isRunning())
93  stop();
94 }
95 
96 
97 //*************************************************************************************************************
98 
99 QSharedPointer<IPlugin> SourceLab::clone() const
100 {
101  QSharedPointer<SourceLab> pSourceLabClone(new SourceLab());
102  return pSourceLabClone;
103 }
104 
105 
106 //*************************************************************************************************************
107 //=============================================================================================================
108 // Creating required display instances and set configurations
109 //=============================================================================================================
110 
112 {
113  // Inits
114  m_pFwd = MNEForwardSolution::SPtr(new MNEForwardSolution(m_qFileFwdSolution));
115  m_pAnnotationSet = AnnotationSet::SPtr(new AnnotationSet(m_sAtlasDir+"/lh.aparc.a2009s.annot", m_sAtlasDir+"/rh.aparc.a2009s.annot"));
116  m_pSurfaceSet = SurfaceSet::SPtr(new SurfaceSet(m_sSurfaceDir+"/lh.white", m_sSurfaceDir+"/rh.white"));
117 
118  //Delete Buffer - will be initailzed with first incoming data
119  if(!m_pSourceLabBuffer.isNull())
120  m_pSourceLabBuffer = CircularMatrixBuffer<double>::SPtr();
121 
122  // Input
123  m_pRTMSAInput = PluginInputData<NewRealTimeMultiSampleArray>::create(this, "SourceLabIn", "SourceLab input data");
124  connect(m_pRTMSAInput.data(), &PluginInputConnector::notify, this, &SourceLab::update, Qt::DirectConnection);
125  m_inputConnectors.append(m_pRTMSAInput);
126 
127  // Output
128  m_pRTSEOutput = PluginOutputData<RealTimeSourceEstimate>::create(this, "SourceLabOut", "SourceLab output data");
129  m_outputConnectors.append(m_pRTSEOutput);
130  m_pRTSEOutput->data()->setName("Real-Time Source Estimate");
131  m_pRTSEOutput->data()->setAnnotSet(m_pAnnotationSet);
132  m_pRTSEOutput->data()->setSurfSet(m_pSurfaceSet);
133  m_pRTSEOutput->data()->setSamplingRate(600/m_iDownSample);
134 
135  // start clustering
136  QFuture<void> future = QtConcurrent::run(this, &SourceLab::doClustering);
137 
138 }
139 
140 
141 //*************************************************************************************************************
142 
143 void SourceLab::doClustering()
144 {
145  emit clusteringStarted();
146  m_bFinishedClustering = false;
147  m_pClusteredFwd = MNEForwardSolution::SPtr(new MNEForwardSolution(m_pFwd->cluster_forward_solution(*m_pAnnotationSet.data(), 40)));
148 
149  finishedClustering();
150 }
151 
152 
153 //*************************************************************************************************************
154 
155 void SourceLab::finishedClustering()
156 {
157  m_pRTSEOutput->data()->setSrc(m_pClusteredFwd->src);
158 
159  m_bFinishedClustering = true;
160  emit clusteringFinished();
161 }
162 
163 
164 //*************************************************************************************************************
165 
167 {
168  //Check if the thread is already or still running. This can happen if the start button is pressed immediately after the stop button was pressed. In this case the stopping process is not finished yet but the start process is initiated.
169  if(this->isRunning())
170  QThread::wait();
171 
172  if(m_bFinishedClustering)
173  {
174  m_bIsRunning = true;
175  QThread::start();
176  return true;
177  }
178  else
179  return false;
180 }
181 
182 
183 //*************************************************************************************************************
184 
186 {
187  //Check if the thread is already or still running. This can happen if the start button is pressed immediately after the stop button was pressed. In this case the stopping process is not finished yet but the start process is initiated.
188  if(this->isRunning())
189  QThread::wait();
190 
191  m_bIsRunning = false;
192 
193  if(m_bProcessData) // Only clear if buffers have been initialised
194  {
195  m_pSourceLabBuffer->releaseFromPop();
196  m_pSourceLabBuffer->releaseFromPush();
197  }
198 
199  // Stop filling buffers with data from the inputs
200  m_bProcessData = false;
201 
202  if(m_pRtCov->isRunning())
203  m_pRtCov->stop();
204 
205  if(m_pRtInvOp->isRunning())
206  m_pRtInvOp->stop();
207 
208  if(m_pSourceLabBuffer)
209  m_pSourceLabBuffer->clear();
210 
211  m_bReceiveData = false;
212 
213  return true;
214 }
215 
216 
217 //*************************************************************************************************************
218 
220 {
221  return _IAlgorithm;
222 }
223 
224 
225 //*************************************************************************************************************
226 
227 QString SourceLab::getName() const
228 {
229  return "SourceLab";
230 }
231 
232 
233 //*************************************************************************************************************
234 
236 {
237  SourceLabSetupWidget* setupWidget = new SourceLabSetupWidget(this);//widget is later distroyed by CentralWidget - so it has to be created everytime new
238 
239  if(!m_bFinishedClustering)
240  setupWidget->setClusteringState();
241 
244 
245  return setupWidget;
246 }
247 
248 
249 //*************************************************************************************************************
250 
251 void SourceLab::update(XMEASLIB::NewMeasurement::SPtr pMeasurement)
252 {
253  QSharedPointer<NewRealTimeMultiSampleArray> pRTMSA = pMeasurement.dynamicCast<NewRealTimeMultiSampleArray>();
254 
255  //MEG
256  if(pRTMSA && m_bReceiveData)
257  {
258  //Check if buffer initialized
259  if(!m_pSourceLabBuffer)
260  m_pSourceLabBuffer = CircularMatrixBuffer<double>::SPtr(new CircularMatrixBuffer<double>(64, pRTMSA->getNumChannels(), pRTMSA->getMultiArraySize()));
261 
262  //Fiff information
263  if(!m_pFiffInfo)
264  m_pFiffInfo = pRTMSA->getFiffInfo();
265 
266  if(m_bProcessData)
267  {
268  MatrixXd t_mat(pRTMSA->getNumChannels(), pRTMSA->getMultiArraySize());
269 
270  for(unsigned char i = 0; i < pRTMSA->getMultiArraySize(); ++i)
271  t_mat.col(i) = pRTMSA->getMultiSampleArray()[i];
272 
273  m_pSourceLabBuffer->push(&t_mat);
274  }
275  }
276 }
277 
278 
279 //*************************************************************************************************************
280 
282 {
283  if(p_pEvoked->comment == QString("Stim %1").arg(m_iStimChan))
284  {
285  std::cout << p_pEvoked->comment.toLatin1().constData() << " append" << std::endl;
286 
287  mutex.lock();
288  m_qVecEvokedData.push_back(p_pEvoked);
289  mutex.unlock();
290  }
291 }
292 
293 
294 //*************************************************************************************************************
295 
297 {
298  m_pFiffCov = p_pFiffCov;
299 
300  if(m_pRtInvOp)
301  m_pRtInvOp->appendNoiseCov(m_pFiffCov);
302 }
303 
304 
305 //*************************************************************************************************************
306 
308 {
309  m_pInvOp = p_pInvOp;
310 
311  double snr = 3.0;
312  double lambda2 = 1.0 / pow(snr, 2); //ToDO estimate lambda using covariance
313 
314  QString method("dSPM"); //"MNE" | "dSPM" | "sLORETA"
315 
316  mutex.lock();
317  m_pMinimumNorm = MinimumNorm::SPtr(new MinimumNorm(*m_pInvOp.data(), lambda2, method));
318  //
319  // Set up the inverse according to the parameters
320  //
321  m_pMinimumNorm->doInverseSetup(m_iNumAverages,false);
322  mutex.unlock();
323 }
324 
325 
326 //*************************************************************************************************************
327 
329 {
330  //
331  // Cluster forward solution;
332  //
333 // qDebug() << "Start Clustering";
334 // QFuture<MNEForwardSolution> future = QtConcurrent::run(this->m_pFwd.data(), &MNEForwardSolution::cluster_forward_solution, m_annotationSet, 40);
335 // qDebug() << "Run Clustering";
336 // future.waitForFinished();
337 // m_pClusteredFwd = MNEForwardSolution::SPtr(new MNEForwardSolution(future.result()));
338 
339 
340  //Do this already in init
341 // m_pClusteredFwd = MNEForwardSolution::SPtr(new MNEForwardSolution(m_pFwd->cluster_forward_solution(*m_pAnnotationSet.data(), 40)));
342 
343 // m_pRTSEOutput->data()->setSrc(m_pClusteredFwd->src);
344 
345  //
346  // start receiving data
347  //
348  m_bReceiveData = true;
349 
350  //
351  // Read Fiff Info
352  //
353  while(!m_pFiffInfo)
354  msleep(10);// Wait for fiff Info
355 
356  //
357  // Init Real-Time Covariance estimator
358  //
359  m_pRtCov = RtCov::SPtr(new RtCov(5000, m_pFiffInfo));
360  connect(m_pRtCov.data(), &RtCov::covCalculated, this, &SourceLab::updateFiffCov);
361 
362  //
363  // Init Real-Time inverse estimator
364  //
365  m_pRtInvOp = RtInvOp::SPtr(new RtInvOp(m_pFiffInfo, m_pClusteredFwd));
366  connect(m_pRtInvOp.data(), &RtInvOp::invOperatorCalculated, this, &SourceLab::updateInvOp);
367 
368  if(!m_bSingleTrial)
369  {
370  //
371  // Init Real-Time average
372  //
373  m_pRtAve = RtAve::SPtr(new RtAve(m_iNumAverages, 750, 750, m_pFiffInfo));
374  connect(m_pRtAve.data(), &RtAve::evokedStim, this, &SourceLab::appendEvoked);
375  }
376 
377  //
378  // Start the rt helpers
379  //
380  m_pRtCov->start();
381  m_pRtInvOp->start();
382  if(!m_bSingleTrial)
383  m_pRtAve->start();
384 
385  //
386  // start processing data
387  //
388  m_bProcessData = true;
389 
390  qint32 skip_count = 0;
391 
392  while(m_bIsRunning)
393  {
394  qint32 nrows = m_pSourceLabBuffer->rows();
395 
396  if(nrows > 0) // check if init
397  {
398  /* Dispatch the inputs */
399  MatrixXd t_mat = m_pSourceLabBuffer->pop();
400 
401  qDebug() << "received data" << t_mat.rows();
402 
403  //Add to covariance estimation
404  m_pRtCov->append(t_mat);
405 
406  if(m_bSingleTrial)
407  {
408  //Continous Data
409  mutex.lock();
410  if(m_pMinimumNorm && t_mat.cols() > 0)
411  {
412  //
413  // calculate the inverse
414  //
415  MNESourceEstimate sourceEstimate = m_pMinimumNorm->calculateInverse(t_mat, 0, 1/m_pFiffInfo->sfreq);
416 
417  std::cout << "Source Estimated" << std::endl;
418  }
419  mutex.unlock();
420  }
421  else
422  {
423  //Average Data
424  m_pRtAve->append(t_mat);
425 
426  mutex.lock();
427  if(m_pMinimumNorm && m_qVecEvokedData.size() > 0 && skip_count > 2)
428  {
429  FiffEvoked t_fiffEvoked = *m_qVecEvokedData[0].data();
430 
431  float tmin = ((float)t_fiffEvoked.first) / t_fiffEvoked.info.sfreq;
432  float tstep = 1/t_fiffEvoked.info.sfreq;
433 
434  MNESourceEstimate sourceEstimate = m_pMinimumNorm->calculateInverse(t_fiffEvoked.data, tmin, tstep);
435 
436  std::cout << "SourceEstimated:\n" << std::endl;
437  // std::cout << "SourceEstimated:\n" << sourceEstimate.data.block(0,0,10,10) << std::endl;
438 
439  //emit source estimates sample wise
440  for(qint32 i = 0; i < sourceEstimate.data.cols(); i += m_iDownSample)
441  m_pRTSEOutput->data()->setValue(sourceEstimate.data.col(i));
442 
443  m_qVecEvokedData.pop_front();
444 
445  skip_count = 0;
446  }
447  mutex.unlock();
448 
449  ++skip_count;
450  }
451  }
452  }
453 }
virtual IPlugin::PluginType getType() const
Definition: sourcelab.cpp:219
void appendEvoked(FiffEvoked::SPtr p_pEvoked)
Definition: sourcelab.cpp:281
OutputConnectorList m_outputConnectors
Definition: IPlugin.h:232
void updateFiffCov(FiffCov::SPtr p_pFiffCov)
Definition: sourcelab.cpp:296
QSharedPointer< FiffCov > SPtr
Definition: fiff_cov.h:97
Real-time inverse operator estimation.
Definition: rtinvop.h:106
QSharedPointer< MinimumNorm > SPtr
Definition: minimumnorm.h:85
void evokedStim(FIFFLIB::FiffEvoked::SPtr p_pEvokedStim)
void invOperatorCalculated(MNELIB::MNEInverseOperator::SPtr p_pInvOp)
virtual QSharedPointer< IPlugin > clone() const
Definition: sourcelab.cpp:99
Real-time covariance estimation.
Definition: rtcov.h:107
QSharedPointer< MNEForwardSolution > SPtr
QSharedPointer< CircularMatrixBuffer > SPtr
evoked data
Definition: fiff_evoked.h:91
The circular matrix buffer.
void covCalculated(FIFFLIB::FiffCov::SPtr p_pCov)
void push(const Matrix< _Tp, Dynamic, Dynamic > *pMatrix)
Real-time averaging helper.
Definition: rtave.h:110
QSharedPointer< FiffEvoked > SPtr
Definition: fiff_evoked.h:94
Annotation set.
Definition: annotationset.h:96
virtual QString getName() const
Definition: sourcelab.cpp:227
QSharedPointer< SurfaceSet > SPtr
Definition: surfaceset.h:86
void updateInvOp(MNEInverseOperator::SPtr p_pInvOp)
Definition: sourcelab.cpp:307
Matrix< _Tp, Dynamic, Dynamic > pop()
Contains the declaration of the SourceLab class.
QSharedPointer< NewMeasurement > SPtr
QSharedPointer< AnnotationSet > SPtr
Definition: annotationset.h:99
Minimum norm estimation.
Definition: minimumnorm.h:82
static QSharedPointer< PluginOutputData< T > > create(IPlugin *parent, const QString &name, const QString &descr)
QSharedPointer< RtCov > SPtr
Definition: rtcov.h:111
virtual QWidget * setupWidget()
Definition: sourcelab.cpp:235
Contains the declaration of the SourceLabSetupWidget class.
InputConnectorList m_inputConnectors
Definition: IPlugin.h:231
The DummySetupWidget class provides the DummyToolbox configuration window.
A hemisphere set of surfaces.
Definition: surfaceset.h:83
QSharedPointer< RtAve > SPtr
Definition: rtave.h:114
QSharedPointer< MNEInverseOperator > SPtr
static QSharedPointer< PluginInputData< T > > create(IPlugin *parent, const QString &name, const QString &descr)
The RealTimeMultiSampleArrayNew class is the base class of every RealTimeMultiSampleArrayNew Measurem...
QSharedPointer< RtInvOp > SPtr
Definition: rtinvop.h:110