Skip to content

Commit f443d1c

Browse files
myd7349larsonerpre-commit-ci[bot]
authored
FIX: Correctly set the calibration factor in Nihon Kohden reader (#13468)
Co-authored-by: Eric Larson <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent ce0e1c0 commit f443d1c

File tree

4 files changed

+55
-11
lines changed

4 files changed

+55
-11
lines changed

doc/changes/dev/13468.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correctly set the calibration factor in Nihon Kohden reader (which affects channel amplitudes), by `Tom Ma`_.

mne/datasets/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
# update the checksum in the MNE_DATASETS dict below, and change version
8888
# here: ↓↓↓↓↓↓↓↓
8989
RELEASES = dict(
90-
testing="0.166",
90+
testing="0.167",
9191
misc="0.27",
9292
phantom_kit="0.2",
9393
ucl_opm_auditory="0.2",
@@ -115,7 +115,7 @@
115115
# Testing and misc are at the top as they're updated most often
116116
MNE_DATASETS["testing"] = dict(
117117
archive_name=f"{TESTING_VERSIONED}.tar.gz",
118-
hash="md5:273c5919cf74198a39146e9cbc146ce0",
118+
hash="md5:d82318a83b436ca2c7ca8420487c05c2",
119119
url=(
120120
"https://codeload.github.com/mne-tools/mne-testing-data/"
121121
f"tar.gz/{RELEASES['testing']}"

mne/io/nihon/nihon.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,14 @@ def _read_21e_file(fname):
162162
break
163163
else:
164164
warn(
165-
f"Could not decode 21E file as one of {_encodings}; "
165+
f"Could not decode {e_fname} as one of {_encodings}; "
166166
f"Default channel names are chosen."
167167
)
168+
else:
169+
warn(
170+
f"Could not find {e_fname} containing channel definitions; "
171+
f"Default channel names are chosen."
172+
)
168173

169174
return _chan_labels
170175

@@ -280,7 +285,7 @@ def _read_nihon_header(fname):
280285
"Cannot read NK file with different sfreq in each datablock"
281286
)
282287

283-
return header
288+
return header, _chan_labels
284289

285290

286291
def _read_event_log_block(fid, t_block, version):
@@ -380,13 +385,13 @@ def _map_ch_to_type(ch_name):
380385
return "eeg"
381386

382387

383-
def _map_ch_to_specs(ch_name):
388+
def _map_ch_to_specs(ch_name, chan_labels_upper):
384389
unit_mult = 1e-3
385390
phys_min = -12002.9
386391
phys_max = 12002.56
387392
dig_min = -32768
388-
if ch_name.upper() in _default_chan_labels:
389-
idx = _default_chan_labels.index(ch_name.upper())
393+
if ch_name.upper() in chan_labels_upper:
394+
idx = chan_labels_upper.index(ch_name.upper())
390395
if (idx < 42 or idx > 73) and idx not in [76, 77]:
391396
unit_mult = 1e-6
392397
phys_min = -3200
@@ -432,7 +437,9 @@ def __init__(self, fname, preload=False, *, encoding="utf-8", verbose=None):
432437
data_name = fname.name
433438
logger.info(f"Loading {data_name}")
434439

435-
header = _read_nihon_header(fname)
440+
# chan_labels are electrode codes defined in the .21E file.
441+
# It is not the same as header["ch_names"].
442+
header, chan_labels = _read_nihon_header(fname)
436443
metadata = _read_nihon_metadata(fname)
437444

438445
# n_chan = len(header['ch_names']) + 1
@@ -447,8 +454,9 @@ def __init__(self, fname, preload=False, *, encoding="utf-8", verbose=None):
447454
if "meas_date" in metadata:
448455
with info._unlock():
449456
info["meas_date"] = metadata["meas_date"]
450-
chs = {x: _map_ch_to_specs(x) for x in info["ch_names"]}
451457

458+
chan_labels_upper = [x.upper() for x in chan_labels]
459+
chs = {x: _map_ch_to_specs(x, chan_labels_upper) for x in info["ch_names"]}
452460
cal = np.array([chs[x]["cal"] for x in info["ch_names"]], float)[:, np.newaxis]
453461
offsets = np.array([chs[x]["offset"] for x in info["ch_names"]], float)[
454462
:, np.newaxis

mne/io/nihon/tests/test_nihon.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def test_nihon_eeg():
3030
raw_edf.drop_channels(["Events/Markers"])
3131

3232
assert raw._data.shape == raw_edf._data.shape
33-
assert raw.info["sfreq"] == raw.info["sfreq"]
33+
assert raw.info["sfreq"] == raw_edf.info["sfreq"]
3434
# a couple of ch names differ in the EDF
3535
edf_ch_names = {"EEG Mark1": "$A2", "EEG Mark2": "$A1"}
3636
raw_edf.rename_channels(edf_ch_names)
@@ -48,7 +48,7 @@ def test_nihon_eeg():
4848
raw = read_raw_nihon(fname_edf, preload=True)
4949

5050
with pytest.raises(ValueError, match="Not a valid Nihon Kohden EEG file"):
51-
raw = _read_nihon_header(fname_edf)
51+
header, _ = _read_nihon_header(fname_edf)
5252

5353
bad_fname = data_path / "eximia" / "text_eximia.nxe"
5454

@@ -90,3 +90,38 @@ def return_channel_duplicates(fname):
9090
)
9191
with pytest.warns(RuntimeWarning, match=msg):
9292
read_raw_nihon(fname)
93+
94+
95+
@testing.requires_testing_data
96+
def test_nihon_calibration():
97+
"""Test handling of calibration factor and range in Nihon Kohden EEG files."""
98+
fname = data_path / "NihonKohden" / "DA00100E.EEG"
99+
raw = read_raw_nihon(fname, preload=True, encoding="cp936")
100+
101+
Fp1_idx = raw.ch_names.index("Fp1")
102+
M1_idx = raw.ch_names.index("M1")
103+
M2_idx = raw.ch_names.index("M2")
104+
105+
Fp1_info = raw.info["chs"][Fp1_idx]
106+
M1_info = raw.info["chs"][M1_idx]
107+
M2_info = raw.info["chs"][M2_idx]
108+
109+
# M1, M2 are EEG channels, just like Fp1.
110+
# So they should have the same calibration factor and physical range.
111+
assert_allclose(M1_info["cal"], Fp1_info["cal"])
112+
assert_allclose(M2_info["cal"], Fp1_info["cal"])
113+
assert_allclose(M1_info["range"], Fp1_info["range"])
114+
assert_allclose(M2_info["range"], Fp1_info["range"])
115+
116+
fname_edf = data_path / "NihonKohden" / "DA00100E.EDF"
117+
raw_edf = read_raw_edf(fname_edf, preload=True)
118+
raw_edf.drop_channels(["Events/Markers"])
119+
# a couple of ch names differ in the EDF
120+
edf_ch_names = {"EEG Mark1": "$M1", "EEG Mark2": "$M2"}
121+
raw_edf.rename_channels(edf_ch_names)
122+
123+
assert raw.ch_names == raw_edf.ch_names
124+
assert raw._data.shape == raw_edf._data.shape
125+
assert raw.info["sfreq"] == raw_edf.info["sfreq"]
126+
127+
assert_allclose(raw.get_data(), raw_edf.get_data())

0 commit comments

Comments
 (0)