1515Author: Samuel Garcia
1616
1717"""
18+ from pathlib import Path
1819
1920from .baserawio import (BaseRawIO , _signal_channel_dtype , _signal_stream_dtype ,
20- _spike_channel_dtype , _event_channel_dtype )
21+ _spike_channel_dtype , _event_channel_dtype )
2122
2223import numpy as np
2324from xml .etree import ElementTree
2425
2526
2627class NeuroScopeRawIO (BaseRawIO ):
27- extensions = ['xml' , 'dat' ]
28+ extensions = ['xml' , 'dat' , 'lfp' , 'eeg' ]
2829 rawmode = 'one-file'
2930
30- def __init__ (self , filename = '' ):
31+ def __init__ (self , filename = '' , binary_file = None ):
32+ """raw reader for Neuroscope
33+
34+ Parameters
35+ ----------
36+ filename : str, optional
37+ Usually the file_path of an xml file
38+ binary_file : _type_, optional
39+ The binary data file corresponding to the xml file:
40+ Supported formats: ['.dat', '.lfp', '.eeg']
41+ """
3142 BaseRawIO .__init__ (self )
3243 self .filename = filename
44+ self .binary_file = binary_file
3345
3446 def _source_name (self ):
35- return self .filename . replace ( '.xml' , '' ). replace ( '.dat' , '' )
47+ return Path ( self .filename ). stem
3648
3749 def _parse_header (self ):
38- filename = self .filename .replace ('.xml' , '' ).replace ('.dat' , '' )
50+ # Load the right paths to xml and data
51+ self ._resolve_xml_and_data_paths ()
3952
40- tree = ElementTree .parse (filename + '.xml' )
53+ # Parse XML-file
54+ tree = ElementTree .parse (self .xml_file_path )
4155 root = tree .getroot ()
4256 acq = root .find ('acquisitionSystem' )
4357 nbits = int (acq .find ('nBits' ).text )
@@ -64,7 +78,8 @@ def _parse_header(self):
6478 else :
6579 raise (NotImplementedError )
6680
67- self ._raw_signals = np .memmap (filename + '.dat' , dtype = sig_dtype ,
81+ # Extract signal from the data file
82+ self ._raw_signals = np .memmap (self .data_file_path , dtype = sig_dtype ,
6883 mode = 'r' , offset = 0 ).reshape (- 1 , nb_channel )
6984
7085 # one unique stream
@@ -122,3 +137,43 @@ def _get_analogsignal_chunk(self, block_index, seg_index, i_start, i_stop,
122137 channel_indexes = slice (None )
123138 raw_signals = self ._raw_signals [slice (i_start , i_stop ), channel_indexes ]
124139 return raw_signals
140+
141+ def _resolve_xml_and_data_paths (self ):
142+ file_path = Path (self .filename )
143+ supported_data_extensions = ['.dat' , '.lfp' , '.eeg' ]
144+ suffix = file_path .suffix
145+ if suffix == '.xml' :
146+ xml_file_path = file_path
147+ data_file_path = self .binary_file
148+ # If no binary file provided iterate over the formats
149+ if data_file_path is None :
150+ for extension in supported_data_extensions :
151+ data_file_path = file_path .with_suffix (extension )
152+ if data_file_path .is_file ():
153+ break
154+ assert data_file_path .is_file (), "data binary not found for file " \
155+ f"{ data_file_path } with supported extensions: { supported_data_extensions } "
156+ elif suffix == '' :
157+ xml_file_path = file_path .with_suffix (".xml" )
158+ data_file_path = self .binary_file
159+ # Empty suffix to keep testing behavior with non-existing
160+ # file passing for backwards compatibility
161+ if data_file_path is None :
162+ data_file_path = file_path .with_suffix (".dat" )
163+ elif suffix in supported_data_extensions :
164+ xml_file_path = file_path .with_suffix (".xml" )
165+ data_file_path = file_path
166+ else :
167+ error_string = (
168+ f"Format { suffix } not supported "
169+ f"filename format should be { supported_data_extensions } or .xml"
170+ )
171+ raise KeyError (error_string )
172+
173+ msg = f"xml file not found at the expected location { xml_file_path } "
174+ assert xml_file_path .is_file (), msg
175+ msg = f"binary file not found at the expected location { data_file_path } "
176+ assert data_file_path .is_file (), msg
177+
178+ self .xml_file_path = xml_file_path
179+ self .data_file_path = data_file_path
0 commit comments