MNE-CPP  beta 1.0
averaging.cpp
Go to the documentation of this file.
1 //=============================================================================================================
36 //*************************************************************************************************************
37 //=============================================================================================================
38 // INCLUDES
39 //=============================================================================================================
40 
41 #include "averaging.h"
43 #include "FormFiles/averagingsettingswidget.h"
44 
45 #include <iostream>
46 #include <time.h>
47 
48 
49 //*************************************************************************************************************
50 //=============================================================================================================
51 // QT INCLUDES
52 //=============================================================================================================
53 
54 #include <QtCore/QtPlugin>
55 #include <QMutexLocker>
56 #include <QSettings>
57 
58 #include <QDebug>
59 
60 
61 //*************************************************************************************************************
62 //=============================================================================================================
63 // USED NAMESPACES
64 //=============================================================================================================
65 
66 using namespace AveragingPlugin;
67 using namespace MNEX;
68 using namespace XMEASLIB;
69 
70 
71 //*************************************************************************************************************
72 //=============================================================================================================
73 // DEFINE MEMBER METHODS
74 //=============================================================================================================
75 
77 : m_pAveragingInput(NULL)
78 //, m_pAveragingOutput(NULL)
79 , m_pAveragingBuffer(CircularMatrixBuffer<double>::SPtr())
80 , m_bIsRunning(false)
81 , m_bProcessData(false)
82 , m_iPreStimSamples(400)
83 , m_iPostStimSamples(750)
84 , m_iNumAverages(10)
85 , m_iStimChan(0)
86 , m_pAveragingWidget(AveragingSettingsWidget::SPtr())
87 , m_pActionShowAdjustment(Q_NULLPTR)
88 #ifdef DEBUG_AVERAGING
89 , m_iTestCount(0)
90 , m_iTestCount2(0)
91 #endif
92 {
93  m_pActionShowAdjustment = new QAction(QIcon(":/images/averagingadjustments.png"), tr("Averaging Adjustments"),this);
94 // m_pActionSetupProject->setShortcut(tr("F12"));
95  m_pActionShowAdjustment->setStatusTip(tr("Averaging Adjustments"));
96  connect(m_pActionShowAdjustment, &QAction::triggered, this, &Averaging::showAveragingWidget);
97  addPluginAction(m_pActionShowAdjustment);
98 
99  m_pActionShowAdjustment->setVisible(false);
100 }
101 
102 
103 //*************************************************************************************************************
104 
106 {
107  if(this->isRunning())
108  stop();
109 }
110 
111 
112 //*************************************************************************************************************
113 
114 QSharedPointer<IPlugin> Averaging::clone() const
115 {
116  QSharedPointer<Averaging> pAveragingClone(new Averaging);
117  return pAveragingClone;
118 }
119 
120 
121 //*************************************************************************************************************
122 //=============================================================================================================
123 // Creating required display instances and set configurations
124 //=============================================================================================================
125 
127 {
128  //
129  // Load Settings
130  //
131  QSettings settings;
132  m_iPreStimSamples = settings.value(QString("Plugin/%1/preStimSamples").arg(this->getName()), 400).toInt();
133  m_iPostStimSamples = settings.value(QString("Plugin/%1/postStimSamples").arg(this->getName()), 750).toInt();
134  m_iNumAverages = settings.value(QString("Plugin/%1/numAverages").arg(this->getName()), 10).toInt();
135  m_iStimChan = settings.value(QString("Plugin/%1/stimChannel").arg(this->getName()), 0).toInt();
136 
137  // Input
138  m_pAveragingInput = PluginInputData<NewRealTimeMultiSampleArray>::create(this, "AveragingIn", "Averaging input data");
139  connect(m_pAveragingInput.data(), &PluginInputConnector::notify, this, &Averaging::update, Qt::DirectConnection);
140  m_inputConnectors.append(m_pAveragingInput);
141 
142  // Output
143  m_pAveragingOutput = PluginOutputData<RealTimeEvoked>::create(this, "AveragingOut", "Averaging Output Data");
144  m_pAveragingOutput->data()->setName(this->getName());//Provide name to auto store widget settings
145  m_outputConnectors.append(m_pAveragingOutput);
146 
147  //init channels when fiff info is available
148  connect(this, &Averaging::fiffInfoAvailable, this, &Averaging::initConnector);
149 
150  //Delete Buffer - will be initailzed with first incoming data
151  if(!m_pAveragingBuffer.isNull())
152  m_pAveragingBuffer = CircularMatrixBuffer<double>::SPtr();
153 }
154 
155 
156 //*************************************************************************************************************
157 
159 {
160  //
161  // Store Settings
162  //
163  QSettings settings;
164  settings.setValue(QString("Plugin/%1/preStimSamples").arg(this->getName()), m_iPreStimSamples);
165  settings.setValue(QString("Plugin/%1/postStimSamples").arg(this->getName()), m_iPostStimSamples);
166  settings.setValue(QString("Plugin/%1/numAverages").arg(this->getName()), m_iNumAverages);
167  settings.setValue(QString("Plugin/%1/stimChannel").arg(this->getName()), m_iStimChan);
168 }
169 
170 
171 //*************************************************************************************************************
172 
173 void Averaging::changeNumAverages(qint32 numAve)
174 {
175  QMutexLocker locker(&m_qMutex);
176  m_iNumAverages = numAve;
177  if(m_pRtAve)
178  m_pRtAve->setAverages(numAve);
179 }
180 
181 
182 //*************************************************************************************************************
183 
184 void Averaging::initConnector()
185 {
186 // if(m_pFiffInfo)
187 // {
188 
189 // }
190 }
191 
192 
193 //*************************************************************************************************************
194 
196 {
197  //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.
198  if(this->isRunning())
199  QThread::wait();
200 
201  m_qMutex.lock();
202  m_bIsRunning = true;
203  m_qMutex.unlock();
204 
205  // Start threads
206  QThread::start();
207 
208  return true;
209 }
210 
211 
212 //*************************************************************************************************************
213 
215 {
216  //Wait until this thread is stopped
217  m_qMutex.lock();
218  m_bIsRunning = false;
219 
220  if(m_bProcessData)
221  {
222  //In case the semaphore blocks the thread -> Release the QSemaphore and let it exit from the pop function (acquire statement)
223  m_pAveragingBuffer->releaseFromPop();
224  m_pAveragingBuffer->releaseFromPush();
225 
226  m_pAveragingBuffer->clear();
227 
228 // m_pRTMSAOutput->data()->clear();
229  }
230  m_qMutex.unlock();
231 
232  return true;
233 }
234 
235 
236 //*************************************************************************************************************
237 
239 {
240  return _IAlgorithm;
241 }
242 
243 
244 //*************************************************************************************************************
245 
246 QString Averaging::getName() const
247 {
248  return "Averaging";
249 }
250 
251 
252 //*************************************************************************************************************
253 
254 void Averaging::changeStimChannel(qint32 index)
255 {
256  Q_UNUSED(index)
257  QMutexLocker locker(&m_qMutex);
258  m_iStimChan = m_pAveragingWidget->m_pComboBoxChSelection->currentData().toInt();
259 // qDebug() << "Averaging::changeStimChannel(qint32 index)" << m_pAveragingWidget->m_pComboBoxChSelection->currentData().toInt();
260 }
261 
262 //*************************************************************************************************************
263 
264 void Averaging::changePreStim(qint32 samples)
265 {
266  QMutexLocker locker(&m_qMutex);
267  m_iPreStimSamples = samples;
268  if(m_pRtAve)
269  m_pRtAve->setPreStim(m_iPreStimSamples);
270 
271 }
272 
273 
274 //*************************************************************************************************************
275 
276 void Averaging::changePostStim(qint32 samples)
277 {
278  QMutexLocker locker(&m_qMutex);
279  m_iPostStimSamples = samples;
280  if(m_pRtAve)
281  m_pRtAve->setPostStim(m_iPostStimSamples);
282 }
283 
284 
285 //*************************************************************************************************************
286 
288 {
289  AveragingSetupWidget* setupWidget = new AveragingSetupWidget(this);//widget is later distroyed by CentralWidget - so it has to be created everytime new
290  return setupWidget;
291 }
292 
293 
294 //*************************************************************************************************************
295 
296 void Averaging::showAveragingWidget()
297 {
298  QMutexLocker locker(&m_qMutex);
299  m_pAveragingWidget = AveragingSettingsWidget::SPtr(new AveragingSettingsWidget(this));
300  m_pAveragingWidget->show();
301 }
302 
303 
304 //*************************************************************************************************************
305 
306 void Averaging::update(XMEASLIB::NewMeasurement::SPtr pMeasurement)
307 {
308  QSharedPointer<NewRealTimeMultiSampleArray> pRTMSA = pMeasurement.dynamicCast<NewRealTimeMultiSampleArray>();
309 
310  if(pRTMSA)
311  {
312  //Check if buffer initialized
313  if(!m_pAveragingBuffer)
314  m_pAveragingBuffer = CircularMatrixBuffer<double>::SPtr(new CircularMatrixBuffer<double>(64, pRTMSA->getNumChannels(), pRTMSA->getMultiSampleArray()[0].cols()));
315 
316  //Fiff information
317  if(!m_pFiffInfo)
318  {
319  m_pFiffInfo = pRTMSA->info();
320  emit fiffInfoAvailable();
321 
322 #ifdef DEBUG_AVERAGING
323  for(qint32 i = 0; i < m_pFiffInfo->nchan; ++i)
324  {
325  if(m_pFiffInfo->chs[i].kind == FIFFV_STIM_CH)
326  {
327  m_iTestStimCh = i;
328  break;
329  }
330  }
331 #endif
332  }
333 
334 
335  if(m_bProcessData)
336  {
337  for(qint32 i = 0; i < pRTMSA->getMultiSampleArray().size(); ++i)
338  {
339  MatrixXd t_mat = pRTMSA->getMultiSampleArray()[i];
340 
341 #ifdef DEBUG_AVERAGING
342  qsrand(time(NULL)+m_iTestCount);
343 
344  t_mat = MatrixXd::Zero(t_mat.rows(), t_mat.cols());
345 
346  if(m_iTestCount%10 == 0)//GEN test stim
347  {
348  qint32 samp = (qrand() % (t_mat.cols()/8))+1; //exclude buggy 0
349  if(m_iTestCount2 % 5 == 0) // create zero every 5 generations
350  samp = 0;
351  RowVectorXd stim = RowVectorXd::Ones(8)*5;
352  t_mat.block(m_iTestStimCh,samp,1,8) = stim;
353 
354  t_mat.block(0,samp+1,m_iTestStimCh, t_mat.cols()-(samp+1)) = MatrixXd::Ones(m_iTestStimCh, t_mat.cols()-(samp+1));
355 
356  qDebug() << "Pos:" << samp;
357  ++m_iTestCount2;
358  }
359  ++m_iTestCount;
360 #endif
361  m_pAveragingBuffer->push(&t_mat);
362  }
363  }
364  }
365 }
366 
367 
368 //*************************************************************************************************************
369 
370 void Averaging::appendEvoked(FiffEvoked::SPtr p_pEvoked)
371 {
372 // qDebug() << "void Averaging::appendEvoked";// << p_pEvoked->comment;
373 // qDebug() << p_pEvoked->comment;
374  QString t_sStimulusChannel = m_pFiffInfo->chs[m_qListStimChs[m_iStimChan]].ch_name;
375 
376  if(p_pEvoked->comment == t_sStimulusChannel)
377  {
378 // qDebug()<< "append" << p_pEvoked->comment << "=" << t_sStimulusChannel;
379  m_qMutex.lock();
380  m_qVecEvokedData.push_back(p_pEvoked);
381  m_qMutex.unlock();
382 // qDebug() << "append after" << m_qVecEvokedData.size();
383  }
384 }
385 
386 
387 //*************************************************************************************************************
388 
390 {
391  qDebug() << "START void Averaging::run()";
392  //
393  // Read Fiff Info
394  //
395  while(!m_pFiffInfo)
396  msleep(10);// Wait for fiff Info
397 
398  m_pActionShowAdjustment->setVisible(true);
399 
400  for(qint32 i = 0; i < m_pFiffInfo->chs.size(); ++i)
401  {
402  if(m_pFiffInfo->chs[i].kind == FIFFV_STIM_CH)
403  {
404  qDebug() << "Stim" << i << "Name" << m_pFiffInfo->chs[i].ch_name;
405  m_qListStimChs.append(i);
406  }
407  }
408 
409  m_qMutex.lock();
410  m_bProcessData = true;
411  m_qMutex.unlock();
412 
413  //
414  // Init Real-Time average
415  //
416  m_pRtAve = RtAve::SPtr(new RtAve(m_iNumAverages, m_iPreStimSamples, m_iPostStimSamples, m_pFiffInfo));
417  connect(m_pRtAve.data(), &RtAve::evokedStim, this, &Averaging::appendEvoked);
418 
419  m_pRtAve->start();
420 
421  while(true)
422  {
423  {
424  QMutexLocker locker(&m_qMutex);
425  if(!m_bIsRunning)
426  break;
427  }
428 
429  bool doProcessing = false;
430  {
431  QMutexLocker locker(&m_qMutex);
432  doProcessing = m_bProcessData;
433  }
434 
435  if(doProcessing)
436  {
437  /* Dispatch the inputs */
438  MatrixXd rawSegment = m_pAveragingBuffer->pop();
439 
440  m_pRtAve->append(rawSegment);
441 
442  m_qMutex.lock();
443  if(m_qVecEvokedData.size() > 0)
444  {
445  FiffEvoked t_fiffEvoked = *m_qVecEvokedData[0].data();
446 
447 #ifdef DEBUG_AVERAGING
448  std::cout << "EVK:" << t_fiffEvoked.data.row(0) << std::endl;
449 #endif
450  m_pAveragingOutput->data()->setValue(t_fiffEvoked);
451 
452  m_qVecEvokedData.pop_front();
453 
454  }
455  m_qMutex.unlock();
456 
457  }
458  }
459 
460  m_pActionShowAdjustment->setVisible(false);
461 
462  m_pRtAve->stop();
463 }
QSharedPointer< AveragingSettingsWidget > SPtr
QSharedPointer< IAlgorithm > SPtr
Definition: IAlgorithm.h:74
virtual QWidget * setupWidget()
Definition: averaging.cpp:287
OutputConnectorList m_outputConnectors
Definition: IPlugin.h:222
void evokedStim(FIFFLIB::FiffEvoked::SPtr p_pEvokedStim)
The AveragingSetupWidget class provides the AveragingToolbox configuration window.
The Averaging class provides a Averaging algorithm structure.
Definition: averaging.h:109
virtual IPlugin::PluginType getType() const
Definition: averaging.cpp:238
QSharedPointer< CircularMatrixBuffer > SPtr
void addPluginAction(QAction *pAction)
Definition: IPlugin.h:249
virtual QSharedPointer< IPlugin > clone() const
Definition: averaging.cpp:114
evoked data
Definition: fiff_evoked.h:91
virtual QString getName() const
Definition: averaging.cpp:246
The circular matrix buffer.
void push(const Matrix< _Tp, Dynamic, Dynamic > *pMatrix)
Real-time averaging helper.
Definition: rtave.h:109
QSharedPointer< FiffEvoked > SPtr
Definition: fiff_evoked.h:94
Definition: arrow.h:75
Matrix< _Tp, Dynamic, Dynamic > pop()
QSharedPointer< NewMeasurement > SPtr
Contains the declaration of the Averaging class.
static QSharedPointer< PluginOutputData< T > > create(IPlugin *parent, const QString &name, const QString &descr)
void changeNumAverages(qint32 numAve)
Definition: averaging.cpp:173
Contains the declaration of the AveragingSetupWidget class.
InputConnectorList m_inputConnectors
Definition: IPlugin.h:221
QSharedPointer< RtAve > SPtr
Definition: rtave.h:113
static QSharedPointer< PluginInputData< T > > create(IPlugin *parent, const QString &name, const QString &descr)
The RealTimeMultiSampleArrayNew class is the base class of every RealTimeMultiSampleArrayNew Measurem...