@@ -37,6 +37,11 @@ def _check_curry_filename(fname):
3737 # try suffixes
3838 if fname_in .suffix in CURRY_SUFFIX_DATA :
3939 fname_out = fname_in
40+ elif (
41+ fname_in .with_suffix ("" ).exists ()
42+ and fname_in .with_suffix ("" ).suffix in CURRY_SUFFIX_DATA
43+ ):
44+ fname_out = fname_in .with_suffix ("" )
4045 else :
4146 for data_suff in CURRY_SUFFIX_DATA :
4247 if fname_in .with_suffix (data_suff ).exists ():
@@ -116,10 +121,12 @@ def _get_curry_meas_info(fname):
116121 )
117122 is_ascii = byteorder == "ASCII"
118123
119- # amp info
120- # TODO - seems like there can be identifiable information (serial numbers, dates).
124+ # amplifier info
125+ # TODO - PRIVACY
126+ # seems like there can be identifiable information (serial numbers, dates).
121127 # MNE anonymization functions only overwrite "serial" and "site", though
122- # TODO - there can be filter details, too
128+ # TODO - FUTURE ENHANCEMENT
129+ # # there can be filter details in AmplifierInfo, too
123130 amp_info = (
124131 re .compile (r"AmplifierInfo\s*=.*\n" )
125132 .search (content_hdr )
@@ -182,9 +189,12 @@ def _get_curry_epoch_info(fname):
182189 event_id = event_id ,
183190 tmin = 0.0 ,
184191 tmax = (n_samples - 1 ) / sfreq ,
185- baseline = (0 , 0 ),
192+ baseline = None ,
193+ detrend = None ,
194+ verbose = False ,
186195 metadata = epochmetainfo ,
187196 reject_by_annotation = False ,
197+ reject = None ,
188198 )
189199
190200
@@ -251,9 +261,18 @@ def _extract_curry_info(fname):
251261
252262 # events
253263 events = currydata ["events" ]
254- # annotations = currydata[
255- # "annotations"
256- # ] # TODO - these dont really seem to correspond to events! what is it?
264+ # BUG in curryreader (v.0.1.1)! annotations read incorrectly (shifted by 1 line)
265+ annotations = currydata ["annotations" ]
266+ assert len (annotations ) == len (events )
267+ if len (events ) > 0 :
268+ # quick fix: shift annotation down, last one is missing
269+ annotations = annotations [1 :] + ["" ]
270+ event_desc = dict ()
271+ for k , v in zip (events [:, 1 ], annotations ):
272+ if int (k ) not in event_desc .keys ():
273+ event_desc [int (k )] = v .strip () if (v .strip () != "" ) else str (int (k ))
274+ else :
275+ event_desc = None
257276
258277 # impedance measurements
259278 # moved to standalone def; see read_impedances_curry
@@ -324,8 +343,7 @@ def _extract_curry_info(fname):
324343 assert len (ch_pos ) == ch_types .count ("eeg" ) + ch_types .count ("mag" )
325344
326345 # finetune channel types (e.g. stim, eog etc might be identified by name)
327- # TODO?
328-
346+ # TODO - FUTURE ENHANCEMENT
329347 # scale data to SI units
330348 orig_units = dict (zip (ch_names , units ))
331349 cals = [
@@ -342,6 +360,7 @@ def _extract_curry_info(fname):
342360 landmarkslabels ,
343361 hpimatrix ,
344362 events ,
363+ event_desc ,
345364 orig_format ,
346365 orig_units ,
347366 cals ,
@@ -366,7 +385,9 @@ def _read_annotations_curry(fname, sfreq="auto"):
366385 """
367386 fname = _check_curry_filename (fname )
368387
369- (sfreq_fromfile , _ , _ , _ , _ , _ , _ , _ , events , _ , _ , _ ) = _extract_curry_info (fname )
388+ (sfreq_fromfile , _ , _ , _ , _ , _ , _ , _ , events , event_desc , _ , _ , _ ) = (
389+ _extract_curry_info (fname )
390+ )
370391 if sfreq == "auto" :
371392 sfreq = sfreq_fromfile
372393 elif np .isreal (sfreq ):
@@ -381,7 +402,7 @@ def _read_annotations_curry(fname, sfreq="auto"):
381402 if isinstance (events , np .ndarray ): # if there are events
382403 events = events .astype ("int" )
383404 events = np .insert (events , 1 , np .diff (events [:, 2 :]).flatten (), axis = 1 )[:, :3 ]
384- return annotations_from_events (events , sfreq )
405+ return annotations_from_events (events , sfreq , event_desc = event_desc )
385406 else :
386407 warn ("no event annotations found" )
387408 return None
@@ -417,18 +438,18 @@ def _make_curry_montage(ch_names, ch_types, ch_pos, landmarks, landmarkslabels):
417438 hsp_pos = None
418439 # make dig montage for eeg
419440 mont = None
420- if ch_pos . shape [ 1 ] in [ 3 , 6 ]: # eeg xyz space
441+ if len ( ch_pos_eeg ) > 0 :
421442 mont = make_dig_montage (
422443 ch_pos = ch_pos_eeg ,
423444 nasion = landmark_dict ["Nas" ],
424445 lpa = landmark_dict ["LPA" ],
425446 rpa = landmark_dict ["RPA" ],
426447 hsp = hsp_pos ,
427448 hpi = hpi_pos ,
428- coord_frame = "unknown " ,
449+ coord_frame = "head " ,
429450 )
430451 else : # not recorded?
431- pass
452+ warn ( "No eeg sensor locations found in file." )
432453
433454 return mont
434455
@@ -532,21 +553,23 @@ def _set_chanloc_curry(inst, ch_types, ch_pos, landmarks, landmarkslabels):
532553 else :
533554 raise NotImplementedError
534555
535- # _make_trans_dig(curry_paths, inst.info, curry_dev_dev_t) # TODO - necessary?!
556+ # TODO - REVIEW NEEDED
557+ # do we need further transpositions for MEG channel positions?
558+ # the testfiles i got look good to me..
559+ # _make_trans_dig(curry_paths, inst.info, curry_dev_dev_t)
536560
537561
538562@verbose
539563def read_raw_curry (
540- fname , import_epochs_as_events = False , preload = False , verbose = None
564+ fname , import_epochs_as_annotations = False , preload = False , verbose = None
541565) -> "RawCurry" :
542566 """Read raw data from Curry files.
543567
544568 Parameters
545569 ----------
546570 fname : path-like
547- Path to a curry file with extensions ``.dat``, ``.dap``, ``.rs3``,
548- ``.cdt``, ``.cdt.dpa``, ``.cdt.cef`` or ``.cef``.
549- import_epochs_as_events : bool
571+ Path to a valid curry file.
572+ import_epochs_as_annotations : bool
550573 Set to ``True`` if you want to import epoched recordings as continuous ``raw``
551574 object with event annotations. Only do this if you know your data allows it.
552575 %(preload)s
@@ -568,19 +591,19 @@ def read_raw_curry(
568591 inst = RawCurry (fname , preload , verbose )
569592 if rectype in ["epochs" , "evoked" ]:
570593 curry_epoch_info = _get_curry_epoch_info (fname )
571- if import_epochs_as_events :
594+ if import_epochs_as_annotations :
595+ # TODO - REVIEW NEEDED
596+ # give those annotations a specific name/type?
572597 epoch_annotations = annotations_from_events (
573598 events = curry_epoch_info ["events" ],
574599 event_desc = {v : k for k , v in curry_epoch_info ["event_id" ].items ()},
575600 sfreq = inst .info ["sfreq" ],
576601 )
577602 inst .set_annotations (inst .annotations + epoch_annotations )
578603 else :
579- inst = Epochs (
580- inst , ** curry_epoch_info
581- ) # TODO - seems to reject flat channel
604+ inst = Epochs (inst , ** curry_epoch_info )
582605 if rectype == "evoked" :
583- raise NotImplementedError
606+ raise NotImplementedError # not sure this is even supported format
584607 return inst
585608
586609
@@ -590,8 +613,7 @@ class RawCurry(BaseRaw):
590613 Parameters
591614 ----------
592615 fname : path-like
593- Path to a curry file with extensions ``.dat``, ``.dap``, ``.rs3``,
594- ``.cdt``, ``.cdt.dpa``, ``.cdt.cef`` or ``.cef``.
616+ Path to a valid curry file.
595617 %(preload)s
596618 %(verbose)s
597619
@@ -615,6 +637,7 @@ def __init__(self, fname, preload=False, verbose=None):
615637 landmarkslabels ,
616638 hpimatrix ,
617639 events ,
640+ event_desc ,
618641 orig_format ,
619642 orig_units ,
620643 cals ,
@@ -655,11 +678,11 @@ def __init__(self, fname, preload=False, verbose=None):
655678 events = np .insert (events , 1 , np .diff (events [:, 2 :]).flatten (), axis = 1 )[
656679 :, :3
657680 ]
658- annot = annotations_from_events (events , sfreq )
681+ annot = annotations_from_events (events , sfreq , event_desc = event_desc )
659682 self .set_annotations (annot )
660683
661684 # add sensor locations
662- # TODO - review wanted!
685+ # TODO - REVIEW NEEDED
663686 assert len (self .info ["ch_names" ]) == len (ch_types ) >= len (ch_pos )
664687 _set_chanloc_curry (
665688 inst = self ,
@@ -670,13 +693,15 @@ def __init__(self, fname, preload=False, verbose=None):
670693 )
671694
672695 # add HPI data (if present)
696+ # TODO - FUTURE ENHANCEMENT
673697 # from curryreader docstring:
674698 # "HPI-coil measurements matrix (Orion-MEG only) where every row is:
675699 # [measurementsample, dipolefitflag, x, y, z, deviation]"
676- # that's incorrect, though. it seems to be:
700+ #
701+ # that's incorrect, though. it ratehr seems to be:
677702 # [sample, dipole_1, x_1,y_1, z_1, dev_1, ..., dipole_n, x_n, ...]
678703 # for all n coils.
679- # TODO - do they actually store cHPI?
704+ # We are missing good example data or format specs! do not implement for now.
680705 if not isinstance (hpimatrix , list ):
681706 warn ("cHPI data found, but reader not implemented." )
682707 hpisamples = hpimatrix [:, 0 ]
@@ -707,8 +732,7 @@ def read_impedances_curry(fname, verbose=None):
707732 Parameters
708733 ----------
709734 fname : path-like
710- Path to a curry file with extensions ``.dat``, ``.dap``, ``.rs3``,
711- ``.cdt``, ``.cdt.dpa``, ``.cdt.cef`` or ``.cef``.
735+ Path to a valid curry file.
712736 %(verbose)s
713737
714738 Returns
@@ -730,15 +754,10 @@ def read_impedances_curry(fname, verbose=None):
730754 impedances = currydata ["impedances" ]
731755 ch_names = currydata ["labels" ]
732756
733- # try get measurement times
734- # TODO - is this even possible?
735- annotations = currydata [
736- "annotations"
737- ] # dont really seem to correspond to events!?!
738- for anno in set (annotations ):
739- if "impedance" in anno .lower ():
740- print ("FOUND IMPEDANCE ANNOTATION!" )
741- print (f"'{ anno } ' - N={ len ([a for a in annotations if a == anno ])} " )
757+ # get impedance measurement times
758+ # TODO - FUTURE ENHANCEMENT
759+ # info can be in the files (as events or IMPEDANCE_TIMES)
760+ # inconsistently, though, and low priority
742761
743762 # print impedances
744763 print ("impedance measurements:" )
@@ -749,22 +768,24 @@ def read_impedances_curry(fname, verbose=None):
749768
750769
751770@verbose
752- def read_montage_curry (fname , verbose = None ):
753- """Read eeg montage from Curry files.
771+ def read_dig_curry (fname , verbose = None ):
772+ """Read electrode locations from Curry files.
754773
755774 Parameters
756775 ----------
757776 fname : path-like
758- The filename .
777+ A valid Curry file .
759778 %(verbose)s
760779
761780 Returns
762781 -------
763782 montage : instance of DigMontage | None
764783 The montage.
765784 """
785+ # TODO - REVIEW NEEDED
786+ # API? do i need to add this in the docs somewhere?
766787 fname = _check_curry_filename (fname )
767- (_ , _ , ch_names , ch_types , ch_pos , landmarks , landmarkslabels , _ , _ , _ , _ , _ ) = (
788+ (_ , _ , ch_names , ch_types , ch_pos , landmarks , landmarkslabels , _ , _ , _ , _ , _ , _ ) = (
768789 _extract_curry_info (fname )
769790 )
770791 return _make_curry_montage (ch_names , ch_types , ch_pos , landmarks , landmarkslabels )
0 commit comments