Skip to content

Commit b2b3716

Browse files
committed
FIX: More
1 parent 6ee5068 commit b2b3716

File tree

3 files changed

+108
-38
lines changed

3 files changed

+108
-38
lines changed

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.156",
90+
testing="0.162",
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:d94fe9f3abe949a507eaeb865fb84a3f",
118+
hash="md5:34d4f174adbb211ba58a584b6c1d348c",
119119
url=(
120120
"https://codeload.github.com/mne-tools/mne-testing-data/"
121121
f"tar.gz/{RELEASES['testing']}"

mne/io/snirf/_snirf.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@
5959
"HRF BFi": 14, # HRF for blood flow index
6060
}
6161

62+
# In each file, the TD moment order maps to these values
63+
_TD_MOMENT_ORDER_MAP = {
64+
0: "intensity",
65+
1: "mean",
66+
2: "variance",
67+
}
68+
6269

6370
@fill_doc
6471
def read_raw_snirf(
@@ -313,12 +320,14 @@ def natural_keys(text):
313320
)[0]
314321
)
315322
# append moment order
316-
ch_name = (
317-
f"{ch_name} moment{fnirs_moment_orders[moment_idx - 1]}"
323+
order = fnirs_moment_orders[moment_idx - 1]
324+
_check_option(
325+
f"SNIRF channel {chan} moment order",
326+
order,
327+
_TD_MOMENT_ORDER_MAP,
318328
)
319-
ch_type = "fnirs_td_moments_intensity" # TODO: FIX AND TRIAGE!
320-
# raise RuntimeError("FIX THE TYPE")
321-
need_data_scale = True
329+
ch_name = f"{ch_name} moment{order}"
330+
ch_type = f"fnirs_td_moments_{_TD_MOMENT_ORDER_MAP[order]}"
322331

323332
elif snirf_data_type == SNIRF_PROCESSED:
324333
dt_id = _correct_shape(
@@ -358,7 +367,7 @@ def natural_keys(text):
358367
dat.get("nirs/data1/measurementList1/dataUnit", b"M")
359368
)
360369
snirf_data_unit = snirf_data_unit.item().decode("utf-8")
361-
scale = _get_dataunit_scaling(snirf_data_unit)
370+
scale = _get_dataunit_scaling(snirf_data_unit) # " " or "M")
362371
if scale is not None:
363372
for ch in info["chs"]:
364373
ch["cal"] = scale

mne/io/snirf/tests/test_snirf.py

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,14 @@
5757
)
5858

5959
# Kernel
60-
kernel_path = testing_path / "SNIRF" / "Kernel" / "Flow50" / "Portal_2021_11"
61-
kernel_hb = kernel_path / "hb.snirf"
62-
kernel_td = kernel_path / "td_moments.snirf"
60+
kernel_flow1_path = testing_path / "SNIRF" / "Kernel" / "Flow50" / "Portal_2021_11"
61+
kernel_hb_old = kernel_flow1_path / "hb.snirf"
62+
kernel_td_moments_old = kernel_flow1_path / "td_moments.snirf"
63+
kernel_flow2_path = testing_path / "SNIRF" / "Kernel" / "Flow2" / "Portal_2024_10_23"
64+
kernel_td_gated = kernel_flow2_path / "c345d04_2.snirf" # Type 201 (TD Gated, 201)
65+
kernel_td_moments = kernel_flow2_path / "c345d04_3.snirf" # Type 202 (TD Moments, 301)
66+
kernel_hb = kernel_flow2_path / "c345d04_5.snirf" # Type 203 (Hb, 99999)
67+
6368

6469
h5py = pytest.importorskip("h5py") # module-level
6570

@@ -87,8 +92,13 @@ def _get_loc(raw, ch_name):
8792
nirx_nirsport2_103,
8893
nirx_nirsport2_103_2,
8994
nirx_nirsport2_103_2,
90-
kernel_hb,
91-
kernel_td,
95+
pytest.param(kernel_hb_old, id=f"kernel: {kernel_hb_old.stem}"),
96+
pytest.param(
97+
kernel_td_moments_old, id=f"kernel: {kernel_td_moments_old.stem}"
98+
),
99+
pytest.param(kernel_td_gated, id=f"kernel: {kernel_td_gated.stem}"),
100+
pytest.param(kernel_td_moments, id=f"kernel: {kernel_td_moments.stem}"),
101+
pytest.param(kernel_hb, id=f"kernel: {kernel_hb.stem}"),
92102
lumo110,
93103
]
94104
),
@@ -97,15 +107,26 @@ def test_basic_reading_and_min_process(fname):
97107
"""Test reading SNIRF files and minimum typical processing."""
98108
raw = read_raw_snirf(fname, preload=True)
99109
# SNIRF data can contain several types, so only apply appropriate functions
110+
kinds = [
111+
"fnirs_cw_amplitude",
112+
"fnirs_od",
113+
"fnirs_td_gated_amplitude",
114+
"fnirs_td_moments_intensity",
115+
"hbo",
116+
# TODO: add fd_*
117+
]
118+
ch_types = raw.get_channel_types(unique=True)
119+
got_kinds = [kind for kind in kinds if kind in raw]
120+
assert len(got_kinds) == 1, f"Need one data type, {got_kinds=} and {ch_types=}"
100121
if "fnirs_cw_amplitude" in raw:
101122
raw = optical_density(raw)
102-
if "fnirs_od" in raw:
123+
elif "fnirs_od" in raw:
103124
raw = beer_lambert_law(raw, ppf=6)
104-
if "fnirs_td_moments_intensity" in raw:
105-
# TODO: Re-enable once types are triaged
106-
# assert "fnirs_td_moments_mean" in raw
107-
# assert "fnirs_td_moments_variance" in raw
125+
elif "fnirs_td_gated_amplitude" in raw:
108126
pass
127+
elif "fnirs_td_moments_intensity" in raw:
128+
assert "fnirs_td_moments_mean" in raw
129+
assert "fnirs_td_moments_variance" in raw
109130
else:
110131
assert "hbo" in raw
111132
assert "hbr" in raw
@@ -422,40 +443,80 @@ def test_snirf_fieldtrip_od():
422443

423444

424445
@requires_testing_data
425-
@pytest.mark.parametrize("kind", ("hb", "td"))
426-
def test_snirf_kernel(kind):
446+
@pytest.mark.parametrize(
447+
"kind, ver, shape, n_nan, fname",
448+
[
449+
pytest.param("hb", "new", (4, 38), 0, kernel_hb, id="hb"),
450+
pytest.param("hb", "old", (180 * 2, 14), 20, kernel_hb_old, id="hb old"),
451+
pytest.param(
452+
"td moments", "new", (12, 38), 0, kernel_td_moments, id="td moments"
453+
),
454+
pytest.param("td gated", "new", (100, 38), 0, kernel_td_gated, id="td gated"),
455+
pytest.param(
456+
"td moments",
457+
"old",
458+
(1080, 14),
459+
60,
460+
kernel_td_moments_old,
461+
id="td moments old",
462+
),
463+
],
464+
)
465+
def test_snirf_kernel_basic(kind, ver, shape, n_nan, fname):
427466
"""Test reading Kernel SNIRF files with haemoglobin or TD data."""
428-
fname = dict(hb=kernel_hb, td=kernel_td)[kind]
429467
raw = read_raw_snirf(fname, preload=True)
430468
if kind == "hb":
431469
# Test data import
432-
assert raw._data.shape == (180 * 2, 14)
470+
assert raw._data.shape == shape
433471
hbo_data = raw.get_data("hbo")
434472
hbr_data = raw.get_data("hbr")
435-
assert hbo_data.shape == hbr_data.shape == (180, 14)
473+
assert hbo_data.shape == hbr_data.shape == (shape[0] // 2, shape[1])
436474
hbo_norm = np.nanmedian(np.linalg.norm(hbo_data, axis=-1))
437475
hbr_norm = np.nanmedian(np.linalg.norm(hbr_data, axis=-1))
438-
assert 1 < hbr_norm < hbo_norm < 3
439-
n_nan = 20
476+
# TODO: Old file vs new file scaling, one is wrong!
477+
if ver == "new":
478+
assert 1e-5 < hbr_norm < hbo_norm < 1e-4
479+
else:
480+
assert 1 < hbr_norm < 3
481+
elif kind == "td moments":
482+
assert raw._data.shape == shape
483+
n_ch = 0
484+
# TODO: Reasonable values here???
485+
lims = dict(intensity=(1e4, 1e7), mean=(1e3, 1e4), variance=(1e5, 1e7))
486+
for key, val in lims.items():
487+
data = raw.get_data(f"fnirs_td_moments_{key}")
488+
assert data.shape[1] == len(raw.times)
489+
norm = np.nanmedian(np.linalg.norm(data, axis=-1))
490+
min_, max_ = val
491+
assert min_ < norm < max_, key
492+
n_ch += data.shape[0]
493+
assert raw._data.shape[0] == len(raw.ch_names) == n_ch
440494
else:
441-
assert raw._data.shape == (1080, 14)
442-
data = raw.get_data("fnirs_td_moments_intensity")
443-
# TODO: This will need to update once we triage properly
444-
assert data.shape == raw._data.shape
445-
norm = np.nanmedian(np.linalg.norm(data, axis=-1))
446-
assert 1e5 < norm < 1e6 # TODO: 429256, is this reasonable Molars!??
447-
n_nan = 60
495+
pass # TODO: add some gated tests
496+
if ver == "old":
497+
sfreq = 8.257638
498+
n_annot = 2
499+
else:
500+
sfreq = 3.759398
501+
n_annot = 8
448502

449-
assert_allclose(raw.info["sfreq"], 8.257638)
503+
assert_allclose(raw.info["sfreq"], sfreq, atol=1e-5)
450504

451505
bad_nans = np.isnan(raw.get_data()).any(axis=1)
452506
assert np.sum(bad_nans) == n_nan
453507

454-
assert len(raw.annotations.description) == 2
455-
assert raw.annotations.onset[0] == 0.036939
456-
assert raw.annotations.onset[1] == 0.874633
457-
assert raw.annotations.description[0] == "StartTrial"
458-
assert raw.annotations.description[1] == "StartIti"
508+
if n_annot == 2:
509+
assert len(raw.annotations.description) == n_annot
510+
assert raw.annotations.onset[0] == 0.036939
511+
assert raw.annotations.onset[1] == 0.874633
512+
assert raw.annotations.description[0] == "StartTrial"
513+
assert raw.annotations.description[1] == "StartIti"
514+
else:
515+
assert len(raw.annotations.description) == n_annot
516+
assert raw.annotations.onset[0] == 4.988107
517+
assert raw.annotations.onset[1] == 5.988107
518+
assert raw.annotations.description[0] == "StartBlock"
519+
assert raw.annotations.description[1] == "StartTrial"
459520

460521

461522
@requires_testing_data

0 commit comments

Comments
 (0)