Skip to content

Commit 636c42a

Browse files
committed
FIX: Tests passing
1 parent 63b9f35 commit 636c42a

7 files changed

Lines changed: 49 additions & 38 deletions

File tree

mne/_fiff/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,7 @@
931931
FIFF.FIFF_UNIT_LM = 115 # lumen
932932
FIFF.FIFF_UNIT_LX = 116 # lux
933933
FIFF.FIFF_UNIT_V_M2 = 117 # V/m^2
934+
FIFF.FIFF_UNIT_SEC2 = 118 # second^2
934935
#
935936
# Others we need
936937
#
@@ -972,6 +973,7 @@
972973
FIFF.FIFF_UNIT_LM,
973974
FIFF.FIFF_UNIT_LX,
974975
FIFF.FIFF_UNIT_V_M2,
976+
FIFF.FIFF_UNIT_SEC2,
975977
FIFF.FIFF_UNIT_T_M,
976978
FIFF.FIFF_UNIT_AM,
977979
FIFF.FIFF_UNIT_AM_M2,

mne/_fiff/meas_info.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ def set_montage(
444444
FIFF.FIFF_UNIT_NONE: "NA",
445445
FIFF.FIFF_UNIT_CEL: "C",
446446
FIFF.FIFF_UNIT_S: "S",
447+
FIFF.FIFF_UNIT_SEC: "s",
447448
FIFF.FIFF_UNIT_PX: "px",
448449
}
449450

mne/_fiff/pick.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,12 @@ def get_channel_type_constants(include_defaults=False):
116116
),
117117
fnirs_td_moments_mean=dict(
118118
kind=FIFF.FIFFV_FNIRS_CH,
119-
unit=FIFF.FIFF_UNIT_S,
119+
unit=FIFF.FIFF_UNIT_SEC,
120120
coil_type=FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_MEAN,
121121
),
122122
fnirs_td_moments_variance=dict(
123123
kind=FIFF.FIFFV_FNIRS_CH,
124-
unit=FIFF.FIFF_UNIT_NONE, # TODO: Maybe someday add s^2
124+
unit=FIFF.FIFF_UNIT_SEC2,
125125
coil_type=FIFF.FIFFV_COIL_FNIRS_TD_MOMENTS_VARIANCE,
126126
),
127127
fnirs_od=dict(kind=FIFF.FIFFV_FNIRS_CH, coil_type=FIFF.FIFFV_COIL_FNIRS_OD),

mne/_fiff/tests/test_constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
# https://github.com/mne-tools/fiff-constants/commits/master
3030
REPO = "larsoner" # TODO: Replace with upstream once merged
31-
COMMIT = "ba2288355b61b00d65d4f1d8a47ef82b83414201"
31+
COMMIT = "63cdf5d64a7006d2b8d21931bd7c4d898e310df8"
3232

3333
# These are oddities that we won't address:
3434
iod_dups = (355, 359) # these are in both MEGIN and MNE files

mne/defaults.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@
6666
fnirs_od="V",
6767
fnirs_td_gated_amplitude="AU", # counts
6868
fnirs_td_moments_intensity="AU", # counts
69-
fnirs_td_moments_mean="S",
70-
fnirs_td_moments_variance="S²",
69+
fnirs_td_moments_mean="s",
70+
fnirs_td_moments_variance="s²",
7171
csd="V/m²",
7272
whitened="Z",
7373
gsr="S",
@@ -98,16 +98,16 @@
9898
fnirs_od="V",
9999
fnirs_td_gated_amplitude="AU",
100100
fnirs_td_moments_intensity="AU",
101-
fnirs_td_moments_mean="S",
102-
fnirs_td_moments_variance="S²",
101+
fnirs_td_moments_mean="ps",
102+
fnirs_td_moments_variance="ps²",
103103
csd="mV/m²",
104104
whitened="Z",
105105
gsr="S",
106106
temperature="C",
107107
eyegaze="rad",
108108
pupil="µm",
109109
),
110-
# scalings for the units
110+
# scalings for the "units" above (must match!)
111111
scalings=dict(
112112
mag=1e15,
113113
grad=1e13,
@@ -131,8 +131,8 @@
131131
fnirs_od=1.0,
132132
fnirs_td_gated_amplitude=1.0,
133133
fnirs_td_moments_intensity=1.0,
134-
fnirs_td_moments_mean=1.0,
135-
fnirs_td_moments_variance=1.0,
134+
fnirs_td_moments_mean=1e12,
135+
fnirs_td_moments_variance=1e24,
136136
csd=1e3,
137137
whitened=1.0,
138138
gsr=1.0,
@@ -169,8 +169,8 @@
169169
fnirs_od=2e-2,
170170
fnirs_td_gated_amplitude=1.0,
171171
fnirs_td_moments_intensity=1.0,
172-
fnirs_td_moments_mean=1.0,
173-
fnirs_td_moments_variance=1.0,
172+
fnirs_td_moments_mean=1e-10,
173+
fnirs_td_moments_variance=1e-20,
174174
csd=200e-4,
175175
dipole=1e-7,
176176
gof=1e2,

mne/io/snirf/_snirf.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,16 @@ def natural_keys(text):
288288
"location information"
289289
)
290290

291+
# Uniform scale factor assumed here!
292+
snirf_data_unit = np.array(
293+
dat.get("nirs/data1/measurementList1/dataUnit", b"M")
294+
)
295+
snirf_data_unit = snirf_data_unit.item().decode("utf-8")
296+
scale = _get_dataunit_scaling(snirf_data_unit)
297+
291298
chnames = []
292299
ch_types = []
293-
need_data_scale = False
300+
ch_cals = []
294301
for chan in channels:
295302
ch_root = f"nirs/data1/{chan}"
296303
src_idx = int(
@@ -300,6 +307,7 @@ def natural_keys(text):
300307
_correct_shape(np.array(dat.get(f"{ch_root}/detectorIndex")))[0]
301308
)
302309
ch_name = f"{sources[src_idx]}_{detectors[det_idx]}"
310+
ch_cal = scale
303311

304312
if snirf_data_type in (
305313
SNIRF_CW_AMPLITUDE,
@@ -324,7 +332,6 @@ def natural_keys(text):
324332
# append time delay
325333
ch_name = f"{ch_name} bin{fnirs_time_delays[bin_idx - 1]}"
326334
ch_type = "fnirs_td_gated_amplitude"
327-
need_data_scale = True
328335
else:
329336
assert snirf_data_type == SNIRF_TD_MOMENTS_AMPLITUDE
330337
moment_idx = int(
@@ -340,8 +347,13 @@ def natural_keys(text):
340347
_TD_MOMENT_ORDER_MAP,
341348
)
342349
ch_name = f"{ch_name} moment{order}"
343-
ch_type = f"fnirs_td_moments_{_TD_MOMENT_ORDER_MAP[order]}"
344-
350+
kind = _TD_MOMENT_ORDER_MAP[order]
351+
ch_type = f"fnirs_td_moments_{kind}"
352+
if kind == "mean":
353+
# Stored in picoseconds
354+
ch_cal = 1e-12
355+
elif kind == "variance":
356+
ch_cal = 1e-24
345357
elif snirf_data_type == SNIRF_PROCESSED:
346358
dt_id = _correct_shape(
347359
np.array(dat.get(f"{ch_root}/dataTypeLabel"))
@@ -365,25 +377,18 @@ def natural_keys(text):
365377
f"HbO/HbR, but got type f{dt_id}"
366378
)
367379
suffix = dt_id.lower()
368-
need_data_scale = True
369380
ch_name = f"{ch_name} {suffix}"
370381
ch_type = dt_id
371382
chnames.append(ch_name)
372383
ch_types.append(ch_type)
373-
del ch_root, ch_name, ch_type
384+
ch_cals.append(ch_cal)
385+
del ch_root, ch_name, ch_type, ch_cal
386+
del scale
374387

375388
# Create mne structure
376389
info = create_info(chnames, sampling_rate, ch_types=ch_types)
377-
378-
if need_data_scale:
379-
snirf_data_unit = np.array(
380-
dat.get("nirs/data1/measurementList1/dataUnit", b"M")
381-
)
382-
snirf_data_unit = snirf_data_unit.item().decode("utf-8")
383-
scale = _get_dataunit_scaling(snirf_data_unit) # " " or "M")
384-
if scale is not None:
385-
for ch in info["chs"]:
386-
ch["cal"] = scale
390+
for ch, ch_cal in zip(info["chs"], ch_cals):
391+
ch["cal"] = ch_cal
387392

388393
subject_info = {}
389394
names = np.array(dat.get("nirs/metaDataTags/SubjectID"))
@@ -417,8 +422,8 @@ def natural_keys(text):
417422
length_unit = _get_metadata_str(dat, "LengthUnit")
418423
length_scaling = _get_lengthunit_scaling(length_unit)
419424

420-
srcPos3D /= length_scaling
421-
detPos3D /= length_scaling
425+
srcPos3D *= length_scaling
426+
detPos3D *= length_scaling
422427

423428
if optode_frame in ["mri", "meg"]:
424429
# These are all in MNI or MEG coordinates, so let's transform
@@ -651,7 +656,7 @@ def _get_timeunit_scaling(time_unit):
651656

652657
def _get_lengthunit_scaling(length_unit):
653658
"""MNE expects distance in m, return required scaling."""
654-
scalings = {"m": 1, "cm": 100, "mm": 1000}
659+
scalings = {"m": 1.0, "cm": 1e-2, "mm": 1e-3}
655660
if length_unit in scalings:
656661
return scalings[length_unit]
657662
else:
@@ -664,7 +669,7 @@ def _get_lengthunit_scaling(length_unit):
664669

665670
def _get_dataunit_scaling(hbx_unit):
666671
"""MNE expects hbo/hbr in M, return required scaling."""
667-
scalings = {"M": None, "uM": 1e-6}
672+
scalings = {"M": 1.0, "uM": 1e-6, "": 1.0}
668673
try:
669674
return scalings[hbx_unit]
670675
except KeyError:

mne/io/snirf/tests/test_snirf.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -474,16 +474,15 @@ def test_snirf_kernel_basic(kind, ver, shape, n_nan, fname):
474474
assert hbo_data.shape == hbr_data.shape == (shape[0] // 2, shape[1])
475475
hbo_norm = np.nanmedian(np.linalg.norm(hbo_data, axis=-1))
476476
hbr_norm = np.nanmedian(np.linalg.norm(hbr_data, axis=-1))
477-
# TODO: Old file vs new file scaling, one is wrong!
477+
# TODO: Old file vs new file scaling, old one is wrong most likely!
478478
if ver == "new":
479479
assert 1e-5 < hbr_norm < hbo_norm < 1e-4
480480
else:
481481
assert 1 < hbr_norm < 3
482482
elif kind == "td moments":
483483
assert raw._data.shape == shape
484484
n_ch = 0
485-
# TODO: Reasonable values here???
486-
lims = dict(intensity=(1e4, 1e7), mean=(1e3, 1e4), variance=(1e5, 1e7))
485+
lims = dict(intensity=(1e4, 1e7), mean=(1e-9, 1e-8), variance=(1e-19, 1e-16))
487486
for key, val in lims.items():
488487
data = raw.get_data(f"fnirs_td_moments_{key}")
489488
assert data.shape[1] == len(raw.times)
@@ -492,13 +491,17 @@ def test_snirf_kernel_basic(kind, ver, shape, n_nan, fname):
492491
assert min_ < norm < max_, key
493492
n_ch += data.shape[0]
494493
assert raw._data.shape[0] == len(raw.ch_names) == n_ch
494+
mean_ch = raw.copy().pick("fnirs_td_moments_mean").info["chs"][0]
495+
assert mean_ch["unit"] == FIFF.FIFF_UNIT_SEC
496+
var_ch = raw.copy().pick("fnirs_td_moments_variance").info["chs"][0]
497+
assert var_ch["unit"] == FIFF.FIFF_UNIT_SEC2
495498
else:
496499
pass # TODO: add some gated tests
497500
if ver == "old":
498-
sfreq = 8.257638
501+
sfreq = 8.256495
499502
n_annot = 2
500503
else:
501-
sfreq = 3.759398
504+
sfreq = 3.759351
502505
n_annot = 8
503506

504507
assert_allclose(raw.info["sfreq"], sfreq, atol=1e-5)
@@ -533,7 +536,7 @@ def test_user_set_sfreq(sfreq, context):
533536
with context:
534537
# both sfreqs are far enough from true rate to yield >1% jitter
535538
with pytest.warns(RuntimeWarning, match=r"jitter of \d+\.\d*% in sample times"):
536-
raw = read_raw_snirf(kernel_hb, preload=False, sfreq=sfreq)
539+
raw = read_raw_snirf(kernel_hb_old, preload=False, sfreq=sfreq)
537540
assert raw.info["sfreq"] == sfreq
538541

539542

0 commit comments

Comments
 (0)