MNE-CPP  beta 1.0
tmsi.cpp
Go to the documentation of this file.
1 //=============================================================================================================
37 //*************************************************************************************************************
38 //=============================================================================================================
39 // INCLUDES
40 //=============================================================================================================
41 
42 #include "tmsi.h"
43 #include "tmsiproducer.h"
44 
45 
46 //*************************************************************************************************************
47 //=============================================================================================================
48 // QT INCLUDES
49 //=============================================================================================================
50 
51 #include <QtCore/QtPlugin>
52 #include <QtCore/QTextStream>
53 #include <QDebug>
54 
55 
56 //*************************************************************************************************************
57 //=============================================================================================================
58 // USED NAMESPACES
59 //=============================================================================================================
60 
61 using namespace TMSIPlugin;
62 
63 
64 //*************************************************************************************************************
65 //=============================================================================================================
66 // DEFINE MEMBER METHODS
67 //=============================================================================================================
68 
70 : m_pRMTSA_TMSI(0)
71 , m_qStringResourcePath(qApp->applicationDirPath()+"/mne_x_plugins/resources/tmsi/")
72 , m_pRawMatrixBuffer_In(0)
73 , m_pTMSIProducer(new TMSIProducer(this))
74 {
75  // Create record file option action bar item/button
76  m_pActionSetupProject = new QAction(QIcon(":/images/database.png"), tr("Setup project"), this);
77  m_pActionSetupProject->setStatusTip(tr("Setup project"));
78  connect(m_pActionSetupProject, &QAction::triggered, this, &TMSI::showSetupProjectDialog);
79  addPluginAction(m_pActionSetupProject);
80 
81  // Create start recordin action bar item/button
82  m_pActionStartRecording = new QAction(QIcon(":/images/record.png"), tr("Start recording data to fif file"), this);
83  m_pActionStartRecording->setStatusTip(tr("Start recording data to fif file"));
84  connect(m_pActionStartRecording, &QAction::triggered, this, &TMSI::showStartRecording);
85  addPluginAction(m_pActionStartRecording);
86 
87  // Create impedance action bar item/button
88  m_pActionImpedance = new QAction(QIcon(":/images/impedances.png"), tr("Check impedance values"), this);
89  m_pActionImpedance->setStatusTip(tr("Check impedance values"));
90  connect(m_pActionImpedance, &QAction::triggered, this, &TMSI::showImpedanceDialog);
91  addPluginAction(m_pActionImpedance);
92 }
93 
94 
95 //*************************************************************************************************************
96 
98 {
99  //std::cout << "TMSI::~TMSI() " << std::endl;
100 
101  //If the program is closed while the sampling is in process
102  if(this->isRunning())
103  this->stop();
104 }
105 
106 
107 //*************************************************************************************************************
108 
109 QSharedPointer<IPlugin> TMSI::clone() const
110 {
111  QSharedPointer<TMSI> pTMSIClone(new TMSI());
112  return pTMSIClone;
113 }
114 
115 
116 //*************************************************************************************************************
117 
119 {
120  m_pRMTSA_TMSI = PluginOutputData<NewRealTimeMultiSampleArray>::create(this, "TMSI", "EEG output data");
121 
122  m_outputConnectors.append(m_pRMTSA_TMSI);
123 
124  //default values used by the setupGUI class must be set here
125  m_iSamplingFreq = 1024;
126  m_iNumberOfChannels = 138;
127  m_iSamplesPerBlock = 16;
128  m_iTriggerInterval = 5000;
129 
130  m_bUseChExponent = true;
131  m_bUseUnitGain = true;
132  m_bUseUnitOffset = true;
133  m_bWriteToFile = false;
134  m_bWriteDriverDebugToFile = false;
135  m_bUseFiltering = false;
136  m_bUseFFT = false;
137  m_bIsRunning = false;
138  m_bBeepTrigger = false;
139  m_bUseCommonAverage = true;
140  m_bUseKeyboardTrigger = false;
141  m_bCheckImpedances = false;
142 
143  m_iTriggerType = 0;
144 
145  QDate date;
146  m_sOutputFilePath = QString ("%1Sequence_01/Subject_01/%2_%3_%4_EEG_001_raw.fif").arg(m_qStringResourcePath).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day());
147 
148  m_sElcFilePath = QString("./mne_x_plugins/resources/tmsi/loc_files/Lorenz-Duke128-28-11-2013.elc");
149 
150  m_pFiffInfo = QSharedPointer<FiffInfo>(new FiffInfo());
151 
152  //Initialise matrix used to perform a very simple high pass filter operation
153  m_matOldMatrix = MatrixXf::Zero(m_iNumberOfChannels, m_iSamplesPerBlock);
154 }
155 
156 
157 //*************************************************************************************************************
158 
160 {
161 
162 }
163 
164 
165 //*************************************************************************************************************
166 
168 {
169  // Only works for ANT Neuro Waveguard Duke caps
170  //
171  //Clear old fiff info data
172  //
173  m_pFiffInfo->clear();
174 
175  //
176  //Set number of channels, sampling frequency and high/-lowpass
177  //
178  m_pFiffInfo->nchan = m_iNumberOfChannels;
179  m_pFiffInfo->sfreq = m_iSamplingFreq;
180  m_pFiffInfo->highpass = (float)0.001;
181  m_pFiffInfo->lowpass = m_iSamplingFreq/2;
182 
183  //
184  //Read electrode positions from .elc file
185  //
186  QVector< QVector<double> > elcLocation3D;
187  QVector< QVector<double> > elcLocation2D;
188  QString unit;
189  QStringList elcChannelNames;
190 
191  if(!LayoutLoader::readAsaElcFile(m_sElcFilePath, elcChannelNames, elcLocation3D, elcLocation2D, unit))
192  qDebug() << "Error: Reading elc file.";
193 
194  //qDebug() << elcLocation3D;
195  //qDebug() << elcLocation2D;
196  //qDebug() << elcChannelNames;
197 
198  //The positions read from the asa elc file do not correspond to a RAS coordinate system - use a simple 90° z transformation to fix this
199  Matrix3f rotation_z;
200  rotation_z = AngleAxisf((float)M_PI/2, Vector3f::UnitZ()); //M_PI/2 = 90°
201  QVector3D center_pos;
202 
203  for(int i = 0; i<elcLocation3D.size(); i++)
204  {
205  Vector3f point;
206  point << elcLocation3D[i][0], elcLocation3D[i][1] , elcLocation3D[i][2];
207  Vector3f point_rot = rotation_z * point;
208 // cout<<"point: "<<endl<<point<<endl<<endl;
209 // cout<<"matrix: "<<endl<<rotation_z<<endl<<endl;
210 // cout<<"point_rot: "<<endl<<point_rot<<endl<<endl;
211 // cout<<"-----------------------------"<<endl;
212  elcLocation3D[i][0] = point_rot[0];
213  elcLocation3D[i][1] = point_rot[1];
214  elcLocation3D[i][2] = point_rot[2];
215 
216  //Also calculate the center position of the electrode positions in this for routine
217  center_pos.setX(center_pos.x() + elcLocation3D[i][0]);
218  center_pos.setY(center_pos.y() + elcLocation3D[i][1]);
219  center_pos.setZ(center_pos.z() + elcLocation3D[i][2]);
220  }
221 
222  center_pos.setX(center_pos.x()/elcLocation3D.size());
223  center_pos.setY(center_pos.y()/elcLocation3D.size());
224  center_pos.setZ(center_pos.z()/elcLocation3D.size());
225 
226  //
227  //Write electrode positions to the digitizer info in the fiffinfo
228  //
229  QList<FiffDigPoint> digitizerInfo;
230 
231  //Only write the EEG channel positions to the fiff info. The Refa devices have next to the EEG input channels 10 other input channels (Bipolar, Auxilary, Digital, Test)
232  int numberEEGCh;
233  if(m_iNumberOfChannels>128)
234  numberEEGCh = 138 - (m_iNumberOfChannels-128);
235  else
236  numberEEGCh = m_iNumberOfChannels;
237 
238  //Check if channel size by user corresponds with read channel informations from the elc file. If not append zeros and string 'unknown' until the size matches.
239  if(numberEEGCh > elcLocation3D.size())
240  {
241  qDebug()<<"Warning: setUpFiffInfo() - Not enough positions read from the elc file. Filling missing channel names and positions with zeroes and 'unknown' strings.";
242  QVector<double> tempA(3, 0.0);
243  QVector<double> tempB(2, 0.0);
244  int size = numberEEGCh-elcLocation3D.size();
245  for(int i = 0; i<size; i++)
246  {
247  elcLocation3D.push_back(tempA);
248  elcLocation2D.push_back(tempB);
249  elcChannelNames.append(QString("Unknown"));
250  }
251  }
252 
253  //Append LAP value to digitizer data. Take location of LE2 electrode minus 1 cm as approximation.
254  FiffDigPoint digPoint;
255  int indexLE2 = elcChannelNames.indexOf("LE2");
256  digPoint.kind = FIFFV_POINT_CARDINAL;
257  digPoint.ident = FIFFV_POINT_LPA;//digitizerInfo.size();
258 
259  //Set EEG electrode location - Convert from mm to m
260  if(indexLE2!=-1)
261  {
262  digPoint.r[0] = elcLocation3D[indexLE2][0]*0.001;
263  digPoint.r[1] = elcLocation3D[indexLE2][1]*0.001;
264  digPoint.r[2] = (elcLocation3D[indexLE2][2]-10)*0.001;
265  digitizerInfo.push_back(digPoint);
266  }
267  else
268  cout<<"Plugin TMSI - ERROR - LE2 not found. Check loaded layout."<<endl;
269 
270  //Append nasion value to digitizer data. Take location of Z1 electrode minus 6 cm as approximation.
271  int indexZ1 = elcChannelNames.indexOf("Z1");
272  digPoint.kind = FIFFV_POINT_CARDINAL;//FIFFV_POINT_NASION;
273  digPoint.ident = FIFFV_POINT_NASION;//digitizerInfo.size();
274 
275  //Set EEG electrode location - Convert from mm to m
276  if(indexZ1!=-1)
277  {
278  digPoint.r[0] = elcLocation3D[indexZ1][0]*0.001;
279  digPoint.r[1] = elcLocation3D[indexZ1][1]*0.001;
280  digPoint.r[2] = (elcLocation3D[indexZ1][2]-60)*0.001;
281  digitizerInfo.push_back(digPoint);
282  }
283  else
284  cout<<"Plugin TMSI - ERROR - Z1 not found. Check loaded layout."<<endl;
285 
286  //Append RAP value to digitizer data. Take location of RE2 electrode minus 1 cm as approximation.
287  int indexRE2 = elcChannelNames.indexOf("RE2");
288  digPoint.kind = FIFFV_POINT_CARDINAL;
289  digPoint.ident = FIFFV_POINT_RPA;//digitizerInfo.size();
290 
291  //Set EEG electrode location - Convert from mm to m
292  if(indexRE2!=-1)
293  {
294  digPoint.r[0] = elcLocation3D[indexRE2][0]*0.001;
295  digPoint.r[1] = elcLocation3D[indexRE2][1]*0.001;
296  digPoint.r[2] = (elcLocation3D[indexRE2][2]-10)*0.001;
297  digitizerInfo.push_back(digPoint);
298  }
299  else
300  cout<<"Plugin TMSI - ERROR - RE2 not found. Check loaded layout."<<endl;
301 
302  //Add EEG electrode positions as digitizers
303  for(int i=0; i<numberEEGCh; i++)
304  {
305  FiffDigPoint digPoint;
306  digPoint.kind = FIFFV_POINT_EEG;
307  digPoint.ident = i;
308 
309  //Set EEG electrode location - Convert from mm to m
310  digPoint.r[0] = elcLocation3D[i][0]*0.001;
311  digPoint.r[1] = elcLocation3D[i][1]*0.001;
312  digPoint.r[2] = elcLocation3D[i][2]*0.001;
313  digitizerInfo.push_back(digPoint);
314  }
315 
316  //Set the final digitizer values to the fiff info
317  m_pFiffInfo->dig = digitizerInfo;
318 
319  //
320  //Set up the channel info
321  //
322  QStringList QSLChNames;
323  m_pFiffInfo->chs.clear();
324 
325  for(int i=0; i<m_iNumberOfChannels; i++)
326  {
327  //Create information for each channel
328  QString sChType;
329  FiffChInfo fChInfo;
330 
331  //EEG Channels
332  if(i<=numberEEGCh-1)
333  {
334  //Set channel name
335  //fChInfo.ch_name = elcChannelNames.at(i);
336  sChType = QString("EEG ");
337  if(i<10)
338  sChType.append("00");
339 
340  if(i>=10 && i<100)
341  sChType.append("0");
342 
343  fChInfo.ch_name = sChType.append(sChType.number(i));
344 
345  //Set channel type
346  fChInfo.kind = FIFFV_EEG_CH;
347 
348  //Set coil type
349  fChInfo.coil_type = FIFFV_COIL_EEG;
350 
351  //Set logno
352  fChInfo.logno = i;
353 
354  //Set coord frame
355  fChInfo.coord_frame = FIFFV_COORD_HEAD;
356 
357  //Set unit
358  fChInfo.unit = FIFF_UNIT_V;
359  fChInfo.unit_mul = 0;
360 
361  //Set EEG electrode location - Convert from mm to m
362  fChInfo.eeg_loc(0,0) = elcLocation3D[i][0]*0.001;
363  fChInfo.eeg_loc(1,0) = elcLocation3D[i][1]*0.001;
364  fChInfo.eeg_loc(2,0) = elcLocation3D[i][2]*0.001;
365 
366  //Set EEG electrode direction - Convert from mm to m
367  fChInfo.eeg_loc(0,1) = center_pos.x()*0.001;
368  fChInfo.eeg_loc(1,1) = center_pos.y()*0.001;
369  fChInfo.eeg_loc(2,1) = center_pos.z()*0.001;
370 
371  //Also write the eeg electrode locations into the meg loc variable (mne_ex_read_raw() matlab function wants this)
372  fChInfo.loc(0,0) = elcLocation3D[i][0]*0.001;
373  fChInfo.loc(1,0) = elcLocation3D[i][1]*0.001;
374  fChInfo.loc(2,0) = elcLocation3D[i][2]*0.001;
375 
376  fChInfo.loc(3,0) = center_pos.x()*0.001;
377  fChInfo.loc(4,0) = center_pos.y()*0.001;
378  fChInfo.loc(5,0) = center_pos.z()*0.001;
379 
380  fChInfo.loc(6,0) = 0;
381  fChInfo.loc(7,0) = 1;
382  fChInfo.loc(8,0) = 0;
383 
384  fChInfo.loc(9,0) = 0;
385  fChInfo.loc(10,0) = 0;
386  fChInfo.loc(11,0) = 1;
387 
388  //cout<<i<<endl<<fChInfo.eeg_loc<<endl;
389  }
390 
391  //Bipolar channels
392  if(i>=128 && i<=131)
393  {
394  //Set channel type
395  fChInfo.kind = FIFFV_MISC_CH;
396 
397  sChType = QString("BIPO ");
398  fChInfo.ch_name = sChType.append(sChType.number(i-128));
399  }
400 
401  //Auxilary input channels
402  if(i>=132 && i<=135)
403  {
404  //Set channel type
405  fChInfo.kind = FIFFV_MISC_CH;
406 
407  sChType = QString("AUX ");
408  fChInfo.ch_name = sChType.append(sChType.number(i-132));
409  }
410 
411  //Digital input channel
412  if(i==136)
413  {
414  //Set channel type
415  fChInfo.kind = FIFFV_STIM_CH;
416 
417  sChType = QString("STI 014");
418  fChInfo.ch_name = sChType;
419  }
420 
421  //Internally generated test signal - ramp signal
422  if(i==137)
423  {
424  //Set channel type
425  fChInfo.kind = FIFFV_MISC_CH;
426 
427  sChType = QString("TEST RAMP");
428  fChInfo.ch_name = sChType;
429  }
430 
431  QSLChNames << sChType;
432 
433  m_pFiffInfo->chs.append(fChInfo);
434  }
435 
436  //Set channel names in fiff_info_base
437  m_pFiffInfo->ch_names = QSLChNames;
438 
439  //
440  //Set head projection
441  //
442  m_pFiffInfo->dev_head_t.from = FIFFV_COORD_DEVICE;
443  m_pFiffInfo->dev_head_t.to = FIFFV_COORD_HEAD;
444  m_pFiffInfo->ctf_head_t.from = FIFFV_COORD_DEVICE;
445  m_pFiffInfo->ctf_head_t.to = FIFFV_COORD_HEAD;
446 
447  //
448  //Set projection data
449  //
450  m_pFiffInfo->projs.clear();
451  FiffProj proj;
452  proj.kind = 1;
453  proj.active = false;
454 
455  FiffNamedMatrix::SDPtr namedMatrix = proj.data;
456  namedMatrix->ncol = numberEEGCh/3;
457  namedMatrix->nrow = 1;
458  namedMatrix->data = MatrixXd::Ones(1, namedMatrix->ncol);
459 
460  //Set projection 1
461  for(int i=0; i<namedMatrix->ncol; i++)
462  namedMatrix->col_names << QSLChNames.at(i);
463 
464  proj.data = namedMatrix;
465  proj.desc = QString("PCA-v1");
466  m_pFiffInfo->projs.append(proj);
467 
468  //Set projection 2
469  namedMatrix->col_names.clear();
470  for(int i=0; i<namedMatrix->ncol; i++)
471  namedMatrix->col_names << QSLChNames.at(i+namedMatrix->ncol);
472 
473  proj.data = namedMatrix;
474  proj.desc = QString("PCA-v2");
475  m_pFiffInfo->projs.append(proj);
476 
477  //Set projection 3
478  namedMatrix->col_names.clear();
479  for(int i=0; i<namedMatrix->ncol; i++)
480  namedMatrix->col_names << QSLChNames.at(i+(2*namedMatrix->ncol));
481 
482  proj.data = namedMatrix;
483  proj.desc = QString("PCA-v3");
484  m_pFiffInfo->projs.append(proj);
485 }
486 
487 
488 //*************************************************************************************************************
489 
491 {
492  //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.
493  if(this->isRunning())
494  QThread::wait();
495 
496  if(m_bBeepTrigger)
497  m_qTimerTrigger.start();
498 
499  //Setup fiff info
500  setUpFiffInfo();
501 
502  //Set the channel size of the RMTSA - this needs to be done here and NOT in the init() function because the user can change the number of channels during runtime
503  m_pRMTSA_TMSI->data()->initFromFiffInfo(m_pFiffInfo);
504  m_pRMTSA_TMSI->data()->setMultiArraySize(m_iSamplesPerBlock);
505  m_pRMTSA_TMSI->data()->setSamplingRate(m_iSamplingFreq);
506 
507  //Buffer
508  m_pRawMatrixBuffer_In = QSharedPointer<RawMatrixBuffer>(new RawMatrixBuffer(8, m_iNumberOfChannels, m_iSamplesPerBlock));
509 
510  m_pTMSIProducer->start(m_iNumberOfChannels,
511  m_iSamplingFreq,
512  m_iSamplesPerBlock,
513  m_bUseChExponent,
514  m_bUseUnitGain,
515  m_bUseUnitOffset,
516  m_bWriteDriverDebugToFile,
517  m_sOutputFilePath,
518  m_bUseCommonAverage,
519  m_bCheckImpedances);
520 
521  if(m_pTMSIProducer->isRunning())
522  {
523  // Init BCIFeatureWindow for visualization
524  m_tmsiManualAnnotationWidget = QSharedPointer<TMSIManualAnnotationWidget>(new TMSIManualAnnotationWidget(this));
525 
526  if(m_bUseKeyboardTrigger && !m_bCheckImpedances)
527  {
528  m_tmsiManualAnnotationWidget->initGui();
529  m_tmsiManualAnnotationWidget->show();
530  }
531 
532  m_bIsRunning = true;
533  QThread::start();
534  return true;
535  }
536  else
537  {
538  qWarning() << "Plugin TMSI - ERROR - TMSIProducer thread could not be started - Either the device is turned off (check your OS device manager) or the driver DLL (TMSiSDK.dll / TMSiSDK32bit.dll) is not installed in the system32 / SysWOW64 directory" << endl;
539  return false;
540  }
541 }
542 
543 
544 //*************************************************************************************************************
545 
547 {
548  //Stop the producer thread first
549  m_pTMSIProducer->stop();
550 
551  //Wait until this thread (TMSI) is stopped
552  m_bIsRunning = false;
553 
554  //In case the semaphore blocks the thread -> Release the QSemaphore and let it exit from the pop function (acquire statement)
555  m_pRawMatrixBuffer_In->releaseFromPop();
556 
557  m_pRawMatrixBuffer_In->clear();
558 
559  m_pRMTSA_TMSI->data()->clear();
560 
561  m_tmsiManualAnnotationWidget->hide();
562 
563  return true;
564 }
565 
566 
567 //*************************************************************************************************************
568 
570 {
571  return _ISensor;
572 }
573 
574 
575 //*************************************************************************************************************
576 
577 QString TMSI::getName() const
578 {
579  return "TMSI EEG";
580 }
581 
582 
583 //*************************************************************************************************************
584 
586 {
587  TMSISetupWidget* widget = new TMSISetupWidget(this);//widget is later destroyed by CentralWidget - so it has to be created everytime new
588 
589  //init properties dialog
590  widget->initGui();
591 
592  return widget;
593 }
594 
595 
596 //*************************************************************************************************************
597 
598 void TMSI::setKeyboardTriggerType(int type)
599 {
600  m_qMutex.lock();
601  m_iTriggerType =type;
602  m_qMutex.unlock();
603 }
604 
605 
606 //*************************************************************************************************************
607 
608 void TMSI::run()
609 {
610  while(m_bIsRunning)
611  {
612  //std::cout<<"TMSI::run(s)"<<std::endl;
613 
614  // Check impedances - send new impedance values to graphic scene
615  if(m_pTMSIProducer->isRunning() && m_bCheckImpedances)
616  {
617  MatrixXf matValue = m_pRawMatrixBuffer_In->pop();
618 
619  for(qint32 i = 0; i < matValue.cols(); ++i)
620  m_pTmsiImpedanceWidget->updateGraphicScene(matValue.col(i).cast<double>());
621  }
622 
623  //pop matrix only if the producer thread is running
624  if(m_pTMSIProducer->isRunning() && !m_bCheckImpedances)
625  {
626  MatrixXf matValue = m_pRawMatrixBuffer_In->pop();
627 
628  // Set Beep trigger (if activated)
629  if(m_bBeepTrigger && m_qTimerTrigger.elapsed() >= m_iTriggerInterval)
630  {
631  QtConcurrent::run(Beep, 450, 700);
632  //Set trigger in received data samples - just for one sample, so that this event is easy to detect
633  matValue(136, m_iSamplesPerBlock-1) = 252;
634  m_qTimerTrigger.restart();
635  }
636 
637  // Set keyboard trigger (if activated and !=0)
638  if(m_bUseKeyboardTrigger && m_iTriggerType!=0)
639  matValue(136, m_iSamplesPerBlock-1) = m_iTriggerType;
640 
641  //Write raw data to fif file
642  if(m_bWriteToFile)
643  m_pOutfid->write_raw_buffer(matValue.cast<double>(), m_cals);
644 
645  // TODO: Use preprocessing if wanted by the user
646  if(m_bUseFiltering)
647  {
648  MatrixXf temp = matValue;
649 
650  matValue = matValue - m_matOldMatrix;
651  m_matOldMatrix = temp;
652 
653  // //Check filter class - will be removed in the future - testing purpose only!
654  // FilterTools* filterObject = new FilterTools();
655 
656  // //kaiser window testing
657  // qint32 numberCoeff = 51;
658  // QVector<float> impulseResponse(numberCoeff);
659  // filterObject->createDynamicFilter(QString('LP'), numberCoeff, (float)0.3, impulseResponse);
660 
661  // ofstream outputFileStream("mne_x_plugins/resources/tmsi/filterToolsTest.txt", ios::out);
662 
663  // outputFileStream << "impulseResponse:\n";
664  // for(int i=0; i<impulseResponse.size(); i++)
665  // outputFileStream << impulseResponse[i] << " ";
666  // outputFileStream << endl;
667 
668  // //convolution testing
669  // QVector<float> in (12, 2);
670  // QVector<float> kernel (4, 2);
671 
672  // QVector<float> out = filterObject->convolve(in, kernel);
673 
674  // outputFileStream << "convolution result:\n";
675  // for(int i=0; i<out.size(); i++)
676  // outputFileStream << out[i] << " ";
677  // outputFileStream << endl;
678  }
679 
680  // TODO: Perform a fft if wanted by the user
681  if(m_bUseFFT)
682  {
683  QElapsedTimer timer;
684  timer.start();
685 
686  FFT<float> fft;
687  Matrix<complex<float>, 138, 16> freq;
688 
689  for(qint32 i = 0; i < matValue.rows(); ++i)
690  fft.fwd(freq.row(i), matValue.row(i));
691 
692 // cout<<"FFT postprocessing done in "<<timer.nsecsElapsed()<<" nanosec"<<endl;
693 // cout<<"matValue before FFT:"<<endl<<matValue<<endl;
694 // cout<<"freq after FFT:"<<endl<<freq<<endl;
695 // matValue = freq.cwiseAbs();
696 // cout<<"matValue after FFT:"<<endl<<matValue<<endl;
697  }
698 
699  //Change values of the trigger channel for better plotting - this change is not saved in the produced fif file
700  if(m_iNumberOfChannels>137)
701  {
702  for(int i = 0; i<matValue.row(137).cols(); i++)
703  {
704  // Left keyboard or capacitive
705  if(matValue.row(136)[i] == 254)
706  matValue.row(136)[i] = 4000;
707 
708  // Right keyboard
709  if(matValue.row(136)[i] == 253)
710  matValue.row(136)[i] = 8000;
711 
712  // Beep
713  if(matValue.row(136)[i] == 252)
714  matValue.row(136)[i] = 2000;
715  }
716  }
717 
718  //emit values to real time multi sample array
719  m_pRMTSA_TMSI->data()->setValue(matValue.cast<double>());
720 
721  // Reset keyboard trigger
722  m_iTriggerType = 0;
723  }
724  }
725 
726  //Close the fif output stream
727  if(m_bWriteToFile)
728  {
729  m_pOutfid->finish_writing_raw();
730  m_bWriteToFile = false;
731  m_pTimerRecordingChange->stop();
732  m_pActionStartRecording->setIcon(QIcon(":/images/record.png"));
733  }
734 
735  //std::cout<<"EXITING - TMSI::run()"<<std::endl;
736 }
737 
738 
739 //*************************************************************************************************************
740 
742 {
743  // Open Impedance dialog only if no sampling process is active
744  if(!m_bIsRunning)
745  {
746  if(m_pTmsiImpedanceWidget == NULL)
747  m_pTmsiImpedanceWidget = QSharedPointer<TMSIImpedanceWidget>(new TMSIImpedanceWidget(this));
748 
749  if(!m_pTmsiImpedanceWidget->isVisible())
750  {
751  m_pTmsiImpedanceWidget->setWindowTitle("MNE-X - Measure impedances");
752  m_pTmsiImpedanceWidget->show();
753  m_pTmsiImpedanceWidget->raise();
754  }
755 
756  m_pTmsiImpedanceWidget->initGraphicScene();
757  }
758 }
759 
760 //*************************************************************************************************************
761 
763 {
764  // Open setup project widget
765  if(m_pTmsiSetupProjectWidget == NULL)
766  m_pTmsiSetupProjectWidget = QSharedPointer<TMSISetupProjectWidget>(new TMSISetupProjectWidget(this));
767 
768  if(!m_pTmsiSetupProjectWidget->isVisible())
769  {
770  m_pTmsiSetupProjectWidget->setWindowTitle("TMSI EEG Connector - Setup project");
771  m_pTmsiSetupProjectWidget->initGui();
772  m_pTmsiSetupProjectWidget->show();
773  m_pTmsiSetupProjectWidget->raise();
774  }
775 }
776 
777 //*************************************************************************************************************
778 
780 {
781  //Setup writing to file
782  if(m_bWriteToFile)
783  {
784  m_pOutfid->finish_writing_raw();
785  m_bWriteToFile = false;
786  m_pTimerRecordingChange->stop();
787  m_pActionStartRecording->setIcon(QIcon(":/images/record.png"));
788  }
789  else
790  {
791  if(!m_bIsRunning)
792  {
793  QMessageBox msgBox;
794  msgBox.setText("Start data acquisition first!");
795  msgBox.exec();
796  return;
797  }
798 
799  if(!m_pFiffInfo)
800  {
801  QMessageBox msgBox;
802  msgBox.setText("FiffInfo missing!");
803  msgBox.exec();
804  return;
805  }
806 
807  //Initiate the stream for writing to the fif file
808  m_fileOut.setFileName(m_sOutputFilePath);
809  if(m_fileOut.exists())
810  {
811  QMessageBox msgBox;
812  msgBox.setText("The file you want to write already exists.");
813  msgBox.setInformativeText("Do you want to overwrite this file?");
814  msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
815  int ret = msgBox.exec();
816  if(ret == QMessageBox::No)
817  return;
818  }
819 
820  // Check if path exists -> otherwise create it
821  QStringList list = m_sOutputFilePath.split("/");
822  list.removeLast(); // remove file name
823  QString fileDir = list.join("/");
824 
825  if(!dirExists(fileDir.toStdString()))
826  {
827  QDir dir;
828  dir.mkpath(fileDir);
829  }
830 
831  m_pOutfid = Fiff::start_writing_raw(m_fileOut, *m_pFiffInfo, m_cals);
832  fiff_int_t first = 0;
833  m_pOutfid->write_int(FIFF_FIRST_SAMPLE, &first);
834 
835  m_bWriteToFile = true;
836 
837  m_pTimerRecordingChange = QSharedPointer<QTimer>(new QTimer);
838  connect(m_pTimerRecordingChange.data(), &QTimer::timeout, this, &TMSI::changeRecordingButton);
839  m_pTimerRecordingChange->start(500);
840  }
841 }
842 
843 
844 //*************************************************************************************************************
845 
847 {
848  if(m_iBlinkStatus == 0)
849  {
850  m_pActionStartRecording->setIcon(QIcon(":/images/record.png"));
851  m_iBlinkStatus = 1;
852  }
853  else
854  {
855  m_pActionStartRecording->setIcon(QIcon(":/images/record_active.png"));
856  m_iBlinkStatus = 0;
857  }
858 }
859 
860 
861 //*************************************************************************************************************
862 
863 bool TMSI::dirExists(const std::string& dirName_in)
864 {
865  DWORD ftyp = GetFileAttributesA(dirName_in.c_str());
866  if (ftyp == INVALID_FILE_ATTRIBUTES)
867  return false; //something is wrong with your path!
868 
869  if (ftyp & FILE_ATTRIBUTE_DIRECTORY)
870  return true; // this is a directory!
871 
872  return false; // this is not a directory!
873 }
874 
875 
virtual bool start()
Definition: tmsi.cpp:490
The TMSIManualAnnotationWidget class provides a widget/window for manually annotating the trigger...
The EEGProducer class provides a EEG data producer for a given sampling rate.
Definition: tmsiproducer.h:89
FIFF measurement file information.
Definition: fiff_info.h:96
void showImpedanceDialog()
Definition: tmsi.cpp:741
OutputConnectorList m_outputConnectors
Definition: IPlugin.h:222
virtual QString getName() const
Definition: tmsi.cpp:577
fiff_int_t kind
Definition: fiff_proj.h:164
bool dirExists(const std::string &dirName_in)
Definition: tmsi.cpp:863
Digitization point description.
QSharedDataPointer< FiffNamedMatrix > SDPtr
virtual void run()
Definition: tmsi.cpp:608
virtual bool stop()
Definition: tmsi.cpp:546
virtual IPlugin::PluginType getType() const
Definition: tmsi.cpp:569
The TMSISetupWidget class provides the TMSI configuration window.
virtual void init()
Definition: tmsi.cpp:118
void addPluginAction(QAction *pAction)
Definition: IPlugin.h:249
#define FIFFV_COIL_EEG
FiffNamedMatrix::SDPtr data
Definition: fiff_proj.h:168
void setUpFiffInfo()
Definition: tmsi.cpp:167
SSP projector data.
Definition: fiff_proj.h:89
static bool readAsaElcFile(QString path, QStringList &channelNames, QVector< QVector< double > > &location3D, QVector< QVector< double > > &location2D, QString &unit)
Contains the declaration of the TMSI class.
Channel info descriptor.
Definition: fiff_ch_info.h:87
static FiffStream::SPtr start_writing_raw(QIODevice &p_IODevice, const FiffInfo &info, RowVectorXd &cals, MatrixXi sel=defaultMatrixXi)
Definition: fiff.h:719
void showStartRecording()
Definition: tmsi.cpp:779
fiff_int_t coord_frame
Definition: fiff_ch_info.h:137
static QSharedPointer< PluginOutputData< T > > create(IPlugin *parent, const QString &name, const QString &descr)
void changeRecordingButton()
Definition: tmsi.cpp:846
void showSetupProjectDialog()
Definition: tmsi.cpp:762
The TMSIImpedanceWidget class provides the TMSIImpedanceWidget configuration window.
fiff_int_t coil_type
Definition: fiff_ch_info.h:128
Matrix< double, 3, 2, DontAlign > eeg_loc
Definition: fiff_ch_info.h:136
The TMSISetupProjectWidget class provides the TMSISetupProjectWidget configuration window...
Contains the declaration of the TMSIProducer class.
virtual QWidget * setupWidget()
Definition: tmsi.cpp:585
virtual QSharedPointer< IPlugin > clone() const
Definition: tmsi.cpp:109
Matrix< double, 12, 1, DontAlign > loc
Definition: fiff_ch_info.h:130
virtual ~TMSI()
Definition: tmsi.cpp:97
virtual void unload()
Definition: tmsi.cpp:159