From 65733f200ea6428a010766d27a23a873bc7987ff Mon Sep 17 00:00:00 2001 From: Pavel Kulik Date: Mon, 21 Oct 2024 17:44:43 -0700 Subject: [PATCH] Add saving/loading ContinuousChannel type --- Source/FileSource/NWBFileSource.cpp | 7 ++++- Source/RecordEngine/NWBFormat.cpp | 41 ++++++++++++++++++++++++++--- Source/RecordEngine/NWBFormat.h | 16 +++++++++-- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/Source/FileSource/NWBFileSource.cpp b/Source/FileSource/NWBFileSource.cpp index dbca74f..0f12f0e 100644 --- a/Source/FileSource/NWBFileSource.cpp +++ b/Source/FileSource/NWBFileSource.cpp @@ -154,13 +154,18 @@ void NWBFileSource::fillRecordInfo() data = dataSource.openDataSet ("channel_conversion"); data.read (ccArray.getData(), PredType::NATIVE_FLOAT); + HeapBlock ctArray (dims[1]); + data = dataSource.openDataSet ("channel_type"); + data.read (ctArray.getData(), PredType::NATIVE_UINT8); + try { for (int k = 0; k < dims[1]; k++) { RecordedChannelInfo c; c.name = "CH" + String (k); - c.bitVolts = ccArray[k] * 1e6; + c.bitVolts = ccArray[k] * 1e6; //TOFIX? Scaling should depend on channel type? + c.type = ctArray[k]; info.channels.add (c); } infoArray.add (info); diff --git a/Source/RecordEngine/NWBFormat.cpp b/Source/RecordEngine/NWBFormat.cpp index 0bad794..40a388a 100644 --- a/Source/RecordEngine/NWBFormat.cpp +++ b/Source/RecordEngine/NWBFormat.cpp @@ -118,15 +118,16 @@ TimeSeries::TimeSeries (String rootPath, String name, String description_) { } -ecephys::ElectricalSeries::ElectricalSeries (String rootPath, String name, String description_, int channel_count_, Array channel_conversion_) +ecephys::ElectricalSeries::ElectricalSeries (String rootPath, String name, String description_, int channel_count_, Array channel_conversion_, Array channel_type_) : TimeSeries (rootPath, name, description_), channel_conversion (channel_conversion_), + channel_type (channel_type_), channel_count (channel_count_) { } -ecephys::SpikeEventSeries::SpikeEventSeries (String rootPath, String name, String description_, int channel_count, Array channel_conversion_) - : ecephys::ElectricalSeries (rootPath, name, description_, channel_count, channel_conversion_) +ecephys::SpikeEventSeries::SpikeEventSeries (String rootPath, String name, String description_, int channel_count, Array channel_conversion_, Array channel_type_) + : ecephys::ElectricalSeries (rootPath, name, description_, channel_count, channel_conversion_, channel_type_) { } @@ -187,6 +188,12 @@ bool NWBFile::startNewRecording ( + String (group[0]->getSourceNodeId()) + "." + group[0]->getStreamName(); + Array channel_type; + for (int ch = 0; ch < group.size(); ch++) + { + channel_type.add (group[ch]->getChannelType()); + } + String fullPath = "general/extracellular_ephys/" + groupName; createGroup (fullPath); setAttributeStr ("description", fullPath, "description"); @@ -217,7 +224,8 @@ bool NWBFile::startNewRecording ( groupName, "Stores continuously sampled voltage data from an extracellular ephys recording", group.size(), - channel_conversion); + channel_conversion, + channel_type); if (recordingNumber == 0) if (! createTimeSeriesBase (electricalSeries)) @@ -255,6 +263,12 @@ bool NWBFile::startNewRecording ( return false; writeChannelConversions (electricalSeries); + electricalSeries->channelTypesDataSet = createChannelTypesDataSet (electricalSeries->basePath + "/channel_type", "Channel types for all channels", CHUNK_XSIZE); + + if (electricalSeries->channelTypesDataSet == nullptr) + return false; + writeChannelTypes (electricalSeries); + electricalSeries->electrodeDataSet = createElectrodeDataSet (electricalSeries->basePath + "/electrodes", "Electrode index for each channel", CHUNK_XSIZE); if (electricalSeries->electrodeDataSet == nullptr) @@ -573,6 +587,15 @@ void NWBFile::writeChannelConversions (ecephys::ElectricalSeries* electricalSeri CHECK_ERROR (electricalSeries->channelConversionDataSet->writeDataBlock (conversions.size(), BaseDataType::F32, &conversions[0])); } +void NWBFile::writeChannelTypes (ecephys::ElectricalSeries* electricalSeries) +{ + std::vector channel_type; + for (auto i : electricalSeries->channel_type) + channel_type.push_back (i); + + CHECK_ERROR (electricalSeries->channelTypesDataSet->writeDataBlock (channel_type.size(), BaseDataType::U8, &channel_type[0])); +} + void NWBFile::writeElectrodes (ecephys::ElectricalSeries* electricalSeries, Array electrodeInds) { std::vector electrodeNumbers; @@ -759,6 +782,16 @@ HDF5RecordingData* NWBFile::createChannelConversionDataSet (String path, String return elSet; } +HDF5RecordingData* NWBFile::createChannelTypesDataSet (String path, String description, int chunk_size) +{ + HDF5RecordingData* elSet = createDataSet (BaseDataType::U8, 1, chunk_size, path); + if (! elSet) + std::cerr << "Error creating electrode dataset in " << path << std::endl; + else + CHECK_ERROR (setAttributeStr (description, path, "description")); + return elSet; +} + HDF5RecordingData* NWBFile::createElectrodeDataSet (String path, String description, int chunk_size) { HDF5RecordingData* elSet = createDataSet (BaseDataType::I32, 1, chunk_size, path); diff --git a/Source/RecordEngine/NWBFormat.h b/Source/RecordEngine/NWBFormat.h index 4d7f53c..b46ddd3 100644 --- a/Source/RecordEngine/NWBFormat.h +++ b/Source/RecordEngine/NWBFormat.h @@ -78,17 +78,23 @@ namespace ecephys { public: /** Constructor */ - ElectricalSeries (String rootPath, String name, String description, int channel_count, Array channel_conversion); + ElectricalSeries (String rootPath, String name, String description, int channel_count, Array channel_conversion, Array channel_type); /** Holds the sample number for each sample (relative to the start of acquisition) */ ScopedPointer channelConversionDataSet; + /** Holds the channel types */ + ScopedPointer channelTypesDataSet; + /** Holds the DynamicTableRegion index of each electrode */ ScopedPointer electrodeDataSet; /** Channel conversion values */ Array channel_conversion; + /** Channel types */ + Array channel_type; + /** Number of channels to write */ int channel_count; @@ -103,7 +109,7 @@ namespace ecephys { public: /** Constructor */ - SpikeEventSeries (String rootPath, String name, String description, int channel_count, Array channel_conversion); + SpikeEventSeries (String rootPath, String name, String description, int channel_count, Array channel_conversion, Array channel_type = {}); /** Get neurodata_type */ virtual String getNeurodataType() override { return "SpikeEventSeries"; } @@ -178,6 +184,9 @@ class NWBFile : public HDF5FileBase /** Writes channel conversion values */ void writeChannelConversions (ecephys::ElectricalSeries* series); + /** Writes channel types */ + void writeChannelTypes (ecephys::ElectricalSeries* series); + /** Writes a spike event*/ void writeSpike (int electrodeId, const SpikeChannel* channel, const Spike* event); @@ -239,6 +248,9 @@ class NWBFile : public HDF5FileBase /** Creates a dataset for electrode indices */ HDF5RecordingData* createChannelConversionDataSet (String basePath, String description, int chunk_size); + /** Creates a dataset for channel types */ + HDF5RecordingData* createChannelTypesDataSet (String basePath, String description, int chunk_size); + /** Adds attributes (e.g. conversion, resolution) to a continuous dataset */ void createDataAttributes (String basePath, float conversion, float resolution, String unit);