v2.0.0
Loading...
Searching...
No Matches
fiff_info.cpp
Go to the documentation of this file.
1//=============================================================================================================
21
22//=============================================================================================================
23// INCLUDES
24//=============================================================================================================
25
26#include "fiff_info.h"
27#include "fiff_stream.h"
28#include "fiff_file.h"
29
30#include <utils/ioutils.h>
31#include <QDebug>
32
33#include <Eigen/LU>
34
35//=============================================================================================================
36// USED NAMESPACES
37//=============================================================================================================
38
39using namespace FIFFLIB;
40using namespace UTILSLIB;
41using namespace Eigen;
42
43//=============================================================================================================
44// DEFINE MEMBER METHODS
45//=============================================================================================================
46
48: FiffInfoBase() //nchan(-1)
49, sfreq(-1.0)
50, linefreq(-1.0)
51, highpass(-1.0)
52, lowpass(-1.0)
53, gantry_angle(-1)
54, acq_pars("")
55, acq_stim("")
56{
57 meas_date[0] = -1;
58}
59
60//=============================================================================================================
61
62FiffInfo::FiffInfo(const FiffInfo& p_FiffInfo)
63: FiffInfoBase(p_FiffInfo)
64, file_id(p_FiffInfo.file_id)
65, sfreq(p_FiffInfo.sfreq)
66, linefreq(p_FiffInfo.linefreq)
67, highpass(p_FiffInfo.highpass)
68, lowpass(p_FiffInfo.lowpass)
69, proj_id(p_FiffInfo.proj_id)
70, proj_name(p_FiffInfo.proj_name)
72, experimenter(p_FiffInfo.experimenter)
73, description(p_FiffInfo.description)
74, utc_offset(p_FiffInfo.utc_offset)
75, gantry_angle(p_FiffInfo.gantry_angle)
76, dev_ctf_t(p_FiffInfo.dev_ctf_t)
77, dig(p_FiffInfo.dig)
78, dig_trans(p_FiffInfo.dig_trans)
79, projs(p_FiffInfo.projs)
80, comps(p_FiffInfo.comps)
81, acq_pars(p_FiffInfo.acq_pars)
82, acq_stim(p_FiffInfo.acq_stim)
84{
85 meas_date[0] = p_FiffInfo.meas_date[0];
86 meas_date[1] = p_FiffInfo.meas_date[1];
87}
88
89//=============================================================================================================
90
94
95//=============================================================================================================
96
98{
100 file_id = FiffId();
101 meas_date[0] = -1;
102 sfreq = -1.0;
103 linefreq = -1.0;
104 highpass = -1.0;
105 lowpass = -1.0;
106 proj_id = -1;
107 proj_name = "";
108 xplotter_layout = "";
109 experimenter = "";
110 description = "";
111 utc_offset = "";
112 gantry_angle = -1;
113 dev_ctf_t.clear();
114 dig.clear();
115 dig_trans.clear();
116 projs.clear();
117 comps.clear();
118 acq_pars = "";
119 acq_stim = "";
120 hpi_coil_freqs.clear();
121}
122
123//=============================================================================================================
124
126{
127 qint32 comp = 0;
128 qint32 first_comp = -1;
129
130 qint32 k = 0;
131 for (k = 0; k < this->nchan; ++k) {
132 if (this->chs[k].kind == FIFFV_MEG_CH) {
133 comp = this->chs[k].chpos.coil_type >> 16;
134 if (first_comp < 0)
135 first_comp = comp;
136 else if (comp != first_comp)
137 qWarning("Compensation is not set equally on all MEG channels");
138 }
139 }
140 return comp;
141}
142
143//=============================================================================================================
144
145bool FiffInfo::make_compensator(fiff_int_t from, fiff_int_t to, FiffCtfComp& ctf_comp, bool exclude_comp_chs) const
146{
147 MatrixXd C1, C2, comp_tmp;
148
149 // if(ctf_comp.data)
150 // delete ctf_comp.data;
151 ctf_comp.data->clear();
152
153 if (from == to) {
154 ctf_comp.data->data = MatrixXd::Identity(this->nchan, this->nchan);
155 return false;
156 }
157
158 if (from == 0)
159 C1 = MatrixXd::Zero(this->nchan, this->nchan);
160 else {
161 if (!this->make_compensator(from, C1)) {
162 qWarning("Cannot create compensator C1\n");
163 qWarning("Desired compensation matrix (kind = %d) not found\n", from);
164 return false;
165 }
166 }
167
168 if (to == 0)
169 C2 = MatrixXd::Zero(this->nchan, this->nchan);
170 else {
171 if (!this->make_compensator(to, C2)) {
172 qWarning("Cannot create compensator C2\n");
173 qWarning("Desired compensation matrix (kind = %d) not found\n", to);
174 return false;
175 }
176 }
177 //
178 // s_from = (I - C1)*s_orig => s_orig = (I - C1)^-1 * s_from
179 // s_to = (I - C2)*s_orig
180 // (I + C1) is only the inverse when C1*C1 = 0, which fails as soon as
181 // reference channels are themselves compensated (e.g. CTF grade 1).
182 //
183 const MatrixXd eye = MatrixXd::Identity(this->nchan, this->nchan);
184 comp_tmp = (eye - C2) * (eye - C1).partialPivLu().inverse();
185
186 qint32 k;
187 if (exclude_comp_chs) {
188 VectorXi pick = VectorXi::Zero(this->nchan);
189 qint32 npick = 0;
190 for (k = 0; k < this->nchan; ++k) {
191 if (this->chs[k].kind != FIFFV_REF_MEG_CH) {
192 pick(npick) = k;
193 ++npick;
194 }
195 }
196 if (npick == 0) {
197 qWarning("Nothing remains after excluding the compensation channels\n");
198 return false;
199 }
200
201 ctf_comp.data->data.resize(npick, this->nchan);
202 for (k = 0; k < npick; ++k)
203 ctf_comp.data->data.row(k) = comp_tmp.block(pick(k), 0, 1, this->nchan);
204 } else {
205 ctf_comp.data->data = comp_tmp;
206 }
207 // FiffRawData applies the compensator only when kind is set.
208 ctf_comp.kind = to;
209
210 return true;
211}
212
213//=============================================================================================================
214
215bool FiffInfo::make_compensator(fiff_int_t kind, MatrixXd& this_comp) const //private method
216{
217 FiffNamedMatrix::SDPtr this_data;
218 MatrixXd presel, postsel;
219 qint32 k, col, c, ch = 0, row, row_ch = 0, channelAvailable;
220
221 for (k = 0; k < this->comps.size(); ++k) {
222 if (this->comps[k].kind == kind) {
223 this_data = this->comps[k].data;
224
225 //
226 // Create the preselector
227 //
228 presel = MatrixXd::Zero(this_data->ncol, this->nchan);
229
230 for (col = 0; col < this_data->ncol; ++col) {
231 channelAvailable = 0;
232 for (c = 0; c < this->ch_names.size(); ++c) {
233 if (QString::compare(this_data->col_names.at(col), this->ch_names.at(c)) == 0) {
234 ++channelAvailable;
235 ch = c;
236 }
237 }
238 if (channelAvailable == 0) {
239 qWarning("Channel %s is not available in data\n", this_data->col_names.at(col).toUtf8().constData());
240 return false;
241 } else if (channelAvailable > 1) {
242 qWarning("Ambiguous channel %s", this_data->col_names.at(col).toUtf8().constData());
243 return false;
244 }
245 presel(col, ch) = 1.0;
246 }
247 //
248 // Create the postselector
249 //
250 postsel = MatrixXd::Zero(this->nchan, this_data->nrow);
251
252 for (c = 0; c < this->nchan; ++c) {
253 channelAvailable = 0;
254 for (row = 0; row < this_data->row_names.size(); ++row) {
255 if (QString::compare(this->ch_names.at(c), this_data->row_names.at(row)) == 0) {
256 ++channelAvailable;
257 row_ch = row;
258 }
259 }
260 if (channelAvailable > 1) {
261 qWarning("Ambiguous channel %s", this->ch_names.at(c).toUtf8().constData());
262 return false;
263 } else if (channelAvailable == 1) {
264 postsel(c, row_ch) = 1.0;
265 }
266 }
267 this_comp = postsel * this_data->data * presel;
268 return true;
269 }
270 }
271 this_comp = defaultMatrixXd;
272 return false;
273}
274
275//=============================================================================================================
276
277FiffInfo FiffInfo::pick_info(const RowVectorXi& sel) const
278{
279 FiffInfo res = *this; //new FiffInfo(this);
280 if (sel.size() == 0)
281 return res;
282
283 //ToDo when pointer List do delation
284 res.chs.clear();
285 res.ch_names.clear();
286
287 qint32 idx;
288 for (qint32 i = 0; i < sel.size(); ++i) {
289 idx = sel[i];
290 res.chs.append(this->chs[idx]);
291 res.ch_names.append(this->ch_names[idx]);
292 }
293 res.nchan = static_cast<int>(sel.size());
294 res.bads.removeIf([&res](const QString& bad) { return !res.ch_names.contains(bad); });
295
296 return res;
297}
298
299//=============================================================================================================
300
301QList<FiffChInfo> FiffInfo::set_current_comp(QList<FiffChInfo>& listFiffChInfo, fiff_int_t value)
302{
303 QList<FiffChInfo> newList;
304 qint32 k;
305 fiff_int_t coil_type;
306
307 for (k = 0; k < listFiffChInfo.size(); ++k)
308 newList.append(listFiffChInfo[k]);
309
310 qint32 lower_half = 65535; // hex2dec('FFFF');
311 for (k = 0; k < listFiffChInfo.size(); ++k) {
312 if (listFiffChInfo[k].kind == FIFFV_MEG_CH) {
313 coil_type = listFiffChInfo[k].chpos.coil_type & lower_half;
314 newList[k].chpos.coil_type = (coil_type | (value << 16));
315 }
316 }
317 return newList;
318}
319
320//=============================================================================================================
321
322bool FiffInfo::readMegEegChannels(const QString& name,
323 bool do_meg,
324 bool do_eeg,
325 const QStringList& bads,
326 QList<FiffChInfo>& chsp,
327 int& nmegp,
328 int& neegp)
329{
330 QFile file(name);
331 FiffStream::SPtr stream(new FiffStream(&file));
332
333 if (!stream->open())
334 return false;
335
336 FiffInfo info;
337 FiffDirNode::SPtr infoNode;
338 if (!stream->read_meas_info(stream->dirtree(), info, infoNode)) {
339 qCritical("%s : could not read measurement info", name.toUtf8().data());
340 stream->close();
341 return false;
342 }
343 stream->close();
344
345 QList<FiffChInfo> meg;
346 QList<FiffChInfo> eeg;
347
348 for (int k = 0; k < info.chs.size(); k++) {
349 if (bads.contains(info.chs[k].ch_name))
350 continue;
351 if (do_meg && info.chs[k].kind == FIFFV_MEG_CH)
352 meg.append(info.chs[k]);
353 else if (do_eeg && info.chs[k].isValidEeg())
354 eeg.append(info.chs[k]);
355 }
356
357 chsp.clear();
358 chsp.reserve(meg.size() + eeg.size());
359 chsp.append(meg);
360 chsp.append(eeg);
361
362 nmegp = meg.size();
363 neegp = eeg.size();
364 return true;
365}
366
367//=============================================================================================================
368
370{
371 //
372 // We will always write floats
373 //
374 fiff_int_t data_type = 4;
375 QList<FiffChInfo> chsToWrite;
376
377 for (qint32 k = 0; k < this->nchan; ++k)
378 chsToWrite << this->chs[k];
379
380 fiff_int_t nchanToWrite = chsToWrite.size();
381
382 //
383 // write the essentials
384 //
385 p_pStream->start_block(FIFFB_MEAS); //4
386 p_pStream->write_id(FIFF_BLOCK_ID); //5
387 if (this->meas_id.version != -1) {
388 p_pStream->write_id(FIFF_PARENT_BLOCK_ID, this->meas_id); //6
389 }
390 //
391 // Measurement info
392 //
393 p_pStream->start_block(FIFFB_MEAS_INFO); //7
394
395 //
396 // Blocks from the original -> skip this
397 //
398 // QList<fiff_int_t> blocks;
399 // blocks << FIFFB_SUBJECT << FIFFB_HPI_MEAS << FIFFB_HPI_RESULT << FIFFB_ISOTRAK << FIFFB_PROCESSING_HISTORY;
400 bool have_hpi_result = false;
401 bool have_isotrak = false;
402 //
403 // megacq parameters
404 //
405 if (!this->acq_pars.isEmpty() || !this->acq_stim.isEmpty()) {
406 p_pStream->start_block(FIFFB_DACQ_PARS);
407 if (!this->acq_pars.isEmpty())
408 p_pStream->write_string(FIFF_DACQ_PARS, this->acq_pars);
409
410 if (!this->acq_stim.isEmpty())
411 p_pStream->write_string(FIFF_DACQ_STIM, this->acq_stim);
412
413 p_pStream->end_block(FIFFB_DACQ_PARS);
414 }
415 //
416 // Coordinate transformations if the HPI result block was not there
417 //
418 if (!have_hpi_result) {
419 if (!this->dev_head_t.isEmpty())
420 p_pStream->write_coord_trans(this->dev_head_t);
421
422 if (!this->ctf_head_t.isEmpty())
423 p_pStream->write_coord_trans(this->ctf_head_t);
424 }
425 //
426 // Polhemus data
427 //
428 if (this->dig.size() > 0 && !have_isotrak) {
429 p_pStream->start_block(FIFFB_ISOTRAK);
430 for (qint32 k = 0; k < this->dig.size(); ++k)
431 p_pStream->write_dig_point(this->dig[k]);
432
433 p_pStream->end_block(FIFFB_ISOTRAK);
434 }
435 //
436 // Projectors
437 //
438 p_pStream->write_proj(this->projs);
439 //
440 // CTF compensation info
441 //
442 p_pStream->write_ctf_comp(this->comps);
443 //
444 // Bad channels
445 //
446 if (this->bads.size() > 0) {
448 p_pStream->write_name_list(FIFF_MNE_CH_NAME_LIST, this->bads);
450 }
451 //
452 // General
453 //
454 p_pStream->write_float(FIFF_SFREQ, &this->sfreq);
455 p_pStream->write_float(FIFF_LINE_FREQ, &this->linefreq);
456 p_pStream->write_float(FIFF_HIGHPASS, &this->highpass);
457 p_pStream->write_float(FIFF_LOWPASS, &this->lowpass);
459 p_pStream->write_string(FIFF_DESCRIPTION, this->description);
460 p_pStream->write_string(FIFF_UTC_OFFSET, this->utc_offset);
461 p_pStream->write_string(FIFF_PROJ_NAME, this->proj_name);
462 p_pStream->write_int(FIFF_PROJ_ID, &this->proj_id);
463 p_pStream->write_int(FIFF_GANTRY_ANGLE, &this->gantry_angle);
464 p_pStream->write_int(FIFF_NCHAN, &nchanToWrite);
465 p_pStream->write_int(FIFF_DATA_PACK, &data_type);
466 if (this->meas_date[0] != -1)
467 p_pStream->write_int(FIFF_MEAS_DATE, this->meas_date, 2);
468 //
469 // Channel info
470 //
471 MatrixXd cals(1, nchanToWrite);
472
473 for (qint32 k = 0; k < nchanToWrite; ++k) {
474 //
475 // Scan numbers may have been messed up
476 //
477 chsToWrite[k].scanNo = k + 1; //+1 because
478 // chs[k].range = 1.0f;//Why? -> cause its already calibrated through reading
479 cals(0, k) = static_cast<double>(chsToWrite[k].cal); //ToDo whats going on with cals?
480 p_pStream->write_ch_info(chsToWrite[k]);
481 }
482 //
483 //
484 p_pStream->end_block(FIFFB_MEAS_INFO);
485}
486
487//=============================================================================================================
488
489void FiffInfo::print() const
490{
491 qInfo().noquote().nospace() << "Sample frequency: " << sfreq;
492 qInfo().noquote().nospace() << "LineFreq: " << linefreq << " | Highpass: " << highpass << " | Lowpass: " << lowpass;
493 qInfo().noquote().nospace() << "Number of digitizer points: " << dig.size();
494 for (auto& point : dig) {
495 if (point.kind == FIFFV_POINT_HPI) {
496 qInfo().noquote().nospace() << "HPI Point " << point.ident << " - " << point.r[0] << ", " << point.r[1] << ", " << point.r[2];
497 }
498 }
499}
constexpr auto FIFFV_POINT_HPI
#define FIFFV_MEG_CH
constexpr auto FIFF_MNE_CH_NAME_LIST
constexpr auto FIFFB_MNE_BAD_CHANNELS
constexpr auto FIFFV_REF_MEG_CH
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
constexpr auto FIFFB_DACQ_PARS
Definition fiff_file.h:370
constexpr auto FIFF_PROJ_NAME
Definition fiff_file.h:567
constexpr auto FIFF_DACQ_STIM
Definition fiff_file.h:342
constexpr auto FIFF_GANTRY_ANGLE
Definition fiff_file.h:546
constexpr auto FIFF_EXPERIMENTER
Definition fiff_file.h:456
constexpr auto FIFF_UTC_OFFSET
Definition fiff_file.h:442
constexpr auto FIFF_LINE_FREQ
Definition fiff_file.h:480
constexpr auto FIFF_HIGHPASS
Definition fiff_file.h:467
constexpr auto FIFF_DATA_PACK
Definition fiff_file.h:446
#define FIFF_DESCRIPTION
Definition fiff_file.h:477
constexpr auto FIFF_DACQ_PARS
Definition fiff_file.h:341
constexpr auto FIFF_SFREQ
Definition fiff_file.h:445
constexpr auto FIFFB_ISOTRAK
Definition fiff_file.h:361
constexpr auto FIFF_NCHAN
Definition fiff_file.h:444
constexpr auto FIFF_LOWPASS
Definition fiff_file.h:463
constexpr auto FIFF_PARENT_BLOCK_ID
Definition fiff_file.h:324
constexpr auto FIFF_PROJ_ID
Definition fiff_file.h:566
constexpr auto FIFFB_MEAS_INFO
Definition fiff_file.h:354
constexpr auto FIFFB_MEAS
Definition fiff_file.h:353
constexpr auto FIFF_BLOCK_ID
Definition fiff_file.h:317
constexpr auto FIFF_MEAS_DATE
Definition fiff_file.h:448
Header-only Eigen matrix text I/O — round-trips dense matrices to whitespace-separated ASCII for cros...
FIFF file I/O, in-memory data structures and high-level readers/writers.
qint32 fiff_int_t
Definition fiff_types.h:86
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
One CTF software-gradient compensation matrix: grade kind, calibration flag and the gradiometer × ref...
QSharedPointer< FiffDirNode > SPtr
128-bit FIFF identifier: hardware machine ID plus creation time, stamped on every file and block.
Definition fiff_id.h:69
FiffInfo pick_info(const Eigen::RowVectorXi &sel=defaultVectorXi) const
void set_current_comp(fiff_int_t value)
Definition fiff_info.h:317
void print() const
static bool readMegEegChannels(const QString &name, bool do_meg, bool do_eeg, const QStringList &bads, QList< FiffChInfo > &chsp, int &nmegp, int &neegp)
QString description
Definition fiff_info.h:286
FiffCoordTrans dev_ctf_t
Definition fiff_info.h:289
QList< float > hpi_coil_freqs
Definition fiff_info.h:296
fiff_int_t gantry_angle
Definition fiff_info.h:288
void writeToStream(FiffStream *p_pStream) const
FiffCoordTrans dig_trans
Definition fiff_info.h:291
QList< FiffCtfComp > comps
Definition fiff_info.h:293
qint32 get_current_comp()
QString xplotter_layout
Definition fiff_info.h:284
fiff_int_t meas_date[2]
Definition fiff_info.h:277
bool make_compensator(fiff_int_t from, fiff_int_t to, FiffCtfComp &ctf_comp, bool exclude_comp_chs=false) const
QList< FiffDigPoint > dig
Definition fiff_info.h:290
QString experimenter
Definition fiff_info.h:285
QList< FiffProj > projs
Definition fiff_info.h:292
QList< FiffChInfo > chs
FiffCoordTrans ctf_head_t
FiffCoordTrans dev_head_t
QSharedDataPointer< FiffNamedMatrix > SDPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
fiff_long_t start_block(fiff_int_t kind)
fiff_long_t write_proj(const QList< FiffProj > &projs)
QSharedPointer< FiffStream > SPtr
fiff_long_t write_dig_point(const FiffDigPoint &dig)
fiff_long_t write_int(fiff_int_t kind, const fiff_int_t *data, fiff_int_t nel=1, fiff_int_t next=FIFFV_NEXT_SEQ)
fiff_long_t write_float(fiff_int_t kind, const float *data, fiff_int_t nel=1)
fiff_long_t write_id(fiff_int_t kind, const FiffId &id=FiffId::getDefault())
fiff_long_t write_coord_trans(const FiffCoordTrans &trans)
fiff_long_t write_name_list(fiff_int_t kind, const QStringList &data)
fiff_long_t write_string(fiff_int_t kind, const QString &data)
fiff_long_t end_block(fiff_int_t kind, fiff_int_t next=FIFFV_NEXT_SEQ)
fiff_long_t write_ch_info(const FiffChInfo &ch)
fiff_long_t write_ctf_comp(const QList< FiffCtfComp > &comps)