From dbf6fd2be473f2442ff4d8f990b6ecee85eca835 Mon Sep 17 00:00:00 2001 From: Teon Brooks Date: Tue, 16 Dec 2025 18:53:07 +0000 Subject: [PATCH 01/10] Update values to int64 --- mne/io/cnt/cnt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mne/io/cnt/cnt.py b/mne/io/cnt/cnt.py index 196a87564d1..123efeaa3f2 100644 --- a/mne/io/cnt/cnt.py +++ b/mne/io/cnt/cnt.py @@ -310,7 +310,7 @@ def _get_cnt_info(input_fname, eog, ecg, emg, misc, data_format, date_format, he meas_date = _session_date_2_meas_date(session_date, date_format) fid.seek(370) - n_channels = np.fromfile(fid, dtype=" Date: Tue, 16 Dec 2025 14:17:02 -0500 Subject: [PATCH 02/10] FIX: Numbers --- doc/changes/dev/bugfix.rst | 1 + mne/io/cnt/_utils.py | 6 +++--- mne/io/cnt/cnt.py | 44 ++++++++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 doc/changes/dev/bugfix.rst diff --git a/doc/changes/dev/bugfix.rst b/doc/changes/dev/bugfix.rst new file mode 100644 index 00000000000..01c0d2eafcc --- /dev/null +++ b/doc/changes/dev/bugfix.rst @@ -0,0 +1 @@ +Fix bug with reading large CNT files by `Teon Brooks`_. diff --git a/mne/io/cnt/_utils.py b/mne/io/cnt/_utils.py index cf2d45cb1ef..25dfcad4886 100644 --- a/mne/io/cnt/_utils.py +++ b/mne/io/cnt/_utils.py @@ -124,7 +124,7 @@ def _compute_robust_event_table_position(fid, data_format="int32"): if fid.seek(0, SEEK_END) < 2e9: fid.seek(SETUP_EVENTTABLEPOS_OFFSET) - (event_table_pos,) = np.frombuffer(fid.read(4), dtype="= 0] fid.seek(438) - lowpass_toggle = np.fromfile(fid, "i1", count=1).item() - highpass_toggle = np.fromfile(fid, "i1", count=1).item() + lowpass_toggle = bool(np.fromfile(fid, "i1", count=1).item()) + highpass_toggle = bool(np.fromfile(fid, "i1", count=1).item()) # Header has a field for number of samples, but it does not seem to be # too reliable. That's why we have option for setting n_bytes manually. fid.seek(864) - n_samples = np.fromfile(fid, dtype=" 0: info["lowpass"] = highcutoff - if highpass_toggle == 1: + if highpass_toggle and lowcutoff > 0: info["highpass"] = lowcutoff subject_info = { "hand": hand, @@ -540,7 +550,9 @@ def __init__( else: _date_format = "%m/%d/%y %H:%M:%S" - input_fname = path.abspath(input_fname) + input_fname = _check_fname( + input_fname, overwrite="read", must_exist=True, name="input_fname" + ) try: info, cnt_info = _get_cnt_info( input_fname, eog, ecg, emg, misc, data_format, _date_format, header From 61c4a8dee8fd27f05cf23782ba36bd839d289d77 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:17:38 +0000 Subject: [PATCH 03/10] [autofix.ci] apply automated fixes --- doc/changes/dev/{bugfix.rst => 13548.bugfix.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/changes/dev/{bugfix.rst => 13548.bugfix.rst} (100%) diff --git a/doc/changes/dev/bugfix.rst b/doc/changes/dev/13548.bugfix.rst similarity index 100% rename from doc/changes/dev/bugfix.rst rename to doc/changes/dev/13548.bugfix.rst From 272ba472b252884c5759dd45ae69ca9733c3b024 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 16 Dec 2025 14:26:29 -0500 Subject: [PATCH 04/10] FIX: Unify --- mne/io/cnt/_utils.py | 22 +++++++++++++--------- mne/io/cnt/cnt.py | 35 ++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/mne/io/cnt/_utils.py b/mne/io/cnt/_utils.py index 25dfcad4886..28dd1471445 100644 --- a/mne/io/cnt/_utils.py +++ b/mne/io/cnt/_utils.py @@ -12,6 +12,12 @@ from ...utils import warn +_NCHANNELS_OFFSET = 370 +_NSAMPLES_OFFSET = 864 +_EVENTTABLEPOS_OFFSET = 886 +_DATA_OFFSET = 900 # Size of the 'SETUP' header. +_CH_SIZE = 75 # Size of each channel in bytes + def _read_teeg(f, teeg_offset): """ @@ -116,14 +122,10 @@ def _compute_robust_event_table_position(fid, data_format="int32"): Otherwise, the address of the table position is computed from: n_samples, n_channels, and the bytes size. """ - SETUP_NCHANNELS_OFFSET = 370 - SETUP_NSAMPLES_OFFSET = 864 - SETUP_EVENTTABLEPOS_OFFSET = 886 - fid_origin = fid.tell() # save the state if fid.seek(0, SEEK_END) < 2e9: - fid.seek(SETUP_EVENTTABLEPOS_OFFSET) + fid.seek(_EVENTTABLEPOS_OFFSET) event_table_pos = int(np.frombuffer(fid.read(4), dtype=" 0 - fid.seek(SETUP_NCHANNELS_OFFSET) + fid.seek(_NCHANNELS_OFFSET) n_channels = int(np.frombuffer(fid.read(2), dtype=" 0 event_table_pos = ( - 900 + 75 * int(n_channels) + n_bytes * int(n_channels) * int(n_samples) + _DATA_OFFSET + _CH_SIZE * n_channels + n_bytes * n_channels * n_samples ) fid.seek(fid_origin) # restore the state diff --git a/mne/io/cnt/cnt.py b/mne/io/cnt/cnt.py index 17a7a2d357f..ea94d9cc88c 100644 --- a/mne/io/cnt/cnt.py +++ b/mne/io/cnt/cnt.py @@ -22,6 +22,10 @@ ) from ..base import BaseRaw from ._utils import ( + _CH_SIZE, + _DATA_OFFSET, + _NCHANNELS_OFFSET, + _NSAMPLES_OFFSET, CNTEventType3, _compute_robust_event_table_position, _get_event_parser, @@ -69,7 +73,7 @@ def _translating_function(offset, n_channels, event_type, data_format=data_forma n_bytes = 2 if data_format == "int16" else 4 if event_type == CNTEventType3: offset *= n_bytes * n_channels - event_time = offset - 900 - (75 * n_channels) + event_time = offset - _DATA_OFFSET - (_CH_SIZE * n_channels) event_time //= n_channels * n_bytes event_time = event_time - 1 # Prevent negative event times @@ -281,7 +285,6 @@ def read_raw_cnt( def _get_cnt_info(input_fname, eog, ecg, emg, misc, data_format, date_format, header): """Read the cnt header.""" - data_offset = 900 # Size of the 'SETUP' header. cnt_info = dict() # Reading only the fields of interest. Structure of the whole header at # http://paulbourke.net/dataformats/eeg/ @@ -314,7 +317,7 @@ def _get_cnt_info(input_fname, eog, ecg, emg, misc, data_format, date_format, he session_date = f"{read_str(fid, 10)} {read_str(fid, 12)}" meas_date = _session_date_2_meas_date(session_date, date_format) - fid.seek(370) + fid.seek(_NCHANNELS_OFFSET) n_channels = np.fromfile(fid, dtype=" Date: Wed, 17 Dec 2025 14:38:03 +0000 Subject: [PATCH 05/10] Remove code used for debugging --- mne/io/cnt/cnt.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mne/io/cnt/cnt.py b/mne/io/cnt/cnt.py index ea94d9cc88c..5573a667d5f 100644 --- a/mne/io/cnt/cnt.py +++ b/mne/io/cnt/cnt.py @@ -332,8 +332,6 @@ def _get_cnt_info(input_fname, eog, ecg, emg, misc, data_format, date_format, he # too reliable. That's why we have option for setting n_bytes manually. fid.seek(_NSAMPLES_OFFSET) n_samples = np.fromfile(fid, dtype=" Date: Sat, 20 Dec 2025 00:19:27 +0000 Subject: [PATCH 06/10] added note --- mne/io/cnt/cnt.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mne/io/cnt/cnt.py b/mne/io/cnt/cnt.py index 5573a667d5f..f030ee9739d 100644 --- a/mne/io/cnt/cnt.py +++ b/mne/io/cnt/cnt.py @@ -328,8 +328,12 @@ def _get_cnt_info(input_fname, eog, ecg, emg, misc, data_format, date_format, he lowpass_toggle = bool(np.fromfile(fid, "i1", count=1).item()) highpass_toggle = bool(np.fromfile(fid, "i1", count=1).item()) + # Reference: https://paulbourke.net/dataformats/eeg/ # Header has a field for number of samples, but it does not seem to be # too reliable. That's why we have option for setting n_bytes manually. + # According to link above, the number of samples should be calculated as follows: + # nsamples = SETUP.EventTablePos - (900 + 75 * nchannels) / (2 * nchannels) + # where 2 likely refers to the data format with default 2 bytes. fid.seek(_NSAMPLES_OFFSET) n_samples = np.fromfile(fid, dtype=" Date: Sat, 20 Dec 2025 00:26:21 +0000 Subject: [PATCH 07/10] revert back to " Date: Sat, 20 Dec 2025 00:40:00 +0000 Subject: [PATCH 08/10] formatting --- mne/io/cnt/cnt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mne/io/cnt/cnt.py b/mne/io/cnt/cnt.py index 0b0b03ec8ae..ba30146e341 100644 --- a/mne/io/cnt/cnt.py +++ b/mne/io/cnt/cnt.py @@ -331,7 +331,8 @@ def _get_cnt_info(input_fname, eog, ecg, emg, misc, data_format, date_format, he # Reference: https://paulbourke.net/dataformats/eeg/ # Header has a field for number of samples, but it does not seem to be # too reliable. That's why we have option for setting n_bytes manually. - # According to link above, the number of samples should be calculated as follows: + # According to link above, the number of samples should be + # calculated as follows: # nsamples = SETUP.EventTablePos - (900 + 75 * nchannels) / (2 * nchannels) # where 2 likely refers to the data format with default 2 bytes. fid.seek(_NSAMPLES_OFFSET) From 246da2f46ca4b3e1836ef2db6793fbee4ddb1184 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 8 Jan 2026 15:03:54 -0500 Subject: [PATCH 09/10] TST: Skip From 519106749fb501070984382d9091448da6d0efee Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Thu, 8 Jan 2026 15:04:02 -0500 Subject: [PATCH 10/10] TST: Skip [ci skip]