Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions neo/rawio/spikeglxrawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def _source_name(self):
def _parse_header(self):
self.signals_info_list = scan_files(self.dirname)
_add_segment_order(self.signals_info_list)
_add_segment_timing(self.signals_info_list)

# sort stream_name by higher sampling rate first
srates = {info["stream_name"]: info["sampling_rate"] for info in self.signals_info_list}
Expand Down Expand Up @@ -269,9 +270,7 @@ def _parse_header(self):
for seg_index in range(nb_segment):
info = self.signals_info_dict[seg_index, stream_name]

frame_start = float(info["meta"]["firstSample"])
sampling_frequency = info["sampling_rate"]
t_start = frame_start / sampling_frequency
t_start = info["t_start"]

self._t_starts[stream_name][seg_index] = t_start

Expand All @@ -282,8 +281,7 @@ def _parse_header(self):
self._t_starts[sync_stream_name] = {}
self._t_starts[sync_stream_name][seg_index] = t_start

t_stop = info["sample_length"] / info["sampling_rate"]
self._t_stops[seg_index] = max(self._t_stops[seg_index], t_stop)
self._t_stops[seg_index] = max(self._t_stops[seg_index], info["t_stop"])

# fille into header dict
self.header = {}
Expand Down Expand Up @@ -407,6 +405,40 @@ def scan_files(dirname):
return info_list


def _add_segment_timing(info_list):
"""
Add ``info["first_sample"]``, ``info["t_start"]``, and ``info["t_stop"]`` per signal.

Reads ``meta["firstSample"]`` (documented in every SpikeGLX phase) and converts
to float. When absent, defaults to 0 with a UserWarning naming the file. Zero
is the correct fallback for a file that starts at the beginning of its SpikeGLX
run, which covers the expected causes of a missing tag: pre-2016 builds (the
tag was introduced in 2016), recordings where the end-of-run write was
interrupted, and ``.meta`` files modified after acquisition.

Then stores ``info["t_start"] = info["first_sample"] / info["sampling_rate"]``
and ``info["t_stop"] = info["sample_length"] / info["sampling_rate"]`` in
seconds, so downstream code can read both directly without recomputation.
"""
for info in info_list:
meta = info["meta"]
if "firstSample" in meta:
info["first_sample"] = float(meta["firstSample"])
else:
warn(
f"'firstSample' missing from {info['meta_file']}; defaulting to 0. "
f"Zero is correct for files that start at the beginning of their SpikeGLX run, "
f"but wrong for any file that represents a non-initial trigger or that was "
f"derived from a longer recording. Typical causes: pre-2016 SpikeGLX build, "
f"interrupted end-of-run write, or post-acquisition modification of the .meta file.",
UserWarning,
stacklevel=2,
)
info["first_sample"] = 0.0
info["t_start"] = info["first_sample"] / info["sampling_rate"]
info["t_stop"] = info["sample_length"] / info["sampling_rate"]


def _build_signals_info_dict(info_list):
"""
Re-index a flat list of info dicts into a dict keyed by (seg_index, stream_name).
Expand Down
Loading