diff --git a/neo/rawio/neuralynxrawio/ncssections.py b/neo/rawio/neuralynxrawio/ncssections.py index cf3da6113..8eb4b98c1 100644 --- a/neo/rawio/neuralynxrawio/ncssections.py +++ b/neo/rawio/neuralynxrawio/ncssections.py @@ -37,6 +37,19 @@ import math import numpy as np +from enum import IntEnum, auto + + +class AcqType(IntEnum): + PRE4 = auto() + BML = auto() + DIGITALLYNX = auto() + DIGITALLYNXSX = auto() + CHEETAH64 = auto() + RAWDATAFILE = auto() + CHEETAH560 = auto() + ATLAS = auto() + UNKNOWN = auto() class NcsSections: """ @@ -250,7 +263,7 @@ def build_for_ncs_file(ncsMemMap, nlxHdr, gapTolerance=None, strict_gap_mode=Tru acqType = nlxHdr.type_of_recording() freq = nlxHdr["sampling_rate"] - if acqType == "PRE4": + if acqType == AcqType.PRE4: # Old Neuralynx style with truncated whole microseconds for actual sampling. This # restriction arose from the sampling being based on a master 1 MHz clock. microsPerSampUsed = math.floor(NcsSectionsFactory.get_micros_per_samp_for_freq(freq)) @@ -266,7 +279,8 @@ def build_for_ncs_file(ncsMemMap, nlxHdr, gapTolerance=None, strict_gap_mode=Tru ncsSects.sampFreqUsed = sampFreqUsed ncsSects.microsPerSampUsed = microsPerSampUsed - elif acqType in ["DIGITALLYNX", "DIGITALLYNXSX", "CHEETAH64", "CHEETAH560", "RAWDATAFILE"]: + elif acqType in [AcqType.DIGITALLYNX, AcqType.DIGITALLYNXSX, AcqType.CHEETAH64, + AcqType.CHEETAH560, AcqType.RAWDATAFILE]: # digital lynx style with fractional frequency and micros per samp determined from block times if gapTolerance is None: if strict_gap_mode: @@ -293,7 +307,7 @@ def build_for_ncs_file(ncsMemMap, nlxHdr, gapTolerance=None, strict_gap_mode=Tru ncsSects.sampFreqUsed = sampFreqUsed ncsSects.microsPerSampUsed = NcsSectionsFactory.get_micros_per_samp_for_freq(sampFreqUsed) - elif acqType == "BML" or acqType == "ATLAS": + elif acqType == AcqType.BML or acqType == AcqType.ATLAS: # BML & ATLAS style with fractional frequency and micros per samp if strict_gap_mode: # this is the old behavior, maybe we could put 0.9 sample interval no ? diff --git a/neo/rawio/neuralynxrawio/nlxheader.py b/neo/rawio/neuralynxrawio/nlxheader.py index ea3afd01a..f643a3d52 100644 --- a/neo/rawio/neuralynxrawio/nlxheader.py +++ b/neo/rawio/neuralynxrawio/nlxheader.py @@ -1,14 +1,23 @@ import datetime +import dateutil from packaging.version import Version import os import re from collections import OrderedDict +from neo.rawio.neuralynxrawio.ncssections import AcqType + class NlxHeader(OrderedDict): """ Representation of basic information in all 16 kbytes Neuralynx file headers, including dates opened and closed if given. + + The OrderedDict contains entries for each property given in the header with '-' in front + of the key value as well as an 'ApplicationName', 'ApplicationVersion', 'recording_opened' + and 'recording_closed' entries. The 'InputRange', 'channel_ids', 'channel_names' and + 'bit_to_microvolt' properties are set to lists of entries for each channel which may be + in the file. """ HEADER_SIZE = 2**14 # Neuralynx files have a txt header of 16kB @@ -22,7 +31,9 @@ def _to_bool(txt): else: raise Exception("Can not convert %s to bool" % txt) - # keys that may be present in header which we parse + # Keys that may be present in header which we parse. First entry of tuple is what is + # present in header, second entry is key which will be used in dictionary, third entry + # type the value will be converted to. txt_header_keys = [ ("AcqEntName", "channel_names", None), # used ("FileType", "", None), @@ -71,65 +82,92 @@ def _to_bool(txt): # Filename and datetime may appear in header lines starting with # at # beginning of header or in later versions as a property. The exact format # used depends on the application name and its version as well as the - # -FileVersion property. + # -FileVersion property. Examples of each known case are shown below in comments. # - # There are 4 styles understood by this code and the patterns used for parsing - # the items within each are stored in a dictionary. Each dictionary is then - # stored in main dictionary keyed by an abbreviation for the style. - header_pattern_dicts = { - # BML - "bml": dict( - datetime1_regex=r"## Time Opened: \(m/d/y\): (?P\S+)" r" At Time: (?P