Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
6de026b
initial commit
dominikwelke Mar 26, 2025
e640023
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 26, 2025
d533a5f
remove local curryreader module + impedance reader + minor fixes
dominikwelke Apr 2, 2025
63c4da3
add dependencies
dominikwelke Apr 11, 2025
a8286d8
add preload option
dominikwelke Apr 11, 2025
0a5a82a
[ci skip] handle supply of wrong file extensions
dominikwelke Apr 14, 2025
4cd41d6
refactoring
dominikwelke Apr 23, 2025
d338280
add high level unit test
dominikwelke May 9, 2025
e3b91d8
CI: style + curryreader version
dominikwelke May 13, 2025
7e8f1cc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 13, 2025
8ec4a17
bump curryreader version, second shot
dominikwelke May 13, 2025
7eea5aa
test fixes
dominikwelke May 14, 2025
5afa604
add curryreader to test_extra dependencies
dominikwelke May 14, 2025
07d4256
try fixing tests
dominikwelke May 14, 2025
e4ae650
again, try fixing tests
dominikwelke May 15, 2025
b005c57
_soft_import + nesting
dominikwelke May 16, 2025
c75a8e4
add back and adapt some more tests
dominikwelke May 21, 2025
99e968d
add curryreader to circleci dependencies
dominikwelke May 21, 2025
0364225
style
dominikwelke May 21, 2025
e0c7eee
montage wip
dominikwelke Jul 2, 2025
ec4a25c
merge changes from upstream/main
dominikwelke Jul 2, 2025
1e01667
reintroduce test: test_read_files_missing_channel
dominikwelke Jul 2, 2025
3ffd6aa
soft_import pandas
dominikwelke Jul 2, 2025
3cca0d3
set sensor locations
dominikwelke Jul 15, 2025
5de6062
merge changes from upstream/main
dominikwelke Jul 15, 2025
2495cd9
extract device_info + refactoring
dominikwelke Jul 16, 2025
feb7def
test for epoched recording
dominikwelke Jul 16, 2025
e850201
add changelog entry
dominikwelke Jul 16, 2025
e452a1a
merge changes from upstream/main
dominikwelke Jul 17, 2025
7260675
fixes, refactoring, documentation, clean up
dominikwelke Jul 18, 2025
a43d363
Merge remote-tracking branch 'upstream/main' into new-curry-reader
dominikwelke Jul 18, 2025
fd15e6e
vulture_allowlist
dominikwelke Jul 21, 2025
051da8b
bump testing-data version
dominikwelke Jul 29, 2025
168eb8b
remove import_epochs_as_annotations option
dominikwelke Jul 29, 2025
0ce2f77
move amplifier info to device_info['serial']
dominikwelke Jul 29, 2025
e8016ad
merge changes from upstream/main
dominikwelke Jul 29, 2025
2768f82
dev_head_t removed after upstream changes
dominikwelke Jul 29, 2025
966cc6a
move read_dig_curry to mne.channels
dominikwelke Jul 30, 2025
20c76a8
MAINT: expanduser [ci skip]
larsoner Aug 11, 2025
0b87c82
bump curryreader version tp 0.1.2
dominikwelke Sep 8, 2025
c3677b9
reintroduce legacy tests; update head-dev transform for MEG data (WIP)
dominikwelke Sep 8, 2025
3b57bd4
Merge remote-tracking branch 'origin/new-curry-reader' into new-curry…
dominikwelke Sep 8, 2025
a3caad0
Merge remote-tracking branch 'upstream/main' into new-curry-reader
dominikwelke Sep 8, 2025
2c5f1a6
add read_dig_curry to docs; fix docstring error
dominikwelke Sep 8, 2025
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
2 changes: 1 addition & 1 deletion azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ stages:
- bash: |
set -e
python -m pip install --progress-bar off --upgrade pip
python -m pip install --progress-bar off "mne-qt-browser[opengl] @ git+https://github.com/mne-tools/mne-qt-browser.git" pyvista scikit-learn python-picard qtpy nibabel sphinx-gallery "PySide6!=6.8.0,!=6.8.0.1,!=6.8.1.1,!=6.9.1" pandas neo pymatreader antio defusedxml
python -m pip install --progress-bar off "mne-qt-browser[opengl] @ git+https://github.com/mne-tools/mne-qt-browser.git" pyvista scikit-learn python-picard qtpy nibabel sphinx-gallery "PySide6!=6.8.0,!=6.8.0.1,!=6.8.1.1,!=6.9.1" pandas neo pymatreader antio defusedxml curryreader
python -m pip uninstall -yq mne
python -m pip install --progress-bar off --upgrade -e .[test]
displayName: 'Install dependencies with pip'
Expand Down
1 change: 1 addition & 0 deletions doc/api/preprocessing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Projections:
make_dig_montage
read_dig_polhemus_isotrak
read_dig_captrak
read_dig_curry
read_dig_dat
read_dig_egi
read_dig_fif
Expand Down
1 change: 1 addition & 0 deletions doc/changes/dev/13176.dependency.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
New reader for Neuroscan Curry files, using the curry-python-reader module, by `Dominik Welke`_.
1 change: 1 addition & 0 deletions doc/changes/dev/13176.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Read impedances and montage from Neuroscan Curry files, by `Dominik Welke`_.
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ channels:
dependencies:
- python >=3.10
- antio >=0.5.0
- curryreader >=0.1.2
- darkdetect
- decorator
- defusedxml
Expand Down
2 changes: 2 additions & 0 deletions mne/channels/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ __all__ = [
"read_ch_adjacency",
"read_custom_montage",
"read_dig_captrak",
"read_dig_curry",
"read_dig_dat",
"read_dig_egi",
"read_dig_fif",
Expand Down Expand Up @@ -67,6 +68,7 @@ from .montage import (
make_standard_montage,
read_custom_montage,
read_dig_captrak,
read_dig_curry,
read_dig_dat,
read_dig_egi,
read_dig_fif,
Expand Down
46 changes: 46 additions & 0 deletions mne/channels/_dig_montage_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,49 @@ def _parse_brainvision_dig_montage(fname, scale):
ch_pos=dig_ch_pos,
coord_frame="unknown",
)


def _read_dig_montage_curry(ch_names, ch_types, ch_pos, landmarks, landmarkslabels):
import re
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to nest this, should go at the top


# scale ch_pos to m?!
ch_pos /= 1000.0
landmarks /= 1000.0
# channel locations
# what about misc without pos? can they mess things up if unordered?
assert len(ch_pos) >= (ch_types.count("mag") + ch_types.count("eeg"))
assert len(ch_pos) == (ch_types.count("mag") + ch_types.count("eeg"))
ch_pos_eeg = {
ch_names[i]: ch_pos[i, :3] for i, t in enumerate(ch_types) if t == "eeg"
}
# landmarks and headshape
landmark_dict = dict(zip(landmarkslabels, landmarks))
for k in ["Nas", "RPA", "LPA"]:
if k not in landmark_dict.keys():
landmark_dict[k] = None
if len(landmarkslabels) > 0:
hpi_pos = landmarks[
[i for i, n in enumerate(landmarkslabels) if re.match("HPI[1-99]", n)], :
]
else:
hpi_pos = None
if len(landmarkslabels) > 0:
hsp_pos = landmarks[
[i for i, n in enumerate(landmarkslabels) if re.match("H[1-99]", n)], :
]
else:
hsp_pos = None
# compile dig montage positions for eeg
if len(ch_pos_eeg) > 0:
return dict(
ch_pos=ch_pos_eeg,
nasion=landmark_dict["Nas"],
lpa=landmark_dict["LPA"],
rpa=landmark_dict["RPA"],
hsp=hsp_pos,
hpi=hpi_pos,
coord_frame="head",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless you've transformed this to the head coord frame already (and it doesn't look like you have?) this should probalby be

Suggested change
coord_frame="head",
coord_frame="unknown",

that way it'll transform everything to head coords using lpa/nasion/rpa

)
else: # not recorded?
warn("No eeg sensor locations found in file.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should be an error if you try to read dig and can't

return None
55 changes: 53 additions & 2 deletions mne/channels/montage.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,7 @@ def read_dig_dat(fname):
See Also
--------
read_dig_captrak
read_dig_curry
read_dig_dat
read_dig_egi
read_dig_fif
Expand Down Expand Up @@ -842,6 +843,7 @@ def read_dig_fif(fname, *, verbose=None):
read_dig_dat
read_dig_egi
read_dig_captrak
read_dig_curry
read_dig_polhemus_isotrak
read_dig_hpts
read_dig_localite
Expand Down Expand Up @@ -892,6 +894,7 @@ def read_dig_hpts(fname, unit="mm"):
--------
DigMontage
read_dig_captrak
read_dig_curry
read_dig_dat
read_dig_egi
read_dig_fif
Expand Down Expand Up @@ -985,6 +988,7 @@ def read_dig_egi(fname):
--------
DigMontage
read_dig_captrak
read_dig_curry
read_dig_dat
read_dig_fif
read_dig_hpts
Expand Down Expand Up @@ -1017,6 +1021,7 @@ def read_dig_captrak(fname):
See Also
--------
DigMontage
read_dig_curry
read_dig_dat
read_dig_egi
read_dig_fif
Expand All @@ -1031,6 +1036,50 @@ def read_dig_captrak(fname):
return make_dig_montage(**data)


def read_dig_curry(fname):
"""Read electrode locations from Neuroscan Curry files.

Parameters
----------
fname : path-like
A valid Curry file.

Returns
-------
montage : instance of DigMontage | None
The montage.

See Also
--------
DigMontage
read_dig_captrak
read_dig_dat
read_dig_egi
read_dig_fif
read_dig_hpts
read_dig_localite
read_dig_polhemus_isotrak
make_dig_montage
"""
from ..io.curry.curry import (
_check_curry_filename,
_extract_curry_info,
)
from ._dig_montage_utils import _read_dig_montage_curry
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to nest this one


# TODO - REVIEW NEEDED
# API? do i need to add this in the docs somewhere?
fname = _check_curry_filename(fname)
(_, _, ch_names, ch_types, ch_pos, landmarks, landmarkslabels, _, _, _, _, _, _) = (
_extract_curry_info(fname)
)
data = _read_dig_montage_curry(
ch_names, ch_types, ch_pos, landmarks, landmarkslabels
)
mont = make_dig_montage(**data) if data else None
return mont


def read_dig_localite(fname, nasion=None, lpa=None, rpa=None):
"""Read Localite .csv file.

Expand All @@ -1054,6 +1103,7 @@ def read_dig_localite(fname, nasion=None, lpa=None, rpa=None):
--------
DigMontage
read_dig_captrak
read_dig_curry
read_dig_dat
read_dig_egi
read_dig_fif
Expand Down Expand Up @@ -1455,6 +1505,7 @@ def read_dig_polhemus_isotrak(fname, ch_names=None, unit="m"):
make_dig_montage
read_polhemus_fastscan
read_dig_captrak
read_dig_curry
read_dig_dat
read_dig_egi
read_dig_fif
Expand Down Expand Up @@ -1815,8 +1866,8 @@ def make_standard_montage(kind, head_size="auto"):
Notes
-----
Individualized (digitized) electrode positions should be read in using
:func:`read_dig_captrak`, :func:`read_dig_dat`, :func:`read_dig_egi`,
:func:`read_dig_fif`, :func:`read_dig_polhemus_isotrak`,
:func:`read_dig_captrak`, :func:`read_dig_curry`, :func:`read_dig_dat`,
:func:`read_dig_egi`, :func:`read_dig_fif`, :func:`read_dig_polhemus_isotrak`,
:func:`read_dig_hpts`, or manually made with :func:`make_dig_montage`.

.. versionadded:: 0.19.0
Expand Down
4 changes: 2 additions & 2 deletions mne/datasets/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
# update the checksum in the MNE_DATASETS dict below, and change version
# here: ↓↓↓↓↓↓↓↓
RELEASES = dict(
testing="0.161",
testing="0.164",
misc="0.27",
phantom_kit="0.2",
ucl_opm_auditory="0.2",
Expand Down Expand Up @@ -115,7 +115,7 @@
# Testing and misc are at the top as they're updated most often
MNE_DATASETS["testing"] = dict(
archive_name=f"{TESTING_VERSIONED}.tar.gz",
hash="md5:a32cfb9e098dec39a5f3ed6c0833580d",
hash="md5:7b9ac40783f8131f5cefa3a103be2daf",
url=(
"https://codeload.github.com/mne-tools/mne-testing-data/"
f"tar.gz/{RELEASES['testing']}"
Expand Down
1 change: 1 addition & 0 deletions mne/io/curry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
# Copyright the MNE-Python contributors.

from .curry import read_raw_curry
from .curry import read_impedances_curry
Loading
Loading