Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
197649b
ENH: Add initial BCI2000 .dat reader (preload-only)
HansujaB Feb 26, 2026
2c83c2a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2026
83d11de
Merge branch 'main' into add-bci2000-reader
HansujaB Feb 26, 2026
20a89f5
MAINT: Address CI feedback (style and import fixes)
HansujaB Feb 26, 2026
31bb511
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 26, 2026
21d9519
MAINT: Use explicit relative import for create_info
HansujaB Feb 27, 2026
fdca559
DOC: Add read_raw_bci2k to reading_raw_data autosummary
HansujaB Feb 27, 2026
77dc37f
DOC: Fix numpydoc format for read_raw_bci2k
HansujaB Feb 27, 2026
d871e26
DOC/MAINT: Document read_raw_bci2k and fix numpydoc/sphinx warnings
HansujaB Mar 2, 2026
090d208
DOC: Remove duplicate read_raw_bci2k from mne/io/tests/__init__.pyi
HansujaB Mar 3, 2026
142967e
FIX: Improve BCI2000 header parsing for multi-field header line
HansujaB Mar 15, 2026
37f00ff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 15, 2026
6f4033f
TEST: use BCI2k test dataset from mne-testing-data in BCI2K reader tests
HansujaB Mar 17, 2026
bfc888d
Merge branch 'add-bci2000-reader' of https://github.com/HansujaB/mne-…
HansujaB Mar 17, 2026
4073dff
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 17, 2026
dc50c11
TEST : remove unused imports and variables in BCI2k reader test
HansujaB Mar 17, 2026
1e3b777
Merge branch 'add-bci2000-reader' of https://github.com/HansujaB/mne-…
HansujaB Mar 17, 2026
3c763ad
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 17, 2026
02c6d88
FIX: remove unused stim_data and _bci2k_states assignments
HansujaB Mar 18, 2026
346ae38
ENH: add example for reading BCI2000 .dat files
HansujaB Mar 18, 2026
8b1757e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 18, 2026
3b16882
FIX: move nested function outside , add bci2k_data save path in examp…
HansujaB Mar 24, 2026
5dff160
Merge branch 'main' into add-bci2000-reader
HansujaB Mar 24, 2026
ab5c597
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 24, 2026
c551e4d
DOC : add changelog entry
HansujaB Mar 24, 2026
9c5dd22
FIX: Fixes
larsoner Apr 3, 2026
b7465c8
FIX: Exception
larsoner Apr 3, 2026
63dfae1
FIX: More
larsoner Apr 3, 2026
2da83bc
FIX: Better
larsoner Apr 3, 2026
52c16bb
FIX: mark
larsoner Apr 3, 2026
8baf714
Merge remote-tracking branch 'upstream/main' into add-bci2000-reader
larsoner Apr 3, 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
1 change: 1 addition & 0 deletions doc/api/reading_raw_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Reading raw data
read_raw
read_raw_ant
read_raw_artemis123
read_raw_bci2k
read_raw_bdf
read_raw_boxy
read_raw_brainvision
Expand Down
1 change: 1 addition & 0 deletions doc/changes/dev/13699.newfeature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add support for reading BCI2000 ``.dat`` files via :func:`mne.io.read_raw_bci2k`, and an example :file:`examples/io/read_bci2k.py` for downloading and visualizing BCI2000 data, by :newcontrib:`Hansuja Budhiraja`.
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@
"EpochsEEGLAB": "mne.Epochs",
"EpochsKIT": "mne.Epochs",
"RawANT": "mne.io.Raw",
"RawBCI2k": "mne.io.Raw",
"RawBOXY": "mne.io.Raw",
"RawBrainVision": "mne.io.Raw",
"RawBTi": "mne.io.Raw",
Expand Down
1 change: 1 addition & 0 deletions doc/sphinxext/related_software_nodeps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ cross-domain-saliency-maps
fsleyes
mne-kit-gui
mne-videobrowser
hedtools # because it limits our Pandas version currently
66 changes: 66 additions & 0 deletions examples/io/read_bci2k.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
======================
Reading BCI2000 files
======================

In this example, we use MNE-Python to read a BCI2000 ``.dat`` file.
BCI2000 is a general-purpose brain-computer interface (BCI) system widely
used in EEG research. The file is downloaded from the MNE testing data
repository using ``pooch``.
""" # noqa: D205 D400

# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

import pooch

import mne

# %%
# First, we download the sample BCI2000 ``.dat`` file using ``pooch``.

data_dir = mne.datasets.default_path() / "bci2k_data"
data_dir.mkdir(exist_ok=True)

fname = pooch.retrieve(
url="https://raw.githubusercontent.com/mne-tools/mne-testing-data/master/BCI2k/bci2k_test.dat",
known_hash="sha256:8efc7b5f700660a044086cb1449806ca408c2e6d32d9338c32e1bf31ce3ca9cb",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should save to whatever this path is, plus maybe a new folder (if needed) bci_data or similar?

https://mne.tools/stable/generated/mne.datasets.default_path.html#mne.datasets.default_path

So something like

data_dir = mne.datasets.default_path() / "bci2k_data"
data_dir.mkdir(exist_ok=True)
...

path=data_dir,
)

# %%
# Now we can read the file using :func:`mne.io.read_raw_bci2k`.
# Note that ``preload=True`` is required for BCI2000 files.

raw = mne.io.read_raw_bci2k(fname, preload=True)
print(raw.info)

# %%
# We can inspect the object representation, channel names, types, sampling
# frequency, and recording duration.

print(raw)
print(f"Channel names : {raw.ch_names}")
print(f"Channel types : {raw.get_channel_types()}")
print(f"Sampling freq : {raw.info['sfreq']} Hz")
print(f"Duration : {raw.times[-1]:.2f} s")
print(f"n_channels : {raw.info['nchan']}")
print(f"Data shape : {raw.get_data().shape} (n_channels, n_samples)")

# %%
# If the BCI2000 file contains a ``StimulusCode`` state, it is automatically
# mapped to a ``STI 014`` stim channel. We can extract events from it using
# :func:`mne.find_events`.

if "STI 014" in raw.ch_names:
events = mne.find_events(raw, shortest_event=1)
print(f"Found {len(events)} events")
print(mne.count_events(events))
else:
print("No stim channel found in this file.")

# %%
# Finally, we can visualize the raw data.

raw.plot(duration=5, n_channels=len(raw.ch_names), scalings="auto")
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.172",
testing="0.173",
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:9d031f1a91d2bd903a9464ca2cd7dc09",
hash="md5:0973c25cd0e3ca43a75ea6953ace1daa",
url=(
"https://codeload.github.com/mne-tools/mne-testing-data/"
f"tar.gz/{RELEASES['testing']}"
Expand Down
2 changes: 2 additions & 0 deletions mne/io/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ __all__ = [
"read_raw",
"read_raw_ant",
"read_raw_artemis123",
"read_raw_bci2k",
"read_raw_bdf",
"read_raw_boxy",
"read_raw_brainvision",
Expand Down Expand Up @@ -65,6 +66,7 @@ from .ant import read_raw_ant
from .array import RawArray
from .artemis123 import read_raw_artemis123
from .base import BaseRaw, concatenate_raws, match_channel_orders
from .bci2k import read_raw_bci2k
from .besa import read_evoked_besa
from .boxy import read_raw_boxy
from .brainvision import read_raw_brainvision
Expand Down
7 changes: 6 additions & 1 deletion mne/io/_read_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def _get_supported():
from . import (
read_raw_ant,
read_raw_artemis123,
read_raw_bci2k,
read_raw_bdf,
read_raw_boxy,
read_raw_brainvision,
Expand Down Expand Up @@ -79,7 +80,10 @@ def _get_supported():
".ds": dict(CTF=read_raw_ctf),
".txt": dict(BOXY=read_raw_boxy),
# Curry
".dat": dict(CURRY=read_raw_curry),
".dat": dict(
CURRY=read_raw_curry,
BCI2K=read_raw_bci2k,
),
".dap": dict(CURRY=read_raw_curry),
".rs3": dict(CURRY=read_raw_curry),
".cdt": dict(CURRY=read_raw_curry),
Expand Down Expand Up @@ -130,6 +134,7 @@ def read_raw(fname, *, preload=False, verbose=None, **kwargs) -> BaseRaw:

* `~mne.io.read_raw_ant`
* `~mne.io.read_raw_artemis123`
* `~mne.io.read_raw_bci2k`
* `~mne.io.read_raw_bdf`
* `~mne.io.read_raw_boxy`
* `~mne.io.read_raw_brainvision`
Expand Down
5 changes: 5 additions & 0 deletions mne/io/bci2k/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

from .bci2k import read_raw_bci2k, RawBCI2k
Loading
Loading