diff --git a/Packages/MIES/MIES_Cache.ipf b/Packages/MIES/MIES_Cache.ipf index 8264c05d8a..a8b72d5295 100644 --- a/Packages/MIES/MIES_Cache.ipf +++ b/Packages/MIES/MIES_Cache.ipf @@ -209,6 +209,13 @@ Function/S CA_AveragingWaveModKey(WAVE wv) return num2istr(CA_RecursiveWavemodCRC(wv)) + "Version 1" End +/// @brief Cache key generator for the tau range calculation +/// of psx events +Function/S CA_PSXEventGoodTauRange(WAVE wv) + + return num2istr(CA_RecursiveWavemodCRC(wv)) + "Version 1" +End + /// @brief Calculated a CRC from non wave reference waves using modification data, wave modification count and wave location. /// If the given wave is a wave reference wave, then the CRC is calculated recursively from /// all non wave reference waves and null wave references found. @@ -438,12 +445,7 @@ End Function/S CA_PSXOperationKey(string comboKey, string psxParameters) - return CA_PSXBaseKey(comboKey, psxParameters) + " Operation " + ":Version 2" -End - -Function/S CA_PSXRiseTimeKey(string comboKey, string psxParameters) - - return CA_PSXBaseKey(comboKey, psxParameters) + " PSX Rise time " + ":Version 2" + return CA_PSXBaseKey(comboKey, psxParameters) + " Operation " + ":Version 3" End Function/S CA_PSXAnalyzePeaks(string comboKey, string psxParameters) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 885bd7624b..63d9ab4e95 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -22,6 +22,7 @@ Constant DA_EPHYS_PANEL_VERSION = 64 Constant DATA_SWEEP_BROWSER_PANEL_VERSION = 51 Constant WAVEBUILDER_PANEL_VERSION = 14 Constant ANALYSISBROWSER_PANEL_VERSION = 5 +Constant PSX_PLOT_PANEL_VERSION = 1 /// Version of the stimset wave note Constant STIMSET_NOTE_VERSION = 11 @@ -2032,6 +2033,7 @@ StrConstant CO_EMPTY_DAC_LIST = "emptyDACList" StrConstant CO_SF_TOO_MANY_TRACES = "SF_tooManyTraces" StrConstant CO_PSX_CLIPPED_STATS = "psx_clippedStats" StrConstant CO_ARCHIVE_ONCE = "ArchiveLogs" +StrConstant CO_PSX_UPGRADE_EVENT = "psx_updateEvent" ///@} /// @name Constants for SweepFormula Meta data in JSON format @@ -2347,11 +2349,12 @@ StrConstant PSX_STATS_LABELS = "Average;Median;Average Deviation;Standard deviat ///@{ Constant PSX_HORIZ_OFFSET_ONSET = 0 Constant PSX_HORIZ_OFFSET_PEAK = 1 +Constant PSX_HORIZ_OFFSET_SLEW = 2 ///@} -Constant PSX_DECONV_FILTER_DEF_LOW = 0.002 -Constant PSX_DECONV_FILTER_DEF_HIGH = 0.004 -Constant PSX_DECONV_FILTER_DEF_ORDER = 101 +Constant PSX_DECONV_FILTER_DEF_LOW = 500 +Constant PSX_DECONV_FILTER_DEF_HIGH = 50 +Constant PSX_DECONV_FILTER_DEF_ORDER = 7 StrConstant PSX_JWN_COMBO_KEYS_NAME = "ComboKeys" diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 45b8f582e9..bf9477d2de 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1516,7 +1516,7 @@ Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE return [s] endif - WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) + WAVE/Z numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) if(!WaveExists(numericalValues)) return [s] endif diff --git a/Packages/MIES/MIES_SweepFormula_PSX.ipf b/Packages/MIES/MIES_SweepFormula_PSX.ipf index 473f9e5d00..b82ffceb9c 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX.ipf @@ -59,12 +59,20 @@ static Constant PSX_KEYBOARD_DIR_LR = 1 static Constant PSX_NUMBER_OF_SDS_DEFAULT = 2.5 +static constant PSX_TAU_CALC_FACTOR = 2.5 +static Constant PSX_BASELINE_RANGE_FACTOR = 10 +static Constant PSX_FIT_RANGE_FACTOR = 10 +static Constant PSX_FIT_RANGE_PERC = 0.9 +static Constant PSX_BASELINE_NUM_POINTS_AVERAGE = 5 +static Constant PSX_PEAK_RANGE_FACTOR_LEFT = 5 +static Constant PSX_PEAK_RANGE_FACTOR_RIGHT = 0.33 +static Constant PSX_PEAK_NUM_HIST_BINS = 20 + static Constant PSX_NUM_PEAKS_MAX = 2000 static Constant PSX_PLOT_DEFAULT_X_RANGE = 200 static Constant PSX_DEFAULT_X_START_OFFSET = 2 -static Constant PSX_DEFAULT_RANGE_FACTOR = 3 static StrConstant USER_DATA_KEYBOARD_DIR = "keyboard_direction" @@ -127,8 +135,6 @@ static Constant PSX_TUD_AVERAGE_ALL_COMBO_INDEX = NaN static StrConstant PSX_AVERAGE_FIT_RESULT_DEFAULT_HELP = "No fit results available for average accept" -static Constant PSX_DEFAULT_PEAK_SEARCH_RANGE_MS = 5 - static Constant PSX_STATS_TAU_FACTOR = 10 static Constant PSX_STATS_AMP_FACTOR = 100 @@ -140,7 +146,6 @@ static StrConstant PSX_PANEL_MACRO = "PSXPanel" /// @anchor PSXCacheKeyType ///@{ static Constant PSX_CACHE_KEY_EVENTS = 0x1 -static Constant PSX_CACHE_KEY_RISETIME = 0x2 static Constant PSX_CACHE_KEY_ANALYZE_PEAKS = 0x3 ///@} @@ -251,8 +256,11 @@ static Function/WAVE PSX_FilterSweepData(WAVE sweepData, variable low, variable ASSERT(low > high, "Expected a band pass filter with low > high") Duplicate/FREE sweepData, filtered + // Duplicate/O sweepData, root:sweepData - FilterIIR/ENDV=(filtered[0])/LO=(low / samp)/HI=(high / samp)/DIM=(ROWS) filtered; err = GetRTError(1) + // print low, high, samp + // Abort + FilterIIR/LO=(low / samp)/HI=(high / samp)/DIM=(ROWS)/ORD=6 filtered; err = GetRTError(1) SFH_ASSERT(!err, "Error filtering the data, msg: " + GetErrMessage(err)) return filtered @@ -324,7 +332,7 @@ End /// @param deconvFilter deconvolution filter settings static Function/WAVE PSX_DeconvoluteSweepData(WAVE sweepData, WAVE/C psxKernelFFT, WAVE deconvFilter) - variable numPoints, fftSize, samp, low, high, order, lowFrac, highFrac + variable numPoints, fftSize, samp, low, high, order, lowFrac, highFrac, err samp = 1 / (deltax(sweepData) * MILLI_TO_ONE) low = deconvFilter[%$"Filter Low"] @@ -332,13 +340,13 @@ static Function/WAVE PSX_DeconvoluteSweepData(WAVE sweepData, WAVE/C psxKernelFF order = deconvFilter[%$"Filter Order"] if(IsNaN(low)) - lowFrac = PSX_DECONV_FILTER_DEF_LOW + lowFrac = PSX_DECONV_FILTER_DEF_LOW / samp else lowFrac = low / samp endif if(IsNaN(high)) - highFrac = PSX_DECONV_FILTER_DEF_HIGH + highFrac = PSX_DECONV_FILTER_DEF_HIGH / samp else highFrac = high / samp endif @@ -347,7 +355,7 @@ static Function/WAVE PSX_DeconvoluteSweepData(WAVE sweepData, WAVE/C psxKernelFF order = PSX_DECONV_FILTER_DEF_ORDER endif - ASSERT(lowFrac < highFrac, "Expected a low pass filter with lowFrac < highFrac") + ASSERT(lowFrac > highFrac, "Expected a band pass filter with low > high") numPoints = DimSize(sweepData, ROWS) fftSize = DimSize(psxKernelFFT, ROWS) @@ -363,8 +371,12 @@ static Function/WAVE PSX_DeconvoluteSweepData(WAVE sweepData, WAVE/C psxKernelFF ASSERT(V_Value == -1, "Can not handle NaN in the deconvoluted wave") CopyScales sweepData, Deconv + FilterIIR/LO=(lowFrac)/HI=(highFrac)/ORD=(order) Deconv; err = GetRTError(0) - FilterFIR/ENDV={3}/LO={lowFrac, highFrac, order} Deconv + if(err) + printf "Error applying deconvolution filter: %s\r", GetRTErrMessage() + ClearRTError() + endif return Deconv End @@ -376,12 +388,10 @@ static Function/WAVE PSX_CreateHistogramOfDeconvSweepData(WAVE deconvSweepData) // we take +/- 80% of the average deviation around the average value WaveStats/Q deconvSweepData - binWidth = 0.0001 - range = V_adev * 0.8 + range = V_adev * 2 start = V_avg - range - n_bins = 2 * range / binWidth - - SFH_ASSERT(n_bins > 10, "Histogram creation failed due to too few data points") + n_bins = PSX_PEAK_NUM_HIST_BINS + binWidth = 2 * range / n_bins Make/D/FREE/N=0 hist Histogram/B={start, binWidth, n_bins}/DEST=hist deconvSweepData @@ -485,10 +495,10 @@ static Function [WAVE/D peakX, WAVE/D peakY] PSX_FindPeaks(WAVE sweepDataOffFilt return [peakX, peakY] End -static Function [WAVE/D peakX, WAVE/D peakY] PSX_FilterEventsKernelAmpSign(WAVE/Z peakXUnfiltered, WAVE/Z peakYUnfiltered, WAVE sweepDataOffFilt, variable kernelAmp, WAVE psxEvent) +static Function [WAVE/D peakX, WAVE/D peakY] PSX_FilterEventsKernelAmpSign(WAVE/Z peakXUnfiltered, WAVE/Z peakYUnfiltered, WAVE sweepDataOffFilt, variable kernelAmp, variable kernelRiseTau, variable kernelDecayTau, WAVE psxEvent) variable numCrossings, idx, i - variable post_min, post_min_t, pre_max, pre_max_t, rel_peak + variable peak, peak_t, baseline, baseline_t, amplitude variable overrideSignQC = NaN string comboKey @@ -502,7 +512,7 @@ static Function [WAVE/D peakX, WAVE/D peakY] PSX_FilterEventsKernelAmpSign(WAVE/ for(i = 0; i < numCrossings; i += 1) - [post_min, post_min_t, pre_max, pre_max_t, rel_peak] = PSX_CalculateEventProperties(peakXUnfiltered, peakYUnfiltered, sweepDataOffFilt, i, kernelAmp) + [peak, peak_t, baseline, baseline_t, amplitude] = PSX_CalculateEventProperties(peakXUnfiltered, peakYUnfiltered, sweepDataOffFilt, kernelAmp, kernelRiseTau, kernelDecayTau, i) #ifdef AUTOMATED_TESTING WAVE/Z overrideResults = GetOverrideResults() @@ -515,7 +525,7 @@ static Function [WAVE/D peakX, WAVE/D peakY] PSX_FilterEventsKernelAmpSign(WAVE/ #endif if(IsNaN(overrideSignQC)) - if(sign(rel_peak) != sign(kernelAmp)) + if(sign(amplitude) != sign(kernelAmp)) continue endif elseif(overrideSignQC == 0) @@ -537,54 +547,93 @@ static Function [WAVE/D peakX, WAVE/D peakY] PSX_FilterEventsKernelAmpSign(WAVE/ return [peakX, peakY] End -static Function [variable post_min, variable post_min_t, variable pre_max, variable pre_max_t, variable rel_peak] PSX_CalculateEventProperties(WAVE peakX, WAVE peakY, WAVE sweepDataOffFilt, variable index, variable kernelAmp) +static Function [variable peak_t, variable peak] PSX_CalculateEventPeak(WAVE peakX, WAVE peakY, WAVE sweepDataOffFilt, variable kernelAmp, variable kernelRiseTau, variable kernelDecayTau, variable index) - variable numCrossings, i_time, peak, peak_end_search + variable numCrossings, deconvPeak_t, prevDeconvPeak_t, nextDeconvPeak_t + variable peak_start_search, peak_end_search numCrossings = DimSize(peakX, ROWS) - i_time = peakX[index] - peak = peakY[index] + deconvPeak_t = peakX[index] + + // lower bound + if(index > 0) + prevDeconvPeak_t = peakX[index - 1] + else + prevDeconvPeak_t = -Inf + endif + + peak_start_search = max(deconvPeak_t - PSX_PEAK_RANGE_FACTOR_LEFT * kernelRiseTau, prevDeconvPeak_t) + // upper bound if(index < numCrossings - 1) - peak_end_search = min(i_time + PSX_DEFAULT_PEAK_SEARCH_RANGE_MS, peakX[index + 1]) + nextDeconvPeak_t = peakX[index + 1] else - peak_end_search = i_time + PSX_DEFAULT_PEAK_SEARCH_RANGE_MS + nextDeconvPeak_t = Inf + endif + + peak_end_search = min(deconvPeak_t + PSX_PEAK_RANGE_FACTOR_RIGHT * kernelDecayTau, nextDeconvPeak_t) + + WaveStats/M=1/Q/R=(peak_start_search, peak_end_search) sweepDataOffFilt + + if(kernelAmp > 0) + peak = V_max + peak_t = V_maxloc + elseif(kernelAmp < 0) + peak = V_min + peak_t = V_minloc + else + ASSERT(0, "Can't handle kernelAmp of zero") endif - WaveStats/M=1/Q/R=(i_time, peak_end_search) sweepDataOffFilt + return [peak_t, peak] +End + +static Function [variable baseline_t, variable baseline] PSX_CalculateEventBaseline(WAVE sweepDataOffFilt, variable peak_t, variable kernelAmp, variable kernelRiseTau) + + variable range + + WaveStats/M=1/Q/R=(peak_t - PSX_BASELINE_RANGE_FACTOR * kernelRiseTau, peak_t) sweepDataOffFilt if(kernelAmp > 0) - post_min = V_max - post_min_t = V_maxloc + baseline_t = V_minloc elseif(kernelAmp < 0) - post_min = V_min - post_min_t = V_minloc + baseline_t = V_maxloc else ASSERT(0, "Can't handle kernelAmp of zero") endif - WaveStats/Q/R=(i_time - 2, i_time) sweepDataOffFilt - pre_max = V_max - pre_max_t = V_maxloc + range = PSX_BASELINE_NUM_POINTS_AVERAGE * DimDelta(sweepDataOffFilt, ROWS) + WaveStats/M=1/Q/R=(baseline_t - range, baseline_t + range) sweepDataOffFilt + baseline = V_avg + + return [baseline_t, baseline] +End + +static Function [variable peak, variable peak_t, variable baseline, variable baseline_t, variable amplitude] PSX_CalculateEventProperties(WAVE peakX, WAVE peakY, WAVE sweepDataOffFilt, variable kernelAmp, variable kernelRiseTau, variable kernelDecayTau, variable index) + + variable deconvPeak, deconvPeak_t + + deconvPeak_t = peakX[index] + deconvPeak = peakY[index] - WaveStats/Q/R=(pre_max_t - 0.1, pre_max_t + 0.1) sweepDataOffFilt - pre_max = V_avg + [peak_t, peak] = PSX_CalculateEventPeak(peakX, peakY, sweepDataOffFilt, kernelAmp, kernelRiseTau, kernelDecayTau, index) + [baseline_t, baseline] = PSX_CalculateEventBaseline(sweepDataOffFilt, deconvPeak_t, kernelAmp, kernelRiseTau) - rel_peak = post_min - pre_max + amplitude = peak - baseline - return [post_min, post_min_t, pre_max, pre_max_t, rel_peak] + return [peak, peak_t, baseline, baseline_t, amplitude] End /// @brief Analyze the peaks -static Function [WAVE/D peakX, WAVE/D peakY] PSX_AnalyzePeaks(WAVE sweepDataOffFiltDeconv, WAVE sweepDataOffFilt, WAVE/Z peakXUnfiltered, WAVE/Z peakYUnfiltered, variable maxTauFactor, variable kernelAmp, WAVE psxEvent, WAVE eventFit) +static Function [WAVE/D peakX, WAVE/D peakY] PSX_AnalyzePeaks(WAVE sweepDataOffFiltDeconv, WAVE sweepDataOffFilt, WAVE sweepData, WAVE/Z peakXUnfiltered, WAVE/Z peakYUnfiltered, variable maxTauFactor, variable kernelAmp, variable kernelRiseTau, variable kernelDecayTau, WAVE riseTimeParams, WAVE psxEvent, WAVE eventFit) - variable i, i_time, peak, isi, post_min, post_min_t, pre_max, pre_max_t, numCrossings, rel_peak + variable i, numCrossings, deconvPeak, deconvPeak_t, peak, peak_t, baseline, baseline_t, amplitude, iei // we need to first throw away events with invalid amplitude so that // we can then calculate the distance to the neighbour in peakX[i + 1] below - [WAVE peakX, WAVE peakY] = PSX_FilterEventsKernelAmpSign(peakXUnfiltered, peakYUnfiltered, sweepDataOffFilt, kernelAmp, psxEvent) + [WAVE peakX, WAVE peakY] = PSX_FilterEventsKernelAmpSign(peakXUnfiltered, peakYUnfiltered, sweepDataOffFilt, kernelAmp, kernelRiseTau, kernelDecayTau, psxEvent) WaveClear peakXUnfiltered, peakYUnfiltered if(!WaveExists(peakX) || !WaveExists(peakY)) @@ -598,26 +647,27 @@ static Function [WAVE/D peakX, WAVE/D peakY] PSX_AnalyzePeaks(WAVE sweepDataOffF for(i = 0; i < numCrossings; i += 1) - i_time = peakX[i] - peak = peakY[i] + deconvPeak_t = peakX[i] + deconvPeak = peakY[i] - [post_min, post_min_t, pre_max, pre_max_t, rel_peak] = PSX_CalculateEventProperties(peakX, peakY, sweepDataOffFilt, i, kernelAmp) + [peak, peak_t, baseline, baseline_t, amplitude] = PSX_CalculateEventProperties(peakX, peakY, sweepDataOffFilt, \ + kernelAmp, kernelRiseTau, kernelDecayTau, i) if(i == 0) - isi = NaN + iei = NaN else - isi = i_time - psxEvent[i - 1][%peak_t] + iei = deconvPeak_t - psxEvent[i - 1][%deconvPeak_t] endif - psxEvent[i][%index] = i - psxEvent[i][%peak_t] = i_time - psxEvent[i][%peak] = peak - psxEvent[i][%post_min] = post_min - psxEvent[i][%post_min_t] = post_min_t - psxEvent[i][%pre_max] = pre_max - psxEvent[i][%pre_max_t] = pre_max_t - psxEvent[i][%rel_peak] = rel_peak - psxEvent[i][%isi] = isi + psxEvent[i][%index] = i + psxEvent[i][%deconvPeak] = deconvPeak + psxEvent[i][%deconvPeak_t] = deconvPeak_t + psxEvent[i][%peak] = peak + psxEvent[i][%peak_t] = peak_t + psxEvent[i][%baseline] = baseline + psxEvent[i][%baseline_t] = baseline_t + psxEvent[i][%amplitude] = amplitude + psxEvent[i][%iei] = iei endfor // safe defaults @@ -625,32 +675,86 @@ static Function [WAVE/D peakX, WAVE/D peakY] PSX_AnalyzePeaks(WAVE sweepDataOffF psxEvent[][%$"Fit manual QC call"] = PSX_UNDET psxEvent[][%$"Fit result"] = 0 + Multithread psxEvent[][%$"Rise Time"] = PSX_CalculateRiseTime(sweepDataOffFilt, psxEvent, kernelAmp, \ + riseTimeParams[%$"Lower Threshold"], \ + riseTimeParams[%$"Upper Threshold"], \ + p) + + Make/FREE/D/N=0 sweepDataDiff + Differentiate/EP=1 sweepDataOffFilt/D=sweepDataDiff + + Duplicate/O sweepDataDiff, root:SweepDataDiff + + // Also adds "Slew Rate" and "Slew Rate Time" + Multithread psxEvent[][%$"Onset Time"] = PSX_CalculateOnsetTime(sweepDataDiff, psxEvent, kernelAmp, \ + riseTimeParams[%$"Differentiate Threshold"], \ + p) + psxEvent[][%tau] = PSX_FitEventDecay(sweepDataOffFilt, psxEvent, maxTauFactor, eventFit, p) return [peakX, peakY] End +static Function PSX_GetGoodTau(WAVE psxEvent) + + string key + + key = CA_PSXEventGoodTauRange(psxEvent) + + WAVE/ZZ/D result = CA_TryFetchingEntryFromCache(key) + + if(!WaveExists(result)) + Make/FREE/D result = {PSX_GetGoodTauImpl(psxEvent)} + + CA_StoreEntryIntoCache(key, result, options = CA_OPTS_NO_DUPLICATE) + endif + + return result[0] +End + +// @brief Returns a good tau which does capture a lot of the tau events +static Function PSX_GetGoodTauImpl(WAVE psxEvent) + + variable numEvents, err, xVal, idx + + idx = FindDimLabel(psxEvent, COLS, "tau") + Duplicate/FREE/RMD=[][idx] psxEvent, tauWithNaN + + WAVE/Z tau = ZapNaNs(tauWithNaN) + + if(!WaveExists(tau)) + return PSX_DEFAULT_X_START_OFFSET + endif + + WaveStats/Q tau + + return V_avg + PSX_TAU_CALC_FACTOR * V_sdev +End + /// @brief Return the x-axis range useful for displaying and extracting a single event -static Function [variable first, variable last] PSX_GetSingleEventRange(WAVE psxEvent, variable index) +static Function [variable first, variable last] PSX_GetSingleEventRange(WAVE psxEvent, WAVE sweepDataOffFilt, variable index) - variable numEvents, offset + variable numEvents, offset, onset, baseline numEvents = DimSize(psxEvent, ROWS) index = limit(index, 0, numEvents - 1) - offset = PSX_DEFAULT_RANGE_FACTOR * psxEvent[index][%tau] + offset = PSX_GetGoodTau(psxEvent) - if(IsNaN(offset)) - offset = PSX_DEFAULT_X_START_OFFSET + onset = psxEvent[index][%$"Onset Time"] + baseline = psxEvent[index][%baseline_t] + + if(!IsNaN(onset)) + first = min(onset, baseline) + else + first = baseline endif if(index == numEvents - 1) - first = psxEvent[index][%peak_t] - offset - last = psxEvent[index][%post_min_t] + offset + last = min(first + offset, IndexToScale(sweepDataOffFilt, DimSize(sweepDataOffFilt, ROWS) - 1, ROWS)) else - first = psxEvent[index][%peak_t] - offset - last = psxEvent[index + 1][%peak_t] - 0.5 + last = min(first + offset, psxEvent[index + 1][%baseline_t]) endif return [first, last] @@ -663,14 +767,14 @@ static Function [variable start, variable stop] PSX_GetEventFitRange(WAVE sweepD variable calcLength, maxLength - start = psxEvent[eventIndex][%post_min_t] + start = psxEvent[eventIndex][%deconvPeak_t] - maxLength = 10 * JWN_GetNumberFromWaveNote(psxEvent, SF_META_USER_GROUP + PSX_JWN_PARAMETERS + "/psxKernel/decayTau") + maxLength = PSX_FIT_RANGE_FACTOR * JWN_GetNumberFromWaveNote(psxEvent, SF_META_USER_GROUP + PSX_JWN_PARAMETERS + "/psxKernel/decayTau") if(eventIndex == (DimSize(psxEvent, ROWS) - 1)) calcLength = maxLength else - calcLength = min((psxEvent[eventIndex + 1][%post_min_t] - start) * 0.9, maxLength) + calcLength = min((psxEvent[eventIndex + 1][%deconvPeak_t] - start) * PSX_FIT_RANGE_PERC, maxLength) endif if(calcLength == 0) @@ -694,10 +798,10 @@ End /// \endrst static Function PSX_FitEventDecay(WAVE sweepDataOffFilt, WAVE psxEvent, variable maxTauFactor, WAVE/WAVE eventFit, variable eventIndex) - variable post_min_t, n_min_t, err, decayTau, fitRange, overrideTau + variable startTime, endTime, err, decayTau, fitRange, overrideTau string comboKey - [post_min_t, n_min_t] = PSX_GetEventFitRange(sweepDataOffFilt, psxEvent, eventIndex) + [startTime, endTime] = PSX_GetEventFitRange(sweepDataOffFilt, psxEvent, eventIndex) DFREF currDFR = GetDataFolderDFR() SetDataFolder NewFreeDataFolder() @@ -708,7 +812,7 @@ static Function PSX_FitEventDecay(WAVE sweepDataOffFilt, WAVE psxEvent, variable Make/FREE/D/N=3 coefWave AssertOnAndClearRTError() - CurveFit/Q/N=1/NTHR=1/M=0/W=2 exp_XOffset, kwCWave=coefWave, sweepDataOffFilt(post_min_t, n_min_t)/D/C=constraints; err = GetRTError(1) + CurveFit/Q/N=1/NTHR=1/M=0/W=2 exp_XOffset, kwCWave=coefWave, sweepDataOffFilt(startTime, endTime)/D/C=constraints; err = GetRTError(1) WAVE fit = MakeWaveFree($"fit__free_") @@ -739,7 +843,7 @@ static Function PSX_FitEventDecay(WAVE sweepDataOffFilt, WAVE psxEvent, variable return NaN endif - fitRange = n_min_t - post_min_t + fitRange = endTime - startTime if(IsFinite(decayTau) && decayTau > maxTauFactor * fitRange) psxEvent[eventIndex][%$"Fit manual QC call"] = PSX_REJECT @@ -845,7 +949,7 @@ static Function PSX_OperationSweepGathering(string graph, WAVE/WAVE psxKernelDat End /// @brief Implementation of psx operation -static Function PSX_OperationImpl(string graph, variable parameterJSONID, string id, variable peakThresh, variable maxTauFactor, WAVE riseTimeParams, variable kernelAmp, variable index, WAVE/WAVE output) +static Function PSX_OperationImpl(string graph, variable parameterJSONID, string id, variable peakThresh, variable maxTauFactor, WAVE riseTimeParams, variable kernelAmp, variable kernelRiseTau, variable kernelDecayTau, variable index, WAVE/WAVE output) string comboKey, key, psxOperationKey, psxParametersEvents @@ -883,7 +987,12 @@ static Function PSX_OperationImpl(string graph, variable parameterJSONID, string JWN_SetStringInWaveNote(psxEvent, PSX_X_DATA_UNIT, WaveUnits(sweepData, ROWS)) JWN_SetStringInWaveNote(psxEvent, PSX_Y_DATA_UNIT, WaveUnits(sweepData, -1)) - [WAVE peakX, WAVE peakY] = PSX_AnalyzePeaks(sweepDataOffFiltDeconv, sweepDataOffFilt, peakXUnfiltered, peakYUnfiltered, maxTauFactor, kernelAmp, psxEvent, eventFit) + [WAVE peakX, WAVE peakY] = PSX_AnalyzePeaks(sweepDataOffFiltDeconv, sweepDataOffFilt, \ + sweepData, \ + peakXUnfiltered, peakYUnfiltered, \ + maxTauFactor, kernelAmp, kernelRiseTau, \ + kernelDecayTau, riseTimeParams, \ + psxEvent, eventFit) Make/FREE/WAVE/N=(4) psxOperation SetDimensionLabels(psxOperation, "peakX;peakY;psxEvent;eventFit", ROWS) @@ -905,7 +1014,7 @@ static Function PSX_OperationImpl(string graph, variable parameterJSONID, string WAVE/Z psxEventFromCache = PSX_LoadEventsFromCache(comboKey, psxParametersEvents) if(WaveExists(psxEventFromCache)) - WAVE psxEvent = psxEventFromCache + WAVE/Z psxEventCandidate = UpgradePSXEventWave(psxEventFromCache) else // no cached psxEvent data exists // look into the results wave @@ -913,17 +1022,14 @@ static Function PSX_OperationImpl(string graph, variable parameterJSONID, string WAVE/Z psxEventFromResults = PSX_FilterEventContainer(psxEventContainer, comboKey) if(WaveExists(psxEventFromResults)) - WAVE psxEvent = psxEventFromResults + WAVE/Z psxEventCandidate = UpgradePSXEventWave(psxEventFromResults) endif endif - if(WaveExists(psxEvent)) - UpgradePSXEventWave(psxEvent) - - WAVE riseTime = PSX_CalculateRiseTime(psxEvent, sweepDataOffFilt, parameterJsonID, kernelAmp, riseTimeParams[%$"Lower Threshold"], riseTimeParams[%$"Upper Threshold"]) - ASSERT(DimSize(riseTime, ROWS) == DimSize(psxEvent, ROWS), "Unmatched number of rows for rise time") - psxEvent[][%$"Rise Time"] = riseTime[p] - WaveClear riseTime + if(WaveExists(psxEventCandidate) \ + && DimSize(peakX, ROWS) == DimSize(psxEventCandidate, ROWS) \ + && DimSize(peakY, ROWS) == DimSize(psxEventCandidate, ROWS)) + WAVE psxEvent = psxEventCandidate endif key = PSX_GenerateKey("peakX", index) @@ -1019,13 +1125,28 @@ static Function [WAVE/D results, WAVE eventIndex, WAVE marker, WAVE/T comboKeys] strswitch(prop) case "amp": - propLabel = "rel_peak" + propLabel = "amplitude" + break + case "peak": + propLabel = "peak" break - case "xpos": + case "peaktime": propLabel = "peak_t" break + case "deconvpeak": + propLabel = "deconvpeak" + break + case "deconvpeaktime": + propLabel = "deconvpeak_t" + break + case "baseline": + propLabel = "baseline" + break + case "baselinetime": + propLabel = "baseline_t" + break case "xinterval": - propLabel = "isi" + propLabel = "iei" break case "tau": propLabel = "tau" @@ -1039,20 +1160,37 @@ static Function [WAVE/D results, WAVE eventIndex, WAVE marker, WAVE/T comboKeys] case "fitresult": propLabel = "Fit result" break + case "slewrate": + propLabel = "Slew Rate" + break + case "slewratetime": + propLabel = "Slew Rate Time" + break case "risetime": propLabel = "Rise Time" break + case "onsettime": + propLabel = "Onset Time" + break default: - ASSERT(0, "Impossible prop") + ASSERT(0, "Impossible prop: " + prop) endswitch // use the correct event/fit state for the property strswitch(propLabel) - case "rel_peak": + case "amplitude": + case "peak": case "peak_t": - case "isi": + case "deconvPeak": + case "deconvPeak_t": + case "baseline": + case "baseline_t": + case "iei": case "Event manual QC call": + case "Slew Rate": + case "Slew Rate Time": case "Rise Time": + case "Onset Time": stateType = "Event manual QC call" break case "Fit result": @@ -1061,7 +1199,7 @@ static Function [WAVE/D results, WAVE eventIndex, WAVE marker, WAVE/T comboKeys] stateType = "Fit manual QC call" break default: - ASSERT(0, "Unknown propLabel") + ASSERT(0, "Unknown propLabel: " + propLabel) endswitch Make/FREE/N=0 allEventIndex, allMarkers @@ -1083,9 +1221,9 @@ static Function [WAVE/D results, WAVE eventIndex, WAVE marker, WAVE/T comboKeys] Redimension/N=(numEntries) results, marker, eventIndex, comboKeys - if(!cmpstr(propLabel, "isi") && numEntries >= 2) - // recalculate the isi as that might have changed due to in-between events being not selected - Multithread results[0, numEntries - 1] = events[indizes[p]][%peak_t] - (p >= 1 ? events[indizes[p - 1]][%peak_t] : NaN) + if(!cmpstr(propLabel, "iei") && numEntries >= 2) + // recalculate the iei as that might have changed due to in-between events being not selected + Multithread results[0, numEntries - 1] = events[indizes[p]][%deconvPeak_t] - (p >= 1 ? events[indizes[p - 1]][%deconvPeak_t] : NaN) else Multithread results[] = events[indizes[p]][%$propLabel] endif @@ -1308,9 +1446,24 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r case "amp": propLabelAxis = "Amplitude" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_Y_DATA_UNIT) + ")" break - case "xpos": + case "peak": + propLabelAxis = "Event" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_Y_DATA_UNIT) + ")" + break + case "peaktime": propLabelAxis = "Event time" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" break + case "deconvpeak": + propLabelAxis = "Deconvoluted peak" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_Y_DATA_UNIT) + ")" + break + case "deconvpeaktime": + propLabelAxis = "Deconvoluted peak time" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" + break + case "baseline": + propLabelAxis = "Baseline" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_Y_DATA_UNIT) + ")" + break + case "baselinetime": + propLabelAxis = "Baseline time" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" + break case "xinterval": propLabelAxis = "Event interval" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" break @@ -1326,11 +1479,21 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r case "fitresult": propLabelAxis = "Fit result" + " (0/1)" break + case "slewrate": + propLabelAxis = "Slew Rate" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_Y_DATA_UNIT) + ")" + break + case "slewratetime": + propLabelAxis = "Slew Rate time" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" + break case "risetime": propLabelAxis = "Rise time" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" break + case "onsettime": + propLabelAxis = "Onset time" + " (" + JWN_GetStringFromWaveNote(allEvents[0], PSX_X_DATA_UNIT) + ")" + break + default: - ASSERT(0, "Impossible prop") + ASSERT(0, "Impossible prop: " + prop) endswitch if(!cmpstr(stateAsStr, "every")) @@ -1469,6 +1632,7 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r JWN_SetNumberInWaveNote(results, SF_META_SWEEPNO, sweepNo) JWN_SetNumberInWaveNote(results, SF_META_CHANNELTYPE, chanType) JWN_SetNumberInWaveNote(results, SF_META_CHANNELNUMBER, chanNr) + JWN_SetNumberInWaveNote(results, SF_META_SWEEPMAPINDEX, mapIndex) ASSERT(DimSize(results, ROWS) <= DimSize(marker, ROWS), "results wave got larger unexpectedly") Redimension/N=(DimSize(results, ROWS)) marker, comboKeys @@ -1528,44 +1692,19 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r return output End -static Function/WAVE PSX_CalculateRiseTime(WAVE psxEvent, WAVE sweepDataOffFilt, variable parameterJsonID, variable kernelAmp, variable lowerThreshold, variable upperThreshold) - - string psxParameters, comboKey, cacheKey - variable numEvents - - psxParameters = PSX_GetPSXParameters(parameterJsonID, PSX_CACHE_KEY_RISETIME) - comboKey = JWN_GetStringFromWaveNote(psxEvent, PSX_EVENTS_COMBO_KEY_WAVE_NOTE) - - cacheKey = CA_PSXRiseTimeKey(comboKey, psxParameters) - WAVE/Z riseTimeFromCache = CA_TryFetchingEntryFromCache(cacheKey) - - if(WaveExists(riseTimeFromCache)) - return riseTimeFromCache - endif - - numEvents = DimSize(psxEvent, ROWS) - - Make/D/FREE/N=(numEvents) riseTime - - Multithread riseTime[] = PSX_CalculateRiseTimeImpl(psxEvent, sweepDataOffFilt, kernelAmp, psxEvent[p][%index], \ - lowerThreshold, upperThreshold) - - CA_StoreEntryIntoCache(cacheKey, riseTime) - - return riseTime -End - -threadsafe static Function PSX_CalculateRiseTimeImpl(WAVE psxEvent, WAVE sweepDataOffFilt, variable kernelAmp, variable index, variable lowerThreshold, variable upperThreshold) +threadsafe static Function PSX_CalculateRiseTime(WAVE sweepDataOffFilt, WAVE psxEvent, variable kernelAmp, variable lowerThreshold, variable upperThreshold, variable index) variable dY, xStart, xEnd, yStart, yEnd, xlt, xupt, lowerLevel, upperLevel, riseTime variable printDebug string comboKey - xStart = psxEvent[index][%peak_t] + // deconvPeak is defined in the deconvoluted wave, + // so we can't use %deconvPeak as y-value + xStart = psxEvent[index][%deconvPeak_t] yStart = sweepDataOffFilt(xStart) - xEnd = psxEvent[index][%post_min_t] - yEnd = psxEvent[index][%post_min] + xEnd = psxEvent[index][%peak_t] + yEnd = psxEvent[index][%peak] dY = abs(yStart - yEnd) @@ -1606,6 +1745,49 @@ threadsafe static Function PSX_CalculateRiseTimeImpl(WAVE psxEvent, WAVE sweepDa return riseTime End +threadsafe static Function PSX_CalculateOnsetTime(WAVE sweepDataDiff, WAVE psxEvent, variable kernelAmp, variable diffThreshPerc, variable index) + + variable slewRate, slewRate_t, level, baseline_t, peak_t + string msg + + peak_t = psxEvent[index][%peak_t] + baseline_t = psxEvent[index][%baseline_t] + + WaveStats/Q/M=1/R=(peak_t, baseline_t) sweepDataDiff + if(kernelAmp > 0) + slewRate = V_max + slewRate_t = V_maxLoc + elseif(kernelAmp < 0) + slewRate = V_min + slewRate_t = V_minLoc + else + ASSERT_TS(0, "Unsupported case: kernelAmp being zero") + endif + + psxEvent[index][%$"Slew Rate"] = slewRate + psxEvent[index][%$"Slew Rate Time"] = slewRate_t + + level = diffThreshPerc * (slewRate - sweepDataDiff(baseline_t)) + +#ifdef DEBUGGING_ENABLED + sprintf msg, "comboKey = %s\r", JWN_GetStringFromWaveNote(psxEvent, PSX_EVENTS_COMBO_KEY_WAVE_NOTE) + DEBUGPRINT_TS(msg) + sprintf msg, "index = %d, peak_t = %g, baseline_t = %g, slew rate = %g, slew rate time = %g\r", index, peak_t, baseline_t, slewRate, slewRate_t + DEBUGPRINT_TS(msg) + sprintf msg, "level = %g, [%g, %g]\r", level, slewRate_t, baseline_t + DEBUGPRINT_TS(msg) +#endif + + // search backwards in time + FindLevel/R=(slewRate_t, baseline_t)/Q sweepDataDiff, level + + if(V_flag) + return NaN + endif + + return V_levelX +End + /// @brief Return all possible fit/event states /// /// @param withAllState [optional, defaults to false] choose to include #PSX_ALL (true) or not (false) @@ -1743,8 +1925,10 @@ static Function PSX_UpdateSingleEventGraph(string win, variable index) PSX_UpdateSingleEventTextbox(extSingleGraph, eventIndex = index) - WAVE psxEvent = GetPSXEventWaveFromDFR(comboDFR) - [first, last] = PSX_GetSingleEventRange(psxEvent, index) + WAVE psxEvent = GetPSXEventWaveFromDFR(comboDFR) + WAVE sweepDataOffFilt = GetPSXSweepDataOffFiltWaveFromDFR(comboDFR) + + [first, last] = PSX_GetSingleEventRange(psxEvent, sweepDataOffFilt, index) WAVE singleEventFit = GetPSXSingleEventFitWaveFromDFR(comboDFR) @@ -1817,17 +2001,21 @@ static Function PSX_UpdateOffsetInAllEventGraph(string win) WAVE/Z/SDFR=singleEventDFR singleEvent = $GetIndexedObjNameDFR(singleEventDFR, COUNTOBJECTS_WAVES, i) ASSERT(WaveExists(singleEvent), "Non-existing single event wave") - [first, last] = PSX_GetSingleEventRange(psxEvent, i) + [first, last] = PSX_GetSingleEventRange(psxEvent, sweepDataOffFilt, i) Duplicate/FREE/R=(first, last) sweepDataOffFilt, singleEventRaw switch(offsetMode) case PSX_HORIZ_OFFSET_ONSET: - xOffset = 0 - yOffset = sweepDataOffFilt(psxEvent[i][%peak_t]) + xOffset = IsFinite(psxEvent[i][%$"Onset Time"]) ? first - psxEvent[i][%$"Onset Time"] : 0 + yOffset = 0 break case PSX_HORIZ_OFFSET_PEAK: - xOffset = first - psxEvent[i][%post_min_t] + xOffset = first - psxEvent[i][%peak_t] + yOffset = 0 + break + case PSX_HORIZ_OFFSET_SLEW: + xOffset = first - psxEvent[i][%$"Slew Rate Time"] yOffset = 0 break default: @@ -2015,7 +2203,7 @@ static Function PSX_UpdateAverageTraces(string win, WAVE/T eventIndexFromTraces, numEvents = DimSize(eventIndexFromTraces, ROWS) Make/WAVE/FREE/N=(numEvents) contAverageAll, contAverageAccept, contAverageReject, contAverageUndet - Make/FREE/D/N=(numEvents) eventStartTime, eventStopTime + Make/FREE/D/N=(numEvents) eventStopTime for(i = 0; i < numEvents; i += 1) idx = str2num(eventIndexFromTraces[i]) @@ -2036,11 +2224,7 @@ static Function PSX_UpdateAverageTraces(string win, WAVE/T eventIndexFromTraces, WAVE psxEvent = GetPSXEventWaveFromDFR(comboDFR) // single event waves are zeroed in x-direction to extractStartAbs - [extractStartAbs, extractStopAbs] = PSX_GetSingleEventRange(psxEvent, idx) - fitStartAbs = psxEvent[idx][%peak_t] - ASSERT(fitStartAbs > extractStartAbs, "Unexpected fit/extraction start positions") - - eventStartTime[acceptIndex] = fitStartAbs - extractStartAbs + [extractStartAbs, extractStopAbs] = PSX_GetSingleEventRange(psxEvent, sweepDataOffFilt, idx) eventStopTime[acceptIndex] = extractStopAbs - extractStartAbs acceptIndex += 1 @@ -2067,8 +2251,8 @@ static Function PSX_UpdateAverageTraces(string win, WAVE/T eventIndexFromTraces, PSX_UpdateAverageWave(contAverageUndet, undetIndex, averageDFR, PSX_UNDET) PSX_UpdateAverageWave(contAverageAll, numEvents, averageDFR, PSX_ALL) - Redimension/N=(acceptIndex) eventStartTime, eventStopTime - PSX_FitAcceptAverage(win, averageDFR, eventStartTime, eventStopTime) + Redimension/N=(acceptIndex) eventStopTime + PSX_FitAcceptAverage(win, averageDFR, eventStopTime) End /// @brief Helper function to update the average waves for the all event graph @@ -2092,10 +2276,10 @@ static Function/DF PSX_GetAverageFolder(string win) endif End -static Function PSX_FitAcceptAverage(string win, DFREF averageDFR, WAVE eventStartTime, WAVE eventStopTime) +static Function PSX_FitAcceptAverage(string win, DFREF averageDFR, WAVE eventStopTime) string specialEventPanel, str, htmlStr, rawCode, browser, msg, fitFunc - variable err, numAveragePoints, start, stop + variable err, numAveragePoints, start, stop, meanStopTime WAVE acceptedAverageFit = GetPSXAcceptedAverageFitWaveFromDFR(averageDFR) @@ -2120,8 +2304,15 @@ static Function PSX_FitAcceptAverage(string win, DFREF averageDFR, WAVE eventSta FastOp acceptedAverageFit = (NaN) CopyScales average, acceptedAverageFit - start = max(0, mean(eventStartTime)) - stop = min(IndexToScale(average, DimSize(average, ROWS) - 1, ROWS), mean(eventStopTime)) + WAVE/Z eventStopTimeClean = ZapNaNs(eventStopTime) + if(WaveExists(eventStopTimeClean)) + meanStopTime = mean(eventStopTime) + else + meanStopTime = Inf + endif + + start = 0 + stop = min(IndexToScale(average, DimSize(average, ROWS) - 1, ROWS), meanStopTime) AssertOnAndClearRTError() fitFunc = GetPopupMenuString(specialEventPanel, "popupmenu_accept_fit_function") @@ -2207,10 +2398,6 @@ static Function/S PSX_GetPSXParameters(variable jsonID, variable cacheKeyType) switch(cacheKeyType) case PSX_CACHE_KEY_EVENTS: case PSX_CACHE_KEY_ANALYZE_PEAKS: - // remove riseTime as that does not influence the found events or the results from PSX_AnalyzePeaks - JSON_Remove(subJsonID, SF_OP_PSX_RISETIME) - break - case PSX_CACHE_KEY_RISETIME: // do nothing break default: @@ -2252,9 +2439,9 @@ static Function/WAVE PSX_LoadEventsFromCache(string comboKey, string psxParamete return $"" endif - UpgradePSXEventWave(psxEvent) + WAVE/Z psxEventUpgraded = UpgradePSXEventWave(psxEvent) - return psxEvent + return psxEventUpgraded End /// @brief Return the trace user data keys/values wave for the given trace type @@ -2626,15 +2813,18 @@ static Function PSX_UpdateSingleEventTextbox(string win, [variable eventIndex]) Make/FREE/T/N=(8, 2) input - input[0][0] = {"Event State:", "Fit State:", "Fit Result:", "Event:", "Position:", "IsI:", "Amp (rel.):", "Tau:", "Rise time:"} - input[0][1] = {PSX_StateToString(psxEvent[eventIndex][%$"Event manual QC call"]), \ - PSX_StateToString(psxEvent[eventIndex][%$"Fit manual QC call"]), \ - PSX_FitResultToString(psxEvent[eventIndex][%$"Fit Result"]), \ - num2istr(eventIndex), \ - num2str(psxEvent[eventIndex][%peak_t], "%8.02f") + " [ms]", \ - num2str(psxEvent[eventIndex][%isi], "%8.02f") + " [" + yUnit + "]", \ - num2str(psxEvent[eventIndex][%rel_peak], "%8.02f") + " [" + yUnit + "]", \ - num2str(psxEvent[eventIndex][%tau], "%8.02f") + " [ms]", \ + input[0][0] = {"Event State:", "Fit State:", "Fit Result:", "Event:", "Deconv Peak:", "Peak:", "Baseline:", "IeI:", "Amp (rel.):", "Tau:", "Onset time:", "Rise time:"} + input[0][1] = {PSX_StateToString(psxEvent[eventIndex][%$"Event manual QC call"]), \ + PSX_StateToString(psxEvent[eventIndex][%$"Fit manual QC call"]), \ + PSX_FitResultToString(psxEvent[eventIndex][%$"Fit Result"]), \ + num2istr(eventIndex), \ + num2str(psxEvent[eventIndex][%deconvPeak_t], "%8.02f") + " [ms]", \ + num2str(psxEvent[eventIndex][%peak_t], "%8.02f") + " [ms]", \ + num2str(psxEvent[eventIndex][%baseline_t], "%8.02f") + " [ms]", \ + num2str(psxEvent[eventIndex][%iei], "%8.02f") + " [ms]", \ + num2str(psxEvent[eventIndex][%amplitude], "%8.02f") + " [" + yUnit + "]", \ + num2str(psxEvent[eventIndex][%tau], "%8.02f") + " [ms]", \ + num2str(psxEvent[eventIndex][%$"Onset Time"], "%8.02f") + " [ms]", \ num2str(psxEvent[eventIndex][%$"Rise Time"], "%8.02f") + " [ms]"} str = "\F'Consolas'" + FormatTextWaveForLegend(input) @@ -3172,6 +3362,7 @@ End static Function/WAVE PSX_GetEventContainerFromResults(string id) string entry, name + variable idx, hasValidWave WAVE/T textualResultsValues = GetLogbookWaves(LBT_RESULTS, LBN_TEXTUAL_VALUES) @@ -3188,10 +3379,23 @@ static Function/WAVE PSX_GetEventContainerFromResults(string id) for(WAVE/Z psxEvent : container) ASSERT(WaveExists(psxEvent), "Missing psxEvent") - UpgradePSXEventWave(psxEvent) + WAVE/Z psxEventUpgraded = UpgradePSXEventWave(psxEvent) + + if(WaveExists(psxEventUpgraded)) + hasValidWave = 1 + endif + + container[idx] = psxEventUpgraded + idx += 1 endfor - return container + ASSERT(idx > 0, "Expected at least one entry in container") + + if(hasValidWave) + return container + endif + + return $"" End static Function/WAVE PSX_FilterEventContainer(WAVE/Z/WAVE eventContainer, string refComboKey) @@ -3283,13 +3487,16 @@ End /// @brief Store the PSX panel GUI state in the window user data of `browser` static Function PSX_StoreGuiState(string win, string browser) - variable jsonID, childID + variable jsonID, childID, latestPanelVersion string specialEventPanel, mainWindow, ctrl, extAllGraph extAllGraph = PSX_GetAllEventGraph(win) specialEventPanel = PSX_GetSpecialPanel(win) - if(IsEmpty(browser) \ + latestPanelVersion = HasPanelLatestVersion(win, PSX_PLOT_PANEL_VERSION) + + if(!latestPanelVersion \ + || IsEmpty(browser) \ || !WindowExists(browser) \ || !WindowExists(extAllGraph) \ || !WindowExists(specialEventPanel)) @@ -3577,7 +3784,7 @@ static Function PSX_CreateSingleEventWaves(DFREF comboDFR, WAVE psxEvent, WAVE s for(i = 0; i < numEvents; i += 1) - [first, last] = PSX_GetSingleEventRange(psxEvent, i) + [first, last] = PSX_GetSingleEventRange(psxEvent, sweepDataOffFilt, i) Duplicate/FREE/R=(first, last) sweepDataOffFilt, singleEvent @@ -3648,6 +3855,8 @@ static Function PSX_CreatePSXGraphAndSubwindows(string win, string graph, STRUCT PSX_ApplyMacroToExistingPanel(mainWin, "PSXPanel") + AddVersionToPanel(mainWin, PSX_PLOT_PANEL_VERSION) + DFREF workDFR = PSX_GetWorkingFolder(win) DFREF comboDFR = GetPSXFolderForCombo(workDFR, 0) @@ -3914,6 +4123,13 @@ static Function [variable eventIndex, variable waveIndex, variable comboIndex] P return [eventIndex, yPointNumber, comboIndex] End +static Function PSX_AbortWithOldPanel(string win) + + if(!HasPanelLatestVersion(win, PSX_PLOT_PANEL_VERSION)) + DoAbortNow("Can not continue with this psx plot. The psx panel is too old to be usable. Please close it and open a new one.") + endif +End + /// @brief Window hook responsible for keyboard and mouse support /// /// Works with `psx` and `psxStats` graphs. @@ -3930,10 +4146,12 @@ Function PSX_PlotInteractionHook(STRUCT WMWinHookStruct &s) break endif - win = s.winName - eventIndex = s.pointNumber + win = s.winName - psxGraph = PSX_GetPSXGraph(win) + PSX_AbortWithOldPanel(win) + + eventIndex = s.pointNumber + psxGraph = PSX_GetPSXGraph(win) if(!cmpstr(win, psxGraph)) PSX_UpdateSingleEventGraph(psxGraph, eventIndex) @@ -3962,6 +4180,7 @@ Function PSX_PlotInteractionHook(STRUCT WMWinHookStruct &s) case EVENT_WINDOW_HOOK_KEYBOARD: win = s.winName + PSX_AbortWithOldPanel(win) // workaround IP bug where the currently selected graph is not in s.winName GetWindow $win, activeSW @@ -4032,6 +4251,8 @@ Function PSX_PlotInteractionHook(STRUCT WMWinHookStruct &s) break endif + PSX_AbortWithOldPanel(win) + // psxGraph if((s.eventMod & WINDOW_HOOK_EMOD_CTRLKEYDOWN) == WINDOW_HOOK_EMOD_CTRLKEYDOWN) DEBUGPRINT("Left mouse click and CTRL") @@ -4346,8 +4567,8 @@ End Function/WAVE PSX_Operation(variable jsonId, string jsonPath, string graph) variable numberOfSDs, sweepFilterLow, sweepFilterHigh, parameterJsonID, numCombos, i, addedData, kernelAmp - variable maxTauFactor, peakThresh, idx, success - string parameterPath, id, psxParameters, dataUnit + variable maxTauFactor, peakThresh, idx, success, kernelRiseTau, kernelDecayTau + string parameterPath, id, psxParameters, dataUnit, path id = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX, 0, checkFunc = IsValidObjectName) @@ -4374,6 +4595,7 @@ Function/WAVE PSX_Operation(variable jsonId, string jsonPath, string graph) JSON_AddTreeObject(parameterJsonID, parameterPath) JSON_AddVariable(parameterJsonID, parameterPath + "/upperThreshold", riseTime[%$"Upper Threshold"]) JSON_AddVariable(parameterJsonID, parameterPath + "/lowerThreshold", riseTime[%$"Lower Threshold"]) + JSON_AddVariable(parameterJsonID, parameterPath + "/differentiateThreshold", riseTime[%$"Differentiate Threshold"]) parameterPath = SF_META_USER_GROUP + PSX_JWN_PARAMETERS + "/" + SF_OP_PSX_DECONV_FILTER JSON_AddTreeObject(parameterJsonID, parameterPath) JSON_AddVariable(parameterJsonID, parameterPath + "/filterLow", deconvFilter[%$"Filter Low"]) @@ -4385,8 +4607,13 @@ Function/WAVE PSX_Operation(variable jsonId, string jsonPath, string graph) WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_PSX, numCombos * PSX_OPERATION_OUTPUT_WAVES_PER_ENTRY) - kernelAmp = JWN_GetNumberFromWaveNote(psxKernelDataset, SF_META_USER_GROUP + PSX_JWN_PARAMETERS + "/" + SF_OP_PSX_KERNEL + "/amp") + path = SF_META_USER_GROUP + PSX_JWN_PARAMETERS + "/" + SF_OP_PSX_KERNEL + kernelAmp = JWN_GetNumberFromWaveNote(psxKernelDataset, path + "/amp") ASSERT(IsFinite(kernelAmp), "psxKernel amplitude must be finite") + kernelRiseTau = JWN_GetNumberFromWaveNote(psxKernelDataset, path + "/riseTau") + ASSERT(IsFinite(kernelRiseTau), "riseTau must be finite") + kernelDecayTau = JWN_GetNumberFromWaveNote(psxKernelDataset, path + "/decayTau") + ASSERT(IsFinite(kernelDecayTau), "decayTau must be finite") WAVE/T labelsTemplate = ListToTextWave(PSX_EVENT_DIMENSION_LABELS, ";") ASSERT(DimSize(labelsTemplate, ROWS) == PSX_OPERATION_OUTPUT_WAVES_PER_ENTRY, "Mismatched label wave") @@ -4411,7 +4638,7 @@ Function/WAVE PSX_Operation(variable jsonId, string jsonPath, string graph) WaveClear hist, fit for(i = 0; i < numCombos; i += 1) - PSX_OperationImpl(graph, parameterJsonID, id, peakThresh, maxTauFactor, riseTime, kernelAmp, i, output) + PSX_OperationImpl(graph, parameterJsonID, id, peakThresh, maxTauFactor, riseTime, kernelAmp, kernelRiseTau, kernelDecayTau, i, output) endfor catch if(WaveExists(output)) @@ -4455,6 +4682,8 @@ Function/WAVE PSX_OperationKernel(variable jsonId, string jsonPath, string graph decayTau = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 2, defValue = 15, checkFunc = IsStrictlyPositiveAndFinite) amp = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 3, defValue = -5, checkFunc = IsFinite) + SFH_ASSERT(decayTau > riseTau, "decay tau must be strictly larger than the rise tau") + SFH_ASSERT(DimSize(selectDataCompArray, ROWS) == 1, "Only supports a single selection at the moment") WAVE/WAVE selectDataComp = selectDataCompArray[0] @@ -4527,15 +4756,16 @@ End Function/WAVE PSX_OperationRiseTime(variable jsonId, string jsonPath, string graph) - variable lowerThreshold, upperThreshold + variable lowerThreshold, upperThreshold, differentiateThreshold - SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_PSX_RISETIME, 0, maxArgs = 2) + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_PSX_RISETIME, 0, maxArgs = 3) - lowerThreshold = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_RISETIME, 0, defValue = 20, checkFunc = BetweenZeroAndOneHoundredExc) - upperThreshold = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_RISETIME, 1, defValue = 80, checkFunc = BetweenZeroAndOneHoundredExc) + lowerThreshold = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_RISETIME, 0, defValue = 20, checkFunc = BetweenZeroAndOneHoundredExc) + upperThreshold = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_RISETIME, 1, defValue = 80, checkFunc = BetweenZeroAndOneHoundredExc) + differentiateThreshold = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_RISETIME, 2, defValue = 5, checkFunc = BetweenZeroAndOneHoundredExc) - Make/D/FREE thresholds = {lowerThreshold / ONE_TO_PERCENT, upperThreshold / ONE_TO_PERCENT} - SetDimensionLabels(thresholds, "Lower Threshold;Upper Threshold", ROWS) + Make/D/FREE thresholds = {lowerThreshold / ONE_TO_PERCENT, upperThreshold / ONE_TO_PERCENT, differentiateThreshold / ONE_TO_PERCENT} + SetDimensionLabels(thresholds, "Lower Threshold;Upper Threshold;Differentiate Threshold", ROWS) WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_PSX_RISETIME, 1) @@ -4552,7 +4782,7 @@ Function/WAVE PSX_OperationDeconvFilter(variable jsonId, string jsonPath, string low = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_DECONV_FILTER, 0, defValue = NaN, checkFunc = IsNullOrPositiveAndFinite, checkDefault = 0) high = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_DECONV_FILTER, 1, defValue = NaN, checkFunc = IsNullOrPositiveAndFinite, checkDefault = 0) - order = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_DECONV_FILTER, 2, defValue = NaN, checkFunc = IsOdd, checkDefault = 0) + order = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_PSX_DECONV_FILTER, 2, defValue = NaN, checkDefault = 0) Make/D/FREE params = {low, high, order} SetDimensionLabels(params, "Filter Low;Filter High;Filter Order", ROWS) @@ -4564,6 +4794,21 @@ Function/WAVE PSX_OperationDeconvFilter(variable jsonId, string jsonPath, string return SFH_GetOutputForExecutor(output, graph, SF_OP_PSX_DECONV_FILTER) End +static Function/WAVE PSX_GetAllStatsProperties() + + Make/FREE/T allProps = {"amp", \ + "peak", "peaktime", \ + "deconvpeak", "deconvpeaktime", \ + "baseline", "baselinetime", \ + "xinterval", \ + "tau", \ + "estate", "fstate", "fitresult", \ + "slewrate", "slewratetime", \ + "risetime", "onsettime"} + + return allProps +End + Function/WAVE PSX_OperationStats(variable jsonId, string jsonPath, string graph) string stateAsStr, postProc, id, prop @@ -4580,7 +4825,7 @@ Function/WAVE PSX_OperationStats(variable jsonId, string jsonPath, string graph) WAVE/Z selectData = selectDataComp[%SELECTION] WAVE/WAVE range = selectDataComp[%RANGE] - Make/FREE/T allProps = {"amp", "xpos", "xinterval", "tau", "estate", "fstate", "fitresult", "risetime"} + WAVE allProps = PSX_GetAllStatsProperties() prop = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 2, allowedValues = allProps) Make/FREE/T allStates = {"accept", "reject", "undetermined", "all", "every"} stateAsStr = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 3, allowedValues = allStates) @@ -4659,7 +4904,7 @@ static Function [WAVE hist, WAVE fit, variable peakThresh, string dataUnit] PSX_ [WAVE coef, WAVE fit] = PSX_FitHistogram(hist) if(WaveExists(coef) && WaveExists(fit)) - peakThresh = RoundNumber(coef[3] * numSDs, 3) + peakThresh = coef[3] * numSDs // RoundNumber(coef[3] * numSDs, 3) dataUnit = WaveUnits(sweepDataOffFiltDeconv, -1) return [hist, fit, peakThresh, dataUnit] diff --git a/Packages/MIES/MIES_SweepFormula_PSX_Macro.ipf b/Packages/MIES/MIES_SweepFormula_PSX_Macro.ipf index 30d2cd5c39..8afc551a11 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX_Macro.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX_Macro.ipf @@ -126,7 +126,7 @@ Window PSXPanel() : Panel PopupMenu popup_block, mode=1, popvalue="", value=#"\"\"" PopupMenu popupmenu_event_offset, pos={136.00, 168.00}, size={53.00, 19.00}, proc=PSX_PopupMenuState PopupMenu popupmenu_event_offset, help={"Select the time point in x direction for aligning the single event traces in the all event graph"} - PopupMenu popupmenu_event_offset, mode=1, popvalue="Onset", value=#"\"Onset;Peak\"" + PopupMenu popupmenu_event_offset, mode=1, popvalue="Onset", value=#"\"Onset;Peak;Slew\"" DefineGuide leftMenu={FL, 0.2, FR}, horizCenter={leftMenu, 0.5, FR} SetWindow kwTopWin, hook(resetScaling)=IH_ResetScaling SetWindow kwTopWin, hook(ctrl)=PSX_AllEventGraphHook diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index bc87b11667..24aa398c09 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -8191,18 +8191,30 @@ End /// @name SweepFormula PSX ///@{ -static Constant PSX_WAVE_VERSION = 2 -static Constant PSX_EVENT_WAVE_COLUMNS = 14 +static Constant PSX_WAVE_VERSION = 3 +static Constant PSX_EVENT_WAVE_COLUMNS = 17 -Function UpgradePSXEventWave(WAVE psxEvent) +/// @brief Return the upgraded psxEvent wave +Function/WAVE UpgradePSXEventWave(WAVE psxEvent) if(WaveVersionIsAtLeast(psxEvent, PSX_WAVE_VERSION)) - // latest version + return psxEvent + elseif(WaveVersionIsAtLeast(psxEvent, 2)) + + if(!AlreadyCalledOnce(CO_PSX_UPGRADE_EVENT)) + print "The algorithm for psp/psc event detection was heavily overhauled, therefore we are very sorry " \ + + "to say that we can't upgrade your existing data." + ControlWindowToFront() + endif + + return $"" elseif(WaveVersionIsAtLeast(psxEvent, 1)) SetPSXEventDimensionLabels(psxEvent) else ASSERT(0, "Missing upgrade path") endif + + return psxEvent End /// @brief Return a 2D events wave as free wave @@ -8211,22 +8223,29 @@ End /// - count /// /// Cols: -/// - 0/index: Event index -/// - 1/peak_t: Event time [ms] -/// - 2/peak: Event amplitude in deconvoluted data [y unit of data] -/// - 3/post_min: Minimum of filtered and offsetted data in the range [time, time + 2ms] -/// - 4/post_min_t: X location of [2] -/// - 5/pre_max: Maximum of filtered and offsetted data in the range [time - 2ms, time], averaged over +/- 0.1 ms -/// - 6/pre_max_t: X location of [5] -/// - 7/rel_peak: Relative amplitude: [2] - [4] -/// - 8/isi: Time difference to previous event [ms] +/// - 0/index: event index +/// - 1/deconvPeak: event amplitude in deconvoluted data [y unit of data] +/// - 2/deconvPeak_t: deconvolved peak time [ms] +/// - 3/peak: Maximum (positive kernel amp sign) or minimum (negative kernel amp sign) in the range of +/// [deconvPeak_t – kernelRiseTau or devonvPeak_t of the previous event (whichever comes later), +/// deconvPeak_t + 0.33 * kernelDecayTau or deconvPeak_t of the next event (which ever comes first)] +/// in the filtered sweep wave +/// - 4/peak_t: peak time +/// - 5/baseline: Maximum (negative kernel amp sign) or minimum (positive kernel amp sign) in the range of +/// [peak_t – 10 * kernelRiseTau, peak_t], averaged over +/- 5 points, in the filtered sweep wave +/// - 6/baseline_t: baseline time +/// - 7/amplitude: Relative amplitude: [3] - [5] +/// - 8/iei: Time difference to previous event (inter event interval) [ms] /// - 9/tau: Decay constant tau of exponential fit /// - 10/Fit manual QC call: One of @ref PSXStates /// - 11/Fit result: 1 for success, everything smaller than 0 is failure: -/// - `]-10000, 0[`: CurveFit error codes -/// - `]inf, -10000]`: Custom error codes, one of @ref FitEventDecayCustomErrors +/// - `]-10000, 0[`: CurveFit error codes +/// - `]-inf, -10000]`: Custom error codes, one of @ref FitEventDecayCustomErrors /// - 12/Event manual QC call: One of @ref PSXStates -/// - 13/Rise Time: rise time as calculated by PSX_CalculateRiseTime() +/// - 13/Onset time as calculated by PSX_CalculateOnsetTime +/// - 14/Rise Time as calculated by PSX_CalculateRiseTime +/// - 15/Slew Rate +/// - 16/Slew Rate Time Function/WAVE GetPSXEventWaveAsFree() variable versionOfWave = PSX_WAVE_VERSION @@ -8240,22 +8259,25 @@ Function/WAVE GetPSXEventWaveAsFree() return wv End -Function SetPSXEventDimensionLabels(WAVE wv) +static Function SetPSXEventDimensionLabels(WAVE wv) SetDimLabel COLS, 0, index, wv - SetDimLabel COLS, 1, peak_t, wv - SetDimLabel COLS, 2, peak, wv - SetDimLabel COLS, 3, post_min, wv - SetDimLabel COLS, 4, post_min_t, wv - SetDimLabel COLS, 5, pre_max, wv - SetDimLabel COLS, 6, pre_max_t, wv - SetDimLabel COLS, 7, rel_peak, wv - SetDimLabel COLS, 8, isi, wv + SetDimLabel COLS, 1, deconvPeak, wv + SetDimLabel COLS, 2, deconvPeak_t, wv + SetDimLabel COLS, 3, peak, wv + SetDimLabel COLS, 4, peak_t, wv + SetDimLabel COLS, 5, baseline, wv + SetDimLabel COLS, 6, baseline_t, wv + SetDimLabel COLS, 7, amplitude, wv + SetDimLabel COLS, 8, iei, wv SetDimLabel COLS, 9, tau, wv SetDimLabel COLS, 10, $"Fit manual QC call", wv SetDimLabel COLS, 11, $"Fit result", wv SetDimLabel COLS, 12, $"Event manual QC call", wv - SetDimLabel COLS, 13, $"Rise Time", wv + SetDimLabel COLS, 13, $"Onset Time", wv + SetDimLabel COLS, 14, $"Rise Time", wv + SetDimLabel COLS, 15, $"Slew Rate", wv + SetDimLabel COLS, 16, $"Slew Rate Time", wv End Function/WAVE GetPSXSingleEventFitWaveFromDFR(DFREF dfr) diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index 228af8bd88..2b0372bc33 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -1368,9 +1368,10 @@ numberOfSDs psxRiseTime """"""""""" -The `psxRiseTime` operation is a helper operation for `psx` to manage the lower and upper thresholds for the rise time calculation. +The `psxRiseTime` operation is a helper operation for `psx` to manage the lower and upper thresholds for the rise time calculation +and the differential threshold for the onset time calculcation. - psxRiseTime([lowerThreshold, upperThreshold]) + psxRiseTime([lowerThreshold, upperThreshold, diffThreshold]) The function accepts zero to two arguments. @@ -1380,15 +1381,20 @@ lowerThreshold upperThreshold defaults to 80% +diffThreshold + defaults to 5% + .. code-block:: bash psxRiseTime(0.5) psxRiseTime(0.5, 0.9) + psxRiseTime(0.5, 0.9, 0.15) psxDeconvFilter """"""""""""""" The `psxDeconvFilter` operation is a helper operation for `psx` to manage the deconvolution filter settings. +This filter is a bandpass filter. psxDeconvFilter([lowFreq, highFreq, order]) @@ -1404,15 +1410,14 @@ order defaults to `NaN` The default values of `NaN` are replaced inside `psx`. For the order this is -`101`, for the frequencies this is a normalized frequency which depends on the -sampling interval of the data. Here `lowFreq` is the end of the passband and -`highFreq` the start of the reject band see also the description of `/LO` from -`FilterFIR`. +`7`, for the frequencies `500` (`lowFreq`) and `50` (`highFreq`). +Here `lowFreq` is the end and `highFreq` the start of the +passband, see also the description of `/LO` and `/HI` from `FilterIIR`. .. code-block:: bash - psxDeconvFilter(500, 1000) - psxDeconvFilter(400, 600, 91) + psxDeconvFilter(800, 100) + psxDeconvFilter(400, 50, 11) psxstats """""""" @@ -1444,7 +1449,8 @@ select prop column of the `psx` event results waves to plot. - Choices are: `amp`, `xpos`, `xinterval`, `tau`, `estate`, `fstate`, `fitresult`, `risetime` + Choices are: `amp`, `peak`, `peaktime`, `deconvpeak`, `deconvpeaktime`, `baseline`, `baselinetime`, `xinterval`, + `tau`, `estate`, `fstate`, `fitresult`, `slewrate`, `slewratetime`, `risetime`, `onsettime` state QC state to select the events. @@ -1452,8 +1458,8 @@ state The used QC state depends on `prop`: - - Event state QC -> `amp`/`xpos`/`xinterval`/`estate`/`risetime` - Fit state QC -> `tau`/`fstate`/`fitresult` + - Event state QC for everything else The difference between `all` and `every` is that `all` plots the events from all possible states in **one** trace whereas `every` creates **multiple** diff --git a/Packages/doc/SweepFormula_PSX.rst b/Packages/doc/SweepFormula_PSX.rst index 140c75d691..2d5c6e9f56 100644 --- a/Packages/doc/SweepFormula_PSX.rst +++ b/Packages/doc/SweepFormula_PSX.rst @@ -144,7 +144,7 @@ the selection, either fit or event state, is determined by the popup menu. combination (checked) or use all of them (unchecked) - ``Fit State/Event State``: Select the state to use as basis for selection - ``dblexp_peak/dblexp_XOffset``: Select the fit curve for the average fit -- ``Onset/peak``: Select the event property to offset the events in the all +- ``Onset/Peak/Slew``: Select the event property to offset the events in the all events graph to - ``Block size [%]``: Percentage to select what part of the events are displayed in the all events graph. This can help with reducing the number of diff --git a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf index a10effa43e..5a0f945ed6 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf @@ -651,19 +651,20 @@ static Function FillEventWave_IGNORE(WAVE psxEvent, string id, string comboKey) variable jsonID - CHECK_EQUAL_VAR(DimSize(psxEvent, COLS), 14) // test needs update if that fails + CHECK_EQUAL_VAR(DimSize(psxEvent, COLS), 17) // test needs update if that fails psxEvent[][%index] = p - psxEvent[][%peak_t] = 100 * p + psxEvent[][%deconvpeak_t] = 100 * p + psxEvent[][%deconvpeak] = NaN psxEvent[][%peak] = NaN - psxEvent[][%post_min] = NaN - psxEvent[][%post_min_t] = -10 * p - psxEvent[][%pre_max] = NaN - psxEvent[][%pre_max_t] = NaN - psxEvent[][%rel_peak] = p == 0 ? NaN : 10 * p - psxEvent[][%isi] = 1000 * p + psxEvent[][%peak_t] = -10 * p + psxEvent[][%baseline] = NaN + psxEvent[][%baseline_t] = NaN + psxEvent[][%amplitude] = p == 0 ? NaN : 10 * p + psxEvent[][%iei] = 1000 * p psxEvent[][%tau] = 1e-6 * p psxEvent[][%$"Rise Time"] = p == 0 ? NaN : 0.1 * p + // TODO fill new entries // PSX_ACCEPT:1 // PSX_REJECT:2 // PSX_UNDET: 4 @@ -1233,6 +1234,27 @@ static Function StatsComplainsAboutIntersectingRanges() endtry End +/// IUTF_TD_GENERATOR s0:MIES_PSX#PSX_GetAllStatsProperties +static Function StatsAllProperties([STRUCT IUTF_mData &m]) + + string browser, device, formulaGraph, comboKey, id, error, prop + + prop = m.s0 + + [browser, device, formulaGraph] = CreateFakeDataBrowserWithSweepFormulaGraph() + + [WAVE range, WAVE selectData] = GetFakeRangeAndSelectData() + + // 1st event wave + WAVE/Z psxEvent = GetEventWave(comboIndex = 0) + comboKey = MIES_PSX#PSX_GenerateComboKey(browser, selectData, range) + id = "myID" + FillEventWave_IGNORE(psxEvent, id, comboKey) + + MIES_PSX#PSX_OperationStatsImpl(browser, id, {range}, selectData, prop, "all", "nothing") + CHECK_NO_RTE() +End + Function/WAVE FakeSweepDataGeneratorPSXKernel(WAVE sweep, variable numChannels) variable pnts = 1001 @@ -3240,46 +3262,58 @@ static Function TestBlockIndexLogic() CHECK_EQUAL_TEXTWAVES(dispTracesRef, dispTraces) End -static Function [variable lowerThreshold, variable upperThreshold] TestRiseTimeContainer(WAVE/WAVE dataWref) +static Function [variable lowerThreshold, variable upperThreshold, variable diffThreshold] TestRiseTimeContainer(WAVE/WAVE dataWref) CHECK_WAVE(dataWref, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 1) WAVE/Z data = dataWref[0] CHECK_WAVE(data, NUMERIC_WAVE) - CHECK_EQUAL_VAR(DimSize(data, ROWS), 2) + CHECK_EQUAL_VAR(DimSize(data, ROWS), 3) upperThreshold = data[%$"Upper Threshold"] CHECK(BetweenZeroAndOneExc(upperThreshold)) lowerThreshold = data[%$"Lower Threshold"] CHECK(BetweenZeroAndOneExc(lowerThreshold)) + diffThreshold = data[%$"Differentiate Threshold"] + CHECK(BetweenZeroAndOneExc(lowerThreshold)) - return [lowerThreshold, upperThreshold] + return [lowerThreshold, upperThreshold, diffThreshold] End static Function TestOperationRiseTime() string win, str - variable lowerThreshold, upperThreshold + variable lowerThreshold, upperThreshold, diffThreshold win = SetupDatabrowserWithSomeData() str = "psxRiseTime()" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) - [lowerThreshold, upperThreshold] = TestRiseTimeContainer(dataWref) + [lowerThreshold, upperThreshold, diffThreshold] = TestRiseTimeContainer(dataWref) CHECK_EQUAL_VAR(lowerThreshold, 0.2) CHECK_EQUAL_VAR(upperThreshold, 0.8) + CHECK_EQUAL_VAR(diffThreshold, 0.05) str = "psxRiseTime(10)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) - [lowerThreshold, upperThreshold] = TestRiseTimeContainer(dataWref) + [lowerThreshold, upperThreshold, diffThreshold] = TestRiseTimeContainer(dataWref) CHECK_EQUAL_VAR(lowerThreshold, 0.1) CHECK_EQUAL_VAR(upperThreshold, 0.8) + CHECK_EQUAL_VAR(diffThreshold, 0.05) str = "psxRiseTime(10, 90)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) - [lowerThreshold, upperThreshold] = TestRiseTimeContainer(dataWref) + [lowerThreshold, upperThreshold, diffThreshold] = TestRiseTimeContainer(dataWref) + CHECK_EQUAL_VAR(lowerThreshold, 0.1) + CHECK_EQUAL_VAR(upperThreshold, 0.9) + CHECK_EQUAL_VAR(diffThreshold, 0.05) + + str = "psxRiseTime(10, 90, 45)" + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) + [lowerThreshold, upperThreshold, diffThreshold] = TestRiseTimeContainer(dataWref) CHECK_EQUAL_VAR(lowerThreshold, 0.1) CHECK_EQUAL_VAR(upperThreshold, 0.9) + CHECK_EQUAL_VAR(diffThreshold, 0.45) // checks parameters try