Skip to content

Commit

Permalink
ENH: Add temperature and galvanic (#11090)
Browse files Browse the repository at this point in the history
* ENH: Add temperature and galvanic

* FIX: Use correct name

* FIX: Flake

* FIX: Rename

* DOC: Sp

* Update doc/_includes/channel_types.rst

Co-authored-by: Richard Höchenberger <richard.hoechenberger@gmail.com>

Co-authored-by: Richard Höchenberger <richard.hoechenberger@gmail.com>
  • Loading branch information
larsoner and hoechenberger authored Aug 26, 2022
1 parent bdc435d commit 7aa3a27
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 24 deletions.
4 changes: 4 additions & 0 deletions doc/_includes/channel_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,8 @@ ias Internal Active Shielding data

syst System status channel information
(Triux systems only)

temperature Temperature Degrees Celsius

gsr Galvanic skin response Siemens
============= ========================================= =================
1 change: 1 addition & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Enhancements
- Add ``starting_affine`` keyword argument to :func:`mne.transforms.compute_volume_registration` to initialize an alignment with an affine (:gh:`11020` by `Alex Rockhill`_)
- The ``trans`` parameter in :func:`mne.make_field_map` now accepts a :class:`~pathlib.Path` object, and uses standardised loading logic (:gh:`10784` by :newcontrib:`Andrew Quinn`)
- Add HTML representation for `~mne.Evoked` in Jupyter Notebooks (:gh:`11075` by `Valerii Chirkov`_ and `Andrew Quinn`_)
- Add support for ``temperature`` and ``gsr`` (galvanic skin response, i.e., electrodermal activity) channel types (:gh:`11090` by `Eric Larson`_)
- Allow :func:`mne.beamformer.make_dics` to take ``pick_ori='vector'`` to compute vector source estimates (:gh:`19080` by `Alex Rockhill`_)
- Add ``on_missing`` functionality to all of our classes that have a ``drop_channels`` method, to control what happens when channel names are not in the object (:gh:`11077` by `Andrew Quinn`_)

Expand Down
12 changes: 7 additions & 5 deletions mne/channels/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def set_channel_types(self, mapping, verbose=None):
ecg, eeg, emg, eog, exci, ias, misc, resp, seeg, dbs, stim, syst,
ecog, hbo, hbr, fnirs_cw_amplitude, fnirs_fd_ac_amplitude,
fnirs_fd_phase, fnirs_od
fnirs_fd_phase, fnirs_od, temperature, gsr
.. versionadded:: 0.9.0
"""
Expand Down Expand Up @@ -590,11 +590,12 @@ class UpdateChannelsMixin(object):

@verbose
def pick_types(self, meg=False, eeg=False, stim=False, eog=False,
ecg=False, emg=False, ref_meg='auto', misc=False,
ecg=False, emg=False, ref_meg='auto', *, misc=False,
resp=False, chpi=False, exci=False, ias=False, syst=False,
seeg=False, dipole=False, gof=False, bio=False,
ecog=False, fnirs=False, csd=False, dbs=False, include=(),
exclude='bads', selection=None, verbose=None):
ecog=False, fnirs=False, csd=False, dbs=False,
temperature=False, gsr=False,
include=(), exclude='bads', selection=None, verbose=None):
"""Pick some channels by type and names.
Parameters
Expand All @@ -620,7 +621,8 @@ def pick_types(self, meg=False, eeg=False, stim=False, eog=False,
ref_meg=ref_meg, misc=misc, resp=resp, chpi=chpi, exci=exci,
ias=ias, syst=syst, seeg=seeg, dipole=dipole, gof=gof, bio=bio,
ecog=ecog, fnirs=fnirs, csd=csd, dbs=dbs, include=include,
exclude=exclude, selection=selection)
exclude=exclude, selection=selection, temperature=temperature,
gsr=gsr)

self._pick_drop_channels(idx)

Expand Down
16 changes: 10 additions & 6 deletions mne/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,27 @@
exci='k', ias='k', syst='k', seeg='saddlebrown', dbs='seagreen',
dipole='k', gof='k', bio='k', ecog='k', hbo='#AA3377', hbr='b',
fnirs_cw_amplitude='k', fnirs_fd_ac_amplitude='k',
fnirs_fd_phase='k', fnirs_od='k', csd='k', whitened='k'),
fnirs_fd_phase='k', fnirs_od='k', csd='k', whitened='k',
gsr='#666633', temperature='#663333'),
si_units=dict(mag='T', grad='T/m', eeg='V', eog='V', ecg='V', emg='V',
misc='AU', seeg='V', dbs='V', dipole='Am', gof='GOF',
bio='V', ecog='V', hbo='M', hbr='M', ref_meg='T',
fnirs_cw_amplitude='V', fnirs_fd_ac_amplitude='V',
fnirs_fd_phase='rad', fnirs_od='V', csd='V/m²',
whitened='Z'),
whitened='Z', gsr='S', temperature='C'),
units=dict(mag='fT', grad='fT/cm', eeg='µV', eog='µV', ecg='µV', emg='µV',
misc='AU', seeg='mV', dbs='µV', dipole='nAm', gof='GOF',
bio='µV', ecog='µV', hbo='µM', hbr='µM', ref_meg='fT',
fnirs_cw_amplitude='V', fnirs_fd_ac_amplitude='V',
fnirs_fd_phase='rad', fnirs_od='V', csd='mV/m²',
whitened='Z'),
whitened='Z', gsr='S', temperature='C'),
# scalings for the units
scalings=dict(mag=1e15, grad=1e13, eeg=1e6, eog=1e6, emg=1e6, ecg=1e6,
misc=1.0, seeg=1e3, dbs=1e6, ecog=1e6, dipole=1e9, gof=1.0,
bio=1e6, hbo=1e6, hbr=1e6, ref_meg=1e15,
fnirs_cw_amplitude=1.0, fnirs_fd_ac_amplitude=1.0,
fnirs_fd_phase=1., fnirs_od=1.0, csd=1e3, whitened=1.),
fnirs_fd_phase=1., fnirs_od=1.0, csd=1e3, whitened=1.,
gsr=1., temperature=1.),
# rough guess for a good plot
scalings_plot_raw=dict(mag=1e-12, grad=4e-11, eeg=20e-6, eog=150e-6,
ecg=5e-4, emg=1e-3, ref_meg=1e-12, misc='auto',
Expand All @@ -39,7 +41,8 @@
hbr=10e-6, whitened=10., fnirs_cw_amplitude=2e-2,
fnirs_fd_ac_amplitude=2e-2, fnirs_fd_phase=2e-1,
fnirs_od=2e-2, csd=200e-4,
dipole=1e-7, gof=1e2),
dipole=1e-7, gof=1e2,
gsr=1., temperature=1.),
scalings_cov_rank=dict(mag=1e12, grad=1e11, eeg=1e5, # ~100x scalings
seeg=1e1, dbs=1e4, ecog=1e4, hbo=1e4, hbr=1e4),
ylim=dict(mag=(-600., 600.), grad=(-200., 200.), eeg=(-200., 200.),
Expand All @@ -55,7 +58,8 @@
fnirs_fd_phase='fNIRS (FD phase)',
fnirs_od='fNIRS (OD)', hbr='Deoxyhemoglobin',
gof='Goodness of fit', csd='Current source density',
stim='Stimulus',
stim='Stimulus', gsr='Galvanic skin response',
temperature='Temperature',
),
mask_params=dict(marker='o',
markerfacecolor='w',
Expand Down
8 changes: 6 additions & 2 deletions mne/io/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@
FIFF.FIFFV_DIPOLE_WAVE = 1000 # Dipole time curve (xplotter/xfit)
FIFF.FIFFV_GOODNESS_FIT = 1001 # Goodness of fit (xplotter/xfit)
FIFF.FIFFV_FNIRS_CH = 1100 # Functional near-infrared spectroscopy
FIFF.FIFFV_TEMPERATURE_CH = 1200 # Functional near-infrared spectroscopy
FIFF.FIFFV_GALVANIC_CH = 1300 # Galvanic skin response
_ch_kind_named = {key: key for key in (
FIFF.FIFFV_BIO_CH,
FIFF.FIFFV_MEG_CH,
Expand All @@ -223,6 +225,8 @@
FIFF.FIFFV_DIPOLE_WAVE,
FIFF.FIFFV_GOODNESS_FIT,
FIFF.FIFFV_FNIRS_CH,
FIFF.FIFFV_GALVANIC_CH,
FIFF.FIFFV_TEMPERATURE_CH,
)}

#
Expand Down Expand Up @@ -839,7 +843,7 @@
FIFF.FIFF_UNIT_V = 107 # volt
FIFF.FIFF_UNIT_F = 108 # farad
FIFF.FIFF_UNIT_OHM = 109 # ohm
FIFF.FIFF_UNIT_MHO = 110 # one per ohm
FIFF.FIFF_UNIT_S = 110 # Siemens (same as Moh, what fiff-constants calls it)
FIFF.FIFF_UNIT_WB = 111 # weber
FIFF.FIFF_UNIT_T = 112 # tesla
FIFF.FIFF_UNIT_H = 113 # Henry
Expand All @@ -861,7 +865,7 @@
FIFF.FIFF_UNIT_CD, FIFF.FIFF_UNIT_MOL_M3, FIFF.FIFF_UNIT_HZ,
FIFF.FIFF_UNIT_N, FIFF.FIFF_UNIT_PA, FIFF.FIFF_UNIT_J, FIFF.FIFF_UNIT_W,
FIFF.FIFF_UNIT_C, FIFF.FIFF_UNIT_V, FIFF.FIFF_UNIT_F, FIFF.FIFF_UNIT_OHM,
FIFF.FIFF_UNIT_MHO, FIFF.FIFF_UNIT_WB, FIFF.FIFF_UNIT_T, FIFF.FIFF_UNIT_H,
FIFF.FIFF_UNIT_S, FIFF.FIFF_UNIT_WB, FIFF.FIFF_UNIT_T, FIFF.FIFF_UNIT_H,
FIFF.FIFF_UNIT_CEL, FIFF.FIFF_UNIT_LM, FIFF.FIFF_UNIT_LX,
FIFF.FIFF_UNIT_V_M2, FIFF.FIFF_UNIT_T_M, FIFF.FIFF_UNIT_AM,
FIFF.FIFF_UNIT_AM_M2, FIFF.FIFF_UNIT_AM_M3,
Expand Down
29 changes: 20 additions & 9 deletions mne/io/pick.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ def get_channel_type_constants(include_defaults=False):
coil_type=FIFF.FIFFV_COIL_FNIRS_HBR),
csd=dict(kind=FIFF.FIFFV_EEG_CH,
unit=FIFF.FIFF_UNIT_V_M2,
coil_type=FIFF.FIFFV_COIL_EEG_CSD))
coil_type=FIFF.FIFFV_COIL_EEG_CSD),
temperature=dict(kind=FIFF.FIFFV_TEMPERATURE_CH,
unit=FIFF.FIFF_UNIT_C),
gsr=dict(kind=FIFF.FIFFV_GALVANIC_CH,
unit=FIFF.FIFF_UNIT_S),
)
if include_defaults:
coil_none = dict(coil_type=FIFF.FIFFV_COIL_NONE)
unit_none = dict(unit=FIFF.FIFF_UNIT_NONE)
Expand Down Expand Up @@ -146,6 +151,8 @@ def get_channel_type_constants(include_defaults=False):
FIFF.FIFFV_GOODNESS_FIT: 'gof',
FIFF.FIFFV_ECOG_CH: 'ecog',
FIFF.FIFFV_FNIRS_CH: 'fnirs',
FIFF.FIFFV_TEMPERATURE_CH: 'temperature',
FIFF.FIFFV_GALVANIC_CH: 'gsr',
}
# How to reduce our categories in channel_type (originally)
_second_rules = {
Expand Down Expand Up @@ -186,7 +193,8 @@ def channel_type(info, idx):
{'grad', 'mag', 'eeg', 'csd', 'stim', 'eog', 'emg', 'ecg',
'ref_meg', 'resp', 'exci', 'ias', 'syst', 'misc', 'seeg', 'dbs',
'bio', 'chpi', 'dipole', 'gof', 'ecog', 'hbo', 'hbr'}
'bio', 'chpi', 'dipole', 'gof', 'ecog', 'hbo', 'hbr',
'temperature', 'gsr'}
"""
# This is faster than the original _channel_type_old now in test_pick.py
# because it uses (at most!) two dict lookups plus one conditional
Expand Down Expand Up @@ -368,10 +376,11 @@ def _check_info_exclude(info, exclude):

@fill_doc
def pick_types(info, meg=False, eeg=False, stim=False, eog=False, ecg=False,
emg=False, ref_meg='auto', misc=False, resp=False, chpi=False,
exci=False, ias=False, syst=False, seeg=False, dipole=False,
gof=False, bio=False, ecog=False, fnirs=False, csd=False,
dbs=False, include=(), exclude='bads', selection=None):
emg=False, ref_meg='auto', *, misc=False, resp=False,
chpi=False, exci=False, ias=False, syst=False, seeg=False,
dipole=False, gof=False, bio=False, ecog=False, fnirs=False,
csd=False, dbs=False, temperature=False, gsr=False,
include=(), exclude='bads', selection=None):
"""Pick channels by type and names.
Parameters
Expand Down Expand Up @@ -399,7 +408,8 @@ def pick_types(info, meg=False, eeg=False, stim=False, eog=False, ecg=False,
len(info['comps']) > 0 and meg is not False)

for param in (eeg, stim, eog, ecg, emg, misc, resp, chpi, exci,
ias, syst, seeg, dipole, gof, bio, ecog, csd, dbs):
ias, syst, seeg, dipole, gof, bio, ecog, csd, dbs,
temperature, gsr):
if not isinstance(param, bool):
w = ('Parameters for all channel types (with the exception of '
'"meg", "ref_meg" and "fnirs") must be of type bool, not {}.')
Expand All @@ -408,7 +418,8 @@ def pick_types(info, meg=False, eeg=False, stim=False, eog=False, ecg=False,
param_dict = dict(eeg=eeg, stim=stim, eog=eog, ecg=ecg, emg=emg,
misc=misc, resp=resp, chpi=chpi, exci=exci,
ias=ias, syst=syst, seeg=seeg, dbs=dbs, dipole=dipole,
gof=gof, bio=bio, ecog=ecog, csd=csd)
gof=gof, bio=bio, ecog=ecog, csd=csd,
temperature=temperature, gsr=gsr)
# avoid triage if possible
if isinstance(meg, bool):
for key in ('grad', 'mag'):
Expand Down Expand Up @@ -911,7 +922,7 @@ def _check_excludes_includes(chs, info=None, allow_bads=False):
meg=True, eeg=True, csd=True, stim=False, eog=False, ecg=False, emg=False,
misc=False, resp=False, chpi=False, exci=False, ias=False, syst=False,
seeg=True, dipole=False, gof=False, bio=False, ecog=True, fnirs=True,
dbs=True)
dbs=True, temperature=False, gsr=False)
_PICK_TYPES_KEYS = tuple(list(_PICK_TYPES_DATA_DICT) + ['ref_meg'])
_MEG_CH_TYPES_SPLIT = ('mag', 'grad', 'planar1', 'planar2')
_FNIRS_CH_TYPES_SPLIT = ('hbo', 'hbr', 'fnirs_cw_amplitude',
Expand Down
2 changes: 1 addition & 1 deletion mne/io/tests/test_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

# https://github.com/mne-tools/fiff-constants/commits/master
REPO = 'mne-tools'
COMMIT = 'aa49e20cff5791fbaf01d77ad4ec2e0ecb69840d'
COMMIT = '6d9ca9ce7fb44c63d429c2986a953500743dfb22'

# These are oddities that we won't address:
iod_dups = (355, 359) # these are in both MEGIN and MNE files
Expand Down
4 changes: 4 additions & 0 deletions mne/utils/docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2357,6 +2357,10 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75):
EEG-CSD channels.
dbs : bool
Deep brain stimulation channels.
temperature : bool
Temperature channels.
gsr : bool
Galvanic skin response channels.
include : list of str
List of additional channels to include. If empty do not include
any.
Expand Down
2 changes: 1 addition & 1 deletion mne/viz/raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ def _setup_channel_selections(raw, kind, order):
ecg=True, emg=True, ref_meg=False, misc=True,
resp=True, chpi=True, exci=True, ias=True, syst=True,
seeg=False, bio=True, ecog=False, fnirs=False, dbs=False,
exclude=())
temperature=True, gsr=True, exclude=())
if len(misc) and np.in1d(misc, order).any():
selections_dict['Misc'] = misc
return selections_dict

0 comments on commit 7aa3a27

Please sign in to comment.