Skip to content

Commit

Permalink
Merge pull request #15 from AllenInstitute/minor-changes
Browse files Browse the repository at this point in the history
Minor changes
  • Loading branch information
t-b authored Mar 13, 2019
2 parents 5e112fc + fe989d5 commit 550cea2
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 54 deletions.
2 changes: 1 addition & 1 deletion IPNWB_Constants.ipf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IndependentModule=IPNWB
#pragma version=0.18
Expand Down
2 changes: 1 addition & 1 deletion IPNWB_Debugging.ipf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IndependentModule=IPNWB
#pragma version=0.18
Expand Down
2 changes: 1 addition & 1 deletion IPNWB_HDF5Helpers.ipf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IndependentModule=IPNWB
#pragma version=0.18
Expand Down
2 changes: 1 addition & 1 deletion IPNWB_Include.ipf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IgorVersion=7.0

Expand Down
56 changes: 53 additions & 3 deletions IPNWB_Reader.ipf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IndependentModule=IPNWB
#pragma version=0.18
Expand Down Expand Up @@ -264,8 +264,6 @@ Function LoadSourceAttribute(locationID, channel, p)
default:
endswitch
endfor

KillWaves/Z wv
End

/// @brief Load data wave from specified path
Expand Down Expand Up @@ -424,3 +422,55 @@ Function ReadSubjectInfo(fileID, subjectInfo)

HDF5CloseGroup/Z groupID
End

/// @brief Read the TimeSeries properties from the given group in locationID
///
/// @param[in] locationID TimeSeries group ID
/// @param[in] channel TimeSeries group name
/// @param[out] tsp TimeSeriesProperties structure
Function ReadTimeSeriesProperties(locationID, channel, tsp)
variable locationID
string channel
STRUCT TimeSeriesProperties &tsp

variable clampMode, i, numEntries, value, channelType, groupID, idx
string ancestry, entry, list

ancestry = ReadTextAttributeAsList(locationID, channel, "ancestry")
clampMode = GetClampModeFromAncestry(ancestry)
channelType = GetChannelTypeFromAncestry(ancestry)

InitTimeSeriesProperties(tsp, channelType, clampMode)

groupID = IPNWB#H5_OpenGroup(locationID, channel)

list = ""
numEntries = ItemsInList(tsp.missing_fields)
for(i = 0; i < numEntries; i += 1)
entry = StringFromList(i, tsp.missing_fields)

value = ReadDataSetAsNumber(groupID, entry)
if(IsNaN(value))
continue
endif

tsp.names[idx] = entry
tsp.data[idx] = value
tsp.isCustom[idx] = 0

idx += 1

list = AddListItem(entry, list, ";", inf)
endfor

HDF5CloseGroup/Z groupID

Redimension/N=(idx) tsp.names, tsp.data, tsp.isCustom

tsp.missing_fields = RemoveFromList(list, tsp.missing_fields)

if(strlen(tsp.missing_fields) > 0)
// unify list formatting to end with ;
tsp.missing_fields = RemoveEnding(tsp.missing_fields, ";") + ";"
endif
End
53 changes: 30 additions & 23 deletions IPNWB_Structures.ipf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IndependentModule=IPNWB
#pragma version=0.18
Expand Down Expand Up @@ -45,6 +45,20 @@ Structure ReadChannelParams
variable ttlBit ///< unambigous ttl-channel-number
EndStructure

/// @brief Initialization routine for InitReadChannelParams
Function InitReadChannelParams(p)
STRUCT ReadChannelParams &p

p.device = ""
p.channelSuffix = ""
p.sweep = NaN
p.channelType = NaN
p.channelNumber = NaN
p.electrodeNumber = NaN
p.groupIndex = NaN
p.ttlBit = NaN
End

/// @brief Structure to hold all properties of the NWB file directly below `/general`
Structure GeneralInfo
string session_id
Expand Down Expand Up @@ -132,14 +146,22 @@ End

/// @brief Holds class specific entries for TimeSeries objects
///
/// Usage:
/// Usage for writers
/// @code
/// STRUCT TimeSeriesProperties tsp
/// InitTimeSeriesProperties(tsp, channelType, clampMode)
/// AddProperty(tsp, "gain", 1.23456)
/// // more calls tp AddProperty()
/// WriteSingleChannel(locationID, path, p, tsp)
/// @endcode
///
/// and for readers
/// @code
/// STRUCT TimeSeriesProperties tsp
/// InitTimeSeriesProperties(tsp, channelType, clampMode)
/// AddProperty(tsp, "gain", 1.23456)
/// // more calls tp AddProperty()
/// WriteSingleChannel(locationID, path, p, tsp)
/// STRUCT TimeSeriesProperties tsp
/// InitTimeSeriesProperties(tsp, channelType, clampMode)
/// ReadTimeSeriesProperties(groupID, channel, tsp)
/// @endcode
///
Structure TimeSeriesProperties
WAVE/T names
WAVE data
Expand Down Expand Up @@ -167,20 +189,5 @@ Function InitTimeSeriesProperties(tsp, channelType, clampMode)
WAVE tsp.isCustom = isCustom

// AddProperty() will remove the entries on addition of values
if(channelType == CHANNEL_TYPE_ADC)
if(clampMode == V_CLAMP_MODE)
// VoltageClampSeries
tsp.missing_fields = "gain;capacitance_fast;capacitance_slow;resistance_comp_bandwidth;resistance_comp_correction;resistance_comp_prediction;whole_cell_capacitance_comp;whole_cell_series_resistance_comp"
elseif(clampMode == I_CLAMP_MODE || clampMode == I_EQUAL_ZERO_MODE)
// CurrentClampSeries
tsp.missing_fields = "gain;bias_current;bridge_balance;capacitance_compensation"
else
// unassociated channel
tsp.missing_fields = ""
endif
elseif(channelType == CHANNEL_TYPE_DAC)
tsp.missing_fields = "gain"
else
tsp.missing_fields = ""
endif
tsp.missing_fields = GetTimeSeriesMissingFields(channelType, clampMode)
End
192 changes: 190 additions & 2 deletions IPNWB_Utils.ipf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma rtFunctionErrors=1
#pragma IndependentModule=IPNWB
#pragma version=0.18
Expand All @@ -19,6 +19,16 @@ Function IsFinite(var)
return numType(var) == 0
End

/// @brief Returns 1 if var is a NaN, 0 otherwise
///
/// @hidecallgraph
/// @hidecallergraph
threadsafe Function IsNaN(var)
variable var

return numType(var) == 2
End

/// @brief Returns 1 if str is null, 0 otherwise
/// @param str must not be a SVAR
///
Expand Down Expand Up @@ -193,6 +203,93 @@ threadsafe Function IsTextWave(wv)
return WaveType(wv, 1) == 2
End

/// @brief Return 1 if the wave is a numeric wave, zero otherwise
threadsafe Function IsNumericWave(wv)
WAVE wv

return WaveType(wv, 1) == 1
End

/// @brief Read a text attribute as semicolon `;` separated list
///
/// @param[in] locationID HDF5 identifier, can be a file or group
/// @param[in] path Additional path on top of `locationID` which identifies
/// the group or dataset
/// @param[in] name Name of the attribute to load
Function/S ReadTextAttributeAsList(locationID, path, name)
variable locationID
string path, name

return TextWaveToList(ReadTextAttribute(locationID, path, name), ";")
End

/// @brief Read a text attribute as text wave, return a single element
/// wave with #PLACEHOLDER if it does not exist.
///
/// @param[in] locationID HDF5 identifier, can be a file or group
/// @param[in] path Additional path on top of `locationID` which identifies
/// the group or dataset
/// @param[in] name Name of the attribute to load
Function/WAVE ReadTextAttribute(locationID, path, name)
variable locationID
string path, name

WAVE/T/Z wv = H5_LoadAttribute(locationID, path, name)

if(!WaveExists(wv))
Make/FREE/T/N=1 wv = PLACEHOLDER
return wv
endif

ASSERT(IsTextWave(wv), "Expected a text wave")

return wv
End

/// @brief Read a text attribute as string, return #PLACEHOLDER if it does not exist
///
/// @param[in] locationID HDF5 identifier, can be a file or group
/// @param[in] path Additional path on top of `locationID` which identifies
/// the group or dataset
/// @param[in] name Name of the attribute to load
Function/S ReadTextAttributeAsString(locationID, path, name)
variable locationID
string path, name

WAVE/T/Z wv = H5_LoadAttribute(locationID, path, name)

if(!WaveExists(wv))
return PLACEHOLDER
endif

ASSERT(DimSize(wv, ROWS) == 1, "Expected exactly one row")
ASSERT(IsTextWave(wv), "Expected a text wave")

return wv[0]
End

/// @brief Read a text attribute as number, return `NaN` if it does not exist
///
/// @param[in] locationID HDF5 identifier, can be a file or group
/// @param[in] path Additional path on top of `locationID` which identifies
/// the group or dataset
/// @param[in] name Name of the attribute to load
Function ReadAttributeAsNumber(locationID, path, name)
variable locationID
string path, name

WAVE/Z wv = H5_LoadAttribute(locationID, path, name)

if(!WaveExists(wv))
return NaN
endif

ASSERT(DimSize(wv, ROWS) == 1, "Expected exactly one row")
ASSERT(IsNumericWave(wv), "Expected a text wave")

return wv[0]
End

/// @brief Read a text dataset as text wave, return a single element
/// wave with #PLACEHOLDER if it does not exist.
///
Expand Down Expand Up @@ -249,7 +346,7 @@ Function ReadDataSetAsNumber(locationID, name)
endif

ASSERT(DimSize(wv, ROWS) == 1, "Expected exactly one row")
ASSERT(WaveType(wv, 1) == 1, "Expected a numeric wave")
ASSERT(IsNumericWave(wv), "Expected a numeric wave")

return wv[0]
End
Expand Down Expand Up @@ -458,3 +555,94 @@ Function ParseISO8601TimeStamp(timestamp)

return secondsSinceEpoch
End

/// @brief Convert a text wave to string list
Function/S TextWaveToList(txtWave, sep)
WAVE/T txtWave
string sep

string list = ""
variable i, numRows

ASSERT(IsTextWave(txtWave), "Expected a text wave")
ASSERT(DimSize(txtWave, COLS) == 0, "Expected a 1D wave")

numRows = DimSize(txtWave, ROWS)
for(i = 0; i < numRows; i += 1)
list = AddListItem(txtWave[i], list, sep, Inf)
endfor

return list
End

/// @brief Return the initial values for the missing_fields attribute depending
/// on the channel type, one of @ref IPNWB_ChannelTypes, and the clamp mode.
Function/S GetTimeSeriesMissingFields(channelType, clampMode)
variable channelType, clampMode

if(channelType == CHANNEL_TYPE_ADC)
if(clampMode == V_CLAMP_MODE)
// VoltageClampSeries
return "gain;capacitance_fast;capacitance_slow;resistance_comp_bandwidth;resistance_comp_correction;resistance_comp_prediction;whole_cell_capacitance_comp;whole_cell_series_resistance_comp"
elseif(clampMode == I_CLAMP_MODE || clampMode == I_EQUAL_ZERO_MODE)
// CurrentClampSeries
return "gain;bias_current;bridge_balance;capacitance_compensation"
endif
elseif(channelType == CHANNEL_TYPE_DAC)
if(clampMode == V_CLAMP_MODE || clampMode == I_CLAMP_MODE || clampMode == I_EQUAL_ZERO_MODE)
return "gain"
endif
endif

return ""
End

/// @brief Derive the clamp mode from the `ancestry` attribute and return it
///
/// @param ancestry Contents of ancestry attribute
Function GetClampModeFromAncestry(ancestry)
string ancestry

ancestry = RemoveEnding(ancestry, ";")

strswitch(ancestry)
case "TimeSeries;PatchClampSeries;VoltageClampSeries":
case "TimeSeries;PatchClampSeries;VoltageClampStimulusSeries":
return V_CLAMP_MODE
case "TimeSeries;PatchClampSeries;CurrentClampSeries":
case "TimeSeries;PatchClampSeries;CurrentClampStimulusSeries":
return I_CLAMP_MODE
case "TimeSeries;PatchClampSeries;CurrentClampSeries;IZeroClampSeries":
return I_EQUAL_ZERO_MODE
case "TimeSeries": // unassociated channel data
return NaN
default:
ASSERT(0, "Unknown ancestry: " + ancestry)
break
endswitch
End

/// @brief Derive the channel type, one of @ref IPNWB_ChannelTypes, from the
/// `ancestry` attribute and return it
///
/// @param ancestry Contents of ancestry attribute
Function GetChannelTypeFromAncestry(ancestry)
string ancestry

ancestry = RemoveEnding(ancestry, ";")

strswitch(ancestry)
case "TimeSeries;PatchClampSeries;VoltageClampSeries":
case "TimeSeries;PatchClampSeries;CurrentClampSeries":
case "TimeSeries;PatchClampSeries;CurrentClampSeries;IZeroClampSeries":
return CHANNEL_TYPE_ADC
case "TimeSeries;PatchClampSeries;VoltageClampStimulusSeries":
case "TimeSeries;PatchClampSeries;CurrentClampStimulusSeries":
return CHANNEL_TYPE_DAC
case "TimeSeries": // unassociated channel
return CHANNEL_TYPE_OTHER
default:
ASSERT(0, "Unknown ancestry: " + ancestry)
break
endswitch
End
Loading

0 comments on commit 550cea2

Please sign in to comment.