"""Collection of Axona interfaces."""
import warnings
from pydantic import FilePath
from pynwb import NWBFile
from .axona_utils import (
get_eeg_sampling_frequency,
get_position_object,
read_all_eeg_file_lfp_data,
)
from ..baselfpextractorinterface import BaseLFPExtractorInterface
from ..baserecordingextractorinterface import BaseRecordingExtractorInterface
from ....basedatainterface import BaseDataInterface
from ....tools.nwb_helpers import get_module
from ....utils import DeepDict, get_json_schema_from_method_signature
[docs]
class AxonaRecordingInterface(BaseRecordingExtractorInterface):
"""
DataInterface for converting raw Axona data.
Uses the :py:func:`~spikeinterface.extractors.read_axona` reader from SpikeInterface.
"""
display_name = "Axona Recording"
associated_suffixes = (".bin", ".set")
info = "Interface for Axona recording data."
[docs]
@classmethod
def get_source_schema(cls) -> dict:
source_schema = super().get_source_schema()
source_schema["properties"]["file_path"]["description"] = "Path to .bin file."
return source_schema
def __init__(
self, file_path: FilePath, *args, verbose: bool = False, es_key: str = "ElectricalSeries"
): # TODO: change to * (keyword only) on or after August 2026
"""
Parameters
----------
file_path: FilePath
Path to .bin file.
verbose: bool, optional, default: True
es_key: str, default: "ElectricalSeries"
"""
# Handle deprecated positional arguments
if args:
parameter_names = [
"verbose",
"es_key",
]
num_positional_args_before_args = 1 # file_path
if len(args) > len(parameter_names):
raise TypeError(
f"__init__() takes at most {len(parameter_names) + num_positional_args_before_args + 1} positional arguments but "
f"{len(args) + num_positional_args_before_args + 1} were given. "
"Note: Positional arguments are deprecated and will be removed on or after August 2026. "
"Please use keyword arguments."
)
positional_values = dict(zip(parameter_names, args))
passed_as_positional = list(positional_values.keys())
warnings.warn(
f"Passing arguments positionally to AxonaRecordingInterface.__init__() is deprecated "
f"and will be removed on or after August 2026. "
f"The following arguments were passed positionally: {passed_as_positional}. "
"Please use keyword arguments instead.",
FutureWarning,
stacklevel=2,
)
verbose = positional_values.get("verbose", verbose)
es_key = positional_values.get("es_key", es_key)
super().__init__(file_path=file_path, verbose=verbose, es_key=es_key)
self.metadata_in_set_file = self.recording_extractor.neo_reader.file_parameters["set"]["file_header"]
# Set the channel groups
tetrode_id = self.recording_extractor.get_property("tetrode_id")
self.recording_extractor.set_channel_groups(tetrode_id)
[docs]
class AxonaUnitRecordingInterface(AxonaRecordingInterface):
"""Primary data interface class for converting a AxonaRecordingExtractor."""
display_name = "Axona Units"
associated_suffixes = (".bin", ".set")
info = "Interface for Axona recording data."
[docs]
@classmethod
def get_source_schema(cls) -> dict:
return dict(
required=["file_path"],
properties=dict(
file_path=dict(
type="string",
format="file",
description="Path to Axona file.",
),
noise_std=dict(type="number"),
),
type="object",
)
def __init__(
self, file_path: FilePath, *args, noise_std: float = 3.5
): # TODO: change to * (keyword only) on or after August 2026
# Handle deprecated positional arguments
if args:
parameter_names = [
"noise_std",
]
num_positional_args_before_args = 1 # file_path
if len(args) > len(parameter_names):
raise TypeError(
f"__init__() takes at most {len(parameter_names) + num_positional_args_before_args + 1} positional arguments but "
f"{len(args) + num_positional_args_before_args + 1} were given. "
"Note: Positional arguments are deprecated and will be removed on or after August 2026. "
"Please use keyword arguments."
)
positional_values = dict(zip(parameter_names, args))
passed_as_positional = list(positional_values.keys())
warnings.warn(
f"Passing arguments positionally to AxonaUnitRecordingInterface.__init__() is deprecated "
f"and will be removed on or after August 2026. "
f"The following arguments were passed positionally: {passed_as_positional}. "
"Please use keyword arguments instead.",
FutureWarning,
stacklevel=2,
)
noise_std = positional_values.get("noise_std", noise_std)
super().__init__(filename=file_path, noise_std=noise_std)
self.source_data = dict(file_path=file_path, noise_std=noise_std)
[docs]
class AxonaLFPDataInterface(BaseLFPExtractorInterface):
"""
Primary data interface class for converting Axona LFP data.
Note that this interface is not lazy and will load all data into memory.
"""
display_name = "Axona LFP"
associated_suffixes = (".bin", ".set")
info = "Interface for Axona LFP data."
def _initialize_extractor(self, interface_kwargs: dict):
"""Override to use NumpyRecording with custom parameters."""
self.extractor_kwargs = interface_kwargs.copy()
self.extractor_kwargs.pop("file_path")
self.extractor_kwargs.pop("verbose", None)
self.extractor_kwargs.pop("es_key", None)
self.extractor_kwargs["traces_list"] = self.traces_list
self.extractor_kwargs["sampling_frequency"] = self.sampling_frequency
extractor_class = self.get_extractor_class()
extractor_instance = extractor_class(**self.extractor_kwargs)
return extractor_instance
[docs]
@classmethod
def get_source_schema(cls) -> dict:
return dict(
required=["file_path"],
properties=dict(file_path=dict(type="string")),
type="object",
additionalProperties=False,
)
def __init__(self, file_path: FilePath):
data = read_all_eeg_file_lfp_data(file_path).T
self.traces_list = [data]
self.sampling_frequency = get_eeg_sampling_frequency(file_path)
super().__init__(file_path=file_path)
self.source_data = dict(file_path=file_path)
[docs]
class AxonaPositionDataInterface(BaseDataInterface):
"""Primary data interface class for converting Axona position data."""
display_name = "Axona Position"
keywords = ("position tracking",)
associated_suffixes = (".bin", ".set")
info = "Interface for Axona position data."
[docs]
@classmethod
def get_source_schema(cls) -> dict:
return get_json_schema_from_method_signature(cls.__init__)
def __init__(self, file_path: str):
"""
Parameters
----------
file_path: str
Path to .bin or .set file.
"""
super().__init__(filename=file_path)
self.source_data = dict(file_path=file_path)
[docs]
def add_to_nwbfile(self, nwbfile: NWBFile, metadata: dict):
"""
Run conversion for this data interface.
Parameters
----------
nwbfile : NWBFile
metadata : dict
"""
file_path = self.interface_kwargs["file_path"]
# Create or update processing module for behavioral data
behavior_module = get_module(nwbfile=nwbfile, name="behavior", description="behavioral data")
behavior_module.add(get_position_object(file_path))