diff --git a/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_notes.md b/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_notes.md index 4d04ab3..bcfacf2 100644 --- a/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_notes.md +++ b/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_notes.md @@ -129,6 +129,29 @@ column_descriptions = dict( ) ``` +### Temporal alignment + +Align TTL signals to Raw Bpod trial times: +Compute the time shift from raw Bpod trial start times to the aligned center port timestamps. +The aligned center port times can be accessed from the processed behavior data using the `"Cled"` field. + +```python +from ndx_structured_behavior.utils import loadmat + +bpod_data = loadmat("path/to/bpod_session.mat")["SessionData"] # should contain "SessionData" named struct +S_struct_data = loadmat("path/to/processed_behavior.mat")["S"] # should contain "S" named struct + +# The trial start times from the Bpod data +bpod_trial_start_times = bpod_data['TrialStartTimestamp'] + +# "Cled" field contains the aligned onset and offset times for each trial [2 x ntrials] +center_port_aligned_onset_times = [center_port_times[0] for center_port_times in S_struct_data["Cled"]] +time_shift = bpod_trial_start_times[0] - center_port_aligned_onset_times[0] +``` + +We are using this computed time shift to shift the ephys timestamps. + + ### Mapping to NWB The following UML diagram shows the mapping of source data to NWB. diff --git a/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_nwbconverter.py b/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_nwbconverter.py index db703fe..32a807a 100644 --- a/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_nwbconverter.py +++ b/src/constantinople_lab_to_nwb/schierek_embargo_2024/schierek_embargo_2024_nwbconverter.py @@ -141,3 +141,31 @@ def get_metadata(self): ) return metadata + + def temporally_align_data_interfaces(self): + processed_behavior_interface = self.data_interface_objects["ProcessedBehavior"] + center_port_aligned_times = processed_behavior_interface._get_aligned_center_port_times() + + raw_behavior_interface = self.data_interface_objects["RawBehavior"] + trial_start_times_from_bpod, _ = raw_behavior_interface.get_trial_times() + bpod_first_trial_start_time = trial_start_times_from_bpod[0] + time_shift = bpod_first_trial_start_time - center_port_aligned_times[0] + + recording_interface_names = [ + interface_name for interface_name in self.data_interface_objects if "Recording" in interface_name + ] + + for recording_interface_name in recording_interface_names: + recording_interface = self.data_interface_objects[recording_interface_name] + unaligned_timestamps = recording_interface.get_timestamps() + unaligned_starting_time = unaligned_timestamps[0] + if unaligned_starting_time + time_shift < 0: + raise ValueError("The recording aligned starting time should not be negative.") + recording_interface.set_aligned_starting_time(aligned_starting_time=time_shift) + + sorting_interface_names = [ + interface_name for interface_name in self.data_interface_objects if "Sorting" in interface_name + ] + for sorting_interface_name in sorting_interface_names: + sorting_interface = self.data_interface_objects[sorting_interface_name] + sorting_interface.register_recording(self.data_interface_objects[recording_interface_names[0]])