39#include <Eigen/SparseCore>
64 qRegisterMetaType<FIFFLIB::FiffEvokedSet>(
"FIFFLIB::FiffEvokedSet");
65 qRegisterMetaType<FIFFLIB::FiffEvokedSet::SPtr>(
"FIFFLIB::FiffEvokedSet::SPtr");
72 qRegisterMetaType<FIFFLIB::FiffEvokedSet>(
"FIFFLIB::FiffEvokedSet");
73 qRegisterMetaType<FIFFLIB::FiffEvokedSet::SPtr>(
"FIFFLIB::FiffEvokedSet::SPtr");
76 throw std::runtime_error(
"Fiff evoked data set not found");
83: info(p_FiffEvokedSet.info)
84, evoked(p_FiffEvokedSet.evoked)
105 const QStringList& exclude)
const
113 if (sel.cols() > 0) {
116 res.
info = this->info;
119 QList<FiffEvoked>::ConstIterator ev;
120 for (ev = evoked.begin(); ev != evoked.end(); ++ev)
121 res.
evoked.push_back(ev->pick_channels(include, exclude));
130 const qint32 now = info.get_current_comp();
133 qInfo(
"Data is already compensated as desired.\n");
138 if (!info.make_compensator(now, to,
ctf_comp)) {
139 qWarning(
"[FiffEvokedSet::compensate_to] Cannot change compensation from grade %d to %d.", now, to);
144 response.data =
ctf_comp.data->data * response.data;
145 response.info.set_current_comp(to);
147 info.set_current_comp(to);
156 if (!p_FiffEvokedSet.
evoked.size()) {
157 qWarning(
"No evoked response data sets in %s\n", p_FiffEvokedSet.
info.
filename.toUtf8().constData());
160 qInfo(
"\nFound %lld evoked response data sets in %s :\n",
static_cast<long long>(p_FiffEvokedSet.
evoked.size()), p_FiffEvokedSet.
info.
filename.toUtf8().constData());
162 for (qint32 i = 0; i < p_FiffEvokedSet.
evoked.size(); ++i) {
163 qInfo(
"%s (%s)\n", p_FiffEvokedSet.
evoked.at(i).comment.toUtf8().constData(), p_FiffEvokedSet.
evoked.at(i).aspectKindToString().toUtf8().constBegin());
172 FiffEvokedSet& p_FiffEvokedSet, QPair<float, float> baseline,
175 p_FiffEvokedSet.
clear();
181 QString t_sFileName = t_pStream->streamName();
183 qInfo(
"Exploring %s ...\n", t_sFileName.toUtf8().constData());
185 if (!t_pStream->open())
191 if (!t_pStream->read_meas_info(t_pStream->dirtree(), p_FiffEvokedSet.
info, meas))
198 if (processed.size() == 0) {
199 qWarning(
"Could not find processed data");
203 QList<FiffDirNode::SPtr> evoked_node = meas->dir_tree_find(
FIFFB_EVOKED);
204 if (evoked_node.size() == 0) {
205 qWarning(
"Could not find evoked data");
209 QStringList comments;
210 QList<fiff_int_t> aspect_kinds;
212 if (!t_pStream->get_evoked_entries(evoked_node, comments, aspect_kinds, t))
213 t = QString(
"None found, must use integer");
214 qInfo(
"\tFound %lld datasets\n",
static_cast<long long>(evoked_node.size()));
216 for (qint32 i = 0; i < comments.size(); ++i) {
218 qInfo(
">> Processing %s <<\n", comments[i].toUtf8().constData());
221 p_FiffEvokedSet.
evoked.push_back(t_FiffEvoked);
231 if (fileName.isEmpty()) {
232 qWarning() <<
"[FiffEvokedSet::save] Output file not specified.";
236 QFile file(fileName);
238 qWarning() <<
"[FiffEvokedSet::save] Cannot open" << fileName;
242 qInfo() <<
"[FiffEvokedSet::save] Saved" << evoked.size()
243 <<
"average(s) to" << fileName;
256 pStream->write_evoked_set(*
this);
268 if (evokedSets.isEmpty()) {
269 qWarning() <<
"[FiffEvokedSet::computeGrandAverage] No evoked sets provided.";
273 int nCat = evokedSets.first().evoked.size();
275 nCat = qMin(nCat,
static_cast<int>(eset.evoked.size()));
278 for (
int j = 0; j < nCat; ++j) {
282 for (
const QString& bad : eset.evoked[j].info.bads) {
283 if (!bads.contains(bad)) {
289 for (
const QString& name : evokedSets.first().evoked[j].info.ch_names) {
290 bool inAll = !bads.contains(name);
292 inAll = inAll && eset.evoked[j].info.ch_names.contains(name);
299 QList<FiffEvoked> picked;
301 picked.append(eset.evoked[j].pick_channels(common));
308 avg.
comment = QString(
"Grand average (n = %1)").arg(avg.
nave);
310 grandAvg.
evoked.append(avg);
322 if (bmaxSamp >= epoch.cols())
323 bmaxSamp =
static_cast<int>(epoch.cols()) - 1;
324 if (bminSamp >= bmaxSamp)
327 int nBase = bmaxSamp - bminSamp + 1;
328 for (
int c = 0; c < epoch.rows(); ++c) {
329 double baseVal = epoch.row(c).segment(bminSamp, nBase).mean();
330 epoch.row(c).array() -= baseVal;
338 const MatrixXi& events,
348 log += QString(
"Averaging: %1\n").arg(desc.
comment);
351 for (
int j = 0; j < desc.
categories.size(); ++j) {
355 int minSamp =
static_cast<int>(std::round(cat.
tmin * sfreq));
356 int maxSamp =
static_cast<int>(std::round(cat.
tmax * sfreq));
357 int ns = maxSamp - minSamp + 1;
358 int delaySamp =
static_cast<int>(std::round(cat.
delay * sfreq));
361 int bminSamp = 0, bmaxSamp = 0;
363 bminSamp =
static_cast<int>(std::round(cat.
bmin * sfreq)) - minSamp;
364 bmaxSamp =
static_cast<int>(std::round(cat.
bmax * sfreq)) - minSamp;
368 MatrixXd sumData = MatrixXd::Zero(nchan, ns);
369 MatrixXd sumSqData = MatrixXd::Zero(nchan, ns);
372 log += QString(
"\n Category: %1\n").arg(cat.
comment);
373 log += QString(
" t = %1 ... %2 ms\n").arg(1000.0 * cat.
tmin, 0,
'f', 1).arg(1000.0 * cat.
tmax, 0,
'f', 1);
376 for (
int k = 0; k < events.rows(); ++k) {
380 int evSample = events(k, 0);
381 int epochStart = evSample + delaySamp + minSamp;
382 int epochEnd = evSample + delaySamp + maxSamp;
385 if (epochStart < raw.first_samp || epochEnd > raw.
last_samp)
392 log += QString(
" Error reading epoch at sample %1\n").arg(evSample);
398 if (!checkArtifacts(epochData, raw.
info, raw.
info.
bads, desc.
rej, rejReason)) {
399 log += QString(
" %1 %2 %3 %4 [%5] %6 [omit]\n")
401 .arg(
static_cast<float>(evSample) / sfreq, -10,
'f', 3)
402 .arg(events(k, 1), 3)
403 .arg(events(k, 2), 3)
411 subtractBaseline(epochData, bminSamp, bmaxSamp);
416 epochData = epochData.cwiseAbs();
420 sumData += epochData;
422 sumSqData += epochData.cwiseProduct(epochData);
426 log += QString(
" %1 %2 %3 %4 [%5]\n")
428 .arg(
static_cast<float>(evSample) / sfreq, -10,
'f', 3)
429 .arg(events(k, 1), 3)
430 .arg(events(k, 2), 3)
437 evoked.
first = minSamp;
438 evoked.
last = maxSamp;
442 RowVectorXf times(ns);
443 for (
int s = 0; s < ns; ++s)
444 times(s) =
static_cast<float>(minSamp + s) / sfreq;
445 evoked.
times = times;
448 evoked.
data = sumData /
static_cast<double>(nave);
450 evoked.
data = MatrixXd::Zero(nchan, ns);
455 evokedSet.
evoked.append(evoked);
456 log += QString(
" nave = %1\n").arg(nave);
466 const QStringList& bads,
470 for (
int c = 0; c < epoch.rows(); ++c) {
472 if (bads.contains(info.
ch_names[c]))
475 double minVal = epoch.row(c).minCoeff();
476 double maxVal = epoch.row(c).maxCoeff();
477 double pp = maxVal - minVal;
479 int chKind = info.
chs[c].kind;
480 int chUnit = info.
chs[c].unit;
486 reason = QString(
"%1 : %2 fT > %3 fT")
488 .arg(pp * 1e15, 0,
'f', 1)
493 reason = QString(
"%1 : %2 fT < %3 fT (flat)")
495 .arg(pp * 1e15, 0,
'f', 1)
502 reason = QString(
"%1 : %2 fT/cm > %3 fT/cm")
504 .arg(pp * 1e13, 0,
'f', 1)
509 reason = QString(
"%1 : %2 fT/cm < %3 fT/cm (flat)")
511 .arg(pp * 1e13, 0,
'f', 1)
518 reason = QString(
"%1 : %2 uV > %3 uV")
520 .arg(pp * 1e6, 0,
'f', 1)
525 reason = QString(
"%1 : %2 uV < %3 uV (flat)")
527 .arg(pp * 1e6, 0,
'f', 1)
528 .arg(rej.
eegFlat * 1e6, 0,
'f', 1);
533 reason = QString(
"%1 : %2 uV > %3 uV (EOG)")
535 .arg(pp * 1e6, 0,
'f', 1)
540 reason = QString(
"%1 : EOG flat").arg(info.
ch_names[c]);
545 reason = QString(
"%1 : %2 mV > %3 mV (ECG)")
547 .arg(pp * 1e3, 0,
'f', 2)
552 reason = QString(
"%1 : ECG flat").arg(info.
ch_names[c]);
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
constexpr auto FIFFV_ECG_CH
constexpr auto FIFF_UNIT_T
constexpr auto FIFFV_EOG_CH
FIFF tag: the 16-byte tag header (kind, type, size, next) plus its decoded payload.
Set of averaged evoked responses sharing a FiffInfo, plus the ave-style category / rejection descript...
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
constexpr auto FIFFB_PROCESSED_DATA
constexpr auto FIFFB_EVOKED
Recursive node of the parsed FIFF block tree (FIFFB_* hierarchy with directory entries and children).
Stim-channel event list (sample, previous value, new value triples) with FIFF read/write helpers.
FIFF continuous raw recording: FiffInfo plus a directory of FIFF_DATA_BUFFER tags for random-access s...
FIFF file I/O, in-memory data structures and high-level readers/writers.
One CTF software-gradient compensation matrix: grade kind, calibration flag and the gradiometer × ref...
QSharedPointer< FiffDirNode > SPtr
static bool matchEvent(const AverageCategory &cat, const Eigen::MatrixXi &events, int eventIdx)
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
static FiffEvoked combine(const QList< FiffEvoked > &evokeds, const Eigen::VectorXd &weights)
static bool read(QIODevice &p_IODevice, FiffEvoked &p_FiffEvoked, QVariant setno=0, QPair< float, float > t_baseline=defaultFloatPair, bool proj=true, fiff_int_t p_aspect_kind=FIFFV_ASPECT_AVERAGE)
Artifact-rejection thresholds for the MNE-C batch averaging pipeline (gradiometer / magnetometer / EE...
One averaging category in an MNE-C ave-description file: trigger logic, timing window,...
Top-level MNE-C ave-description record: comment, category list, shared rejection limits and output fi...
QList< AverageCategory > categories
Set of FiffEvoked instances sharing one FiffInfo, plus channel-picking and compensation helpers.
static FiffEvokedSet computeAverages(const FiffRawData &raw, const AverageDescription &desc, const Eigen::MatrixXi &events, QString &log)
bool find_evoked(const FiffEvokedSet &p_FiffEvokedSet) const
static FiffEvokedSet computeGrandAverage(const QList< FiffEvokedSet > &evokedSets)
static bool checkArtifacts(const Eigen::MatrixXd &epoch, const FiffInfo &info, const QStringList &bads, const RejectionParams &rej, QString &reason)
static void subtractBaseline(Eigen::MatrixXd &epoch, int bminSamp, int bmaxSamp)
Subtract baseline from each channel of an epoch.
FiffEvokedSet pick_channels(const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
static bool read(QIODevice &p_IODevice, FiffEvokedSet &p_FiffEvokedSet, QPair< float, float > baseline=defaultFloatPair, bool proj=true)
QList< FiffEvoked > evoked
bool compensate_to(fiff_int_t to)
bool save(const QString &fileName) const
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
FiffInfo pick_info(const Eigen::RowVectorXi &sel=defaultVectorXi) const
static Eigen::RowVectorXi pick_channels(const QStringList &ch_names, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList)
Continuous FIFF raw recording: FiffInfo plus a random-access directory of FIFF_DATA_BUFFER tags.
bool read_raw_segment(Eigen::MatrixXd &data, Eigen::MatrixXd ×, 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)