From 3df2d0fe13567e82f8ebfefe50a5c7c1d538c1c8 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Mon, 24 Jul 2023 16:08:43 +0200 Subject: [PATCH 01/13] add gin test --- tests/test_on_data/test_behavior_interfaces.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_on_data/test_behavior_interfaces.py b/tests/test_on_data/test_behavior_interfaces.py index e3a7e55e7..595801953 100644 --- a/tests/test_on_data/test_behavior_interfaces.py +++ b/tests/test_on_data/test_behavior_interfaces.py @@ -11,6 +11,7 @@ from neuroconv.datainterfaces import ( DeepLabCutInterface, + FicTracDataInterface, MiniscopeBehaviorInterface, SLEAPInterface, VideoInterface, @@ -28,6 +29,15 @@ from setup_paths import BEHAVIOR_DATA_PATH, OUTPUT_PATH +class TestFicTracDataInterface(DataInterfaceTestMixin, unittest.TestCase): + data_interface_cls = FicTracDataInterface + interface_kwargs = [ + dict(file_path=str(BEHAVIOR_DATA_PATH / "FicTrac" / "sample" / "sample-20230724_113055.dat")), + ] + + save_directory = OUTPUT_PATH + + class TestVideoInterface(VideoInterfaceMixin, unittest.TestCase): data_interface_cls = VideoInterface interface_kwargs = [ From 53bc1858ec41abc70a4683fcc89d2bb81d880524 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Mon, 24 Jul 2023 16:27:50 +0200 Subject: [PATCH 02/13] passing tests --- src/neuroconv/datainterfaces/__init__.py | 3 + .../behavior/fictrac/__init__.py | 0 .../behavior/fictrac/fictracdatainterface.py | 476 ++++++++++++++++++ .../behavior/fictrac/requirements.txt | 0 src/neuroconv/tools/__init__.py | 1 + 5 files changed, 480 insertions(+) create mode 100644 src/neuroconv/datainterfaces/behavior/fictrac/__init__.py create mode 100644 src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py create mode 100644 src/neuroconv/datainterfaces/behavior/fictrac/requirements.txt diff --git a/src/neuroconv/datainterfaces/__init__.py b/src/neuroconv/datainterfaces/__init__.py index 4ca5ff1f9..1a3eaaeaa 100644 --- a/src/neuroconv/datainterfaces/__init__.py +++ b/src/neuroconv/datainterfaces/__init__.py @@ -1,6 +1,7 @@ # Behavior from .behavior.audio.audiointerface import AudioInterface from .behavior.deeplabcut.deeplabcutdatainterface import DeepLabCutInterface +from .behavior.fictrac.fictracdatainterface import FicTracDataInterface from .behavior.miniscope.miniscopedatainterface import MiniscopeBehaviorInterface from .behavior.sleap.sleapdatainterface import SLEAPInterface from .behavior.video.videodatainterface import VideoInterface @@ -147,6 +148,7 @@ DeepLabCutInterface, SLEAPInterface, MiniscopeBehaviorInterface, + FicTracDataInterface, # Text CsvTimeIntervalsInterface, ExcelTimeIntervalsInterface, @@ -178,6 +180,7 @@ Video=VideoInterface, DeepLabCut=DeepLabCutInterface, SLEAP=SLEAPInterface, + FicTrac=FicTracDataInterface, # Text CsvTimeIntervals=CsvTimeIntervalsInterface, ExcelTimeIntervals=ExcelTimeIntervalsInterface, diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/__init__.py b/src/neuroconv/datainterfaces/behavior/fictrac/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py new file mode 100644 index 000000000..5f533551f --- /dev/null +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -0,0 +1,476 @@ +from pathlib import Path +from typing import Optional + +import numpy as np +from pynwb.behavior import CompassDirection, SpatialSeries +from pynwb.file import NWBFile + +# from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface TODO: Add timing methods +from ....basedatainterface import BaseDataInterface +from ....tools import get_module, get_package +from ....utils import FilePathType, calculate_regular_series_rate + + +class FicTracDataInterface(BaseDataInterface): + """Data interface for FicTrac datasets.""" + + keywords = [ + "fictrack", + "visual tracking", + "fictive path", + "spherical treadmill", + "visual fixation", + ] + + data_columns = [ + "frame_counter", + "rotation_delta_x_cam", + "rotation_delta_y_cam", + "rotation_delta_z_cam", + "rotation_delta_error", + "rotation_delta_x_lab", + "rotation_delta_y_lab", + "rotation_delta_z_lab", + "rotation_x_cam", + "rotation_y_cam", + "rotation_z_cam", + "rotation_x_lab", + "rotation_y_lab", + "rotation_z_lab", + "x_pos_radians_lab", + "y_pos_radians_lab", + "movement_direction", + "movement_speed", + "forward_motion_lab", + "side_motion_lab", + "timestamp", + "sequence_counter", + "delta_timestamp", + "alt_timestamp", + ] + + def __init__( + self, + file_path: FilePathType, + verbose: bool = True, + ): + """ + Interface for writing fictract files to nwb. + + Parameters + ---------- + file_path : FilePathType + Path to the .slp file (the output of sleap) + verbose : bool, default: True + controls verbosity. ``True`` by default. + """ + self.file_path = Path(file_path) + self.verbose = verbose + self._timestamps = None + super().__init__(file_path=file_path) + + def add_to_nwbfile( + self, + nwbfile: NWBFile, + metadata: Optional[dict] = None, + ): + """ + Parameters + ---------- + nwbfile: NWBFile + nwb file to which the recording information is to be added + metadata: dict + metadata info for constructing the nwb file (optional). + """ + + import pandas as pd + + fictrac_data_df = pd.read_csv(self.file_path, sep=",", header=None, names=self.data_columns) + + # Get the timestamps + timestamps = fictrac_data_df["timestamp"].values + rate = calculate_regular_series_rate(series=timestamps) # Returns None if it is not regular + write_timestamps = True + if rate: + write_timestamps = False + + processing_module = get_module(nwbfile=nwbfile, name="Behavior") + + # All the units in FicTrac are in radians, the radius of the ball required to transform to + # Distances is not specified in the format + compass_direction_container = CompassDirection(name="FictracData") + + # Add rotation delta from camera + rotation_delta_cam_columns = [ + "rotation_delta_x_cam", + "rotation_delta_y_cam", + "rotation_delta_z_cam", + ] + + description = ( + "Change in orientation since last frame in radians from the camera frame. \n" + "From the point of view of the camera:" + "x: represents rotation of the axis to the right of the sphere (pitch) " + "y: represents rotation of the axis under the sphere (yaw)" + "z: represents rotation of the axis behind the sphere and into the picture (roll)" + ) + + df_cam_delta_rotation = fictrac_data_df[rotation_delta_cam_columns] + data = df_cam_delta_rotation.to_numpy() + reference_frame = "camera" + spatial_seriess_kwargs = dict( + name="cam_delta_rotation", + data=data, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add rotation delta from lab + rotation_delta_lab_columns = [ + "rotation_delta_x_lab", + "rotation_delta_y_lab", + "rotation_delta_z_lab", + ] + + description = ( + "Change in orientation since last frame in radians from the lab frame. \n" + "From the point of view of the lab:" + "x: represents rotation of the axis in front of the subject (roll) " + "y: represents rotation of the axis to the right of the subject (pitch)" + "z: represents rotation of the axis under the subject (yaw)" + ) + + df_lab_delta_rotation = fictrac_data_df[rotation_delta_lab_columns] + data = df_lab_delta_rotation.to_numpy() + reference_frame = "lab" + spatial_seriess_kwargs = dict( + name="lab_delta_rotation", + data=data, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add rotation from camera + rotation_cam_columns = [ + "rotation_x_cam", + "rotation_y_cam", + "rotation_z_cam", + ] + + description = ( + "Orientation in radians from the camera frame. \n" + "From the point of view of the camera:" + "x: represents rotation of the axis to the right of the sphere (pitch) " + "y: represents rotation of the axis under the sphere (yaw)" + "z: represents rotation of the axis behind the sphere and into the picture (roll)" + ) + + df_cam_rotation = fictrac_data_df[rotation_cam_columns] + data = df_cam_rotation.to_numpy() + reference_frame = "camera" + spatial_seriess_kwargs = dict( + name="cam_rotation", + data=data, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add rotation from the lab + rotation_lab_columns = [ + "rotation_x_lab", + "rotation_y_lab", + "rotation_z_lab", + ] + + description = ( + "Orientation in radians from the lab frame. \n" + "From the point of view of the lab:" + "x: represents rotation of the axis in front of the subject (roll) " + "y: represents rotation of the axis to the right of the subject (pitch)" + "z: represents rotation of the axis under the subject (yaw)" + ) + + df_lab_rotation = fictrac_data_df[rotation_lab_columns] + data = df_lab_rotation.to_numpy() + reference_frame = "lab" + spatial_seriess_kwargs = dict( + name="lab_rotation", + data=data, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add position in radians from the lab + position_lab_columns = [ + "x_pos_radians_lab", + "y_pos_radians_lab", + ] + + description = ( + "Position in radians from the lab frame. \n" + "From the point of view of the lab:" + "x: represents position of the axis in front of the subject (roll) " + "y: represents position of the axis to the right of the subject (pitch)" + ) + + df_lab_position = fictrac_data_df[position_lab_columns] + data = df_lab_position.to_numpy() + reference_frame = "lab" + spatial_seriess_kwargs = dict( + name="lab_position", + data=data, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add movement direction + movement_direction = fictrac_data_df["movement_direction"].values + reference_frame = "lab" + description = "Direction of movement in radians from the lab frame" + + spatial_seriess_kwargs = dict( + name="movement_direction", + reference_frame=reference_frame, + data=movement_direction, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add movement speed + movement_speed = fictrac_data_df["movement_speed"].values + reference_frame = "lab" + description = "Speed of movement in radians from the lab frame" + + spatial_seriess_kwargs = dict( + name="movement_speed", + data=movement_speed, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add integrated motion + integrated_motin_columns = [ + "forward_motion_lab", + "side_motion_lab", + ] + + description = "Integrated motion in radians from the lab frame" + + df_integrated_motion = fictrac_data_df[integrated_motin_columns] + data = df_integrated_motion.to_numpy() + reference_frame = "lab" + + spatial_seriess_kwargs = dict( + name="integrated_motion", + data=data, + unit="radians", + reference_frame=reference_frame, + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add error in rotation delta + rotation_delta_error = fictrac_data_df["rotation_delta_error"].values + reference_frame = "lab" + description = "Error in rotation delta in radians from the lab frame" + + spatial_seriess_kwargs = dict( + name="rotation_delta_error", + data=rotation_delta_error, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + + # Add the compass direction container to the processing module + processing_module.add_data_interface(compass_direction_container) + + +def parse_fictrac_config(filename) -> dict: + """ + Parse a FicTrac configuration file and return a dictionary of its parameters. See the + definition of the parameters in https://github.com/rjdmoore/fictrac/blob/master/doc/params.md for more information. + + Parameters + ---------- + filename : str, Path + Path to the configuration file. + + Returns + ------- + dict + A dictionary where the keys are the parameter names and the values are the parameter values. + + Raises + ------ + IOError + If the file cannot be read. + ValueError + If a value in the file cannot be converted to the expected type. + + Examples + -------- + >>> config = parse_fictrac_config('/path/to/config.txt') + >>> print(config['src_fn']) + 'sample.mp4' + """ + import re + + # Tyiping information based on https://github.com/rjdmoore/fictrac/blob/master/doc/params.md + type_info = { + "src_fn": "string OR int", + "vfov": "float", + "do_display": "bool", + "save_debug": "bool", + "save_raw": "bool", + "sock_host": "string", + "sock_port": "int", + "com_port": "string", + "com_baud": "int", + "fisheye": "bool", + "q_factor": "int", + "src_fps": "float", + "max_bad_frames": "int", + "opt_do_global": "bool", + "opt_max_err": "float", + "thr_ratio": "float", + "thr_win_pc": "float", + "vid_codec": "string", + "sphere_map_fn": "string", + "opt_max_evals": "int", + "opt_bound": "float", + "opt_tol": "float", + "c2a_cnrs_xy": "vec", + "c2a_cnrs_yz": "vec", + "c2a_cnrs_xz": "vec", + "c2a_src": "string", + "c2a_r": "vec", + "c2a_t": "vec", + "roi_circ": "vec", + "roi_c": "vec", + "roi_r": "float", + "roi_ignr": "vec>", + } + + # Function to parse value based on type information + def parse_value(value_str, type_str): + value_str = value_str.strip() + if type_str == "bool": + return value_str == "y" + elif "vec" in type_str: + # remove curly braces and split by comma + values = value_str.replace("{", "").replace("}", "").split(",") + if "int" in type_str: + return [int(val) for val in values] + elif "float" in type_str: + return [float(val) for val in values] + elif type_str == "int": + return int(value_str) + elif type_str == "float": + return float(value_str) + else: + return value_str + + # Open and read the file + with open(filename, "r") as f: + file_lines = f.readlines() + + parsed_config = {} + + header_line = file_lines[0] + version, build_date = re.search( + r"FicTrac (v[0-9.]+) config file \(build date ([A-Za-z0-9 ]+)\)", header_line + ).groups() + parsed_config["version"] = version + parsed_config["build_date"] = build_date + + # Parse the file + file_content = file_lines[1:] + for line in file_content[1:]: # Skip the first line + key, value_str = line.split(":") + key = key.strip() + value_str = value_str.strip() + if key in type_info: + parsed_config[key] = parse_value(value_str, type_info[key]) + else: + raise ValueError(f"Unknown key {key} in the file.") + + return parsed_config diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/requirements.txt b/src/neuroconv/datainterfaces/behavior/fictrac/requirements.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/neuroconv/tools/__init__.py b/src/neuroconv/tools/__init__.py index ee48cda14..4a1a637d2 100644 --- a/src/neuroconv/tools/__init__.py +++ b/src/neuroconv/tools/__init__.py @@ -1,3 +1,4 @@ from .importing import get_package +from .nwb_helpers import get_module from .path_expansion import LocalPathExpander from .processes import deploy_process From 06d2361955bd88aa0438dfee9ea885931dae7cc5 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Mon, 24 Jul 2023 16:46:15 +0200 Subject: [PATCH 03/13] correct descriptions --- .../behavior/fictrac/fictracdatainterface.py | 71 +++++++++++++------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 5f533551f..d6f012c31 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -39,6 +39,7 @@ class FicTracDataInterface(BaseDataInterface): "rotation_z_lab", "x_pos_radians_lab", "y_pos_radians_lab", + "animal_heading", "movement_direction", "movement_speed", "forward_motion_lab", @@ -168,7 +169,7 @@ def add_to_nwbfile( spatial_series = SpatialSeries(**spatial_seriess_kwargs) compass_direction_container.add_spatial_series(spatial_series) - # Add rotation from camera + # Add absolute rotation from camera rotation_cam_columns = [ "rotation_x_cam", "rotation_y_cam", @@ -202,7 +203,7 @@ def add_to_nwbfile( spatial_series = SpatialSeries(**spatial_seriess_kwargs) compass_direction_container.add_spatial_series(spatial_series) - # Add rotation from the lab + # Add absolute rotation from the lab rotation_lab_columns = [ "rotation_x_lab", "rotation_y_lab", @@ -236,25 +237,17 @@ def add_to_nwbfile( spatial_series = SpatialSeries(**spatial_seriess_kwargs) compass_direction_container.add_spatial_series(spatial_series) - # Add position in radians from the lab - position_lab_columns = [ - "x_pos_radians_lab", - "y_pos_radians_lab", - ] - + # Add animal heading in radians + animal_heading = fictrac_data_df["animal_heading"].values + reference_frame = "lab" description = ( - "Position in radians from the lab frame. \n" - "From the point of view of the lab:" - "x: represents position of the axis in front of the subject (roll) " - "y: represents position of the axis to the right of the subject (pitch)" + "Animal heading in radians from the lab frame. " + "This is calculated by integrating the yaw (z-axis) rotations across time." ) - df_lab_position = fictrac_data_df[position_lab_columns] - data = df_lab_position.to_numpy() - reference_frame = "lab" spatial_seriess_kwargs = dict( - name="lab_position", - data=data, + name="animal_heading", + data=animal_heading, reference_frame=reference_frame, unit="radians", description=description, @@ -271,7 +264,12 @@ def add_to_nwbfile( # Add movement direction movement_direction = fictrac_data_df["movement_direction"].values reference_frame = "lab" - description = "Direction of movement in radians from the lab frame" + description = ( + "Instantaneous running direction (radians) of the animal in laboratory coordinates" + "This is the direction the animal is moving in the lab frame. " + "add to animal heading to get direction in the world." + "This values is infered by the rotation of the ball (roll and pitch)" + ) spatial_seriess_kwargs = dict( name="movement_direction", @@ -292,7 +290,7 @@ def add_to_nwbfile( # Add movement speed movement_speed = fictrac_data_df["movement_speed"].values reference_frame = "lab" - description = "Speed of movement in radians from the lab frame" + description = "Instantaneous running speed (radians/frame) of the animal." spatial_seriess_kwargs = dict( name="movement_speed", @@ -310,13 +308,46 @@ def add_to_nwbfile( spatial_series = SpatialSeries(**spatial_seriess_kwargs) compass_direction_container.add_spatial_series(spatial_series) + # Add position in radians from the lab + position_lab_columns = [ + "x_pos_radians_lab", + "y_pos_radians_lab", + ] + + description = ( + "x and y positions in the lab frame in radians. These values are infered by integrating " + "the rotation of the across time. " + ) + + df_lab_position = fictrac_data_df[position_lab_columns] + data = df_lab_position.to_numpy() + reference_frame = "lab" + spatial_seriess_kwargs = dict( + name="lab_position", + data=data, + reference_frame=reference_frame, + unit="radians", + description=description, + ) + + if write_timestamps: + spatial_seriess_kwargs["timestamps"] = timestamps + else: + spatial_seriess_kwargs["rate"] = rate + + spatial_series = SpatialSeries(**spatial_seriess_kwargs) + compass_direction_container.add_spatial_series(spatial_series) + # Add integrated motion integrated_motin_columns = [ "forward_motion_lab", "side_motion_lab", ] - description = "Integrated motion in radians from the lab frame" + description = ( + "Integrated x/y position (radians) of the sphere in laboratory coordinates neglecting" + " heading. Equivalent to the output from two optical mice" + ) df_integrated_motion = fictrac_data_df[integrated_motin_columns] data = df_integrated_motion.to_numpy() From fae4972d8278034ec69e62664fc880fc6564f989 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Mon, 24 Jul 2023 17:02:03 +0200 Subject: [PATCH 04/13] add metadata --- .../behavior/fictrac/fictracdatainterface.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index d6f012c31..7648e5be5 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -61,7 +61,7 @@ def __init__( Parameters ---------- file_path : FilePathType - Path to the .slp file (the output of sleap) + Path to the .dat file (the output of fictrac) verbose : bool, default: True controls verbosity. ``True`` by default. """ @@ -70,6 +70,22 @@ def __init__( self._timestamps = None super().__init__(file_path=file_path) + def get_metadata(self): + metadata = super().get_metadata() + from datetime import datetime + + config_file = self.file_path.parent / "fictrac_config.txt" + if config_file.exists(): + self._config_file = parse_fictrac_config(config_file) + date = self._config_file["build_date"] + date_object = datetime.strptime(date_string, "%b %d %Y") + + metadata["NWBFile"].update( + session_start_time=date_object, + ) + + return metadata + def add_to_nwbfile( self, nwbfile: NWBFile, From 9dac11752f4fe3c76901532af407c39a5b23742b Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 18:40:40 +0200 Subject: [PATCH 05/13] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3be2ee90..083a38832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # (Upcoming) +## Features +Added FicTrac data interface [PR #499](https://github.com/catalystneuro/neuroconv/pull/#499) + # v0.4.0 ### Back-compatibility break From afbc5cf41d23a1c0b82886e9bc0bb0f7b19ebfac Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 18:43:04 +0200 Subject: [PATCH 06/13] trigger cache change --- .github/workflows/dev-testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dev-testing.yml b/.github/workflows/dev-testing.yml index 93f328b64..96dc2ee77 100644 --- a/.github/workflows/dev-testing.yml +++ b/.github/workflows/dev-testing.yml @@ -86,7 +86,7 @@ jobs: id: cache-behavior-datasets with: path: ./behavior_testing_data - key: behavior-datasets-2022-08-18-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }} + key: behavior-datasets-2023-07-26-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }} From fbfedbd73823476b5e96cd68d2ebdbd031c7411e Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 18:45:10 +0200 Subject: [PATCH 07/13] spelling --- .../datainterfaces/behavior/fictrac/fictracdatainterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 7648e5be5..03edb62e6 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -284,7 +284,7 @@ def add_to_nwbfile( "Instantaneous running direction (radians) of the animal in laboratory coordinates" "This is the direction the animal is moving in the lab frame. " "add to animal heading to get direction in the world." - "This values is infered by the rotation of the ball (roll and pitch)" + "This values is inferred by the rotation of the ball (roll and pitch)" ) spatial_seriess_kwargs = dict( @@ -331,7 +331,7 @@ def add_to_nwbfile( ] description = ( - "x and y positions in the lab frame in radians. These values are infered by integrating " + "x and y positions in the lab frame in radians. These values are inferred by integrating " "the rotation of the across time. " ) From 7d1b10851a5348f2250e9a446ebfe5d512de7f4b Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 18:58:42 +0200 Subject: [PATCH 08/13] update import test --- tests/imports.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/imports.py b/tests/imports.py index cabed6df1..781a4bb97 100644 --- a/tests/imports.py +++ b/tests/imports.py @@ -63,6 +63,7 @@ def test_tools(self): "processes", "deploy_process", "LocalPathExpander", + "get_module", ] self.assertCountEqual(first=current_structure, second=expected_structure) From a4c7e53a26f9e5f76c9903b028e1f627366c39bc Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 19:14:52 +0200 Subject: [PATCH 09/13] adjust names to best practices --- .github/workflows/testing.yml | 2 +- .../behavior/fictrac/fictracdatainterface.py | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 1378d796e..a08183723 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -106,7 +106,7 @@ jobs: id: cache-behavior-datasets with: path: ./behavior_testing_data - key: behavior-datasets-2022-08-18-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }} + key: behavior-datasets-2023-07-26-${{ matrix.os }}-${{ steps.behavior.outputs.HASH_behavior_DATASET }} diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 03edb62e6..3cb689125 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -136,7 +136,7 @@ def add_to_nwbfile( data = df_cam_delta_rotation.to_numpy() reference_frame = "camera" spatial_seriess_kwargs = dict( - name="cam_delta_rotation", + name="SpatialSeriesRotationDeltaCameraFrame", data=data, reference_frame=reference_frame, unit="radians", @@ -170,7 +170,7 @@ def add_to_nwbfile( data = df_lab_delta_rotation.to_numpy() reference_frame = "lab" spatial_seriess_kwargs = dict( - name="lab_delta_rotation", + name="SpatialSeriesRotationDeltaLabFrame", data=data, reference_frame=reference_frame, unit="radians", @@ -204,7 +204,7 @@ def add_to_nwbfile( data = df_cam_rotation.to_numpy() reference_frame = "camera" spatial_seriess_kwargs = dict( - name="cam_rotation", + name="SpatialSeriesRotationCameraFrame", data=data, reference_frame=reference_frame, unit="radians", @@ -238,7 +238,7 @@ def add_to_nwbfile( data = df_lab_rotation.to_numpy() reference_frame = "lab" spatial_seriess_kwargs = dict( - name="lab_rotation", + name="SpatialSeriesRotationLabFrame", data=data, reference_frame=reference_frame, unit="radians", @@ -262,7 +262,7 @@ def add_to_nwbfile( ) spatial_seriess_kwargs = dict( - name="animal_heading", + name="SpatialSeriesAnimalHeading", data=animal_heading, reference_frame=reference_frame, unit="radians", @@ -288,7 +288,7 @@ def add_to_nwbfile( ) spatial_seriess_kwargs = dict( - name="movement_direction", + name="SpatialSeriesMovementDirection", reference_frame=reference_frame, data=movement_direction, unit="radians", @@ -309,7 +309,7 @@ def add_to_nwbfile( description = "Instantaneous running speed (radians/frame) of the animal." spatial_seriess_kwargs = dict( - name="movement_speed", + name="SpatialSeriesMovementSpeed", data=movement_speed, reference_frame=reference_frame, unit="radians", @@ -339,7 +339,7 @@ def add_to_nwbfile( data = df_lab_position.to_numpy() reference_frame = "lab" spatial_seriess_kwargs = dict( - name="lab_position", + name="SpatialSeriesPosition", data=data, reference_frame=reference_frame, unit="radians", @@ -370,7 +370,7 @@ def add_to_nwbfile( reference_frame = "lab" spatial_seriess_kwargs = dict( - name="integrated_motion", + name="SpatialSeriesIntegratedMotion", data=data, unit="radians", reference_frame=reference_frame, @@ -391,7 +391,7 @@ def add_to_nwbfile( description = "Error in rotation delta in radians from the lab frame" spatial_seriess_kwargs = dict( - name="rotation_delta_error", + name="SpatialSeriesRotationDeltaError", data=rotation_delta_error, reference_frame=reference_frame, unit="radians", From 771262f8a2cd44d393613efc57a2566a84d28a91 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 19:15:49 +0200 Subject: [PATCH 10/13] correct changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 083a38832..d9d28c00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # (Upcoming) ## Features -Added FicTrac data interface [PR #499](https://github.com/catalystneuro/neuroconv/pull/#499) +Added FicTrac data interface [PR #517](https://github.com/catalystneuro/neuroconv/pull/#517) # v0.4.0 From 5ddae552523514ab671e338f49771fcf6d2c87f9 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Wed, 26 Jul 2023 20:33:07 +0200 Subject: [PATCH 11/13] change CompassDirection name --- .../datainterfaces/behavior/fictrac/fictracdatainterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 3cb689125..8ea6a68c4 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -115,7 +115,7 @@ def add_to_nwbfile( # All the units in FicTrac are in radians, the radius of the ball required to transform to # Distances is not specified in the format - compass_direction_container = CompassDirection(name="FictracData") + compass_direction_container = CompassDirection(name="FicTrac") # Add rotation delta from camera rotation_delta_cam_columns = [ From c0e8f5d922bffeb789048452324018def4858c8b Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Thu, 7 Sep 2023 12:43:39 +0200 Subject: [PATCH 12/13] update timestamps to seconds --- .../datainterfaces/behavior/fictrac/fictracdatainterface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 8ea6a68c4..94596ac25 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -77,7 +77,7 @@ def get_metadata(self): config_file = self.file_path.parent / "fictrac_config.txt" if config_file.exists(): self._config_file = parse_fictrac_config(config_file) - date = self._config_file["build_date"] + date_string = self._config_file["build_date"] date_object = datetime.strptime(date_string, "%b %d %Y") metadata["NWBFile"].update( @@ -105,7 +105,8 @@ def add_to_nwbfile( fictrac_data_df = pd.read_csv(self.file_path, sep=",", header=None, names=self.data_columns) # Get the timestamps - timestamps = fictrac_data_df["timestamp"].values + timestamps_milliseconds = fictrac_data_df["timestamp"].values + timestamps = timestamps_milliseconds / 1000.0 rate = calculate_regular_series_rate(series=timestamps) # Returns None if it is not regular write_timestamps = True if rate: From 5d19e537f553ed13c209364e2cbeb9988b35807d Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Fri, 8 Sep 2023 09:11:17 +0200 Subject: [PATCH 13/13] update docstring --- .../datainterfaces/behavior/fictrac/fictracdatainterface.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py index 94596ac25..fe8adb992 100644 --- a/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py +++ b/src/neuroconv/datainterfaces/behavior/fictrac/fictracdatainterface.py @@ -361,10 +361,7 @@ def add_to_nwbfile( "side_motion_lab", ] - description = ( - "Integrated x/y position (radians) of the sphere in laboratory coordinates neglecting" - " heading. Equivalent to the output from two optical mice" - ) + description = "Integrated x/y position (radians) of the sphere in laboratory coordinates neglecting heading." df_integrated_motion = fictrac_data_df[integrated_motin_columns] data = df_integrated_motion.to_numpy()