v2.0.0
Loading...
Searching...
No Matches
fiff_events.cpp
Go to the documentation of this file.
1//=============================================================================================================
36
37//=============================================================================================================
38// INCLUDES
39//=============================================================================================================
40
41#include "fiff_events.h"
42#include "fiff_evoked_set.h"
43#include "fiff_raw_data.h"
44#include "fiff_stream.h"
45#include "fiff_dir_node.h"
46#include "fiff_tag.h"
47#include "fiff_constants.h"
48#include "fiff_file.h"
49
50//=============================================================================================================
51// QT INCLUDES
52//=============================================================================================================
53
54#include <QFile>
55#include <QDebug>
56#include <QTextStream>
57
58//=============================================================================================================
59// USED NAMESPACES
60//=============================================================================================================
61
62using namespace FIFFLIB;
63using namespace Eigen;
64
65//=============================================================================================================
66// DEFINE MEMBER METHODS
67//=============================================================================================================
68
72
73//=============================================================================================================
74
75FiffEvents::FiffEvents(QIODevice &p_IODevice)
76{
77 // Try FIFF first, then ASCII
78 if (!read_from_fif(p_IODevice, *this)) {
79 read_from_ascii(p_IODevice, *this);
80 }
81}
82
83//=============================================================================================================
84
85bool FiffEvents::read(const QString &t_sEventName,
86 const QString &t_fileRawName,
87 FiffEvents &p_Events)
88{
89 QString eventName = t_sEventName;
90 QFile t_EventFile;
91 qint32 p;
92
93 if (eventName.isEmpty()) {
94 eventName = t_fileRawName;
95 p = eventName.indexOf(".fif");
96 if (p > 0) {
97 eventName.replace(p, 4, "-eve.fif");
98 } else {
99 qWarning("Raw file name does not end properly\n");
100 return false;
101 }
102
103 t_EventFile.setFileName(eventName);
104 if(!read_from_fif(t_EventFile, p_Events)) {
105 qWarning("Error while read events.\n");
106 return false;
107 }
108 qInfo("Events read from %s\n",eventName.toUtf8().constData());
109 } else {
110 // Binary file
111 if (eventName.contains(".fif")) {
112 t_EventFile.setFileName(eventName);
113 if(!read_from_fif(t_EventFile, p_Events)) {
114 qWarning("Error while read events.\n");
115 return false;
116 }
117 qInfo("Binary event file %s read\n",eventName.toUtf8().constData());
118 } else if(eventName.contains(".eve")){
119
120 } else {
121 // Text file
122 qWarning("Text file %s is not supported jet.\n",eventName.toUtf8().constData());
123 }
124 }
125
126 return true;
127}
128
129//=============================================================================================================
130
131bool FiffEvents::read_from_fif(QIODevice &p_IODevice,
132 FiffEvents &p_Events)
133{
134 //
135 // Open file
136 //
137 FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
138
139 if(!t_pStream->open()) {
140 return false;
141 }
142
143 //
144 // Find the desired block
145 //
146 QList<FiffDirNode::SPtr> eventsBlocks = t_pStream->dirtree()->dir_tree_find(FIFFB_MNE_EVENTS);
147
148 if (eventsBlocks.size() == 0)
149 {
150 qWarning("Could not find event data\n");
151 return false;
152 }
153
154 qint32 k, nelem;
155 fiff_int_t kind, pos;
156 FiffTag::UPtr t_pTag;
157 quint32* serial_eventlist_uint = nullptr;
158 qint32* serial_eventlist_int = nullptr;
159
160 for(k = 0; k < eventsBlocks[0]->nent(); ++k)
161 {
162 kind = eventsBlocks[0]->dir[k]->kind;
163 pos = eventsBlocks[0]->dir[k]->pos;
164 if (kind == FIFF_MNE_EVENT_LIST)
165 {
166 t_pStream->read_tag(t_pTag,pos);
167 if(t_pTag->type == FIFFT_UINT)
168 {
169 serial_eventlist_uint = t_pTag->toUnsignedInt();
170 nelem = t_pTag->size()/4;
171 }
172
173 if(t_pTag->type == FIFFT_INT)
174 {
175 serial_eventlist_int = t_pTag->toInt();
176 nelem = t_pTag->size()/4;
177 }
178
179 break;
180 }
181 }
182
183 if(serial_eventlist_uint == nullptr && serial_eventlist_int == nullptr)
184 {
185 qWarning("Could not find any events\n");
186 return false;
187 }
188
189 p_Events.events.resize(nelem/3,3);
190 if(serial_eventlist_uint != nullptr)
191 {
192 for(k = 0; k < nelem/3; ++k)
193 {
194 p_Events.events(k,0) = serial_eventlist_uint[k*3];
195 p_Events.events(k,1) = serial_eventlist_uint[k*3+1];
196 p_Events.events(k,2) = serial_eventlist_uint[k*3+2];
197 }
198 }
199
200 if(serial_eventlist_int != nullptr)
201 {
202 for(k = 0; k < nelem/3; ++k)
203 {
204 p_Events.events(k,0) = serial_eventlist_int[k*3];
205 p_Events.events(k,1) = serial_eventlist_int[k*3+1];
206 p_Events.events(k,2) = serial_eventlist_int[k*3+2];
207 }
208 }
209
210 return true;
211}
212
213//=============================================================================================================
214
215bool FiffEvents::read_from_ascii(QIODevice &p_IODevice,
216 FiffEvents &p_Events)
217{
218 if (!p_IODevice.open(QIODevice::ReadOnly | QIODevice::Text)){
219 return false;
220 }
221 QTextStream textStream(&p_IODevice);
222
223 QList<int> sampleList;
224 QList<int> beforeList;
225 QList<int> afterList;
226
227 while(!textStream.atEnd()){
228 QString line = textStream.readLine().trimmed();
229 if (line.isEmpty())
230 continue;
231 QTextStream lineStream(&line);
232 int iSample = 0, iBefore = 0, iAfter = 0;
233 lineStream >> iSample;
234 if (!lineStream.atEnd())
235 lineStream >> iBefore;
236 if (!lineStream.atEnd())
237 lineStream >> iAfter;
238 sampleList.append(iSample);
239 beforeList.append(iBefore);
240 afterList.append(iAfter);
241 qDebug() << "Added event:" << iSample << iBefore << iAfter;
242 }
243
244 p_Events.events.resize(sampleList.size(), 3);
245
246 for(int i = 0; i < sampleList.size(); i++){
247 p_Events.events(i,0) = sampleList[i];
248 p_Events.events(i,1) = beforeList[i];
249 p_Events.events(i,2) = afterList[i];
250 }
251 return true;
252}
253
254//=============================================================================================================
255
256bool FiffEvents::write_to_fif(QIODevice &p_IODevice) const
257{
258 if (events.rows() == 0 || events.cols() < 3)
259 return false;
260
261 FiffStream::SPtr pStream = FiffStream::start_file(p_IODevice);
262 if (!pStream)
263 return false;
264
265 pStream->start_block(FIFFB_MNE_EVENTS);
266 pStream->write_int(FIFF_MNE_EVENT_LIST, events.data(), events.rows() * 3);
267 pStream->end_block(FIFFB_MNE_EVENTS);
268 pStream->end_file();
269
270 return true;
271}
272
273//=============================================================================================================
274
275bool FiffEvents::write_to_ascii(QIODevice &p_IODevice,
276 float sfreq) const
277{
278 if (!p_IODevice.open(QIODevice::WriteOnly | QIODevice::Text))
279 return false;
280
281 for (int k = 0; k < events.rows(); ++k) {
282 int sample = events(k, 0);
283 int before = (events.cols() > 1) ? events(k, 1) : 0;
284 int after = (events.cols() > 2) ? events(k, 2) : 0;
285 float time = (sfreq > 0.0f) ? static_cast<float>(sample) / sfreq : 0.0f;
286 QTextStream out(&p_IODevice);
287 out << QString("%1 %2 %3 %4\n")
288 .arg(sample, 6)
289 .arg(time, -10, 'f', 3)
290 .arg(before, 3)
291 .arg(after, 3);
292 }
293
294 p_IODevice.close();
295 return true;
296}
297
298//=============================================================================================================
299
301 FiffEvents &p_Events,
302 const QString &triggerCh,
303 unsigned int triggerMask,
304 bool leadingEdge)
305{
306 QString stimCh = triggerCh.isEmpty() ? QString("STI 014") : triggerCh;
307
308 // Find trigger channel index
309 int triggerChIdx = -1;
310 for (int k = 0; k < raw.info.ch_names.size(); ++k) {
311 if (raw.info.ch_names[k] == stimCh) {
312 triggerChIdx = k;
313 break;
314 }
315 }
316 if (triggerChIdx < 0) {
317 qWarning() << "[FiffEvents::detect_from_raw] Trigger channel" << stimCh << "not found.";
318 return false;
319 }
320
321 // Read trigger channel data
322 MatrixXd data;
323 MatrixXd times;
324 if (!raw.read_raw_segment(data, times, raw.first_samp, raw.last_samp,
325 RowVectorXi::LinSpaced(1, triggerChIdx, triggerChIdx))) {
326 qWarning() << "[FiffEvents::detect_from_raw] Could not read trigger channel data.";
327 return false;
328 }
329
330 RowVectorXd trigData = data.row(0);
331 int nSamples = static_cast<int>(trigData.cols());
332
333 // Detect flanks
334 QList<int> eventSamples;
335 QList<int> eventBefore;
336 QList<int> eventAfter;
337
338 int prevVal = static_cast<int>(trigData(0)) & triggerMask;
339 for (int s = 1; s < nSamples; ++s) {
340 int curVal = static_cast<int>(trigData(s)) & triggerMask;
341 if (curVal != prevVal) {
342 if (!leadingEdge || (leadingEdge && prevVal == 0 && curVal != 0)) {
343 eventSamples.append(static_cast<int>(raw.first_samp) + s);
344 eventBefore.append(prevVal);
345 eventAfter.append(curVal);
346 }
347 }
348 prevVal = curVal;
349 }
350
351 int nEvents = eventSamples.size();
352 p_Events.events.resize(nEvents, 3);
353 for (int k = 0; k < nEvents; ++k) {
354 p_Events.events(k, 0) = eventSamples[k];
355 p_Events.events(k, 1) = eventBefore[k];
356 p_Events.events(k, 2) = eventAfter[k];
357 }
358
359 return nEvents > 0;
360}
361
362//=============================================================================================================
363
365 const MatrixXi &events,
366 int eventIdx)
367{
368 if (eventIdx < 0 || eventIdx >= events.rows())
369 return false;
370
371 int evFrom = events(eventIdx, 1);
372 int evTo = events(eventIdx, 2);
373
374 // Check if any of the category's event codes match
375 bool match = false;
376 for (int k = 0; k < cat.events.size(); ++k) {
377 if ((evFrom & ~cat.ignore) == 0 &&
378 (evTo & ~cat.ignore) == cat.events[k]) {
379 match = true;
380 break;
381 }
382 }
383 if (!match)
384 return false;
385
386 // Check previous event constraint
387 if (cat.prevEvent != 0) {
388 bool found = false;
389 for (int j = eventIdx - 1; j >= 0; --j) {
390 if ((events(j, 1) & ~cat.prevIgnore) == 0) {
391 found = true;
392 match = match && ((events(j, 2) & ~cat.prevIgnore) == cat.prevEvent);
393 break;
394 }
395 }
396 if (!found)
397 match = false;
398 }
399
400 // Check next event constraint
401 if (cat.nextEvent != 0) {
402 bool found = false;
403 for (int j = eventIdx + 1; j < events.rows(); ++j) {
404 if ((events(j, 1) & ~cat.nextIgnore) == 0) {
405 found = true;
406 match = match && ((events(j, 2) & ~cat.nextIgnore) == cat.nextEvent);
407 break;
408 }
409 }
410 if (!found)
411 match = false;
412 }
413
414 return match;
415}
FiffTag class declaration, which provides fiff tag I/O and processing methods.
Fiff constants.
#define FIFF_MNE_EVENT_LIST
#define FIFFB_MNE_EVENTS
FiffStream class declaration.
FiffRawData class declaration.
FiffDirNode class declaration, which provides fiff dir tree processing methods.
FiffEvokedSet class declaration.
Header file describing the numerical values used in fif files.
#define FIFFT_UINT
Definition fiff_file.h:236
#define FIFFT_INT
Definition fiff_file.h:231
FiffEvents class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
qint32 fiff_int_t
Definition fiff_types.h:89
bool write_to_ascii(QIODevice &p_IODevice, float sfreq=0.0f) const
static bool read_from_fif(QIODevice &p_IODevice, FiffEvents &p_Events)
bool write_to_fif(QIODevice &p_IODevice) const
static bool read_from_ascii(QIODevice &p_IODevice, FiffEvents &p_Events)
Eigen::MatrixXi events
static bool detect_from_raw(const FiffRawData &raw, FiffEvents &p_Events, const QString &triggerCh=QString("STI 014"), unsigned int triggerMask=0xFFFFFFFF, bool leadingEdge=true)
static bool matchEvent(const AverageCategory &cat, const Eigen::MatrixXi &events, int eventIdx)
static bool read(const QString &t_sEventName, const QString &t_fileRawName, FiffEvents &p_Events)
QVector< unsigned int > events
FIFF raw measurement data.
bool read_raw_segment(Eigen::MatrixXd &data, Eigen::MatrixXd &times, fiff_int_t from=-1, fiff_int_t to=-1, const Eigen::RowVectorXi &sel=defaultRowVectorXi, bool do_debug=false) const
FIFF File I/O routines.
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:158