BioSignalPi  v2
samplingthread.cpp
Go to the documentation of this file.
1 #include "samplingthread.h"
2 #include "ecgcapture.h"
3 #include "settingssingleton.h"
4 #include <QVector>
5 #include <QPointF>
6 #include <QDebug>
7 #include <QFile>
8 #include <QSettings>
9 #include <QCoreApplication>
10 //#include <qwt_system_clock.h>
11 #include <QtMath>
12 #include "ecgstreamobject.h"
13 //#include "edflib.h"
14 //#include "settings.h"
15 
16 SamplingThread::SamplingThread(DataStream& inputStream):channelId(1), DeviceInterface(inputStream)
17 {
18  privateSamples = new QVector<QVector<QPointF> >;
19  //sampleData = new QVector<QVector<double> >;
20  ecg = new EcgCapture();
21  fileName = "ECG";
22  fileType = "text";
23 }
24 
25 
27  delete sampleData;
28  delete ecg;
29  delete privateSamples;
30 }
31 
32 
33 
35 {
36  clock.start();
37  prevTime = clock.elapsed();
38  emit updateStatus(QString("Collecting samples..."));
39  startThread();
40  while(!stopThread) {
41 
42  double elapsed = clock.elapsed();
43  sample(elapsed, stream);
44 
45  //If sampling is too fast the sampling thread will need to wait
46  //The wait interval is depending on the sampling frequency
47  const double usec = (sleepInterval-(clock.elapsed()-elapsed))*1000;
48 
49  if (usec>1) {
50  usleep(usec);
51  }
52 
53  }
54 
55 
56 
57 
58 
59 
60 }
61 
63 {
64  stopThread = true;
65 }
66 
68 {
69 
70  QString iniPath = qApp->applicationDirPath() + "/settings.ini";
71  QSettings settings(iniPath, QSettings::IniFormat);
72 
74  sourceId = SettingsSingleton::instance().getSourceId(); //settings.value("/ECGCapture/Source", 0).toInt();
75  sampleRate =SettingsSingleton::instance().getSampleRate(); //settings.value("/ECGCapture/SampleRate", 0).toInt();
76  counter = 0;
77  counterII=0;
78  sleepInterval = 0.0;
79 
80  qDebug() << "SourceID:" << sourceId << endl;
81 
84 
85  switch (sourceId) {
86  case 0:
87  source = EcgCapture::ecgCapture;
88  break;
89  case 1:
91  break;
92  case 2:
94  break;
95  case 3:
97  break;
98  default:
99  source = EcgCapture::ecgCapture;
100  break;
101  }
102 
103  switch (sampleRate) {
104  case 0:
105  freq = EcgCapture::lowFreq;
106  sleepInterval = 1.70;
107  Fs = 500;
108  break;
109  case 1:
110  freq = EcgCapture::midFreq;
111  sleepInterval = 0.70;
112  Fs = 1000;
113  break;
114  case 2:
115  freq = EcgCapture::highFreq;
116  sleepInterval = 0.0;
117  Fs = 2000;
118  break;
119  default:
120  freq = EcgCapture::midFreq;
121  sleepInterval = 0.70;
122  Fs = 1000;
123  break;
124  }
125 
126  ecg->init(source, freq);
127  ecg->start();
128 }
129 
130 
132 {
133  privateSamples->clear();
134 }
135 
136 void SamplingThread::setChannel(int channel)
137 {
138 
139  channelId = channel;
140 }
141 
143 {
144  sourceId = source;
145 }
146 
147 void SamplingThread::setFileName(QString fname)
148 {
149  if (fname != "") {
150  fileName = fname;
151  }
152 }
153 
154 void SamplingThread::setFileType(QString ftype)
155 {
156  if (ftype == "text") {
157  fileType = "text";
158  } else if (ftype == "bdf") {
159  fileType = "bdf";
160  }
161 }
162 
167 void SamplingThread::saveAsText()
168 {
169  /* QFile outFileHeader(fileName+"_header.txt");
170 
171  if (!outFileHeader.open(QIODevice::WriteOnly | QIODevice::Text)) {
172  emit updateStatus("Failed to open data file for write!");
173  } else {
174  QTextStream outHeader(&outFileHeader);
175 
176  outHeader << "[ECG CAPTURE SETTINGS]\n"
177  << "Filename: " << Settings::instance().getFilename() << ".txt \n"
178  << "Samples: " << QString::number(sampleData->length()) << "\n"
179  << "Duration: " << QString::number(sampleData->last().at(0)) << "ms \n"
180  << "Sample rate: " << QString::number(Settings::instance().getSampleRate()) << " Hz\n"
181  << "Source: " << Settings::instance().getSource() << "\n"
182  << "\n[RECORDING INFORMATION]\n"
183  << "Recording name: " << Settings::instance().getRecordingName() << "\n"
184  << "Patient code: " << Settings::instance().getPatientCode() << "\n"
185  << "Name: " << Settings::instance().getName() << "\n"
186  << "Gender: " << Settings::instance().getGender() << "\n"
187  << "Birthdate: " << Settings::instance().getBirthDate() << "\n"
188  << "Notes: " << Settings::instance().getNotes() << "\n";
189 
190  outFileHeader.close();
191 
192  }
193 
194  QFile outFile(fileName+".txt");
195 
196  if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
197  emit updateStatus("Failed to open data file for write!");
198  } else {
199  QTextStream out(&outFile);
200 
201  emit updateStatus(QString("Writing " + QString::number(sampleData->length()) + " samples to " + fileName + "..."));
202 
203  for (int ii =0; ii < sampleData->length(); ii++) {
204  out << QString::number(sampleData->at(ii).at(0)) << "\t"
205  << QString::number(sampleData->at(ii).at(1)) << "\t"
206  << QString::number(sampleData->at(ii).at(2)) << "\t"
207  << QString::number(sampleData->at(ii).at(3)) << "\t"
208  << QString::number(sampleData->at(ii).at(4)) << "\n";
209  }
210 
211  emit updateStatus(QString("Samples saved in text format"));
212 
213  outFile.close();
214  }
215  */
216 }
217 
222 void SamplingThread::saveAsBDF()
223 { /*
224  emit updateStatus(QString("Saving as BDF file..."));
225 
226  int nseconds = static_cast<int>(sampleData->last().at(0)/1000);
227  int smpfreq = Fs;
228 
229  if (sampleData->length() < nseconds*smpfreq)
230  {
231  emit updateStatus("Error: too many samples missing!");
232  } else {
233  int i, j, k,
234  hdl,
235  chns;
236 
237  double buf[smpfreq];
238 
239  //QString tmp = fileName+".bdf";
240  QString tmp = fileName+".edf";
241 
242  //Convert the filename to the correct format
243  const char* fname = tmp.toLocal8Bit().data();
244 
245  chns = 4;
246 
247  //Open the file for write
248  //hdl = edfopen_file_writeonly(fname, EDFLIB_FILETYPE_BDFPLUS, chns);
249  hdl = edfopen_file_writeonly(fname, EDFLIB_FILETYPE_EDFPLUS, chns);
250 
251  //All channels have the same sampling frequency
252  for (i=0; i<chns; i++) {
253  edf_set_samplefrequency(hdl, i, smpfreq);
254  }
255 
256 // for (i=0; i<chns; i++) {
257 // edf_set_physical_maximum(hdl, i, 3.0);
258 // }
259 
260 // for (i=0; i<chns; i++) {
261 // edf_set_digital_maximum(hdl, i, 8388607);
262 // }
263 
264 // for (i=0; i<chns; i++) {
265 // edf_set_digital_minimum(hdl, i, -8388607);
266 // }
267 
268 // for (i=0; i<chns; i++) {
269 // edf_set_physical_minimum(hdl, i, -3.0);
270 // }
271 
272  for (i=0; i<chns; i++) {
273  edf_set_physical_maximum(hdl, i, 1);
274  }
275 
276  for (i=0; i<chns; i++) {
277  edf_set_digital_maximum(hdl, i, 32767);
278  }
279 
280  for (i=0; i<chns; i++) {
281  edf_set_digital_minimum(hdl, i, -32768);
282  }
283 
284  for (i=0; i<chns; i++) {
285  edf_set_physical_minimum(hdl, i, -1);
286  }
287 
288  edf_set_label(hdl, 0, "Lead I");
289  edf_set_label(hdl, 1, "Lead II");
290  edf_set_label(hdl, 2, "Lead III");
291  edf_set_label(hdl, 3, "Respiration");
292 
293  for (i=0; i<chns; i++) {
294  edf_set_physical_dimension(hdl, i, "V");
295  }
296 
297  //Data is written in blocks of 1s each
298  for (j=0; j<nseconds; j++) {
299  for (k=1; k<=chns; k++) {
300  for (i=0; i<smpfreq; i++) {
301  buf[i] = sampleData->at(i+(j*smpfreq)).at(k);
302  }
303  edfwrite_physical_samples(hdl, buf);
304  }
305  }
306 
307  edfclose_file(hdl);
308 
309  emit updateStatus(QString("Samples saved in BDF format..."));
310  }
311  */
312 }
313 
314 
325 void SamplingThread::sample(double elapsed,DataStream& inputStream){
326 
327  // FRAME FORMAT:
328  //ELAPSED (ms) | LEAD 1 | LEAD 2 | LEAD 3 | RESPIRATION | LEAD OFF DETECTION
329  //Lead off: 1 = leads disconnected, 0 = leads connected
330 
331  QVector<double> frame;
332  frame.append(elapsed);
333  frame << ecg->readFrame();
334  //double y = frame.at(channelId);
335  static double lead1, lead2, lead3, respiration;
336  static bool userNotified = false;
337 
338  if (sourceId == 0) {
339  //If lead is disconnected, notify
340  if (frame[5] == 1) {
341  if (!userNotified) {
342  userNotified = true;
343  emit updateStatus("Leads disconnected");
344  }
345  }
346  }
347 
348  lead1 += frame.at(1);
349  lead2 += frame.at(2);
350  lead3 += frame.at(3);
351  respiration += frame.at(4);
352 
353  inputStream.push_back(EcgStreamObject(frame.at(1), frame.at(2),frame.at(3), frame.at(4),elapsed/1000));
354 
355  counter++;
356 
357 
358  if (counter >= 10) {
359  QVector<QPointF> tmp;
360  tmp << QPointF(elapsed/1000,lead1/10) << QPointF(elapsed/1000,lead2/10) << QPointF(elapsed/1000,lead3/10) << QPointF(elapsed/1000, respiration/10);
361 
362  privateSamples->append(tmp);
363  tmp.clear();
364 
365  counter = 0;
366  lead1 = 0.0;
367  lead2 = 0.0;
368  lead3 = 0.0;
369  respiration = 0.0;
370  }
371 
372  if (privateSamples->size() >= 20) {
373  QVector<QVector<QPointF> > sampleVector = *privateSamples;
374 
375  privateSamples->clear();
376  emit sendSampleVector(sampleVector);
377 
378  }
379 
380 }
381 
382 
388  clock.start();
389  prevTime = clock.elapsed();
390  startThread();
391  for (int ii=0; ii<30000 ;ii++) {
392 
393  double elapsed = clock.elapsed();
394  sample(elapsed, inputStream);
395 
396  //If sampling is too fast the sampling thread will need to wait
397  //The wait interval is depending on the sampling frequency
398  const double usec = (sleepInterval-(clock.elapsed()-elapsed))*1000;
399 
400  if (usec>1) {
401  usleep(usec);
402  }
403 
404  }
405 
406 }
407 
414  return true;
415 }
421 
422 }
const QVector< double > readFrame()
Reads a single frame.
Definition: ecgcapture.cpp:400
void setChannel(int)
int getSampleRate()
Returns a int containing the value from private int sampleRate.
virtual void run()
void sendSampleVector(QVector< QVector< QPointF > >)
void setFileName(QString)
void setFileType(QString)
Responsible for communication with ADAS.
Definition: ecgcapture.h:24
void start()
Start capturing frames from the ADAS1000.
Definition: ecgcapture.cpp:353
virtual void getData(DataStream &)
Populates DataStream vector with values.
SamplingThread(DataStream &inputStream)
static SettingsSingleton & instance()
Retrives a reference to the ONE and ONLY SettingsSingleton created for the application.
QString getFileName()
Returns a QString containing the value from private QString fileName.
void init(OperatingMode, Frequency)
Initiate the device by configuring the registers depending on operating mode and sampling frequency...
Definition: ecgcapture.cpp:26
void updateStatus(QString)
Abstract Interface that should be used for storing data in the memory.
Definition: datastream.h:16
virtual void close()
Closes the connection to bcm2835.
DataStream & stream
virtual bool connected()
Boolean for check whether the device is connected<.
Struct to store Biosignal data from a specific time, combined with DataStream it will store a sequenc...
int getSourceId()
Returns a int containing the value from private int sourceId.
Abstract interface for all devices.
virtual void stop()
virtual ~SamplingThread()