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