[7f9fb8]: / mne / _fiff / compensator.py

Download this file

169 lines (143 with data), 5.7 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
import numpy as np
from ..utils import fill_doc
from .constants import FIFF
def get_current_comp(info):
"""Get the current compensation in effect in the data."""
comp = None
first_comp = -1
for k, chan in enumerate(info["chs"]):
if chan["kind"] == FIFF.FIFFV_MEG_CH:
comp = int(chan["coil_type"]) >> 16
if first_comp < 0:
first_comp = comp
elif comp != first_comp:
raise ValueError("Compensation is not set equally on all MEG channels")
return comp
def set_current_comp(info, comp):
"""Set the current compensation in effect in the data."""
comp_now = get_current_comp(info)
for k, chan in enumerate(info["chs"]):
if chan["kind"] == FIFF.FIFFV_MEG_CH:
rem = chan["coil_type"] - (comp_now << 16)
chan["coil_type"] = int(rem + (comp << 16))
def _make_compensator(info, grade):
"""Auxiliary function for make_compensator."""
for k in range(len(info["comps"])):
if info["comps"][k]["kind"] == grade:
this_data = info["comps"][k]["data"]
# Create the preselector
presel = np.zeros((this_data["ncol"], info["nchan"]))
for col, col_name in enumerate(this_data["col_names"]):
ind = [k for k, ch in enumerate(info["ch_names"]) if ch == col_name]
if len(ind) == 0:
raise ValueError(f"Channel {col_name} is not available in data")
elif len(ind) > 1:
raise ValueError(f"Ambiguous channel {col_name}")
presel[col, ind[0]] = 1.0
# Create the postselector (zero entries for channels not found)
postsel = np.zeros((info["nchan"], this_data["nrow"]))
for c, ch_name in enumerate(info["ch_names"]):
ind = [
k for k, ch in enumerate(this_data["row_names"]) if ch == ch_name
]
if len(ind) > 1:
raise ValueError(f"Ambiguous channel {ch_name}")
elif len(ind) == 1:
postsel[c, ind[0]] = 1.0
# else, don't use it at all (postsel[c, ?] = 0.0) by allocation
this_comp = np.dot(postsel, np.dot(this_data["data"], presel))
return this_comp
raise ValueError(f"Desired compensation matrix (grade = {grade:d}) not found")
@fill_doc
def make_compensator(info, from_, to, exclude_comp_chs=False):
"""Return compensation matrix eg. for CTF system.
Create a compensation matrix to bring the data from one compensation
state to another.
Parameters
----------
%(info_not_none)s
from_ : int
Compensation in the input data.
to : int
Desired compensation in the output.
exclude_comp_chs : bool
Exclude compensation channels from the output.
Returns
-------
comp : array | None.
The compensation matrix. Might be None if no compensation
is needed (from == to).
"""
if from_ == to:
return None
# s_orig = s_from + C1*s_from = (I + C1)*s_from
# s_to = s_orig - C2*s_orig = (I - C2)*s_orig
# s_to = (I - C2)*(I + C1)*s_from = (I + C1 - C2 - C2*C1)*s_from
if from_ != 0:
C1 = _make_compensator(info, from_)
comp_from_0 = np.linalg.inv(np.eye(info["nchan"]) - C1)
if to != 0:
C2 = _make_compensator(info, to)
comp_0_to = np.eye(info["nchan"]) - C2
if from_ != 0:
if to != 0:
# This is mathematically equivalent, but has higher numerical
# error than using the inverse to always go to zero and back
# comp = np.eye(info['nchan']) + C1 - C2 - np.dot(C2, C1)
comp = np.dot(comp_0_to, comp_from_0)
else:
comp = comp_from_0
else:
# from == 0, to != 0 guaranteed here
comp = comp_0_to
if exclude_comp_chs:
pick = [
k for k, c in enumerate(info["chs"]) if c["kind"] != FIFF.FIFFV_REF_MEG_CH
]
if len(pick) == 0:
raise ValueError(
"Nothing remains after excluding the compensation channels"
)
comp = comp[pick, :]
return comp
# @verbose
# def compensate_to(data, to, verbose=None):
# """
# %
# % [newdata] = mne_compensate_to(data,to)
# %
# % Apply compensation to the data as desired
# %
# """
#
# newdata = data.copy()
# now = get_current_comp(newdata['info'])
#
# # Are we there already?
# if now == to:
# logger.info('Data are already compensated as desired')
#
# # Make the compensator and apply it to all data sets
# comp = make_compensator(newdata['info'], now, to)
# for k in range(len(newdata['evoked'])):
# newdata['evoked'][k]['epochs'] = np.dot(comp,
# newdata['evoked'][k]['epochs'])
#
# # Update the compensation info in the channel descriptors
# newdata['info']['chs'] = set_current_comp(newdata['info']['chs'], to)
# return newdata
# def set_current_comp(chs, value):
# """Set the current compensation value in the channel info structures
# """
# new_chs = chs
#
# lower_half = int('FFFF', 16) # hex2dec('FFFF')
# for k in range(len(chs)):
# if chs[k]['kind'] == FIFF.FIFFV_MEG_CH:
# coil_type = float(chs[k]['coil_type']) & lower_half
# new_chs[k]['coil_type'] = int(coil_type | (value << 16))
#
# return new_chs