@@ -124,18 +124,46 @@ def _to_bool(txt):
124124 ),
125125 }
126126
127- def __init__ (self , filename ):
127+ def __init__ (self , filename , props_only = False ):
128128 """
129129 Factory function to build NlxHeader for a given file.
130+
131+ :param filename: name of Neuralynx file
132+ :param props_only: if true, will not try and read time and date or check start
130133 """
131134 super (OrderedDict , self ).__init__ ()
132135 with open (filename , "rb" ) as f :
133136 txt_header = f .read (NlxHeader .HEADER_SIZE )
134137 txt_header = txt_header .strip (b"\x00 " ).decode ("latin-1" )
135138
136139 # must start with 8 # characters
137- assert txt_header .startswith ("########" ), "Neuralynx files must start with 8 # characters."
140+ if not props_only and not txt_header .startswith ("########" ):
141+ ValueError ("Neuralynx files must start with 8 # characters." )
142+
143+ self .read_properties (filename , txt_header )
144+
145+ if not props_only :
146+ self .readTimeDate (txt_header )
147+
148+ @staticmethod
149+ def build_with_properties_only (filename ):
150+ """
151+ Builds a version of the header without time and date or other validity checking.
152+
153+ Intended mostly for utilities but may also be useful for some recalcitrant header formats.
138154
155+ :param filename: name of Neuralynx file.
156+ :return: NlxHeader with properties from header text
157+ """
158+ res = OrderedDict ()
159+
160+
161+ def read_properties (self , filename , txt_header ):
162+ """
163+ Read properties from header and place in OrderedDictionary which this object is.
164+ :param filename: name of ncs file, used for extracting channel number
165+ :param txt_header: header text
166+ """
139167 # find keys
140168 for k1 , k2 , type_ in NlxHeader .txt_header_keys :
141169 pattern = r"-(?P<name>" + k1 + r")\s+(?P<value>[\S ]*)"
@@ -149,17 +177,14 @@ def __init__(self, filename):
149177 if type_ is not None :
150178 value = type_ (value )
151179 self [name ] = value
152-
153180 # if channel_ids or s not in self then the filename is used
154181 name = os .path .splitext (os .path .basename (filename ))[0 ]
155-
156182 # convert channel ids
157183 if "channel_ids" in self :
158184 chid_entries = re .findall (r"\S+" , self ["channel_ids" ])
159185 self ["channel_ids" ] = [int (c ) for c in chid_entries ]
160186 else :
161187 self ["channel_ids" ] = ["unknown" ]
162-
163188 # convert channel names
164189 if "channel_names" in self :
165190 name_entries = re .findall (r"\S+" , self ["channel_names" ])
@@ -170,7 +195,6 @@ def __init__(self, filename):
170195 ), "Number of channel ids does not match channel names."
171196 else :
172197 self ["channel_names" ] = ["unknown" ] * len (self ["channel_ids" ])
173-
174198 # version and application name
175199 # older Cheetah versions with CheetahRev property
176200 if "CheetahRev" in self :
@@ -192,11 +216,9 @@ def __init__(self, filename):
192216 else :
193217 self ["ApplicationName" ] = "Neuraview"
194218 app_version = "2"
195-
196219 if " Development" in app_version :
197220 app_version = app_version .replace (" Development" , ".dev0" )
198221 self ["ApplicationVersion" ] = Version (app_version )
199-
200222 # convert bit_to_microvolt
201223 if "bit_to_microVolt" in self :
202224 btm_entries = re .findall (r"\S+" , self ["bit_to_microVolt" ])
@@ -206,7 +228,6 @@ def __init__(self, filename):
206228 assert len (self ["bit_to_microVolt" ]) == len (
207229 self ["channel_ids" ]
208230 ), "Number of channel ids does not match bit_to_microVolt conversion factors."
209-
210231 if "InputRange" in self :
211232 ir_entries = re .findall (r"\w+" , self ["InputRange" ])
212233 if len (ir_entries ) == 1 :
@@ -217,9 +238,13 @@ def __init__(self, filename):
217238 chid_entries
218239 ), "Number of channel ids does not match input range values."
219240
220- # Format of datetime depends on app name, app version
221- # :TODO: this works for current examples but is not likely actually related
222- # to app version in this manner.
241+ def readTimeDate (self , txt_header ):
242+ """
243+ Read time and date from text of header appropriate for app name and version
244+
245+ :TODO: this works for current examples but is not likely actually related
246+ to app version in this manner.
247+ """
223248 an = self ["ApplicationName" ]
224249 if an == "Cheetah" :
225250 av = self ["ApplicationVersion" ]
@@ -245,7 +270,6 @@ def __init__(self, filename):
245270 an = "Unknown"
246271 av = "NA"
247272 hpd = NlxHeader .header_pattern_dicts ["def" ]
248-
249273 # opening time
250274 sr = re .search (hpd ["datetime1_regex" ], txt_header )
251275 if not sr :
@@ -257,7 +281,6 @@ def __init__(self, filename):
257281 self ["recording_opened" ] = datetime .datetime .strptime (
258282 dt1 ["date" ] + " " + dt1 ["time" ], hpd ["datetimeformat" ]
259283 )
260-
261284 # close time, if available
262285 if "datetime2_regex" in hpd :
263286 sr = re .search (hpd ["datetime2_regex" ], txt_header )
0 commit comments