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 printf("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 printf("Error while read events.\n");
106 return false;
107 }
108 printf("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 printf("Error while read events.\n");
115 return false;
116 }
117 printf("Binary event file %s read\n",eventName.toUtf8().constData());
118 } else if(eventName.contains(".eve")){
119
120 } else {
121 // Text file
122 printf("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 printf("Could not find event data\n");
151 return false;
152 }
153
154 qint32 k, nelem;
155 fiff_int_t kind, pos;
156 FiffTag::SPtr 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 printf("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> simpleList;
224
225 while(!textStream.atEnd()){
226 int iSample;
227 textStream >> iSample;
228 simpleList.append(iSample);
229 textStream.readLine();
230 qDebug() << "Added event:" << iSample;
231 }
232
233 p_Events.events.resize(simpleList.size(), 1);
234
235 for(int i = 0; i < simpleList.size(); i++){
236 p_Events.events(i,0) = simpleList[i];
237 }
238 return true;
239}
240
241//=============================================================================================================
242
243bool FiffEvents::write_to_fif(QIODevice &p_IODevice) const
244{
245 if (events.rows() == 0 || events.cols() < 3)
246 return false;
247
248 FiffStream::SPtr pStream = FiffStream::start_file(p_IODevice);
249 if (!pStream)
250 return false;
251
252 pStream->start_block(FIFFB_MNE_EVENTS);
253 pStream->write_int(FIFF_MNE_EVENT_LIST, events.data(), events.rows() * 3);
254 pStream->end_block(FIFFB_MNE_EVENTS);
255 pStream->end_file();
256
257 return true;
258}
259
260//=============================================================================================================
261
262bool FiffEvents::write_to_ascii(QIODevice &p_IODevice,
263 float sfreq) const
264{
265 if (!p_IODevice.open(QIODevice::WriteOnly | QIODevice::Text))
266 return false;
267
268 for (int k = 0; k < events.rows(); ++k) {
269 int sample = events(k, 0);
270 int before = (events.cols() > 1) ? events(k, 1) : 0;
271 int after = (events.cols() > 2) ? events(k, 2) : 0;
272 float time = (sfreq > 0.0f) ? static_cast<float>(sample) / sfreq : 0.0f;
273 QTextStream out(&p_IODevice);
274 out << QString("%1 %2 %3 %4\n")
275 .arg(sample, 6)
276 .arg(time, -10, 'f', 3)
277 .arg(before, 3)
278 .arg(after, 3);
279 }
280
281 p_IODevice.close();
282 return true;
283}
284
285//=============================================================================================================
286
288 FiffEvents &p_Events,
289 const QString &triggerCh,
290 unsigned int triggerMask,
291 bool leadingEdge)
292{
293 QString stimCh = triggerCh.isEmpty() ? QString("STI 014") : triggerCh;
294
295 // Find trigger channel index
296 int triggerChIdx = -1;
297 for (int k = 0; k < raw.info.ch_names.size(); ++k) {
298 if (raw.info.ch_names[k] == stimCh) {
299 triggerChIdx = k;
300 break;
301 }
302 }
303 if (triggerChIdx < 0) {
304 qWarning() << "[FiffEvents::detect_from_raw] Trigger channel" << stimCh << "not found.";
305 return false;
306 }
307
308 // Read trigger channel data
309 MatrixXd data;
310 MatrixXd times;
311 if (!raw.read_raw_segment(data, times, raw.first_samp, raw.last_samp,
312 RowVectorXi::LinSpaced(1, triggerChIdx, triggerChIdx))) {
313 qWarning() << "[FiffEvents::detect_from_raw] Could not read trigger channel data.";
314 return false;
315 }
316
317 RowVectorXd trigData = data.row(0);
318 int nSamples = static_cast<int>(trigData.cols());
319
320 // Detect flanks
321 QList<int> eventSamples;
322 QList<int> eventBefore;
323 QList<int> eventAfter;
324
325 int prevVal = static_cast<int>(trigData(0)) & triggerMask;
326 for (int s = 1; s < nSamples; ++s) {
327 int curVal = static_cast<int>(trigData(s)) & triggerMask;
328 if (curVal != prevVal) {
329 if (!leadingEdge || (leadingEdge && prevVal == 0 && curVal != 0)) {
330 eventSamples.append(static_cast<int>(raw.first_samp) + s);
331 eventBefore.append(prevVal);
332 eventAfter.append(curVal);
333 }
334 }
335 prevVal = curVal;
336 }
337
338 int nEvents = eventSamples.size();
339 p_Events.events.resize(nEvents, 3);
340 for (int k = 0; k < nEvents; ++k) {
341 p_Events.events(k, 0) = eventSamples[k];
342 p_Events.events(k, 1) = eventBefore[k];
343 p_Events.events(k, 2) = eventAfter[k];
344 }
345
346 return nEvents > 0;
347}
348
349//=============================================================================================================
350
352 const MatrixXi &events,
353 int eventIdx)
354{
355 if (eventIdx < 0 || eventIdx >= events.rows())
356 return false;
357
358 int evFrom = events(eventIdx, 1);
359 int evTo = events(eventIdx, 2);
360
361 // Check if any of the category's event codes match
362 bool match = false;
363 for (int k = 0; k < cat.events.size(); ++k) {
364 if ((evFrom & ~cat.ignore) == 0 &&
365 (evTo & ~cat.ignore) == cat.events[k]) {
366 match = true;
367 break;
368 }
369 }
370 if (!match)
371 return false;
372
373 // Check previous event constraint
374 if (cat.prevEvent != 0) {
375 bool found = false;
376 for (int j = eventIdx - 1; j >= 0; --j) {
377 if ((events(j, 1) & ~cat.prevIgnore) == 0) {
378 found = true;
379 match = match && ((events(j, 2) & ~cat.prevIgnore) == cat.prevEvent);
380 break;
381 }
382 }
383 if (!found)
384 match = false;
385 }
386
387 // Check next event constraint
388 if (cat.nextEvent != 0) {
389 bool found = false;
390 for (int j = eventIdx + 1; j < events.rows(); ++j) {
391 if ((events(j, 1) & ~cat.nextIgnore) == 0) {
392 found = true;
393 match = match && ((events(j, 2) & ~cat.nextIgnore) == cat.nextEvent);
394 break;
395 }
396 }
397 if (!found)
398 match = false;
399 }
400
401 return match;
402}
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.
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
QSharedPointer< FiffStream > SPtr
QSharedPointer< FiffTag > SPtr
Definition fiff_tag.h:155