Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e74649c
ENH: Support TD-NIRS data types in SNIRF reader
jcrdubois May 29, 2026
80afbd1
MAINT: Remove old Kernel Flow50 test files
jcrdubois May 29, 2026
66814c2
TST: Add assertions for TD gated data
jcrdubois May 29, 2026
5cd4516
FIX: Use UNITLESS for TD gated amplitude (photon counts)
jcrdubois May 29, 2026
b6efe96
MAINT: Move changelog to doc/changes/dev/
jcrdubois May 29, 2026
ba5810a
MAINT: Remove stale libxml2 pin from environment.yml
jcrdubois May 29, 2026
d80af36
handle more units
jcrdubois Jun 1, 2026
c9b5623
change github handle
jcrdubois Jun 1, 2026
064bff2
add original source and detector labels to info
jcrdubois Jun 1, 2026
a5c9eb1
widen allowable fnirs datatypes for topomap
jcrdubois Jun 1, 2026
446aa72
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 1, 2026
d117f81
Merge branch 'main' into feature/TD-nirs_snirf
jcrdubois Jun 2, 2026
7d4815c
cleanup
jcrdubois Jun 2, 2026
5fd7c08
PR # change
jcrdubois Jun 2, 2026
2321266
WIP: Vulture
larsoner Jun 5, 2026
b8da6cf
Merge remote-tracking branch 'upstream/main' into feature/TD-nirs_snirf
larsoner Jun 5, 2026
030e0a7
address PR feedback
jcrdubois Jun 5, 2026
6eb4f7e
FIX: Make style happy
larsoner Jun 5, 2026
05f6f5a
FIX: Units
larsoner Jun 5, 2026
afa3e2e
handle voltages
jcrdubois Jun 5, 2026
e7c0b2b
revert event metadata parsing (not canonical)
jcrdubois Jun 8, 2026
d1a28af
FIX: unit for optical density (unitless)
jcrdubois Jun 8, 2026
2c6a306
Merge branch 'main' into feature/TD-nirs_snirf
larsoner Jun 8, 2026
5d86202
FIX: Try caching
larsoner Jun 8, 2026
93edf79
FIX: Succeed
larsoner Jun 8, 2026
0590566
FIX: Stable
larsoner Jun 8, 2026
5e34105
FIX: One more ignore
larsoner Jun 8, 2026
a4cfeec
FIX: Restore
larsoner Jun 8, 2026
58e24e8
FIX: Restore
larsoner Jun 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ jobs:
- run: python ./tools/github_actions_check_old_env.py
if: matrix.kind == 'old'
# Minimal commands on Linux (macOS stalls)
- uses: actions/cache@v5
with:
key: minimal-commands-0
path: ~/minimal_cmds
if: startswith(matrix.os, 'ubuntu') && matrix.kind != 'minimal' && matrix.kind != 'old'
- run: bash ./tools/get_minimal_commands.sh
if: startswith(matrix.os, 'ubuntu') && matrix.kind != 'minimal' && matrix.kind != 'old'
- run: bash ./tools/github_actions_infos.sh
Expand Down
1 change: 1 addition & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ Yousra Bekhti <yousra.bekhti@gmail.com> Yoursa BEKHTI <ybekhti@is222485.intra.ce
Yousra Bekhti <yousra.bekhti@gmail.com> Yoursa BEKHTI <yousra.bekhti@gmail.com>
Yousra Bekhti <yousra.bekhti@gmail.com> Yousra BEKHTI <yousra.bekhti@gmail.com>
Yousra Bekhti <yousra.bekhti@gmail.com> yousrabk <yousra.bekhti@gmail.com>
Zahra M. Aghajan <zahraa.aghajani@gmail.com> Zahra M. Aghajan <zahraa.aghajani@gmail.com>
Zhi Zhang <850734033@qq.com> ZHANG Zhi <850734033@qq.com>
Zhi Zhang <850734033@qq.com> ZHANG Zhi <zhi271.zhang@connect.polyu.hk>
Ziyi ZENG <ziyizeng@link.cuhk.edu.cn> ZIYI ZENG <CME1909120@XMU.EDU.MY>
4 changes: 4 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ stages:
./tools/setup_xvfb.sh
sudo apt install -yq tcsh
displayName: Install Ubuntu dependencies
- task: Cache@2
inputs:
key: minimal-cmds-0
path: /home/vsts/minimal_cmds
- bash: ./tools/get_minimal_commands.sh
displayName: Get minimal commands
- bash: |
Expand Down
1 change: 1 addition & 0 deletions doc/changes/dev/13938.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added basic support for TD fNIRS data, by :newcontrib:`Zahra Aghajan`, :newcontrib:`Julien Dubois`, :newcontrib:`John Griffiths`, `Robert Luke`_, and `Eric Larson`_.
3 changes: 3 additions & 0 deletions doc/changes/names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
.. _Johann Benerradi: https://github.com/HanBnrd
.. _Johannes Herforth: https://herforth.net
.. _Johannes Niediek: https://github.com/jniediek
.. _John Griffiths: https://www.grifflab.com
.. _John Samuelsson: https://github.com/johnsam7
.. _John Veillette: https://psychology.uchicago.edu/directory/john-veillette
.. _Jon Houck: https://www.mrn.org/people/jon-m.-houck/principal-investigators
Expand All @@ -174,6 +175,7 @@
.. _Judy D Zhu: https://github.com/JD-Zhu
.. _Juergen Dammers: https://github.com/jdammers
.. _Jukka Nenonen: https://www.linkedin.com/pub/jukka-nenonen/28/b5a/684
.. _Julien Dubois: https://github.com/jcrdubois
.. _Jussi Nurminen: https://github.com/jjnurminen
.. _Kaisu Lankinen: http://bishoplab.berkeley.edu/Kaisu.html
.. _Kalle Makela: https://github.com/Kallemakela
Expand Down Expand Up @@ -376,6 +378,7 @@
.. _Yixiao Shen: https://github.com/SYXiao2002
.. _Yousra Bekhti: https://www.linkedin.com/pub/yousra-bekhti/56/886/421
.. _Yu-Han Luo: https://github.com/yh-luo
.. _Zahra Aghajan: https://github.com/Zahra-M-Aghajan
.. _Zhi Zhang: https://github.com/tczhangzhi/
.. _Ziyi ZENG: https://github.com/ZiyiTsang
.. _Zvi Baratz: https://github.com/ZviBaratz
2 changes: 2 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@
),
)
)
# Broken as of 2026/06/08 (https://github.com/joblib/joblib/issues/1796)
intersphinx_mapping["joblib"] = ("https://joblib.readthedocs.io/en/stable", None)


# NumPyDoc configuration -----------------------------------------------------
Expand Down
10 changes: 8 additions & 2 deletions mne/_fiff/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@
FIFF.FIFF_UNIT_LM = 115 # lumen
FIFF.FIFF_UNIT_LX = 116 # lux
FIFF.FIFF_UNIT_V_M2 = 117 # V/m^2
FIFF.FIFF_UNIT_SEC2 = 118 # second^2
#
# Others we need
#
Expand Down Expand Up @@ -972,6 +973,7 @@
FIFF.FIFF_UNIT_LM,
FIFF.FIFF_UNIT_LX,
FIFF.FIFF_UNIT_V_M2,
FIFF.FIFF_UNIT_SEC2,
FIFF.FIFF_UNIT_T_M,
FIFF.FIFF_UNIT_AM,
FIFF.FIFF_UNIT_AM_M2,
Expand Down Expand Up @@ -1044,7 +1046,9 @@
FIFF.FIFFV_COIL_FNIRS_FD_PHASE = 305 # fNIRS frequency domain phase
FIFF.FIFFV_COIL_FNIRS_RAW = FIFF.FIFFV_COIL_FNIRS_CW_AMPLITUDE # old alias
FIFF.FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE = 306 # fNIRS time-domain gated amplitude
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_AMPLITUDE = 307 # fNIRS time-domain moments amplitude
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY = 307 # fNIRS time-domain moments intensity
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN = 308 # fNIRS time-domain moments mean
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE = 309 # fNIRS time-domain moments variance

FIFF.FIFFV_COIL_EYETRACK_POS = 400 # Eye-tracking gaze position
FIFF.FIFFV_COIL_EYETRACK_PUPIL = 401 # Eye-tracking pupil size
Expand Down Expand Up @@ -1145,7 +1149,9 @@
FIFF.FIFFV_COIL_FNIRS_FD_AC_AMPLITUDE,
FIFF.FIFFV_COIL_FNIRS_FD_PHASE,
FIFF.FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE,
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_AMPLITUDE,
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY,
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN,
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE,
FIFF.FIFFV_COIL_MCG_42,
FIFF.FIFFV_COIL_EYETRACK_POS,
FIFF.FIFFV_COIL_EYETRACK_PUPIL,
Expand Down
1 change: 1 addition & 0 deletions mne/_fiff/meas_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ def set_montage(
FIFF.FIFF_UNIT_NONE: "NA",
FIFF.FIFF_UNIT_CEL: "C",
FIFF.FIFF_UNIT_S: "S",
FIFF.FIFF_UNIT_SEC: "s",
FIFF.FIFF_UNIT_PX: "px",
}

Expand Down
54 changes: 53 additions & 1 deletion mne/_fiff/pick.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,26 @@ def get_channel_type_constants(include_defaults=False):
unit=FIFF.FIFF_UNIT_RAD,
coil_type=FIFF.FIFFV_COIL_FNIRS_FD_PHASE,
),
fnirs_td_gated_amplitude=dict(
kind=FIFF.FIFFV_FNIRS_CH,
unit=FIFF.FIFF_UNIT_UNITLESS,
coil_type=FIFF.FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE,
),
fnirs_td_moments_intensity=dict(
kind=FIFF.FIFFV_FNIRS_CH,
unit=FIFF.FIFF_UNIT_UNITLESS,
coil_type=FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY,
),
fnirs_td_moments_mean=dict(
kind=FIFF.FIFFV_FNIRS_CH,
unit=FIFF.FIFF_UNIT_SEC,
coil_type=FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN,
),
fnirs_td_moments_variance=dict(
kind=FIFF.FIFFV_FNIRS_CH,
unit=FIFF.FIFF_UNIT_SEC2,
coil_type=FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE,
),
fnirs_od=dict(kind=FIFF.FIFFV_FNIRS_CH, coil_type=FIFF.FIFFV_COIL_FNIRS_OD),
hbo=dict(
kind=FIFF.FIFFV_FNIRS_CH,
Expand Down Expand Up @@ -197,6 +217,10 @@ def get_channel_type_constants(include_defaults=False):
FIFF.FIFFV_COIL_FNIRS_FD_AC_AMPLITUDE: "fnirs_fd_ac_amplitude",
FIFF.FIFFV_COIL_FNIRS_FD_PHASE: "fnirs_fd_phase",
FIFF.FIFFV_COIL_FNIRS_OD: "fnirs_od",
FIFF.FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE: "fnirs_td_gated_amplitude",
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY: "fnirs_td_moments_intensity",
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN: "fnirs_td_moments_mean",
FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE: "fnirs_td_moments_variance",
},
),
"eeg": (
Expand Down Expand Up @@ -385,6 +409,26 @@ def _triage_fnirs_pick(ch, fnirs, warned):
return True
elif ch["coil_type"] == FIFF.FIFFV_COIL_FNIRS_OD and "fnirs_od" in fnirs:
return True
elif (
ch["coil_type"] == FIFF.FIFFV_COIL_FNIRS_TD_GATED_AMPLITUDE
and "fnirs_td_gated_amplitude" in fnirs
):
return True
elif (
ch["coil_type"] == FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_INTENSITY
and "fnirs_td_moments_intensity" in fnirs
):
return True
elif (
ch["coil_type"] == FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN
and "fnirs_td_moments_mean" in fnirs
):
return True
elif (
ch["coil_type"] == FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE
and "fnirs_td_moments_variance" in fnirs
):
return True
return False


Expand Down Expand Up @@ -569,7 +613,7 @@ def pick_types(
pick[k] = _triage_meg_pick(info["chs"][k], ref_meg)
elif ch_type in ("eyegaze", "pupil"):
pick[k] = _triage_eyetrack_pick(info["chs"][k], eyetrack)
else: # ch_type in ('hbo', 'hbr')
else: # ch_type in ('hbo', 'hbr', ...)
pick[k] = _triage_fnirs_pick(info["chs"][k], fnirs, warned)

# restrict channels to selection if provided
Expand Down Expand Up @@ -867,6 +911,10 @@ def channel_indices_by_type(info, picks=None, *, exclude=()):
fnirs_fd_ac_amplitude=list(),
fnirs_fd_phase=list(),
fnirs_od=list(),
fnirs_td_gated_amplitude=list(),
fnirs_td_moments_intensity=list(),
fnirs_td_moments_mean=list(),
fnirs_td_moments_variance=list(),
eyegaze=list(),
pupil=list(),
)
Expand Down Expand Up @@ -1104,6 +1152,10 @@ def _check_excludes_includes(chs, info=None, allow_bads=False):
"fnirs_fd_ac_amplitude",
"fnirs_fd_phase",
"fnirs_od",
"fnirs_td_gated_amplitude",
"fnirs_td_moments_intensity",
"fnirs_td_moments_mean",
"fnirs_td_moments_variance",
)
_EYETRACK_CH_TYPES_SPLIT = ("eyegaze", "pupil")
_DATA_CH_TYPES_ORDER_DEFAULT = (
Expand Down
11 changes: 8 additions & 3 deletions mne/_fiff/tests/test_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@

# https://github.com/mne-tools/fiff-constants/commits/master
REPO = "mne-tools"
COMMIT = "e27f68cbf74dbfc5193ad429cc77900a59475181"
COMMIT = "9ccb09d69daa8332f2e7252638ba397b60ba2502"

# These are oddities that we won't address:
iod_dups = (355, 359) # these are in both MEGIN and MNE files
tag_dups = (3501,) # in both MEGIN and MNE files
tag_dups = ( # in both MEGIN and MNE files
3501, # FIFF.FIFF_XPLOTTER_LAYOUT
3507, # FIFF.FIFF_MNE_CH_NAME_LIST
)

_dir_ignore_names = (
"clear",
Expand Down Expand Up @@ -91,7 +94,9 @@
304, # fNIRS frequency domain AC amplitude
305, # fNIRS frequency domain phase
306, # fNIRS time domain gated amplitude
307, # fNIRS time domain moments amplitude
307, # fNIRS time domain moments intensity
308, # fNIRS time domain moments mean
309, # fNIRS time domain moments variance
400, # Eye-tracking gaze position
401, # Eye-tracking pupil size
1000, # For testing the MCG software
Expand Down
35 changes: 35 additions & 0 deletions mne/cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -1538,6 +1538,7 @@ def __init__(
grad=0.1,
mag=0.1,
eeg=0.1,
*,
seeg=0.1,
ecog=0.1,
hbo=0.1,
Expand All @@ -1546,6 +1547,10 @@ def __init__(
fnirs_fd_ac_amplitude=0.1,
fnirs_fd_phase=0.1,
fnirs_od=0.1,
fnirs_td_gated_amplitude=0.1,
fnirs_td_moments_intensity=0.1,
fnirs_td_moments_mean=0.1,
fnirs_td_moments_variance=0.1,
csd=0.1,
dbs=0.1,
store_precision=False,
Expand All @@ -1566,6 +1571,10 @@ def __init__(
self.fnirs_fd_ac_amplitude = fnirs_fd_ac_amplitude
self.fnirs_fd_phase = fnirs_fd_phase
self.fnirs_od = fnirs_od
self.fnirs_td_gated_amplitude = fnirs_td_gated_amplitude
self.fnirs_td_moments_intensity = fnirs_td_moments_intensity
self.fnirs_td_moments_mean = fnirs_td_moments_mean
self.fnirs_td_moments_variance = fnirs_td_moments_variance
self.csd = csd
self.store_precision = store_precision
self.assume_centered = assume_centered
Expand Down Expand Up @@ -1598,6 +1607,15 @@ def fit(self, X):
dbs=self.dbs,
hbo=self.hbo,
hbr=self.hbr,
fnirs_cw_amplitude=self.fnirs_cw_amplitude,
fnirs_fd_ac_amplitude=self.fnirs_fd_ac_amplitude,
fnirs_fd_phase=self.fnirs_fd_phase,
fnirs_od=self.fnirs_od,
fnirs_td_gated_amplitude=self.fnirs_td_gated_amplitude,
fnirs_td_moments_intensity=self.fnirs_td_moments_intensity,
fnirs_td_moments_mean=self.fnirs_td_moments_mean,
fnirs_td_moments_variance=self.fnirs_td_moments_variance,
csd=self.csd,
rank="full",
)
self.estimator_.covariance_ = self.covariance_ = cov_.data
Expand Down Expand Up @@ -1926,6 +1944,7 @@ def regularize(
eeg=0.1,
exclude="bads",
proj=True,
*,
seeg=0.1,
ecog=0.1,
hbo=0.1,
Expand All @@ -1934,6 +1953,10 @@ def regularize(
fnirs_fd_ac_amplitude=0.1,
fnirs_fd_phase=0.1,
fnirs_od=0.1,
fnirs_td_gated_amplitude=0.1,
fnirs_td_moments_intensity=0.1,
fnirs_td_moments_mean=0.1,
fnirs_td_moments_variance=0.1,
csd=0.1,
dbs=0.1,
rank=None,
Expand Down Expand Up @@ -1985,6 +2008,14 @@ def regularize(
Regularization factor for fNIRS raw phase signals.
fnirs_od : float (default 0.1)
Regularization factor for fNIRS optical density signals.
fnirs_td_gated_amplitude : float (default 0.1)
Regularization factor for fNIRS time domain gated amplitude signals.
fnirs_td_moments_intensity : float (default 0.1)
Regularization factor for fNIRS time domain moments amplitude signals.
fnirs_td_moments_mean : float (default 0.1)
Regularization factor for fNIRS time domain moments mean signals.
fnirs_td_moments_variance : float (default 0.1)
Regularization factor for fNIRS time domain moments variance signals.
csd : float (default 0.1)
Regularization factor for EEG-CSD signals.
dbs : float (default 0.1)
Expand Down Expand Up @@ -2025,6 +2056,10 @@ def regularize(
fnirs_fd_ac_amplitude=fnirs_fd_ac_amplitude,
fnirs_fd_phase=fnirs_fd_phase,
fnirs_od=fnirs_od,
fnirs_td_gated_amplitude=fnirs_td_gated_amplitude,
fnirs_td_moments_intensity=fnirs_td_moments_intensity,
fnirs_td_moments_mean=fnirs_td_moments_mean,
fnirs_td_moments_variance=fnirs_td_moments_variance,
csd=csd,
)

Expand Down
Loading
Loading