v2.0.0
Loading...
Searching...
No Matches
mne_description_parser.cpp
Go to the documentation of this file.
1//=============================================================================================================
16
17//=============================================================================================================
18// INCLUDES
19//=============================================================================================================
20
22
23#include <QFile>
24#include <QDebug>
25#include <cmath>
26
27//=============================================================================================================
28// USED NAMESPACES
29//=============================================================================================================
30
31using namespace MNELIB;
32
33//=============================================================================================================
34// STATIC HELPERS
35//=============================================================================================================
36
37void MNEDescriptionParser::skipComments(QTextStream& in)
38{
39 while (!in.atEnd()) {
40 QChar c;
41 in >> c;
42 if (c == '#') {
43 in.readLine(); // consume rest of comment line
44 } else {
45 // push back by seeking
46 in.seek(in.pos() - 1);
47 return;
48 }
49 }
50}
51
52//=============================================================================================================
53
54QString MNEDescriptionParser::nextWord(QTextStream& in)
55{
56 skipComments(in);
57
58 // Skip whitespace
59 while (!in.atEnd()) {
60 QChar c;
61 in >> c;
62 if (!c.isSpace()) {
63 // Check if quoted string
64 if (c == '"') {
65 QString word;
66 while (!in.atEnd()) {
67 in >> c;
68 if (c == '"')
69 break;
70 word.append(c);
71 }
72 return word;
73 }
74 // Regular word
75 QString word;
76 word.append(c);
77 while (!in.atEnd()) {
78 in >> c;
79 if (c.isSpace())
80 break;
81 if (c == '#') {
82 in.readLine();
83 break;
84 }
85 word.append(c);
86 }
87 return word;
88 }
89 }
90 return QString(); // EOF
91}
92
93//=============================================================================================================
94
95bool MNEDescriptionParser::getInt(QTextStream& in, int& val)
96{
97 QString word = nextWord(in);
98 if (word.isEmpty()) {
99 qWarning() << "MNEDescriptionParser: expected integer, got EOF";
100 return false;
101 }
102 bool ok;
103 val = word.toInt(&ok);
104 if (!ok) {
105 qWarning() << "MNEDescriptionParser: bad integer:" << word;
106 return false;
107 }
108 return true;
109}
110
111//=============================================================================================================
112
113bool MNEDescriptionParser::getFloat(QTextStream& in, float& val)
114{
115 QString word = nextWord(in);
116 if (word.isEmpty()) {
117 qWarning() << "MNEDescriptionParser: expected float, got EOF";
118 return false;
119 }
120 bool ok;
121 val = word.toFloat(&ok);
122 if (!ok) {
123 qWarning() << "MNEDescriptionParser: bad float:" << word;
124 return false;
125 }
126 return true;
127}
128
129//=============================================================================================================
130
131bool MNEDescriptionParser::parseRejectionParam(const QString& keyword, QTextStream& in,
132 FIFFLIB::RejectionParams& rej, bool& ok)
133{
134 ok = true;
135 float fval;
136
137 if (keyword.compare("gradReject", Qt::CaseInsensitive) == 0) {
138 ok = getFloat(in, fval);
139 if (ok)
140 rej.megGradReject = fval;
141 } else if (keyword.compare("magReject", Qt::CaseInsensitive) == 0) {
142 ok = getFloat(in, fval);
143 if (ok)
144 rej.megMagReject = fval;
145 } else if (keyword.compare("eegReject", Qt::CaseInsensitive) == 0) {
146 ok = getFloat(in, fval);
147 if (ok)
148 rej.eegReject = fval;
149 } else if (keyword.compare("eogReject", Qt::CaseInsensitive) == 0) {
150 ok = getFloat(in, fval);
151 if (ok)
152 rej.eogReject = fval;
153 } else if (keyword.compare("ecgReject", Qt::CaseInsensitive) == 0) {
154 ok = getFloat(in, fval);
155 if (ok)
156 rej.ecgReject = fval;
157 } else if (keyword.compare("gradFlat", Qt::CaseInsensitive) == 0) {
158 ok = getFloat(in, fval);
159 if (ok)
160 rej.megGradFlat = fval;
161 } else if (keyword.compare("magFlat", Qt::CaseInsensitive) == 0) {
162 ok = getFloat(in, fval);
163 if (ok)
164 rej.megMagFlat = fval;
165 } else if (keyword.compare("eegFlat", Qt::CaseInsensitive) == 0) {
166 ok = getFloat(in, fval);
167 if (ok)
168 rej.eegFlat = fval;
169 } else if (keyword.compare("eogFlat", Qt::CaseInsensitive) == 0) {
170 ok = getFloat(in, fval);
171 if (ok)
172 rej.eogFlat = fval;
173 } else if (keyword.compare("ecgFlat", Qt::CaseInsensitive) == 0) {
174 ok = getFloat(in, fval);
175 if (ok)
176 rej.ecgFlat = fval;
177 } else if (keyword.compare("stimIgnore", Qt::CaseInsensitive) == 0) {
178 ok = getFloat(in, fval);
179 if (ok)
180 rej.stimIgnore = std::fabs(fval);
181 } else {
182 return false; // not a rejection keyword
183 }
184 return true; // was a rejection keyword (ok indicates parse success)
185}
186
187//=============================================================================================================
188// AVERAGE DESCRIPTION PARSER
189//=============================================================================================================
190
192{
193 QFile file(fileName);
194 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
195 qWarning() << "MNEDescriptionParser: cannot open" << fileName;
196 return false;
197 }
198
199 QTextStream in(&file);
201
202 bool expectBrace = false;
203 bool bminSet = false, bmaxSet = false;
204 bool inAverage = false;
205 bool inCategory = false;
206 FIFFLIB::AverageCategory currentCat;
207
208 QString word;
209 while (!(word = nextWord(in)).isEmpty()) {
210 if (expectBrace) {
211 if (word == "{") {
212 expectBrace = false;
213 } else {
214 qWarning() << "MNEDescriptionParser: expected '{', got" << word;
215 return false;
216 }
217 continue;
218 }
219
220 if (word == "{") {
221 qWarning() << "MNEDescriptionParser: unexpected '{'";
222 return false;
223 }
224
225 if (word == "}") {
226 if (inCategory) {
227 currentCat.doBaseline = bminSet && bmaxSet;
228 // Validate category
229 if (currentCat.comment.isEmpty()) {
230 qWarning() << "MNEDescriptionParser: category name missing";
231 return false;
232 }
233 if (currentCat.tmin >= currentCat.tmax) {
234 qWarning() << "MNEDescriptionParser: illegal time range for" << currentCat.comment;
235 return false;
236 }
237 if (currentCat.events.isEmpty()) {
238 qWarning() << "MNEDescriptionParser: no events for" << currentCat.comment;
239 return false;
240 }
241 if (currentCat.doBaseline && currentCat.bmin > currentCat.bmax) {
242 qWarning() << "MNEDescriptionParser: illegal baseline for" << currentCat.comment;
243 return false;
244 }
245 if (!currentCat.prevIgnore)
246 currentCat.prevIgnore = currentCat.ignore;
247 if (!currentCat.nextIgnore)
248 currentCat.nextIgnore = currentCat.ignore;
249 desc.categories.append(currentCat);
250 currentCat = FIFFLIB::AverageCategory();
251 inCategory = false;
252 bminSet = bmaxSet = false;
253 } else if (inAverage) {
254 if (desc.comment.isEmpty())
255 desc.comment = "Average";
256 inAverage = false;
257 break; // done
258 }
259 continue;
260 }
261
262 // Top-level: "average" keyword
263 if (word.compare("average", Qt::CaseInsensitive) == 0) {
264 if (inAverage || inCategory) {
265 qWarning() << "MNEDescriptionParser: nested average";
266 return false;
267 }
268 inAverage = true;
269 expectBrace = true;
270 continue;
271 }
272
273 // Category start
274 if (word.compare("category", Qt::CaseInsensitive) == 0 ||
275 word.compare("condition", Qt::CaseInsensitive) == 0) {
276 if (!inAverage || inCategory) {
277 qWarning() << "MNEDescriptionParser: misplaced category";
278 return false;
279 }
280 inCategory = true;
281 expectBrace = true;
282 bminSet = bmaxSet = false;
283 continue;
284 }
285
286 // "name" keyword
287 if (word.compare("name", Qt::CaseInsensitive) == 0) {
288 QString val = nextWord(in);
289 if (val.isEmpty()) {
290 qWarning() << "MNEDescriptionParser: name requires a value";
291 return false;
292 }
293 if (inCategory)
294 currentCat.comment = val;
295 else if (inAverage)
296 desc.comment = val;
297 else {
298 qWarning() << "MNEDescriptionParser: misplaced name";
299 return false;
300 }
301 continue;
302 }
303
304 // "outfile" keyword
305 if (word.compare("outfile", Qt::CaseInsensitive) == 0) {
306 QString val = nextWord(in);
307 if (val.isEmpty()) {
308 qWarning() << "MNEDescriptionParser: outfile requires a value";
309 return false;
310 }
311 if (inAverage && !inCategory)
312 desc.filename = val;
313 else {
314 qWarning() << "MNEDescriptionParser: misplaced outfile";
315 return false;
316 }
317 continue;
318 }
319
320 // "eventfile" keyword
321 if (word.compare("eventfile", Qt::CaseInsensitive) == 0) {
322 QString val = nextWord(in);
323 if (val.isEmpty()) {
324 qWarning() << "MNEDescriptionParser: eventfile requires a value";
325 return false;
326 }
327 if (inAverage && !inCategory)
328 desc.eventFile = val;
329 else {
330 qWarning() << "MNEDescriptionParser: misplaced eventfile";
331 return false;
332 }
333 continue;
334 }
335
336 // "logfile" keyword
337 if (word.compare("logfile", Qt::CaseInsensitive) == 0) {
338 QString val = nextWord(in);
339 if (val.isEmpty()) {
340 qWarning() << "MNEDescriptionParser: logfile requires a value";
341 return false;
342 }
343 if (inAverage && !inCategory)
344 desc.logFile = val;
345 else {
346 qWarning() << "MNEDescriptionParser: misplaced logfile";
347 return false;
348 }
349 continue;
350 }
351
352 // "fixskew" keyword
353 if (word.compare("fixskew", Qt::CaseInsensitive) == 0) {
354 if (inAverage && !inCategory)
355 desc.fixSkew = true;
356 else {
357 qWarning() << "MNEDescriptionParser: misplaced fixskew";
358 return false;
359 }
360 continue;
361 }
362
363 // --- Category-level keywords ---
364 if (word.compare("tmin", Qt::CaseInsensitive) == 0) {
365 float fval;
366 if (!getFloat(in, fval))
367 return false;
368 if (inCategory)
369 currentCat.tmin = fval;
370 else {
371 qWarning() << "MNEDescriptionParser: misplaced tmin";
372 return false;
373 }
374 continue;
375 }
376 if (word.compare("tmax", Qt::CaseInsensitive) == 0) {
377 float fval;
378 if (!getFloat(in, fval))
379 return false;
380 if (inCategory)
381 currentCat.tmax = fval;
382 else {
383 qWarning() << "MNEDescriptionParser: misplaced tmax";
384 return false;
385 }
386 continue;
387 }
388 if (word.compare("basemin", Qt::CaseInsensitive) == 0 ||
389 word.compare("bmin", Qt::CaseInsensitive) == 0) {
390 float fval;
391 if (!getFloat(in, fval))
392 return false;
393 if (inCategory) {
394 currentCat.bmin = fval;
395 bminSet = true;
396 } else {
397 qWarning() << "MNEDescriptionParser: misplaced basemin";
398 return false;
399 }
400 continue;
401 }
402 if (word.compare("basemax", Qt::CaseInsensitive) == 0 ||
403 word.compare("bmax", Qt::CaseInsensitive) == 0) {
404 float fval;
405 if (!getFloat(in, fval))
406 return false;
407 if (inCategory) {
408 currentCat.bmax = fval;
409 bmaxSet = true;
410 } else {
411 qWarning() << "MNEDescriptionParser: misplaced basemax";
412 return false;
413 }
414 continue;
415 }
416 if (word.compare("event", Qt::CaseInsensitive) == 0) {
417 int ival;
418 if (!getInt(in, ival))
419 return false;
420 if (ival <= 0) {
421 qWarning() << "MNEDescriptionParser: event numbers must be positive";
422 return false;
423 }
424 if (inCategory)
425 currentCat.events.append(static_cast<unsigned int>(ival));
426 else {
427 qWarning() << "MNEDescriptionParser: misplaced event";
428 return false;
429 }
430 continue;
431 }
432 if (word.compare("nextevent", Qt::CaseInsensitive) == 0) {
433 int ival;
434 if (!getInt(in, ival))
435 return false;
436 if (ival <= 0) {
437 qWarning() << "MNEDescriptionParser: nextevent must be positive";
438 return false;
439 }
440 if (inCategory)
441 currentCat.nextEvent = static_cast<unsigned int>(ival);
442 else {
443 qWarning() << "MNEDescriptionParser: misplaced nextevent";
444 return false;
445 }
446 continue;
447 }
448 if (word.compare("prevevent", Qt::CaseInsensitive) == 0) {
449 int ival;
450 if (!getInt(in, ival))
451 return false;
452 if (ival <= 0) {
453 qWarning() << "MNEDescriptionParser: prevevent must be positive";
454 return false;
455 }
456 if (inCategory)
457 currentCat.prevEvent = static_cast<unsigned int>(ival);
458 else {
459 qWarning() << "MNEDescriptionParser: misplaced prevevent";
460 return false;
461 }
462 continue;
463 }
464 if (word.compare("ignore", Qt::CaseInsensitive) == 0) {
465 int ival;
466 if (!getInt(in, ival))
467 return false;
468 if (ival < 0) {
469 qWarning() << "MNEDescriptionParser: ignore numbers must be positive or zero";
470 return false;
471 }
472 if (inCategory)
473 currentCat.ignore = static_cast<unsigned int>(ival);
474 else {
475 qWarning() << "MNEDescriptionParser: misplaced ignore";
476 return false;
477 }
478 continue;
479 }
480 if (word.compare("prevignore", Qt::CaseInsensitive) == 0) {
481 int ival;
482 if (!getInt(in, ival))
483 return false;
484 if (ival < 0) {
485 qWarning() << "MNEDescriptionParser: ignore numbers must be positive or zero";
486 return false;
487 }
488 if (inCategory)
489 currentCat.prevIgnore = static_cast<unsigned int>(ival);
490 else {
491 qWarning() << "MNEDescriptionParser: misplaced prevignore";
492 return false;
493 }
494 continue;
495 }
496 if (word.compare("nextignore", Qt::CaseInsensitive) == 0) {
497 int ival;
498 if (!getInt(in, ival))
499 return false;
500 if (ival < 0) {
501 qWarning() << "MNEDescriptionParser: ignore numbers must be positive or zero";
502 return false;
503 }
504 if (inCategory)
505 currentCat.nextIgnore = static_cast<unsigned int>(ival);
506 else {
507 qWarning() << "MNEDescriptionParser: misplaced nextignore";
508 return false;
509 }
510 continue;
511 }
512 if (word.compare("mask", Qt::CaseInsensitive) == 0) {
513 int ival;
514 if (!getInt(in, ival))
515 return false;
516 if (ival <= 0) {
517 qWarning() << "MNEDescriptionParser: mask must be positive";
518 return false;
519 }
520 if (inCategory) {
521 currentCat.ignore = static_cast<unsigned int>(ival);
522 currentCat.ignore = ~currentCat.ignore;
523 } else {
524 qWarning() << "MNEDescriptionParser: misplaced mask";
525 return false;
526 }
527 continue;
528 }
529 if (word.compare("prevmask", Qt::CaseInsensitive) == 0) {
530 int ival;
531 if (!getInt(in, ival))
532 return false;
533 if (ival <= 0) {
534 qWarning() << "MNEDescriptionParser: mask numbers must be positive";
535 return false;
536 }
537 if (inCategory) {
538 currentCat.prevIgnore = static_cast<unsigned int>(ival);
539 currentCat.prevIgnore = ~currentCat.prevIgnore;
540 } else {
541 qWarning() << "MNEDescriptionParser: misplaced prevmask";
542 return false;
543 }
544 continue;
545 }
546 if (word.compare("nextmask", Qt::CaseInsensitive) == 0) {
547 int ival;
548 if (!getInt(in, ival))
549 return false;
550 if (ival <= 0) {
551 qWarning() << "MNEDescriptionParser: mask numbers must be positive";
552 return false;
553 }
554 if (inCategory) {
555 currentCat.nextIgnore = static_cast<unsigned int>(ival);
556 currentCat.nextIgnore = ~currentCat.nextIgnore;
557 } else {
558 qWarning() << "MNEDescriptionParser: misplaced nextmask";
559 return false;
560 }
561 continue;
562 }
563 if (word.compare("delay", Qt::CaseInsensitive) == 0) {
564 float fval;
565 if (!getFloat(in, fval))
566 return false;
567 if (inCategory)
568 currentCat.delay = fval;
569 else {
570 qWarning() << "MNEDescriptionParser: misplaced delay";
571 return false;
572 }
573 continue;
574 }
575 if (word.compare("stderr", Qt::CaseInsensitive) == 0) {
576 if (inCategory)
577 currentCat.doStdErr = true;
578 else {
579 qWarning() << "MNEDescriptionParser: misplaced stderr";
580 return false;
581 }
582 continue;
583 }
584 if (word.compare("abs", Qt::CaseInsensitive) == 0) {
585 if (inCategory)
586 currentCat.doAbs = true;
587 else {
588 qWarning() << "MNEDescriptionParser: misplaced abs";
589 return false;
590 }
591 continue;
592 }
593 if (word.compare("color", Qt::CaseInsensitive) == 0) {
594 float r, g, b;
595 if (!getFloat(in, r) || !getFloat(in, g) || !getFloat(in, b))
596 return false;
597 if (r < 0.0f || r > 1.0f || g < 0.0f || g > 1.0f || b < 0.0f || b > 1.0f) {
598 qWarning() << "MNEDescriptionParser: color components must be within 0 ... 1";
599 return false;
600 }
601 if (inCategory) {
602 currentCat.color[0] = r;
603 currentCat.color[1] = g;
604 currentCat.color[2] = b;
605 } else {
606 qWarning() << "MNEDescriptionParser: misplaced color";
607 return false;
608 }
609 continue;
610 }
611
612 // Try rejection parameters (average-level only)
613 bool parseOk;
614 if (parseRejectionParam(word, in, desc.rej, parseOk)) {
615 if (!(inAverage && !inCategory)) {
616 qWarning() << "MNEDescriptionParser: misplaced" << word;
617 return false;
618 }
619 if (!parseOk)
620 return false;
621 continue;
622 }
623
624 qWarning() << "MNEDescriptionParser: unknown keyword" << word << "in" << fileName;
625 }
626
627 if (desc.categories.isEmpty()) {
628 qWarning() << "MNEDescriptionParser: no categories found in" << fileName;
629 return false;
630 }
631
632 return true;
633}
634
635//=============================================================================================================
636// COVARIANCE DESCRIPTION PARSER
637//=============================================================================================================
638
640{
641 QFile file(fileName);
642 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
643 qWarning() << "MNEDescriptionParser: cannot open" << fileName;
644 return false;
645 }
646
647 QTextStream in(&file);
648 desc = CovDescription();
649
650 bool expectBrace = false;
651 bool bminSet = false, bmaxSet = false;
652 bool inCov = false;
653 bool seenCov = false;
654 bool inDef = false;
655 CovDefinition currentDef;
656
657 QString word;
658 while (!(word = nextWord(in)).isEmpty()) {
659 if (expectBrace) {
660 if (word == "{") {
661 expectBrace = false;
662 } else {
663 qWarning() << "MNEDescriptionParser: expected '{', got" << word;
664 return false;
665 }
666 continue;
667 }
668
669 if (word == "{") {
670 qWarning() << "MNEDescriptionParser: unexpected '{'";
671 return false;
672 }
673
674 if (word == "}") {
675 if (inDef) {
676 currentDef.doBaseline = bminSet && bmaxSet;
677 if (currentDef.tmin >= currentDef.tmax) {
678 qWarning() << "MNEDescriptionParser: illegal time range in def";
679 return false;
680 }
681 if (currentDef.doBaseline && currentDef.bmin > currentDef.bmax) {
682 qWarning() << "MNEDescriptionParser: illegal baseline in def";
683 return false;
684 }
685 desc.defs.append(currentDef);
686 currentDef = CovDefinition();
687 inDef = false;
688 bminSet = bmaxSet = false;
689 } else if (inCov) {
690 inCov = false;
691 break;
692 }
693 continue;
694 }
695
696 if (word.compare("cov", Qt::CaseInsensitive) == 0) {
697 if (inCov || inDef) {
698 qWarning() << "MNEDescriptionParser: nested cov";
699 return false;
700 }
701 inCov = true;
702 seenCov = true;
703 expectBrace = true;
704 continue;
705 }
706
707 if (word.compare("def", Qt::CaseInsensitive) == 0) {
708 if (!inCov || inDef) {
709 qWarning() << "MNEDescriptionParser: misplaced def";
710 return false;
711 }
712 inDef = true;
713 expectBrace = true;
714 bminSet = bmaxSet = false;
715 continue;
716 }
717
718 // Cov-level keywords
719 if (word.compare("outfile", Qt::CaseInsensitive) == 0) {
720 QString val = nextWord(in);
721 if (val.isEmpty()) {
722 qWarning() << "MNEDescriptionParser: outfile requires a value";
723 return false;
724 }
725 if (inCov && !inDef)
726 desc.filename = val;
727 else {
728 qWarning() << "MNEDescriptionParser: misplaced outfile";
729 return false;
730 }
731 continue;
732 }
733 if (word.compare("eventfile", Qt::CaseInsensitive) == 0) {
734 QString val = nextWord(in);
735 if (val.isEmpty()) {
736 qWarning() << "MNEDescriptionParser: eventfile requires a value";
737 return false;
738 }
739 if (inCov && !inDef)
740 desc.eventFile = val;
741 else {
742 qWarning() << "MNEDescriptionParser: misplaced eventfile";
743 return false;
744 }
745 continue;
746 }
747 if (word.compare("logfile", Qt::CaseInsensitive) == 0) {
748 QString val = nextWord(in);
749 if (val.isEmpty()) {
750 qWarning() << "MNEDescriptionParser: logfile requires a value";
751 return false;
752 }
753 if (inCov && !inDef)
754 desc.logFile = val;
755 else {
756 qWarning() << "MNEDescriptionParser: misplaced logfile";
757 return false;
758 }
759 continue;
760 }
761 if (word.compare("keepsamplemean", Qt::CaseInsensitive) == 0) {
762 if (inCov && !inDef)
763 desc.removeSampleMean = false;
764 else {
765 qWarning() << "MNEDescriptionParser: misplaced keepsamplemean";
766 return false;
767 }
768 continue;
769 }
770 if (word.compare("fixskew", Qt::CaseInsensitive) == 0) {
771 if (inCov && !inDef)
772 desc.fixSkew = true;
773 else {
774 qWarning() << "MNEDescriptionParser: misplaced fixskew";
775 return false;
776 }
777 continue;
778 }
779
780 // Def-level keywords
781 if (word.compare("tmin", Qt::CaseInsensitive) == 0) {
782 float fval;
783 if (!getFloat(in, fval))
784 return false;
785 if (inDef)
786 currentDef.tmin = fval;
787 else {
788 qWarning() << "MNEDescriptionParser: misplaced tmin";
789 return false;
790 }
791 continue;
792 }
793 if (word.compare("tmax", Qt::CaseInsensitive) == 0) {
794 float fval;
795 if (!getFloat(in, fval))
796 return false;
797 if (inDef)
798 currentDef.tmax = fval;
799 else {
800 qWarning() << "MNEDescriptionParser: misplaced tmax";
801 return false;
802 }
803 continue;
804 }
805 if (word.compare("basemin", Qt::CaseInsensitive) == 0 ||
806 word.compare("bmin", Qt::CaseInsensitive) == 0) {
807 float fval;
808 if (!getFloat(in, fval))
809 return false;
810 if (inDef) {
811 currentDef.bmin = fval;
812 bminSet = true;
813 } else {
814 qWarning() << "MNEDescriptionParser: misplaced basemin";
815 return false;
816 }
817 continue;
818 }
819 if (word.compare("basemax", Qt::CaseInsensitive) == 0 ||
820 word.compare("bmax", Qt::CaseInsensitive) == 0) {
821 float fval;
822 if (!getFloat(in, fval))
823 return false;
824 if (inDef) {
825 currentDef.bmax = fval;
826 bmaxSet = true;
827 } else {
828 qWarning() << "MNEDescriptionParser: misplaced basemax";
829 return false;
830 }
831 continue;
832 }
833 if (word.compare("event", Qt::CaseInsensitive) == 0) {
834 int ival;
835 if (!getInt(in, ival))
836 return false;
837 if (ival <= 0) {
838 qWarning() << "MNEDescriptionParser: event must be positive";
839 return false;
840 }
841 if (inDef)
842 currentDef.events.append(static_cast<unsigned int>(ival));
843 else {
844 qWarning() << "MNEDescriptionParser: misplaced event";
845 return false;
846 }
847 continue;
848 }
849 if (word.compare("ignore", Qt::CaseInsensitive) == 0) {
850 int ival;
851 if (!getInt(in, ival))
852 return false;
853 if (ival < 0) {
854 qWarning() << "MNEDescriptionParser: ignore numbers must be positive or zero";
855 return false;
856 }
857 if (inDef)
858 currentDef.ignore = static_cast<unsigned int>(ival);
859 else {
860 qWarning() << "MNEDescriptionParser: misplaced ignore";
861 return false;
862 }
863 continue;
864 }
865 if (word.compare("mask", Qt::CaseInsensitive) == 0) {
866 int ival;
867 if (!getInt(in, ival))
868 return false;
869 if (ival <= 0) {
870 qWarning() << "MNEDescriptionParser: mask numbers must be positive";
871 return false;
872 }
873 if (inDef) {
874 currentDef.ignore = static_cast<unsigned int>(ival);
875 currentDef.ignore = ~currentDef.ignore;
876 } else {
877 qWarning() << "MNEDescriptionParser: misplaced mask";
878 return false;
879 }
880 continue;
881 }
882 if (word.compare("delay", Qt::CaseInsensitive) == 0) {
883 float fval;
884 if (!getFloat(in, fval))
885 return false;
886 if (inDef)
887 currentDef.delay = fval;
888 else {
889 qWarning() << "MNEDescriptionParser: misplaced delay";
890 return false;
891 }
892 continue;
893 }
894
895 // Rejection parameters (cov-level only)
896 bool parseOk;
897 if (parseRejectionParam(word, in, desc.rej, parseOk)) {
898 if (!(inCov && !inDef)) {
899 qWarning() << "MNEDescriptionParser: misplaced" << word;
900 return false;
901 }
902 if (!parseOk)
903 return false;
904 continue;
905 }
906
907 qWarning() << "MNEDescriptionParser: unknown keyword" << word << "in" << fileName;
908 }
909
910 if (!seenCov) {
911 qWarning() << "MNEDescriptionParser: nothing useful read from" << fileName;
912 return false;
913 }
914
915 return true;
916}
Parser for the legacy MNE-C .ave / .cov plain-text description files.
Core MNE data structures (source spaces, source estimates, hemispheres).
One averaging category in an MNE-C ave-description file: trigger logic, timing window,...
QVector< unsigned int > events
Top-level MNE-C ave-description record: comment, category list, shared rejection limits and output fi...
QList< AverageCategory > categories
static bool parseCovarianceFile(const QString &fileName, CovDescription &desc)
static bool parseAverageFile(const QString &fileName, FIFFLIB::AverageDescription &desc)
QVector< unsigned int > events
QList< CovDefinition > defs
FIFFLIB::RejectionParams rej