diff --git a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf index 98246c2380..0d11a3a3a9 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf @@ -446,7 +446,7 @@ static Function PSQ_EvaluateBaselineProperties(string device, STRUCT AnalysisFun variable leakCurPassedAll, maxLeakCurrent, targetVThreshold variable rmsShortThreshold, rmsLongThreshold variable chunkPassedRMSShortOverride, chunkPassedRMSLongOverride, chunkPassedTargetVOverride, chunkPassedLeakCurOverride - string msg, adUnit, ctrl, key, epName, epShortName + string msg, adUnit, ctrl, key STRUCT PSQ_PulseSettings ps PSQ_GetPulseSettingsForType(type, ps) @@ -596,6 +596,7 @@ static Function PSQ_EvaluateBaselineProperties(string device, STRUCT AnalysisFun endif // END TEST + WAVE/T epochWave = GetEpochsWave(device) for(i = 0; i < NUM_HEADSTAGES; i += 1) if(!statusHS[i]) @@ -604,9 +605,7 @@ static Function PSQ_EvaluateBaselineProperties(string device, STRUCT AnalysisFun DAC = AFH_GetDACFromHeadstage(device, i) ASSERT(IsFinite(DAC), "Could not determine DAC channel number for HS " + num2istr(i) + " for device " + device) - epName = "Name=Baseline Chunk;Index=" + num2istr(chunk) - epShortName = PSQ_BASELINE_CHUNK_SHORT_NAME_PREFIX + num2istr(chunk) - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, chunkStartTimeMax * MILLI_TO_ONE, (chunkStartTimeMax + chunkLengthTime) * MILLI_TO_ONE, epName, shortname = epShortName) + PSQ_AddBaselineEpoch(epochWave, DAC, chunk, chunkStartTimeMax, chunkLengthTime) if(chunk == 0) // store baseline RMS short/long tartget V analysis parameters in labnotebook on first use @@ -847,6 +846,22 @@ static Function PSQ_EvaluateBaselineProperties(string device, STRUCT AnalysisFun endif End +/// @brief Add base line epoch to user epochs +/// +/// @param epochWave 4D epochs wave +/// @param DAC DAC channel number +/// @param chunk number of evaluated chunk +/// @param chunkStartTimeMax start time of chunk in ms +/// @param chunkLengthTime time length of chunk in ms +Function PSQ_AddBaselineEpoch(WAVE epochWave, variable DAC, variable chunk, variable chunkStartTimeMax, variable chunkLengthTime) + + string epName, epShortName + + epName = "Name=Baseline Chunk;Index=" + num2istr(chunk) + epShortName = PSQ_BASELINE_CHUNK_SHORT_NAME_PREFIX + num2istr(chunk) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, chunkStartTimeMax * MILLI_TO_ONE, (chunkStartTimeMax + chunkLengthTime) * MILLI_TO_ONE, epName, shortname = epShortName) +End + /// @brief Return the number of chunks /// /// A chunk is #PSQ_BL_EVAL_RANGE [ms] of baseline. @@ -3598,7 +3613,8 @@ static Function PSQ_Ramp_AddEpoch(string device, variable headstage, WAVE wv, st epBegin = IndexToScale(wv, first, ROWS) * MILLI_TO_ONE epEnd = IndexToScale(wv, last, ROWS) * MILLI_TO_ONE - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + WAVE/T epochWave = GetEpochsWave(device) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) End /// @brief Determine if we have three passing sweeps with the same DAScale value @@ -4670,8 +4686,7 @@ End /// /// Zero is the DA/AD wave zero. static Function [variable epBegin, variable epEnd] PSQ_CR_GetSpikeEvaluationRange(string device, variable sweepNo, variable headstage) - variable DAC, totalOnsetDelay, chirpStart, chirpEnd - string tags, shortname + variable DAC, totalOnsetDelay WAVE numericalValues = GetLBNumericalValues(device) WAVE textualValues = GetLBTextualValues(device) @@ -4694,7 +4709,28 @@ static Function [variable epBegin, variable epEnd] PSQ_CR_GetSpikeEvaluationRang WAVE durations = PSQ_GetPulseDurations(device, PSQ_CHIRP, sweepNo, totalOnsetDelay) - chirpStart = totalOnsetDelay + PSQ_BL_EVAL_RANGE + WAVE/T epochWave = GetEpochsWave(device) + [epBegin, epEnd] = PSQ_CR_AddSpikeEvaluationEpoch(epochsWave, DAC, headstage, durations, totalOnsetDelay) + + return [epBegin * ONE_TO_MILLI, epEnd * ONE_TO_MILLI] +End + +/// @brief Adds PSQ_Chirp spike evaluation epoch +/// +/// @param epochsWave 4d epochs wave +/// @param DAC DAC number +/// @param headstage headstage number +/// @param durations pulse duration wave +/// @param totalOnsetDelayMS totalOnsetDelay in ms +/// +/// @retval epBegin epoch begin in s +/// @retval epEnd epoch end in s +Function [variable epBegin, variable epEnd] PSQ_CR_AddSpikeEvaluationEpoch(WAVE/T epochsWave, variable DAC, variable headstage, WAVE durations, variable totalOnsetDelayMS) + + variable chirpStart, chirpEnd + string tags, shortName + + chirpStart = totalOnsetDelayMS + PSQ_BL_EVAL_RANGE chirpEnd = chirpStart + durations[headstage] epBegin = chirpStart * MILLI_TO_ONE @@ -4703,9 +4739,9 @@ static Function [variable epBegin, variable epEnd] PSQ_CR_GetSpikeEvaluationRang sprintf tags, "Type=Chirp spike evaluation" sprintf shortName, "CR_SE" - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochsWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) - return [epBegin * ONE_TO_MILLI, epEnd * ONE_TO_MILLI] + return [epBegin, epEnd] End /// @brief Return the begin/start [ms] of the chirp bounds evaluation range @@ -4714,7 +4750,7 @@ End static Function [variable epBegin, variable epEnd] PSQ_CR_GetChirpEvaluationRange(string device, variable sweepNo, variable headstage, variable requestedCycles) variable DAC, stimsetQC - string name, tags, regexp, shortname, key + string name, tags, shortname, key WAVE numericalValues = GetLBNumericalValues(device) WAVE textualValues = GetLBTextualValues(device) @@ -4733,10 +4769,7 @@ static Function [variable epBegin, variable epEnd] PSQ_CR_GetChirpEvaluationRang return [epBegin, epEnd] endif - sprintf regexp, "^(E1_TG_C%d|E1_TG_C%d)$", 0, requestedCycles - 1 - WAVE/T/Z fullCycleEpochs = EP_GetEpochs(numericalValues, textualValues, NaN, XOP_CHANNEL_TYPE_DAC, DAC, \ - regexp, treelevel = 2, epochsWave = epochsWave) - + WAVE/Z/T fullCycleEpochs = PSQ_CR_GetFullCycleEpochs(numericalValues, textualValues, DAC, epochsWave, requestedCycles) if(!WaveExists(fullCycleEpochs)) printf "Could not find chirp cycles in epoch 1.\r" ControlWindowToFront() @@ -4758,15 +4791,43 @@ static Function [variable epBegin, variable epEnd] PSQ_CR_GetChirpEvaluationRang return [NaN, NaN] endif + [epBegin, epEnd] = PSQ_CR_AddCycleEvaluationEpoch(epochsWave, fullCycleEpochs, DAC) + + return [epBegin * ONE_TO_MILLI, epEnd * ONE_TO_MILLI] +End + +/// @brief Add PSQ_Chirp cycle evaluation epoch +/// +/// @param epochsWave 4d epoch wave +/// @param fullCycleEpochs 2d epoch wave with epochs of full cycles (sorted) +/// @param DAC DAC number +/// +/// @retval epBegin epoch begin in s +/// @retval epEnd epoch end in s +Function [variable epBegin, variable epEnd] PSQ_CR_AddCycleEvaluationEpoch(WAVE/T epochsWave, WAVE/T fullCycleEpochs, variable DAC) + + string tags, shortName + epBegin = str2num(fullCycleEpochs[0][EPOCH_COL_STARTTIME]) epEnd = str2num(fullCycleEpochs[DimSize(fullCycleEpochs, ROWS) - 1][EPOCH_COL_ENDTIME]) sprintf tags, "Type=Chirp cycles evaluation" sprintf shortName, "CR_CE" - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochsWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) - return [epBegin * ONE_TO_MILLI, epEnd * ONE_TO_MILLI] + return [epBegin, epEnd] +End + +Function/WAVE PSQ_CR_GetFullCycleEpochs(WAVE numericalValues, WAVE/T textualValues, variable DAC, WAVE/T epochsWave, variable requestedCycles) + + string regexp + + sprintf regexp, "^(E1_TG_C%d|E1_TG_C%d)$", 0, requestedCycles - 1 + WAVE/T/Z fullCycleEpochs = EP_GetEpochs(numericalValues, textualValues, NaN, XOP_CHANNEL_TYPE_DAC, DAC, \ + regexp, treelevel = 2, epochsWave = epochsWave) + + return fullCycleEpochs End /// @brief Manually force the pre/post set events @@ -5245,7 +5306,8 @@ static Function PSQ_CreateTestpulseLikeEpoch(string device, variable DAC, string sprintf tags, "Type=Testpulse Like;Index=%d", tpIndex sprintf shortName, "TP%d", tpIndex - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + WAVE/T epochWave = GetEpochsWave(device) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) // pre TP baseline // same epBegin as full TP @@ -5253,7 +5315,7 @@ static Function PSQ_CreateTestpulseLikeEpoch(string device, variable DAC, string sprintf tags, "Type=Testpulse Like;SubType=Baseline;Index=%d;", tpIndex sprintf shortName, "TP%d_B0", tpIndex - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) // pulse TP epBegin = epEnd @@ -5262,7 +5324,7 @@ static Function PSQ_CreateTestpulseLikeEpoch(string device, variable DAC, string sprintf tags, "Type=Testpulse Like;SubType=Pulse;Amplitude=%g;Index=%d;", amplitude, tpIndex sprintf shortName, "TP%d_P", tpIndex - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) // post TP baseline epBegin = epEnd @@ -5270,7 +5332,7 @@ static Function PSQ_CreateTestpulseLikeEpoch(string device, variable DAC, string sprintf tags, "Type=Testpulse Like;SubType=Baseline;Index=%d;", tpIndex sprintf shortName, "TP%d_B1", tpIndex - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) return epEnd End @@ -5698,6 +5760,7 @@ static Function PSQ_SE_CreateEpochs(string device, variable headstage, string pa wbBegin = 0 wbEnd = totalOnsetDelay * MILLI_TO_ONE + WAVE/T epochWave = GetEpochsWave(device) for(i = 0; i < numEpochs; i += 1) duration = ST_GetStimsetParameterAsVariable(setName, "Duration", epochIndex = i) * MILLI_TO_ONE @@ -5727,7 +5790,7 @@ static Function PSQ_SE_CreateEpochs(string device, variable headstage, string pa epEnd = epBegin + chunkLength [tags, shortName] = PSQ_CreateBaselineChunkSelectionStrings(userEpochIndexBLC) - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) userEpochIndexBLC += 1 elseif(i == 1 || i == 4 || i == 7 || i == 12 || i == 15 || i == 18) epBegin = wbBegin @@ -6115,6 +6178,7 @@ static Function PSQ_CreateBaselineChunkSelectionEpochs(string device, variable h wbBegin = 0 wbEnd = totalOnsetDelay * MILLI_TO_ONE + WAVE/T epochWave = GetEpochsWave(device) for(i = 0; i < numEpochs; i += 1) duration = ST_GetStimsetParameterAsVariable(setName, "Duration", epochIndex = i) * MILLI_TO_ONE @@ -6139,7 +6203,7 @@ static Function PSQ_CreateBaselineChunkSelectionEpochs(string device, variable h epEnd = epBegin + chunkLength [tags, shortName] = PSQ_CreateBaselineChunkSelectionStrings(index) - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, epBegin, epEnd, tags, shortName = shortName) index += 1 endfor diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 5837c2cea2..27e0bb048d 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -1119,7 +1119,8 @@ Constant PSQ_CALC_METHOD_RMS = 0x2 // root-mean-square (rms) Constant PSQ_BL_FAILED = 1 -StrConstant PSQ_BASELINE_CHUNK_SHORT_NAME_PREFIX = "BLC" +StrConstant PSQ_BASELINE_CHUNK_SHORT_NAME_PREFIX = "BLC" +StrConstant PSQ_BASELINE_CHUNK_SHORT_NAME_RE_MATCHER = "^U_BLC[[:digit:]]+$" /// @} /// @name PatchSeq labnotebook constants diff --git a/Packages/MIES/MIES_Epochs.ipf b/Packages/MIES/MIES_Epochs.ipf index 9db63def09..8c8c3b90e4 100644 --- a/Packages/MIES/MIES_Epochs.ipf +++ b/Packages/MIES/MIES_Epochs.ipf @@ -1152,7 +1152,7 @@ End /// Allows to add user epochs for not yet finished sweeps. The tree level /// is fixed to #EPOCH_USER_LEVEL to not collide with stock entries. /// -/// @param device device +/// @param epochWave epoch wave /// @param channelType channel type, currently only #XOP_CHANNEL_TYPE_DAC and #XOP_CHANNEL_TYPE_TTL is supported /// @param channelNumber channel number /// @param epBegin start time of the epoch in seconds @@ -1160,11 +1160,10 @@ End /// @param tags tags for the epoch /// @param shortName [optional, defaults to auto-generated] user defined short name for the epoch, will /// be prefixed with #EPOCH_SHORTNAME_USER_PREFIX -Function EP_AddUserEpoch(string device, variable channelType, variable channelNumber, variable epBegin, variable epEnd, string tags, [string shortName]) +Function EP_AddUserEpoch(WAVE/T epochWave, variable channelType, variable channelNumber, variable epBegin, variable epEnd, string tags, [string shortName]) ASSERT(channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_TTL, "Currently only epochs for the DA and TTL channels are supported") - WAVE/T epochWave = GetEpochsWave(device) if(ParamIsDefault(shortName)) sprintf shortName, "%s%d", EPOCH_SHORTNAME_USER_PREFIX, EP_GetEpochCount(epochWave, channelNumber, channelType) else @@ -1172,7 +1171,7 @@ Function EP_AddUserEpoch(string device, variable channelType, variable channelNu shortName = EPOCH_SHORTNAME_USER_PREFIX + shortName endif - return EP_AddEpoch(epochWave, channelNumber, channelType, epBegin * ONE_TO_MICRO, epEnd * ONE_TO_MICRO, tags, shortName, EPOCH_USER_LEVEL) + EP_AddEpoch(epochWave, channelNumber, channelType, epBegin * ONE_TO_MICRO, epEnd * ONE_TO_MICRO, tags, shortName, EPOCH_USER_LEVEL) End /// @brief Adds a epoch to the epochsWave @@ -1753,6 +1752,7 @@ static Function/WAVE EP_RecreateEpochsFromLoadedData(WAVE numericalValues, WAVE/ WAVE/T recEpochWave = GetEpochsWaveAsFree() EP_CollectEpochInfoDA(recEpochWave, s) + EP_AddRecreatedUserEpochs(numericalValues, textualValues, sweepDFR, sweepNo, s, recEpochWave) WAVE/Z channelDA = GetDAQDataSingleColumnWaveNG(numericalValues, textualValues, sweepNo, sweepDFR, XOP_CHANNEL_TYPE_DAC, s.DACList[0]) ASSERT(WaveExists(channelDA), "Could not retrieve first DA sweep") @@ -1773,3 +1773,106 @@ static Function/WAVE EP_RecreateEpochsFromLoadedData(WAVE numericalValues, WAVE/ return recEpochWave End + +static Function EP_AddRecreatedUserEpochs(WAVE numericalValues, WAVE/T textualValues, DFREF sweepDFR, variable sweepNo, STRUCT DataConfigurationResult &s, WAVE/T epochWave) + + variable firstDAC, firstHS, totalOnsetDelayMS, index, type + string key + + firstDAC = s.DACList[0] + firstHS = s.headstageDAC[0] + totalOnsetDelayMS = s.onsetDelay * s.samplingIntervalDA * MICRO_TO_MILLI + key = "Generic function" + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, key, firstDAC, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) + if(!WaveExists(settings)) + return NaN + endif + WAVE/T settingsT = settings + type = MapAnaFuncToConstant(settingsT[index]) + + switch(type) + case PSQ_CHIRP: + EP_AddRecreatedUserEpochs_PSQ_Chirp(numericalValues, textualValues, sweepDFR, sweepNo, firstDAC, firstHS, totalOnsetDelayMS, epochWave) + break + default: + DEBUGPRINT("EP_Recreation: Unsupported analysis function -> skipped.") + return NaN + endswitch +End + +static Function EP_AddRecreatedUserEpochs_PSQ_Chirp(WAVE numericalValues, WAVE/T textualValues, DFREF sweepDFR, variable sweepNo, variable DAC, variable headStage, variable totalOnsetDelayMS, WAVE/T epochWave) + + variable index, stimsetQC, chirpCycles, spikeCheck, baselineQC + variable epBegin, epEnd, chunk, chunkPassed, chunkStartTimeMax, chunkLengthTime + string key, params + + key = "Function params (encoded)" + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, key, DAC, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) + ASSERT(WaveExists(settings), "EP_Recreation: Could not query analysis function parameters from LNB.") + WAVE/T settingsT = settings + params = settingsT[index] + + key = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_PULSE_DUR, query = 1) + WAVE/Z durations = GetLastSetting(numericalValues, sweepNo, key, UNKNOWN_MODE) + ASSERT(WaveExists(durations), "Could not find durations in LNB") + + spikeCheck = AFH_GetAnalysisParamNumerical("SpikeCheck", params) + ASSERT(IsFinite(spikeCheck), "Invalid SpikeCheck param") + if(spikeCheck) + [epBegin, epEnd] = PSQ_CR_AddSpikeEvaluationEpoch(epochWave, DAC, headStage, durations, totalOnsetDelayMS) + endif + + for(chunk = 0;; chunk += 1) + key = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_CHUNK_PASS, chunk = chunk, query = 1) + chunkPassed = GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE, defValue = NaN) + if(IsNaN(chunkPassed)) + break + endif + chunkLengthTime = PSQ_BL_EVAL_RANGE + chunkStartTimeMax = chunk ? (totalOnsetDelayMS + PSQ_BL_EVAL_RANGE + WaveMax(durations)) + chunk * PSQ_BL_EVAL_RANGE : totalOnsetDelayMS + PSQ_AddBaselineEpoch(epochWave, DAC, chunk, chunkStartTimeMax, chunkLengthTime) + endfor + + chirpCycles = AFH_GetAnalysisParamNumerical("NumberOfChirpCycles", params) + ASSERT(IsFinite(chirpCycles) && chirpCycles > 0, "Invalid chirp cycles") + WAVE/Z/T fullCycleEpochs = PSQ_CR_GetFullCycleEpochs(numericalValues, textualValues, DAC, epochWave, chirpCycles) + if(!WaveExists(fullCycleEpochs)) + return NaN + endif + + key = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_CR_STIMSET_QC, query = 1) + stimsetQC = GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE) + if(IsNaN(stimsetQC)) + // fallback + stimsetQC = !(chirpCycles > 1 && DimSize(fullCycleEpochs, ROWS) == 1) + endif + if(!stimsetQC) + return NaN + endif + + ASSERT(DimSize(fullCycleEpochs, ROWS) == (chirpCycles == 1 ? 1 : 2), "Chirp resulted in successful stimset QC, but could not find cycle base epochs E1_TG_C0 && E1_TG_C" + num2istr(chirpCycles - 1)) + [epBegin, epEnd] = PSQ_CR_AddCycleEvaluationEpoch(epochWave, fullCycleEpochs, DAC) +End + +/// @brief Fetches a single epoch channel from a recreated epoch wave. +/// The returned epoch channel wave has the same form as epoch information that was stored in the LNB returned by @ref EP_FetchEpochs +/// +/// @param epochWave 4d epoch wave +/// @param channelNumber GUI channel number +/// @param channelType channel type, one of @ref XopChannelConstants +/// @returns epoch channel wave (2d) +Function/WAVE EP_FetchEpochsFromRecreated(WAVE epochWave, variable channelNumber, variable channelType) + + string epList + + epList = EP_EpochWaveToStr(epochWave, channelNumber, channelType) + if(IsEmpty(epList)) + return $"" + endif + WAVE epChannel = EP_EpochStrToWave(epList) + if(!DimSize(epChannel, ROWS)) + return $"" + endif + + return epChannel +End diff --git a/Packages/tests/HardwareBasic/UTF_Epochs.ipf b/Packages/tests/HardwareBasic/UTF_Epochs.ipf index 612bb5b59a..61cdb40edf 100644 --- a/Packages/tests/HardwareBasic/UTF_Epochs.ipf +++ b/Packages/tests/HardwareBasic/UTF_Epochs.ipf @@ -467,7 +467,7 @@ static Function TestEpochsGeneric(string device) endfor endfor - TestEpochReceation(device) + TestEpochRecreation(device, sweepNo) End static Function TestUnacquiredEpoch(WAVE sweep, WAVE epochChannel) @@ -1221,46 +1221,3 @@ static Function EP_EpochTest18_REENTRY([STRUCT IUTF_mData &mData]) TestEpochsGeneric(mData.s0) End - -static Function TestEpochReceationRemoveUserEpochs(WAVE/T epochChannel) - - variable i, epochCnt - - epochCnt = DimSize(epochChannel, ROWS) - Make/FREE/T/N=(epochCnt) shortnames = EP_GetShortName(epochChannel[p][EPOCH_COL_TAGS]) - for(i = epochCnt - 1; i >= 0; i -= 1) - if(GrepString(shortnames[i], "^" + EPOCH_SHORTNAME_USER_PREFIX)) - DeleteWavePoint(epochChannel, ROWS, i) - endif - endfor -End - -static Function TestEpochReceation(string device) - - variable channelNumber - variable sweepNo = 0 - - WAVE/Z numericalValues = GetLBNumericalValues(device) - WAVE/Z textualValues = GetLBTextualValues(device) - DFREF deviceDFR = GetDeviceDataPath(device) - DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) - - WAVE/Z activeChannels = GetActiveChannels(numericalValues, textualValues, sweepNo, XOP_CHANNEL_TYPE_DAC) - CHECK_WAVE(activeChannels, FREE_WAVE | NUMERIC_WAVE) - for(channelNumber = 0; channelNumber < NUM_DA_TTL_CHANNELS; channelNumber += 1) - - if(IsNaN(activeChannels[channelNumber])) - continue - endif - WAVE/Z/T epochChannelRef = EP_FetchEpochs(numericalValues, textualValues, sweepNo, sweepDFR, channelNumber, XOP_CHANNEL_TYPE_DAC) - WAVE/Z/T epochChannelRec = MIES_EP#EP_FetchEpochsFromRecreation(numericalValues, textualValues, sweepNo, sweepDFR, channelNumber, XOP_CHANNEL_TYPE_DAC) - - if(WaveExists(epochChannelRef)) - TestEpochReceationRemoveUserEpochs(epochChannelRef) - // also TP channels can be active but have no epochs - CHECK_EQUAL_WAVES(epochChannelRec, epochChannelRef) - else - CHECK_WAVE(epochChannelRec, NULL_WAVE) - endif - endfor -End diff --git a/Packages/tests/HistoricData/HistoricData.pxp b/Packages/tests/HistoricData/HistoricData.pxp index 20ff2eef4b..42288342fc 100644 Binary files a/Packages/tests/HistoricData/HistoricData.pxp and b/Packages/tests/HistoricData/HistoricData.pxp differ diff --git a/Packages/tests/HistoricData/UTF_AttemptNWB2ExportOnOldData.ipf b/Packages/tests/HistoricData/UTF_AttemptNWB2ExportOnOldData.ipf index cd02338b5d..5c94c6a399 100644 --- a/Packages/tests/HistoricData/UTF_AttemptNWB2ExportOnOldData.ipf +++ b/Packages/tests/HistoricData/UTF_AttemptNWB2ExportOnOldData.ipf @@ -3,7 +3,7 @@ #pragma rtFunctionErrors=1 #pragma ModuleName=ExportToNWB -/// UTF_TD_GENERATOR GetHistoricDataFiles +/// UTF_TD_GENERATOR GetHistoricDataFilesPXP static Function TestExportingDataToNWB([string str]) string file, miesPath, nwbFileName diff --git a/Packages/tests/HistoricData/UTF_EpochRecreation.ipf b/Packages/tests/HistoricData/UTF_EpochRecreation.ipf index eca4cbeaec..bf2bae5437 100644 --- a/Packages/tests/HistoricData/UTF_EpochRecreation.ipf +++ b/Packages/tests/HistoricData/UTF_EpochRecreation.ipf @@ -3,8 +3,46 @@ #pragma rtFunctionErrors=1 #pragma ModuleName=EpochRecreation -/// UTF_TD_GENERATOR GetHistoricDataFiles -static Function TestEpochRecreation([string str]) +/// @brief This function is a helper function to create copies of recreated epochs waves for each sweep of an experiment file +/// as preparation for testcase @ref TestEpochRecreationShortNames +/// +/// @param dfName name of data folder that is created in root: +Function ExportEpochsFromFileToDF(string dfName) + + string file, win, abWin, sweepBrowsers + variable first, last, i + + Open/D/R/F="Data Files (*.pxp,*.nwb):.pxp,.nwb;" i + if(IsEmpty(s_fileName)) + return NaN + endif + file = S_fileName + + [abWin, sweepBrowsers] = OpenAnalysisBrowser({file}, loadSweeps = 1, loadStimsets = 1, absolutePaths = 1) + win = StringFromList(0, sweepBrowsers) + [first, last] = BSP_FirstAndLastSweepAcquired(win) + + KillOrMoveToTrash(dfr = root:$dfName) + DFREF dfr = createDFWithAllParents("root:" + dfName) + + for(i = first; i < last; i += 1) + WAVE/Z/T numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = i) + ASSERT(WaveExists(numericalValues), "Numerical LabNotebook not found.") + WAVE/Z/T textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = i) + ASSERT(WaveExists(textualValues), "Textual LabNotebook not found.") + + DFREF sweepDFR = BSP_GetSweepDF(win, i) + WAVE/Z epochs = MIES_EP#EP_RecreateEpochsFromLoadedData(numericalValues, textualValues, sweepDFR, i) + Duplicate epochs, dfr:$("epoch_" + num2istr(i)) + endfor + + string/G dfr:fullFileName = file + + printf "Exported recreated epochs for %d sweeps from file %s to root:%s\r", last, file, dfName +End + +/// UTF_TD_GENERATOR GetHistoricDataFilesPXP +static Function TestEpochRecreationFromLoadedPXP([string str]) string file, miesPath, win, device variable numObjectsLoaded, first, last, i @@ -50,3 +88,51 @@ static Function TestEpochRecreation([string str]) CHECK_WAVE(epochs, TEXT_WAVE) endfor End + +/// UTF_TD_GENERATOR GetHistoricDataFiles +static Function TestEpochRecreationShortNames([string str]) + + string file, win, abWin, sweepBrowsers, refEpFolder + variable first, last, i + + // Data file to datafolder matching for data files that require to also use @ref ExtendRefEpochsWithUserEpochs + if(!CmpStr(str, "C57BL6J-628261.02.01.02.nwb")) + refEpFolder = "EpochsC57" + elseif(!CmpStr(str, "Gad2-IRES-Cre;Ai14-709273.06.02.02.nwb")) + refEpFolder = "EpochsGad2" + else + refEpFolder = "" + endif + + file = "input:" + str + + [abWin, sweepBrowsers] = OpenAnalysisBrowser({file}, loadSweeps = 1, loadStimsets = 1) + win = StringFromList(0, sweepBrowsers) + [first, last] = BSP_FirstAndLastSweepAcquired(win) + + for(i = first; i < last; i += 1) + WAVE/Z/T numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = i) + ASSERT(WaveExists(numericalValues), "Numerical LabNotebook not found.") + WAVE/Z/T textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = i) + ASSERT(WaveExists(textualValues), "Textual LabNotebook not found.") + + DFREF sweepDFR = BSP_GetSweepDF(win, i) + try + WAVE/Z epochs = MIES_EP#EP_RecreateEpochsFromLoadedData(numericalValues, textualValues, sweepDFR, i) + catch + // This can happen for old nwb2 data where some stimsets are created from formulas. + // In these formulas other "dependend" stimsets are refrenced that were not saved to the nwb files. + // As a result of this missing data the stimsets from formulas can not be recreated and thus, the epochs can not be recreated. + // Can potentially happen in all nwb2 files that were exported before commit + // ad9130c2 (WB: fix recursive resolving dependet stimsets (e.g. formulas), 2024-03-12) + printf "Note: Could not recreate epochs for sweep %d in file %s.\r", i, str + continue + endtry + WAVE/ZZ refEpochs + if(!IsEmpty(refEpFolder)) + DFREF dfr = root:$refEpFolder + WAVE refEpochs = dfr:$("epoch_" + num2istr(i)) + endif + CompareEpochsOfSweep(numericalValues, textualValues, i, sweepDFR, epochs, historic = 1, userEpochRef = refEpochs) + endfor +End diff --git a/Packages/tests/HistoricData/UTF_HistoricData.ipf b/Packages/tests/HistoricData/UTF_HistoricData.ipf index 719714f57e..15127bf4df 100644 --- a/Packages/tests/HistoricData/UTF_HistoricData.ipf +++ b/Packages/tests/HistoricData/UTF_HistoricData.ipf @@ -212,6 +212,19 @@ End Function/WAVE GetHistoricDataFiles() + WAVE/T pxpFiles = GetHistoricDataFilesPXP() + WAVE/T nwbFiles = GetHistoricDataFilesNWB() + + Concatenate/FREE/NP/T {pxpFiles, nwbFiles}, files + + DownloadFilesIfRequired(files) + SetLabelsForDGWave(files) + + return files +End + +Function/WAVE GetHistoricDataFilesPXP() + Make/FREE/T files = {"C57BL6J-629713.05.01.02.pxp", \ "Chat-IRES-Cre-neo;Ai14-582723.15.10.01.pxp", \ "Pvalb-IRES-Cre;Ai14-646904.13.03.02.pxp", \ @@ -226,6 +239,19 @@ Function/WAVE GetHistoricDataFiles() return files End +Function/WAVE GetHistoricDataFilesNWB() + + Make/FREE/T files = {"nwb2_H17.03.016.11.09.01.nwb", \ + "C57BL6J-628261.02.01.02.nwb", \ + "Gad2-IRES-Cre;Ai14-709273.06.02.02.nwb", \ + "H22.03.311.11.08.01.06.nwb"} + + DownloadFilesIfRequired(files) + SetLabelsForDGWave(files) + + return files +End + Function/WAVE GetHistoricDataFilesSweepUpgrade() Make/FREE/T files = {"single_numeric_sweep.pxp"} diff --git a/Packages/tests/UTF_HardwareHelperFunctions.ipf b/Packages/tests/UTF_HardwareHelperFunctions.ipf index 0e0e86d356..f2732244da 100644 --- a/Packages/tests/UTF_HardwareHelperFunctions.ipf +++ b/Packages/tests/UTF_HardwareHelperFunctions.ipf @@ -562,8 +562,8 @@ static Function CheckAnaFuncVersion(string device, variable type) End Function CommonAnalysisFunctionChecks(string device, variable sweepNo, WAVE headstageQC) - string key - variable type + string key + variable type, DAC, index CHECK_EQUAL_VAR(GetSetVariable(device, "SetVar_Sweep"), sweepNo + 1) CHECK_EQUAL_VAR(AFH_GetLastSweepAcquired(device), sweepNo) @@ -597,6 +597,10 @@ Function CommonAnalysisFunctionChecks(string device, variable sweepNo, WAVE head CheckForOtherUserLBNKeys(device, type) CheckRangeOfUserLabnotebookKeys(device, type, sweepNo) CheckDAStimulusSets(device, sweepNo, type) + + if(type == PSQ_CHIRP) + TestEpochRecreation(device, sweepNo) + endif End /// Used for patch seq analysis functions and NextStimSetName/NextIndexingEndStimSetName analysis parameters diff --git a/Packages/tests/UTF_HelperFunctions.ipf b/Packages/tests/UTF_HelperFunctions.ipf index fed7e32c50..03a8f5672c 100644 --- a/Packages/tests/UTF_HelperFunctions.ipf +++ b/Packages/tests/UTF_HelperFunctions.ipf @@ -972,12 +972,13 @@ Function/S LoadSweeps(string winAB) return StringFromList(0, WinList("*", ";", "WIN:" + num2istr(WINTYPE_GRAPH))) End -/// @brief Open the given files, located relative to the symbolic path `home`, into an analysis browser -Function [string abWin, string sweepBrowsers] OpenAnalysisBrowser(WAVE/T files, [variable loadSweeps, variable loadStimsets]) +/// @brief Open the given files in the analysis browser. By default files are located relative to the symbolic path `home` unless absolutePaths is set +Function [string abWin, string sweepBrowsers] OpenAnalysisBrowser(WAVE/T files, [variable loadSweeps, variable loadStimsets, variable absolutePaths]) variable idx string filePath, fullFilePath + absolutePaths = ParamIsDefault(absolutePaths) ? 0 : !!absolutePaths if(ParamIsDefault(loadSweeps)) loadSweeps = 0 else @@ -985,17 +986,18 @@ Function [string abWin, string sweepBrowsers] OpenAnalysisBrowser(WAVE/T files, endif loadStimsets = ParamIsDefault(loadStimsets) ? 0 : !!loadStimsets - PathInfo home - REQUIRE_EQUAL_VAR(V_flag, 1) + if(absolutePaths) + WAVE/T filesWithPath = files + else + PathInfo home - Duplicate/FREE/T files, filesWithPath - filesWithPath[] = S_path + GetHFSPath(files[p]) + Duplicate/FREE/T files, filesWithPath + filesWithPath[] = S_path + GetHFSPath(files[p]) + endif abWin = AB_OpenAnalysisBrowser(restoreSettings = 0) - CHECK_PROPER_STR(abWin) for(fullFilePath : filesWithPath) - REQUIRE(FileExists(fullFilePath)) MIES_AB#AB_AddElementToSourceList(fullFilePath) endfor @@ -1010,8 +1012,6 @@ Function [string abWin, string sweepBrowsers] OpenAnalysisBrowser(WAVE/T files, WAVE expBrowserSel = GetExperimentBrowserGUISel() WAVE/Z indizes = FindIndizes(expBrowserList, colLabel = "file", prop = PROP_NON_EMPTY) - INFO("Trying to load files @%s", s = files) - CHECK_WAVE(indizes, NUMERIC_WAVE) if(loadSweeps) for(idx : indizes) @@ -1020,7 +1020,6 @@ Function [string abWin, string sweepBrowsers] OpenAnalysisBrowser(WAVE/T files, endfor sweepBrowsers = WinList("*", ";", "WIN:" + num2istr(WINTYPE_GRAPH)) - CHECK_PROPER_STR(sweepBrowsers) endif if(loadStimsets) @@ -1135,3 +1134,162 @@ Function [string baseSet, string stimsetList, string customWavePath, variable am return [setNameB, stimsetList, wPath, amplitude] End + +static Function TestEpochRecreationRemoveUnsupportedUserEpochs(WAVE/T epochChannel, variable type) + + variable index, epochCnt + string shortName, supportedUserEpochsRegExp + string regexpUserEpochs = "^" + EPOCH_SHORTNAME_USER_PREFIX + ".*" + + Make/FREE/T supportedUserEpochs = {"^U_CR_CE$", "^U_CR_SE$"} + Make/FREE/T psqChirpEpochs = {PSQ_BASELINE_CHUNK_SHORT_NAME_RE_MATCHER} + if(type == PSQ_CHIRP) + Concatenate/FREE/T/NP {psqChirpEpochs}, supportedUserEpochs + endif + supportedUserEpochsRegExp = TextWaveToList(supportedUserEpochs, "|") + supportedUserEpochsRegExp = RemoveEnding(supportedUserEpochsRegExp, "|") + supportedUserEpochsRegExp = "^(?![\s\S]*" + supportedUserEpochsRegExp + ")[\s\S]*$" + Make/FREE/T/N=(DimSize(epochChannel, ROWS)) shortnames = EP_GetShortName(epochChannel[p][EPOCH_COL_TAGS]) + WAVE/Z userEpochIndices = FindIndizes(shortNames, str = regexpUserEpochs, prop = PROP_GREP) + if(!WaveExists(userEpochIndices)) + return NaN + endif + Make/FREE/T/N=(DimSize(userEpochIndices, ROWS)) userEpochShortNames = shortnames[userEpochIndices[p]] + WAVE/Z matches = FindIndizes(userEpochShortNames, str = supportedUserEpochsRegExp, prop = PROP_GREP) + if(!WaveExists(matches)) + return NaN + endif + matches[] = userEpochIndices[matches[p]] + Sort/R matches, matches + for(index : matches) + DeleteWavePoint(epochChannel, ROWS, index) + endfor +End + +Function TestEpochRecreation(string device, variable sweepNo) + + WAVE/Z numericalValues = GetLBNumericalValues(device) + WAVE/Z textualValues = GetLBTextualValues(device) + DFREF deviceDFR = GetDeviceDataPath(device) + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + + WAVE epochWave = MIES_EP#EP_RecreateEpochsFromLoadedData(numericalValues, textualValues, sweepDFR, sweepNo) + + CompareEpochsOfSweep(numericalValues, textualValues, sweepNo, sweepDFR, epochWave) + +End + +/// @brief Compares epochs of all channels of a given sweep. By default equality is checked, unless historic is set. +/// Because the epoch recreation does not support recreation of all user epochs, in @ref TestEpochRecreationRemoveUnsupportedUserEpochs a +/// list of supported user epochs in epoch recreation is set. All other (unsupported) user epochs are removed from the reference epochs of +/// the experiment before comparison. +/// +/// @param numericalValues numerical labnotebook values +/// @param textualValues textual labnotebook values +/// @param sweepNo sweep number +/// @param sweepDFR sweep datafolder +/// @param epochRec 4d epochs wave from recreated epochs +/// @param historic [optional, default 0], when set instead of equality only the existence of epochs based on the shortNames is checked. +/// All non-user epochs that exist in the reference epochs from the experiment must exist in the recreated epochs +/// All user epochs that were recreated must exist in the reference epochs from the experiment +/// @param userEpochRef [optional, default null] When set the user epochs from this wave are used to extend the epochs from the experiment if not already present +/// These epochs usually originate from exported recreated epochs used for test case @ref TestEpochRecreationShortNames +Function CompareEpochsOfSweep(WAVE/Z numericalValues, WAVE/Z/T textualValues, variable sweepNo, DFREF sweepDFR, WAVE epochRec, [variable historic, WAVE/Z userEpochRef]) + + variable channelNumber, index, type + string anaFunc + + historic = ParamIsDefault(historic) ? 0 : !!historic + + for(channelNumber = 0; channelNumber < NUM_DA_TTL_CHANNELS; channelNumber += 1) + + WAVE/Z/T epochChannelRef = EP_FetchEpochs(numericalValues, textualValues, sweepNo, sweepDFR, channelNumber, XOP_CHANNEL_TYPE_DAC) + WAVE/Z/T epochChannelRec = EP_FetchEpochsFromRecreated(epochRec, channelNumber, XOP_CHANNEL_TYPE_DAC) + + if(WaveExists(epochChannelRef)) + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Generic function", channelNumber, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) + if(WaveExists(settings)) + WAVE/T settingsT = settings + type = MapAnaFuncToConstant(settingsT[index]) + TestEpochRecreationRemoveUnsupportedUserEpochs(epochChannelRef, type) + endif + if(historic) + if(WaveExists(userEpochRef)) + WAVE/T epochChannelRecUser = EP_FetchEpochsFromRecreated(userEpochRef, channelNumber, XOP_CHANNEL_TYPE_DAC) + ExtendRefEpochsWithUserEpochs(epochChannelRef, epochChannelRecUser) + endif + CompareEpochsHistoricChannel(epochChannelRef, epochChannelRec) + else + // also TP channels can be active but have no epochs + CHECK_EQUAL_WAVES(epochChannelRec, epochChannelRef) + endif + else + CHECK_WAVE(epochChannelRec, NULL_WAVE) + endif + endfor +End + +/// @brief This function extends epochs of a single channel from the experiment with user epochs from a reference source if this user epochs do +/// not exist in the epochs of the experiment. The reference source is typically a set of exported recreated epochs as implemented in test case +/// @ref TestEpochRecreationShortNames +/// A typical case is an experiment with data created with an old MIES version where user epochs from analysis function were not implemented. +/// In that case the current epoch recreation creates more epochs than there were originally in the experiment. +/// The extension from this function fulfills then two points: +/// - the comparison as implemented in @ref CompareEpochsHistoricChannel works +/// - changes in epoch recreation since the point of exporting the reference epochs can be found +static Function ExtendRefEpochsWithUserEpochs(WAVE/T epochChannelRef, WAVE/T epochChannelRecRef) + + variable i, numEpochs, numEpochsRef + string shortName + + numEpochsRef = DimSize(epochChannelRef, ROWS) + Make/FREE/T/N=(numEpochsRef) epRefShortnames = EP_GetShortName(epochChannelRef[p][EPOCH_COL_TAGS]) + numEpochs = DimSize(epochChannelRecRef, ROWS) + for(i = 0; i < numEpochs; i += 1) + shortName = EP_GetShortName(epochChannelRecRef[i][EPOCH_COL_TAGS]) + if(strsearch(shortName, EPOCH_SHORTNAME_USER_PREFIX, 0) != 0) + continue + endif + FindValue/TEXT=shortName/TXOP=4 epRefShortnames + if(V_value >= 0) + continue + endif + Redimension/N=(numEpochsRef + 1, -1) epochChannelRef + epochChannelRef[numEpochsRef][] = epochChannelRecRef[i][q] + numEpochsRef += 1 + endfor +End + +/// @brief The historic epoch comparison only compares existence of epochs from shortnames if present +static Function CompareEpochsHistoricChannel(WAVE/T epochChannelRef, WAVE/T epochChannelRec) + + string shortName + variable i, numEpochs, numEpochsRec + + numEpochs = DimSize(epochChannelRef, ROWS) + Make/FREE/T/N=(numEpochs) epRefShortnames = EP_GetShortName(epochChannelRef[p][EPOCH_COL_TAGS]) + numEpochsRec = DimSize(epochChannelRec, ROWS) + Make/FREE/T/N=(numEpochsRec) epRecShortnames = EP_GetShortName(epochChannelRec[p][EPOCH_COL_TAGS]) + ASSERT(numEpochs > 0, "numEpochs is zero") + // test if all reference epochs are also present in recreated + for(i = 0; i < numEpochs; i += 1) + shortName = epRefShortnames[i] + if(IsEmpty(shortName)) + continue + endif + FindValue/TEXT=shortName/TXOP=4 epRecShortnames + INFO("Could not find reference epoch %s in recreated epochs.", s0 = shortName) + CHECK_GE_VAR(V_value, 0) + endfor + // test if all recreated user epochs are also present in reference + ASSERT(numEpochsRec > 0, "numEpochsRec is zero") + for(i = 0; i < numEpochsRec; i += 1) + shortName = epRecShortnames[i] + if(strsearch(shortName, EPOCH_SHORTNAME_USER_PREFIX, 0) != 0) + continue + endif + FindValue/TEXT=shortName/TXOP=4 epRefShortnames + INFO("Could not find recreated user epoch %s in reference epochs.", s0 = shortName) + CHECK_GE_VAR(V_value, 0) + endfor +End diff --git a/Packages/tests/UserAnalysisFunctions.ipf b/Packages/tests/UserAnalysisFunctions.ipf index ba9ecf5d56..87370dcd1c 100644 --- a/Packages/tests/UserAnalysisFunctions.ipf +++ b/Packages/tests/UserAnalysisFunctions.ipf @@ -967,7 +967,8 @@ Function StopMidSweep_V3(string device, STRUCT AnalysisFunction_V3 &s) case MID_SWEEP_EVENT: DAC = AFH_GetDACFromHeadstage(device, s.headstage) - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, 0, 1e9, "key=value") + WAVE/T epochWave = GetEpochsWave(device) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, 0, 1e9, "key=value") return ANALYSIS_FUNC_RET_EARLY_STOP endswitch @@ -981,7 +982,8 @@ Function AddTooLargeUserEpoch_V3(string device, STRUCT AnalysisFunction_V3 &s) switch(s.eventType) case PRE_SWEEP_CONFIG_EVENT: DAC = AFH_GetDACFromHeadstage(device, s.headstage) - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, 0, 1e9, "key=value") + WAVE/T epochWave = GetEpochsWave(device) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, 0, 1e9, "key=value") break endswitch End @@ -993,7 +995,8 @@ Function AddUserEpoch_V3(string device, STRUCT AnalysisFunction_V3 &s) DAC = AFH_GetDACFromHeadstage(device, s.headstage) sprintf tags, "HS=%d;eventType=%d;", s.headstage, s.eventType - EP_AddUserEpoch(device, XOP_CHANNEL_TYPE_DAC, DAC, 0.5, 0.6, tags) + WAVE/T epochWave = GetEpochsWave(device) + EP_AddUserEpoch(epochWave, XOP_CHANNEL_TYPE_DAC, DAC, 0.5, 0.6, tags) End Function ChangeTPSettings(device, s)