From 6df5e1fca988455279c5a8ba7b10e7e22559cbb2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 23 May 2024 16:50:06 +0200 Subject: [PATCH 01/38] AF: Move workaround for PSQ_RHEOBASE sweepQC determination to own AFH function - introduced AFH_GetRheobaseSweepsSCISweepQCSplitted - no functional change --- .../MIES/MIES_AnalysisFunctionHelpers.ipf | 34 +++++++++++++++++++ .../MIES/MIES_AnalysisFunctions_Dashboard.ipf | 17 ++-------- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf b/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf index 72a77a4935..194f382fdf 100644 --- a/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf +++ b/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf @@ -1180,3 +1180,37 @@ Function/S AFH_GetAnaFuncParamsFromLNB(WAVE numericalValues, WAVE/T textualValue return WaveText(settings, row = index) End + +/// @brief Only for PSQ_RHEOBASE, returns a wave with passing and failing sweep numbers for sweepQC with all sweep numbers of the same SCI range +/// This is a workaround and uses a different approach as for other analysis functions. +/// +/// @param numericalValues numerical LNB +/// @param sweepNo sweep number +/// @param headstage headstage number +/// @param sweepsSCI numerical wave with all sweep numbers from the SCI where sweepNo is part of, @ref AFH_GetSweepsFromSameSCI +/// @param passedSetQC flag that indicates if for this sweepNo the setQC passed, @ref GetLastSettingIndepSCI for PSQ_FMT_LBN_SET_PASS +/// +/// @retval passingSweeps numerical wave containing the sweep numbers of the sweeps from this SCI that passed the sweepQC +/// @retval failingSweeps numerical wave containing the sweep numbers of the sweeps from this SCI that failed the sweepQC +Function [WAVE passingSweeps, WAVE failingSweeps] AFH_GetRheobaseSweepsSCISweepQCSplitted(WAVE numericalValues, variable sweepNo, variable headstage, WAVE sweepsSCI, variable passedSetQC) + + variable firstValid, lastValid + string key + + key = CreateAnaFuncLBNKey(PSQ_RHEOBASE, PSQ_FMT_LBN_SPIKE_DETECT, query = 1) + if(passedSetQC) + WAVE spikeDetection = GetLastSettingEachSCI(numericalValues, sweepNo, key, headstage, UNKNOWN_MODE) + ASSERT(DimSize(sweepsSCI, ROWS) == DimSize(spikeDetection, ROWS), "Unexpected wave sizes") + + firstValid = DimSize(spikeDetection, ROWS) - 2 + lastValid = DimSize(spikeDetection, ROWS) - 1 + ASSERT(Sum(spikeDetection, firstValid, lastValid) == 1, "Unexpected spike/non-spike duo") + Duplicate/FREE/R=[firstValid, lastValid] sweepsSCI, passingSweeps + WAVE/Z failingSweeps = GetSetDifference(sweepsSCI, passingSweeps) + else + Duplicate/FREE sweepsSCI, failingSweeps + WAVE/Z passingSweeps + endif + + return [passingSweeps, failingSweeps] +End diff --git a/Packages/MIES/MIES_AnalysisFunctions_Dashboard.ipf b/Packages/MIES/MIES_AnalysisFunctions_Dashboard.ipf index 61e7d06153..d5a540452f 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_Dashboard.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_Dashboard.ipf @@ -190,7 +190,7 @@ static Function AD_FillWaves(win, list, info) WAVE/T list, info variable i, j, headstage, passed, sweepNo, numEntries, ongoingDAQ, acqState - variable index, anaFuncType, stimsetCycleID, firstValid, lastValid, waMode + variable index, anaFuncType, stimsetCycleID, waMode string key, anaFunc, stimset, msg, device, opMode WAVE/Z totalSweepsPresent = GetPlainSweepList(win) @@ -353,20 +353,7 @@ static Function AD_FillWaves(win, list, info) break case PSQ_RHEOBASE: - key = CreateAnaFuncLBNKey(anaFuncType, PSQ_FMT_LBN_SPIKE_DETECT, query = 1) - if(passed) - WAVE spikeDetection = GetLastSettingEachSCI(numericalValues, sweepNo, key, headstage, UNKNOWN_MODE) - ASSERT(DimSize(sweeps, ROWS) == DimSize(spikeDetection, ROWS), "Unexpected wave sizes") - - firstValid = DimSize(spikeDetection, ROWS) - 2 - lastValid = DimSize(spikeDetection, ROWS) - 1 - ASSERT(Sum(spikeDetection, firstValid, lastValid) == 1, "Unexpected spike/non-spike duo") - Duplicate/FREE/R=[firstValid, lastValid] sweeps, passingSweeps - WAVE/Z failingSweeps = GetSetDifference(sweeps, passingSweeps) - else - Duplicate/FREE sweeps, failingSweeps - WAVE/Z passingSweeps - endif + [WAVE passingSweeps, WAVE failingSweeps] = AFH_GetRheobaseSweepsSCISweepQCSplitted(numericalValues, sweepNo, headstage, sweeps, passed) break #ifdef AUTOMATED_TESTING case TEST_ANALYSIS_FUNCTION: // fallthrough-by-design From 2445fcc29f762bd6cd9c03e3a850a4fa460eec9c Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 13 May 2024 14:59:28 +0200 Subject: [PATCH 02/38] SF: Add data type to sweeps and channels operation output --- Packages/MIES/MIES_Constants.ipf | 2 ++ Packages/MIES/MIES_SweepFormula.ipf | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index ffd5410667..94c64877f3 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2067,6 +2067,8 @@ StrConstant SF_META_FIT_SIGMA = "FitSigma" StrConstant SF_META_FIT_PARAMETER = "FitParameter" StrConstant SF_DATATYPE_SWEEP = "SweepData" +StrConstant SF_DATATYPE_SWEEPNO = "SweepNumbers" +StrConstant SF_DATATYPE_CHANNELS = "Channels" StrConstant SF_DATATYPE_FINDLEVEL = "FindLevel" StrConstant SF_DATATYPE_APFREQUENCY = "ApFrequency" StrConstant SF_DATATYPE_LABNOTEBOOK = "LabNotebook" diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index f7126f0612..700b7142ff 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -4153,7 +4153,7 @@ static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, stri endif endfor - return SFH_GetOutputForExecutorSingle(channels, graph, SF_OP_CHANNELS, discardOpStack = 1) + return SFH_GetOutputForExecutorSingle(channels, graph, SF_OP_CHANNELS, discardOpStack = 1, dataType = SF_DATATYPE_CHANNELS) End /// `sweeps()` @@ -4168,7 +4168,7 @@ static Function/WAVE SF_OperationSweeps(variable jsonId, string jsonPath, string WAVE/Z sweeps = OVS_GetSelectedSweeps(graph, OVS_SWEEP_ALL_SWEEPNO) - return SFH_GetOutputForExecutorSingle(sweeps, graph, SF_OP_SWEEPS, discardOpStack = 1) + return SFH_GetOutputForExecutorSingle(sweeps, graph, SF_OP_SWEEPS, discardOpStack = 1, dataType = SF_DATATYPE_SWEEPNO) End static Function/WAVE SF_OperationPowerSpectrum(variable jsonId, string jsonPath, string graph) From b00cb11af9663308d38e5a61b6c6f57cd9b69fe8 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 15 May 2024 15:02:38 +0200 Subject: [PATCH 03/38] SFH: Added a variant of SFH_ResolveDatasetElementFromJSON that also returns the datasets datatype The datatype is in the note of the wave reference wave and not in the data wave itself. Thus, the returned wave of SFH_ResolveDatasetElementFromJSON does not contain this information. SFH_ResolveDatasetElementFromJSONAndType was added that returns both. This allows operations with loose argument order if all the arguments are typed. --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 5dca395a48..5722b86444 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -723,12 +723,20 @@ static Function SFH_ConvertAllReturnDataToPermanent(WAVE/WAVE output, string win endfor End -/// @brief Retrieves from an argument the first dataset and disposes the argument -Function/WAVE SFH_ResolveDatasetElementFromJSON(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [variable checkExist]) +/// @brief Retrieves from an argument the datatype and the first dataset and disposes the argument +Function [WAVE data, string dataType] SFH_ResolveDatasetElementFromJSONAndType(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [variable checkExist]) checkExist = ParamIsDefault(checkExist) ? 0 : !!checkExist WAVE/WAVE input = SF_ResolveDatasetFromJSON(jsonId, jsonPath, graph, argNum) + dataType = JWN_GetStringFromWaveNote(input, SF_META_DATATYPE) + WAVE/Z data = SFH_CheckForSingleDSAndGetData(input, checkExist, opShort, argNum) + + return [data, dataType] +End + +static Function/WAVE SFH_CheckForSingleDSAndGetData(WAVE/WAVE input, variable checkExist, string opShort, variable argNum) + SFH_ASSERT(DimSize(input, ROWS) == 1, "Expected only a single dataSet") WAVE/Z data = input[0] SFH_ASSERT(!(checkExist && !WaveExists(data)), "No data in dataSet at operation " + opShort + " arg num " + num2istr(argNum)) @@ -737,6 +745,17 @@ Function/WAVE SFH_ResolveDatasetElementFromJSON(variable jsonId, string jsonPath return data End +/// @brief Retrieves from an argument the first dataset and disposes the argument +Function/WAVE SFH_ResolveDatasetElementFromJSON(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [variable checkExist]) + + checkExist = ParamIsDefault(checkExist) ? 0 : !!checkExist + + WAVE/WAVE input = SF_ResolveDatasetFromJSON(jsonId, jsonPath, graph, argNum) + WAVE/Z data = SFH_CheckForSingleDSAndGetData(input, checkExist, opShort, argNum) + + return data +End + /// @brief Transfer wavenote from input data sets to output data sets /// set a label for a x-axis and x-value(s) for data waves /// From a2a4729d551baf8fb4ccbc4effff1849a67ff00c Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 2 Oct 2024 15:09:03 +0200 Subject: [PATCH 04/38] SF: Fix prefix of function SFH_GetNumericVarArgs to SF The function SFH_GetNumericVarArgs used the wrong prefix for the ipf file. Use the function for Min and Max operation. --- Packages/MIES/MIES_SweepFormula.ipf | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 700b7142ff..211e237348 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -3565,7 +3565,7 @@ End static Function/WAVE SF_OperationMin(variable jsonId, string jsonPath, string graph) - WAVE/WAVE input = SFH_GetNumericVarArgs(jsonId, jsonPath, graph, SF_OP_MIN) + WAVE/WAVE input = SF_GetNumericVarArgs(jsonId, jsonPath, graph, SF_OP_MIN) WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_MIN, DimSize(input, ROWS)) output[] = SF_OperationMinImpl(input[p]) @@ -3595,15 +3595,7 @@ End static Function/WAVE SF_OperationMax(variable jsonId, string jsonPath, string graph) - variable numArgs - - numArgs = SFH_GetNumberOfArguments(jsonId, jsonPath) - SFH_ASSERT(numArgs > 0, "max requires at least one argument") - if(numArgs > 1) - WAVE/WAVE input = SF_GetArgumentTop(jsonId, jsonPath, graph, SF_OP_MAX) - else - WAVE/WAVE input = SF_ResolveDatasetFromJSON(jsonId, jsonPath, graph, 0) - endif + WAVE/WAVE input = SF_GetNumericVarArgs(jsonId, jsonPath, graph, SF_OP_MAX) WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_MAX, DimSize(input, ROWS)) output[] = SF_OperationMaxImpl(input[p]) @@ -5195,7 +5187,7 @@ static Function/WAVE SF_GetArgumentTop(variable jsonId, string jsonPath, string return input End -static Function/WAVE SFH_GetNumericVarArgs(variable jsonId, string jsonPath, string graph, string opShort) +static Function/WAVE SF_GetNumericVarArgs(variable jsonId, string jsonPath, string graph, string opShort) variable numArgs From 83713dd901238533bc5c8dc84ebb9d2f4e3fb8cf Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 2 Oct 2024 15:11:26 +0200 Subject: [PATCH 05/38] SF Implement new select operation - Add select filter ops: - selvis - selcm - selrange - selstimset - selivsccsetqc - selivsccsweepqc - renamed channels -> selchannels - renamed sweeps -> selsweeps - Implementation of new select operation - select takes now upto one of each sel* filter operation as argument and additional select arguments - filter properties except selrange() are logical ANDed - all select arguments are additionally intersected if present - select takes the outermost selrange() information - if another argument of select is a select result, then unspecified filter arguments of the outer select are filled by default with values that accept all. In contrast, when no argument is a select result then the previous defaults are used, e.g. "displayed" for selvis() when not specified. Specifically that only a single selrange() argument is allowd for select allows to keep the rule of either one or exactly as many ranges as selects for indexing over the selects/ranges when sweep data is extracted - Adapt SFH_GetArgumentSelect for new select return format - gets datasets instead of a single dataset - checks validity of argument through set datatype - returns "composite structure" aka 2 datasets - Adapt epochs operation for new format of select argument - epochs takes only a single select argument (no array) - Adapt operation tp to take a single select or array of selects - Adapt operation labnotebook to take a single select or array of selects - Adapt operation selsweeps to take numbers, arrays or ranges as arguments to specify sweep numbers. - arguments for selvis() are: all, displayed - arguments for selcm() are: all, vc, ic, izero, none - arguments for selstimset() are strings with optional wildcard pattern like "myStimset*" - arguments for selivsccsetqc() are: failed, passed - arguments for selivsccsweepqc() are: failed, passed --- Packages/MIES/MIES_Constants.ipf | 84 +- Packages/MIES/MIES_Structures.ipf | 13 + Packages/MIES/MIES_SweepFormula.ipf | 817 +++++++++++++++---- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 258 +++++- Packages/MIES/MIES_SweepFormula_PSX.ipf | 36 +- Packages/MIES/MIES_WaveDataFolderGetters.ipf | 9 + 6 files changed, 986 insertions(+), 231 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 94c64877f3..3965ab8da8 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2066,36 +2066,70 @@ StrConstant SF_META_FIT_COEFF = "FitCoefficients" StrConstant SF_META_FIT_SIGMA = "FitSigma" StrConstant SF_META_FIT_PARAMETER = "FitParameter" -StrConstant SF_DATATYPE_SWEEP = "SweepData" -StrConstant SF_DATATYPE_SWEEPNO = "SweepNumbers" -StrConstant SF_DATATYPE_CHANNELS = "Channels" -StrConstant SF_DATATYPE_FINDLEVEL = "FindLevel" -StrConstant SF_DATATYPE_APFREQUENCY = "ApFrequency" -StrConstant SF_DATATYPE_LABNOTEBOOK = "LabNotebook" -StrConstant SF_DATATYPE_BUTTERWORTH = "Butterworth" -StrConstant SF_DATATYPE_AREA = "Area" -StrConstant SF_DATATYPE_INTEGRATE = "Integrate" -StrConstant SF_DATATYPE_DERIVATIVE = "Derivative" -StrConstant SF_DATATYPE_STDEV = "StDev" -StrConstant SF_DATATYPE_VARIANCE = "Variance" -StrConstant SF_DATATYPE_RMS = "RMS" -StrConstant SF_DATATYPE_AVG = "Average" -StrConstant SF_DATATYPE_MAX = "Max" -StrConstant SF_DATATYPE_MIN = "Min" -StrConstant SF_DATATYPE_RANGE = "Range" -StrConstant SF_DATATYPE_EPOCHS = "Epochs" -StrConstant SF_DATATYPE_TP = "TestPulse" -StrConstant SF_DATATYPE_TPSS = "TestPulseMode_SteadyState" -StrConstant SF_DATATYPE_TPINST = "TestPulseMode_Instantaneous" -StrConstant SF_DATATYPE_TPBASE = "TestPulseMode_Baseline" -StrConstant SF_DATATYPE_TPFIT = "TestPulseMode_Fit" -StrConstant SF_DATATYPE_POWERSPECTRUM = "Powerspectrum" -StrConstant SF_DATATYPE_PSX = "PSX" +StrConstant SF_DATATYPE_SWEEP = "SweepData" +StrConstant SF_DATATYPE_SWEEPNO = "SweepNumbers" +StrConstant SF_DATATYPE_CHANNELS = "Channels" +StrConstant SF_DATATYPE_SELECTCOMP = "SelectionComposite" +StrConstant SF_DATATYPE_SELECT = "Selection" +StrConstant SF_DATATYPE_FINDLEVEL = "FindLevel" +StrConstant SF_DATATYPE_APFREQUENCY = "ApFrequency" +StrConstant SF_DATATYPE_LABNOTEBOOK = "LabNotebook" +StrConstant SF_DATATYPE_BUTTERWORTH = "Butterworth" +StrConstant SF_DATATYPE_AREA = "Area" +StrConstant SF_DATATYPE_INTEGRATE = "Integrate" +StrConstant SF_DATATYPE_DERIVATIVE = "Derivative" +StrConstant SF_DATATYPE_STDEV = "StDev" +StrConstant SF_DATATYPE_VARIANCE = "Variance" +StrConstant SF_DATATYPE_RMS = "RMS" +StrConstant SF_DATATYPE_AVG = "Average" +StrConstant SF_DATATYPE_MAX = "Max" +StrConstant SF_DATATYPE_MIN = "Min" +StrConstant SF_DATATYPE_RANGE = "Range" +StrConstant SF_DATATYPE_EPOCHS = "Epochs" +StrConstant SF_DATATYPE_TP = "TestPulse" +StrConstant SF_DATATYPE_TPSS = "TestPulseMode_SteadyState" +StrConstant SF_DATATYPE_TPINST = "TestPulseMode_Instantaneous" +StrConstant SF_DATATYPE_TPBASE = "TestPulseMode_Baseline" +StrConstant SF_DATATYPE_TPFIT = "TestPulseMode_Fit" +StrConstant SF_DATATYPE_POWERSPECTRUM = "Powerspectrum" +StrConstant SF_DATATYPE_PSX = "PSX" +StrConstant SF_DATATYPE_SELECTVIS = "SelectVis" +StrConstant SF_DATATYPE_SELECTCM = "SelectClampMode" +StrConstant SF_DATATYPE_SELECTSTIMSET = "selectStimset" +StrConstant SF_DATATYPE_SELECTIVSCCSWEEPQC = "selectIVSCCSweepQC" +StrConstant SF_DATATYPE_SELECTIVSCCSETQC = "selectIVSCCSetQC" +StrConstant SF_DATATYPE_SELECTRANGE = "selectRange" StrConstant SF_WREF_MARKER = "\"WREF@\":" StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric ///@} +/// @name Constants for SweepFormula Clampmode codes returned by operation selcm() +/// @anchor SFClampcodeConstants +/// @{ +Constant SF_OP_SELECT_CLAMPCODE_ALL = 0x0F +Constant SF_OP_SELECT_CLAMPCODE_NONE = 0x01 +Constant SF_OP_SELECT_CLAMPCODE_IC = 0x02 +Constant SF_OP_SELECT_CLAMPCODE_VC = 0x04 +Constant SF_OP_SELECT_CLAMPCODE_IZERO = 0x08 +/// @} + +/// @name Constants for SweepFormula IVSCC SweepQC codes returned by operation selivsccsweepqc() +/// @anchor SFIVSCCSweepQCConstants +/// @{ +Constant SF_OP_SELECT_IVSCCSWEEPQC_IGNORE = 0x01 +Constant SF_OP_SELECT_IVSCCSWEEPQC_PASSED = 0x02 +Constant SF_OP_SELECT_IVSCCSWEEPQC_FAILED = 0x04 +/// @} + +/// @name Constants for SweepFormula IVSCC SetQC codes returned by operation selivsccsetqc() +/// @anchor SFIVSCCSetQCConstants +/// @{ +Constant SF_OP_SELECT_IVSCCSETQC_IGNORE = 0x01 +Constant SF_OP_SELECT_IVSCCSETQC_PASSED = 0x02 +Constant SF_OP_SELECT_IVSCCSETQC_FAILED = 0x04 +/// @} + /// @name Available source options for RA_SkipSweeps() /// @anchor SkipSweepOptions ///@{ diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index 54f43af94e..717460f3fb 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -645,3 +645,16 @@ Structure ASYNC_ReadOutStruct string rtErrMsg // runtime error message variable abortCode // abort code EndStructure + +/// @brief Wraps all parameters combined for one SF select call +Structure SF_SelectParameters + WAVE selects + WAVE channels + WAVE sweeps + string vis + variable clampMode + WAVE/T stimsets + WAVE ranges + variable sweepQC + variable setQC +EndStructure diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 211e237348..6233d57bf7 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -59,63 +59,80 @@ static Constant SF_APFREQUENCY_INSTANTANEOUS = 0x1 static Constant SF_APFREQUENCY_APCOUNT = 0x2 static Constant SF_APFREQUENCY_INSTANTANEOUS_PAIR = 0x3 -static StrConstant SF_OP_MINUS = "-" -static StrConstant SF_OP_PLUS = "+" -static StrConstant SF_OP_MULT = "*" -static StrConstant SF_OP_DIV = "~1" -static StrConstant SF_OP_RANGE = "range" -static StrConstant SF_OP_RANGESHORT = "…" -static StrConstant SF_OP_MIN = "min" -static StrConstant SF_OP_MAX = "max" -static StrConstant SF_OP_AVG = "avg" -static StrConstant SF_OP_MEAN = "mean" -static StrConstant SF_OP_RMS = "rms" -static StrConstant SF_OP_VARIANCE = "variance" -static StrConstant SF_OP_STDEV = "stdev" -static StrConstant SF_OP_DERIVATIVE = "derivative" -static StrConstant SF_OP_INTEGRATE = "integrate" -static StrConstant SF_OP_TIME = "time" -static StrConstant SF_OP_XVALUES = "xvalues" -static StrConstant SF_OP_TEXT = "text" -static StrConstant SF_OP_LOG = "log" -static StrConstant SF_OP_LOG10 = "log10" -static StrConstant SF_OP_APFREQUENCY = "apfrequency" -static StrConstant SF_OP_CURSORS = "cursors" -static StrConstant SF_OP_SWEEPS = "sweeps" -static StrConstant SF_OP_AREA = "area" -static StrConstant SF_OP_SETSCALE = "setscale" -static StrConstant SF_OP_BUTTERWORTH = "butterworth" -static StrConstant SF_OP_CHANNELS = "channels" -static StrConstant SF_OP_DATA = "data" -static StrConstant SF_OP_LABNOTEBOOK = "labnotebook" -static StrConstant SF_OP_WAVE = "wave" -static StrConstant SF_OP_FINDLEVEL = "findlevel" -static StrConstant SF_OP_EPOCHS = "epochs" -static StrConstant SF_OP_TP = "tp" -static StrConstant SF_OP_STORE = "store" -static StrConstant SF_OP_SELECT = "select" -static StrConstant SF_OP_POWERSPECTRUM = "powerspectrum" -static StrConstant SF_OP_TPSS = "tpss" -static StrConstant SF_OP_TPINST = "tpinst" -static StrConstant SF_OP_TPBASE = "tpbase" -static StrConstant SF_OP_TPFIT = "tpfit" +static StrConstant SF_OP_MINUS = "-" +static StrConstant SF_OP_PLUS = "+" +static StrConstant SF_OP_MULT = "*" +static StrConstant SF_OP_DIV = "~1" +static StrConstant SF_OP_RANGE = "range" +static StrConstant SF_OP_RANGESHORT = "…" +static StrConstant SF_OP_MIN = "min" +static StrConstant SF_OP_MAX = "max" +static StrConstant SF_OP_AVG = "avg" +static StrConstant SF_OP_MEAN = "mean" +static StrConstant SF_OP_RMS = "rms" +static StrConstant SF_OP_VARIANCE = "variance" +static StrConstant SF_OP_STDEV = "stdev" +static StrConstant SF_OP_DERIVATIVE = "derivative" +static StrConstant SF_OP_INTEGRATE = "integrate" +static StrConstant SF_OP_TIME = "time" +static StrConstant SF_OP_XVALUES = "xvalues" +static StrConstant SF_OP_TEXT = "text" +static StrConstant SF_OP_LOG = "log" +static StrConstant SF_OP_LOG10 = "log10" +static StrConstant SF_OP_APFREQUENCY = "apfrequency" +static StrConstant SF_OP_CURSORS = "cursors" +static StrConstant SF_OP_SELECTSWEEPS = "selsweeps" +static StrConstant SF_OP_AREA = "area" +static StrConstant SF_OP_SETSCALE = "setscale" +static StrConstant SF_OP_BUTTERWORTH = "butterworth" +static StrConstant SF_OP_SELECTCHANNELS = "selchannels" +static StrConstant SF_OP_DATA = "data" +static StrConstant SF_OP_LABNOTEBOOK = "labnotebook" +static StrConstant SF_OP_WAVE = "wave" +static StrConstant SF_OP_FINDLEVEL = "findlevel" +static StrConstant SF_OP_EPOCHS = "epochs" +static StrConstant SF_OP_TP = "tp" +static StrConstant SF_OP_STORE = "store" +static StrConstant SF_OP_SELECT = "select" +static StrConstant SF_OP_SELECTVIS = "selvis" +static StrConstant SF_OP_SELECTCM = "selcm" +static StrConstant SF_OP_SELECTSTIMSET = "selstimset" +static StrConstant SF_OP_SELECTIVSCCSWEEPQC = "selivsccsweepqc" +static StrConstant SF_OP_SELECTIVSCCSETQC = "selivsccsetqc" +static StrConstant SF_OP_SELECTRANGE = "selrange" +static StrConstant SF_OP_POWERSPECTRUM = "powerspectrum" +static StrConstant SF_OP_TPSS = "tpss" +static StrConstant SF_OP_TPINST = "tpinst" +static StrConstant SF_OP_TPBASE = "tpbase" +static StrConstant SF_OP_TPFIT = "tpfit" static StrConstant SF_OPSHORT_MINUS = "minus" static StrConstant SF_OPSHORT_PLUS = "plus" static StrConstant SF_OPSHORT_MULT = "mult" static StrConstant SF_OPSHORT_DIV = "div" -static StrConstant SF_OP_EPOCHS_TYPE_RANGE = "range" -static StrConstant SF_OP_EPOCHS_TYPE_NAME = "name" -static StrConstant SF_OP_EPOCHS_TYPE_TREELEVEL = "treelevel" -static StrConstant SF_OP_TP_TYPE_BASELINE = "base" -static StrConstant SF_OP_TP_TYPE_INSTANT = "inst" -static StrConstant SF_OP_TP_TYPE_STATIC = "ss" -static StrConstant SF_OP_SELECT_CLAMPMODE_ALL = "all" -static StrConstant SF_OP_SELECT_CLAMPMODE_IC = "ic" -static StrConstant SF_OP_SELECT_CLAMPMODE_VC = "vc" -static StrConstant SF_OP_SELECT_CLAMPMODE_IZERO = "izero" -static Constant SF_OP_SELECT_CLAMPCODE_ALL = -1 +static StrConstant SF_OP_EPOCHS_TYPE_RANGE = "range" +static StrConstant SF_OP_EPOCHS_TYPE_NAME = "name" +static StrConstant SF_OP_EPOCHS_TYPE_TREELEVEL = "treelevel" +static StrConstant SF_OP_TP_TYPE_BASELINE = "base" +static StrConstant SF_OP_TP_TYPE_INSTANT = "inst" +static StrConstant SF_OP_TP_TYPE_STATIC = "ss" + +static StrConstant SF_OP_SELECTVIS_ALL = "all" +static StrConstant SF_OP_SELECTVIS_DISPLAYED = "displayed" + +/// @name Constants for SweepFormula Clampmode user argument strings used in selcm() +/// @anchor SFClampModeStrings +/// @{ +static StrConstant SF_OP_SELECTCM_CLAMPMODE_ALL = "all" +static StrConstant SF_OP_SELECTCM_CLAMPMODE_NONE = "none" +static StrConstant SF_OP_SELECTCM_CLAMPMODE_IC = "ic" +static StrConstant SF_OP_SELECTCM_CLAMPMODE_VC = "vc" +static StrConstant SF_OP_SELECTCM_CLAMPMODE_IZERO = "izero" +/// @} + +static StrConstant SF_OP_SELECT_IVSCCQC_PASSED = "passed" +static StrConstant SF_OP_SELECT_IVSCCQC_FAILED = "failed" static StrConstant SF_OP_TPFIT_FUNC_EXP = "exp" static StrConstant SF_OP_TPFIT_FUNC_DEXP = "doubleexp" @@ -140,6 +157,8 @@ static StrConstant SF_OP_APFREQUENCY_X_TIME = "time" static StrConstant SF_OP_AVG_INSWEEPS = "in" static StrConstant SF_OP_AVG_OVERSWEEPS = "over" +static StrConstant SF_OP_SELECT_STIMSETS_ALL = "*" + static Constant EPOCHS_TYPE_INVALID = -1 static Constant EPOCHS_TYPE_RANGE = 0 static Constant EPOCHS_TYPE_NAME = 1 @@ -179,6 +198,8 @@ static Constant SF_POWERSPECTRUM_RATIO_GAUSS_NUMCOEFS = 4 static Constant SF_VARIABLE_PREFIX = 36 +static StrConstant SF_GETSETINTERSECTIONSELECT_FORMAT = "%d_%d_%d" + Menu "GraphPopup" "Bring browser to front", /Q, SF_BringBrowserToFront() End @@ -202,13 +223,14 @@ End Function/WAVE SF_GetNamedOperations() - Make/FREE/T wt = {SF_OP_RANGE, SF_OP_MIN, SF_OP_MAX, SF_OP_AVG, SF_OP_MEAN, SF_OP_RMS, SF_OP_VARIANCE, SF_OP_STDEV, \ - SF_OP_DERIVATIVE, SF_OP_INTEGRATE, SF_OP_TIME, SF_OP_XVALUES, SF_OP_TEXT, SF_OP_LOG, \ - SF_OP_LOG10, SF_OP_APFREQUENCY, SF_OP_CURSORS, SF_OP_SWEEPS, SF_OP_AREA, SF_OP_SETSCALE, SF_OP_BUTTERWORTH, \ - SF_OP_CHANNELS, SF_OP_DATA, SF_OP_LABNOTEBOOK, SF_OP_WAVE, SF_OP_FINDLEVEL, SF_OP_EPOCHS, SF_OP_TP, \ - SF_OP_STORE, SF_OP_SELECT, SF_OP_POWERSPECTRUM, SF_OP_TPSS, SF_OP_TPBASE, SF_OP_TPINST, SF_OP_TPFIT, \ - SF_OP_PSX, SF_OP_PSX_KERNEL, SF_OP_PSX_STATS, SF_OP_PSX_RISETIME, SF_OP_PSX_PREP, SF_OP_PSX_DECONV_FILTER, \ - SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET} + Make/FREE/T wt = {SF_OP_RANGE, SF_OP_MIN, SF_OP_MAX, SF_OP_AVG, SF_OP_MEAN, SF_OP_RMS, SF_OP_VARIANCE, SF_OP_STDEV, \ + SF_OP_DERIVATIVE, SF_OP_INTEGRATE, SF_OP_TIME, SF_OP_XVALUES, SF_OP_TEXT, SF_OP_LOG, \ + SF_OP_LOG10, SF_OP_APFREQUENCY, SF_OP_CURSORS, SF_OP_SELECTSWEEPS, SF_OP_AREA, SF_OP_SETSCALE, SF_OP_BUTTERWORTH, \ + SF_OP_SELECTCHANNELS, SF_OP_DATA, SF_OP_LABNOTEBOOK, SF_OP_WAVE, SF_OP_FINDLEVEL, SF_OP_EPOCHS, SF_OP_TP, \ + SF_OP_STORE, SF_OP_SELECT, SF_OP_POWERSPECTRUM, SF_OP_TPSS, SF_OP_TPBASE, SF_OP_TPINST, SF_OP_TPFIT, \ + SF_OP_PSX, SF_OP_PSX_KERNEL, SF_OP_PSX_STATS, SF_OP_PSX_RISETIME, SF_OP_PSX_PREP, SF_OP_PSX_DECONV_FILTER, \ + SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ + SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE} return wt End @@ -1045,11 +1067,11 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_WAVE: WAVE out = SF_OperationWave(jsonId, jsonPath, graph) break - case SF_OP_CHANNELS: - WAVE out = SF_OperationChannels(jsonId, jsonPath, graph) + case SF_OP_SELECTCHANNELS: + WAVE out = SF_OperationSelectChannels(jsonId, jsonPath, graph) break - case SF_OP_SWEEPS: - WAVE out = SF_OperationSweeps(jsonId, jsonPath, graph) + case SF_OP_SELECTSWEEPS: + WAVE out = SF_OperationSelectSweeps(jsonId, jsonPath, graph) break case SF_OP_DATA: WAVE out = SF_OperationData(jsonId, jsonPath, graph) @@ -1126,6 +1148,24 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_DATASET: WAVE out = SF_OperationDataset(jsonId, jsonPath, graph) break + case SF_OP_SELECTVIS: + WAVE out = SF_OperationSelectVis(jsonId, jsonPath, graph) + break + case SF_OP_SELECTCM: + WAVE out = SF_OperationSelectCM(jsonId, jsonPath, graph) + break + case SF_OP_SELECTSTIMSET: + WAVE out = SF_OperationSelectStimset(jsonId, jsonPath, graph) + break + case SF_OP_SELECTIVSCCSWEEPQC: + WAVE out = SF_OperationSelectIVSCCSweepQC(jsonId, jsonPath, graph) + break + case SF_OP_SELECTIVSCCSETQC: + WAVE out = SF_OperationSelectIVSCCSetQC(jsonId, jsonPath, graph) + break + case SF_OP_SELECTRANGE: + WAVE out = SF_OperationSelectRange(jsonId, jsonPath, graph) + break default: SFH_ASSERT(0, "Undefined Operation", jsonId = jsonId) endswitch @@ -2154,33 +2194,29 @@ End /// @brief Use the labnotebook information to return the active channel numbers /// for a given set of sweeps /// -/// @param graph DataBrowser or SweepBrowser reference graph -/// @param channels @c SF_FormulaExecutor style @c channels() wave -/// @param sweeps @c SF_FormulaExecutor style @c sweeps() wave -/// @param fromDisplayed boolean variable, if set the selectdata is determined from the displayed sweeps -/// @param clampMode numerical variable, sets the clamp mode considered +/// @param graph DataBrowser or SweepBrowser reference graph +/// @param filter filled SF_SelectParameters structure /// /// @return a selectData style wave with three columns /// containing sweepNumber, channelType and channelNumber -static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE/Z channels, WAVE/Z sweeps, variable fromDisplayed, variable clampMode) +static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters &filter) variable i, j, k, l, channelType, channelNumber, sweepNo, sweepNoT, outIndex variable numSweeps, numInChannels, numActiveChannels, index variable isSweepBrowser variable dimPosSweep, dimPosChannelNumber, dimPosChannelType variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode - variable numTraces + variable numTraces, fromDisplayed, clampCode string msg, device, singleSweepDFStr - if(!WaveExists(sweeps) || !DimSize(sweeps, ROWS)) - return $"" - endif + WAVE/Z sweeps = filter.sweeps + WAVE/Z channels = filter.channels - if(!WaveExists(channels) || !DimSize(channels, ROWS)) + if(!WaveExists(sweeps) || !WaveExists(channels)) return $"" endif - fromDisplayed = !!fromDisplayed + fromDisplayed = !CmpStr(filter.vis, SF_OP_SELECTVIS_DISPLAYED) if(fromDisplayed) WAVE/T/Z traces = GetTraceInfos(graph) @@ -2305,15 +2341,16 @@ static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE/Z ch if(fromDisplayed) for(l = 0; l < numTraces; l += 1) - if(sweepNo == selectDisplayed[l][dimPosSweep] && channelType == selectDisplayed[l][dimPosChannelType] && (IsNaN(channelNumber) || channelNumber == selectDisplayed[l][dimPosChannelNumber])) - if(clampMode != SF_OP_SELECT_CLAMPCODE_ALL && (channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_ADC) && clampMode != clampModeDisplayed[l]) - continue - endif - selectData[outIndex][dimPosSweep] = sweepNo - selectData[outIndex][dimPosChannelType] = channelType - selectData[outIndex][dimPosChannelNumber] = selectDisplayed[l][dimPosChannelNumber] - outIndex += 1 + + clampCode = SF_MapClampModeToSelectCM(clampModeDisplayed[l]) + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode)) + continue endif + + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = selectDisplayed[l][dimPosChannelNumber] + outIndex += 1 endfor else WAVE/Z activeChannels = GetActiveChannels(numericalValues, textualValues, sweepNo, channelType) @@ -2323,15 +2360,19 @@ static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE/Z ch // faster than ZapNaNs due to no mem alloc numActiveChannels = DimSize(activeChannels, ROWS) for(l = 0; l < numActiveChannels; l += 1) - if(IsNan(activeChannels[l]) || (!IsNaN(channelNumber) && channelNumber != l)) + if(IsNan(activeChannels[l])) continue endif - if(clampMode != SF_OP_SELECT_CLAMPCODE_ALL && (channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_ADC)) + + if(SF_FilterByClampModeEnabled(filter.clampMode, channelType)) [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, CLAMPMODE_ENTRY_KEY, l, channelType, DATA_ACQUISITION_MODE) - if(!WaveExists(setting) || (WaveExists(setting) && clampMode != setting[index])) - continue - endif + clampCode = WaveExists(setting) ? SF_MapClampModeToSelectCM(setting[index]) : SF_OP_SELECT_CLAMPCODE_NONE endif + + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode)) + continue + endif + selectData[outIndex][dimPosSweep] = sweepNo selectData[outIndex][dimPosChannelType] = channelType selectData[outIndex][dimPosChannelNumber] = l @@ -2352,6 +2393,73 @@ static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE/Z ch return out End +static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode) + + variable sweepQC, setQC + string setName + + if(filtSweepNo != sweepNo) + return 0 + endif + + if(!IsNaN(filtChannelNumber) && filtChannelNumber != channelNumber) + return 0 + endif + + if(filtChannelType != channelType) + return 0 + endif + + if(SF_FilterByClampModeEnabled(filter.clampMode, channelType) && !(filter.clampMode & clampMode)) + return 0 + endif + + WAVE/Z indizes = FindIndizes(filter.stimsets, str = SF_OP_SELECT_STIMSETS_ALL) + if(!WaveExists(indizes)) + setName = SFH_GetStimsetName(graph, sweepNo, channelNumber, channelType) + if(!MatchAgainstWildCardPatterns(filter.stimsets, setName)) + return 0 + endif + endif + + if(filter.sweepQC != SF_OP_SELECT_IVSCCSWEEPQC_IGNORE) + sweepQC = SFH_IsSweepQCPassed(graph, sweepNo, channelNumber, channelType) == 1 ? SF_OP_SELECT_IVSCCSWEEPQC_PASSED : SF_OP_SELECT_IVSCCSWEEPQC_FAILED + if(!(filter.sweepQC & sweepQC)) + return 0 + endif + endif + + if(filter.setQC != SF_OP_SELECT_IVSCCSETQC_IGNORE) + setQC = SFH_IsSetQCPassed(graph, sweepNo, channelNumber, channelType) == 1 ? SF_OP_SELECT_IVSCCSETQC_PASSED : SF_OP_SELECT_IVSCCSETQC_FAILED + if(!(filter.setQC & setQC)) + return 0 + endif + endif + + return 1 +End + +static Function SF_MapClampModeToSelectCM(variable clampMode) + + if(IsNaN(clampMode)) + return SF_OP_SELECT_CLAMPCODE_NONE + endif + + switch(clampMode) + case V_CLAMP_MODE: + return SF_OP_SELECT_CLAMPCODE_VC + break + case I_CLAMP_MODE: + return SF_OP_SELECT_CLAMPCODE_IC + break + case I_EQUAL_ZERO_MODE: + return SF_OP_SELECT_CLAMPCODE_IZERO + break + default: + ASSERT(0, "Unknown clamp mode") + endswitch +End + static Function/WAVE SF_SortSelectData(WAVE selectData) variable dimPosSweep, dimPosChannelType, dimPosChannelNumber @@ -2757,7 +2865,10 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra allowedTypes = AddListItem(SF_DATATYPE_TPFIT, allowedTypes) SFH_ASSERT(WhichListItem(dataType, allowedTypes) >= 0, "Unknown TP mode.") - WAVE/WAVE output = SF_OperationTPImpl(graph, wMode, selectData, ignoreTPs, SF_OP_TP) + WAVE/Z/WAVE output = SF_OperationTPIterate(graph, wMode, selectData, ignoreTPs, SF_OP_TP) + if(!WaveExists(output)) + WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_TP, 0) + endif JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_TP) JWN_SetStringInWaveNote(output, SF_META_OPSTACK, AddListItem(SF_OP_TP, "")) @@ -2777,6 +2888,37 @@ static Function SF_GetTPFitQuality(WAVE residuals, WAVE sweepData, variable begi return sum(residuals, beginTrail, endTrail) / (endTrailIndex - beginTrailIndex) End +static Function/WAVE SF_OperationTPIterate(string graph, WAVE/WAVE mode, WAVE/WAVE/Z selectDataArray, WAVE/Z ignoreTPs, string opShort) + + if(!WaveExists(selectDataArray)) + return $"" + endif + + WAVE/WAVE/Z result = $"" + + for(WAVE/WAVE/Z selectDataComp : selectDataArray) + + if(!WaveExists(selectDataComp)) + continue + endif + + WAVE/Z selectData = selectDataComp[%SELECTION] + WAVE/WAVE sweepData = SF_OperationTPImpl(graph, mode, selectData, ignoreTPs, opShort) + if(!WaveExists(sweepData)) + continue + endif + + if(!WaveExists(result)) + WAVE/WAVE result = sweepData + continue + endif + + Concatenate/FREE/WAVE/NP {sweepData}, result + endfor + + return result +End + static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z selectDataPreFilter, WAVE/Z ignoreTPs, string opShort) variable i, j, numSelected, sweepNo, chanNr, chanType, dacChannelNr, settingsIndex, headstage, tpBaseLinePoints, index, err, maxTrailLength @@ -2797,8 +2939,7 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel WAVE/Z selectData = SFH_FilterSelect(selectDataPreFilter, XOP_CHANNEL_TYPE_ADC) if(!WaveExists(selectData)) - WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) - return output + return $"" endif dataType = JWN_GetStringFromWaveNote(mode, SF_META_DATATYPE) @@ -2820,8 +2961,6 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel numSelected = DimSize(selectData, ROWS) WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numSelected) - WAVE singleSelect = SFH_NewSelectDataWave(1, 1) - WAVE/Z settings for(i = 0; i < numSelected; i += 1) @@ -2839,12 +2978,8 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel continue endif - singleSelect[0][%SWEEP] = sweepNo - singleSelect[0][%CHANNELTYPE] = chanType - singleSelect[0][%CHANNELNUMBER] = chanNr - - WAVE/WAVE range = SFH_AsDataSet(SFH_GetFullRange()) - WAVE/WAVE sweepDataRef = SFH_GetSweepsForFormula(graph, range, singleSelect, SF_OP_TP) + WAVE/WAVE singleSelect = SFH_GetSingleSelect(graph, opShort, sweepNo, chanType, chanNr) + WAVE/WAVE sweepDataRef = SFH_GetSweepsForFormula(graph, singleSelect, SF_OP_TP) SFH_ASSERT(DimSize(sweepDataRef, ROWS) == 1, "Could not retrieve sweep data for " + num2istr(sweepNo)) WAVE/Z sweepData = sweepDataRef[0] SFH_ASSERT(WaveExists(sweepData), "No sweep data for " + num2istr(sweepNo) + " found.") @@ -3088,6 +3223,9 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel index += 1 endif endfor + if(!index) + return $"" + endif Redimension/N=(index) output if(debugMode) @@ -3159,7 +3297,15 @@ static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string epType = EPOCHS_TYPE_RANGE endif - WAVE/Z selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_EPOCHS, 1) + WAVE/WAVE/Z selectData = $"" + WAVE/WAVE/Z selectDataArray = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_EPOCHS, 1) + if(WaveExists(selectDataArray)) + SFH_ASSERT(DimSize(selectDataArray, ROWS) == 1, "Expected a single select specification") + WAVE/WAVE/Z selectDataComp = selectDataArray[0] + if(WaveExists(selectDataComp)) + WAVE/Z selectData = selectDataComp[%SELECTION] + endif + endif WAVE/T epochPatterns = SFH_ResolveDatasetElementFromJSON(jsonID, jsonPath, graph, SF_OP_EPOCHS, 0, checkExist = 1) SFH_ASSERT(IsTextWave(epochPatterns), "Epoch pattern argument must be textual") @@ -4109,10 +4255,10 @@ static Function/WAVE SF_OperationWave(variable jsonId, string jsonPath, string g return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_WAVE, discardOpStack = 1) End -/// `channels([str name]+)` converts a named channel from string to numbers. +/// `selchannels([str name]+)` converts a named channel from string to numbers. /// /// returns [[channelName, channelNumber]+] -static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, string graph) +static Function/WAVE SF_OperationSelectChannels(variable jsonId, string jsonPath, string graph) variable numArgs, i, channelType string channelName, channelNumber @@ -4122,8 +4268,8 @@ static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, stri numArgs = SFH_GetNumberOfArguments(jsonId, jsonPath) WAVE channels = SF_NewChannelsWave(numArgs ? numArgs : 1) for(i = 0; i < numArgs; i += 1) - WAVE chanSpec = SFH_ResolveDatasetElementFromJSON(jsonId, jsonPath, graph, SF_OP_CHANNELS, i, checkExist = 1) channelName = "" + WAVE chanSpec = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, SF_OP_SELECTCHANNELS, i, singleResult = 1) if(IsNumericWave(chanSpec)) channels[i][%channelNumber] = chanSpec[0] elseif(IsTextWave(chanSpec)) @@ -4134,7 +4280,7 @@ static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, stri endif channels[i][%channelNumber] = str2num(channelNumber) else - SFH_ASSERT(0, "Unsupported arg type for channels.") + SFH_ASSERT(0, "Unsupported arg type for selchannels.") endif SFH_ASSERT(!isFinite(channels[i][%channelNumber]) || channels[i][%channelNumber] < NUM_MAX_CHANNELS, "Maximum Number Of Channels exceeded.") if(!IsEmpty(channelName)) @@ -4145,22 +4291,34 @@ static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, stri endif endfor - return SFH_GetOutputForExecutorSingle(channels, graph, SF_OP_CHANNELS, discardOpStack = 1, dataType = SF_DATATYPE_CHANNELS) + return SFH_GetOutputForExecutorSingle(channels, graph, SF_OP_SELECTCHANNELS, discardOpStack = 1, dataType = SF_DATATYPE_CHANNELS) End -/// `sweeps()` +/// `selsweeps()`, `selsweeps(1,2,3, [4...6])` /// returns all possible sweeps as 1d array -static Function/WAVE SF_OperationSweeps(variable jsonId, string jsonPath, string graph) +static Function/WAVE SF_OperationSelectSweeps(variable jsonId, string jsonPath, string graph) - variable numArgs + variable i, numArgs - numArgs = SFH_GetNumberOfArguments(jsonId, jsonPath) - SFH_ASSERT(numArgs == 0, "Sweep function takes no arguments.") SFH_ASSERT(!IsEmpty(graph), "Graph not specified.") - WAVE/Z sweeps = OVS_GetSelectedSweeps(graph, OVS_SWEEP_ALL_SWEEPNO) + numArgs = SFH_GetNumberOfArguments(jsonId, jsonPath) + if(!numArgs) + WAVE/D/Z sweeps = OVS_GetSelectedSweeps(graph, OVS_SWEEP_ALL_SWEEPNO) + else + for(i = 0; i < numArgs; i += 1) + WAVE data = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, SF_OP_SELECTSWEEPS, i, singleResult = 1, expectedWaveType = IGOR_TYPE_64BIT_FLOAT) + SFH_ASSERT(!DimSize(data, COLS), "Argument of selsweeps must be a number or a 1d numeric array") + Concatenate/FREE/D/NP {data}, sweeps + endfor + endif + if(WaveExists(sweeps)) + WAVE uniqueSweeps = GetUniqueEntries(sweeps) + else + WAVE/ZZ uniqueSweeps + endif - return SFH_GetOutputForExecutorSingle(sweeps, graph, SF_OP_SWEEPS, discardOpStack = 1, dataType = SF_DATATYPE_SWEEPNO) + return SFH_GetOutputForExecutorSingle(sweeps, graph, SF_OP_SELECTSWEEPS, discardOpStack = 1, dataType = SF_DATATYPE_SWEEPNO) End static Function/WAVE SF_OperationPowerSpectrum(variable jsonId, string jsonPath, string graph) @@ -4403,60 +4561,316 @@ threadsafe static Function/WAVE SF_OperationPowerSpectrumImpl(WAVE/Z input, stri return output End -/// `select([array channels, array sweeps, [string mode, [string clamp]]])` +/// `selvis(mode)` // mode can be `all` or `displayed` /// -/// returns n x 3 with columns [sweepNr][channelType][channelNr] -static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string graph) +/// returns a one element text wave with either SF_OP_SELECTVIS_ALL or SF_OP_SELECTVIS_DISPLAYED +static Function/WAVE SF_OperationSelectVis(variable jsonId, string jsonPath, string graph) + + string vis + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTVIS, 0, maxArgs = 1) + + vis = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTVIS, 0, allowedValues = {SF_OP_SELECTVIS_DISPLAYED, SF_OP_SELECTVIS_ALL}, defValue = SF_OP_SELECTVIS_DISPLAYED) + Make/FREE/T output = {vis} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTVIS, discardOpStack = 1, dataType = SF_DATATYPE_SELECTVIS) +End + +/// `selcm(mode, mode, ...)` // mode can be `ic`, `vc`, `izero`, `all` +/// see @ref SFClampModeStrings +/// +/// returns a one element numeric wave with SF_OP_SELECTCM_CLAMPMODE_* ORed together from all arguments, see @ref SFClampcodeConstants +static Function/WAVE SF_OperationSelectCM(variable jsonId, string jsonPath, string graph) + + variable numArgs, i, mode + string clampMode + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + numArgs = SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTCM, 0) + if(!numArgs) + mode = SF_OP_SELECT_CLAMPCODE_ALL + else + for(i = 0; i < numArgs; i += 1) + clampMode = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTCM, i, allowedValues = {SF_OP_SELECTCM_CLAMPMODE_ALL, SF_OP_SELECTCM_CLAMPMODE_NONE, SF_OP_SELECTCM_CLAMPMODE_IZERO, SF_OP_SELECTCM_CLAMPMODE_IC, SF_OP_SELECTCM_CLAMPMODE_VC}, defValue = SF_OP_SELECTCM_CLAMPMODE_ALL) + + strswitch(clampMode) + case SF_OP_SELECTCM_CLAMPMODE_ALL: + mode = mode | SF_OP_SELECT_CLAMPCODE_ALL + break + case SF_OP_SELECTCM_CLAMPMODE_NONE: + mode = mode | SF_OP_SELECT_CLAMPCODE_NONE + break + case SF_OP_SELECTCM_CLAMPMODE_IZERO: + mode = mode | SF_OP_SELECT_CLAMPCODE_IZERO + break + case SF_OP_SELECTCM_CLAMPMODE_IC: + mode = mode | SF_OP_SELECT_CLAMPCODE_IC + break + case SF_OP_SELECTCM_CLAMPMODE_VC: + mode = mode | SF_OP_SELECT_CLAMPCODE_VC + break + default: + ASSERT(0, "Unsupported mode") + endswitch + endfor + endif + + Make/FREE output = {mode} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTCM, discardOpStack = 1, dataType = SF_DATATYPE_SELECTCM) +End + +/// `selstimset(stimsetName, stimsetName, ...)` +/// +/// returns a N element text wave with stimset names +static Function/WAVE SF_OperationSelectStimset(variable jsonId, string jsonPath, string graph) + + variable numArgs, i + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + numArgs = SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTSTIMSET, 0) + + if(!numArgs) + Make/FREE/T output = {SF_OP_SELECT_STIMSETS_ALL} + else + Make/FREE/T/N=(numArgs) output + for(i = 0; i < numArgs; i += 1) + output[i] = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTSTIMSET, i) + endfor + endif + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTSTIMSET, discardOpStack = 1, dataType = SF_DATATYPE_SELECTSTIMSET) +End + +/// `SelIVSCCSweepQC(passed | failed)` +/// +/// returns a one element numeric wave with either SF_OP_SELECT_IVSCCSWEEPQC_PASSED or SF_OP_SELECT_IVSCCSWEEPQC_FAILED +static Function/WAVE SF_OperationSelectIVSCCSweepQC(variable jsonId, string jsonPath, string graph) + + variable mode + string arg + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTIVSCCSWEEPQC, 1, maxArgs = 1) + + arg = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTIVSCCSWEEPQC, 0, allowedValues = {SF_OP_SELECT_IVSCCQC_PASSED, SF_OP_SELECT_IVSCCQC_FAILED}) + mode = !CmpStr(arg, SF_OP_SELECT_IVSCCQC_PASSED) ? SF_OP_SELECT_IVSCCSWEEPQC_PASSED : SF_OP_SELECT_IVSCCSWEEPQC_FAILED + + Make/FREE output = {mode} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTIVSCCSWEEPQC, discardOpStack = 1, dataType = SF_DATATYPE_SELECTIVSCCSWEEPQC) +End + +/// `SelIVSCCSetQC(passed | failed)` +/// +/// returns a one element numeric wave with either SF_OP_SELECT_IVSCCSETQC_PASSED or SF_OP_SELECT_IVSCCSETQC_FAILED +static Function/WAVE SF_OperationSelectIVSCCSetQC(variable jsonId, string jsonPath, string graph) + + variable mode + string arg + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTIVSCCSETQC, 1, maxArgs = 1) + + arg = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTIVSCCSETQC, 0, allowedValues = {SF_OP_SELECT_IVSCCQC_PASSED, SF_OP_SELECT_IVSCCQC_FAILED}) + mode = !CmpStr(arg, SF_OP_SELECT_IVSCCQC_PASSED) ? SF_OP_SELECT_IVSCCSETQC_PASSED : SF_OP_SELECT_IVSCCSETQC_FAILED + + Make/FREE output = {mode} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTIVSCCSETQC, discardOpStack = 1, dataType = SF_DATATYPE_SELECTIVSCCSETQC) +End + +/// `selrange(rangespec)` +/// +/// returns 1 dataset with range specification (either text or 2 point numerical wave) +static Function/WAVE SF_OperationSelectRange(variable jsonId, string jsonPath, string graph) variable numArgs - string clamp - string mode = "displayed" - variable clampMode = SF_OP_SELECT_CLAMPCODE_ALL SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") - numArgs = SFH_GetNumberOfArguments(jsonId, jsonPath) + numArgs = SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTRANGE, 0, maxArgs = 1) if(!numArgs) - WAVE channels = SF_ExecuteFormula("channels()", graph, singleResult = 1, checkExist = 1, useVariables = 0) - WAVE/Z sweeps = SF_ExecuteFormula("sweeps()", graph, singleResult = 1, useVariables = 0) + WAVE/WAVE range = SFH_AsDataSet(SFH_GetFullRange()) else - SFH_ASSERT(numArgs >= 2 && numArgs <= 4, "Function requires None, 2 or 3 arguments.") - WAVE channels = SFH_ResolveDatasetElementFromJSON(jsonId, jsonPath, graph, SF_OP_SELECT, 0, checkExist = 1) - SFH_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") + WAVE/WAVE range = SFH_EvaluateRange(jsonId, jsonPath, graph, SF_OP_SELECTRANGE, 0) + endif - WAVE/Z sweeps = SFH_ResolveDatasetElementFromJSON(jsonId, jsonPath, graph, SF_OP_SELECT, 1) - if(WaveExists(sweeps)) - SFH_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - endif + return SFH_GetOutputForExecutorSingle(range, graph, SF_OP_SELECTRANGE, discardOpStack = 1, dataType = SF_DATATYPE_SELECTRANGE) +End - if(numArgs > 2) - WAVE/T wMode = SFH_ResolveDatasetElementFromJSON(jsonId, jsonPath, graph, SF_OP_SELECT, 2, checkExist = 1) - SFH_ASSERT(IsTextWave(wMode), "mode parameter can not be a number. Use \"all\" or \"displayed\".") - SFH_ASSERT(!DimSize(wMode, COLS) && DimSize(wMode, ROWS) == 1, "mode must not be an array with multiple options.") - mode = wMode[0] - SFH_ASSERT(!CmpStr(mode, "displayed") || !CmpStr(mode, "all"), "mode must be \"all\" or \"displayed\".") - endif +static Function SF_InitSelectFilterUninitalized(STRUCT SF_SelectParameters &s) - if(numArgs > 3) - WAVE/T wClamp = SFH_ResolveDatasetElementFromJSON(jsonId, jsonPath, graph, SF_OP_SELECT, 3, checkExist = 1) - SFH_ASSERT(IsTextWave(wClamp), "clamp parameter can not be a number. Use \"all\", \"ic\" or \"vc\".") - SFH_ASSERT(!DimSize(wClamp, COLS) && DimSize(wClamp, ROWS) == 1, "clamp must not be an array with multiple options.") - clamp = wClamp[0] - if(!CmpStr(clamp, SF_OP_SELECT_CLAMPMODE_VC)) - clampMode = V_CLAMP_MODE - elseif(!CmpStr(clamp, SF_OP_SELECT_CLAMPMODE_IC)) - clampMode = I_CLAMP_MODE - elseif(!CmpStr(clamp, SF_OP_SELECT_CLAMPMODE_IZERO)) - clampMode = I_EQUAL_ZERO_MODE - elseif(CmpStr(clamp, SF_OP_SELECT_CLAMPMODE_ALL)) - SFH_ASSERT(0, "clamp must be \"all\", \"vc\", \"ic\" or \"izero\".") - endif + WAVE/Z s.selects = $"" + WAVE/Z s.channels = $"" + WAVE/Z s.sweeps = $"" + s.vis = "" + s.clampMode = NaN + WAVE/Z/T s.stimsets = $"" + WAVE/Z/WAVE s.ranges = $"" + s.sweepQC = NaN + s.setQC = NaN +End + +/// `select(selectFilterOp...)` +/// +/// returns 2 datasets, main wave typed SF_DATATYPE_SELECTCOMP +/// dataset 0: N x 3 with columns [sweepNr][channelType][channelNr], typed SF_DATATYPE_SELECT +/// dataset 1: WaveRef wave with range specifications, typed SF_DATATYPE_SELECTRANGE +static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string graph) + + STRUCT SF_SelectParameters filter + variable i, numArgs, selectArgPresent + string type, vis + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + SF_InitSelectFilterUninitalized(filter) + + numArgs = SFH_GetNumberOfArguments(jsonId, jsonPath) + for(i = 0; i < numArgs; i += 1) + WAVE/WAVE input = SF_ResolveDatasetFromJSON(jsonId, jsonPath, graph, i) + SFH_ASSERT(DimSize(input, ROWS) >= 1, "Expected at least one dataset") + type = JWN_GetStringFromWaveNote(input, SF_META_DATATYPE) + WAVE/Z arg = input[0] + if(!WaveExists(arg)) + continue endif + strswitch(type) + case SF_DATATYPE_SELECTVIS: + if(IsEmpty(filter.vis)) + filter.vis = WaveText(arg, row = 0) + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTVIS + " argument.") + endif + break + case SF_DATATYPE_SELECTCM: + if(IsNaN(filter.clampMode)) + filter.clampMode = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTCM + " argument.") + endif + break + case SF_DATATYPE_CHANNELS: + if(!WaveExists(filter.channels)) + WAVE filter.channels = arg + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTCHANNELS + " argument.") + endif + break + case SF_DATATYPE_SELECTSTIMSET: + if(!WaveExists(filter.stimsets)) + WAVE/T filter.stimsets = arg + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTSTIMSET + " argument.") + endif + break + case SF_DATATYPE_SWEEPNO: + if(!WaveExists(filter.sweeps)) + WAVE filter.sweeps = arg + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTSWEEPS + " argument.") + endif + break + case SF_DATATYPE_SELECTIVSCCSWEEPQC: + if(IsNaN(filter.sweepQC)) + filter.sweepQC = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTIVSCCSWEEPQC + " argument.") + endif + break + case SF_DATATYPE_SELECTIVSCCSETQC: + if(IsNaN(filter.setQC)) + filter.setQC = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTIVSCCSETQC + " argument.") + endif + break + case SF_DATATYPE_SELECTRANGE: + if(!WaveExists(filter.ranges)) + WAVE filter.ranges = arg + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTRANGE + " argument.") + endif + break + case SF_DATATYPE_SELECTCOMP: + selectArgPresent = 1 + if(!WaveExists(filter.selects)) + WAVE filter.selects = arg + else + WAVE/Z filter.selects = SF_GetSetIntersectionSelect(filter.selects, arg) + endif + break + default: + SFH_ASSERT(0, "Unsupported select argument") + endswitch + endfor + + SF_SetSelectionFilterDefaults(graph, filter, selectArgPresent) + + WAVE/Z selectData = SF_GetSelectData(graph, filter) + + if(!WaveExists(selectData)) + // case: select from added filter arguments leaves empty selection, then result is empty as intersection with any other selection would yield also empty result + WAVE/Z selectResult = $"" + elseif(WaveExists(filter.selects)) + // case: select argument(s) present, selection from argument is intersected with select from added filter arguments + WAVE/Z selectResult = SF_GetSetIntersectionSelect(filter.selects, selectData) + elseif(selectArgPresent) + // case: select argument(s) present, but selection from argument(s) is empty + WAVE/Z selectResult = $"" + else + // case: no select argument and select results from filter arguments + WAVE selectResult = selectData endif - WAVE/Z selectData = SF_GetActiveChannelNumbersForSweeps(graph, channels, sweeps, !CmpStr(mode, "displayed"), clampMode) + WAVE/WAVE output = GetSFSelectDataComp(graph, SF_OP_SELECT) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_SELECTCOMP) + JWN_SetStringInWaveNote(filter.ranges, SF_META_DATATYPE, SF_DATATYPE_SELECTRANGE) + if(WaveExists(selectResult)) + JWN_SetStringInWaveNote(selectResult, SF_META_DATATYPE, SF_DATATYPE_SELECT) + endif + output[%SELECTION] = selectResult + output[%RANGE] = filter.ranges + + return SFH_GetOutputForExecutor(output, graph, SF_OP_SELECT) +End + +/// @brief sets uninitialized fields of the selection filter +static Function SF_SetSelectionFilterDefaults(string graph, STRUCT SF_SelectParameters &filter, variable includeAll) + + includeAll = !!includeAll - return SFH_GetOutputForExecutorSingle(selectData, graph, SF_OP_SELECT, discardOpStack = 1) + if(!WaveExists(filter.channels)) + WAVE filter.channels = SF_ExecuteFormula("selchannels()", graph, singleResult = 1, checkExist = 1, useVariables = 0) + endif + if(!WaveExists(filter.sweeps)) + WAVE/Z filter.sweeps = SF_ExecuteFormula("selsweeps()", graph, singleResult = 1, useVariables = 0) + endif + if(IsEmpty(filter.vis)) + filter.vis = SelectString(includeAll, SF_OP_SELECTVIS_DISPLAYED, SF_OP_SELECTVIS_ALL) + endif + if(IsNaN(filter.clampMode)) + filter.clampMode = SF_OP_SELECT_CLAMPCODE_ALL + endif + if(!WaveExists(filter.stimsets)) + Make/FREE/T allStimsets = {SF_OP_SELECT_STIMSETS_ALL} + WAVE/T filter.stimsets = allStimsets + endif + if(IsNaN(filter.sweepQC)) + filter.sweepQC = SF_OP_SELECT_IVSCCSWEEPQC_IGNORE + endif + if(IsNaN(filter.setQC)) + filter.setQC = SF_OP_SELECT_IVSCCSETQC_IGNORE + endif + if(!WaveExists(filter.ranges)) + WAVE/WAVE filter.ranges = SFH_AsDataSet(SFH_GetFullRange()) + endif End /// `data(array range[, array selectData])` @@ -4464,18 +4878,14 @@ End /// returns [sweepData][sweeps][channelTypeNumber] for all sweeps selected by selectData static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string graph) - variable numArgs - - numArgs = SFH_GetNumberOfArguments(jsonID, jsonPath) + variable i, numArgs SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") - SFH_ASSERT(numArgs >= 1, "data function requires at least 1 argument.") - SFH_ASSERT(numArgs <= 2, "data function has maximal 2 arguments.") - WAVE/WAVE range = SFH_EvaluateRange(jsonId, jsonPath, graph, SF_OP_DATA, 0) - WAVE/Z selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_DATA, 1) + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_DATA, 0, maxArgs = 1) + WAVE/WAVE selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_DATA, 0) - WAVE/WAVE output = SFH_GetSweepsForFormula(graph, range, selectData, SF_OP_DATA) + WAVE/WAVE output = SFH_GetSweepsForFormula(graph, selectData, SF_OP_DATA) if(!DimSize(output, ROWS)) DebugPrint("Call to SFH_GetSweepsForFormula returned no results") endif @@ -4529,22 +4939,55 @@ static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, s SFH_ASSERT(IsTextWave(wLbnKey) && DimSize(wLbnKey, ROWS) == 1 && !DimSize(wLbnKey, COLS), "First parameter needs to be a string labnotebook key.") lbnKey = wLbnKey[0] - WAVE/WAVE output = SF_OperationLabnotebookImpl(graph, lbnKey, selectData, mode, SF_OP_LABNOTEBOOK) + WAVE/Z/WAVE output = SF_OperationLabnotebookIterate(graph, lbnKey, selectData, mode, SF_OP_LABNOTEBOOK) + if(!WaveExists(output)) + WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_LABNOTEBOOK, 0) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) + endif JWN_SetStringInWaveNote(output, SF_META_OPSTACK, AddListItem(SF_OP_LABNOTEBOOK, "")) return SFH_GetOutputForExecutor(output, graph, SF_OP_LABNOTEBOOK) End +static Function/WAVE SF_OperationLabnotebookIterate(string graph, string lbnKey, WAVE/WAVE/Z selectDataArray, variable mode, string opShort) + + if(!WaveExists(selectDataArray)) + return $"" + endif + + WAVE/WAVE/Z result = $"" + + for(WAVE/WAVE/Z selectDataComp : selectDataArray) + + if(!WaveExists(selectDataComp)) + continue + endif + + WAVE/Z selectData = selectDataComp[%SELECTION] + WAVE/WAVE lbnData = SF_OperationLabnotebookImpl(graph, lbnKey, selectData, mode, opShort) + if(!WaveExists(lbnData)) + continue + endif + + if(!WaveExists(result)) + WAVE/WAVE result = lbnData + continue + endif + + Concatenate/FREE/WAVE/NP {lbnData}, result + endfor + + return result +End + static Function/WAVE SF_OperationLabnotebookImpl(string graph, string lbnKey, WAVE/Z selectData, variable mode, string opShort) variable i, numSelected, index, settingsIndex variable sweepNo, chanNr, chanType if(!WaveExists(selectData)) - WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) - JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) - return output + return $"" endif numSelected = DimSize(selectData, ROWS) @@ -4587,11 +5030,15 @@ static Function/WAVE SF_OperationLabnotebookImpl(string graph, string lbnKey, WA output[index] = out index += 1 endfor + if(!index) + return $"" + endif Redimension/N=(index) output JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "Sweeps") JWN_SetStringInWaveNote(output, SF_META_YAXISLABEL, lbnKey) + return output End @@ -5454,10 +5901,10 @@ End Function/S SF_GetDefaultFormula() - return "trange = [0, inf]\r" + \ - "sel = select(channels(AD), sweeps())\r" + \ - "dat = data($trange, $sel)\r" + \ - "\r" + \ + return "trange = [0, inf]\r" + \ + "sel = select(selchannels(AD), selsweeps())\r" + \ + "dat = data($trange, $sel)\r" + \ + "\r" + \ "$dat" End @@ -5725,3 +6172,41 @@ static Function SF_FormulaPlotterExtendResultsIfCompatible(WAVE/WAVE formulaResu formulaResults[][%FORMULAX] = collectX[p] formulaResults[][%FORMULAY] = collectY[p] End + +threadsafe static Function/S SF_GetSelectRowId(WAVE select, variable row) + + string str + + sprintf str, SF_GETSETINTERSECTIONSELECT_FORMAT, select[row][%SWEEP], select[row][%CHANNELTYPE], select[row][%CHANNELNUMBER] + return str +End + +static Function/WAVE SF_CreateSelectWaveRowIds(WAVE select) + + Make/FREE/T/N=(DimSize(select, ROWS)) selectRowId + Multithread selectRowId[] = SF_GetSelectRowId(select, p) + + return selectRowId +End + +/// @brief Returns the set intersection of two select waves from operation select +static Function/WAVE SF_GetSetIntersectionSelect(WAVE select1, WAVE select2) + + WAVE rowId1 = SF_CreateSelectWaveRowIds(select1) + WAVE rowId2 = SF_CreateSelectWaveRowIds(select2) + + WAVE/Z intersect = GetSetIntersection(rowId1, rowId2, getIndices = 1) + if(!WaveExists(intersect)) + return $"" + endif + + WAVE output = SFH_NewSelectDataWave(DimSize(intersect, ROWS), 1) + MultiThread output[][] = select1[intersect[p]][q] + + return output +End + +static Function SF_FilterByClampModeEnabled(variable clampModeFilter, variable channelType) + + return clampModeFilter != SF_OP_SELECT_CLAMPCODE_ALL && (channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_ADC) +End diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 5722b86444..c7ecec0792 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -455,6 +455,39 @@ Function/WAVE SFH_GetRangeFromEpoch(string graph, string epochName, variable swe return range End +Function/WAVE SFH_GetSweepsForFormula(string graph, WAVE/Z/WAVE selectDataArray, string opShort) + + if(!WaveExists(selectDataArray)) + return $"" + endif + + WAVE/WAVE/Z result = $"" + + for(WAVE/Z/WAVE selectDataComp : selectDataArray) + + WAVE/Z/WAVE sweepData = SFH_GetSweepsForFormulaImpl(graph, selectDataComp, opShort) + if(!WaveExists(sweepData)) + continue + endif + + if(!WaveExists(result)) + WAVE/WAVE result = sweepData + continue + endif + + Concatenate/FREE/WAVE/NP {sweepData}, result + endfor + + if(WaveExists(result)) + return result + endif + + WAVE/WAVE result = SFH_CreateSFRefWave(graph, opShort, 0) + JWN_SetStringInWaveNote(result, SF_META_DATATYPE, SF_DATATYPE_SWEEP) + + return result +End + /// @brief Return a wave reference wave with the requested sweep data. The argument range can contain multiple datasets, /// if it is a single dataset the range(s) are extracted from each selection, /// if there are multiple datasets then the number of datasets must equal the number of selections, @@ -463,12 +496,10 @@ End /// /// All wave input parameters are treated as const and are thus *not* modified. /// -/// @param graph name of databrowser graph -/// @param range wave ref wave with range specification defining the x-range of the extracted -/// data, see also SFH_EvaluateRange(), range specification per dataset can be numerical or text -/// @param selectData channel/sweep selection, see also SFH_GetArgumentSelect() -/// @param opShort operation name (short) -Function/WAVE SFH_GetSweepsForFormula(string graph, WAVE/WAVE range, WAVE/Z selectData, string opShort) +/// @param graph name of databrowser graph +/// @param selectDataComp channel/sweep selection composite, see also SFH_GetArgumentSelect() +/// @param opShort operation name (short) +static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectDataComp, string opShort) variable i, j, rangeStart, rangeEnd, sweepNo, isSingleRange variable chanNr, chanType, cIndex, isSweepBrowser @@ -477,13 +508,14 @@ Function/WAVE SFH_GetSweepsForFormula(string graph, WAVE/WAVE range, WAVE/Z sele string dimLabel, device, dataFolder ASSERT(WindowExists(graph), "graph window does not exist") - isSingleRange = DimSize(range, ROWS) == 1 - if(!WaveExists(selectData) || DimSize(range, ROWS) == 0) - WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) - JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_SWEEP) - return output + WAVE/Z selectData = selectDataComp[%SELECTION] + WAVE/WAVE range = selectDataComp[%RANGE] + + if(!WaveExists(selectData) || !DimSize(range, ROWS)) + return $"" endif SFH_ASSERT(DimSize(selectData, COLS) == 3, "Select data must have 3 columns.") + isSingleRange = DimSize(range, ROWS) == 1 numSelected = DimSize(selectData, ROWS) if(!isSingleRange) @@ -592,6 +624,9 @@ Function/WAVE SFH_GetSweepsForFormula(string graph, WAVE/WAVE range, WAVE/Z sele index += 1 endfor endfor + if(!index) + return $"" + endif Redimension/N=(index) output JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_SWEEP) @@ -856,21 +891,52 @@ Function SFH_TransferFormulaDataWaveNoteAndMeta(WAVE/WAVE input, WAVE/WAVE outpu JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, xLabel) End +/// @brief This function returns an array of select composites that are parsed from one argument +/// that can be either a single select: select(...) +/// or an 1d array of selects: [select(...), select(...), ...] +/// The distinction between a single select an array of selects is done through the SF_META_DATATYPE field in the JSON wavenote +/// select composites are of type SF_DATATYPE_SELECTCOMP, whereas arrays are untyped +/// There is also a quick path for argNum >= numArgs, which is the case for e.g. data() +/// For that case numArgs is 0 and select is expected at argNum 0. Then the result of "select()" is +/// returned (as selectArray with a single element) +/// +/// selectArray is wave reference wave containing select composite wave reference waves with SELECTION, RANGE each. +/// +/// This allows operations with selects as arguments to iterate over different selections given by the user Function/WAVE SFH_GetArgumentSelect(variable jsonId, string jsonPath, string graph, string opShort, variable argNum) - string msg + variable numArgs + string type - WAVE/Z selectData = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, opShort, argNum, defOp = "select()", singleResult = 1) + numArgs = SFH_GetNumberOfArguments(jsonID, jsonPath) + if(argNum < numArgs) - if(WaveExists(selectData)) - sprintf msg, "Argument #%d of operation %s: input must have three columns", argNum, opShort - SFH_ASSERT(DimSize(selectData, COLS) == 3, msg) + WAVE/WAVE selectComp = SF_ResolveDatasetFromJSON(jsonId, jsonPath, graph, argNum) + type = JWN_GetStringFromWaveNote(selectComp, SF_META_DATATYPE) + if(!CmpStr(type, SF_DATATYPE_SELECTCOMP)) + Make/FREE/WAVE selectArray = {selectComp} - sprintf msg, "Argument #%d of operation %s: Must be numeric ", argNum, opShort - SFH_ASSERT(IsNumericWave(selectData), msg) + return selectArray + endif + + SFH_ASSERT(DimSize(selectComp, ROWS) == 1, "Expected a single array") + WAVE array = selectComp[0] + SFH_ASSERT(IsTextWave(array), "Expected a text wave") + + Make/FREE/WAVE/N=(DimSize(array, ROWS)) selectArray = SFH_AttemptDatasetResolve(WaveText(array, row = p), checkWithSFHAssert = 1) + for(WAVE/Z/WAVE selectComp : selectArray) + ASSERT(WaveExists(selectComp), "Expected select composite") + type = JWN_GetStringFromWaveNote(selectComp, SF_META_DATATYPE) + SFH_ASSERT(!CmpStr(type, SF_DATATYPE_SELECTCOMP), "Expected select data as argument") + endfor + + return selectArray endif - return selectData + WAVE selectComp = SF_ExecuteFormula("select()", graph, useVariables = 0) + Make/FREE/WAVE selectArray = {selectComp} + + return selectArray End Function/WAVE SFH_GetEpochNamesFromInfo(WAVE/T epochInfo) @@ -966,9 +1032,9 @@ Function [WAVE/T keys, WAVE/T values] SFH_CreateResultsWaveWithCode(string graph WAVE/T/Z cursorInfos = GetCursorInfos(graph) - WAVE/Z selectData = SF_ExecuteFormula("select()", graph, singleResult = 1, useVariables = 0) - if(WaveExists(selectData)) - values[0][%$"Sweep Formula sweeps/channels"][INDEP_HEADSTAGE] = NumericWaveToList(selectData, ",", colSep = ";") + WAVE/WAVE/Z selectData = SF_ExecuteFormula("select()", graph, useVariables = 0) + if(WaveExists(selectData) && WaveExists(selectData[0])) + values[0][%$"Sweep Formula sweeps/channels"][INDEP_HEADSTAGE] = NumericWaveToList(selectData[0], ",", colSep = ";") endif shPanel = LBV_GetSettingsHistoryPanel(graph) @@ -1458,17 +1524,26 @@ Function [WAVE adaptedRange, WAVE/T epochRangeNames] SFH_GetNumericRangeFromEpoc End /// @brief Attempt a resolution of a dataset based on a string input, returns null wave if not resolvable -Function/WAVE SFH_AttemptDatasetResolve(string element) +Function/WAVE SFH_AttemptDatasetResolve(string ref, [variable checkWithSFHAssert]) - string wName + string wName + variable cond + + checkWithSFHAssert = ParamisDefault(checkWithSFHAssert) ? 0 : !!checkWithSFHAssert - if(strsearch(element, SF_WREF_MARKER, 0) != 0) + if(strsearch(ref, SF_WREF_MARKER, 0) != 0) return $"" endif - wName = element[strlen(SF_WREF_MARKER), Inf] + wName = ref[strlen(SF_WREF_MARKER), Inf] WAVE/Z out = $wName - ASSERT(WaveExists(out), "Referenced wave not found: " + wName) + + cond = WaveExists(out) + if(checkWithSFHAssert) + SFH_ASSERT(cond, "Referenced wave not found: " + wName) + else + ASSERT(cond, "Referenced wave not found: " + wName) + endif return out End @@ -1589,3 +1664,132 @@ Function/WAVE SFH_MoveDatasetHigherIfCompatible(WAVE/WAVE data) return data End + +Function/WAVE SFH_GetSingleSelect(string graph, string opShort, variable sweepNo, variable channelType, variable channelNumber) + + WAVE/WAVE range = SFH_AsDataSet(SFH_GetFullRange()) + WAVE singleSelect = SFH_NewSelectDataWave(1, 1) + singleSelect[0][%SWEEP] = sweepNo + singleSelect[0][%CHANNELTYPE] = channelType + singleSelect[0][%CHANNELNUMBER] = channelNumber + + WAVE/WAVE selectDataComp = GetSFSelectDataComp(graph, opShort) + JWN_SetStringInWaveNote(selectDataComp, SF_META_DATATYPE, SF_DATATYPE_SELECTCOMP) + JWN_SetStringInWaveNote(singleSelect, SF_META_DATATYPE, SF_DATATYPE_SELECT) + JWN_SetStringInWaveNote(range, SF_META_DATATYPE, SF_DATATYPE_SELECTRANGE) + selectDataComp[%SELECTION] = singleSelect + selectDataComp[%RANGE] = range + + Make/FREE/WAVE selectDataArray = {selectDataComp} + + return selectDataArray +End + +Function/S SFH_GetStimsetName(string graph, variable sweepNo, variable channelNumber, variable channelType) + + variable index + + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + WAVE/Z/T textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + ASSERT(WaveExists(numericalValues) && WaveExists(textualValues), "Could not retrieve LNB") + + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, STIM_WAVE_NAME_KEY, channelNumber, channelType, DATA_ACQUISITION_MODE) + ASSERT(WaveExists(settings), "Could not retrieve setName") + + return WaveText(settings, row = index) +End + +Function SFH_IsSetQCPassed(string graph, variable sweepNo, variable channelNumber, variable channelType) + + return SFH_GetIndepPSQEntrySCI(graph, sweepNo, channelNumber, channelType, PSQ_FMT_LBN_SET_PASS) +End + +Function SFH_IsSweepQCPassed(string graph, variable sweepNo, variable channelNumber, variable channelType) + + return SFH_GetIndepPSQEntry(graph, sweepNo, channelNumber, channelType, PSQ_FMT_LBN_SWEEP_PASS) +End + +static Function SFH_GetIndepPSQEntrySCI(string graph, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) + + variable type, waMode, headstage + string key + + [type, waMode, headstage] = SFH_GetAnalysisFunctionType(graph, sweepNo, channelNumber, channelType) + if(IsNaN(type)) + return NaN + endif + + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + ASSERT(WaveExists(numericalValues), "Could not retrieve LNB") + + key = CreateAnaFuncLBNKey(type, psqLNBEntry, query = 1, waMode = waMode) + return GetLastSettingIndepSCI(numericalValues, sweepNo, key, headstage, UNKNOWN_MODE) +End + +static Function SFH_GetIndepPSQEntry(string graph, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) + + variable type, waMode, headstage, passed + string key + + [type, waMode, headstage] = SFH_GetAnalysisFunctionType(graph, sweepNo, channelNumber, channelType) + if(IsNaN(type)) + return NaN + endif + + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + ASSERT(WaveExists(numericalValues), "Could not retrieve LNB") + + if(type == PSQ_RHEOBASE && !CmpStr(psqLNBEntry, PSQ_FMT_LBN_SWEEP_PASS)) + passed = SFH_IsSetQCPassed(graph, sweepNo, channelNumber, channelType) + WAVE sweepsSCI = AFH_GetSweepsFromSameSCI(numericalValues, sweepNo, headstage) + [WAVE passingSweeps, WAVE failingSweeps] = AFH_GetRheobaseSweepsSCISweepQCSplitted(numericalValues, sweepNo, headstage, sweepsSCI, passed) + if(WaveExists(passingSweeps)) + FindValue/V=(sweepNo) passingSweeps + return V_value >= 0 + endif + + return 0 + endif + + key = CreateAnaFuncLBNKey(type, psqLNBEntry, query = 1, waMode = waMode) + return GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE) +End + +static Function [variable type, variable waMode, variable headstage] SFH_GetAnalysisFunctionType(string graph, variable sweepNo, variable channelNumber, variable channelType) + + string key, anaFuncName + variable index, DAC + + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + WAVE/Z/T textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + ASSERT(WaveExists(numericalValues) && WaveExists(textualValues), "Could not retrieve LNB") + + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DAC", channelNumber, channelType, DATA_ACQUISITION_MODE) + if(!WaveExists(settings)) + return [NaN, NaN, NaN] + endif + DAC = settings[index] + + key = "Generic function" + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, key, DAC, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) + if(!WaveExists(settings)) + return [NaN, NaN, NaN] + endif + anaFuncName = WaveText(settings, row = index) + + WAVE/Z settings = GetLastSetting(numericalValues, sweepNo, "DAC", DATA_ACQUISITION_MODE) + if(!WaveExists(settings)) + return [NaN, NaN, NaN] + endif + WAVE headstageIndices = FindIndizes(settings, var = DAC) + if(DimSize(headstageIndices, ROWS) != 1) + return [NaN, NaN, NaN] + endif + headstage = headstageIndices[0] + + WAVE anaFuncTypes = LBN_GetNumericWave(defValue = INVALID_ANALYSIS_FUNCTION) + anaFuncTypes[headstage] = MapAnaFuncToConstant(anaFuncName) + [type, waMode] = AD_GetAnalysisFunctionType(numericalValues, anaFuncTypes, sweepNo, headstage) + + return [type, waMode, headstage] +End diff --git a/Packages/MIES/MIES_SweepFormula_PSX.ipf b/Packages/MIES/MIES_SweepFormula_PSX.ipf index 4106335a7f..c3f2ffefce 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX.ipf @@ -4310,17 +4310,22 @@ Function/WAVE PSX_OperationKernel(variable jsonId, string jsonPath, string graph variable riseTau, decayTau, amp, dt, numPoints, numCombos, i, offset string parameterPath, key - WAVE/WAVE range = SFH_EvaluateRange(jsonId, jsonPath, graph, SF_OP_PSX_KERNEL, 0) + WAVE/WAVE selectDataCompArray = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 0) - WAVE/Z selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 1) + riseTau = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 1, defValue = 1, checkFunc = IsStrictlyPositiveAndFinite) + 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(WaveExists(selectData), "Could not gather sweep data from select statement") + SFH_ASSERT(DimSize(selectDataCompArray, ROWS) == 1, "Only supports a single selection at the moment") + + WAVE/WAVE selectDataComp = selectDataCompArray[0] + + WAVE/Z selectData = selectDataComp[%SELECTION] + WAVE/WAVE range = selectDataComp[%RANGE] - riseTau = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 2, defValue = 1, checkFunc = IsStrictlyPositiveAndFinite) - decayTau = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 3, defValue = 15, checkFunc = IsStrictlyPositiveAndFinite) - amp = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX_KERNEL, 4, defValue = -5, checkFunc = IsFinite) + SFH_ASSERT(WaveExists(selectData), "Could not gather sweep data from select statement") - WAVE/WAVE sweepDataRef = SFH_GetSweepsForFormula(graph, range, selectData, SF_OP_PSX_KERNEL) + WAVE/WAVE sweepDataRef = SFH_GetSweepsForFormula(graph, selectDataCompArray, SF_OP_PSX_KERNEL) numCombos = DimSize(sweepDataRef, ROWS) SFH_ASSERT(numCombos > 0, "Could not fetch sweeps") @@ -4411,17 +4416,22 @@ Function/WAVE PSX_OperationStats(variable jsonId, string jsonPath, string graph) id = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX, 0, checkFunc = IsValidObjectName) - WAVE/WAVE range = SFH_EvaluateRange(jsonId, jsonPath, graph, SF_OP_PSX_STATS, 1) + WAVE/WAVE/Z selectDataCompArray = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 1) + SFH_Assert(WaveExists(selectDataCompArray), "Missing select data") + + SFH_ASSERT(DimSize(selectDataCompArray, ROWS) == 1, "Only supports a single selection at the moment") + + WAVE/WAVE selectDataComp = selectDataCompArray[0] - WAVE/Z selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 2) - SFH_Assert(WaveExists(selectData), "Missing select data") + WAVE/Z selectData = selectDataComp[%SELECTION] + WAVE/WAVE range = selectDataComp[%RANGE] Make/FREE/T allProps = {"amp", "xpos", "xinterval", "tau", "estate", "fstate", "fitresult", "risetime"} - prop = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 3, allowedValues = allProps) + 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, 4, allowedValues = allStates) + stateAsStr = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 3, allowedValues = allStates) Make/FREE/T allPostProc = {"nothing", "stats", "count", "hist", "log10", "nonfinite"} - postProc = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 5, defValue = "nothing", allowedValues = allPostProc) + postProc = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_PSX_STATS, 4, defValue = "nothing", allowedValues = allPostProc) WAVE/WAVE output = PSX_OperationStatsImpl(graph, id, range, selectData, prop, stateAsStr, postProc) diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index a67285d1cc..4a995dce53 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -8728,3 +8728,12 @@ Function/S GetSetParamFolderAsString(channelType) ASSERT(0, "unknown channelType") endif End + +/// @brief returns a composite wave for select +Function/WAVE GetSFSelectDataComp(string graph, string opShort) + + WAVE/WAVE selectDataComp = SFH_CreateSFRefWave(graph, opShort, 2) + SetDimensionLabels(selectDataComp, "SELECTION;RANGE;", ROWS) + + return selectDataComp +End From c6e5d92086a244508f06dc9a636d6a4d62062f51 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 29 May 2024 15:26:07 +0200 Subject: [PATCH 06/38] SF: Add meta tag /DoNotPlot flag to suppress plotting of data Formula results tagged with /DoNotPlot flag = 1 are skipped when plotting. This allows to tag data that can not be plotted (e.g. waveref waves) to be skipped. --- Packages/MIES/MIES_Constants.ipf | 1 + Packages/MIES/MIES_SweepFormula.ipf | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 3965ab8da8..7581819446 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2059,6 +2059,7 @@ StrConstant SF_META_TRACECOLOR = "/TraceColor" // numeric wave StrConstant SF_META_LINESTYLE = "/LineStyle" // number StrConstant SF_META_TRACE_MODE = "/TraceMode" // number StrConstant SF_META_TRACETOFRONT = "/TraceToFront" // number, boolean, defaults to false (0) +StrConstant SF_META_DONOTPLOT = "/DoNotPlot" // number, boolean, defaults to false (0) StrConstant SF_META_USER_GROUP = "/User/" // custom metadata for individual operations, // top-level group with individual entries diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 6233d57bf7..cfb4bd0a21 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1766,6 +1766,9 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode if(!WaveExists(wvResultY)) continue endif + if(JWN_GetNumberFromWaveNote(wvResultY, SF_META_DONOTPLOT) == 1) + continue + endif SFH_ASSERT(!(IsTextWave(wvResultY) && WaveDims(wvResultY) > 1), "Plotter got 2d+ text wave as y data.") @@ -4835,6 +4838,8 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string if(WaveExists(selectResult)) JWN_SetStringInWaveNote(selectResult, SF_META_DATATYPE, SF_DATATYPE_SELECT) endif + JWN_SetNumberInWaveNote(filter.ranges, SF_META_DONOTPLOT, 1) + output[%SELECTION] = selectResult output[%RANGE] = filter.ranges From ac7cf22453e2bf522272fca0a54e086d7fac0f31 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 29 May 2024 17:22:54 +0200 Subject: [PATCH 07/38] SF: Add custom legend for plotting select result - The ranges information is covered in this legend --- Packages/MIES/MIES_SweepFormula.ipf | 1 + Packages/MIES/MIES_SweepFormula_Helpers.ipf | 46 ++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index cfb4bd0a21..878c6c7c05 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -4837,6 +4837,7 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string JWN_SetStringInWaveNote(filter.ranges, SF_META_DATATYPE, SF_DATATYPE_SELECTRANGE) if(WaveExists(selectResult)) JWN_SetStringInWaveNote(selectResult, SF_META_DATATYPE, SF_DATATYPE_SELECT) + JWN_SetStringInWaveNote(output, SF_META_CUSTOM_LEGEND, SFH_CreateLegendFromRanges(selectResult, filter.ranges)) endif JWN_SetNumberInWaveNote(filter.ranges, SF_META_DONOTPLOT, 1) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index c7ecec0792..1b6cc726b7 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1723,7 +1723,7 @@ static Function SFH_GetIndepPSQEntrySCI(string graph, variable sweepNo, variable ASSERT(WaveExists(numericalValues), "Could not retrieve LNB") key = CreateAnaFuncLBNKey(type, psqLNBEntry, query = 1, waMode = waMode) - return GetLastSettingIndepSCI(numericalValues, sweepNo, key, headstage, UNKNOWN_MODE) + return GetLastSettingIndepSCI(numericalValues, sweepNo, key, headstage, UNKNOWN_MODE, defValue = 0) End static Function SFH_GetIndepPSQEntry(string graph, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) @@ -1752,7 +1752,7 @@ static Function SFH_GetIndepPSQEntry(string graph, variable sweepNo, variable ch endif key = CreateAnaFuncLBNKey(type, psqLNBEntry, query = 1, waMode = waMode) - return GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE) + return GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE, defValue = 0) End static Function [variable type, variable waMode, variable headstage] SFH_GetAnalysisFunctionType(string graph, variable sweepNo, variable channelNumber, variable channelType) @@ -1793,3 +1793,45 @@ static Function [variable type, variable waMode, variable headstage] SFH_GetAnal return [type, waMode, headstage] End + +Function/S SFH_CreateLegendFromRanges(WAVE selectData, WAVE/WAVE ranges) + + variable i, prefixPerSelect + string prefix = "" + string legendStr = "" + + if(!DimSize(ranges, ROWS)) + return "" + elseif(DimSize(ranges, ROWS) == 1) + prefix = "All sweeps " + elseif(DimSize(ranges, ROWS) == DimSize(selectData, ROWS)) + prefixPerSelect = 1 + else + SFH_ASSERT(0, "selectData != ranges row number") + endif + WAVE fullRange = SFH_GetFullRange() + + for(WAVE range : ranges) + if(!WaveExists(range)) + continue + endif + if(prefixPerSelect) + sprintf prefix, "%d %s%d ", selectData[i][%SWEEP], ChannelTypeToString(selectData[i][%CHANNELTYPE]), selectData[i][%CHANNELNUMBER] + i += 1 + endif + if(IsNumericWave(range)) + if(SFH_IsFullRange(range)) + sprintf legendStr, "%s\r%sfull range", legendStr, prefix + continue + endif + sprintf legendStr, "%s\r%s%.3f - %.3f ms", legendStr, prefix, range[0], range[1] + elseif(IsTextWave(range)) + sprintf legendStr, "%s\r%sepoch %s", legendStr, prefix, WaveText(range, row = 0) + else + SFH_ASSERT(0, "Unsupported range format") + endif + endfor + legendStr = ReplaceString("\r", legendStr, "Ranges:\r", 1, 1) + + return legendStr +End From 3fb1ac227400115baac2f0dc0310fcd1dbba530f Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 18 Jun 2024 18:04:05 +0200 Subject: [PATCH 08/38] ANA: Adapt SF formula used by analysisfunctions Some analysisfunction call SF with formulas to retrieve data. --- Packages/MIES/MIES_AnalysisFunctions.ipf | 2 +- .../MIES/MIES_AnalysisFunctions_PatchSeq.ipf | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctions.ipf b/Packages/MIES/MIES_AnalysisFunctions.ipf index 591fe4aa2d..db7f74b4b1 100644 --- a/Packages/MIES/MIES_AnalysisFunctions.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions.ipf @@ -1322,7 +1322,7 @@ End /// =============================== ============================================================ /// setvar_DataAcq_OnsetDelayUser Pre DAQ;20 /// Popup_Settings_FixedFreq Pre Sweep;100;Post Sweep;Maximum -/// sweepFormula_formula Pre Set;data(cursors(A,B), select(channels(AD), sweeps())) +/// sweepFormula_formula Pre Set;data(select(selchannels(AD))) /// =============================== ============================================================ /// /// \endrst diff --git a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf index a1a24d2cee..28067b5741 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf @@ -2036,9 +2036,9 @@ static Function [WAVE apfreq, WAVE DAScale] PSQ_DS_GatherFrequencyCurrentData(st str += code sprintf code, "sweepr = [%s]\r", NumericWaveToList(sweeps, ",", trailSep = 0) str += code - sprintf code, "sel = select(channels(AD%d), $sweepr, all)\r", ADC + sprintf code, "sel = select(selchannels(AD%d), selsweeps($sweepr), selvis(all))\r", ADC str += code - sprintf code, "dat = data($trange, $sel)\r" + sprintf code, "dat = data(select(selrange($trange), $sel))\r" str += code sprintf code, "freq = merge(apfrequency($dat, 0))\r" // 0 == FULL str += code @@ -6892,7 +6892,7 @@ Function PSQ_PipetteInBath(string device, STRUCT AnalysisFunction_V3 &s) ASSERT(WaveExists(baselineQCPassedLBN), "Missing baseline QC") baselineQCPassed = baselineQCPassedLBN[s.headstage] - sprintf formula, "store(\"Steady state resistance\", tp(tpss(), select(channels(AD), [%d], all), [0]))", s.sweepNo + sprintf formula, "store(\"Steady state resistance\", tp(tpss(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0]))", s.sweepNo databrowser = PSQ_ExecuteSweepFormula(device, formula) minPipetteResistance = AFH_GetAnalysisParamNumerical("MinPipetteResistance", s.params) @@ -7358,17 +7358,17 @@ Function PSQ_SealEvaluation(string device, STRUCT AnalysisFunction_V3 &s) switch(testpulseGroupSel) case PSQ_SE_TGS_BOTH: formula = "" - sprintf str, "store(\"Steady state resistance (group A)\", tp(tpss(), select(channels(AD), [%d], all), [0, 4, 5, 6]))\r", s.sweepNo + sprintf str, "store(\"Steady state resistance (group A)\", tp(tpss(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0, 4, 5, 6]))\r", s.sweepNo formula += str formula += "and\r" - sprintf str, "store(\"Steady state resistance (group B)\", tp(tpss(), select(channels(AD), [%d], all), [0, 1, 2, 3]))", s.sweepNo + sprintf str, "store(\"Steady state resistance (group B)\", tp(tpss(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0, 1, 2, 3]))", s.sweepNo formula += str break case PSQ_SE_TGS_FIRST: - sprintf formula, "store(\"Steady state resistance (group A)\", tp(tpss(), select(channels(AD), [%d], all), [0]))", s.sweepNo + sprintf formula, "store(\"Steady state resistance (group A)\", tp(tpss(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0]))", s.sweepNo break case PSQ_SE_TGS_SECOND: - sprintf formula, "store(\"Steady state resistance (group B)\", tp(tpss(), select(channels(AD), [%d], all), [0]))", s.sweepNo + sprintf formula, "store(\"Steady state resistance (group B)\", tp(tpss(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0]))", s.sweepNo break default: ASSERT(0, "Invalid testpulseGroupSel: " + num2str(testpulseGroupSel)) @@ -8119,10 +8119,10 @@ static Function PSQ_VM_EvaluateAverageVoltage(string device, variable sweepNo, v if(baselineQCPassed) formula = "" - sprintf str, "store(\"Average U_BLS0\", avg(data(U_BLS0, select(channels(AD), [%d], all))))\r", sweepNo + sprintf str, "store(\"Average U_BLS0\", avg(data(select(selrange(U_BLS0),selchannels(AD), selsweeps(%d), selvis(all)))))\r", sweepNo formula += str formula += "and\r" - sprintf str, "store(\"Average U_BLS1\", avg(data(U_BLS1, select(channels(AD), [%d], all))))\r", sweepNo + sprintf str, "store(\"Average U_BLS1\", avg(data(select(selrange(U_BLS1),selchannels(AD), selsweeps(%d), selvis(all)))))\r", sweepNo formula += str databrowser = PSQ_ExecuteSweepFormula(device, formula) @@ -8510,10 +8510,10 @@ Function PSQ_AccessResistanceSmoke(string device, STRUCT AnalysisFunction_V3 &s) if(baselinePassed) cmd = "" - sprintf str, "store(\"Steady state resistance\", tp(tpss(), select(channels(AD), [%d], all), [0]))", s.sweepNo + sprintf str, "store(\"Steady state resistance\", tp(tpss(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0]))", s.sweepNo cmd += str cmd += "\r and \r" - sprintf str, "store(\"Peak resistance\", tp(tpinst(), select(channels(AD), [%d], all), [0]))", s.sweepNo + sprintf str, "store(\"Peak resistance\", tp(tpinst(), select(selchannels(AD), selsweeps(%d), selvis(all)), [0]))", s.sweepNo cmd += str databrowser = PSQ_ExecuteSweepFormula(device, cmd) From 67c18e7e56411955097ab90e9cc54b6738917181 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 13 Jun 2024 19:46:32 +0200 Subject: [PATCH 09/38] SF: Adapt Tests to new select operation Also added new tests for - selvis - selcm - selrange - selivsccsetqc - selivsccsweepqc - selstimset - adapted LBN setup accordingly with analysisfunction entries for ivscc, stimset wave names Adapted tests for - sweeps -> selsweeps - added new tests for selsweeps - channels -> selchannels - added new tests for operation data accepting an array of selects, handling them independently - select, added new tests for interaction of new sel* filter operations - added tests for select as argument for select combined with additional outer filter arguments - adapted all locations where select(...) is used --- Packages/tests/Basic/UTF_SweepFormula.ipf | 26 +- .../Basic/UTF_SweepFormula_Operations.ipf | 956 +++++++++++++----- .../UTF_SweepFormulaHardware.ipf | 95 +- .../HistoricData/UTF_EpochRecreation.ipf | 2 +- .../UTF_HistoricEpochClipping.ipf | 2 +- Packages/tests/UTF_DataGenerators.ipf | 31 +- Packages/tests/UTF_HelperFunctions.ipf | 63 +- Packages/tests/UserAnalysisFunctions.ipf | 2 +- 8 files changed, 848 insertions(+), 329 deletions(-) diff --git a/Packages/tests/Basic/UTF_SweepFormula.ipf b/Packages/tests/Basic/UTF_SweepFormula.ipf index 8d2a20f8f1..d09e6b7043 100644 --- a/Packages/tests/Basic/UTF_SweepFormula.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula.ipf @@ -1184,7 +1184,7 @@ static Function TestArgSetup() win = CreateFakeSweepData(win, device, sweepNo = 0, sweepGen = FakeSweepDataGeneratorAPF0) win = CreateFakeSweepData(win, device, sweepNo = 1, sweepGen = FakeSweepDataGeneratorAPF1) - formula = "apfrequency(data(cursors(A,B),select(channels(AD),[0,1],all)), 3, 15, time, normoversweepsmin,time)" + formula = "apfrequency(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(0,1),selvis(all))), 3, 15, time, normoversweepsmin,time)" WAVE/WAVE outputRef = SF_ExecuteFormula(formula, win, useVariables = 0) argSetupStack = JWN_GetStringFromWaveNote(outputRef, SF_META_ARGSETUPSTACK) jsonId = JSON_Parse(argSetupStack) @@ -1435,7 +1435,7 @@ static Function TestOperationOrVariableInArray() // operation with simple numeric return - channels returns a (2, 1) array // as elements in an outer array -> (2, 1, 2) array - code = "[channels(AD2), channels(DA3)]" + code = "[selchannels(AD2), selchannels(DA3)]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 0) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1444,7 +1444,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{XOP_CHANNEL_TYPE_ADC, XOP_CHANNEL_TYPE_DAC}}, {{2, 3}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "[123, channels(DA3)]" + code = "[123, selchannels(DA3)]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 0) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1453,7 +1453,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{123, XOP_CHANNEL_TYPE_DAC}}, {{123, 3}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "[channels(AD2), 123]" + code = "[selchannels(AD2), 123]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 0) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1462,7 +1462,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{XOP_CHANNEL_TYPE_ADC, 123}}, {{2, 123}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "[\"abc\", channels(DA3)]" + code = "[\"abc\", selchannels(DA3)]" try WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 0) FAIL() @@ -1470,7 +1470,7 @@ static Function TestOperationOrVariableInArray() PASS() endtry - code = "[channels(DA3), \"abc\"]" + code = "[selchannels(DA3), \"abc\"]" try WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 0) FAIL() @@ -1597,7 +1597,7 @@ static Function TestOperationOrVariableInArray() endtry // with variables - code = "var1 = channels(AD2)\r[$var1, channels(DA3)]" + code = "var1 = selchannels(AD2)\r[$var1, selchannels(DA3)]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 1) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1606,7 +1606,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{XOP_CHANNEL_TYPE_ADC, XOP_CHANNEL_TYPE_DAC}}, {{2, 3}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "var1 = channels(DA3)\r[channels(AD2), $var1]" + code = "var1 = selchannels(DA3)\r[selchannels(AD2), $var1]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 1) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1615,7 +1615,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{XOP_CHANNEL_TYPE_ADC, XOP_CHANNEL_TYPE_DAC}}, {{2, 3}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "var1 = channels(AD2)\r[$var1, 123]" + code = "var1 = selchannels(AD2)\r[$var1, 123]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 1) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1624,7 +1624,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{XOP_CHANNEL_TYPE_ADC, 123}}, {{2, 123}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "var1 = channels(DA3)\r[123, $var1]" + code = "var1 = selchannels(DA3)\r[123, $var1]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 1) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return @@ -1633,7 +1633,7 @@ static Function TestOperationOrVariableInArray() Make/FREE/D ref = {{{123, XOP_CHANNEL_TYPE_DAC}}, {{123, 3}}} CHECK_EQUAL_WAVES(arrayNum, ref, mode = WAVE_DATA) - code = "var1 = channels(DA3)\r[$var1, \"abc\"]" + code = "var1 = selchannels(DA3)\r[$var1, \"abc\"]" try WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 1) FAIL() @@ -1641,7 +1641,7 @@ static Function TestOperationOrVariableInArray() PASS() endtry - code = "var1 = channels(DA3)\r[\"abc\", $var1]" + code = "var1 = selchannels(DA3)\r[\"abc\", $var1]" try WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 1) FAIL() @@ -1859,7 +1859,7 @@ static Function CheckAddArraysInArray() Make/FREE ref = {{5}, {7}} CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA) - code = "[sweeps() + [3, 4] + 1]" + code = "[selsweeps() + [3, 4] + 1]" WAVE/WAVE output = SF_ExecuteFormula(code, win, useVariables = 0) CHECK_WAVE(output, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(output, ROWS), 1) // array return diff --git a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf index 787b4b416c..d6e69f7154 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf @@ -237,7 +237,7 @@ static Function StoreWorksWithMultipleDataSets() [numSweeps, numChannels, WAVE/U/I channels] = FillFakeDatabrowserWindow(win, device, XOP_CHANNEL_TYPE_ADC, textKey, textValue) - str = "store(\"ABCD\", data(cursors(A, B), select(channels(), sweeps())))" + str = "store(\"ABCD\", data(select(selrange(), selchannels(), selsweeps())))" CHECK(ExecuteSweepFormulaInDB(str, win)) WAVE textualResultsValues = GetLogbookWaves(LBT_RESULTS, LBN_TEXTUAL_VALUES) @@ -641,7 +641,7 @@ static Function TestOperationButterworth() CHECK_EQUAL_STR(strRef, dataType) End -static Function TestOperationChannels() +static Function TestOperationSelChannels() string win, str @@ -650,56 +650,56 @@ static Function TestOperationChannels() Make/FREE input = {{0}, {NaN}} SetDimLabel COLS, 0, channelType, input SetDimLabel COLS, 1, channelNumber, input - str = "channels(AD)" + str = "selchannels(AD)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output) Make/FREE input = {{0}, {0}} - str = "channels(AD0)" + str = "selchannels(AD0)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{0, 0}, {0, 1}} - str = "channels(AD0,AD1)" + str = "selchannels(AD0,AD1)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{0, 1}, {0, 1}} - str = "channels(AD0,DA1)" + str = "selchannels(AD0,DA1)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{1, 1}, {0, 0}} - str = "channels(DA0,DA0)" + str = "selchannels(DA0,DA0)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{0, 1}, {NaN, NaN}} - str = "channels(AD,DA)" + str = "selchannels(AD,DA)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{NaN}, {1}} - str = "channels(1)" + str = "selchannels(1)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{NaN, NaN}, {1, 3}} - str = "channels(1,3)" + str = "selchannels(1,3)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{0, 1, NaN}, {1, 2, 3}} - str = "channels(AD1,DA2,3)" + str = "selchannels(AD1,DA2,3)" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) Make/FREE input = {{NaN}, {NaN}} - str = "channels()" + str = "selchannels()" WAVE output = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) - str = "channels(unknown)" + str = "selchannels(unknown)" try SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) FAIL() @@ -1089,7 +1089,7 @@ static Function TestOperationAverage() CHECK_EQUAL_VAR(DimSize(data, ROWS), 1) CHECK_EQUAL_VAR(data[0], 2) - str = "avg(data(cursors(A,B), select(channels(AD), sweeps(), all)), in)" + str = "avg(data(select(selrange(),selchannels(AD),selsweeps(),selvis(all))), in)" WAVE/WAVE dataRef = SF_ExecuteFormula(str, win) CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 4) Make/FREE/D ref = {4.5} @@ -1097,7 +1097,7 @@ static Function TestOperationAverage() CHECK_EQUAL_WAVES(data, ref, mode = WAVE_DATA) endfor - str = "avg(data(cursors(A,B), select(channels(AD), sweeps(), all)), over)" + str = "avg(data(select(selrange(),selchannels(AD),selsweeps(),selvis(all))), over)" WAVE/WAVE dataRef = SF_ExecuteFormula(str, win) CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 1) @@ -1149,6 +1149,323 @@ static Function CheckSweepsMetaData(WAVE/WAVE dataWref, WAVE channelTypes, WAVE endfor End +static Function TestOperationSelsweeps() + + string win, device, str + variable i + variable numSweeps = 4 + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + for(i = 0; i < numSweeps; i += 1) + win = CreateFakeSweepData(win, device, sweepNo = i) + endfor + + str = "selsweeps()" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {0, 1, 2, 3}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selsweeps(2,3)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {2, 3}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selsweeps(1...4)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {1, 2, 3}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selsweeps(1...4, 0)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {1, 2, 3, 0}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selsweeps(abc)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +static Function TestOperationSelvis() + + string win, device, str + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + str = "selvis()" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE/T array = wref[0] + Make/FREE/T ref = {"displayed"} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selvis(displayed)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE/T ref = {"displayed"} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selvis(all)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE/T ref = {"all"} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selvis(invalid_option)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + + str = "selvis(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +static Function TestOperationSelcm() + + string win, device, str + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + str = "selcm()" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_ALL}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(all)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_ALL}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(ic)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_IC}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(vc)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_VC}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(izero)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_IZERO}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(none)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_NONE}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(ic, vc)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_IC | SF_OP_SELECT_CLAMPCODE_VC}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(none, ic, vc, izero)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE array = wref[0] + CHECK_EQUAL_WAVES(array, {SF_OP_SELECT_CLAMPCODE_ALL}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selcm(invalid_option)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + + str = "selcm(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +static Function TestOperationSelstimset() + + string win, device, str + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + str = "selstimset()" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE/T array = wref[0] + Make/FREE/T ref = {"*"} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selstimset(enjoy, the, silence)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE/T ref = {"enjoy", "the", "silence"} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selstimset(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +static Function TestOperationSelIVSCCSweepQC() + + string win, device, str + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + str = "selivsccsweepqc(passed)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE/T array = wref[0] + Make/FREE ref = {SF_OP_SELECT_IVSCCSWEEPQC_PASSED} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selivsccsweepqc(failed)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE ref = {SF_OP_SELECT_IVSCCSWEEPQC_FAILED} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selivsccsweepqc(invalid_option)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + + str = "selivsccsweepqc(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +static Function TestOperationSelRange() + + string win, device, str + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + str = "selrange()" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE/WAVE set = wref[0] + CHECK_WAVE(set, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(set, ROWS), 1) + WAVE array = set[0] + WAVE ref = SFH_GetFullRange() + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selrange([1,2])" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/WAVE set = wref[0] + CHECK_WAVE(set, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(set, ROWS), 1) + WAVE array = set[0] + CHECK_EQUAL_WAVES(array, {1, 2}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selrange(abc)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/WAVE set = wref[0] + CHECK_WAVE(set, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(set, ROWS), 1) + WAVE/T arrayT = set[0] + Make/FREE/T refT = {"abc"} + CHECK_EQUAL_WAVES(arrayT, refT, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selrange(abc, def)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + + str = "selrange(123,456)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +static Function TestOperationSelIVSCCSetQC() + + string win, device, str + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + str = "selivsccsetqc(passed)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_WAVE(wref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(wref, ROWS), 1) + WAVE/T array = wref[0] + Make/FREE ref = {SF_OP_SELECT_IVSCCSWEEPQC_PASSED} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selivsccsetqc(failed)" + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE ref = {SF_OP_SELECT_IVSCCSWEEPQC_FAILED} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "selivsccsetqc(invalid_option)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + + str = "selivsccsetqc(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + static Function TestOperationData() variable i, j, numChannels, sweepNo, sweepCnt, numResultsRef, clampMode @@ -1198,7 +1515,7 @@ static Function TestOperationData() sweepRef3[][4] = (sweepRef3[p][4] & 1 << 2) != 0 sweepCnt = 1 - str = "data(TestEpoch2,select(channels(TTL2),[" + num2istr(3) + "],all))" + str = "data(select(selrange(TestEpoch2),selchannels(TTL2),selsweeps(" + num2istr(3) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * 1 Make/FREE/N=(numResultsRef, 2) ranges @@ -1208,28 +1525,28 @@ static Function TestOperationData() CheckSweepsMetaData(dataWref, {3}, {2}, {3}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data(cursors(A,B),select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3}) CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data([0, inf],select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange([0, inf]),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3}) CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * 1 CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1}) CheckSweepsMetaData(dataWref, {0}, {6}, {0}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(TestEpoch),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 Make/FREE/N=(numResultsRef, 2) ranges @@ -1239,7 +1556,7 @@ static Function TestOperationData() CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data(\"Test*\",select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(\"Test*\"),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 * 2 // 2 epochs starting with Test... @@ -1258,8 +1575,8 @@ static Function TestOperationData() // this part specifies to numerical range 0,2 and 0,4 // Selected is sweep 0, AD, channel 6 and sweep 0, AD, channel 7 sweepCnt = 1 - strSelect = "select(channels(AD),[" + num2istr(sweepNo) + "],all)" - str = "data([[0,0],[2,4]]," + strSelect + ")" + strSelect = "select(selrange([[0,0],[2,4]]),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all))" + str = "data(" + strSelect + ")" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 * 2 // 2 ranges specified @@ -1278,10 +1595,11 @@ static Function TestOperationData() // This part uses a epochs operation with offset to retrieve ranges // Selected is sweep 0, AD, channel 6 and sweep 0, AD, channel 7 // The epoch "TestEpoch" is retrieved for both and offsetted by zero. - sweepCnt = 1 - strSelect = "select(channels(AD),[" + num2istr(sweepNo) + "],all)" - str = "data(epochs(\"TestEpoch\"," + strSelect + ")+[0,0]," + strSelect + ")" - WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) + sweepCnt = 1 + str = "sel = select(selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all))\r" + str = str + "ep = epochs(\"TestEpoch\",$sel)+[0,0]\r" + str = str + "data(select(selrange($ep),$sel))" + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 1) numResultsRef = sweepCnt * numChannels / 2 Make/FREE/N=(numResultsRef, 2) ranges @@ -1293,7 +1611,7 @@ static Function TestOperationData() CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data([\"TestEpoch\",\"TestEpoch1\"],select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange([\"TestEpoch\",\"TestEpoch1\"]),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 * 2 // 2 epochs in array @@ -1311,7 +1629,7 @@ static Function TestOperationData() // Finds the NoShortName epoch sweepCnt = 1 - str = "data(\"!TestEpoch*\",select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(\"!TestEpoch*\"),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 @@ -1322,7 +1640,7 @@ static Function TestOperationData() CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) sweepCnt = 1 - str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo + 1) + "],all))" + str = "data(select(selrange(TestEpoch),selchannels(AD),selsweeps(" + num2istr(sweepNo + 1) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 Make/FREE/N=(numResultsRef, 2) ranges @@ -1332,7 +1650,7 @@ static Function TestOperationData() CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {1, 1}, SF_DATATYPE_SWEEP) sweepCnt = 2 - str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all))" + str = "data(select(selrange(TestEpoch),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 Make/FREE/N=(numResultsRef, 2) ranges @@ -1343,35 +1661,45 @@ static Function TestOperationData() // FAIL Tests // non existing channel - str = "data(TestEpoch,select(channels(AD4),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(TestEpoch),selchannels(AD4),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) REQUIRE_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // non existing sweep - str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo + 1337) + "],all))" + str = "data(select(selrange(TestEpoch),selchannels(AD),selsweeps(" + num2istr(sweepNo + 1337) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) REQUIRE_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // non existing epoch - str = "data(WhatEpochIsThis,select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange(WhatEpochIsThis),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) REQUIRE_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // empty range from epochs - str = "select(channels(AD),[" + num2istr(sweepNo) + "],all)" - str = "data(epochs(WhatEpochIsThis, " + str + "), " + str + ")" - WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) + str = "sel = select(selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all))\r" + str = str + "ep = epochs(WhatEpochIsThis, $sel)\r" + str = str + "data(select(selrange($ep),$sel))" + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 1) REQUIRE_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // one null range from epochs as TestEpoch1 only exists for sweepNo - str = "select(channels(AD6),[" + num2istr(sweepNo) + ", " + num2istr(sweepNo + 1) + "],all)" - str = "data(epochs([TestEpoch1], " + str + "), " + str + ")" - WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) + str = "sel = select(selchannels(AD6),selsweeps(" + num2istr(sweepNo) + ", " + num2istr(sweepNo + 1) + "),selvis(all))\r" + str = str + "ep = epochs(TestEpoch1, $sel)\r" + str = str + "data(select(selrange($ep),$sel))" + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 1) REQUIRE_EQUAL_VAR(DimSize(dataWref, ROWS), 1) REQUIRE_EQUAL_VAR(DimSize(dataWref[0], ROWS), 5) + str = "data(1, 2)" + try + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + // range begin - str = "data([12, 10],select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange([12, 10]),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" try WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() @@ -1380,7 +1708,7 @@ static Function TestOperationData() endtry // range end - str = "data([0, 11],select(channels(AD),[" + num2istr(sweepNo) + "],all))" + str = "data(select(selrange([0, 11]),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all)))" try WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() @@ -1390,12 +1718,38 @@ static Function TestOperationData() // One sweep does not exist, it is not result of select, we end up with one sweep sweepCnt = 1 - str = "data(cursors(A,B),select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1337) + "],all))" + str = "data(select(selrange(),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1337) + "),selvis(all)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels / 2 CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3}) CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) + sweepCnt = 1 + str = "sel1 = select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))\r" + str = str + "sel2 = select(selrange(),selchannels(AD7),selsweeps(" + num2istr(sweepNo) + "),selvis(all))\r" + str = str + "data([$sel1, $sel2])" + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 1) + numResultsRef = sweepCnt * numChannels / 2 + CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3}) + CheckSweepsMetaData(dataWref, {0, 0}, {6, 7}, {0, 0}, SF_DATATYPE_SWEEP) + + sweepCnt = 1 + str = "sel1 = select(selrange([0, 2]),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))\r" + str = str + "sel2 = select(selrange([0, 4]),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))\r" + str = str + "data([$sel1, $sel2])" + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 1) + numResultsRef = sweepCnt * numChannels / 2 + + Make/FREE/N=(numResultsRef, 2) ranges + ranges[][0] = 0 + ranges[][1] = {2, 4} + ranges[0][0] = 0 + ranges[0][1] = 2 + ranges[1][0] = 0 + ranges[1][1] = 4 + CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3}, ranges = ranges) + CheckSweepsMetaData(dataWref, {0, 0}, {6, 6}, {0, 0}, SF_DATATYPE_SWEEP) + // Setup graph with equivalent data TUD_Clear(win) @@ -1409,8 +1763,9 @@ static Function TestOperationData() Extract input, $name, q == i && r == j WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace - TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode"}, \ - {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), StringFromList(j, channelNumberList), num2istr(clampMode)}) + WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), StringFromList(j, channelNumberList), num2istr(clampMode), "NaN"}) endfor endfor @@ -1418,12 +1773,12 @@ static Function TestOperationData() dataRef[][][] = sweepRef[p] sweepCnt = 2 - str = "data(cursors(A,B),select())" + str = "data(select())" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) numResultsRef = sweepCnt * numChannels CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3, 0, 2, 1, 3, 0, 2}) CheckSweepsMetaData(dataWref, {0, 0, 1, 1, 0, 0, 1, 1}, {6, 7, 2, 3, 6, 7, 2, 3}, {0, 0, 0, 0, 1, 1, 1, 1}, SF_DATATYPE_SWEEP) - str = "data(cursors(A,B))" + str = "data()" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CheckSweepsFromData(dataWref, sweepRef, numResultsref, {1, 3, 0, 2, 1, 3, 0, 2}) CheckSweepsMetaData(dataWref, {0, 0, 1, 1, 0, 0, 1, 1}, {6, 7, 2, 3, 6, 7, 2, 3}, {0, 0, 0, 0, 1, 1, 1, 1}, SF_DATATYPE_SWEEP) @@ -1483,7 +1838,7 @@ static Function TestOperationPowerSpectrum() win = CreateFakeSweepData(win, device, sweepNo = sweepNo, sweepGen = FakeSweepDataGeneratorPS) win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 1, sweepGen = FakeSweepDataGeneratorPS) - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)))" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(1, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] @@ -1497,7 +1852,7 @@ static Function TestOperationPowerSpectrum() strRef = "^2" CHECK_EQUAL_STR(strRef, str) - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)),dB)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))),dB)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(1, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] @@ -1508,7 +1863,7 @@ static Function TestOperationPowerSpectrum() strRef = "dB" CHECK_EQUAL_STR(strRef, str) - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)),normalized)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))),normalized)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(1, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] @@ -1519,7 +1874,7 @@ static Function TestOperationPowerSpectrum() strRef = "mean(^2)" CHECK_EQUAL_STR(strRef, str) - str = "powerspectrum(data(cursors(A,B),select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)),dB,avg)" + str = "powerspectrum(data(select(selrange(),selchannels(AD),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "),selvis(all))),dB,avg)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(2, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] @@ -1531,7 +1886,7 @@ static Function TestOperationPowerSpectrum() CHECK_CLOSE_VAR(V_maxLoc, 100, tol = 0.01) CHECK_CLOSE_VAR(V_max, 88, tol = 0.01) - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)),dB,noavg,100)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))),dB,noavg,100)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(1, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] @@ -1539,14 +1894,14 @@ static Function TestOperationPowerSpectrum() CHECK_EQUAL_VAR(1, DimSize(data, ROWS)) CHECK_CLOSE_VAR(data[0], 1.32, tol = 0.01) - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)),dB,noavg,0,2000)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))),dB,noavg,0,2000)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(1, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] val = IndexToScale(data, DimSize(data, ROWS), ROWS) CHECK_CLOSE_VAR(val, 2000, tol = 0.001) - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)),dB,noavg,0,1000,HFT248D)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))),dB,noavg,0,1000,HFT248D)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(1, DimSize(dataWref, ROWS)) WAVE data = dataWref[0] @@ -1566,7 +1921,7 @@ static Function TestOperationPowerSpectrum() endtry try - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)), not_exist)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))), not_exist)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() catch @@ -1574,7 +1929,7 @@ static Function TestOperationPowerSpectrum() endtry try - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)), dB, not_exist)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))), dB, not_exist)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() catch @@ -1582,7 +1937,7 @@ static Function TestOperationPowerSpectrum() endtry try - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)), dB, avg, -1)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))), dB, avg, -1)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() catch @@ -1590,7 +1945,7 @@ static Function TestOperationPowerSpectrum() endtry try - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)), dB, avg, 0, -1)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))), dB, avg, 0, -1)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() catch @@ -1598,7 +1953,7 @@ static Function TestOperationPowerSpectrum() endtry try - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)), dB, avg, 0, 1000, not_exist)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))), dB, avg, 0, 1000, not_exist)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() catch @@ -1606,7 +1961,7 @@ static Function TestOperationPowerSpectrum() endtry try - str = "powerspectrum(data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all)), dB, avg, 0, 1000, Bartlet, not_exist)" + str = "powerspectrum(data(select(selrange(),selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))), dB, avg, 0, 1000, Bartlet, not_exist)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() catch @@ -1632,12 +1987,12 @@ static Function TestOperationLabNotebook() channelsRef[] = channels[trunc(p / numChannels)][mod(p, numChannels)] str = "labnotebook(ADC)" TestOperationLabnotebookHelper(win, str, channelsRef) - str = "labnotebook(ADC,select(channels(AD),0..." + num2istr(numSweeps) + "))" + str = "labnotebook(ADC,select(selchannels(AD),selsweeps(0..." + num2istr(numSweeps) + ")))" TestOperationLabnotebookHelper(win, str, channelsRef) - str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + "ADC, select(channels(AD),0..." + num2istr(numSweeps) + "),UNKNOWN_MODE)" + str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + "ADC, select(selchannels(AD),selsweeps(0..." + num2istr(numSweeps) + ")),UNKNOWN_MODE)" TestOperationLabnotebookHelper(win, str, channelsRef) - str = "labnotebook(ADC, select(channels(AD12),-1))" + str = "labnotebook(ADC, select(selchannels(AD12),selsweeps(-1)))" WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) @@ -1697,47 +2052,47 @@ static Function TestOperationEpochs() chanNr[] = mod(p, activeChannelsDA) * 2 CheckSweepsMetaData(dataWref, chanType, chanNr, sweeps, SF_DATATYPE_EPOCHS) - str = "epochs(\"E0_PT_P48\", select(channels(DA0), 0))" + str = "epochs(\"E0_PT_P48\", select(selchannels(DA0), selsweeps(0)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 1) Make/FREE/D refData = {500, 510} WAVE data = dataWref[0] REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", select(channels(DA4), 0))" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA4), selsweeps(0)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 1) Make/FREE/D refData = {503, 510} WAVE data = dataWref[0] REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", select(channels(DA4), 0), range)" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA4), selsweeps(0)), range)" WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) Make/FREE/D refData = {503, 510} REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", select(channels(DA4),0), treelevel)" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA4), selsweeps(0)), treelevel)" WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) Make/FREE/D refData = {3} REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", select(channels(DA4), 9), name)" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA4), selsweeps(9)), name)" WAVE/T dataT = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) Make/FREE/T refDataT = {"E0_PT_P48_B"} REQUIRE_EQUAL_WAVES(dataT, refDataT, mode = WAVE_DATA) - str = "epochs(\"NoShortName\", select(channels(DA4), 9), name)" + str = "epochs(\"NoShortName\", select(selchannels(DA4), selsweeps(9)), name)" WAVE/T dataT = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) Make/FREE/T refDataT = {"NoShortName"} REQUIRE_EQUAL_WAVES(dataT, refDataT, mode = WAVE_DATA) // works case-insensitive - str = "epochs(\"e0_pt_p48_B\", select(channels(DA4), 9), name)" + str = "epochs(\"e0_pt_p48_B\", select(selchannels(DA4), selsweeps(9)), name)" WAVE/T dataT = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) Make/FREE/T refDataT = {"E0_PT_P48_B"} REQUIRE_EQUAL_WAVES(dataT, refDataT, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", select(channels(DA), 0..." + num2istr(numSweeps) + "))" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA), selsweeps(0..." + num2istr(numSweeps) + ")))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), numSweeps * activeChannelsDA) Make/FREE/D refData = {503, 510} @@ -1751,7 +2106,7 @@ static Function TestOperationEpochs() sweepNumbers = trunc(p / activeChannelsDA) CheckSweepsMetaData(dataWref, channelTypes, channelNumbers, sweepNumbers, SF_DATATYPE_EPOCHS) - str = "epochs(\"E0_PT_P48_*\", select(channels(DA), 0))" + str = "epochs(\"E0_PT_P48_*\", select(selchannels(DA), selsweeps(0)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), activeChannelsDA) Make/FREE/D refData = {503, 510} @@ -1761,12 +2116,12 @@ static Function TestOperationEpochs() // find epoch without shortname epochLongName = RemoveEnding(epoch2, ";") - str = "epochs(\"" + epochLongName + "\", select(channels(DA), 0))" + str = "epochs(\"" + epochLongName + "\", select(selchannels(DA), selsweeps(0)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), activeChannelsDA) // finds only epoch without shortname from test epochs - str = "epochs(\"!E0_PT_P48*\", select(channels(DA), 0))" + str = "epochs(\"!E0_PT_P48*\", select(selchannels(DA), selsweeps(0)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), activeChannelsDA) CHECK_EQUAL_VAR(DimSize(dataWref, COLS), 0) @@ -1779,7 +2134,7 @@ static Function TestOperationEpochs() // the first wildcard matches both setup epochs, the second only the first setup epoch // only unique epochs are returned, thus two - str = "epochs([\"E0_PT_*\",\"E0_PT_P48*\"], select(channels(DA), 0))" + str = "epochs([\"E0_PT_*\",\"E0_PT_P48*\"], select(selchannels(DA), selsweeps(0)))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 4) Make/FREE/D refData = {{500, 510}, {503, 510}} @@ -1789,22 +2144,22 @@ static Function TestOperationEpochs() endfor // channel(s) with no epochs - str = "epochs(\"E0_PT_P48_B\", select(channels(AD), 0..." + num2istr(numSweeps) + "))" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(AD), selsweeps(0..." + num2istr(numSweeps) + ")))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // channels with epochs, but name that does not match any epoch - str = "epochs(\"does_not_exist\", select(channels(DA), 0..." + num2istr(numSweeps) + "))" + str = "epochs(\"does_not_exist\", select(selchannels(DA), selsweeps(0..." + num2istr(numSweeps) + ")))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // invalid sweep - str = "epochs(\"E0_PT_P48_B\", select(channels(DA), " + num2istr(numSweeps) + "))" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA), selsweeps(" + num2istr(numSweeps) + ")))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 0) // invalid type - str = "epochs(\"E0_PT_P48_B\", select(channels(DA), 0..." + num2istr(numSweeps) + "), invalid_type)" + str = "epochs(\"E0_PT_P48_B\", select(selchannels(DA), selsweeps(0..." + num2istr(numSweeps) + ")), invalid_type)" try WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() @@ -1931,6 +2286,38 @@ static Function TestOperationFit() endtry End +static Function TestOperationSelectCompareWithFullRange(string win, string formula, WAVE dataRef) + + WAVE/WAVE comp = SF_ExecuteFormula(formula, win, useVariables = 0) + CHECK_WAVE(comp, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(comp, ROWS), 2) + WAVE dataSel = comp[0] + WAVE/WAVE rngSet = comp[1] + CHECK_WAVE(rngSet, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(rngSet, ROWS), 1) + WAVE dataRng = rngSet[0] + CHECK_EQUAL_WAVES(dataRef, dataSel, mode = WAVE_DATA | DIMENSION_SIZES) + WAVE rngRef = SFH_GetFullRange() + CHECK_EQUAL_WAVES(rngRef, dataRng, mode = WAVE_DATA | DIMENSION_SIZES) +End + +// UTF_TD_GENERATOR DataGenerators#SF_TestOperationSelectFails +static Function TestOperationSelectFails([string str]) + + string win, device + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + win = CreateFakeSweepData(win, device, sweepNo = 0) + + try + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + +End + static Function TestOperationSelect() variable numChannels, sweepNo @@ -1950,125 +2337,131 @@ static Function TestOperationSelect() sweepNo = 0 win = CreateFakeSweepData(win, device, sweepNo = sweepNo) - win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 1) - win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 2) - win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 3) numChannels = 4 // from LBN creation in CreateFakeSweepData->PrepareLBN_IGNORE -> DA2, AD6, DA3, AD7 Make/FREE/N=0 sweepTemplate WAVE sweepRef = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) - Make/FREE/N=(4, 3) dataRef + Make/FREE/N=(4, 4) dataRef dataRef[][0] = sweepNo dataRef[0, 1][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[2, 3][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) - dataRef[][2] = {6, 7, 2, 3} // AD6, AD7, DA2, DA3 - str = "select(channels(),[" + num2istr(sweepNo) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {6, 7, 2, 3} // AD6, AD7, DA2, DA3 + dataRef[][3] = NaN + str = "select(selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 1) + win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 2) + win = CreateFakeSweepData(win, device, sweepNo = sweepNo + 3) - Make/FREE/N=(2, 3) dataRef + Make/FREE/N=(2, 4) dataRef dataRef[][0] = sweepNo dataRef[0][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[1][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) - dataRef[][2] = {6, 2} // AD6, DA2 - str = "select(channels(2, 6),[" + num2istr(sweepNo) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {6, 2} // AD6, DA2 + dataRef[][3] = NaN + str = "select(selchannels(2, 6),selsweeps(" + num2istr(sweepNo) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(2, 3) dataRef + Make/FREE/N=(2, 4) dataRef dataRef[][0] = sweepNo dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) - dataRef[][2] = {6, 7} // AD6, AD7 - str = "select(channels(AD),[" + num2istr(sweepNo) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {6, 7} // AD6, AD7 + dataRef[][3] = NaN + str = "select(selchannels(AD),selsweeps(" + num2istr(sweepNo) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) // non-existing sweeps are ignored - str = "select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1337) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + str = "select(selchannels(AD),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1337) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(1, 3) dataRef + Make/FREE/N=(1, 4) dataRef dataRef[][0] = 3 dataRef[][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) - dataRef[][2] = {0} // DA0 (unassoc) - str = "select(channels(DA0),[" + num2istr(3) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {0} // DA0 (unassoc) + dataRef[][3] = NaN + str = "select(selchannels(DA0),selsweeps(" + num2istr(3) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(1, 3) dataRef + Make/FREE/N=(1, 4) dataRef dataRef[][0] = 3 dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) - dataRef[][2] = {1} // AD1 (unassoc) - str = "select(channels(AD1),[" + num2istr(3) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {1} // AD1 (unassoc) + dataRef[][3] = NaN + str = "select(selchannels(AD1),selsweeps(" + num2istr(3) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(1, 3) dataRef + Make/FREE/N=(1, 4) dataRef dataRef[][0] = 3 dataRef[][1] = WhichListItem("TTL", XOP_CHANNEL_NAMES) - dataRef[][2] = {2} // TTL2 - str = "select(channels(TTL2),[" + num2istr(3) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {2} // TTL2 + dataRef[][3] = NaN + str = "select(selchannels(TTL2),selsweeps(" + num2istr(3) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) // clamp mode set filters has no effect on TTL - str = "select(channels(TTL2),[" + num2istr(3) + "],all,vc)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + str = "select(selchannels(TTL2),selsweeps(" + num2istr(3) + "),selvis(all),selcm(vc))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) // clamp mode set filters on DA/AD - str = "select(channels(AD1),[" + num2istr(3) + "],all,vc)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - CHECK_WAVE(data, NULL_WAVE) - str = "select(channels(DA0),[" + num2istr(3) + "],all,vc)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - CHECK_WAVE(data, NULL_WAVE) - - Make/FREE/N=(4, 3) dataRef - dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} // sweep 0, 1 with 2 AD channels each + str = "select(selchannels(AD1),selsweeps(" + num2istr(3) + "),selvis(all),selcm(vc))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + str = "select(selchannels(DA0),selsweeps(" + num2istr(3) + "),selvis(all),selcm(vc))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + + Make/FREE/N=(4, 4) dataRef + dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} // sweep 0, 1 with 2 AD channels each dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) - dataRef[][2] = {6, 7, 6, 7} // AD6, AD7, AD6, AD7 - str = "select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {6, 7, 6, 7} // AD6, AD7, AD6, AD7 + dataRef[][3] = NaN + str = "select(selchannels(AD),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(2, 3) dataRef + Make/FREE/N=(2, 4) dataRef dataRef[][0] = {sweepNo, sweepNo + 1} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) - dataRef[][2] = {6, 6} // AD6, AD6 - str = "select(channels(AD6),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {6, 6} // AD6, AD6 + dataRef[][3] = NaN + str = "select(selchannels(AD6),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(6, 3) dataRef + Make/FREE/N=(6, 4) dataRef dataRef[][0] = {sweepNo, sweepNo, sweepNo, sweepNo + 1, sweepNo + 1, sweepNo + 1} chanList = "AD;DA;DA;AD;DA;DA;" dataRef[][1] = WhichListItem(StringFromList(p, chanList), XOP_CHANNEL_NAMES) - dataRef[][2] = {6, 2, 3, 6, 2, 3} // AD6, DA2, DA3, AD6, DA2, DA3 - str = "select(channels(AD6, DA),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][2] = {6, 2, 3, 6, 2, 3} // AD6, DA2, DA3, AD6, DA2, DA3 + dataRef[][3] = NaN + str = "select(selchannels(AD6, DA),selsweeps(" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) // No existing sweeps - str = "select(channels(AD6, DA),[" + num2istr(sweepNo + 1337) + "],all)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE(!WaveExists(data)) + str = "select(selchannels(AD6, DA),selsweeps(" + num2istr(sweepNo + 1337) + "),selvis(all))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) // No existing channels - str = "select(channels(AD0),[" + num2istr(sweepNo) + "],all)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE(!WaveExists(data)) - - // Invalid channels - try - str = "select([0, 6],[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - FAIL() - catch - PASS() - endtry + str = "select(selchannels(AD0),selsweeps(" + num2istr(sweepNo) + "),selvis(all))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + + str = "select(selvis(all),selrange([1,2]))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/WAVE rngSet = comp[1] + WAVE dataRng = rngSet[0] + CHECK_EQUAL_WAVES(dataRng, {1, 2}, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "select(selvis(all),select(selrange([1,2])))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/WAVE rngSet = comp[1] + WAVE dataRng = rngSet[0] + CHECK_EQUAL_WAVES(dataRng, {-Inf, Inf}, mode = WAVE_DATA | DIMENSION_SIZES) // Setup graph with equivalent data for displayed parameter TUD_Clear(win) @@ -2083,13 +2476,14 @@ static Function TestOperationSelect() Extract input, $name, q == i && r == j WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace - TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber"}, \ - {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList)}) + WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), "NaN"}) endfor endfor sweepNo = 0 - Make/FREE/N=(8, 3) dataRef + Make/FREE/N=(8, 4) dataRef dataRef[0, 3][0] = sweepNo dataRef[4, 7][0] = sweepNo + 1 dataRef[0, 1][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) @@ -2097,108 +2491,152 @@ static Function TestOperationSelect() dataRef[4, 5][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[6, 7][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) dataRef[][2] = {6, 7, 2, 3, 6, 7, 2, 3} + dataRef[][3] = NaN str = "select()" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select())" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(4, 3) dataRef + Make/FREE/N=(4, 4) dataRef dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {6, 7, 6, 7} - str = "select(channels(AD),sweeps(),displayed)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) - - str = "select(channels(AD),sweeps())" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(AD),selsweeps(),selvis(displayed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(AD),selsweeps())" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(2, 3) dataRef + Make/FREE/N=(2, 4) dataRef dataRef[][0] = {sweepNo, sweepNo + 1} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {6, 6} - str = "select(channels(AD6),sweeps(),displayed,all)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(AD6),selsweeps(),selvis(displayed),selcm(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6)),selchannels(AD))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6)),selchannels(6))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6)),selchannels(5))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + str = "select(select(selchannels(AD6)),selchannels(DA))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + str = "select(select(selchannels(AD6),selsweeps()),selsweeps(0,1))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6),selsweeps()),selsweeps(2))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + str = "select(select(selchannels(AD6)),selvis(displayed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6)),selvis(all))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + Make/FREE/N=(1, 4) dataRef + dataRef[][0] = {sweepNo} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6} + dataRef[][3] = NaN + str = "select(select(selchannels(AD6),selcm(all)),selcm(ic))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + dataRef[][0] = {sweepNo + 1} + str = "select(select(selchannels(AD6),selcm(all)),selcm(vc))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + dataRef[][0] = {sweepNo} + str = "select(select(selchannels(AD)),selstimset(stimsetSweep0HS0),selsweeps(0))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD),selstimset(\"stimset*\")),selstimset(stimsetSweep0HS0),selsweeps(0))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD),selstimset(\"stimsetSweep1*\")),selstimset(\"stimsetSweep0*\"),selsweeps(0))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + + Make/FREE/N=(1, 4) dataRef + dataRef[][0] = {sweepNo} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6} + dataRef[][3] = NaN + str = "select(selchannels(AD6),selivsccsetqc(passed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6)),selivsccsetqc(passed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(AD6),selsweeps(0),selivsccsetqc(failed))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + str = "select(select(selchannels(AD6),selivsccsetqc(passed)),selivsccsetqc(failed))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) - Make/FREE/N=(1, 3) dataRef + dataRef[][0] = {sweepNo + 1} + str = "select(selchannels(AD6),selivsccsweepqc(passed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(select(selchannels(AD6)),selivsccsweepqc(passed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(AD6),selsweeps(1),selivsccsweepqc(failed))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + str = "select(select(selchannels(AD6),selivsccsweepqc(passed)),selivsccsweepqc(failed))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {sweepNo} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {6} - str = "select(channels(AD6),sweeps(),displayed, ic)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(AD6),selsweeps(),selvis(displayed), selcm(ic))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(1, 3) dataRef + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {sweepNo + 1} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {6} - str = "select(channels(AD6),sweeps(),displayed, vc)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(AD6),selsweeps(),selvis(displayed), selcm(vc))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - str = "select(channels(AD6),sweeps(),displayed, izero)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - CHECK(!WaveExists(data)) + str = "select(selchannels(AD6),selsweeps(),selvis(displayed), selcm(izero))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} dataRef[][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) dataRef[][2] = {2, 3, 2, 3} - str = "select(channels(DA),sweeps())" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(DA),selsweeps())" + TestOperationSelectCompareWithFullRange(win, str, dataRef) - Make/FREE/N=(6, 3) dataRef + Make/FREE/N=(6, 4) dataRef dataRef[][0] = {sweepNo, sweepNo, sweepNo, sweepNo + 1, sweepNo + 1, sweepNo + 1} chanList = "AD;AD;DA;AD;AD;DA;" dataRef[][1] = WhichListItem(StringFromList(p, chanList), XOP_CHANNEL_NAMES) dataRef[][2] = {6, 7, 2, 6, 7, 2} - str = "select(channels(DA2, AD),sweeps())" // note: channels are sorted AD, DA... - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(DA2, AD),selsweeps())" // note: channels are sorted AD, DA... + TestOperationSelectCompareWithFullRange(win, str, dataRef) // No existing sweeps - str = "select(channels(AD6, DA),[" + num2istr(sweepNo + 1337) + "])" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE(!WaveExists(data)) + str = "select(selchannels(AD6, DA),selsweeps(" + num2istr(sweepNo + 1337) + "))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) // No existing channels - str = "select(channels(AD0),[" + num2istr(sweepNo) + "])" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE(!WaveExists(data)) - - // Invalid channels - try - str = "select([0, 6],[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "])" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - FAIL() - catch - PASS() - endtry - - str = "select(1)" - try - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - FAIL() - catch - PASS() - endtry - - str = "select(channels(AD), sweeps(), 1)" - try - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - FAIL() - catch - PASS() - endtry - - str = "select(channels(AD), sweeps(), all, 1)" - try - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - FAIL() - catch - PASS() - endtry + str = "select(selchannels(AD0),selsweeps(" + num2istr(sweepNo) + "))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) // Setup graph for unassoc DA/AD and TTL numSweeps = 1 @@ -2218,46 +2656,50 @@ static Function TestOperationSelect() Extract input, $name, q == i && r == j WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace - TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "AssociatedHeadstage"}, \ - {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), num2istr(0)}) + WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "AssociatedHeadstage", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), num2istr(0), "NaN"}) endfor endfor - Make/FREE/N=(1, 3) dataRef + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {0} dataRef[][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) dataRef[][2] = {0} - str = "select(channels(DA0),sweeps(),displayed)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) - - Make/FREE/N=(1, 3) dataRef + dataRef[][3] = NaN + str = "select(selchannels(DA0),selsweeps(),selvis(displayed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(DA0),selsweeps(),selvis(displayed),selcm(none))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(DA0),selsweeps(),selvis(displayed),selcm(vc))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {0} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {1} - str = "select(channels(AD1),sweeps(),displayed)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) - - Make/FREE/N=(1, 3) dataRef + dataRef[][3] = NaN + str = "select(selchannels(AD1),selsweeps(),selvis(displayed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(AD1),selsweeps(),selvis(displayed),selcm(none))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(AD1),selsweeps(),selvis(displayed),selcm(vc))" + WAVE/WAVE comp = SF_ExecuteFormula(str, win, useVariables = 0) + WAVE/Z dataSel = comp[0] + CHECK_WAVE(dataSel, NULL_WAVE) + + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {0} dataRef[][1] = WhichListItem("TTL", XOP_CHANNEL_NAMES) dataRef[][2] = {2} - str = "select(channels(TTL2),sweeps(),displayed)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + dataRef[][3] = NaN + str = "select(selchannels(TTL2),selsweeps(),selvis(displayed))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) // clamp mode set filters has no effect on TTL - str = "select(channels(TTL2),sweeps(),displayed,vc)" - WAVE data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) - - // clamp mode set filters on DA/AD - str = "select(channels(AD1),sweeps(),displayed,vc)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - CHECK_WAVE(data, NULL_WAVE) - str = "select(channels(DA0),sweeps(),displayed,vc)" - WAVE/Z data = SF_ExecuteFormula(str, win, singleResult = 1, useVariables = 0) - CHECK_WAVE(data, NULL_WAVE) + str = "select(selchannels(TTL2),selsweeps(),selvis(displayed),selcm(vc))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) // workaround permanent waves being present wvList = GetListOfObjects(GetDataFolderDFR(), "data*") diff --git a/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf b/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf index 00fa3273a1..a29e4fdee7 100644 --- a/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf +++ b/Packages/tests/HardwareBasic/UTF_SweepFormulaHardware.ipf @@ -55,7 +55,7 @@ static Function TestSweepFormulaNoDataPlotted(string device) [dbPanel, plotWin] = GetNewDBforSF_IGNORE() - formula = "data(cursors(A,B),select(channels(AD10),sweeps(),all))" + formula = "data(select(selrange(cursors(A,B)),selchannels(AD10),selsweeps(),selvis(all)))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) PASS() @@ -68,7 +68,7 @@ static Function TestSweepFormulaAnnotations(string device) [dbPanel, plotWin] = GetNewDBforSF_IGNORE() - formula = "data(cursors(A,B),select(channels(AD),sweeps(),all))" + formula = "data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all)))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) annoInfo = AnnotationInfo(plotWin, "metadata", 1) @@ -82,7 +82,7 @@ static Function TestSweepFormulaAnnotations(string device) str = StringByKey("TEXT", annoInfo) CHECK_EQUAL_STR(textRef, str) - formula = "avg(data(cursors(A,B),select(channels(AD),sweeps(),all)))" + formula = "avg(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all))))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) annoInfo = AnnotationInfo(plotWin, "metadata", 1) @@ -96,7 +96,7 @@ static Function TestSweepFormulaAnnotations(string device) str = StringByKey("TEXT", annoInfo) CHECK_EQUAL_STR(textRef, str) - formula = "avg(avg(data(cursors(A,B),select(channels(AD),sweeps(),all))))" + formula = "avg(avg(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all)))))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) annoInfo = AnnotationInfo(plotWin, "metadata", 1) @@ -129,7 +129,7 @@ static Function TestSweepFormulaAxisLabels(string device) [dbPanel, plotWin] = GetNewDBforSF_IGNORE() - formula = "avg(data(cursors(A,B),select(channels(AD),sweeps(),all)))" + formula = "avg(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all))))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) @@ -150,7 +150,7 @@ static Function TestSweepFormulaFittingXAxis(string device) [dbPanel, plotWin] = GetNewDBforSF_IGNORE() - formula = "min(data(cursors(A,B),select(channels(AD),sweeps(),all)))\rvs\r1...7" + formula = "min(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all))))\rvs\r1...7" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) @@ -177,7 +177,7 @@ static Function TestSweepFormulaDefaultMetaDataInheritance(string device) [dbPanel, plotWin] = GetNewDBforSF_IGNORE() - formula = "min(butterworth(integrate(derivative(data(cursors(A,B),select(channels(AD),sweeps(),all)))),4,100,4))" + formula = "min(butterworth(integrate(derivative(data(select(selrange(cursors(A,B)),selchannels(AD),selsweeps(),selvis(all))))),4,100,4))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) @@ -208,46 +208,49 @@ static Function TestSweepFormulaSelectClampMode(string device) [dbPanel, plotWin] = GetNewDBforSF_IGNORE() - formula = "select(channels(AD),sweeps(),all,all)" + formula = "select(selchannels(AD),selsweeps(),selvis(all),selcm(all))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) WAVE/T traces = ListToTextWave(TraceNameList(plotWin, ";", 1), ";") Sort/A traces, traces - CHECK_EQUAL_VAR(3, DimSize(traces, ROWS)) + CHECK_EQUAL_VAR(4, DimSize(traces, ROWS)) WAVE wY = TraceNameToWaveRef(plotWin, traces[0]) - Make/FREE/N=(6, 3) dataRef + Make/FREE/N=(6, 4) dataRef dataRef[][0] = {0, 0, 1, 1, 2, 2} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {1, 2, 1, 2, 1, 2} + dataRef[][3] = NaN CHECK_EQUAL_WAVES(dataRef, wY, mode = WAVE_DATA | DIMENSION_SIZES) - formula = "select(channels(AD),sweeps(),all,ic)" + formula = "select(selchannels(AD),selsweeps(),selvis(all),selcm(ic))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) WAVE/T traces = ListToTextWave(TraceNameList(plotWin, ";", 1), ";") Sort/A traces, traces - CHECK_EQUAL_VAR(3, DimSize(traces, ROWS)) + CHECK_EQUAL_VAR(4, DimSize(traces, ROWS)) WAVE wY = TraceNameToWaveRef(plotWin, traces[0]) - Make/FREE/N=(3, 3) dataRef + Make/FREE/N=(3, 4) dataRef dataRef[][0] = {0, 1, 2} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {1, 1, 1} + dataRef[][3] = NaN CHECK_EQUAL_WAVES(dataRef, wY, mode = WAVE_DATA | DIMENSION_SIZES) - formula = "select(channels(AD),sweeps(),all,vc)" + formula = "select(selchannels(AD),selsweeps(),selvis(all),selcm(vc))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) WAVE/T traces = ListToTextWave(TraceNameList(plotWin, ";", 1), ";") Sort/A traces, traces - CHECK_EQUAL_VAR(3, DimSize(traces, ROWS)) + CHECK_EQUAL_VAR(4, DimSize(traces, ROWS)) WAVE wY = TraceNameToWaveRef(plotWin, traces[0]) - Make/FREE/N=(3, 3) dataRef + Make/FREE/N=(3, 4) dataRef dataRef[][0] = {0, 1, 2} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) dataRef[][2] = {2, 2, 2} + dataRef[][3] = NaN CHECK_EQUAL_WAVES(dataRef, wY, mode = WAVE_DATA | DIMENSION_SIZES) - formula = "select(channels(AD),sweeps(),all,izero)" + formula = "select(selchannels(AD),selsweeps(),selvis(all),selcm(izero))" SF_SetFormula(dbPanel, formula) PGC_SetAndActivateControl(dbPanel, "button_sweepFormula_display", val = 1) WAVE/T traces = ListToTextWave(TraceNameList(plotWin, ";", 1), ";") @@ -275,7 +278,7 @@ static Function TestSweepFormulaTP(string device) endtry // invalid mode - formula = "tp(unknown_mode, select(channels(AD), sweeps()))" + formula = "tp(unknown_mode, select(selchannels(AD), selsweeps()))" try WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) FAIL() @@ -284,7 +287,7 @@ static Function TestSweepFormulaTP(string device) endtry // unknown channel name - formula = "tp(tpss(), select(channels(unknown), sweeps()))" + formula = "tp(tpss(), select(selchannels(unknown), selsweeps()))" try WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) FAIL() @@ -293,7 +296,7 @@ static Function TestSweepFormulaTP(string device) endtry // invalid argument for ignored TPs - formula = "tp(tpss(), select(channels(AD), sweeps()), INVALID)" + formula = "tp(tpss(), select(selchannels(AD), selsweeps()), INVALID)" try WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) FAIL() @@ -302,7 +305,7 @@ static Function TestSweepFormulaTP(string device) endtry // invalid argument for ignored TPs - formula = "tp(tpss(), select(channels(AD), sweeps()), [inf])" + formula = "tp(tpss(), select(selchannels(AD), selsweeps()), [inf])" try WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) FAIL() @@ -311,7 +314,7 @@ static Function TestSweepFormulaTP(string device) endtry // invalid argument for ignored TPs - formula = "tp(tpss(), select(channels(AD), sweeps()), 1)" + formula = "tp(tpss(), select(selchannels(AD), selsweeps()), 1)" try WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) FAIL() @@ -320,22 +323,22 @@ static Function TestSweepFormulaTP(string device) endtry // ignore channels that are not AD - formula = "tp(tpss(), select(channels(DA), sweeps()))" + formula = "tp(tpss(), select(selchannels(DA), selsweeps()))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_EQUAL_VAR(DimSize(tpResult, ROWS), 0) // sweep does not exist -> zero results - formula = "tp(tpss(), select(channels(AD), 3))" + formula = "tp(tpss(), select(selchannels(AD), selsweeps(3)))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_EQUAL_VAR(DimSize(tpResult, ROWS), 0) // we setup only one TP per sweep, but we ignore TP 0 here, so we have zero results - formula = "tp(tpss(), select(channels(AD), sweeps()), 0)" + formula = "tp(tpss(), select(selchannels(AD), selsweeps()), 0)" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_EQUAL_VAR(DimSize(tpResult, ROWS), 0) // expect for 3 sweeps displayed with 2 AD channels each, 6 results - formula = "tp(tpss(), select(channels(AD), sweeps()))" + formula = "tp(tpss(), select(selchannels(AD), selsweeps()))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_EQUAL_VAR(DimSize(tpResult, ROWS), 6) @@ -355,13 +358,13 @@ static Function TestSweepFormulaTP(string device) // Use DA channel for test calculation as it is well defined // Test static state resistance and instantaneous resistance that should be the same here (1000) - formula = "tp(tpss(), select(channels(DA), sweeps()))" + formula = "tp(tpss(), select(selchannels(DA), selsweeps()))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) for(data : tpResult) CHECK_EQUAL_WAVES(wRef, data, tol = 1e-12, mode = ~WAVE_NOTE) endfor - formula = "tp(tpinst(), select(channels(DA), sweeps()))" + formula = "tp(tpinst(), select(selchannels(DA), selsweeps()))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) for(data : tpResult) CHECK_EQUAL_WAVES(wRef, data, tol = 1e-12, mode = ~WAVE_NOTE) @@ -370,7 +373,7 @@ static Function TestSweepFormulaTP(string device) // Test base line wRef = 0 Make/FREE/T units = {"pA", "mV", "pA", "mV", "pA", "mV"} - formula = "tp(tpbase(), select(channels(DA), sweeps()))" + formula = "tp(tpbase(), select(selchannels(DA), selsweeps()))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) i = 0 for(data : tpResult) @@ -381,14 +384,14 @@ static Function TestSweepFormulaTP(string device) // Check also units for AD channel SetScale d, 0, 0, "mV", wRef - formula = "tp(tpbase(), select(channels(AD1), 0))" + formula = "tp(tpbase(), select(selchannels(AD1), selsweeps(0)))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_EQUAL_VAR(DimSize(tpResult, ROWS), 1) WAVE data0 = tpResult[0] CHECK_EQUAL_WAVES(wRef, data0, mode = ~(WAVE_NOTE | WAVE_DATA)) SetScale d, 0, 0, "pA", wRef - formula = "tp(tpbase(), select(channels(AD2), 0))" + formula = "tp(tpbase(), select(selchannels(AD2), selsweeps(0)))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_EQUAL_VAR(DimSize(tpResult, ROWS), 1) WAVE data0 = tpResult[0] @@ -418,7 +421,7 @@ static Function TestSweepFormulaTP(string device) i += 1 endfor - formula = "tp(tpfit(doubleexp, tausmall, 500), select(channels(AD1), sweeps(), all))" + formula = "tp(tpfit(doubleexp, tausmall, 500), select(selchannels(AD1), selsweeps(), selvis(all)))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) dataType = JWN_GetStringFromWaveNote(tpResult, SF_META_DATATYPE) strRef = SF_DATATYPE_TP @@ -432,11 +435,11 @@ static Function TestSweepFormulaTP(string device) i += 1 endfor - formula = "tp(tpfit(doubleexp, tausmall, [-1]), select(channels(AD1), sweeps(), all))" + formula = "tp(tpfit(doubleexp, tausmall, [-1]), select(selchannels(AD1), selsweeps(), selvis(all)))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) try - formula = "tp(tpfit(doubleexp, tausmall, [-10]), select(channels(AD1), sweeps(), all))" + formula = "tp(tpfit(doubleexp, tausmall, [-10]), select(selchannels(AD1), selsweeps(), selvis(all)))" WAVE/WAVE tpResult = SF_ExecuteFormula(formula, graph, useVariables = 0) FAIL() catch @@ -618,7 +621,7 @@ static Function TestSweepFormulaCodeResults_REENTRY([string str]) Make/FREE/T/N=(DimSize(indizes, ROWS)) code = textualResultsValues[indizes[p]][settingsCol][INDEP_HEADSTAGE] - Make/FREE/T/N=(3) ref = {"data(TP, select(channels(AD), [0]))", "data(TP, select(channels(AD), [1]))", "data(TP, select(channels(AD), [2]))"} + Make/FREE/T/N=(3) ref = {"data(select(selrange(TP), selchannels(AD), selsweeps(0)))", "data(select(selrange(TP), selchannels(AD), selsweeps(1)))", "data(select(selrange(TP), selchannels(AD), selsweeps(2)))"} CHECK_EQUAL_TEXTWAVES(ref, code, mode = WAVE_DATA) // set cursors and execute formula again @@ -634,7 +637,7 @@ static Function TestSweepFormulaCodeResults_REENTRY([string str]) CHECK_EQUAL_VAR(DimSize(textualResultsValues, COLS), 20) content = GetLastSettingTextIndep(textualResultsValues, NaN, "Sweep Formula sweeps/channels", UNKNOWN_MODE) - contentRef = "2;0;1;," + contentRef = "2;0;1;nan;," CHECK_EQUAL_STR(content, contentRef) content = GetLastSettingTextIndep(textualResultsValues, NaN, "Sweep Formula experiment", UNKNOWN_MODE) @@ -711,20 +714,20 @@ static Function SF_InsertedTPVersusTP_REENTRY([str]) // check that the inserted TP is roughly the same as the other TPs in the stimset // HS0 - formula = "tp(tpss(), select(channels(AD0), sweeps()), [1, 2, 3])" + formula = "tp(tpss(), select(selchannels(AD0), selsweeps()), [1, 2, 3])" WAVE steadyStateInsertedHS0 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(steadyStateInsertedHS0, NUMERIC_WAVE) - formula = "tp(tpinst(), select(channels(AD0), sweeps()), [1, 2, 3])" + formula = "tp(tpinst(), select(selchannels(AD0), selsweeps()), [1, 2, 3])" WAVE instInsertedHS0 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(instInsertedHS0, NUMERIC_WAVE) - formula = "tp(tpss(), select(channels(AD0), sweeps()), [0])" + formula = "tp(tpss(), select(selchannels(AD0), selsweeps()), [0])" WAVE steadyStateOthersHS0 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(steadyStateOthersHS0, NUMERIC_WAVE) - formula = "tp(tpinst(), select(channels(AD0), sweeps()), [0])" + formula = "tp(tpinst(), select(selchannels(AD0), selsweeps()), [0])" WAVE instOthersHS0 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(instOthersHS0, NUMERIC_WAVE) @@ -732,19 +735,19 @@ static Function SF_InsertedTPVersusTP_REENTRY([str]) CHECK_EQUAL_WAVES(instInsertedHS0, instOthersHS0, mode = WAVE_DATA, tol = 50^2) // HS1 - formula = "tp(tpss(), select(channels(AD1), sweeps()), [1, 2, 3])" + formula = "tp(tpss(), select(selchannels(AD1), selsweeps()), [1, 2, 3])" WAVE steadyStateInsertedHS1 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(steadyStateInsertedHS1, NUMERIC_WAVE) - formula = "tp(tpinst(), select(channels(AD1), sweeps()), [1, 2, 3])" + formula = "tp(tpinst(), select(selchannels(AD1), selsweeps()), [1, 2, 3])" WAVE instInsertedHS1 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(instInsertedHS1, NUMERIC_WAVE) - formula = "tp(tpss(), select(channels(AD1), sweeps()), [0])" + formula = "tp(tpss(), select(selchannels(AD1), selsweeps()), [0])" WAVE steadyStateOthersHS1 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(steadyStateOthersHS1, NUMERIC_WAVE) - formula = "tp(tpinst(), select(channels(AD1), sweeps()), [0])" + formula = "tp(tpinst(), select(selchannels(AD1), selsweeps()), [0])" WAVE instOthersHS1 = SF_ExecuteFormula(formula, graph, singleResult = 1, useVariables = 0) CHECK_WAVE(instOthersHS1, NUMERIC_WAVE) @@ -805,7 +808,7 @@ static Function SF_UnassociatedDATTL_Epochs_REENTRY([string str]) bsPanel = BSP_GetPanel(graph) PGC_SetAndActivateControl(bsPanel, "check_BrowserSettings_DAC", val = 1) - formula = "data(\"E0_PT_*\",select(channels(DA2),sweeps()))" + formula = "data(select(selrange(\"E0_PT_*\"),selchannels(DA2),selsweeps()))" WAVE/WAVE data = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_WAVE(data, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(data, ROWS), 29) @@ -815,7 +818,7 @@ static Function SF_UnassociatedDATTL_Epochs_REENTRY([string str]) CHECK_WAVE(epochData, NUMERIC_WAVE) CHECK_GT_VAR(DimSize(epochData, ROWS), 1) - formula = "epochs(\"E0_PT_*\",select(channels(DA2),sweeps()),name)" + formula = "epochs(\"E0_PT_*\",select(selchannels(DA2),selsweeps()),name)" WAVE/WAVE data = SF_ExecuteFormula(formula, graph, useVariables = 0) CHECK_WAVE(data, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(data, ROWS), 1) diff --git a/Packages/tests/HistoricData/UTF_EpochRecreation.ipf b/Packages/tests/HistoricData/UTF_EpochRecreation.ipf index baf7d7a54e..ef58de6776 100644 --- a/Packages/tests/HistoricData/UTF_EpochRecreation.ipf +++ b/Packages/tests/HistoricData/UTF_EpochRecreation.ipf @@ -89,7 +89,7 @@ static Function TestEpochRecreationFromLoadedPXP([string str]) WAVE textualValues = DB_GetLBNWave(win, LBN_TEXTUAL_VALUES) for(sweep : sweepNums) SplitAndUpgradeSweepGlobal(device, sweep) - DFREF sweepDFR = BSP_GetSweepDF(win, sweep) + DFREF sweepDFR = DB_GetSweepDF(win, sweep) WAVE/Z epochs = MIES_EP#EP_RecreateEpochsFromLoadedData(numericalValues, textualValues, sweepDFR, sweep) CHECK_NO_RTE() CHECK_WAVE(epochs, TEXT_WAVE) diff --git a/Packages/tests/HistoricData/UTF_HistoricEpochClipping.ipf b/Packages/tests/HistoricData/UTF_HistoricEpochClipping.ipf index 605331df99..fe83f7fe9b 100644 --- a/Packages/tests/HistoricData/UTF_HistoricEpochClipping.ipf +++ b/Packages/tests/HistoricData/UTF_HistoricEpochClipping.ipf @@ -16,7 +16,7 @@ static Function TestEpochClipping([string str]) CHECK_PROPER_STR(sbWin) bsPanel = BSP_GetPanel(sbWin) - jsonId = MIES_SF#SF_FormulaParser("data(\"Stimset;\", select(channels(AD),sweeps()))") + jsonId = MIES_SF#SF_FormulaParser("data(select(selrange(\"Stimset;\"), selchannels(AD), selsweeps()))") CHECK_NEQ_VAR(jsonId, NaN) WAVE/WAVE result = MIES_SF#SF_FormulaExecutor(sbWin, jsonId) JSON_Release(jsonId) diff --git a/Packages/tests/UTF_DataGenerators.ipf b/Packages/tests/UTF_DataGenerators.ipf index 9b8898a2d5..ec47df38ad 100644 --- a/Packages/tests/UTF_DataGenerators.ipf +++ b/Packages/tests/UTF_DataGenerators.ipf @@ -211,7 +211,7 @@ static Function/WAVE TestOperationAPFrequency2Gen() Make/FREE/D sweep1Result = {1, 1, 1} results[0, 1] = sweep0Result results[2, 3] = sweep1Result - note/K results, "apfrequency(data(cursors(A,B),select(channels(AD),[0,1],all)), 3, 15, time, normoversweepsmin)" + note/K results, "apfrequency(data(select(selrange(),selchannels(AD),selsweeps(0,1),selvis(all))), 3, 15, time, normoversweepsmin)" wv[0] = results SetDimLabel ROWS, 0, normoversweepsmin, wv @@ -220,7 +220,7 @@ static Function/WAVE TestOperationAPFrequency2Gen() Make/FREE/D sweep1Result = {0.002 / 0.003, 0.002 / 0.003, 0.002 / 0.003} results[0, 1] = sweep0Result results[2, 3] = sweep1Result - note/K results, "apfrequency(data(cursors(A,B),select(channels(AD),[0,1],all)), 3, 15, time, normoversweepsmax)" + note/K results, "apfrequency(data(select(selrange(),selchannels(AD),selsweeps(0,1),selvis(all))), 3, 15, time, normoversweepsmax)" wv[1] = results SetDimLabel ROWS, 1, normoversweepsmax, wv @@ -230,7 +230,7 @@ static Function/WAVE TestOperationAPFrequency2Gen() Make/FREE/D sweep1Result = {0.002 / m, 0.002 / m, 0.002 / m} results[0, 1] = sweep0Result results[2, 3] = sweep1Result - note/K results, "apfrequency(data(cursors(A,B),select(channels(AD),[0,1],all)), 3, 15, time, normoversweepsavg)" + note/K results, "apfrequency(data(select(selrange(),selchannels(AD),selsweeps(0,1),selvis(all))), 3, 15, time, normoversweepsavg)" wv[2] = results SetDimLabel ROWS, 2, normoversweepsavg, wv @@ -241,11 +241,11 @@ static Function/WAVE SF_TestVariablesGen() // note that data is called for d and e to test if variables get cleaned up, if they would, the assignment for e would fail // as c and s were removed by the first data call. - Make/FREE/T t1FormulaAndRest = {"c=cursors(A,B)\rs=select(channels(AD),[0,1],all)\rd=data($c,$s)\re=data($c,$s)\r\r$d", "$d"} + Make/FREE/T t1FormulaAndRest = {"c=cursors(A,B)\rs=select(selrange($c),selchannels(AD),selsweeps(0,1),selvis(all))\rd=data($s)\re=data($s)\r\r$d", "$d"} Make/FREE/T t1DimLbl = {"c", "s", "d", "e"} // case-insensitivity - Make/FREE/T t2FormulaAndRest = {"c=cursors(A,B)\rs=select(channels(AD),[0,1],all)\rd=data($C,$S)\r\r$D", "$D"} + Make/FREE/T t2FormulaAndRest = {"c=cursors(A,B)\rs=select(selrange($c),selchannels(AD),selsweeps(0,1),selvis(all))\rd=data($S)\r\r$D", "$D"} Make/FREE/T t2DimLbl = {"c", "s", "d"} // result test @@ -525,9 +525,9 @@ End static Function/WAVE SweepFormulaFunctionsWithSweepsArgument() - Make/FREE/T wv = {"data(cursors(A,B), select(channels(AD), sweeps()))", \ - "epochs(\"I DONT EXIST\", select(channels(DA), sweeps()))", \ - "labnotebook(\"I DONT EXIST\", select(channels(DA), sweeps()))"} + Make/FREE/T wv = {"data(select(selrange(), selchannels(AD), selsweeps()))", \ + "epochs(\"I DONT EXIST\", select(selchannels(DA), selsweeps()))", \ + "labnotebook(\"I DONT EXIST\", select(selchannels(DA), selsweeps()))"} SetDimensionLabels(wv, "data;epochs;labnotebook", ROWS) @@ -890,3 +890,18 @@ static Function/WAVE GetASYNCThreadErrorFunctions() return wt End + +static Function/WAVE SF_TestOperationSelectFails() + + Make/FREE/T wt = {"select(1)", \ + "select(selrange(),selrange())", \ + "select(selchannels(),selchannels())", \ + "select(selsweeps(),selsweeps())", \ + "select(selcm(all),selcm(all))", \ + "select(selvis(),selvis())", \ + "select(selstimset(),selstimset())", \ + "select(selivsccsetqc(failed),selivsccsetqc(failed))", \ + "select(selivsccsweepqc(failed),selivsccsweepqc(failed))"} + + return wt +End diff --git a/Packages/tests/UTF_HelperFunctions.ipf b/Packages/tests/UTF_HelperFunctions.ipf index a859bb6ada..f036e6b8de 100644 --- a/Packages/tests/UTF_HelperFunctions.ipf +++ b/Packages/tests/UTF_HelperFunctions.ipf @@ -346,8 +346,37 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) keys[2][0][0] = "-" ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // Set a acquisition cycle ID + values[] = NaN + values[0][0][0] = sweepNo + values[0][0][1] = sweepNo + keys[0][0][0] = STIMSET_ACQ_CYCLE_ID_KEY + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + keys[2][0][0] = "-" + + // Set setQC passed + values[] = NaN + values[0][0][INDEP_HEADSTAGE] = 1 + keys[0][0][0] = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_SET_PASS, query = 1) + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, UNKNOWN_MODE) + // textual entries + // Stimset name HS0/HS1 + valuesTxt[] = "" + valuesTxt[0][0][0] = "stimsetSweep0HS0" + valuesTxt[0][0][1] = "stimsetSweep0HS1" + keys[0][0][0] = STIM_WAVE_NAME_KEY + ED_AddEntriesToLabnotebook(valuesTxt, keys, sweepNo, device, DATA_ACQUISITION_MODE) + + // Set some analysis function + valuesTxt[] = "" + valuesTxt[0][0][0] = "PSQ_Chirp" + valuesTxt[0][0][1] = "PSQ_Chirp" + keys[0][0][0] = "Generic function" + ED_AddEntriesToLabnotebook(valuesTxt, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // DAC 4: unassoc (old) valuesTxt[] = "" valuesTxt[0][0][INDEP_HEADSTAGE] = "123" @@ -413,8 +442,37 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) keys[0][0][0] = CLAMPMODE_ENTRY_KEY ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // Set a acquisition cycle ID + values[] = NaN + values[0][0][0] = sweepNo + values[0][0][1] = sweepNo + keys[0][0][0] = STIMSET_ACQ_CYCLE_ID_KEY + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + keys[2][0][0] = "-" + + // Set setQC passed + values[] = NaN + values[0][0][INDEP_HEADSTAGE] = 1 + keys[0][0][0] = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_SWEEP_PASS, query = 1) + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, UNKNOWN_MODE) + // textual entries + // Set some analysis function + valuesTxt[] = "" + valuesTxt[0][0][0] = "PSQ_Chirp" + valuesTxt[0][0][1] = "PSQ_Chirp" + keys[0][0][0] = "Generic function" + ED_AddEntriesToLabnotebook(valuesTxt, keys, sweepNo, device, DATA_ACQUISITION_MODE) + + // Stimset name HS0/HS1 + valuesTxt[] = "" + valuesTxt[0][0][0] = "stimsetSweep1HS0" + valuesTxt[0][0][1] = "stimsetSweep1HS1" + keys[0][0][0] = STIM_WAVE_NAME_KEY + ED_AddEntriesToLabnotebook(valuesTxt, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // DAC 5: unassoc (new) valuesTxt[] = "" valuesTxt[0][0][INDEP_HEADSTAGE] = "456" @@ -1431,8 +1489,9 @@ Function [variable numSweeps, variable numChannels, WAVE/U/I channels] FillFakeD clampMode = mod(sweepNumber, 2) ? V_CLAMP_MODE : I_CLAMP_MODE AppendToGraph/W=$win singleColumnDataWave/TN=$trace - TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode"}, \ - {"blah", GetWavesDataFolder(singleColumnDataWave, 2), "Sweep", "0", channelTypeStr, num2str(channelNumber), num2str(sweepNumber), num2istr(channelNumber), num2istr(clampMode)}) + WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNumber) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(singleColumnDataWave, 2), "Sweep", "0", channelTypeStr, num2str(channelNumber), num2str(sweepNumber), num2istr(channelNumber), num2istr(clampMode), "NaN"}) endfor endfor diff --git a/Packages/tests/UserAnalysisFunctions.ipf b/Packages/tests/UserAnalysisFunctions.ipf index 87370dcd1c..dd0b90c0f4 100644 --- a/Packages/tests/UserAnalysisFunctions.ipf +++ b/Packages/tests/UserAnalysisFunctions.ipf @@ -1038,7 +1038,7 @@ Function SetSweepFormula(string device, STRUCT AnalysisFunction_V3 &s) case PRE_SWEEP_CONFIG_EVENT: win = DB_FindDataBrowser(device) sweepFormulaNB = BSP_GetSFFormula(win) - sprintf code, "data(TP, select(channels(AD), [%d]))\r", s.sweepNo + sprintf code, "data(select(selrange(TP), selchannels(AD), selsweeps(%d)))\r", s.sweepNo ReplaceNotebookText(sweepFormulaNB, code) break default: From 41bc06f03d69add92fa01584248c807f7694a6ac Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 13:35:51 +0200 Subject: [PATCH 10/38] SF: Adapt SF default formula - new default formula uses new select syntax - add test --- Packages/MIES/MIES_SweepFormula.ipf | 8 ++--- .../Basic/UTF_SweepFormula_Operations.ipf | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 878c6c7c05..1e556b1a80 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -5907,10 +5907,10 @@ End Function/S SF_GetDefaultFormula() - return "trange = [0, inf]\r" + \ - "sel = select(selchannels(AD), selsweeps())\r" + \ - "dat = data($trange, $sel)\r" + \ - "\r" + \ + return "trange = [0, inf]\r" + \ + "sel = select(selrange($trange),selchannels(AD), selsweeps())\r" + \ + "dat = data($sel)\r" + \ + "\r" + \ "$dat" End diff --git a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf index d6e69f7154..39608bd5f9 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf @@ -2889,3 +2889,33 @@ static Function BasicMathMismatchedWaves([string str]) error = ROStr(GetSweepFormulaParseErrorMessage()) CHECK_EQUAL_STR(error, opShort + ": wave size mismatch [2, 0, 0, 0] vs [1, 2, 0, 0]") End + +static Function DefaultFormulaWorks() + + variable sweepNo, numChannels + string str + string win, device + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + sweepNo = 0 + win = CreateFakeSweepData(win, device, sweepNo = sweepNo) + + numChannels = 4 // from LBN creation in CreateFakeSweepData->PrepareLBN_IGNORE -> DA2, AD6, DA3, AD7 + Make/FREE/N=0 sweepTemplate + WAVE sweepRef = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) + + str = SF_GetDefaultFormula() + WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 1) + CHECK_WAVE(dataWref, WAVE_WAVE) + // If the default formula changes the following checks need to be adapted + CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), numChannels / 2) + WAVE chan1 = dataWref[0] + WAVE chan3 = dataWref[1] + Duplicate/FREE/RMD=[][1] sweepRef, chan1Ref + Redimension/N=(-1) chan1Ref + Duplicate/FREE/RMD=[][3] sweepRef, chan3Ref + Redimension/N=(-1) chan3Ref + CHECK_EQUAL_WAVES(chan1, chan1Ref, mode = WAVE_DATA) + CHECK_EQUAL_WAVES(chan3, chan3Ref, mode = WAVE_DATA) +End From 543a0031ffdb9e900764a5f0edfbb36b61b38983 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 5 Oct 2024 01:52:50 +0200 Subject: [PATCH 11/38] DB: Recreate DB macro to have a working formula on open - fix related test --- Packages/MIES/MIES_Constants.ipf | 2 +- Packages/MIES/MIES_DataBrowser_Macro.ipf | 2 +- Packages/tests/Basic/UTF_SweepFormula.ipf | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 7581819446..0256c632bb 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -19,7 +19,7 @@ Constant DAQ_CONFIG_WAVE_VERSION = 3 /// Used to upgrade the GuiStateWave as well as the DA Ephys panel Constant DA_EPHYS_PANEL_VERSION = 64 -Constant DATA_SWEEP_BROWSER_PANEL_VERSION = 50 +Constant DATA_SWEEP_BROWSER_PANEL_VERSION = 51 Constant WAVEBUILDER_PANEL_VERSION = 14 Constant ANALYSISBROWSER_PANEL_VERSION = 5 diff --git a/Packages/MIES/MIES_DataBrowser_Macro.ipf b/Packages/MIES/MIES_DataBrowser_Macro.ipf index f2fb0015e5..f8628e39b1 100644 --- a/Packages/MIES/MIES_DataBrowser_Macro.ipf +++ b/Packages/MIES/MIES_DataBrowser_Macro.ipf @@ -1269,7 +1269,7 @@ Window DataBrowser() : Graph NewNotebook/F=1/N=sweepFormula_formula/W=(12, 71, 378, 529)/FG=(UGVL, UGVT, UGVR, UGVB)/HOST=#/V=0 Notebook kwTopWin, defaultTab=10, autoSave=1, magnification=100, showRuler=0, rulerUnits=2 Notebook kwTopWin, newRuler=Normal, justification=0, margins={0, 0, 286}, spacing={0, 0, 0}, tabs={}, rulerDefaults={"Arial", 11, 0, (0, 0, 0)} - Notebook kwTopWin, zdata="GaqDU%ejN7!Z)un`Q5gp6juaS*9:-\\LOi\"3Ld])[.hK#eE=!1`!1=@^Xod&KOHcHFV4hd75b`6!+WpoV?BghDSuQ2!&8e)8OI;FNbKI]j*sb-#s8RkQX0;\\a*`U()2Y1@rq'Go7Iu!s(sas8NXq[U>B>WB!4T5Vb4-6j#u22/9'T%+*K)MBlg[k2iH%'Pf\\>B^1+I(71qr()1OtC@X,fROSS$2'oL1BI]*;e5-22^oEP0X:h!2$Wok%W#\\fsJ\\a\"S-oWjcG6i_@J7pqE+b^64@4'K6P@\\4E@@4jg=Hj-!gnuth1F5o73Bs],5:;`T&HL36>\\!" Notebook kwTopWin, zdataEnd=1 SetWindow kwTopWin, hook(ResizeControls)=ResizeControlsSafe SetWindow kwTopWin, userdata(tabnum)="0" diff --git a/Packages/tests/Basic/UTF_SweepFormula.ipf b/Packages/tests/Basic/UTF_SweepFormula.ipf index d09e6b7043..fcabfd193f 100644 --- a/Packages/tests/Basic/UTF_SweepFormula.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula.ipf @@ -1367,13 +1367,15 @@ End static Function TestDefaultFormula() - string win, bsPanel + string win, bsPanel, winRec, str win = GetDataBrowserWithData() bsPanel = BSP_GetPanel(win) PGC_SetAndActivateControl(bsPanel, "check_BrowserSettings_SF", val = CHECKBOX_SELECTED) PGC_SetAndActivateControl(bsPanel, "button_sweepFormula_display") + + CHECK_EQUAL_VAR(stringmatch(WinRecreation("", 0), "*Display*"), 1) End static Function TestParseFitConstraints() From d7f14e9f501c397b97ef3ac27ff0aa4ae26c7dc4 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 14:19:19 +0200 Subject: [PATCH 12/38] BP: Move file description header to top of file - description header was in between functions in the middle of file --- Packages/MIES/MIES_Browser_Plotter.ipf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_Browser_Plotter.ipf b/Packages/MIES/MIES_Browser_Plotter.ipf index 9746f2df83..74395edc09 100644 --- a/Packages/MIES/MIES_Browser_Plotter.ipf +++ b/Packages/MIES/MIES_Browser_Plotter.ipf @@ -6,6 +6,9 @@ #pragma ModuleName=MIES_BROWSER_PLOTTER #endif +/// @file MIES_Browser_Plotter.ipf +/// @brief Functions for plotting DataBrowser/Sweepbrowser Graphs + static Constant NUM_CHANNEL_TYPES = 3 static Constant ADC_SLOT_MULTIPLIER = 4 static Constant EPOCH_SLOT_MULTIPLIER = 3 @@ -57,9 +60,6 @@ static Function [string oodDAQRegionsAll, variable totalXRange] GetOodDAQFullRan return [oodDAQRegionsAll, totalXRange] End -/// @file MIES_Browser_Plotter.ipf -/// @brief Functions for plotting DataBrowser/Sweepbrowser Graphs - /// @brief Create a vertically tiled graph for displaying AD and DA channels /// /// For preservering the axis scaling callers should do the following: From 300bdd10cb46ab25e7cf62710d39719350eee6cc Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 14:22:01 +0200 Subject: [PATCH 13/38] BP: Adapt CreateTiledChannelGraph for multi-device-exp support CreateTiledChannelGraph is used in the SweepBrowser and the SB supports dispalying data that is loaded from multiple experiments and could be acquired from multiple devices. This information needs to be properly included in the trace user data as sweepformula needs it to be able to refer to the correct source sweep data from the trace user data. The mapIndex in the sweepBrowser sweepMap is unique for a specific device/experiment combination. --- Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf | 8 ++++---- Packages/MIES/MIES_Browser_Plotter.ipf | 13 ++++++++----- Packages/MIES/MIES_DataBrowser.ipf | 6 +++--- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf index 9a0cf5dac4..9668abe1f6 100644 --- a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf +++ b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf @@ -346,8 +346,8 @@ Function SB_UpdateSweepPlot(win) DFREF sweepDFR = GetAnalysisSweepPath(dataFolder, device) WAVE configWave = GetAnalysisConfigWave(dataFolder, device, sweepNo) - CreateTiledChannelGraph(graph, configWave, sweepNo, numericalValues, textualValues, tgs, sweepDFR, \ - axisLabelCache, traceIndex, experiment, sweepChannelSel) + CreateTiledChannelGraph(graph, configWave, sweepNo, numericalValues, textualValues, tgs, sweepDFR, \ + axisLabelCache, traceIndex, experiment, sweepChannelSel, device, mapIndex = mapIndex) AR_UpdateTracesIfReq(graph, sweepDFR, sweepNo) endfor @@ -691,8 +691,8 @@ Function SB_AddSweepToGraph(string win, variable index) WAVE axisLabelCache = GetAxisLabelCacheWave() traceIndex = GetNextTraceIndex(graph) - CreateTiledChannelGraph(graph, config, sweepNo, numericalValues, textualValues, tgs, sweepDFR, \ - axisLabelCache, traceIndex, experiment, sweepChannelSel) + CreateTiledChannelGraph(graph, config, sweepNo, numericalValues, textualValues, tgs, sweepDFR, \ + axisLabelCache, traceIndex, experiment, sweepChannelSel, device, mapIndex = index) AR_UpdateTracesIfReq(graph, dfr, sweepNo) End diff --git a/Packages/MIES/MIES_Browser_Plotter.ipf b/Packages/MIES/MIES_Browser_Plotter.ipf index 74395edc09..96b56d1eb8 100644 --- a/Packages/MIES/MIES_Browser_Plotter.ipf +++ b/Packages/MIES/MIES_Browser_Plotter.ipf @@ -84,8 +84,10 @@ End /// @param traceIndex [internal use only] set to zero on the first call in a row of successive calls /// @param experiment name of the experiment the sweep stems from /// @param channelSelWave channel selection wave +/// @param device device name /// @param bdi [optional, default = n/a] initialized BufferedDrawInfo structure, when given draw calls are buffered instead for later execution @sa OVS_EndIncrementalUpdate -Function CreateTiledChannelGraph(string graph, WAVE config, variable sweepNo, WAVE numericalValues, WAVE/T textualValues, STRUCT TiledGraphSettings &tgs, DFREF sweepDFR, WAVE/T axisLabelCache, variable &traceIndex, string experiment, WAVE channelSelWave, [STRUCT BufferedDrawInfo &bdi]) +/// @param mapIndex [optional, default = NaN] if the data originates from a sweepBrowser then the mapIndex is given here +Function CreateTiledChannelGraph(string graph, WAVE config, variable sweepNo, WAVE numericalValues, WAVE/T textualValues, STRUCT TiledGraphSettings &tgs, DFREF sweepDFR, WAVE/T axisLabelCache, variable &traceIndex, string experiment, WAVE channelSelWave, string device, [STRUCT BufferedDrawInfo &bdi, variable mapIndex]) variable axisIndex, numChannels variable numDACs, numADCs, numTTLs, i, j, k, hasPhysUnit, hardwareType @@ -104,10 +106,11 @@ Function CreateTiledChannelGraph(string graph, WAVE config, variable sweepNo, WA ASSERT(!isEmpty(graph), "Empty graph") ASSERT(IsFinite(sweepNo), "Non-finite sweepNo") + mapIndex = ParamIsDefault(mapIndex) ? NaN : mapIndex - Make/T/FREE userDataKeys = {"fullPath", "channelType", "channelNumber", "sweepNumber", "headstage", \ - "textualValues", "numericalValues", "clampMode", "TTLBit", "experiment", "traceType", \ - "occurence", "XAXIS", "YAXIS", "YRANGE", "TRACECOLOR", "AssociatedHeadstage", "GUIChannelNumber"} + Make/T/FREE userDataKeys = {"fullPath", "channelType", "channelNumber", "sweepNumber", "headstage", \ + "textualValues", "numericalValues", "clampMode", "TTLBit", "experiment", "traceType", \ + "occurence", "XAXIS", "YAXIS", "YRANGE", "TRACECOLOR", "AssociatedHeadstage", "GUIChannelNumber", "Device", "SweepMapIndex"} WAVE ADCs = GetADCListFromConfig(config) WAVE DACs = GetDACListFromConfig(config) @@ -532,7 +535,7 @@ Function CreateTiledChannelGraph(string graph, WAVE config, variable sweepNo, WA GetWavesDataFolder(textualValues, 2), GetWavesDataFolder(numericalValues, 2), \ num2str(IsFinite(headstage) ? clampModes[headstage] : NaN), num2str(ttlBit), experiment, "Sweep", \ num2str(k), horizAxis, vertAxis, traceRange, traceColor, num2istr(IsFinite(headstage)), \ - num2istr(guiChannelNumber)}) + num2istr(guiChannelNumber), device, num2str(mapIndex)}) endfor endfor diff --git a/Packages/MIES/MIES_DataBrowser.ipf b/Packages/MIES/MIES_DataBrowser.ipf index b2437743be..bacfb29268 100644 --- a/Packages/MIES/MIES_DataBrowser.ipf +++ b/Packages/MIES/MIES_DataBrowser.ipf @@ -462,7 +462,7 @@ Function DB_UpdateSweepPlot(win) WAVE config = GetConfigWave(sweepWave) CreateTiledChannelGraph(graph, config, sweepNo, numericalValues, textualValues, tgs, dfr, \ - axisLabelCache, traceIndex, experiment, sweepChannelSel) + axisLabelCache, traceIndex, experiment, sweepChannelSel, device) AR_UpdateTracesIfReq(graph, dfr, sweepNo) endfor @@ -645,10 +645,10 @@ Function DB_AddSweepToGraph(string win, variable index, [STRUCT BufferedDrawInfo if(ParamIsDefault(bdi)) CreateTiledChannelGraph(graph, config, sweepNo, numericalValues, textualValues, tgs, dfr, axisLabelCache, \ - traceIndex, experiment, sweepChannelSel) + traceIndex, experiment, sweepChannelSel, device) else CreateTiledChannelGraph(graph, config, sweepNo, numericalValues, textualValues, tgs, dfr, axisLabelCache, \ - traceIndex, experiment, sweepChannelSel, bdi = bdi) + traceIndex, experiment, sweepChannelSel, device, bdi = bdi) endif AR_UpdateTracesIfReq(graph, dfr, sweepNo) From 92ae1ec58ec1576de4ced45f596ee3e44d872d68 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 14:42:45 +0200 Subject: [PATCH 14/38] SB/DB: Add specific utility functions to retrieve the correct sweepDF - Functions have the same naming scheme, but use an API that includes the required arguments to correctly retrieve the datafolder of the sweep - preparation commit for the following changes --- .../MIES_AnalysisBrowser_SweepBrowser.ipf | 31 +++++++++++++++++ Packages/MIES/MIES_DataBrowser.ipf | 33 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf index 9668abe1f6..c6bc7f3626 100644 --- a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf +++ b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf @@ -696,3 +696,34 @@ Function SB_AddSweepToGraph(string win, variable index) AR_UpdateTracesIfReq(graph, dfr, sweepNo) End + +Function/WAVE SB_GetSweepMap(string win) + + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(win) + WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + + return sweepMap +End + +Function [WAVE/Z numericalValues, WAVE/Z textualValues] SB_GetLabNotebooks(WAVE/T sweepMap, variable mapIndex) + + string dataFolder, device + + dataFolder = sweepMap[mapIndex][%DataFolder] + device = sweepMap[mapIndex][%Device] + WAVE/Z numericalValues = GetAnalysLBNumericalValues(dataFolder, device) + WAVE/Z textualValues = GetAnalysLBTextualValues(dataFolder, device) + + return [numericalValues, textualValues] +End + +Function/DF SB_GetSweepDF(string win, variable mapIndex) + + ASSERT(BSP_IsSweepBrowser(win), "Requires window to be a SweepBrowser") + + WAVE/T sweepMap = SB_GetSweepMap(win) + DFREF deviceDFR = SB_GetSweepDataFolder(sweepMap, index = mapIndex) + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, str2num(sweepMap[mapIndex][%Sweep])) + + return sweepDFR +End diff --git a/Packages/MIES/MIES_DataBrowser.ipf b/Packages/MIES/MIES_DataBrowser.ipf index bacfb29268..599c11a629 100644 --- a/Packages/MIES/MIES_DataBrowser.ipf +++ b/Packages/MIES/MIES_DataBrowser.ipf @@ -781,3 +781,36 @@ Function DB_SFHelpJumpToLine(string str) return V_flag == 0 End + +Function/S DB_GetDevice(string win) + + ASSERT(BSP_IsDataBrowser(win), "Requires window to be a DataBrowser") + + if(!BSP_HasBoundDevice(win)) + return "" + endif + + return BSP_GetDevice(win) +End + +Function/DF DB_GetDeviceDF(string win) + + string device + + device = DB_GetDevice(win) + if(IsEmpty(device)) + return $"" + endif + + DFREF deviceDFR = GetDeviceDataPath(device) + + return deviceDFR +End + +Function/DF DB_GetSweepDF(string win, variable sweepNo) + + DFREF deviceDFR = DB_GetDeviceDF(win) + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + + return sweepDFR +End From feb3bfdeffb1fcfba85cee5a47d5c39844518726 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 15:05:09 +0200 Subject: [PATCH 15/38] SF: Add utility function SF_GetSweepMapIndices - the function returns the indices into the sweepMap for a given sweep number, experiment name and device name - preparation commit for following changes --- Packages/MIES/MIES_SweepFormula.ipf | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 1e556b1a80..2857df8974 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2194,6 +2194,44 @@ static Function SF_FormulaWaveScaleTransfer(WAVE source, WAVE dest, variable dim endswitch End +/// @brief Return the matching indices of sweepMap, if expName or device is an emtpy string then it is ignored +static Function/WAVE SF_GetSweepMapIndices(WAVE/T sweepMap, variable sweepNo, string expName, string device) + + WAVE/Z sweepIndices = FindIndizes(sweepMap, colLabel = "Sweep", var = sweepNo) + if(!WaveExists(sweepIndices)) + return $"" + endif + if(IsEmpty(expName) && IsEmpty(device)) + return sweepIndices + endif + + if(!IsEmpty(expName)) + WAVE/Z/D expIndices = FindIndizes(sweepMap, colLabel = "FileName", str = expName) + if(!WaveExists(expIndices)) + return $"" + endif + endif + if(!IsEmpty(device)) + WAVE/Z/D devIndices = FindIndizes(sweepMap, colLabel = "Device", str = device) + if(!WaveExists(devIndices)) + return $"" + endif + endif + + if(WaveExists(expIndices) && WaveExists(devIndices)) + WAVE/Z set1 = GetSetIntersection(sweepIndices, expIndices) + if(!WaveExists(set1)) + return $"" + endif + + return GetSetIntersection(set1, devIndices) + elseif(WaveExists(expIndices)) + return GetSetIntersection(sweepIndices, expIndices) + endif + + return GetSetIntersection(sweepIndices, devIndices) +End + /// @brief Use the labnotebook information to return the active channel numbers /// for a given set of sweeps /// From bf1279ed54322c83d405d8c826f1c95708c5ce91 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 15:10:04 +0200 Subject: [PATCH 16/38] SF: Add utility function SF_MatchSweepMapColumn - The function matches a column of the sweepMap with a wildcard pattern and expects a single unique result that is returned. - preparation commit for the following changes --- Packages/MIES/MIES_SweepFormula.ipf | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 2857df8974..11bc3840f2 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2195,7 +2195,15 @@ static Function SF_FormulaWaveScaleTransfer(WAVE source, WAVE dest, variable dim End /// @brief Return the matching indices of sweepMap, if expName or device is an emtpy string then it is ignored -static Function/WAVE SF_GetSweepMapIndices(WAVE/T sweepMap, variable sweepNo, string expName, string device) +static Function/WAVE SF_GetSweepMapIndices(WAVE/T sweepMap, variable sweepNo, string expName, string device, [string colLabel, string wildCardPattern]) + + variable mapSize + + if(!ParamIsDefault(colLabel)) + ASSERT(!IsEmpty(wildCardPattern), "Need a valid wildcard pattern") + mapSize = GetNumberFromWaveNote(sweepMap, NOTE_INDEX) + return FindIndizes(sweepMap, colLabel = colLabel, endRow = mapSize, str = wildCardPattern, prop = PROP_WILDCARD) + endif WAVE/Z sweepIndices = FindIndizes(sweepMap, colLabel = "Sweep", var = sweepNo) if(!WaveExists(sweepIndices)) @@ -6254,3 +6262,22 @@ static Function SF_FilterByClampModeEnabled(variable clampModeFilter, variable c return clampModeFilter != SF_OP_SELECT_CLAMPCODE_ALL && (channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_ADC) End + +static Function/S SF_MatchSweepMapColumn(string graph, string match, string colLabel, string opShort) + + variable col + + WAVE/T sweepMap = SB_GetSweepMap(graph) + WAVE/Z indices = SF_GetSweepMapIndices(sweepMap, NaN, "", "", colLabel = colLabel, wildCardPattern = match) + SFH_ASSERT(WaveExists(indices), "No match found in sweepMap in operation " + opShort) + + col = FindDimlabel(sweepMap, COLS, colLabel) + Make/FREE/T/N=(DimSize(indices, ROWS)) entries + MultiThread entries[] = sweepMap[indices[p]][col] + + WAVE/T uniqueEntries = GetUniqueEntries(entries) + SFH_ASSERT(DimSize(uniqueEntries, ROWS) < 2, "Multiple matches found in sweepMap in operation " + opShort) + SFH_ASSERT(DimSize(uniqueEntries, ROWS) == 1, "No match found in sweepMap in operation " + opShort) + + return uniqueEntries[0] +End From 01317c5a9d729fb4e6b8f9981e948dbfdb80a55a Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 16:28:06 +0200 Subject: [PATCH 17/38] SFH: Add utility function to retrieve the correct LNB and sweepDF from a graph + sweepNo - Added SFH_GetLabNoteBooksForSweep to retrieve LNB only - Added SFH_GetLabNoteBooksAndDFForSweep to retrieve LNB and SweepDFR These functions abstract the differences between a graph that is a DB and a SB. For an SB graph a valid mapIndex must be given as argument. preparation commit for the following changes --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 1b6cc726b7..9ac73d8a01 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1835,3 +1835,48 @@ Function/S SFH_CreateLegendFromRanges(WAVE selectData, WAVE/WAVE ranges) return legendStr End + +/// @brief Function returns the correct numerical and textual LNB and sweepDFR for a given sweepNumber +/// +/// @param graph name of graph window +/// @param sweepNo sweep number +/// @param mapIndex if graph is a SweepBrowser then a non-NaN mapIndex into sweepMap, otherwise must be NaN +Function [WAVE numericalValues, WAVE textualValues, DFREF sweepDFR] SFH_GetLabNoteBooksAndDFForSweep(string graph, variable sweepNo, variable mapIndex) + + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + + if(BSP_IsSweepBrowser(graph)) + ASSERT(!IsNaN(mapIndex), "Can not work with NaN as mapIndex") + DFREF sweepDFR = SB_GetSweepDF(graph, mapIndex) + + return [numericalValues, textualValues, sweepDFR] + endif + + ASSERT(IsNaN(mapIndex), "Window is DataBrowser, but got a mapIndex into a sweepMap") + DFREF deviceDFR = DB_GetDeviceDF(graph) + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + + return [numericalValues, textualValues, sweepDFR] +End + +/// @brief Function returns the correct numerical and textual LNB for a given sweepNumber +/// +/// @param graph name of graph window +/// @param sweepNo sweep number +/// @param mapIndex if graph is a SweepBrowser then a non-NaN mapIndex into sweepMap, otherwise must be NaN +Function [WAVE numericalValues, WAVE textualValues] SFH_GetLabNoteBooksForSweep(string graph, variable sweepNo, variable mapIndex) + + if(BSP_IsSweepBrowser(graph)) + ASSERT(!IsNaN(mapIndex), "Can not work with NaN as mapIndex") + WAVE/T sweepMap = SB_GetSweepMap(graph) + [WAVE numericalValues, WAVE textualValues] = SB_GetLabNotebooks(sweepMap, mapIndex) + + return [numericalValues, textualValues] + endif + + ASSERT(IsNaN(mapIndex), "Window is DataBrowser, but got a mapIndex into a sweepMap") + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + + return [numericalValues, textualValues] +End From efb0a1e58c171fba91af0df172808ace896dd090 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 16:32:15 +0200 Subject: [PATCH 18/38] Bugfix: SF use proper handling of sweepMap for SB problem described in https://github.com/AllenInstitute/MIES/issues/2151 SF requires to retrieve the correct LNBs and sweepDFR for sweeps. Through the analysisbrowser sweeps from multiple devices and/or experiments can be loaded. This must be handled correctly or wrong data is retrieved/analysed. Thus, SF functions that retrieve sweep data are adapted to handle that correctly. For selections a forth column was added to store the mapIndex of the sweepMap that is unique for a sweepNo, experiment, device combination. For graphs from SB the mapIndex is used to retrieve the correct LNB, SweepDF. ** THIS BUGFIX IS LIMITED TO SWEEP HANDLING IN SWEEPFORMULA ** Other parts of MIES may be still unaware of multiple experiment/devices in the SB. --- Packages/MIES/MIES_Constants.ipf | 1 + Packages/MIES/MIES_SweepFormula.ipf | 202 ++++++++++---------- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 90 ++++----- Packages/MIES/MIES_SweepFormula_PSX.ipf | 5 +- 4 files changed, 148 insertions(+), 150 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 0256c632bb..a225a57862 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2042,6 +2042,7 @@ StrConstant SF_META_SWEEPNO = "/SweepNumber" // number StrConstant SF_META_RANGE = "/Range" // numeric wave StrConstant SF_META_CHANNELTYPE = "/ChannelType" // number StrConstant SF_META_CHANNELNUMBER = "/ChannelNumber" // number +StrConstant SF_META_SWEEPMAPINDEX = "/SweepMapIndex" // number StrConstant SF_META_ISAVERAGED = "/IsAveraged" // number StrConstant SF_META_AVERAGED_FIRST_SWEEP = "/AveragedFirstSweep" // number StrConstant SF_META_XVALUES = "/XValues" // numeric wave diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 11bc3840f2..6ad44b7744 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -198,7 +198,7 @@ static Constant SF_POWERSPECTRUM_RATIO_GAUSS_NUMCOEFS = 4 static Constant SF_VARIABLE_PREFIX = 36 -static StrConstant SF_GETSETINTERSECTIONSELECT_FORMAT = "%d_%d_%d" +static StrConstant SF_GETSETINTERSECTIONSELECT_FORMAT = "%d_%d_%d_%f" Menu "GraphPopup" "Bring browser to front", /Q, SF_BringBrowserToFront() @@ -2253,10 +2253,12 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & variable i, j, k, l, channelType, channelNumber, sweepNo, sweepNoT, outIndex variable numSweeps, numInChannels, numActiveChannels, index variable isSweepBrowser - variable dimPosSweep, dimPosChannelNumber, dimPosChannelType - variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode - variable numTraces, fromDisplayed, clampCode - string msg, device, singleSweepDFStr + variable dimPosSweep, dimPosChannelNumber, dimPosChannelType, dimPosSweepMapIndex + variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode, dimPosTSweepMapIndex + variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex + string msg, device, singleSweepDFStr, expName, dataFolder + variable mapSize = 1 + DFREF deviceDFR = $"" WAVE/Z sweeps = filter.sweeps WAVE/Z channels = filter.channels @@ -2265,7 +2267,8 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & return $"" endif - fromDisplayed = !CmpStr(filter.vis, SF_OP_SELECTVIS_DISPLAYED) + fromDisplayed = !CmpStr(filter.vis, SF_OP_SELECTVIS_DISPLAYED) + isSweepBrowser = BSP_IsSweepBrowser(graph) if(fromDisplayed) WAVE/T/Z traces = GetTraceInfos(graph) @@ -2289,10 +2292,12 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & dimPosSweep = FindDimLabel(selectDisplayed, COLS, "SWEEP") dimPosChannelType = FindDimLabel(selectDisplayed, COLS, "CHANNELTYPE") dimPosChannelNumber = FindDimLabel(selectDisplayed, COLS, "CHANNELNUMBER") + dimPosSweepMapIndex = FindDimLabel(selectDisplayed, COLS, "SWEEPMAPINDEX") dimPosTChannelType = FindDimLabel(traces, COLS, "channelType") dimPosTChannelNumber = FindDimLabel(traces, COLS, "GUIChannelNumber") dimPosTClampMode = FindDimLabel(traces, COLS, "clampMode") + dimPosTSweepMapIndex = FindDimLabel(traces, COLS, "SweepMapIndex") for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] for(j = 0; j < numTraces; j += 1) @@ -2301,6 +2306,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & selectDisplayed[outIndex][dimPosSweep] = sweepNo selectDisplayed[outIndex][dimPosChannelType] = WhichListItem(traces[j][dimPosTChannelType], XOP_CHANNEL_NAMES) selectDisplayed[outIndex][dimPosChannelNumber] = str2num(traces[j][dimPosTChannelNumber]) + selectDisplayed[outIndex][dimPosSweepMapIndex] = str2num(traces[j][dimPosTSweepMapIndex]) clampModeDisplayed[outIndex] = str2num(traces[j][dimPosTClampMode]) outIndex += 1 endif @@ -2317,16 +2323,10 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & numTraces = outIndex outIndex = 0 + elseif(isSweepBrowser) + WAVE/T sweepMap = SB_GetSweepMap(graph) else - isSweepBrowser = BSP_IsSweepBrowser(graph) - if(isSweepBrowser) - DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) - WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) - else - SFH_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") - device = BSP_GetDevice(graph) - DFREF deviceDFR = GetDeviceDataPath(device) - endif + DFREF deviceDFR = DB_GetDeviceDF(graph) endif // search sweeps for active channels @@ -2338,6 +2338,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & dimPosSweep = FindDimLabel(selectData, COLS, "SWEEP") dimPosChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") dimPosChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") + dimPosSweepMapIndex = FindDimLabel(selectData, COLS, "SWEEPMAPINDEX") endif for(i = 0; i < numSweeps; i += 1) @@ -2349,86 +2350,85 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & if(!fromDisplayed) if(isSweepBrowser) - DFREF deviceDFR = SB_GetSweepDataFolder(sweepMap, sweepNo = sweepNo) - if(!DataFolderExistsDFR(deviceDFR)) - continue - endif - else - if(DB_SplitSweepsIfReq(graph, sweepNo)) + WAVE/Z mapIndices = SF_GetSweepMapIndices(sweepMap, sweepNo, "", "") + if(!WaveExists(mapIndices)) continue endif - endif - singleSweepDFStr = GetSingleSweepFolderAsString(deviceDFR, sweepNo) - if(!DataFolderExists(singleSweepDFStr)) - continue - endif - DFREF sweepDFR = $singleSweepDFStr - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - if(!WaveExists(numericalValues)) - continue - endif - WAVE/Z/T textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) - if(!WaveExists(textualValues)) + mapSize = DimSize(mapIndices, ROWS) + elseif(DB_SplitSweepsIfReq(graph, sweepNo)) continue endif endif - for(j = 0; j < numInChannels; j += 1) + for(smIndexCounter = 0; smIndexCounter < mapSize; smIndexCounter += 1) + if(!fromDisplayed) + mapIndex = isSweepBrowser ? mapIndices[smIndexCounter] : NaN + DFREF sweepDFR + [WAVE numericalValues, WAVE textualValues, sweepDFR] = SFH_GetLabNoteBooksAndDFForSweep(graph, sweepNo, mapIndex) + if(!WaveExists(numericalValues) || !WaveExists(textualValues) || !DataFolderExistsDFR(sweepDFR)) + continue + endif + endif - channelType = channels[j][%channelType] - channelNumber = channels[j][%channelNumber] + for(j = 0; j < numInChannels; j += 1) - if(IsNaN(channelType)) - Make/FREE/D channelTypes = {XOP_CHANNEL_TYPE_DAC, XOP_CHANNEL_TYPE_ADC, XOP_CHANNEL_TYPE_TTL} - else - sprintf msg, "Unhandled channel type %g in channels() at position %d", channelType, j - SFH_ASSERT(channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_ADC || channelType == XOP_CHANNEL_TYPE_TTL, msg) - Make/FREE/D channelTypes = {channelType} - endif + channelType = channels[j][%channelType] + channelNumber = channels[j][%channelNumber] - for(channelType : channelTypes) + if(IsNaN(channelType)) + Make/FREE/D channelTypes = {XOP_CHANNEL_TYPE_DAC, XOP_CHANNEL_TYPE_ADC, XOP_CHANNEL_TYPE_TTL} + else + sprintf msg, "Unhandled channel type %g in channels() at position %d", channelType, j + SFH_ASSERT(channelType == XOP_CHANNEL_TYPE_DAC || channelType == XOP_CHANNEL_TYPE_ADC || channelType == XOP_CHANNEL_TYPE_TTL, msg) + Make/FREE/D channelTypes = {channelType} + endif - if(fromDisplayed) - for(l = 0; l < numTraces; l += 1) + for(channelType : channelTypes) - clampCode = SF_MapClampModeToSelectCM(clampModeDisplayed[l]) - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode)) - continue - endif + if(fromDisplayed) + for(l = 0; l < numTraces; l += 1) - selectData[outIndex][dimPosSweep] = sweepNo - selectData[outIndex][dimPosChannelType] = channelType - selectData[outIndex][dimPosChannelNumber] = selectDisplayed[l][dimPosChannelNumber] - outIndex += 1 - endfor - else - WAVE/Z activeChannels = GetActiveChannels(numericalValues, textualValues, sweepNo, channelType) - if(!WaveExists(activeChannels)) - continue - endif - // faster than ZapNaNs due to no mem alloc - numActiveChannels = DimSize(activeChannels, ROWS) - for(l = 0; l < numActiveChannels; l += 1) - if(IsNan(activeChannels[l])) + clampCode = SF_MapClampModeToSelectCM(clampModeDisplayed[l]) + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode)) + continue + endif + + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = selectDisplayed[l][dimPosChannelNumber] + selectData[outIndex][dimPosSweepMapIndex] = selectDisplayed[l][dimPosSweepMapIndex] + outIndex += 1 + endfor + else + WAVE/Z activeChannels = GetActiveChannels(numericalValues, textualValues, sweepNo, channelType) + if(!WaveExists(activeChannels)) continue endif + // faster than ZapNaNs due to no mem alloc + numActiveChannels = DimSize(activeChannels, ROWS) + for(l = 0; l < numActiveChannels; l += 1) + if(IsNan(activeChannels[l])) + continue + endif - if(SF_FilterByClampModeEnabled(filter.clampMode, channelType)) - [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, CLAMPMODE_ENTRY_KEY, l, channelType, DATA_ACQUISITION_MODE) - clampCode = WaveExists(setting) ? SF_MapClampModeToSelectCM(setting[index]) : SF_OP_SELECT_CLAMPCODE_NONE - endif + if(SF_FilterByClampModeEnabled(filter.clampMode, channelType)) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, CLAMPMODE_ENTRY_KEY, l, channelType, DATA_ACQUISITION_MODE) + clampCode = WaveExists(setting) ? SF_MapClampModeToSelectCM(setting[index]) : SF_OP_SELECT_CLAMPCODE_NONE + endif - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode)) - continue - endif + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode)) + continue + endif - selectData[outIndex][dimPosSweep] = sweepNo - selectData[outIndex][dimPosChannelType] = channelType - selectData[outIndex][dimPosChannelNumber] = l - outIndex += 1 - endfor - endif + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = l + selectData[outIndex][dimPosSweepMapIndex] = mapIndex + outIndex += 1 + endfor + endif + endfor endfor endfor endfor @@ -2975,7 +2975,7 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel string fitFunc, retWhat, epBaselineTrail, allowedReturns variable numTPs, beginTrail, endTrail, endTrailZero, endTrailIndex, beginTrailIndex, fitResult - variable debugMode + variable debugMode, mapIndex STRUCT TPAnalysisInput tpInput string epochTPRegExp = "^(U_)?TP[[:digit:]]*$" @@ -3013,21 +3013,21 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel WAVE/Z settings for(i = 0; i < numSelected; i += 1) - sweepNo = selectData[i][%SWEEP] - chanNr = selectData[i][%CHANNELNUMBER] - chanType = selectData[i][%CHANNELTYPE] - + sweepNo = selectData[i][%SWEEP] if(!IsValidSweepNumber(sweepNo)) continue endif - - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + chanNr = selectData[i][%CHANNELNUMBER] + chanType = selectData[i][%CHANNELTYPE] + mapIndex = selectData[i][%SWEEPMAPINDEX] + DFREF sweepDFR + [WAVE numericalValues, WAVE textualValues, sweepDFR] = SFH_GetLabNoteBooksAndDFForSweep(graph, sweepNo, mapIndex) if(!WaveExists(numericalValues) || !WaveExists(textualValues)) continue endif + SFH_ASSERT(DataFolderExistsDFR(sweepDFR), "Could not determine sweepDFR") - WAVE/WAVE singleSelect = SFH_GetSingleSelect(graph, opShort, sweepNo, chanType, chanNr) + WAVE/WAVE singleSelect = SFH_GetSingleSelect(graph, opShort, sweepNo, chanType, chanNr, mapIndex) WAVE/WAVE sweepDataRef = SFH_GetSweepsForFormula(graph, singleSelect, SF_OP_TP) SFH_ASSERT(DimSize(sweepDataRef, ROWS) == 1, "Could not retrieve sweep data for " + num2istr(sweepNo)) WAVE/Z sweepData = sweepDataRef[0] @@ -3054,8 +3054,6 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel dacChannelNr = settings[headstage] SFH_ASSERT(IsFinite(dacChannelNr), "DAC channel number must be finite") - DFREF sweepDFR = BSP_GetSweepDF(graph, sweepNo) - SFH_ASSERT(DataFolderExistsDFR(sweepDFR), "Could not determine sweepDFR") WAVE/Z epochMatchesAll = EP_GetEpochs(numericalValues, textualValues, sweepNo, XOP_CHANNEL_TYPE_DAC, dacChannelNr, epochTPRegExp, sweepDFR = sweepDFR) // drop TPs which should be ignored @@ -3393,22 +3391,20 @@ static Function/WAVE SF_OperationEpochsImpl(string graph, WAVE/T epochPatterns, numPatterns = DimSize(epochPatterns, ROWS) for(i = 0; i < numSelected; i += 1) - sweepNo = selectData[i][%SWEEP] - chanNr = selectData[i][%CHANNELNUMBER] - chanType = selectData[i][%CHANNELTYPE] - + sweepNo = selectData[i][%SWEEP] if(!IsValidSweepNumber(sweepNo)) continue endif + chanNr = selectData[i][%CHANNELNUMBER] + chanType = selectData[i][%CHANNELTYPE] - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + DFREF sweepDFR + [WAVE numericalValues, WAVE textualValues, sweepDFR] = SFH_GetLabNoteBooksAndDFForSweep(graph, sweepNo, selectData[i][%SWEEPMAPINDEX]) if(!WaveExists(numericalValues) || !WaveExists(textualValues)) continue endif - - DFREF sweepDFR = BSP_GetSweepDF(graph, sweepNo) SFH_ASSERT(DataFolderExistsDFR(sweepDFR), "Could not determine sweepDFR") + WAVE/Z/T epochInfo = EP_FetchEpochs(numericalValues, textualValues, sweepNo, sweepDFR, chanNr, chanType) if(!WaveExists(epochInfo)) continue @@ -4367,7 +4363,7 @@ static Function/WAVE SF_OperationSelectSweeps(variable jsonId, string jsonPath, WAVE/ZZ uniqueSweeps endif - return SFH_GetOutputForExecutorSingle(sweeps, graph, SF_OP_SELECTSWEEPS, discardOpStack = 1, dataType = SF_DATATYPE_SWEEPNO) + return SFH_GetOutputForExecutorSingle(uniqueSweeps, graph, SF_OP_SELECTSWEEPS, discardOpStack = 1, dataType = SF_DATATYPE_SWEEPNO) End static Function/WAVE SF_OperationPowerSpectrum(variable jsonId, string jsonPath, string graph) @@ -5047,16 +5043,14 @@ static Function/WAVE SF_OperationLabnotebookImpl(string graph, string lbnKey, WA for(i = 0; i < numSelected; i += 1) - sweepNo = selectData[i][%SWEEP] - chanNr = selectData[i][%CHANNELNUMBER] - chanType = selectData[i][%CHANNELTYPE] - + sweepNo = selectData[i][%SWEEP] if(!IsValidSweepNumber(sweepNo)) continue endif + chanNr = selectData[i][%CHANNELNUMBER] + chanType = selectData[i][%CHANNELTYPE] - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, selectData[i][%SWEEPMAPINDEX]) if(!WaveExists(numericalValues) || !WaveExists(textualValues)) continue endif @@ -6229,7 +6223,7 @@ threadsafe static Function/S SF_GetSelectRowId(WAVE select, variable row) string str - sprintf str, SF_GETSETINTERSECTIONSELECT_FORMAT, select[row][%SWEEP], select[row][%CHANNELTYPE], select[row][%CHANNELNUMBER] + sprintf str, SF_GETSETINTERSECTIONSELECT_FORMAT, select[row][%SWEEP], select[row][%CHANNELTYPE], select[row][%CHANNELNUMBER], select[row][%SWEEPMAPINDEX] return str End diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 9ac73d8a01..7c7267ad0e 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -414,9 +414,10 @@ End /// @param sweep number of sweep /// @param chanType type of channel /// @param channel number of DA channel +/// @param mapIndex index in sweepMap, required if source is a SweepBrowser /// /// @returns a 1D wave with two elements, [startTime, endTime] in ms, if no epoch could be resolved [NaN, NaN] is returned -Function/WAVE SFH_GetRangeFromEpoch(string graph, string epochName, variable sweep, variable chanType, variable channel) +Function/WAVE SFH_GetRangeFromEpoch(string graph, string epochName, variable sweep, variable chanType, variable channel, variable mapIndex) string regex variable numEpochs @@ -426,18 +427,13 @@ Function/WAVE SFH_GetRangeFromEpoch(string graph, string epochName, variable swe return range endif - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweep) - if(!WaveExists(numericalValues)) + DFREF sweepDFR + [WAVE numericalValues, WAVE textualValues, sweepDFR] = SFH_GetLabNoteBooksAndDFForSweep(graph, sweep, mapIndex) + if(!WaveExists(numericalValues) || !WaveExists(textualValues)) return range endif - - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweep) - if(!WaveExists(textualValues)) - return range - endif - - DFREF sweepDFR = BSP_GetSweepDF(graph, sweep) SFH_ASSERT(DataFolderExistsDFR(sweepDFR), "Could not determine sweepDFR") + regex = "^" + epochName + "$" WAVE/T/Z epochs = EP_GetEpochs(numericalValues, textualValues, sweep, chanType, channel, regex, sweepDFR = sweepDFR) if(!WaveExists(epochs)) @@ -502,10 +498,10 @@ End static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectDataComp, string opShort) variable i, j, rangeStart, rangeEnd, sweepNo, isSingleRange - variable chanNr, chanType, cIndex, isSweepBrowser + variable chanNr, chanType, cIndex, isSweepBrowser, mapIndex variable numSelected, index, numRanges, sweepSize, samplingInterval, samplingOffset variable rangeStartIndex, rangeEndIndex - string dimLabel, device, dataFolder + string dimLabel, device, experiment ASSERT(WindowExists(graph), "graph window does not exist") WAVE/Z selectData = selectDataComp[%SELECTION] @@ -514,7 +510,7 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD if(!WaveExists(selectData) || !DimSize(range, ROWS)) return $"" endif - SFH_ASSERT(DimSize(selectData, COLS) == 3, "Select data must have 3 columns.") + SFH_ASSERT(DimSize(selectData, COLS) == 4, "Select data must have 4 columns.") isSingleRange = DimSize(range, ROWS) == 1 numSelected = DimSize(selectData, ROWS) @@ -525,14 +521,11 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numSelected) isSweepBrowser = BSP_IsSweepBrowser(graph) - if(isSweepBrowser) - DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) - WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + WAVE/T sweepMap = SB_GetSweepMap(graph) else - SFH_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") - device = BSP_GetDevice(graph) - DFREF deviceDFR = GetDeviceDataPath(device) + experiment = GetExperimentName() + device = BSP_GetDevice(graph) endif for(i = 0; i < numSelected; i += 1) @@ -543,28 +536,25 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD continue endif - sweepNo = selectData[i][%SWEEP] + sweepNo = selectData[i][%SWEEP] + if(!isSweepBrowser && DB_SplitSweepsIfReq(graph, sweepNo)) + continue + endif chanNr = selectData[i][%CHANNELNUMBER] chanType = selectData[i][%CHANNELTYPE] + mapIndex = selectData[i][%SWEEPMAPINDEX] + DFREF sweepDFR + [WAVE numericalValues, WAVE textualValues, sweepDFR] = SFH_GetLabNoteBooksAndDFForSweep(graph, sweepNo, selectData[i][%SWEEPMAPINDEX]) + SFH_ASSERT(WaveExists(textualValues) && WaveExists(numericalValues), "LBN not found for sweep " + num2istr(sweepNo)) + if(!DataFolderExistsDFR(sweepDFR)) + continue + endif if(isSweepBrowser) - DFREF deviceDFR = SB_GetSweepDataFolder(sweepMap, sweepNo = sweepNo) - - if(!DataFolderExistsDFR(deviceDFR)) - continue - endif - else - if(DB_SplitSweepsIfReq(graph, sweepNo)) - continue - endif + device = sweepMap[mapIndex][%Device] + experiment = sweepMap[mapIndex][%FileName] endif - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) - SFH_ASSERT(WaveExists(textualValues) && WaveExists(numericalValues), "LBN not found for sweep " + num2istr(sweepNo)) - - DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) - WAVE/Z sweep = GetDAQDataSingleColumnWaveNG(numericalValues, textualValues, sweepNo, sweepDFR, chanType, chanNr) if(!WaveExists(sweep)) continue @@ -572,7 +562,7 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD WAVE/ZZ adaptedRange WAVE/T/ZZ epochRangeNames - [adaptedRange, epochRangeNames] = SFH_GetNumericRangeFromEpoch(graph, numericalValues, textualValues, setRange, sweepNo, chanType, chanNr) + [adaptedRange, epochRangeNames] = SFH_GetNumericRangeFromEpoch(graph, numericalValues, textualValues, setRange, sweepNo, chanType, chanNr, mapIndex) if(!WaveExists(adaptedRange) && !WaveExists(epochRangeNames)) continue @@ -618,6 +608,9 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD JWN_SetNumberInWaveNote(rangedSweepData, SF_META_SWEEPNO, sweepNo) JWN_SetNumberInWaveNote(rangedSweepData, SF_META_CHANNELTYPE, chanType) JWN_SetNumberInWaveNote(rangedSweepData, SF_META_CHANNELNUMBER, chanNr) + if(!IsNaN(mapIndex)) + JWN_SetNumberInWaveNote(rangedSweepData, SF_META_SWEEPMAPINDEX, mapIndex) + endif EnsureLargeEnoughWave(output, indexShouldExist = index) output[index] = rangedSweepData @@ -1097,10 +1090,11 @@ Function/WAVE SFH_NewSelectDataWave(variable numSweeps, variable numChannels) ASSERT(numSweeps >= 0 && numChannels >= 0, "Invalid wave size specified") - Make/FREE/D/N=(numSweeps * numChannels, 3) selectData + Make/FREE/D/N=(numSweeps * numChannels, 4) selectData SetDimLabel COLS, 0, SWEEP, selectData SetDimLabel COLS, 1, CHANNELTYPE, selectData SetDimLabel COLS, 2, CHANNELNUMBER, selectData + SetDimLabel COLS, 3, SWEEPMAPINDEX, selectData return selectData End @@ -1407,15 +1401,16 @@ End /// equivalent from labnotebook entries. Function/WAVE SFH_GetStimsetRange(string graph, WAVE data, WAVE selectData) - variable sweepNo, channel, chanType, dDAQ, oodDAQ, onsetDelay, terminationDelay, lengthInMS + variable sweepNo, channel, chanType, mapIndex, dDAQ, oodDAQ, onsetDelay, terminationDelay, lengthInMS sweepNo = selectData[0][%SWEEP] channel = selectData[0][%CHANNELNUMBER] chanType = selectData[0][%CHANNELTYPE] + mapIndex = selectData[0][%SWEEPMAPINDEX] // stimset epoch "ST" does not include any onset or termination delay and only the stimset epochs // and it also works with dDAQ/oodDAQ - WAVE range = SFH_GetRangeFromEpoch(graph, "ST", sweepNo, chanType, channel) + WAVE range = SFH_GetRangeFromEpoch(graph, "ST", sweepNo, chanType, channel, mapIndex) if(!SFH_IsEmptyRange(range)) return range @@ -1423,7 +1418,7 @@ Function/WAVE SFH_GetStimsetRange(string graph, WAVE data, WAVE selectData) // data prior to 13b3499d (Add short names for Epochs stored in epoch name, 2021-09-06) // try the long name instead - WAVE range = SFH_GetRangeFromEpoch(graph, "Stimset;", sweepNo, chanType, channel) + WAVE range = SFH_GetRangeFromEpoch(graph, "Stimset;", sweepNo, chanType, channel, mapIndex) if(!SFH_IsEmptyRange(range)) return range @@ -1431,7 +1426,7 @@ Function/WAVE SFH_GetStimsetRange(string graph, WAVE data, WAVE selectData) // data prior to a2172f03 (Added generations of epoch information wave, 2019-05-22) // remove total onset delay and termination delay iff we have neither dDAQ nor oodDAQ enabled - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) ASSERT(WaveExists(numericalValues), "Missing numerical labnotebook") // 778969b0 (DC_PlaceDataInITCDataWave: Document all other settings from the DAQ groupbox, 2015-11-26) @@ -1470,10 +1465,11 @@ End /// @param sweepNo sweep number /// @param chanType channel type /// @param chanNr channel number +/// @param mapIndex index in sweepMap if graph is a SweepBrowser /// /// @retval adaptedRange 2xN numeric wave with the start/stop ranges [ms] /// @retval epochRangeNames epoch names (wildcard expanded) in case range was textual, a null wave ref otherwise -Function [WAVE adaptedRange, WAVE/T epochRangeNames] SFH_GetNumericRangeFromEpoch(string graph, WAVE numericalValues, WAVE textualValues, WAVE range, variable sweepNo, variable chanType, variable chanNr) +Function [WAVE adaptedRange, WAVE/T epochRangeNames] SFH_GetNumericRangeFromEpoch(string graph, WAVE numericalValues, WAVE textualValues, WAVE range, variable sweepNo, variable chanType, variable chanNr, variable mapIndex) string epochTag, epochShortName variable numEpochs, epIndex, i, j @@ -1495,7 +1491,12 @@ Function [WAVE adaptedRange, WAVE/T epochRangeNames] SFH_GetNumericRangeFromEpoc WAVE/T epochPatterns = range SFH_ASSERT(IsTextWave(epochPatterns) && !DimSize(epochPatterns, COLS), "Expected 1d text wave for epoch specification") - DFREF sweepDFR = BSP_GetSweepDF(graph, sweepNo) + if(BSP_IsSweepBrowser(graph)) + DFREF sweepDFR = SB_GetSweepDF(graph, mapIndex) + else + DFREF deviceDFR = DB_GetDeviceDF(graph) + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + endif SFH_ASSERT(DataFolderExistsDFR(sweepDFR), "Could not determine sweepDFR") WAVE/T/Z epochInfo = EP_GetEpochs(numericalValues, textualValues, sweepNo, chanType, chanNr, allEpochsRegex, sweepDFR = sweepDFR) if(!WaveExists(epochInfo)) @@ -1665,13 +1666,14 @@ Function/WAVE SFH_MoveDatasetHigherIfCompatible(WAVE/WAVE data) return data End -Function/WAVE SFH_GetSingleSelect(string graph, string opShort, variable sweepNo, variable channelType, variable channelNumber) +Function/WAVE SFH_GetSingleSelect(string graph, string opShort, variable sweepNo, variable channelType, variable channelNumber, variable mapIndex) WAVE/WAVE range = SFH_AsDataSet(SFH_GetFullRange()) WAVE singleSelect = SFH_NewSelectDataWave(1, 1) singleSelect[0][%SWEEP] = sweepNo singleSelect[0][%CHANNELTYPE] = channelType singleSelect[0][%CHANNELNUMBER] = channelNumber + singleSelect[0][%SWEEPMAPINDEX] = mapIndex WAVE/WAVE selectDataComp = GetSFSelectDataComp(graph, opShort) JWN_SetStringInWaveNote(selectDataComp, SF_META_DATATYPE, SF_DATATYPE_SELECTCOMP) diff --git a/Packages/MIES/MIES_SweepFormula_PSX.ipf b/Packages/MIES/MIES_SweepFormula_PSX.ipf index c3f2ffefce..0856df564b 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX.ipf @@ -1086,17 +1086,18 @@ End /// @brief Collect all resolved ranges in allResolvedRanges together with a hash of the select data Function PSX_CollectResolvedRanges(string graph, WAVE range, WAVE singleSelectData, WAVE allResolvedRanges, WAVE/T allSelectHashes) - variable sweepNo, chanNr, chanType, numRows + variable sweepNo, chanNr, chanType, numRows, mapIndex sweepNo = singleSelectData[0][%SWEEP] chanNr = singleSelectData[0][%CHANNELNUMBER] chanType = singleSelectData[0][%CHANNELTYPE] + mapIndex = singleSelectData[0][%SWEEPMAPINDEX] WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) SFH_ASSERT(WaveExists(textualValues) && WaveExists(numericalValues), "LBN not found for sweep " + num2istr(sweepNo)) - [WAVE resolvedRanges, WAVE/T epochRangeNames] = SFH_GetNumericRangeFromEpoch(graph, numericalValues, textualValues, range, sweepNo, chanType, chanNr) + [WAVE resolvedRanges, WAVE/T epochRangeNames] = SFH_GetNumericRangeFromEpoch(graph, numericalValues, textualValues, range, sweepNo, chanType, chanNr, mapIndex) ASSERT(DimSize(resolvedRanges, COLS) == 1, "psxStats does not support epoch wildcards") numRows = DimSize(allSelectHashes, ROWS) From 131dbb517490180c41b9fed9476bf0fcf29f4228 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 18:14:10 +0200 Subject: [PATCH 19/38] SF: Add selection operation selexp and seldev - allows to select device and experiment - only one of these opeations are allowed for select and they must each yield a single result, but allow wildcards - the remaining behavior is like selrange where the information is taken from the outermost select only. - Add sweepMapIndex to trace user data in CreateTiledChannelGraph - Rewrite of selection gathering and adaptations to sweep getting to be aware of different experiment that require to work with sweepmap indices instead of sweep numbers. - adaptations of all SF functions that retrieve LNB and sweepDFR to be aware of multiple experiments present in a sweeepbrowser that could be selected by select --- Packages/MIES/MIES_Constants.ipf | 4 + Packages/MIES/MIES_Structures.ipf | 2 + Packages/MIES/MIES_SweepFormula.ipf | 122 +++++++++++++++++++- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 2 + 4 files changed, 127 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index a225a57862..7102cdec05 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2042,6 +2042,8 @@ StrConstant SF_META_SWEEPNO = "/SweepNumber" // number StrConstant SF_META_RANGE = "/Range" // numeric wave StrConstant SF_META_CHANNELTYPE = "/ChannelType" // number StrConstant SF_META_CHANNELNUMBER = "/ChannelNumber" // number +StrConstant SF_META_DEVICE = "/Device" // string +StrConstant SF_META_EXPERIMENT = "/Experiment" // string StrConstant SF_META_SWEEPMAPINDEX = "/SweepMapIndex" // number StrConstant SF_META_ISAVERAGED = "/IsAveraged" // number StrConstant SF_META_AVERAGED_FIRST_SWEEP = "/AveragedFirstSweep" // number @@ -2096,6 +2098,8 @@ StrConstant SF_DATATYPE_TPFIT = "TestPulseMode_Fit" StrConstant SF_DATATYPE_POWERSPECTRUM = "Powerspectrum" StrConstant SF_DATATYPE_PSX = "PSX" StrConstant SF_DATATYPE_SELECTVIS = "SelectVis" +StrConstant SF_DATATYPE_SELECTEXP = "SelectExp" +StrConstant SF_DATATYPE_SELECTDEV = "SelectDev" StrConstant SF_DATATYPE_SELECTCM = "SelectClampMode" StrConstant SF_DATATYPE_SELECTSTIMSET = "selectStimset" StrConstant SF_DATATYPE_SELECTIVSCCSWEEPQC = "selectIVSCCSweepQC" diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index 717460f3fb..d92ebda82f 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -657,4 +657,6 @@ Structure SF_SelectParameters WAVE ranges variable sweepQC variable setQC + string experimentName + string device EndStructure diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 6ad44b7744..6a98508034 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -95,6 +95,8 @@ static StrConstant SF_OP_TP = "tp" static StrConstant SF_OP_STORE = "store" static StrConstant SF_OP_SELECT = "select" static StrConstant SF_OP_SELECTVIS = "selvis" +static StrConstant SF_OP_SELECTEXP = "selexp" +static StrConstant SF_OP_SELECTDEV = "seldev" static StrConstant SF_OP_SELECTCM = "selcm" static StrConstant SF_OP_SELECTSTIMSET = "selstimset" static StrConstant SF_OP_SELECTIVSCCSWEEPQC = "selivsccsweepqc" @@ -230,7 +232,7 @@ Function/WAVE SF_GetNamedOperations() SF_OP_STORE, SF_OP_SELECT, SF_OP_POWERSPECTRUM, SF_OP_TPSS, SF_OP_TPBASE, SF_OP_TPINST, SF_OP_TPFIT, \ SF_OP_PSX, SF_OP_PSX_KERNEL, SF_OP_PSX_STATS, SF_OP_PSX_RISETIME, SF_OP_PSX_PREP, SF_OP_PSX_DECONV_FILTER, \ SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ - SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE} + SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV} return wt End @@ -1151,6 +1153,12 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_SELECTVIS: WAVE out = SF_OperationSelectVis(jsonId, jsonPath, graph) break + case SF_OP_SELECTEXP: + WAVE out = SF_OperationSelectExperiment(jsonId, jsonPath, graph) + break + case SF_OP_SELECTDEV: + WAVE out = SF_OperationSelectDevice(jsonId, jsonPath, graph) + break case SF_OP_SELECTCM: WAVE out = SF_OperationSelectCM(jsonId, jsonPath, graph) break @@ -2254,7 +2262,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & variable numSweeps, numInChannels, numActiveChannels, index variable isSweepBrowser variable dimPosSweep, dimPosChannelNumber, dimPosChannelType, dimPosSweepMapIndex - variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode, dimPosTSweepMapIndex + variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode, dimPosTExpName, dimPosTDevice, dimPosTSweepMapIndex variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex string msg, device, singleSweepDFStr, expName, dataFolder variable mapSize = 1 @@ -2297,12 +2305,22 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & dimPosTChannelType = FindDimLabel(traces, COLS, "channelType") dimPosTChannelNumber = FindDimLabel(traces, COLS, "GUIChannelNumber") dimPosTClampMode = FindDimLabel(traces, COLS, "clampMode") + dimPosTExpName = FindDimLabel(traces, COLS, "Experiment") + dimPosTDevice = FindDimLabel(traces, COLS, "Device") dimPosTSweepMapIndex = FindDimLabel(traces, COLS, "SweepMapIndex") for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] for(j = 0; j < numTraces; j += 1) sweepNoT = str2num(traces[j][dimPosTSweep]) if(sweepNo == sweepNoT) + if(isSweepBrowser) + if(!IsEmpty(filter.experimentName) && CmpStr(filter.experimentName, traces[j][dimPosTExpName])) + continue + endif + if(!IsEmpty(filter.device) && CmpStr(filter.device, traces[j][dimPosTDevice])) + continue + endif + endif selectDisplayed[outIndex][dimPosSweep] = sweepNo selectDisplayed[outIndex][dimPosChannelType] = WhichListItem(traces[j][dimPosTChannelType], XOP_CHANNEL_NAMES) selectDisplayed[outIndex][dimPosChannelNumber] = str2num(traces[j][dimPosTChannelNumber]) @@ -2350,7 +2368,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & if(!fromDisplayed) if(isSweepBrowser) - WAVE/Z mapIndices = SF_GetSweepMapIndices(sweepMap, sweepNo, "", "") + WAVE/Z mapIndices = SF_GetSweepMapIndices(sweepMap, sweepNo, filter.experimentName, filter.device) if(!WaveExists(mapIndices)) continue endif @@ -4623,6 +4641,40 @@ static Function/WAVE SF_OperationSelectVis(variable jsonId, string jsonPath, str return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTVIS, discardOpStack = 1, dataType = SF_DATATYPE_SELECTVIS) End +/// `selexp(expName)` // expName is a string with optional wildcards +/// +/// returns a one element text wave +static Function/WAVE SF_OperationSelectExperiment(variable jsonId, string jsonPath, string graph) + + string expName + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTEXP, 1, maxArgs = 1) + + expName = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTEXP, 0) + Make/FREE/T output = {expName} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTEXP, discardOpStack = 1, dataType = SF_DATATYPE_SELECTEXP) +End + +/// `seldev(device)` // device is a string with optional wildcards +/// +/// returns a one element text wave +static Function/WAVE SF_OperationSelectDevice(variable jsonId, string jsonPath, string graph) + + string expName + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTDEV, 1, maxArgs = 1) + + expName = SFH_GetArgumentAsText(jsonId, jsonPath, graph, SF_OP_SELECTDEV, 0) + Make/FREE/T output = {expName} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTDEV, discardOpStack = 1, dataType = SF_DATATYPE_SELECTDEV) +End + /// `selcm(mode, mode, ...)` // mode can be `ic`, `vc`, `izero`, `all` /// see @ref SFClampModeStrings /// @@ -4773,6 +4825,8 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string STRUCT SF_SelectParameters filter variable i, numArgs, selectArgPresent string type, vis + string expName = "" + string device = "" SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") SF_InitSelectFilterUninitalized(filter) @@ -4787,6 +4841,20 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string continue endif strswitch(type) + case SF_DATATYPE_SELECTDEV: + if(IsEmpty(device)) + device = WaveText(arg, row = 0) + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTDEV + " argument.") + endif + break + case SF_DATATYPE_SELECTEXP: + if(IsEmpty(expName)) + expName = WaveText(arg, row = 0) + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTEXP + " argument.") + endif + break case SF_DATATYPE_SELECTVIS: if(IsEmpty(filter.vis)) filter.vis = WaveText(arg, row = 0) @@ -4858,6 +4926,13 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string SF_SetSelectionFilterDefaults(graph, filter, selectArgPresent) + if(!IsEmpty(expName)) + filter.experimentName = SF_GetSelectionExperiment(graph, expName) + endif + if(!IsEmpty(device)) + filter.device = SF_GetSelectionDevice(graph, device) + endif + WAVE/Z selectData = SF_GetSelectData(graph, filter) if(!WaveExists(selectData)) @@ -4889,6 +4964,41 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string return SFH_GetOutputForExecutor(output, graph, SF_OP_SELECT) End +static Function/S SF_GetSelectionExperiment(string graph, string expName) + + string currentExperimentName + + if(BSP_IsDataBrowser(graph)) + currentExperimentName = GetExperimentName() + SFH_ASSERT(stringmatch(currentExperimentName, expName), "Selected experiment does not exist") + + return currentExperimentName + endif + if(BSP_IsSweepBrowser(graph)) + return SF_MatchSweepMapColumn(graph, expName, "FileName", SF_OP_SELECTEXP) + endif + + ASSERT(0, "Unknown browser type") +End + +static Function/S SF_GetSelectionDevice(string graph, string device) + + string deviceDB + + if(BSP_IsDataBrowser(graph)) + deviceDB = DB_GetDevice(graph) + SFH_ASSERT(!IsEmpty(deviceDB), "DataBrowser has no locked device") + SFH_ASSERT(stringmatch(deviceDB, device), "Selected device does not exist") + + return deviceDB + endif + if(BSP_IsSweepBrowser(graph)) + return SF_MatchSweepMapColumn(graph, device, "Device", SF_OP_SELECTDEV) + endif + + ASSERT(0, "Unknown browser type") +End + /// @brief sets uninitialized fields of the selection filter static Function SF_SetSelectionFilterDefaults(string graph, STRUCT SF_SelectParameters &filter, variable includeAll) @@ -4919,6 +5029,12 @@ static Function SF_SetSelectionFilterDefaults(string graph, STRUCT SF_SelectPara if(!WaveExists(filter.ranges)) WAVE/WAVE filter.ranges = SFH_AsDataSet(SFH_GetFullRange()) endif + if(numtype(strlen(filter.experimentName)) == 2) + filter.experimentName = "" + endif + if(numtype(strlen(filter.device)) == 2) + filter.device = "" + endif End /// `data(array range[, array selectData])` diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 7c7267ad0e..258a6ff6d2 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -608,6 +608,8 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD JWN_SetNumberInWaveNote(rangedSweepData, SF_META_SWEEPNO, sweepNo) JWN_SetNumberInWaveNote(rangedSweepData, SF_META_CHANNELTYPE, chanType) JWN_SetNumberInWaveNote(rangedSweepData, SF_META_CHANNELNUMBER, chanNr) + JWN_SetStringInWaveNote(rangedSweepData, SF_META_DEVICE, device) + JWN_SetStringInWaveNote(rangedSweepData, SF_META_EXPERIMENT, experiment) if(!IsNaN(mapIndex)) JWN_SetNumberInWaveNote(rangedSweepData, SF_META_SWEEPMAPINDEX, mapIndex) endif From 690d05423e792a530db5fde61079b9deca20091b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 25 Jun 2024 16:57:19 +0200 Subject: [PATCH 20/38] BSP: Adapt AddTracesForEpochs to work in SweepBrowser - remove now unused and obsolete BSP_GetSweepDF function --- Packages/MIES/MIES_BrowserSettingsPanel.ipf | 37 +++------------------ 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/Packages/MIES/MIES_BrowserSettingsPanel.ipf b/Packages/MIES/MIES_BrowserSettingsPanel.ipf index 613bde377f..86d94cad06 100644 --- a/Packages/MIES/MIES_BrowserSettingsPanel.ipf +++ b/Packages/MIES/MIES_BrowserSettingsPanel.ipf @@ -1590,7 +1590,7 @@ Function BSP_AddTracesForEpochs(string win) variable i, j, start_x, start_y, end_x, end_y, yOffset variable headstage, yLevelOffset, level, idx, numTraces, numEpochs - variable sweepNumber, traceIndex, channelType, channelNumber + variable sweepNumber, traceIndex, channelType, channelNumber, mapIndex STRUCT RGBColor c string xaxis, yaxis, axes, axis, levels_x_name, levels_y_name, name, idPart, level_x_trace @@ -1646,13 +1646,13 @@ Function BSP_AddTracesForEpochs(string win) sweepNumber = str2num(traceInfos[i][%sweepNumber]) channelType = WhichListItem(traceInfos[i][%channelType], XOP_CHANNEL_NAMES) channelNumber = str2num(traceInfos[i][%GUIChannelNumber]) + mapIndex = str2num(traceInfos[i][%SweepMapIndex]) - WAVE/Z/T numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNumber) + DFREF sweepDFR + [WAVE numericalValues, WAVE textualValues, sweepDFR] = SFH_GetLabNoteBooksAndDFForSweep(win, sweepNumber, mapIndex) ASSERT(WaveExists(numericalValues), "Numerical LabNotebook not found.") - WAVE/Z/T textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNumber) ASSERT(WaveExists(textualValues), "Textual LabNotebook not found.") - DFREF sweepDFR = BSP_GetSweepDF(win, sweepNumber) // present since a2172f03 (Added generations of epoch information wave, 2019-05-22) WAVE/T/Z epochsFromLBN = EP_FetchEpochs(numericalValues, textualValues, sweepNumber, sweepDFR, channelNumber, channelType) if(!WaveExists(epochsFromLBN)) @@ -2003,32 +2003,3 @@ static Function BSP_MemoryFreeMappedDF(string win) AB_FreeWorkingDFs(dfList, index) End - -/// @brief Gets sweep browser data folder from sweep- or data browser window name and sweep number -/// -/// @param[in] win sweep- or data browser window -/// @param[in] sweepNo sweep number -/// -/// @returns sweep data folder reference, null reference if requirements not met -Function/DF BSP_GetSweepDF(string win, variable sweepNo) - - variable isSweepBrowser - string device - - isSweepBrowser = BSP_IsSweepBrowser(win) - if(isSweepBrowser) - DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(win) - WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) - DFREF deviceDFR = SB_GetSweepDataFolder(sweepMap, sweepNo = sweepNo) - else - if(!BSP_HasBoundDevice(win)) - return $"" - endif - device = BSP_GetDevice(win) - DFREF deviceDFR = GetDeviceDataPath(device) - endif - - DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) - - return sweepDFR -End From 4935cc3e20ee2da84ad2d7012bee69c5d77e09cc Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 25 Jun 2024 18:21:26 +0200 Subject: [PATCH 21/38] SF: Add selexpandsci, selexpandrac operations for use with select selexpandsci: Extends the selected sweeps with all sweeps from the same stimset cycle id selexpandrac: Extends the selected sweeps with all sweeps from the same repeated acquisition cycle The operations take no arguments. The feature is enabled if they are added to select(....) if both are used then first SCI and then RAC is applied. --- Packages/MIES/MIES_Constants.ipf | 2 + Packages/MIES/MIES_Structures.ipf | 3 + Packages/MIES/MIES_SweepFormula.ipf | 262 +++++++++++++++++++++++++++- 3 files changed, 264 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 7102cdec05..e97fc8f86b 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2100,6 +2100,8 @@ StrConstant SF_DATATYPE_PSX = "PSX" StrConstant SF_DATATYPE_SELECTVIS = "SelectVis" StrConstant SF_DATATYPE_SELECTEXP = "SelectExp" StrConstant SF_DATATYPE_SELECTDEV = "SelectDev" +StrConstant SF_DATATYPE_SELECTEXPANDSCI = "SelectExpandSCI" +StrConstant SF_DATATYPE_SELECTEXPANDRAC = "SelectExpandRAC" StrConstant SF_DATATYPE_SELECTCM = "SelectClampMode" StrConstant SF_DATATYPE_SELECTSTIMSET = "selectStimset" StrConstant SF_DATATYPE_SELECTIVSCCSWEEPQC = "selectIVSCCSweepQC" diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index d92ebda82f..5d04311048 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -647,6 +647,7 @@ Structure ASYNC_ReadOutStruct EndStructure /// @brief Wraps all parameters combined for one SF select call +/// When adapting also change @ref SF_DuplicateSelectFilter Structure SF_SelectParameters WAVE selects WAVE channels @@ -659,4 +660,6 @@ Structure SF_SelectParameters variable setQC string experimentName string device + variable expandSCI + variable expandRAC EndStructure diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 6a98508034..7d8b9fccbc 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -97,6 +97,8 @@ static StrConstant SF_OP_SELECT = "select" static StrConstant SF_OP_SELECTVIS = "selvis" static StrConstant SF_OP_SELECTEXP = "selexp" static StrConstant SF_OP_SELECTDEV = "seldev" +static StrConstant SF_OP_SELECTEXPANDSCI = "selexpandsci" +static StrConstant SF_OP_SELECTEXPANDRAC = "selexpandrac" static StrConstant SF_OP_SELECTCM = "selcm" static StrConstant SF_OP_SELECTSTIMSET = "selstimset" static StrConstant SF_OP_SELECTIVSCCSWEEPQC = "selivsccsweepqc" @@ -202,6 +204,9 @@ static Constant SF_VARIABLE_PREFIX = 36 static StrConstant SF_GETSETINTERSECTIONSELECT_FORMAT = "%d_%d_%d_%f" +static Constant SELECTDATA_MODE_SCI = 1 +static Constant SELECTDATA_MODE_RAC = 2 + Menu "GraphPopup" "Bring browser to front", /Q, SF_BringBrowserToFront() End @@ -232,7 +237,8 @@ Function/WAVE SF_GetNamedOperations() SF_OP_STORE, SF_OP_SELECT, SF_OP_POWERSPECTRUM, SF_OP_TPSS, SF_OP_TPBASE, SF_OP_TPINST, SF_OP_TPFIT, \ SF_OP_PSX, SF_OP_PSX_KERNEL, SF_OP_PSX_STATS, SF_OP_PSX_RISETIME, SF_OP_PSX_PREP, SF_OP_PSX_DECONV_FILTER, \ SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ - SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV} + SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV, \ + SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC} return wt End @@ -1159,6 +1165,12 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_SELECTDEV: WAVE out = SF_OperationSelectDevice(jsonId, jsonPath, graph) break + case SF_OP_SELECTEXPANDSCI: + WAVE out = SF_OperationSelectExpandSCI(jsonId, jsonPath, graph) + break + case SF_OP_SELECTEXPANDRAC: + WAVE out = SF_OperationSelectExpandRAC(jsonId, jsonPath, graph) + break case SF_OP_SELECTCM: WAVE out = SF_OperationSelectCM(jsonId, jsonPath, graph) break @@ -4658,6 +4670,34 @@ static Function/WAVE SF_OperationSelectExperiment(variable jsonId, string jsonPa return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTEXP, discardOpStack = 1, dataType = SF_DATATYPE_SELECTEXP) End +/// `selexpandsci()` // no arguments +/// +/// returns a one element numeric wave +static Function/WAVE SF_OperationSelectExpandSCI(variable jsonId, string jsonPath, string graph) + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTEXPANDSCI, 0, maxArgs = 0) + + Make/FREE/D output = {1} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTEXPANDSCI, discardOpStack = 1, dataType = SF_DATATYPE_SELECTEXPANDSCI) +End + +/// `selexpandrac()` // no arguments +/// +/// returns a one element numeric wave +static Function/WAVE SF_OperationSelectExpandRAC(variable jsonId, string jsonPath, string graph) + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTEXPANDRAC, 0, maxArgs = 0) + + Make/FREE/D output = {1} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTEXPANDRAC, discardOpStack = 1, dataType = SF_DATATYPE_SELECTEXPANDRAC) +End + /// `seldev(device)` // device is a string with optional wildcards /// /// returns a one element text wave @@ -4811,8 +4851,10 @@ static Function SF_InitSelectFilterUninitalized(STRUCT SF_SelectParameters &s) s.clampMode = NaN WAVE/Z/T s.stimsets = $"" WAVE/Z/WAVE s.ranges = $"" - s.sweepQC = NaN - s.setQC = NaN + s.sweepQC = NaN + s.setQC = NaN + s.expandSCI = NaN + s.expandRAC = NaN End /// `select(selectFilterOp...)` @@ -4841,6 +4883,20 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string continue endif strswitch(type) + case SF_DATATYPE_SELECTEXPANDSCI: + if(IsNaN(filter.expandSCI)) + filter.expandSCI = 1 + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTEXPANDSCI + " argument.") + endif + break + case SF_DATATYPE_SELECTEXPANDRAC: + if(IsNaN(filter.expandRAC)) + filter.expandRAC = 1 + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTEXPANDRAC + " argument.") + endif + break case SF_DATATYPE_SELECTDEV: if(IsEmpty(device)) device = WaveText(arg, row = 0) @@ -4934,6 +4990,20 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string endif WAVE/Z selectData = SF_GetSelectData(graph, filter) + if(WaveExists(selectData)) + // SCI is a subset of RAC, thus if RAC and SCI is enabled then it is sufficient to extend through RAC + if(filter.expandRAC) + WAVE selectWithRACFilledUp = SF_GetSelectDataWithSCIorRAC(graph, selectData, filter, SELECTDATA_MODE_RAC) + WAVE selectData = selectWithRACFilledUp + elseif(filter.expandSCI) + WAVE selectWithSCIFilledUp = SF_GetSelectDataWithSCIorRAC(graph, selectData, filter, SELECTDATA_MODE_SCI) + WAVE selectData = selectWithSCIFilledUp + endif + if(filter.expandSCI || filter.expandRAC) + WAVE sortedSelectData = SF_SortSelectData(selectData) + WAVE selectData = sortedSelectData + endif + endif if(!WaveExists(selectData)) // case: select from added filter arguments leaves empty selection, then result is empty as intersection with any other selection would yield also empty result @@ -4964,6 +5034,186 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string return SFH_GetOutputForExecutor(output, graph, SF_OP_SELECT) End +static Function [STRUCT SF_SelectParameters filterDup] SF_DuplicateSelectFilter(STRUCT SF_SelectParameters &filter) + + WAVE/Z filterDup.selects = filter.selects + WAVE/Z filterDup.channels = filter.channels + WAVE/Z filterDup.sweeps = filter.sweeps + filterDup.vis = filter.vis + filterDup.clampMode = filter.clampMode + WAVE/Z/T filterDup.stimsets = filter.stimsets + WAVE/Z filterDup.ranges = filter.ranges + filterDup.sweepQC = filter.sweepQC + filterDup.setQC = filter.setQC + filterDup.experimentName = filter.experimentName + filterDup.device = filter.device + filterDup.expandSCI = filter.expandSCI + filterDup.expandRAC = filter.expandRAC + + return [filterDup] +End + +static Function/WAVE SF_RestoreSelectDataFromText(WAVE/T selectText) + + WAVE selectData = SFH_NewSelectDataWave(DimSize(selectText, ROWS), 1) + MultiThread selectData[][%SWEEP] = SF_ParseSelectText(selectText, selectData, p) + + return selectData +End + +threadsafe static Function SF_ParseSelectText(WAVE/T selectText, WAVE selectData, variable index) + + variable sweepNo, channelNumber, channelType, mapIndex + + sscanf selectText[index], SF_GETSETINTERSECTIONSELECT_FORMAT, sweepNo, channelType, channelNumber, mapIndex + ASSERT_TS(V_flag == 4, "Failed parsing selectText") + selectData[index][%SWEEP] = sweepNo + selectData[index][%CHANNELNUMBER] = channelNumber + selectData[index][%CHANNELTYPE] = channelType + selectData[index][%SWEEPMAPINDEX] = mapIndex + + return sweepNo +End + +static Function/WAVE SF_GetUniqueSelectData(WAVE selectData) + + WAVE/T selectText = SF_CreateSelectWaveRowIds(selectData) + WAVE/T selectTextUnique = GetUniqueEntries(selectText) + return SF_RestoreSelectDataFromText(selectTextUnique) +End + +threadsafe static Function/S SF_CreateSweepMapRowId(string experiment, string datafolder, string device, string sweep) + + string id + + sprintf id, "%s|%s|%s|%s", experiment, datafolder, device, sweep + + return id +End + +threadsafe static Function/S SF_GetSweepMapRowId(WAVE/T sweepMap, variable index) + + return SF_CreateSweepMapRowId(sweepMap[index][%FileName], sweepMap[index][%DataFolder], sweepMap[index][%Device], sweepMap[index][%Sweep]) +End + +threadsafe static Function SF_GetSweepMapIndexFromIds(WAVE/T sweepMapIds, string experiment, string datafolder, string device, variable sweepNo) + + string id + + id = SF_CreateSweepMapRowId(experiment, datafolder, device, num2istr(sweepNo)) + FindValue/TXOP=4/TEXT=id sweepMapIds + ASSERT_TS(V_row >= 0, "SweepMap id not found") + + return V_row +End + +static Function/WAVE SF_GetAdditionalSweepsWithSameSCIorRAC(WAVE numericalValues, variable mode, variable sweepNo, variable channelType, variable channelNumber) + + variable headstage + + if(mode == SELECTDATA_MODE_SCI) + headstage = GetHeadstageForChannel(numericalValues, sweepNo, channelType, channelNumber, DATA_ACQUISITION_MODE) + if(IsNaN(headstage)) + return $"" + endif + WAVE/Z additionalSweeps = AFH_GetSweepsFromSameSCI(numericalValues, sweepNo, headstage) + elseif(mode == SELECTDATA_MODE_RAC) + WAVE/Z additionalSweeps = AFH_GetSweepsFromSameRACycle(numericalValues, sweepNo) + endif + if(!WaveExists(additionalSweeps)) + return $"" + endif + if(DimSize(additionalSweeps, ROWS) == 1) + return $"" + endif + + // Need to work on a copy if we modify it or we corrupt the cached wave + Duplicate/FREE additionalSweeps, additionalSweepsDup + FindValue/V=(sweepNo)/UOFV additionalSweepsDup + ASSERT(V_row >= 0, "Expected to find original sweep number") + DeleteWavePoint(additionalSweepsDup, ROWS, index = V_row) + + return additionalSweepsDup +End + +/// @brief Takes input selections and extends them. The extension of the selection is chosen through mode, one of SELECTDATA_MODE_* +/// For RAC: For each input selection adds all selections of the same repeated acquisition cycle +/// For SCI: For each input selection adds all selections of the same stimset cycle id and headstage +/// Returns all resulting unique selections. +static Function/WAVE SF_GetSelectDataWithSCIorRAC(string graph, WAVE selectData, STRUCT SF_SelectParameters &filter, variable mode) + + variable i, j, isSweepBrowser, numSelected + variable sweepNo, channelType, channelNumber, mapIndex + variable addSweepNo + + ASSERT(mode == SELECTDATA_MODE_SCI || mode == SELECTDATA_MODE_RAC, "Unknown SCI/RAC mode") + + [STRUCT SF_SelectParameters filterDup] = SF_DuplicateSelectFilter(filter) + filterDup.vis = SF_OP_SELECTVIS_ALL + + isSweepBrowser = BSP_IsSweepBrowser(graph) + if(isSweepBrowser) + WAVE/T sweepMap = SB_GetSweepMap(graph) + if(mode == SELECTDATA_MODE_SCI) + Make/FREE/T/N=(GetNumberFromWaveNote(sweepMap, NOTE_INDEX)) sweepMapIds + MultiThread sweepMapIds[] = SF_GetSweepMapRowId(sweepMap, p) + endif + else + filterDup.experimentName = GetExperimentName() + filterDup.device = DB_GetDevice(graph) + endif + + numSelected = DimSize(selectData, ROWS) + for(i = 0; i < numSelected; i += 1) + sweepNo = selectData[i][%SWEEP] + mapIndex = selectData[i][%SWEEPMAPINDEX] + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + if(!WaveExists(numericalValues)) + continue + endif + + channelNumber = selectData[i][%CHANNELNUMBER] + channelType = selectData[i][%CHANNELTYPE] + WAVE/Z additionalSweeps = SF_GetAdditionalSweepsWithSameSCIorRAC(numericalValues, mode, sweepNo, channelType, channelNumber) + if(!WaveExists(additionalSweeps)) + continue + endif + + if(isSweepBrowser) + filterDup.experimentName = sweepMap[mapIndex][%FileName] + filterDup.device = sweepMap[mapIndex][%Device] + endif + + if(mode == SELECTDATA_MODE_SCI) + // SCI is headstage specific, we add exact the same channelType and channelNumber as the requested one + WAVE selectDataAdd = SFH_NewSelectDataWave(DimSize(additionalSweeps, ROWS), 1) + selectDataAdd[][%SWEEP] = additionalSweeps[p] + selectDataAdd[][%CHANNELNUMBER] = channelNumber + selectDataAdd[][%CHANNELTYPE] = channelType + if(isSweepBrowser) + MultiThread selectDataAdd[][%SWEEPMAPINDEX] = SF_GetSweepMapIndexFromIds(sweepMapIds, filterDup.experimentName, sweepMap[mapIndex][%DataFolder], filterDup.device, additionalSweeps[p]) + else + selectDataAdd[][%SWEEPMAPINDEX] = NaN + endif + else + WAVE filterDup.sweeps = additionalSweeps + WAVE/Z selectDataAdd = SF_GetSelectData(graph, filterDup) + if(!WaveExists(selectDataAdd)) + continue + endif + endif + Concatenate/FREE/NP=(ROWS) {selectDataAdd}, selectDataCollect + endfor + + if(!WaveExists(selectDataCollect)) + return selectData + endif + + Concatenate/FREE/NP=(ROWS) {selectData}, selectDataCollect + + return SF_GetUniqueSelectData(selectDataCollect) +End + static Function/S SF_GetSelectionExperiment(string graph, string expName) string currentExperimentName @@ -5035,6 +5285,12 @@ static Function SF_SetSelectionFilterDefaults(string graph, STRUCT SF_SelectPara if(numtype(strlen(filter.device)) == 2) filter.device = "" endif + if(IsNaN(filter.expandSCI)) + filter.expandSCI = 0 + endif + if(IsNaN(filter.expandRAC)) + filter.expandRAC = 0 + endif End /// `data(array range[, array selectData])` From f6d4cd431ba35bdf2f4bc4425c3790cdb7add78b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 1 Jul 2024 19:01:04 +0200 Subject: [PATCH 22/38] SF: Add operation selsetcyclecount, selsetsweepcount - both operations take exactly one numerical argument that sepcifies a specific setCycleCount and/or setSweepCount - if not used in select then the filter is ignored --- Packages/MIES/MIES_Constants.ipf | 76 +++++---- Packages/MIES/MIES_Structures.ipf | 2 + Packages/MIES/MIES_SweepFormula.ipf | 251 ++++++++++++++++++++-------- 3 files changed, 221 insertions(+), 108 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index e97fc8f86b..f1caf1a8b5 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2070,43 +2070,45 @@ StrConstant SF_META_FIT_COEFF = "FitCoefficients" StrConstant SF_META_FIT_SIGMA = "FitSigma" StrConstant SF_META_FIT_PARAMETER = "FitParameter" -StrConstant SF_DATATYPE_SWEEP = "SweepData" -StrConstant SF_DATATYPE_SWEEPNO = "SweepNumbers" -StrConstant SF_DATATYPE_CHANNELS = "Channels" -StrConstant SF_DATATYPE_SELECTCOMP = "SelectionComposite" -StrConstant SF_DATATYPE_SELECT = "Selection" -StrConstant SF_DATATYPE_FINDLEVEL = "FindLevel" -StrConstant SF_DATATYPE_APFREQUENCY = "ApFrequency" -StrConstant SF_DATATYPE_LABNOTEBOOK = "LabNotebook" -StrConstant SF_DATATYPE_BUTTERWORTH = "Butterworth" -StrConstant SF_DATATYPE_AREA = "Area" -StrConstant SF_DATATYPE_INTEGRATE = "Integrate" -StrConstant SF_DATATYPE_DERIVATIVE = "Derivative" -StrConstant SF_DATATYPE_STDEV = "StDev" -StrConstant SF_DATATYPE_VARIANCE = "Variance" -StrConstant SF_DATATYPE_RMS = "RMS" -StrConstant SF_DATATYPE_AVG = "Average" -StrConstant SF_DATATYPE_MAX = "Max" -StrConstant SF_DATATYPE_MIN = "Min" -StrConstant SF_DATATYPE_RANGE = "Range" -StrConstant SF_DATATYPE_EPOCHS = "Epochs" -StrConstant SF_DATATYPE_TP = "TestPulse" -StrConstant SF_DATATYPE_TPSS = "TestPulseMode_SteadyState" -StrConstant SF_DATATYPE_TPINST = "TestPulseMode_Instantaneous" -StrConstant SF_DATATYPE_TPBASE = "TestPulseMode_Baseline" -StrConstant SF_DATATYPE_TPFIT = "TestPulseMode_Fit" -StrConstant SF_DATATYPE_POWERSPECTRUM = "Powerspectrum" -StrConstant SF_DATATYPE_PSX = "PSX" -StrConstant SF_DATATYPE_SELECTVIS = "SelectVis" -StrConstant SF_DATATYPE_SELECTEXP = "SelectExp" -StrConstant SF_DATATYPE_SELECTDEV = "SelectDev" -StrConstant SF_DATATYPE_SELECTEXPANDSCI = "SelectExpandSCI" -StrConstant SF_DATATYPE_SELECTEXPANDRAC = "SelectExpandRAC" -StrConstant SF_DATATYPE_SELECTCM = "SelectClampMode" -StrConstant SF_DATATYPE_SELECTSTIMSET = "selectStimset" -StrConstant SF_DATATYPE_SELECTIVSCCSWEEPQC = "selectIVSCCSweepQC" -StrConstant SF_DATATYPE_SELECTIVSCCSETQC = "selectIVSCCSetQC" -StrConstant SF_DATATYPE_SELECTRANGE = "selectRange" +StrConstant SF_DATATYPE_SWEEP = "SweepData" +StrConstant SF_DATATYPE_SWEEPNO = "SweepNumbers" +StrConstant SF_DATATYPE_CHANNELS = "Channels" +StrConstant SF_DATATYPE_SELECTCOMP = "SelectionComposite" +StrConstant SF_DATATYPE_SELECT = "Selection" +StrConstant SF_DATATYPE_FINDLEVEL = "FindLevel" +StrConstant SF_DATATYPE_APFREQUENCY = "ApFrequency" +StrConstant SF_DATATYPE_LABNOTEBOOK = "LabNotebook" +StrConstant SF_DATATYPE_BUTTERWORTH = "Butterworth" +StrConstant SF_DATATYPE_AREA = "Area" +StrConstant SF_DATATYPE_INTEGRATE = "Integrate" +StrConstant SF_DATATYPE_DERIVATIVE = "Derivative" +StrConstant SF_DATATYPE_STDEV = "StDev" +StrConstant SF_DATATYPE_VARIANCE = "Variance" +StrConstant SF_DATATYPE_RMS = "RMS" +StrConstant SF_DATATYPE_AVG = "Average" +StrConstant SF_DATATYPE_MAX = "Max" +StrConstant SF_DATATYPE_MIN = "Min" +StrConstant SF_DATATYPE_RANGE = "Range" +StrConstant SF_DATATYPE_EPOCHS = "Epochs" +StrConstant SF_DATATYPE_TP = "TestPulse" +StrConstant SF_DATATYPE_TPSS = "TestPulseMode_SteadyState" +StrConstant SF_DATATYPE_TPINST = "TestPulseMode_Instantaneous" +StrConstant SF_DATATYPE_TPBASE = "TestPulseMode_Baseline" +StrConstant SF_DATATYPE_TPFIT = "TestPulseMode_Fit" +StrConstant SF_DATATYPE_POWERSPECTRUM = "Powerspectrum" +StrConstant SF_DATATYPE_PSX = "PSX" +StrConstant SF_DATATYPE_SELECTVIS = "SelectVis" +StrConstant SF_DATATYPE_SELECTEXP = "SelectExp" +StrConstant SF_DATATYPE_SELECTDEV = "SelectDev" +StrConstant SF_DATATYPE_SELECTEXPANDSCI = "SelectExpandSCI" +StrConstant SF_DATATYPE_SELECTEXPANDRAC = "SelectExpandRAC" +StrConstant SF_DATATYPE_SELECTCM = "SelectClampMode" +StrConstant SF_DATATYPE_SELECTSTIMSET = "selectStimset" +StrConstant SF_DATATYPE_SELECTIVSCCSWEEPQC = "selectIVSCCSweepQC" +StrConstant SF_DATATYPE_SELECTIVSCCSETQC = "selectIVSCCSetQC" +StrConstant SF_DATATYPE_SELECTRANGE = "selectRange" +StrConstant SF_DATATYPE_SELECTSETCYCLECOUNT = "SelectSetCycleCount" +StrConstant SF_DATATYPE_SELECTSETSWEEPCOUNT = "SelectSetSweepCount" StrConstant SF_WREF_MARKER = "\"WREF@\":" StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index 5d04311048..c697b4bb02 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -662,4 +662,6 @@ Structure SF_SelectParameters string device variable expandSCI variable expandRAC + variable setCycleCount + variable setSweepCount EndStructure diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 7d8b9fccbc..35cfedc45e 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -59,56 +59,58 @@ static Constant SF_APFREQUENCY_INSTANTANEOUS = 0x1 static Constant SF_APFREQUENCY_APCOUNT = 0x2 static Constant SF_APFREQUENCY_INSTANTANEOUS_PAIR = 0x3 -static StrConstant SF_OP_MINUS = "-" -static StrConstant SF_OP_PLUS = "+" -static StrConstant SF_OP_MULT = "*" -static StrConstant SF_OP_DIV = "~1" -static StrConstant SF_OP_RANGE = "range" -static StrConstant SF_OP_RANGESHORT = "…" -static StrConstant SF_OP_MIN = "min" -static StrConstant SF_OP_MAX = "max" -static StrConstant SF_OP_AVG = "avg" -static StrConstant SF_OP_MEAN = "mean" -static StrConstant SF_OP_RMS = "rms" -static StrConstant SF_OP_VARIANCE = "variance" -static StrConstant SF_OP_STDEV = "stdev" -static StrConstant SF_OP_DERIVATIVE = "derivative" -static StrConstant SF_OP_INTEGRATE = "integrate" -static StrConstant SF_OP_TIME = "time" -static StrConstant SF_OP_XVALUES = "xvalues" -static StrConstant SF_OP_TEXT = "text" -static StrConstant SF_OP_LOG = "log" -static StrConstant SF_OP_LOG10 = "log10" -static StrConstant SF_OP_APFREQUENCY = "apfrequency" -static StrConstant SF_OP_CURSORS = "cursors" -static StrConstant SF_OP_SELECTSWEEPS = "selsweeps" -static StrConstant SF_OP_AREA = "area" -static StrConstant SF_OP_SETSCALE = "setscale" -static StrConstant SF_OP_BUTTERWORTH = "butterworth" -static StrConstant SF_OP_SELECTCHANNELS = "selchannels" -static StrConstant SF_OP_DATA = "data" -static StrConstant SF_OP_LABNOTEBOOK = "labnotebook" -static StrConstant SF_OP_WAVE = "wave" -static StrConstant SF_OP_FINDLEVEL = "findlevel" -static StrConstant SF_OP_EPOCHS = "epochs" -static StrConstant SF_OP_TP = "tp" -static StrConstant SF_OP_STORE = "store" -static StrConstant SF_OP_SELECT = "select" -static StrConstant SF_OP_SELECTVIS = "selvis" -static StrConstant SF_OP_SELECTEXP = "selexp" -static StrConstant SF_OP_SELECTDEV = "seldev" -static StrConstant SF_OP_SELECTEXPANDSCI = "selexpandsci" -static StrConstant SF_OP_SELECTEXPANDRAC = "selexpandrac" -static StrConstant SF_OP_SELECTCM = "selcm" -static StrConstant SF_OP_SELECTSTIMSET = "selstimset" -static StrConstant SF_OP_SELECTIVSCCSWEEPQC = "selivsccsweepqc" -static StrConstant SF_OP_SELECTIVSCCSETQC = "selivsccsetqc" -static StrConstant SF_OP_SELECTRANGE = "selrange" -static StrConstant SF_OP_POWERSPECTRUM = "powerspectrum" -static StrConstant SF_OP_TPSS = "tpss" -static StrConstant SF_OP_TPINST = "tpinst" -static StrConstant SF_OP_TPBASE = "tpbase" -static StrConstant SF_OP_TPFIT = "tpfit" +static StrConstant SF_OP_MINUS = "-" +static StrConstant SF_OP_PLUS = "+" +static StrConstant SF_OP_MULT = "*" +static StrConstant SF_OP_DIV = "~1" +static StrConstant SF_OP_RANGE = "range" +static StrConstant SF_OP_RANGESHORT = "…" +static StrConstant SF_OP_MIN = "min" +static StrConstant SF_OP_MAX = "max" +static StrConstant SF_OP_AVG = "avg" +static StrConstant SF_OP_MEAN = "mean" +static StrConstant SF_OP_RMS = "rms" +static StrConstant SF_OP_VARIANCE = "variance" +static StrConstant SF_OP_STDEV = "stdev" +static StrConstant SF_OP_DERIVATIVE = "derivative" +static StrConstant SF_OP_INTEGRATE = "integrate" +static StrConstant SF_OP_TIME = "time" +static StrConstant SF_OP_XVALUES = "xvalues" +static StrConstant SF_OP_TEXT = "text" +static StrConstant SF_OP_LOG = "log" +static StrConstant SF_OP_LOG10 = "log10" +static StrConstant SF_OP_APFREQUENCY = "apfrequency" +static StrConstant SF_OP_CURSORS = "cursors" +static StrConstant SF_OP_SELECTSWEEPS = "selsweeps" +static StrConstant SF_OP_AREA = "area" +static StrConstant SF_OP_SETSCALE = "setscale" +static StrConstant SF_OP_BUTTERWORTH = "butterworth" +static StrConstant SF_OP_SELECTCHANNELS = "selchannels" +static StrConstant SF_OP_DATA = "data" +static StrConstant SF_OP_LABNOTEBOOK = "labnotebook" +static StrConstant SF_OP_WAVE = "wave" +static StrConstant SF_OP_FINDLEVEL = "findlevel" +static StrConstant SF_OP_EPOCHS = "epochs" +static StrConstant SF_OP_TP = "tp" +static StrConstant SF_OP_STORE = "store" +static StrConstant SF_OP_SELECT = "select" +static StrConstant SF_OP_SELECTVIS = "selvis" +static StrConstant SF_OP_SELECTEXP = "selexp" +static StrConstant SF_OP_SELECTDEV = "seldev" +static StrConstant SF_OP_SELECTEXPANDSCI = "selexpandsci" +static StrConstant SF_OP_SELECTEXPANDRAC = "selexpandrac" +static StrConstant SF_OP_SELECTSETCYCLECOUNT = "selsetcyclecount" +static StrConstant SF_OP_SELECTSETSWEEPCOUNT = "selsetsweepcount" +static StrConstant SF_OP_SELECTCM = "selcm" +static StrConstant SF_OP_SELECTSTIMSET = "selstimset" +static StrConstant SF_OP_SELECTIVSCCSWEEPQC = "selivsccsweepqc" +static StrConstant SF_OP_SELECTIVSCCSETQC = "selivsccsetqc" +static StrConstant SF_OP_SELECTRANGE = "selrange" +static StrConstant SF_OP_POWERSPECTRUM = "powerspectrum" +static StrConstant SF_OP_TPSS = "tpss" +static StrConstant SF_OP_TPINST = "tpinst" +static StrConstant SF_OP_TPBASE = "tpbase" +static StrConstant SF_OP_TPFIT = "tpfit" static StrConstant SF_OPSHORT_MINUS = "minus" static StrConstant SF_OPSHORT_PLUS = "plus" @@ -207,6 +209,11 @@ static StrConstant SF_GETSETINTERSECTIONSELECT_FORMAT = "%d_%d_%d_%f" static Constant SELECTDATA_MODE_SCI = 1 static Constant SELECTDATA_MODE_RAC = 2 +static Constant SWEEPPROP_CLAMPMODE = 0 +static Constant SWEEPPROP_SETCYCLECOUNT = 1 +static Constant SWEEPPROP_SETSWEEPCOUNT = 2 +static Constant SWEEPPROP_END = 3 + Menu "GraphPopup" "Bring browser to front", /Q, SF_BringBrowserToFront() End @@ -238,7 +245,7 @@ Function/WAVE SF_GetNamedOperations() SF_OP_PSX, SF_OP_PSX_KERNEL, SF_OP_PSX_STATS, SF_OP_PSX_RISETIME, SF_OP_PSX_PREP, SF_OP_PSX_DECONV_FILTER, \ SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV, \ - SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC} + SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC, SF_OP_SELECTSETCYCLECOUNT, SF_OP_SELECTSETSWEEPCOUNT} return wt End @@ -1171,6 +1178,12 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_SELECTEXPANDRAC: WAVE out = SF_OperationSelectExpandRAC(jsonId, jsonPath, graph) break + case SF_OP_SELECTSETCYCLECOUNT: + WAVE out = SF_OperationSelectSetCycleCount(jsonId, jsonPath, graph) + break + case SF_OP_SELECTSETSWEEPCOUNT: + WAVE out = SF_OperationSelectSetSweepCount(jsonId, jsonPath, graph) + break case SF_OP_SELECTCM: WAVE out = SF_OperationSelectCM(jsonId, jsonPath, graph) break @@ -2260,6 +2273,13 @@ static Function/WAVE SF_GetSweepMapIndices(WAVE/T sweepMap, variable sweepNo, st return GetSetIntersection(sweepIndices, devIndices) End +static Function/WAVE SF_MakeSweepPropertiesDisplayed(variable numTraces) + + Make/FREE/D/N=(numTraces, SWEEPPROP_END) sweepPropertiesDisplayed + + return sweepPropertiesDisplayed +End + /// @brief Use the labnotebook information to return the active channel numbers /// for a given set of sweeps /// @@ -2275,7 +2295,8 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & variable isSweepBrowser variable dimPosSweep, dimPosChannelNumber, dimPosChannelType, dimPosSweepMapIndex variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode, dimPosTExpName, dimPosTDevice, dimPosTSweepMapIndex - variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex + variable dimPosTNumericalValues + variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex, setCycleCount, setSweepCount string msg, device, singleSweepDFStr, expName, dataFolder variable mapSize = 1 DFREF deviceDFR = $"" @@ -2307,19 +2328,20 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & WAVE sweeps = sweepsIntersect numSweeps = DimSize(sweeps, ROWS) - WAVE selectDisplayed = SFH_NewSelectDataWave(numTraces, 1) - Make/FREE/D/N=(numTraces) clampModeDisplayed + WAVE selectDisplayed = SFH_NewSelectDataWave(numTraces, 1) + WAVE sweepPropertiesDisplayed = SF_MakeSweepPropertiesDisplayed(numTraces) dimPosSweep = FindDimLabel(selectDisplayed, COLS, "SWEEP") dimPosChannelType = FindDimLabel(selectDisplayed, COLS, "CHANNELTYPE") dimPosChannelNumber = FindDimLabel(selectDisplayed, COLS, "CHANNELNUMBER") dimPosSweepMapIndex = FindDimLabel(selectDisplayed, COLS, "SWEEPMAPINDEX") - dimPosTChannelType = FindDimLabel(traces, COLS, "channelType") - dimPosTChannelNumber = FindDimLabel(traces, COLS, "GUIChannelNumber") - dimPosTClampMode = FindDimLabel(traces, COLS, "clampMode") - dimPosTExpName = FindDimLabel(traces, COLS, "Experiment") - dimPosTDevice = FindDimLabel(traces, COLS, "Device") - dimPosTSweepMapIndex = FindDimLabel(traces, COLS, "SweepMapIndex") + dimPosTChannelType = FindDimLabel(traces, COLS, "channelType") + dimPosTChannelNumber = FindDimLabel(traces, COLS, "GUIChannelNumber") + dimPosTClampMode = FindDimLabel(traces, COLS, "clampMode") + dimPosTExpName = FindDimLabel(traces, COLS, "Experiment") + dimPosTDevice = FindDimLabel(traces, COLS, "Device") + dimPosTSweepMapIndex = FindDimLabel(traces, COLS, "SweepMapIndex") + dimPosTNumericalValues = FindDimLabel(traces, COLS, "numericalValues") for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] for(j = 0; j < numTraces; j += 1) @@ -2333,12 +2355,30 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & continue endif endif - selectDisplayed[outIndex][dimPosSweep] = sweepNo - selectDisplayed[outIndex][dimPosChannelType] = WhichListItem(traces[j][dimPosTChannelType], XOP_CHANNEL_NAMES) - selectDisplayed[outIndex][dimPosChannelNumber] = str2num(traces[j][dimPosTChannelNumber]) - selectDisplayed[outIndex][dimPosSweepMapIndex] = str2num(traces[j][dimPosTSweepMapIndex]) - clampModeDisplayed[outIndex] = str2num(traces[j][dimPosTClampMode]) - outIndex += 1 + channelType = WhichListItem(traces[j][dimPosTChannelType], XOP_CHANNEL_NAMES) + channelNumber = str2num(traces[j][dimPosTChannelNumber]) + WAVE numericalValues = $traces[j][dimPosTNumericalValues] + if(!IsNaN(filter.setCycleCount)) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, "Set Cycle Count", channelNumber, channelType, DATA_ACQUISITION_MODE) + setCycleCount = WaveExists(setting) ? setting[index] : NaN + else + setCycleCount = NaN + endif + if(!IsNaN(filter.setSweepCount)) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, "Set Sweep Count", channelNumber, channelType, DATA_ACQUISITION_MODE) + setSweepCount = WaveExists(setting) ? setting[index] : NaN + else + setSweepCount = NaN + endif + + selectDisplayed[outIndex][dimPosSweep] = sweepNo + selectDisplayed[outIndex][dimPosChannelType] = channelType + selectDisplayed[outIndex][dimPosChannelNumber] = channelNumber + selectDisplayed[outIndex][dimPosSweepMapIndex] = str2num(traces[j][dimPosTSweepMapIndex]) + sweepPropertiesDisplayed[outIndex][SWEEPPROP_CLAMPMODE] = str2num(traces[j][dimPosTClampMode]) + sweepPropertiesDisplayed[outIndex][SWEEPPROP_SETCYCLECOUNT] = setCycleCount + sweepPropertiesDisplayed[outIndex][SWEEPPROP_SETSWEEPCOUNT] = setSweepCount + outIndex += 1 endif if(outIndex == numTraces) break @@ -2349,7 +2389,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & endif endfor Redimension/N=(outIndex, -1) selectDisplayed - Redimension/N=(outIndex) clampModeDisplayed + Redimension/N=(outIndex, -1) sweepPropertiesDisplayed numTraces = outIndex outIndex = 0 @@ -2418,8 +2458,8 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & if(fromDisplayed) for(l = 0; l < numTraces; l += 1) - clampCode = SF_MapClampModeToSelectCM(clampModeDisplayed[l]) - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode)) + clampCode = SF_MapClampModeToSelectCM(sweepPropertiesDisplayed[l][SWEEPPROP_CLAMPMODE]) + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT])) continue endif @@ -2445,8 +2485,16 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, CLAMPMODE_ENTRY_KEY, l, channelType, DATA_ACQUISITION_MODE) clampCode = WaveExists(setting) ? SF_MapClampModeToSelectCM(setting[index]) : SF_OP_SELECT_CLAMPCODE_NONE endif + if(!IsNaN(filter.setCycleCount)) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, "Set Cycle Count", l, channelType, DATA_ACQUISITION_MODE) + setCycleCount = WaveExists(setting) ? setting[index] : NaN + endif + if(!IsNaN(filter.setSweepCount)) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, "Set Sweep Count", l, channelType, DATA_ACQUISITION_MODE) + setSweepCount = WaveExists(setting) ? setting[index] : NaN + endif - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode)) + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount)) continue endif @@ -2472,7 +2520,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & return out End -static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode) +static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount) variable sweepQC, setQC string setName @@ -2515,6 +2563,14 @@ static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParamete endif endif + if(!IsNaN(filter.setCycleCount) && setCycleCount != filter.setCycleCount) + return 0 + endif + + if(!IsNaN(filter.setSweepCount) && setSweepCount != filter.setSweepCount) + return 0 + endif + return 1 End @@ -4698,6 +4754,40 @@ static Function/WAVE SF_OperationSelectExpandRAC(variable jsonId, string jsonPat return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTEXPANDRAC, discardOpStack = 1, dataType = SF_DATATYPE_SELECTEXPANDRAC) End +/// `selsetcyclecount(x)` // one numeric argument +/// +/// returns a one element numeric wave +static Function/WAVE SF_OperationSelectSetCycleCount(variable jsonId, string jsonPath, string graph) + + variable value + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTSETCYCLECOUNT, 1, maxArgs = 1) + + value = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_SELECTSETCYCLECOUNT, 0) + Make/FREE/D output = {value} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTSETCYCLECOUNT, discardOpStack = 1, dataType = SF_DATATYPE_SELECTSETCYCLECOUNT) +End + +/// `selsetsweepcount(x)` // one numeric argument +/// +/// returns a one element numeric wave +static Function/WAVE SF_OperationSelectSetSweepCount(variable jsonId, string jsonPath, string graph) + + variable value + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTSETSWEEPCOUNT, 1, maxArgs = 1) + + value = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_SELECTSETSWEEPCOUNT, 0) + Make/FREE/D output = {value} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTSETSWEEPCOUNT, discardOpStack = 1, dataType = SF_DATATYPE_SELECTSETSWEEPCOUNT) +End + /// `seldev(device)` // device is a string with optional wildcards /// /// returns a one element text wave @@ -4855,6 +4945,8 @@ static Function SF_InitSelectFilterUninitalized(STRUCT SF_SelectParameters &s) s.setQC = NaN s.expandSCI = NaN s.expandRAC = NaN + s.setCycleCount = NaN + s.setSweepCount = NaN End /// `select(selectFilterOp...)` @@ -4883,6 +4975,20 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string continue endif strswitch(type) + case SF_DATATYPE_SELECTSETCYCLECOUNT: + if(IsNaN(filter.setCycleCount)) + filter.setCycleCount = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTSETCYCLECOUNT + " argument.") + endif + break + case SF_DATATYPE_SELECTSETSWEEPCOUNT: + if(IsNaN(filter.setSweepCount)) + filter.setSweepCount = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTSETSWEEPCOUNT + " argument.") + endif + break case SF_DATATYPE_SELECTEXPANDSCI: if(IsNaN(filter.expandSCI)) filter.expandSCI = 1 @@ -5049,6 +5155,8 @@ static Function [STRUCT SF_SelectParameters filterDup] SF_DuplicateSelectFilter( filterDup.device = filter.device filterDup.expandSCI = filter.expandSCI filterDup.expandRAC = filter.expandRAC + filterDup.setCycleCount = filter.setCycleCount + filterDup.setSweepCount = filter.setSweepCount return [filterDup] End @@ -5291,6 +5399,7 @@ static Function SF_SetSelectionFilterDefaults(string graph, STRUCT SF_SelectPara if(IsNaN(filter.expandRAC)) filter.expandRAC = 0 endif + // setCycleCount, setSweepCount same as uninitialied values End /// `data(array range[, array selectData])` From 2d04575776331f9fafba6c25d4763e48c1c930d2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 2 Jul 2024 16:49:12 +0200 Subject: [PATCH 23/38] SF: Add operation selRACIndex, selSCIIndex both operation take exactly one numeric argument specifying the index selRACIndex selects all selections that have a specific index within a repeated acquisition cycle selSCIIndex selects all selections that have a specific index within a stimset cycle --- Packages/MIES/MIES_Constants.ipf | 2 + Packages/MIES/MIES_Structures.ipf | 2 + Packages/MIES/MIES_SweepFormula.ipf | 266 ++++++++++++++++++++++++++-- 3 files changed, 259 insertions(+), 11 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index f1caf1a8b5..31e1352d52 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2109,6 +2109,8 @@ StrConstant SF_DATATYPE_SELECTIVSCCSETQC = "selectIVSCCSetQC" StrConstant SF_DATATYPE_SELECTRANGE = "selectRange" StrConstant SF_DATATYPE_SELECTSETCYCLECOUNT = "SelectSetCycleCount" StrConstant SF_DATATYPE_SELECTSETSWEEPCOUNT = "SelectSetSweepCount" +StrConstant SF_DATATYPE_SELECTSCIINDEX = "SelectSCIIndex" +StrConstant SF_DATATYPE_SELECTRACINDEX = "SelectRACIndex" StrConstant SF_WREF_MARKER = "\"WREF@\":" StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index c697b4bb02..fa7b938e0a 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -664,4 +664,6 @@ Structure SF_SelectParameters variable expandRAC variable setCycleCount variable setSweepCount + variable sciIndex + variable racIndex EndStructure diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 35cfedc45e..5ea98c4b04 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -101,6 +101,8 @@ static StrConstant SF_OP_SELECTEXPANDSCI = "selexpandsci" static StrConstant SF_OP_SELECTEXPANDRAC = "selexpandrac" static StrConstant SF_OP_SELECTSETCYCLECOUNT = "selsetcyclecount" static StrConstant SF_OP_SELECTSETSWEEPCOUNT = "selsetsweepcount" +static StrConstant SF_OP_SELECTSCIINDEX = "selsciindex" +static StrConstant SF_OP_SELECTRACINDEX = "selracindex" static StrConstant SF_OP_SELECTCM = "selcm" static StrConstant SF_OP_SELECTSTIMSET = "selstimset" static StrConstant SF_OP_SELECTIVSCCSWEEPQC = "selivsccsweepqc" @@ -214,6 +216,8 @@ static Constant SWEEPPROP_SETCYCLECOUNT = 1 static Constant SWEEPPROP_SETSWEEPCOUNT = 2 static Constant SWEEPPROP_END = 3 +static StrConstant DB_EXPNAME_DUMMY = "|DataBrowserExperiment|" + Menu "GraphPopup" "Bring browser to front", /Q, SF_BringBrowserToFront() End @@ -245,7 +249,8 @@ Function/WAVE SF_GetNamedOperations() SF_OP_PSX, SF_OP_PSX_KERNEL, SF_OP_PSX_STATS, SF_OP_PSX_RISETIME, SF_OP_PSX_PREP, SF_OP_PSX_DECONV_FILTER, \ SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV, \ - SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC, SF_OP_SELECTSETCYCLECOUNT, SF_OP_SELECTSETSWEEPCOUNT} + SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC, SF_OP_SELECTSETCYCLECOUNT, SF_OP_SELECTSETSWEEPCOUNT, \ + SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX} return wt End @@ -1184,6 +1189,12 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_SELECTSETSWEEPCOUNT: WAVE out = SF_OperationSelectSetSweepCount(jsonId, jsonPath, graph) break + case SF_OP_SELECTSCIINDEX: + WAVE out = SF_OperationSelectSCIIndex(jsonId, jsonPath, graph) + break + case SF_OP_SELECTRACINDEX: + WAVE out = SF_OperationSelectRACIndex(jsonId, jsonPath, graph) + break case SF_OP_SELECTCM: WAVE out = SF_OperationSelectCM(jsonId, jsonPath, graph) break @@ -2296,7 +2307,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & variable dimPosSweep, dimPosChannelNumber, dimPosChannelType, dimPosSweepMapIndex variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode, dimPosTExpName, dimPosTDevice, dimPosTSweepMapIndex variable dimPosTNumericalValues - variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex, setCycleCount, setSweepCount + variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex, setCycleCount, setSweepCount, doStimsetMatching string msg, device, singleSweepDFStr, expName, dataFolder variable mapSize = 1 DFREF deviceDFR = $"" @@ -2311,6 +2322,11 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & fromDisplayed = !CmpStr(filter.vis, SF_OP_SELECTVIS_DISPLAYED) isSweepBrowser = BSP_IsSweepBrowser(graph) + if(!(DimSize(filter.stimsets, ROWS) == 1 && !CmpStr(filter.stimsets[0], SF_OP_SELECT_STIMSETS_ALL))) + WAVE/Z indizes = FindIndizes(filter.stimsets, str = SF_OP_SELECT_STIMSETS_ALL) + doStimsetMatching = !WaveExists(indizes) + endif + if(fromDisplayed) WAVE/T/Z traces = GetTraceInfos(graph) if(!WaveExists(traces)) @@ -2459,7 +2475,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & for(l = 0; l < numTraces; l += 1) clampCode = SF_MapClampModeToSelectCM(sweepPropertiesDisplayed[l][SWEEPPROP_CLAMPMODE]) - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT])) + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT], doStimsetMatching)) continue endif @@ -2494,7 +2510,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & setSweepCount = WaveExists(setting) ? setting[index] : NaN endif - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount)) + if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount, doStimsetMatching)) continue endif @@ -2520,7 +2536,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & return out End -static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount) +static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount, variable doStimsetMatching) variable sweepQC, setQC string setName @@ -2541,8 +2557,7 @@ static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParamete return 0 endif - WAVE/Z indizes = FindIndizes(filter.stimsets, str = SF_OP_SELECT_STIMSETS_ALL) - if(!WaveExists(indizes)) + if(doStimsetMatching) setName = SFH_GetStimsetName(graph, sweepNo, channelNumber, channelType) if(!MatchAgainstWildCardPatterns(filter.stimsets, setName)) return 0 @@ -4788,6 +4803,40 @@ static Function/WAVE SF_OperationSelectSetSweepCount(variable jsonId, string jso return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTSETSWEEPCOUNT, discardOpStack = 1, dataType = SF_DATATYPE_SELECTSETSWEEPCOUNT) End +/// `selsciindex(x)` // one numeric argument +/// +/// returns a one element numeric wave +static Function/WAVE SF_OperationSelectSCIIndex(variable jsonId, string jsonPath, string graph) + + variable value + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTSCIINDEX, 1, maxArgs = 1) + + value = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_SELECTSCIINDEX, 0) + Make/FREE/D output = {value} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTSCIINDEX, discardOpStack = 1, dataType = SF_DATATYPE_SELECTSCIINDEX) +End + +/// `selracindex(x)` // one numeric argument +/// +/// returns a one element numeric wave +static Function/WAVE SF_OperationSelectRACIndex(variable jsonId, string jsonPath, string graph) + + variable value + + SFH_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + + SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_SELECTRACINDEX, 1, maxArgs = 1) + + value = SFH_GetArgumentAsNumeric(jsonId, jsonPath, graph, SF_OP_SELECTRACINDEX, 0) + Make/FREE/D output = {value} + + return SFH_GetOutputForExecutorSingle(output, graph, SF_OP_SELECTRACINDEX, discardOpStack = 1, dataType = SF_DATATYPE_SELECTRACINDEX) +End + /// `seldev(device)` // device is a string with optional wildcards /// /// returns a one element text wave @@ -4941,12 +4990,14 @@ static Function SF_InitSelectFilterUninitalized(STRUCT SF_SelectParameters &s) s.clampMode = NaN WAVE/Z/T s.stimsets = $"" WAVE/Z/WAVE s.ranges = $"" - s.sweepQC = NaN - s.setQC = NaN - s.expandSCI = NaN - s.expandRAC = NaN + s.sweepQC = NaN + s.setQC = NaN + s.expandSCI = NaN + s.expandRAC = NaN s.setCycleCount = NaN s.setSweepCount = NaN + s.sciIndex = NaN + s.racIndex = NaN End /// `select(selectFilterOp...)` @@ -4975,6 +5026,20 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string continue endif strswitch(type) + case SF_DATATYPE_SELECTSCIINDEX: + if(IsNaN(filter.sciIndex)) + filter.sciIndex = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTSCIINDEX + " argument.") + endif + break + case SF_DATATYPE_SELECTRACINDEX: + if(IsNaN(filter.racIndex)) + filter.racIndex = arg[0] + else + SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTRACINDEX + " argument.") + endif + break case SF_DATATYPE_SELECTSETCYCLECOUNT: if(IsNaN(filter.setCycleCount)) filter.setCycleCount = arg[0] @@ -5097,6 +5162,14 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string WAVE/Z selectData = SF_GetSelectData(graph, filter) if(WaveExists(selectData)) + if(!IsNaN(filter.racIndex)) + WAVE racSelectData = SF_GetSelectDataWithRACorSCIIndex(graph, selectData, filter.racIndex, SELECTDATA_MODE_RAC) + WAVE selectData = racSelectData + endif + if(!IsNaN(filter.sciIndex)) + WAVE sciSelectData = SF_GetSelectDataWithRACorSCIIndex(graph, selectData, filter.sciIndex, SELECTDATA_MODE_SCI) + WAVE selectData = sciSelectData + endif // SCI is a subset of RAC, thus if RAC and SCI is enabled then it is sufficient to extend through RAC if(filter.expandRAC) WAVE selectWithRACFilledUp = SF_GetSelectDataWithSCIorRAC(graph, selectData, filter, SELECTDATA_MODE_RAC) @@ -5157,6 +5230,8 @@ static Function [STRUCT SF_SelectParameters filterDup] SF_DuplicateSelectFilter( filterDup.expandRAC = filter.expandRAC filterDup.setCycleCount = filter.setCycleCount filterDup.setSweepCount = filter.setSweepCount + filterDup.racIndex = filter.racIndex + filterDup.sciIndex = filter.sciIndex return [filterDup] End @@ -5215,6 +5290,175 @@ threadsafe static Function SF_GetSweepMapIndexFromIds(WAVE/T sweepMapIds, string return V_row End +static Function/WAVE SF_GetSelectDataWithRACorSCIIndex(string graph, WAVE selectData, variable index, variable mode) + + variable i, numSelected, mapIndex, outIndex, headstage + variable sweepNo, channelNumber, channelType + variable isSweepBrowser = BSP_IsSweepBrowser(graph) + + if(IsSweepBrowser) + WAVE/T sweepMap = SB_GetSweepMap(graph) + endif + + numSelected = DimSize(selectData, ROWS) + // get CycleIds per select + Make/FREE/D/N=(numSelected) cycleIds + FastOp cycleIds = (NaN) + if(mode == SELECTDATA_MODE_SCI) + Make/FREE/D/N=(numSelected) headStages + FastOp headStages = (NaN) + endif + + for(i = 0; i < numSelected; i += 1) + sweepNo = selectData[i][%SWEEP] + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, selectData[i][%SWEEPMAPINDEX]) + ASSERT(WaveExists(numericalValues), "Could not resolve numerical LNB") + if(mode == SELECTDATA_MODE_RAC) + cycleIds[i] = GetLastSettingIndep(numericalValues, sweepNo, RA_ACQ_CYCLE_ID_KEY, DATA_ACQUISITION_MODE, defValue = NaN) + elseif(mode == SELECTDATA_MODE_SCI) + channelNumber = selectData[i][%CHANNELNUMBER] + channelType = selectData[i][%CHANNELTYPE] + [WAVE settings, headstage] = GetLastSettingChannel(numericalValues, $"", sweepNo, STIMSET_ACQ_CYCLE_ID_KEY, channelNumber, channelType, DATA_ACQUISITION_MODE) + if(WaveExists(settings)) + cycleIds[i] = settings[headstage] + headStages[i] = headstage + endif + endif + endfor + + // remove selections with no cycleId + for(i = 0; i < numSelected; i += 1) + if(!IsNaN(cycleIds[i])) + selectData[outIndex][] = selectData[i][q] + outIndex += 1 + endif + endfor + Redimension/N=(outIndex, -1) selectData + if(!outIndex) + return selectData + endif + WAVE cycleIdsZapped = ZapNaNs(cycleIds) + if(mode == SELECTDATA_MODE_SCI) + WAVE headStagesZapped = ZapNaNs(headStages) + endif + + switch(mode) + case SELECTDATA_MODE_RAC: + return SF_GetSelectDataWithRACIndex(selectData, cycleIdsZapped, sweepMap, index) + case SELECTDATA_MODE_SCI: + return SF_GetSelectDataWithSCIIndex(selectData, cycleIdsZapped, headStagesZapped, sweepMap, index) + default: + ASSERT(0, "Unknown mode") + endswitch +End + +static Function/WAVE SF_GetSelectDataWithRACIndex(WAVE selectData, WAVE cycleIds, WAVE/Z/T sweepMap, variable index) + + variable i, outIndex, numSelected, currIndex + + numSelected = DimSize(selectData, ROWS) + + // Sort + Make/FREE/T/N=(numSelected) expNames, sortKeySweep, sortKeyChannelType, sortKeyChannelNumber + if(WaveExists(sweepMap)) + expNames[] = sweepMap[selectData[p][%SWEEPMAPINDEX]][%FileName] + else + expNames[] = DB_EXPNAME_DUMMY + endif + sortKeySweep[] = num2str(selectData[p][%SWEEP], "%06d") + sortKeyChannelType[] = num2str(selectData[p][%CHANNELTYPE], "%02d") + sortKeyChannelNumber[] = num2str(selectData[p][%CHANNELNUMBER], "%02d") + SortColumns keyWaves={expNames, sortKeySweep, sortKeyChannelType, sortKeyChannelNumber}, sortWaves={selectData, expNames, cycleIds} + + // filter by index + for(i = 0; i < numSelected; i += 1) + if(i > 0) + if(CmpStr(expNames[i], expNames[i - 1], 2)) + currIndex = 0 + elseif(cycleIds[i] != cycleIds[i - 1]) + currIndex += 1 + endif + endif + + if(currIndex < index) + continue + endif + if(currIndex > index) + break + endif + + selectData[outIndex][] = selectData[i][q] + outIndex += 1 + endfor + Redimension/N=(outIndex, -1) selectData + + return selectData +End + +static Function/WAVE SF_GetSelectDataWithSCIIndex(WAVE selectData, WAVE cycleIds, WAVE headstages, WAVE/Z/T sweepMap, variable index) + + variable i, headstage, outIndex, numSelected, currIndex, hsIndex, hsIndexPrev + + numSelected = DimSize(selectData, ROWS) + + // Sort + Make/FREE/T/N=(numSelected) expNames, sortKeySweep, sortKeyChannelType, sortKeyChannelNumber + if(WaveExists(sweepMap)) + expNames[] = sweepMap[selectData[p][%SWEEPMAPINDEX]][%FileName] + else + expNames[] = DB_EXPNAME_DUMMY + endif + sortKeySweep[] = num2str(selectData[p][%SWEEP], "%06d") + sortKeyChannelType[] = num2str(selectData[p][%CHANNELTYPE], "%02d") + sortKeyChannelNumber[] = num2str(selectData[p][%CHANNELNUMBER], "%02d") + SortColumns keyWaves={expNames, sortKeySweep, sortKeyChannelType, sortKeyChannelNumber}, sortWaves={selectData, expNames, cycleIds, headstages} + + Duplicate/FREE selectData, selectDataTgt + + WAVE uniqueHS = GetUniqueEntries(headstages) + for(headstage : uniqueHS) + currIndex = 0 + hsIndex = NaN + hsIndexPrev = NaN + for(i = 0; i < numSelected; i += 1) + if(headstage != headstages[i]) + continue + endif + + if(IsNaN(hsIndexPrev)) + hsIndexPrev = i + elseif(IsNaN(hsIndex)) + hsIndex = i + else + hsIndexPrev = hsIndex + hsIndex = i + endif + + if(!IsNaN(hsIndex)) + if(CmpStr(expNames[hsIndex], expNames[hsIndexPrev], 2)) + currIndex = 0 + elseif(cycleIds[hsIndex] != cycleIds[hsIndexPrev]) + currIndex += 1 + endif + endif + + if(currIndex < index) + continue + endif + if(currIndex > index) + break + endif + + selectDataTgt[outIndex][] = selectData[i][q] + outIndex += 1 + endfor + endfor + + Redimension/N=(outIndex, -1) selectDataTgt + + return selectDataTgt +End + static Function/WAVE SF_GetAdditionalSweepsWithSameSCIorRAC(WAVE numericalValues, variable mode, variable sweepNo, variable channelType, variable channelNumber) variable headstage From 14335724b0b5737d361eb9e1c6b15d7d21d08260 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 17 Sep 2024 14:55:04 +0200 Subject: [PATCH 24/38] SF: Unify select returning a null wave for selection for no results For some cases select was returning a zero sized wave for no results. This was unified to be a null wave and the select argument parsing was adapted to distinguish between regular filters from sel* operations and select arguments --- Packages/MIES/MIES_SweepFormula.ipf | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 5ea98c4b04..052cb1afc2 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -5022,8 +5022,10 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string SFH_ASSERT(DimSize(input, ROWS) >= 1, "Expected at least one dataset") type = JWN_GetStringFromWaveNote(input, SF_META_DATATYPE) WAVE/Z arg = input[0] - if(!WaveExists(arg)) - continue + if(CmpStr(SF_DATATYPE_SELECTCOMP, type)) + // all regular select filters return data from a typed wave from their respective operation, that as sanity check must have valid data + // except data from select, where arg is a selection result that can also be a null wave + ASSERT(WaveExists(arg), "Expected argument with content") endif strswitch(type) case SF_DATATYPE_SELECTSCIINDEX: @@ -5141,7 +5143,7 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string case SF_DATATYPE_SELECTCOMP: selectArgPresent = 1 if(!WaveExists(filter.selects)) - WAVE filter.selects = arg + WAVE/Z filter.selects = arg else WAVE/Z filter.selects = SF_GetSetIntersectionSelect(filter.selects, arg) endif @@ -5163,12 +5165,12 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string WAVE/Z selectData = SF_GetSelectData(graph, filter) if(WaveExists(selectData)) if(!IsNaN(filter.racIndex)) - WAVE racSelectData = SF_GetSelectDataWithRACorSCIIndex(graph, selectData, filter.racIndex, SELECTDATA_MODE_RAC) - WAVE selectData = racSelectData + WAVE/Z racSelectData = SF_GetSelectDataWithRACorSCIIndex(graph, selectData, filter.racIndex, SELECTDATA_MODE_RAC) + WAVE/Z selectData = racSelectData endif if(!IsNaN(filter.sciIndex)) - WAVE sciSelectData = SF_GetSelectDataWithRACorSCIIndex(graph, selectData, filter.sciIndex, SELECTDATA_MODE_SCI) - WAVE selectData = sciSelectData + WAVE/Z sciSelectData = SF_GetSelectDataWithRACorSCIIndex(graph, selectData, filter.sciIndex, SELECTDATA_MODE_SCI) + WAVE/Z selectData = sciSelectData endif // SCI is a subset of RAC, thus if RAC and SCI is enabled then it is sufficient to extend through RAC if(filter.expandRAC) @@ -5333,10 +5335,10 @@ static Function/WAVE SF_GetSelectDataWithRACorSCIIndex(string graph, WAVE select outIndex += 1 endif endfor - Redimension/N=(outIndex, -1) selectData if(!outIndex) - return selectData + return $"" endif + Redimension/N=(outIndex, -1) selectData WAVE cycleIdsZapped = ZapNaNs(cycleIds) if(mode == SELECTDATA_MODE_SCI) WAVE headStagesZapped = ZapNaNs(headStages) @@ -5390,6 +5392,9 @@ static Function/WAVE SF_GetSelectDataWithRACIndex(WAVE selectData, WAVE cycleIds selectData[outIndex][] = selectData[i][q] outIndex += 1 endfor + if(!outIndex) + return $"" + endif Redimension/N=(outIndex, -1) selectData return selectData @@ -5453,7 +5458,9 @@ static Function/WAVE SF_GetSelectDataWithSCIIndex(WAVE selectData, WAVE cycleIds outIndex += 1 endfor endfor - + if(!outIndex) + return $"" + endif Redimension/N=(outIndex, -1) selectDataTgt return selectDataTgt From 840b1387f995c04267cc2baf249214a596a44a82 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 17 Sep 2024 18:18:58 +0200 Subject: [PATCH 25/38] SF: Handle empty result from selsweeps() in select() properly - selsweeps can return a null wave that was used as default value for the select filter. This clash was resolved by adding a seperate flag to indicate if select got any sweep numbers as argument. The checks for the default were changed to check against the new flag instead. The flag approach also keeps the check against specifying selsweeps multiple time intact. --- Packages/MIES/MIES_Structures.ipf | 1 + Packages/MIES/MIES_SweepFormula.ipf | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index fa7b938e0a..2ec04be7b7 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -652,6 +652,7 @@ Structure SF_SelectParameters WAVE selects WAVE channels WAVE sweeps + variable sweepsSet // flag that indicates if sweep input was set string vis variable clampMode WAVE/T stimsets diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 052cb1afc2..60c7ed14ce 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -4986,6 +4986,7 @@ static Function SF_InitSelectFilterUninitalized(STRUCT SF_SelectParameters &s) WAVE/Z s.selects = $"" WAVE/Z s.channels = $"" WAVE/Z s.sweeps = $"" + s.sweepsSet = 0 s.vis = "" s.clampMode = NaN WAVE/Z/T s.stimsets = $"" @@ -5022,9 +5023,10 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string SFH_ASSERT(DimSize(input, ROWS) >= 1, "Expected at least one dataset") type = JWN_GetStringFromWaveNote(input, SF_META_DATATYPE) WAVE/Z arg = input[0] - if(CmpStr(SF_DATATYPE_SELECTCOMP, type)) + if(!(!CmpStr(SF_DATATYPE_SELECTCOMP, type) || !CmpStr(SF_DATATYPE_SWEEPNO, type))) // all regular select filters return data from a typed wave from their respective operation, that as sanity check must have valid data // except data from select, where arg is a selection result that can also be a null wave + // and data from selsweeps ASSERT(WaveExists(arg), "Expected argument with content") endif strswitch(type) @@ -5113,8 +5115,9 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string endif break case SF_DATATYPE_SWEEPNO: - if(!WaveExists(filter.sweeps)) - WAVE filter.sweeps = arg + if(!filter.sweepsSet) + WAVE/Z filter.sweeps = arg + filter.sweepsSet = 1 else SFH_ASSERT(0, "select allows only a single " + SF_OP_SELECTSWEEPS + " argument.") endif @@ -5616,7 +5619,7 @@ static Function SF_SetSelectionFilterDefaults(string graph, STRUCT SF_SelectPara if(!WaveExists(filter.channels)) WAVE filter.channels = SF_ExecuteFormula("selchannels()", graph, singleResult = 1, checkExist = 1, useVariables = 0) endif - if(!WaveExists(filter.sweeps)) + if(!filter.sweepsSet) WAVE/Z filter.sweeps = SF_ExecuteFormula("selsweeps()", graph, singleResult = 1, useVariables = 0) endif if(IsEmpty(filter.vis)) From 43bf105f90235913d54bb95455d58dba4fdf7a99 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 22:11:07 +0200 Subject: [PATCH 26/38] SF: Add Tests, direct use some select filters, some with select - for seldev, selexp, selsetcyclecount, selsetsweepcount, selsciindex, selracindex, selexpandsci, selexpandrac --- .../Basic/UTF_SweepFormula_Operations.ipf | 222 +++++++++++++++++- Packages/tests/Basic/UTF_SweepFormula_PSX.ipf | 30 +-- Packages/tests/UTF_DataGenerators.ipf | 56 ++++- Packages/tests/UTF_HelperFunctions.ipf | 65 ++++- 4 files changed, 334 insertions(+), 39 deletions(-) diff --git a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf index 39608bd5f9..80f8c687b3 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf @@ -1335,6 +1335,113 @@ static Function TestOperationSelstimset() endtry End +// IUTF_TD_GENERATOR DataGenerators#SF_TestOperationSelSingleText +static Function TestOperationSelSingleText([string str]) + + string win, device, formula + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + formula = str + "(AKAelectricRG28)" + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE/T ref = {"AKAelectricRG28"} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + formula = str + "()" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + formula = str + "(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + formula = str + "(dev1, dev2)" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +// IUTF_TD_GENERATOR DataGenerators#SF_TestOperationSelNoArg +static Function TestOperationSelNoArg([string str]) + + string win, device, formula + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + formula = str + "()" + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE ref = {1} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + formula = str + "(123)" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + formula = str + "(exp1, exp2)" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + +// IUTF_TD_GENERATOR DataGenerators#SF_TestOperationSelSingleNumber +static Function TestOperationSelSingleNumber([string str]) + + string win, device, formula + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + win = CreateFakeSweepData(win, device, sweepNo = 0) + + formula = "selsetcyclecount(123)" + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + WAVE/T array = wref[0] + Make/FREE ref = {123} + CHECK_EQUAL_WAVES(array, ref, mode = WAVE_DATA | DIMENSION_SIZES) + + formula = str + "()" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + formula = str + "(text)" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry + formula = str + "(1, 2)" + try + WAVE/WAVE wref = SF_ExecuteFormula(formula, win, useVariables = 0) + FAIL() + catch + PASS() + endtry +End + static Function TestOperationSelIVSCCSweepQC() string win, device, str @@ -2286,18 +2393,20 @@ static Function TestOperationFit() endtry End -static Function TestOperationSelectCompareWithFullRange(string win, string formula, WAVE dataRef) +static Function TestOperationSelectCompareWithFullRange(string win, string formula, WAVE/Z dataRef) WAVE/WAVE comp = SF_ExecuteFormula(formula, win, useVariables = 0) CHECK_WAVE(comp, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(comp, ROWS), 2) - WAVE dataSel = comp[0] + WAVE/Z dataSel = comp[0] WAVE/WAVE rngSet = comp[1] CHECK_WAVE(rngSet, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(rngSet, ROWS), 1) WAVE dataRng = rngSet[0] - CHECK_EQUAL_WAVES(dataRef, dataSel, mode = WAVE_DATA | DIMENSION_SIZES) - WAVE rngRef = SFH_GetFullRange() + WAVE rngRef = SFH_GetFullRange() + if(WaveExists(dataRef) || WaveExists(dataSel)) + CHECK_EQUAL_WAVES(dataRef, dataSel, mode = WAVE_DATA | DIMENSION_SIZES) + endif CHECK_EQUAL_WAVES(rngRef, dataRng, mode = WAVE_DATA | DIMENSION_SIZES) End @@ -2557,10 +2666,10 @@ static Function TestOperationSelect() WAVE/Z dataSel = comp[0] CHECK_WAVE(dataSel, NULL_WAVE) - Make/FREE/N=(1, 4) dataRef - dataRef[][0] = {sweepNo} + Make/FREE/N=(2, 4) dataRef + dataRef[][0] = {sweepNo, sweepNo + 1} // both sweeps have the same SCI dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) - dataRef[][2] = {6} + dataRef[][2] = {6, 6} dataRef[][3] = NaN str = "select(selchannels(AD6),selivsccsetqc(passed))" TestOperationSelectCompareWithFullRange(win, str, dataRef) @@ -2575,7 +2684,11 @@ static Function TestOperationSelect() WAVE/Z dataSel = comp[0] CHECK_WAVE(dataSel, NULL_WAVE) + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {sweepNo + 1} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6} + dataRef[][3] = NaN str = "select(selchannels(AD6),selivsccsweepqc(passed))" TestOperationSelectCompareWithFullRange(win, str, dataRef) str = "select(select(selchannels(AD6)),selivsccsweepqc(passed))" @@ -2589,6 +2702,101 @@ static Function TestOperationSelect() WAVE/Z dataSel = comp[0] CHECK_WAVE(dataSel, NULL_WAVE) + Make/FREE/N=(2, 4) dataRef + dataRef[][0] = {sweepNo, sweepNo + 1} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 6} + dataRef[][3] = NaN + str = "select(selchannels(AD6),selexp(" + GetExperimentName() + "))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selchannels(AD6),seldev(ITC16_Dev_0))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + Make/FREE/N=(2, 4) dataRef + dataRef[][0] = {sweepNo} + dataRef[0][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[1][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 2} + dataRef[][3] = NaN + str = "select(selsetcyclecount(711))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selsetsweepcount(635))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + // sweep 0 and sweep 1 are set to the same SCI and the same RAC + Make/FREE/N=(2, 4) dataRef + dataRef[][0] = {sweepNo, sweepNo + 1} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 6} + dataRef[][3] = NaN + str = "select(selsweeps(0), selchannels(AD6), selexpandrac())" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + str = "select(selsweeps(0), selchannels(AD6), selexpandsci())" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + // sweepNr ChannelNumber RAC + // 0 6 49 + // 0 7 49 + // 1 6 49 + // 1 7 49 + // 2 6 50 + // 2 7 50 + + Make/FREE/N=(4, 4) dataRef + dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 6, 7} + dataRef[][3] = NaN + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selracindex(0))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + Make/FREE/N=(2, 4) dataRef + dataRef[][0] = {sweepNo + 2, sweepNo + 2} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7} + dataRef[][3] = NaN + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selracindex(1))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selracindex(999))" + TestOperationSelectCompareWithFullRange(win, str, $"") + + str = "select(selsweeps(3), selvis(all), selchannels(AD), selracindex(0))" + TestOperationSelectCompareWithFullRange(win, str, $"") + + // sweepNr ChannelNumber Headstage SCI + // 0 6 0 43 + // 0 7 1 45 + // 1 6 0 43 + // 1 7 1 46 <- index 1 for HS1 + // 2 6 0 44 <- index 1 for HS0 + // 2 7 1 46 <- index 1 for HS1 (same SCI 46) + + Make/FREE/N=(3, 4) dataRef + dataRef[][0] = {sweepNo + 2, sweepNo + 1, sweepNo + 2} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 7} + dataRef[][3] = NaN + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selsciindex(1))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + Make/FREE/N=(3, 4) dataRef + dataRef[][0] = {sweepNo, sweepNo + 1, sweepNo} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 6, 7} + dataRef[][3] = NaN + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selsciindex(0))" + TestOperationSelectCompareWithFullRange(win, str, dataRef) + + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selsciindex(999))" + TestOperationSelectCompareWithFullRange(win, str, $"") + + str = "select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selsciindex(0),select(selsweeps([0, 1, 2]), selvis(all), selchannels(AD), selsciindex(999)))" + TestOperationSelectCompareWithFullRange(win, str, $"") + + str = "select(selsweeps(3), selvis(all), selchannels(AD), selsciindex(0))" + TestOperationSelectCompareWithFullRange(win, str, $"") + Make/FREE/N=(1, 4) dataRef dataRef[][0] = {sweepNo} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) diff --git a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf index 2b78cdeb1a..1f3059bc4e 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf @@ -1052,7 +1052,7 @@ static Function TestOperationPSXKernel() win = CreateFakeSweepData(win, device, sweepNo = 0, sweepGen = FakeSweepDataGeneratorPSXKernel) win = CreateFakeSweepData(win, device, sweepNo = 2, sweepGen = FakeSweepDataGeneratorPSXKernel) - str = "psxKernel([50, 150], select(channels(AD6), [0, 2], all), 1, 15, -5)" + str = "psxKernel(select(selRange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all)), 1, 15, -5)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_WAVE(dataWref, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 6) @@ -1088,7 +1088,7 @@ static Function TestOperationPSXKernel() CheckDimensionScaleHelper(dataWref[5], 50, 0.2) // no data from select statement - str = "psxKernel([50, 150], select(channels(AD15), [0]), 1, 15, -5)" + str = "psxKernel(select(selrange([50, 150]), selchannels(AD15), selsweeps(0)), 1, 15, -5)" try WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() @@ -1097,7 +1097,7 @@ static Function TestOperationPSXKernel() endtry // no data from this sweep statement - str = "psxKernel(ABCD, select(channels(AD6), [0, 2], all), 1, 15, -5)" + str = "psxKernel(select(selRange(ABCD), selchannels(AD6), selsweeps([0, 2]), selvis(all)), 1, 15, -5)" try WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() @@ -1156,7 +1156,7 @@ static Function TestOperationPSX([STRUCT IUTF_mData &m]) win = CreateFakeSweepData(win, device, sweepNo = 0, sweepGen = FakeSweepDataGeneratorPSX) win = CreateFakeSweepData(win, device, sweepNo = 2, sweepGen = FakeSweepDataGeneratorPSX) - str = "psx(myID, psxKernel([50, 150], select(channels(AD6), [0, 2], all), 1, 15, " + num2str(kernelAmp) + "), 2.5, 100, 0)" + str = "psx(myID, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all)), 1, 15, " + num2str(kernelAmp) + "), 2.5, 100, 0)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_WAVE(dataWref, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(dataWref, ROWS), 2 * 7) @@ -1187,13 +1187,13 @@ static Function TestOperationPSX([STRUCT IUTF_mData &m]) JSON_Release(jsonID) // check that plain psx does not error out - str = "psx(id, psxKernel([50, 150], select(channels(AD6), [0, 2], all)))" + str = "psx(id, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all))))" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_NO_RTE() CHECK_WAVE(dataWref, WAVE_WAVE) // complains without events found - str = "psx(myID, psxKernel([50, 150], select(channels(AD6), [0, 2], all), 5000, 15, -5), 25, 100, 0)" + str = "psx(myID, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all)), 5000, 15, -5), 25, 100, 0)" try WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) FAIL() @@ -1218,7 +1218,7 @@ static Function TestOperationPSXTooLargeDecayTau() win = CreateFakeSweepData(win, device, sweepNo = 0, sweepGen = FakeSweepDataGeneratorPSX) win = CreateFakeSweepData(win, device, sweepNo = 2, sweepGen = FakeSweepDataGeneratorPSX) - str = "psx(myID, psxKernel([50, 150], select(channels(AD6), [0], all), 1, 15, -5), 10, 100, 0)" + str = "psx(myID, psxKernel(select(selrange([50, 150]),selchannels(AD6), selsweeps([0]), selvis(all)), 1, 15, -5), 10, 100, 0)" WAVE/WAVE dataWref = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_WAVE(dataWref, WAVE_WAVE) @@ -1324,7 +1324,7 @@ static Function MouseSelectionPSX() browser = MIES_DB#DB_LockToDevice(browser, device) - code = "psx(myId, psxKernel([50, 150], select(channels(AD6), [0, 2], all)), 5, 100, 0)" + code = "psx(myId, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all))), 5, 100, 0)" // combo0 is the current one @@ -1677,9 +1677,11 @@ static Function/S GetTestCode(string postProc, [string eventState, string prop]) prop = "xpos" endif - code = "psx(myId, psxKernel([50, 150], select(channels(AD6), [0, 2], all)), 1.5, 100, 0)" + code = "psx(myId, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all))), 5, 100, 0)" + + code = "psx(myId, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all))), 1.5, 100, 0)" code += "\r and \r" - code += "psxStats(myId, [50, 150], select(channels(AD6), [0, 2], all), " + prop + ", " + eventState + ", " + postProc + ")" + code += "psxStats(myId, select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all)), " + prop + ", " + eventState + ", " + postProc + ")" return code End @@ -1694,11 +1696,11 @@ static Function/WAVE GetCodeVariations() code = "" // one sweep per operation separated with `with` - code = "psx(myId, psxKernel([50, 150], select(channels(AD6), [0], all)), 10, 100, 0)" + code = "psx(myId, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0]), selvis(all))), 10, 100, 0)" code += "\r with \r" - code += "psx(myId, psxKernel([50, 150], select(channels(AD6), [2], all)), 2.5, 100, 0)" + code += "psx(myId, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([2]), selvis(all))), 2.5, 100, 0)" code += "\r and \r" - code += "psxStats(myId, [50, 150], select(channels(AD6), [0, 2], all), xpos, all, nothing)" + code += "psxStats(myId, select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all)), xpos, all, nothing)" wv[1] = code code = "" @@ -2967,7 +2969,7 @@ static Function TestOperationPrep() win = CreateFakeSweepData(win, device, sweepNo = 0, sweepGen = FakeSweepDataGeneratorPSX) - psxCode = "psx(myID, psxKernel([50, 150], select(channels(AD6), [0, 2], all), 1, 15, -5), 2.5, 100, 0)" + psxCode = "psx(myID, psxKernel(select(selrange([50, 150]), selchannels(AD6), selsweeps([0, 2]), selvis(all)), 1, 15, -5), 2.5, 100, 0)" sprintf code, "psxPrep(%s)", psxCode WAVE/WAVE dataWref = SF_ExecuteFormula(code, win, useVariables = 0) diff --git a/Packages/tests/UTF_DataGenerators.ipf b/Packages/tests/UTF_DataGenerators.ipf index ec47df38ad..211f336ce8 100644 --- a/Packages/tests/UTF_DataGenerators.ipf +++ b/Packages/tests/UTF_DataGenerators.ipf @@ -893,15 +893,53 @@ End static Function/WAVE SF_TestOperationSelectFails() - Make/FREE/T wt = {"select(1)", \ - "select(selrange(),selrange())", \ - "select(selchannels(),selchannels())", \ - "select(selsweeps(),selsweeps())", \ - "select(selcm(all),selcm(all))", \ - "select(selvis(),selvis())", \ - "select(selstimset(),selstimset())", \ - "select(selivsccsetqc(failed),selivsccsetqc(failed))", \ - "select(selivsccsweepqc(failed),selivsccsweepqc(failed))"} + Make/FREE/T wt = {"select(1)", \ + "select(selrange(),selrange())", \ + "select(selchannels(),selchannels())", \ + "select(selsweeps(),selsweeps())", \ + "select(selcm(all),selcm(all))", \ + "select(selvis(),selvis())", \ + "select(selstimset(),selstimset())", \ + "select(selivsccsetqc(failed),selivsccsetqc(failed))", \ + "select(selivsccsweepqc(failed),selivsccsweepqc(failed))", \ + "select(selexp(" + GetExperimentName() + "not_exist))", \ + "select(seldev(unknown_device))", \ + "select(seldev(device),seldev(device))", \ + "select(selexp(exp),selexp(exp))", \ + "select(selsciindex(0),selsciindex(0))", \ + "select(selracindex(0),selracindex(0))", \ + "select(selsetcyclecount(0),selsetcyclecount(0))", \ + "select(selsetsweepcount(0),selsetsweepcount(0))", \ + "select(selexpandrac(0),selexpandrac(0))", \ + "select(selexpandsci(0),selexpandsci(0))"} + + Duplicate/FREE/T wt, labels + labels[] = CleanUpName(wt[p], 0) + SetDimensionLabels(wt, TextWaveToList(labels, ";"), ROWS) + + return wt +End + +static Function/WAVE SF_TestOperationSelSingleNumber() + + Make/FREE/T wt = {"selsetcyclecount", "selsetsweepcount", "selsciindex", "selracindex"} + SetDimensionLabels(wt, TextWaveToList(wt, ";"), ROWS) + + return wt +End + +static Function/WAVE SF_TestOperationSelSingleText() + + Make/FREE/T wt = {"seldev", "selexp"} + SetDimensionLabels(wt, TextWaveToList(wt, ";"), ROWS) + + return wt +End + +static Function/WAVE SF_TestOperationSelNoArg() + + Make/FREE/T wt = {"selexpandrac", "selexpandsci"} + SetDimensionLabels(wt, TextWaveToList(wt, ";"), ROWS) return wt End diff --git a/Packages/tests/UTF_HelperFunctions.ipf b/Packages/tests/UTF_HelperFunctions.ipf index f036e6b8de..5e51ab2a00 100644 --- a/Packages/tests/UTF_HelperFunctions.ipf +++ b/Packages/tests/UTF_HelperFunctions.ipf @@ -346,22 +346,46 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) keys[2][0][0] = "-" ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) - // Set a acquisition cycle ID + // Set a acquisition cycle ID SCI values[] = NaN - values[0][0][0] = sweepNo - values[0][0][1] = sweepNo + values[0][0][0] = 43 + values[0][0][1] = 45 keys[0][0][0] = STIMSET_ACQ_CYCLE_ID_KEY keys[2][0][0] = "1" ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) - keys[2][0][0] = "-" - // Set setQC passed + // Set a repeated acquisition cycle RAC + values[] = NaN + values[0][0][INDEP_HEADSTAGE] = 49 + keys[0][0][0] = RA_ACQ_CYCLE_ID_KEY + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + + // Set set QC passed values[] = NaN values[0][0][INDEP_HEADSTAGE] = 1 keys[0][0][0] = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_SET_PASS, query = 1) + keys[2][0][0] = "-" ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, UNKNOWN_MODE) + // Set Set Cycle Count + values[] = NaN + values[0][0][0] = 711 + values[0][0][1] = 117 + keys[0][0][0] = "Set Cycle Count" + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + + // Set Set Sweep Count + values[] = NaN + values[0][0][0] = 635 + values[0][0][1] = 251 + keys[0][0][0] = "Set Sweep Count" + keys[2][0][0] = "0.1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // textual entries + keys[2][0][0] = "-" // Stimset name HS0/HS1 valuesTxt[] = "" @@ -444,20 +468,28 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) // Set a acquisition cycle ID values[] = NaN - values[0][0][0] = sweepNo - values[0][0][1] = sweepNo + values[0][0][0] = 43 + values[0][0][1] = 46 keys[0][0][0] = STIMSET_ACQ_CYCLE_ID_KEY keys[2][0][0] = "1" ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) - keys[2][0][0] = "-" - // Set setQC passed + // Set a repeated acquisition cycle RAC + values[] = NaN + values[0][0][INDEP_HEADSTAGE] = 49 + keys[0][0][0] = RA_ACQ_CYCLE_ID_KEY + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + + // Set sweepQC passed values[] = NaN values[0][0][INDEP_HEADSTAGE] = 1 keys[0][0][0] = CreateAnaFuncLBNKey(PSQ_CHIRP, PSQ_FMT_LBN_SWEEP_PASS, query = 1) + keys[2][0][0] = "-" ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, UNKNOWN_MODE) // textual entries + keys[2][0][0] = "-" // Set some analysis function valuesTxt[] = "" @@ -517,6 +549,14 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) keys[0][0][0] = CLAMPMODE_ENTRY_KEY ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // Set a acquisition cycle ID SCI + values[] = NaN + values[0][0][0] = 44 + values[0][0][1] = 46 + keys[0][0][0] = STIMSET_ACQ_CYCLE_ID_KEY + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // indep headstage values[] = NaN values[0][0][INDEP_HEADSTAGE] = 252627 @@ -528,6 +568,13 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) keys[0][0][0] = keyTxt ED_AddEntriesToLabnotebook(valuesTxt, keys, sweepNo, device, DATA_ACQUISITION_MODE) + // Set a repeated acquisition cycle RAC + values[] = NaN + values[0][0][INDEP_HEADSTAGE] = 50 + keys[0][0][0] = RA_ACQ_CYCLE_ID_KEY + keys[2][0][0] = "1" + ED_AddEntriesToLabnotebook(values, keys, sweepNo, device, DATA_ACQUISITION_MODE) + sweepNo = 3 // HS0 with DA1 and AD0 From dd399119a28c0746907d6b63e41980155a24df7f Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 24 Jun 2024 15:49:56 +0200 Subject: [PATCH 27/38] SB: Add testing assertion in SB_GetSweepIndexFromMap for multi device usage In principle the sweep browser supports multiple devices/experiments. However not all sweep browser functions imlement a suitable API for that. SB_GetSweepIndexFromMap is such function. When running tests it asserts now if sweep numbers appear more than once, which can happen when multiple experiments are loaded and this function is called. --- .../MIES_AnalysisBrowser_SweepBrowser.ipf | 30 ++++++++++++++----- .../HistoricData/UTF_HistoricDashboard.ipf | 6 ++++ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf index c6bc7f3626..d8409fd7b8 100644 --- a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf +++ b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf @@ -33,7 +33,27 @@ Function SB_TranslateSBMapIndexToABMapIndex(string win, variable sbIndex) return V_row End -/// @brief Return the sweep data folder +static Function SB_GetSweepIndexFromMap(WAVE/T sweepMap, variable sweepNo) + + variable cIndex + + cIndex = FindDimLabel(sweepMap, COLS, "Sweep") + FindValue/RMD=[][cIndex]/TEXT=(num2istr(sweepNo))/TXOP=4 sweepMap + +#ifdef AUTOMATED_TESTING + variable dummy = V_row + if(V_row >= 0) + FindValue/RMD=[dummy + 1,][cIndex]/TEXT=(num2istr(sweepNo))/TXOP=4 sweepMap + ASSERT(V_row == -1, "Found results for multiple experiments") + endif + V_row = dummy +#endif + + return V_row == -1 ? NaN : V_row +End + +/// @brief Return the sweep data folder for either a given index or sweepNo +/// If a sweepNo is given then the result for the first sweep found with that number is returned Function/DF SB_GetSweepDataFolder(WAVE/T sweepMap, [variable sweepNo, variable index]) string dataFolder, device @@ -42,14 +62,10 @@ Function/DF SB_GetSweepDataFolder(WAVE/T sweepMap, [variable sweepNo, variable i if(!ParamIsDefault(index) && ParamIsDefault(sweepNo)) ASSERT(index >= 0 && index < DimSize(sweepMap, ROWS), "Invalid index") elseif(ParamIsDefault(index) && !ParamIsDefault(sweepNo)) - cIndex = FindDimLabel(sweepMap, COLS, "Sweep") - FindValue/RMD=[][cIndex]/TEXT=(num2istr(sweepNo))/TXOP=4 sweepMap - - if(V_row == -1) + index = SB_GetSweepIndexFromMap(sweepMap, sweepNo) + if(IsNaN(index)) return $"" endif - - index = V_row endif dataFolder = sweepMap[index][%DataFolder] diff --git a/Packages/tests/HistoricData/UTF_HistoricDashboard.ipf b/Packages/tests/HistoricData/UTF_HistoricDashboard.ipf index b69079f228..b5ec1e2098 100644 --- a/Packages/tests/HistoricData/UTF_HistoricDashboard.ipf +++ b/Packages/tests/HistoricData/UTF_HistoricDashboard.ipf @@ -45,6 +45,12 @@ Function TestDashboardWithHistoricData([string str]) string abWin, sweepBrowsers, file, bsPanel, sbWin + if(!CmpStr(str, "NWB-Export-bug-two-devices.pxp")) + // @todo SweepBrowser is not fully multi-device compliant, see issue + // https://github.com/AllenInstitute/MIES/issues/2151 + SKIP_TESTCASE() + endif + file = "input:" + str [abWin, sweepBrowsers] = OpenAnalysisBrowser({file}, loadSweeps = 1) From 259c113a0fc0a82e74928b7801793b98f4c86b01 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 20 Jun 2024 16:55:31 +0200 Subject: [PATCH 28/38] SF: Updating docu - selsetcyclecount, selsetsweepcount, selsciindex, selracindex - selexpandsci, selexpandrac --- Packages/doc/SweepFormula.rst | 696 +++++++++++++++++++++++++++------- 1 file changed, 553 insertions(+), 143 deletions(-) diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index 8132f0df7e..1af97f6b24 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -344,9 +344,9 @@ The returned data type is `SF_DATATYPE_AVG`. avg([1, 2, 3]) == [2] - avg(data(ST, select(channels(AD), sweeps(), all)), over) + avg(data(select(selrange(ST), selchannels(AD), selvis(all))), over) - avg(data(ST, select()), in) + avg(data(select(selrange(ST))), in) root mean square """""""""""""""" @@ -521,7 +521,7 @@ For this case the operation is applied on each input data wave independently and // The sweeps in this example were sampled at 250 kHz. // For each data point in the sweep the time is returned. - time(data([0, 1000], channels(AD), sweeps())) == [0, 0.004, 0.008, 0.012, ...] + time(data(select(selrange([0, 1000]), selchannels(AD), selsweeps()))) == [0, 0.004, 0.008, 0.012, ...] setscale """""""" @@ -558,10 +558,11 @@ For this case the operation is applied on each input data wave independently and xvalues(setscale([0, 1, 2, 3, 4], x, 0, 0.2, firkin)) == [0, 0.2, 0.4, 0.6, 0.8] -channels -"""""""" +selchannels +""""""""""" -`channels([str name]+)` converts named channels from strings to numbers. +The operation `selchannels` allows to select channels. +`selchannels([str name]+)` converts named channels from strings to numbers. The function accepts an arbitrary amount of channel names like `AD`, `DA` or `TTL` with a combination of numbers `AD1` or channel numbers alone like `2`. @@ -571,26 +572,346 @@ The operation returns a numeric array of `[[channelType+], [channelNumber+]]` th row dimension the number of the input strings. When called without argument all channel types / channel numbers are set by setting the returned value for type and number to `NaN`. +The result of `selchannels` has a data type attributed. -`channels` is intended to be used with the `select()` operation. +`selchannels` is intended to be used with the `select()` operation. .. code-block:: bash - channels([AD0,AD1, DA0, DA1]) == [[0, 0, 1, 1], [0, 1, 0, 1]] + selchannels([AD0, AD1, DA0, DA1]) == [[0, 0, 1, 1], [0, 1, 0, 1]] // Internally NaN is evaluated as joker for all channel types and all channel numbers - channels() == [[NaN], [NaN]] + selchannels() == [[NaN], [NaN]] + +selsweeps +""""""""" + +The operation `selsweeps` allows to select sweeps by their number and returns an 1d-array with the sweep numbers. +The operation accepts numbers, arrays and ranges as arguments. Any number of arguments can be specified. +In case no argument is given, then all sweeps are returned or if there are no sweeps a null wave is returned. +Each unique sweep number is returned only once. +The result of `selsweeps` has a data type attributed. + +`selsweeps` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # For this example two sweeps were acquired + selsweeps() == [0, 1] + + selsweeps(0) == 0 + + selsweeps([1, 0]) == [1, 0] + + selsweeps(0...2) == [0, 1] + + # For this example 30 sweeps were acquired + selsweeps(10, [20, 24], 26...30) == [10, 20, 24, 26, 27, 28, 29] + + # Each unique sweep number is returned only once + selsweeps(0, 0, 1) == [0, 1] + +selrange +"""""""" + +The operation `selrange` allows to specify a time interval either by epoch name or numbers in ms. +It takes zero or one argument, an epoch name/wildcard or an array with two +numeric values. The numeric values specify the start and end of a range. +The operation returns a dataset with a range specification array. +In case no argument is given, then a dataset with a full-range specification is returned. +The result of `selrange` has a data type attributed. + +`selrange` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # returns a full-range + selrange() + + # refers to epoch E1 + selrange(E1) + + # all stimset epochs + selrange("E*") + + # refers to 30 ms to 100 ms + selrange([30, 100]) + + # refers to the range set by cursor A and B + selrange(cursors(A,B)) + +selvis +"""""" + +The operation `selvis` allows to specify if selected data is taken from all sweeps or from the displayed sweeps only. +It takes zero or one argument that can be either `all` or `displayed`. +In case no argument is given, then the operation defaults to `displayed`. +The operation returns a text wave with a single element. +The result of `selvis` has a data type attributed. + +`selvis` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # refers to displayed + selvis() + + # refers to all + selvis(all) + + # refers displayed + selvis(displayed) -sweeps +selcm """""" -The operation `sweeps` return an 1d-array with the sweep numbers of all sweeps. The operation takes no arguments. -If there are no sweeps a null wave is returned. +The operation `selcm` allows to specify how select filters data for clamp mode. +It takes between zero and any number of arguments. +Allowed arguments are `none`, `ic`, `vc`, `izero`, `all`. If no argument is given then `selcm` defaults to `all`. +The operation returns a a numeric value with a clamp code that is a logical ORed result of the given clamp modes. +The result of `selcm` has a data type attributed. + +`selcm` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweep data in any clamp mode + selcm() + + # sweep data acquired in current clamp and voltage clamp mode + selcm(ic, vc) + + # sweep data acquired with no clamp mode (unassociated channels) + selcm(none) + +selstimset +"""""""""" + +The operation `selstimset` allows to specify how select filters data regarding the stimset wave name. +It takes between zero and any number of arguments. +Allowed arguments are strings that can contain wildcards. +If no argument is given then `selstimset` defaults to `*`. +The operation returns a a text wave with the stimset wave name wildcard patterns. +The result of `selstimset` has a data type attributed. + +`selstimset` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweep data with any stimset wave name + selstimset() + + # sweep data with all stimset wave names that start with pinky and all that end with brain + selstimset("pinky*", "*brain") + +selexp +"""""" + +The operation `selexp` allows to specify how select filters data regarding the experiment name. +It takes exactly one string argument. The string that can contain wildcards. +The operation returns a a text wave with the experiment name wildcard pattern. +The result of `selexp` has a data type attributed. + +`selexp` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweep data from a specific experiment + selexp("MyFirstExperiment.pxp") + + # sweep data from a specific experiment + selexp("MySecondExp*") + +seldev +"""""" + +The operation `seldev` allows to specify how select filters data regarding the DAC device name. +It takes exactly one string argument. The string that can contain wildcards. +The operation returns a a text wave with the device name wildcard pattern. +The result of `seldev` has a data type attributed. + +`seldev` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweep data from a specific device + seldev("ITC18*") + + # sweep data from a specific device + seldev("Dev*") + +selsetcyclecount +"""""""""""""""" + +When the operation `selsetcyclecount` is used with select it includes all sweeps with the specified set cycle count. +The operation takes exactly one numerical argument. +The operation returns a a numeric wave with a single element that has the value of the given argument. +The result of `selsetcyclecount` has a data type attributed. + +`selsetcyclecount` is intended to be used with the `select()` operation. .. code-block:: bash - // For this example two sweeps were acquired - sweeps() == [0, 1] + # sweeps that have a set cycle count of 5 + selsetcyclecount(5) + +selsetsweepcount +"""""""""""""""" + +When the operation `selsetsweepcount` is used with select it includes all selection with the specified set sweep count. +The operation takes exactly one numerical argument. +The operation returns a a numeric wave with a single element that has the value of the given argument. +The result of `selsetsweepcount` has a data type attributed. + +`selsetsweepcount` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweeps that have a set sweep count of 2 + selsetsweepcount(2) + +selsciindex +""""""""""" + +When the operation `selsciindex` is used with select it includes all selections that have the n-th unique stimset cycle id. +The specific order of the stimset cycle ids before this operation is applied depends on the other select filters applied in the `select` operation. +Selections with no stimset cycle id are discarded and not indexed. +The stimset cycle id depends on the headstage and thus, on channel type and channel number of the specific sweep. +The selection results are determined per headstage. Thus, if the other select filters result +in selections include multiple headstages then the n-th unique stimset cycle id is selected for each headstage seperately. +Selections are sorted by the following priority list (higher to lower): experiment name, sweep number, channel type, channel number. +The operation takes exactly one numerical argument. +The operation returns a a numeric wave with a single element that has the value of the given argument. +The result of `selsciindex` has a data type attributed. + +`selsciindex` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # Looks at all sweep starting from sweep 3 with channel AD0. Selects all sweeps that have starting from sweep 3 the third unique stimset cycle id. + select(selsweeps([3, 1000]), selchannels(AD0), selsciindex(3)) + + # example, where the first three columns are the result of a selection, the last two columns are added for illustration + # a possible selection with a two headstage setup could be select(selvis(all), selsweeps([0, 3]), selchannels(AD)) + Sweep ChannelType ChannelNumer Headstage StimsetCycleId + 0 AD 6 0 43 + 0 AD 7 1 45 + 1 AD 6 0 43 + 1 AD 7 1 46 + 2 AD 6 0 44 + 2 AD 7 1 46 + + # if based on this selection selsciindex(0) is applied: + # select(selvis(all), selsweeps([0, 3]), selchannels(AD), selsciindex(0)) + # The result is + Sweep ChannelType ChannelNumer Headstage StimsetCycleId + 0 AD 6 0 43 + 1 AD 6 0 43 + 0 AD 7 1 45 + # for headstage 0 the 0-th SCI index is 43 + # for headstage 1 the 0-th SCI index is 45 + + # if based on this selection selsciindex(1) is applied: + # select(selvis(all), selsweeps([0, 3]), selchannels(AD), selsciindex(1)) + # The result is + Sweep ChannelType ChannelNumer Headstage StimsetCycleId + 2 AD 6 0 44 + 1 AD 7 1 46 + 2 AD 7 1 46 + # for headstage 0 the 1-th SCI index is 44 + # for headstage 1 the 1-th SCI index is 46 + +selracindex +""""""""""" + +When the operation `selracindex` is used with select it includes all selections that have the n-th unique repeated acquisition cycle id. +The specific order of the repeated acquisition cycle ids before this operation is applied depends on the other select filters applied in the `select` operation. +Selections with no repeated acquisition cycle ids are discarded and not indexed. +The selections prior to the application of `selracindex` are sorted by the following priority list (higher to lower): experiment name, sweep number, channel type, channel number. +The operation takes exactly one numerical argument. +The operation returns a a numeric wave with a single element that has the value of the given argument. +The result of `selracindex` has a data type attributed. + +`selracindex` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # Looks at all sweep starting from sweep 3 with channel AD0. Selects all sweeps that have starting from sweep 3 the third unique repeated acquisition cycle id. + select(selsweeps([3, 1000]), selchannels(AD0), selracindex(3)) + +selexpandsci +"""""""""""" + +When the operation `selexpandsci` is used with select then select operates in a two-step regime. +First the common select filters e.g. by sweep number, stimset, etc. are applied. +Then for each of these selections the selections with the same stimset cycle id are also added. +For example when a single sweep/channel is selected all other sweeps from the same stimset cycle id can be collected for the resulting selections. +Intersections with additional selections from another select are applied afterwards. +The operation takes no argument. +The result of `selexpandsci` has a data type attributed. + +`selexpandsci` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # Looks at all AD channels from sweep 1 and selects all sweeps with the same stimset cycle id. + select(selsweeps(1), selchannels(AD), selexpandsci()) + +selexpandrac +"""""""""""" + +When the operation `selexpandrac` is used with select then select operates in a two-step regime. +First the common select filters e.g. by sweep number, stimset, etc. are applied. +Then for each of these selections the selections with the same repeated acquisition cycle are also added. +So for example when a single sweep/channel is selected all other sweeps from the same repeated acquisition cycle can be collected for the resulting selections. +Intersections with additional selections from another select are applied afterwards. +The operation takes no argument. +The result of `selexpandrac` has a data type attributed. + +`selexpandrac` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # Looks at all AD channels from sweep 1 and selects all sweeps from the same repeated acquisition cycle. + select(selsweeps(1), selchannels(AD), selexpandrac()) + +selivsccsweepqc +""""""""""""""" + +The operation `selivsccsweepqc` allows to specify how select filters data +regarding the sweep quality check from the IVSCC analysis functions. +It takes between one argument that can be either `failed` or `passed`. +The operation returns a a text wave with the argument value as string. +The result of `selivsccsweepqc` has a data type attributed. + +`selivsccsweepqc` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweep data where the analysis function passed the sweepqc check + selivsccsweepqc(passed) + + # sweep data where the analysis function failed the sweepqc check + selivsccsweepqc(failed) + +selivsccsetqc +""""""""""""" + +The operation `selivsccsetqc` allows to specify how select filters data +regarding the set quality check from the IVSCC analysis functions. +It takes between one argument that can be either `failed` or `passed`. +The operation returns a a text wave with the argument value as string. +The result of `selivsccsetqc` has a data type attributed. + +`selivsccsetqc` is intended to be used with the `select()` operation. + +.. code-block:: bash + + # sweep data where the analysis function passed the setqc check + selivsccsetqc(passed) + + # sweep data where the analysis function failed the setqc check + selivsccsetqc(failed) cursors """"""" @@ -653,64 +974,73 @@ data """" The `data` operation is the core of the `SweepFormula` library. It returns sweep data from *MIES*. -It can be called in two variants: .. code-block:: bash - data(array range[, array selectData]) + data(selectData) + + data([selectData, selectData, ...]) - data(string epochShortName[, array selectData]) +The operation `data` retrieves selected sweep data. -The range can be either supplied explicitly using `[100, 300]` which would -select `100 ms` to `300 ms` or by using `cursors` that also returns a range -specification. Use `[0, inf]` to extract the full x-range. A numerical range -applies to all sweeps. +It takes one argument that is either a `select` operation or an array of `select` operations. -Instead of a numerical range also the short names of epochs can be given including wildcard expressions. Then the range -is determined from the epoch information of each sweep/channel/epoch data iterates over. If a specified epoch does not exist in a sweep -that sweep data is not included in the sweep data returned. If the same epoch is resolved multiple times from wildcard expressions or -multiple epoch names then it is included only once per sweep. +If an array of `select` operations is specified then over each selected data is iterated +independently. Thus, one data expression can retrieve sweep data from +multiple `select` operations. -A given range as numbers or epoch extracts a subrange of data points from the sweep. The start and end time is converted to +A given `selrange` in `select` as numbers or epoch extracts a subrange of data points from the sweep. The start and end time is converted to closest integer indices, where the included points range from `startIndex` to `endIndex - 1`. This matches the general handling of epochs in MIES, where the data point at the end time of an epoch is not part of the epoch range. -selectData is retrieved through the `select` operation. It selects for which sweeps and channels sweep data is returned. -`select` also allows to choose currently displayed sweeps or all existing sweeps as data source. -When the optional selectData argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. - For each selected sweep/channel combination data returns a data wave. The data wave contains the sweep data for the specified range/epoch. If no sweep/channel was selected then the number of returned data waves is zero. Each data wave gets meta data about the originating sweep/channel added. The returned data type is `SF_DATATYPE_SWEEP`. .. code-block:: bash - // Shows the AD channels of all displayed sweeps with the range 0 - 1s - data([0, 1000], select(channels(AD), sweeps())) + # AD channels of all displayed sweeps with the range 0 - 1s + data(select(selrange([0, 1000]), selchannels(AD))) + + # epoch "E1" range of the AD channels of all displayed sweeps + data(select(selrange(E1), selchannels(AD))) - // Shows epoch "E1" range of the AD channels of all displayed sweeps - data("E1", select(channels(AD), sweeps())) + # epoch "E1" range with the start offsetted by 10ms of the AD channels of all displayed sweeps + sel = select(selchannels(AD)) + rng = epochs("E1", $sel) + [10, 0] + data(select(selrange($rng), $sel)) - // Shows epoch "E1" range with the start offsetted by 10ms of the AD channels of all displayed sweeps - sel = select(channels(AD), sweeps()) - data(epochs("E1", $sel) + [10, 0], $sel) + # sweep data from all epochs starting with "E" of the AD channels of all displayed sweeps + data(select(selrange("E*"), selchannels(AD))) - // Shows sweep data from all epochs starting with "E" of the AD channels of all displayed sweeps - data("E*", select(channels(AD), sweeps())) + # sweep data from all epochs starting with "E" and "TP" of the AD channels of all displayed sweeps + sel1 = select(selchannels(AD)) + sel2 = select(selrange("E*"), $sel1) + sel3 = select(selrange("TP*"), $sel1) + data([$sel2, $sel3]) - // Shows sweep data from all epochs starting with "E" and "TP" of the AD channels of all displayed sweeps - data(["E*","TP*"], select(channels(AD), sweeps())) + # sweep data from all epochs that do not start with "E" and that do start with "TP" of the AD channels of all displayed sweeps + sel1 = select(selchannels(AD)) + sel2 = select(selrange("!E*"), $sel1) + sel3 = select(selrange("TP*"), $sel1) + data([$sel2, $sel3]) - // Shows sweep data from all epochs that do not start with "E" and that do start with "TP" of the AD channels of all displayed sweeps - data(["!E*","TP*"], select(channels(AD), sweeps())) + # extract the first pulse from TTL1 as epoch and extract the AD data in that range + sel1 = select(selchannels(TTL1)) + ep = epochs(E0_PT_P0, $sel1) + data(select(selrange($ep), selchannels(AD))) - // No double resolve of the same epoch name: Shows sweep data from epoch "TP" of the AD channels of all displayed sweeps. - data(["TP","TP"], select(channels(AD), sweeps())) + # extract the first pulse from TTL1 as epoch with a start and end offset, then extract the AD data in that range + sel1 = select(selchannels(TTL1)) + ep = epochs(E0_PT_P0, $sel1) + [50, 100] + data(select(selrange($ep), selchannels(AD))) - // extract the first pulse from TTL1 as epoch and extract the AD data - // in that range - ep = epochs(E0_PT_P0, select(channels(TTL1),sweeps())) - data($ep,select(channels(AD),sweeps())) + # filter by channel, clamp mode and stimset wave name, then based on that selection create one with epoch E0 and another with epoch E1 range + # retrieve data for these two selections + sel1 = select(selchannels(AD), selcm(ic), selstimsets("AD_phase0*")) + sel2 = select(selrange(E0), $sel1) + sel3 = select(selrange(E1), $sel1) + data([$sel2, $sel3]]) labnotebook """"""""""" @@ -720,8 +1050,9 @@ labnotebook labnotebook(string key[, array selectData [, string entrySourceType]]) The labnotebook function returns the (case insensitive) `key` entry from the -labnotebook for the selected channel and sweep combination(s). The optional -`entrySourceType` can be one of the constants `DataAcqModes` for data +labnotebook for the selected channel and sweep combination(s). For selectData either a single `select` or an array of `select`s can be specified. +If an array is specified then over each selection is iterated independently. +The optional `entrySourceType` can be one of the constants `DataAcqModes` for data acquisition modes as defined in `../MIES/MIES_Constants.ipf`. If the `entrySourceType` is omitted it defaults to `DATA_ACQUISITION_MODE`. @@ -739,15 +1070,18 @@ The suggested y-axis label is the labnotebook key. max( data( - cursors(A, B) - channels(AD), - sweeps() + select( + selrange( + cursors(A, B) + ), + selchannels(AD) + ) ) ) vs labnotebook( "set cycle count", - select(channels(AD), sweeps()), + select(selchannels(AD)), DATA_ACQUISITION_MODE ) @@ -848,9 +1182,9 @@ to the user to select a reasonable range or epoch. apfrequency([10, 20, 30], 1, 15) - apfrequency(data(ST, select(channels(AD), sweeps(), all)), 3, 100, freq, normoversweepsavg, count) + apfrequency(data(select(selrange(ST), selchannels(AD), selvis(all))), 3, 100, freq, normoversweepsavg, count) - apfrequency(data(ST, select(channels(AD), sweeps(), all)), 3, 42, time, norminsweepsmin, time) + apfrequency(data(select(selrange(ST), selchannels(AD), selvis(all))), 3, 42, time, norminsweepsmin, time) powerspectrum """"""""""""" @@ -901,11 +1235,11 @@ If input data type is `SF_DATATYPE_SWEEP` from the data operation and non-averag .. code-block:: bash - powerspectrum(data(ST,select(channels(AD),sweeps(),all))) + powerspectrum(data(select(selrange(ST), selchannels(AD), selvis(all)))) - powerspectrum(data(ST,select(channels(AD),sweeps(),all)),dB,avg,0,100,HFT248D) // db units, averaging on, display up to 100 Hz, use HFT248D window + powerspectrum(data(select(selrange(ST), selchannels(AD), selvis(all))),dB,avg,0,100,HFT248D) // db units, averaging on, display up to 100 Hz, use HFT248D window - powerspectrum(data(ST,select(channels(AD),sweeps(),all)),dB,avg,60) // db units, averaging on, determine power ratio at 60 Hz + powerspectrum(data(select(selrange(ST), selchannels(AD), selvis(all))),dB,avg,60) // db units, averaging on, determine power ratio at 60 Hz .. _sf_op_psx: @@ -965,16 +1299,12 @@ the subset of data to work on. .. code-block:: bash - psxkernel([array range, array selectData, riseTau, decayTau, amp]) + psxkernel([array selectData, riseTau, decayTau, amp]) -The function accepts zero to five arguments. - -range - either an explicit array in milliseconds, `cursors` or a text array with one - or multiple epoch names, see also `data`, defaults to the full range. +The function accepts zero to four arguments. select - sweep and channels to operate on from the `select` operation + selections and range to operate on from the `select` operation riseTau Time constant for kernel, defaults to 1 @@ -989,7 +1319,7 @@ amp psxkernel([100, 200]) psxkernel([E0, E1]) # list of epoch names - psxkernel(ST, select(channels(AD10), [49, 50], all), 2, 13, 2) + psxkernel(select(selrange(ST), selchannels(AD10), selsweeps(49, 50), selvis(all)), 2, 13, 2) psxPrep """"""" @@ -1008,7 +1338,7 @@ numberOfSDs .. code-block:: bash - psxPrep(psx(psxKernel(E0, select()), 0.2, 400, 100, 12)) + psxPrep(psx(psxKernel(select(selrange(E0))), 0.2, 400, 100, 12)) psxRiseTime """"""""""" @@ -1075,20 +1405,16 @@ rejected -> triangle, undetermined -> square). .. code-block:: bash - psxstats(id, array range, array selectData, prop, state, [postproc]) + psxstats(id, array selectData, prop, state, [postproc]) -The function accepts five or six arguments. +The function accepts four or five arguments. id identifier string, must adhere to strict igor object names. Used for identifying the data to query, also from the results wave -range - either an explicit array in milliseconds, `cursors` or a text array with one - or multiple epoch names, see also `data` - select - sweep and channels to operate on from the `select` operation + selections and range to operate on from the `select` operation prop column of the `psx` event results waves to plot. @@ -1131,8 +1457,8 @@ postproc .. code-block:: bash - psxstats(myID, [100, 200], select(channels(AD10), [49, 50], all), amp, accept) - psxstats(otherID, [E0], select(channels(AD7), 40...60, all), xpos, every, log10) + psxstats(myID, select(selrange(100, 200), selchannels(AD10), selsweeps([49, 50]), selvis(all)), amp, accept) + psxstats(otherID, select(selrange(E0), selchannels(AD7), selsweeps(40...60), selvis(all)), xpos, every, log10) fit """ @@ -1162,11 +1488,11 @@ Example: sweeps = [5, 7, 8, 10] # grab the DA data from channel 0 and epoch E1 - selDA = select(channels(DA0), $sweeps) + selDA = select(selchannels(DA0), selsweeps($sweeps)) dDA = data("E1", $selDA) # E2 from AD channel 2 - selAD = select(channels(AD2), $sweeps) + selAD = select(selchannels(AD2), selsweeps($sweeps)) dAD = data("E2", $selAD) # calculate minimum for the data in each sweep, @@ -1219,48 +1545,129 @@ Utility Functions select """""" -The `select` operation allows to choose a selection of sweep data from a given list of sweeps and channels. -It is intended to be used with operations like `data`, `labnotebook`, `epochs` and `tp`. +The `select` operation allows to choose a selection of sweep data from given filter operations. +It is intended to be used with operations like `data`, `labnotebook`, `epochs`, `tp` and `select` itself. + +.. code-block:: bash + + select(filter, filter, ...) + +The function accepts any number of arguments from filter operations. + +Filter operations are `selchannels`, `selsweeps`, `selrange`, `selvis`, `selscm`, `selstimset`, `selivsccsetqc`, `selivsccsweepqc`, `selexp`, `seldev`, `selrac`, `selsci`, `selsetcyclecount`, `selsetsweepcount`, `selsciindex`, `selracindex`, `select`. + +Sweeps that fit all filter criteria are taken into the selection. Each filter operation except `select` may appear once as argument. +It is not required that the arguments have a specific order. + +If a specific filter is not part of the arguments and none of the arguments is a `select` then default values are used: +- `selchannels`: select all channels +- `selsweeps`: select all sweep numbers +- `selrange`: select full range +- `selvis`: select displayed sweeps +- `selscm`: select all clamp modes +- `selstimset`: select all stimset wave names +- `selivsccsetqc`: IVSCC SetQC is ignored +- `selivsccsweepqc`: IVSCC SweepQC is ignored +- `selexp`: experiment name is ignored +- `seldev`: device name is ignored +- `selsetcyclecount`: set cycle count is ignored +- `selsetsweepcount`: set sweep count is ignored +- `selsciindex`: stimset cycle id index is ignored +- `selracindex`: repeated acquisition is index is ignored +- `selexpandrac`: expansion by repeated acquisition cycle is disabled +- `selexpandsci`: expansion by stimset cycle id is disabled + +If a specific filter is not part of the arguments and there exists at least one arguments that is a `select` then these filters will be ignored: +- `selchannels`: select all channels +- `selsweeps`: select all sweep numbers +- `selrange`: select full range +- `selvis`: select all sweeps +- `selscm`: select all clamp modes +- `selstimset`: select all stimset wave names +- `selivsccsetqc`: IVSCC Set QC is ignored +- `selivsccsweepqc`: IVSCC Sweep QC is ignored +- `selexp`: experiment name is ignored +- `seldev`: device name is ignored +- `selsetcyclecount`: set cycle count is ignored +- `selsetsweepcount`: set sweep count is ignored +- `selsciindex`: stimset cycle id index is ignored +- `selracindex`: repeated acquisition is index is ignored +- `selexpandrac`: expansion by repeated acquisition cycle is disabled +- `selexpandsci`: expansion by stimset cycle id is disabled + +If `select` arguments appear multiple times then the resulting selection is an intersection of all sweep/channel combinations that were selected +from all these `select` arguments. +i.e. if one select argument has Sweep 0 AD0, Sweep 1 AD0 selected and a second select argument has Sweep 1 AD0 selected then +only Sweep 1 AD0 remains selected because it appears in all selections. + +The range specified through `selrange` is always taken from the topmost `select`. + +If an experiment is specified with a wildcard pattern through `selexp` then there must be only a single matching experiment. The same applies for `seldev`. Only when the source is from a SweepBrowser with different loaded experiments then using `selexp` is senseful as e.g. for a DataBrowser the experiment is always the current experiment. + +The filter criteria of the select filters are orthogonal (independent of each other) except for `selsciindex`, `selracindex`, `selexpandrac` and `selexpandsci`. +Internally first the orthogonal select filters are applied. Then based on the resulting selections `selsciindex`, `selracindex` is applied, then `selexpandsci`, `selexpandsci`. +Intersections with additional selections from select type arguments are executed afterwards. +This implies that created selections can not be further filtered once created (see example). + +When `selexpandrac` is used then the selected sweep numbers from `selsweeps` are extended. For each sweep selected by +`selsweeps` sweeps numbers of the same repeated acquisition cycle are added. For the new sweep numbers selections are gathered with a modified copy of the initial selection filter: +- `selvis` is changed to `all` +- `selexp` is set to the experiment of the sweep number that was extended +- `seldev` is set to the device of the sweep number that was extended + +The resulting selections are gathered for each additional sweep number. Finally all selections are reduced to be unique only. + +When `selexpandsci` is used then first the selection is retrieved for `selsci` disabled. Then for each selection +for the sweep number / channel number / channel type combination the sweep numbers with the same stimset cycle id are determined. For these sweeps selections with the same channel number / channel type are added. +Finally all selections are reduced to be unique only. + +The expansion through `selexpandsci` and `selexpandsci` operates on the current select filter. + +The output is composite with two datasets of different type. +The first dataset contains a N x 4 array where the columns are sweep number, channel type, GUI channel number and row index of the sweepMap. The sweepMap only exists if the window is a SweepBrowser, for DataBrowser the values are set NaN in that column. +The second dataset contains a dataset with range specification. + +The output of the N x 4 array is sorted. The order is sweep -> channel type -> channel number. +e.g. for two sweeps numbered 0, 1 that have channels AD0, AD1, DA6, DA7 from a DataBrowser: +`{{0, 0, 0, 0, 1, 1, 1, 1}, {0, 0, 1, 1, 0, 0, 1, 1}, {0, 1, 6, 7, 0, 1, 6, 7}, {NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN}}`. + +If the mode for `selvis` is `displayed` and no traces are displayed then a null wave is returned. +If there are no matching sweeps found a null wave is returned. .. code-block:: bash - select([array channels, array sweeps[, string mode[, string clampMode]]]) - -The function accepts none, two, three or four arguments. - -channels - array with channel specification from `channels` operation. When channels is not specified, it defaults to `channels()`. The input channel numbers are treated as GUI channel numbers. - -sweeps - array with sweep number, typically from `sweeps` operation. When sweeps is not specified, it defaults to `sweeps()`. - -mode - string specifying which sweeps are selected. Possible strings are `displayed` and `all` that refer to the currently displayed sweeps or all acquired sweeps. When mode is not specified it defaults to `displayed`. - -clampMode - string specifying which clamp mode is selected. Possible strings are `all`, `vc`, `ic` and `izero`. When clampMode is not specified it defaults to `all`. The clampMode selection is only applied for associated AD/DA channels. - -To retrieve a correct array of channels the `channels` function must be used. + select() + select(selvis(all)) + select(selchannels(AD4, DA), selsweeps(1, 5, 10...16), selvis(all)) + select(selchannels(AD2, DA5, AD0, DA6), selvis(all), selcm(ic, vc)) + select(selcm(none)) + select(selstimset("DA_*", "*cell"), selivsccsetqc(passed)) -If a given channel/sweep combination does not exist it is omitted in the output. +.. code-block:: bash -The output is a N x 3 array where the columns are sweep number, channel type, GUI channel number. + sel1 = select(selchannels(AD0), selcm(ic), selivsccsetqc(passed)) + sel2 = select(selchannels(AD0), selcm(ic), selivsccsweepqc(failed)) + sel3 = select($sel1, $sel2, selrange(cursors(A,B))) + sel4 = select(selsweeps(10...1000), selrange([30, 500]), $sel1, $sel2) + sel5 = select(selsweeps(1, 2, 3), selrange(E1), selstimset("DA_*"), $sel1, $sel2) -The output is sorted. The order is sweep -> channel type -> channel number. -e.g. for two sweeps numbered 0, 1 that have channels AD0, AD1, DA6, DA7: -`{{0, 0, 0, 0, 1, 1, 1, 1}, {0, 0, 1, 1, 0, 0, 1, 1}, {0, 1, 6, 7, 0, 1, 6, 7}}`. +.. code-block:: bash -If the mode is `displayed` and no traces are displayed then a null wave is returned. -If sweeps or channels is a null wave then select returns a null wave. -If there are no matching sweeps found a null wave is returned. + # For sel2 the SCI expansion applies to sweep 0, AD0. The selection result + # of the expansion is then intersected with sweep 1 AD0 that was selected + # through sel1. + sel1 = select(selchannels(AD0), selsweeps(1), selvis(all)) + sel2 = select(selchannels(AD0), selsweeps(0), selvis(all), selexpandsci(), $sel1) .. code-block:: bash - select() - select(channels(AD), sweeps(), all) - select(channels(AD4, DA), [1, 5]], all) - select(channels(AD2, DA5, AD0, DA6), [0, 1, 3, 7], all) - select(channels(AD2, DA5, AD0, DA6), [0, 1, 3, 7], all, ic) + # Note that the sel2 expression does not do a post-filtering of sel1 + # instead selracindex(5) is applied to the selections resulting from + # the default filter setting for select for the case there is a select type argument present + # Then these selections are intersected from sel1 + # Logically the intersection of the resulting selection works only for the orthogonal filter properties as a kind-of post-filter + sel1 = select(selsciindex(3)) + sel2 = select(selracindex(5), $sel1) range """"" @@ -1304,16 +1711,15 @@ name the name(s) of the epoch. The names can contain wildcard `*` and `!`. selectData - the second argument is a selection of sweeps and channels where the epoch information is retrieved from. It must be specified through the `select` operation. When the optional second argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. + the second argument is a selection of sweeps and channels where the epoch information is retrieved from. It must be specified through the `select` operation. Any range specification that is part of the `select` result is ignored. + When the optional second argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. type sets what information is returned. Valid types are: `range`, `name` or `treelevel`. If type is not specified then `range` is used as default. The operation returns for each selected sweep times matching epoch a data wave. The sweep meta data is transferred to the output data waves. If there was nothing selected the number of returned data waves is zero. -If the selection contains channels that do not have epoch information stored, e.g. `AD`, these selections are skipped in the evaluation. -For example if `select()` is used for the selectData argument then all channels are selected, but only for `DA` channels epoch information is stored in the labnotebook. -Thus, there are data waves only returned for the `DA` channels. +If the selection contains channels that do not have epoch information stored these are skipped in the evaluation. For associated AD channels the epoch information is retrieved from the associated DA channel. If a selection has epoch information stored in the labnotebook and the specified epoch does not exist it is skipped and thus, not included in the output waves. The output data varies depending on the requested type. Multiple epochs for one @@ -1337,16 +1743,16 @@ The default suggested x-axis values for the formula plotter are sweep numbers. T epochs(ST) // two sweeps acquired with two headstages set with PulseTrain_100Hz_DA_0 and PulseTrain_150Hz_DA_0 from _2017_09_01_192934-compressed.nwb - epochs(ST, select(channels(AD), sweeps()), range) == [[20, 1376.01], [20, 1342.67], [20, 1376.01], [20, 1342.67]] + epochs(ST, select(selchannels(AD)), range) == [[20, 1376.01], [20, 1342.67], [20, 1376.01], [20, 1342.67]] // get stimset range from epochs starting with TP_ and epochs starting with E from all displayed sweeps and channels - epochs(["TP_*", "E*"], select(channels(AD), sweeps())) + epochs(["TP_*", "E*"], select(selchannels(AD))) // get stimset range from specified epochs from all displayed sweeps and channels - epochs(["TP_B?", "E?_*"], select(channels(AD), sweeps())) + epochs(["TP_B?", "E?_*"], select(selchannels(AD))) // get ranges for epochs TP_B0/TP_B1 where the start is offsetted by 5/10 ms - epochs(["TP_B0", "TP_B1"], select(channels(AD), sweeps())) + [[5, 10], [0, 0]] + epochs(["TP_B0", "TP_B1"], select(selchannels(AD))) + [[5, 10], [0, 0]] tp "" @@ -1371,8 +1777,9 @@ The following tp analysis modes are supported: See specific subsections for more details. The second argument is a selection of sweeps and channels where the test pulse information is retrieved from. -It must be specified through the `select` operation. -When the optional second argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. +It can be either a single `select` or an array with `select`s. If an array of selects is specified then over each selection is iterated independently. +If the optional second argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. +Any range specification from the `select` is ignored when used with `tp`. The `tp` operation pre-filters the selected sweeps, only sweeps with channel type `AD` are used. The optional argument ``ignoreTPs`` allows to ignore some of the found test-pulses. The indices are zero-based and identify the @@ -1393,23 +1800,26 @@ If a selected sweep does not contain any test pulse then for that data wave a nu .. code-block:: bash - // Get steady state resistance from all displayed sweeps and channels + # Get steady state resistance from all displayed sweeps and channels tp(tpss()) - // Get steady state resistance from all displayed sweeps and AD channels - tp(tpss(), select(channels(AD), sweeps())) + # Get steady state resistance from all displayed sweeps and AD channels + tp(tpss(), select(selchannels(AD))) + + # Get base line level from all displayed sweeps and AD1 channel + tp(tpbase(), select(selchannels(AD1))) - // Get base line level from all displayed sweeps and DA1 channel - tp(tpbase(), select(channels(DA1), sweeps())) + # Get base line level from all displayed sweeps with AD1 channel and all sweeps with AD2 channel + tp(tpbase(), [select(selchannels(AD1)), select(selchannels(AD2), selvis(all))]) - // Get base line level from all displayed sweeps and channels ignoring test pulse 0 and 1 + # Get base line level from all displayed sweeps and channels ignoring test pulse 0 and 1 tp(tpbase(), select(), [0, 1]) - // Fit the test pulse from all displayed sweeps and channels exponentially and show the amplitude. + # Fit the test pulse from all displayed sweeps and channels exponentially and show the amplitude. tp(tpfit(exp, amp)) - // Fit the test pulse from all displayed sweeps and channels double-exponentially and show the smaller tau from the two exponentials. - // The fitting range is changed from the default maximum of 250 ms to 500 ms if the next epoch is sufficiently long. + # Fit the test pulse from all displayed sweeps and channels double-exponentially and show the smaller tau from the two exponentials. + # The fitting range is changed from the default maximum of 250 ms to 500 ms if the next epoch is sufficiently long. tp(tpfit(doubleexp, tausmall, 500)) tpbase @@ -1609,7 +2019,7 @@ data points where either an X or Y value for the X, Y value pair is missing. .. code-block:: bash - min(data(TP,select(channels(AD0), 4...11,all))) + min(data(select(selrange(TP), selchannels(AD0), selsweeps(4...11), selvis(all)))) vs 1...8 @@ -1669,9 +2079,9 @@ The variable names are treated case-insensitive. .. code-block:: bash c = cursors(A,B) - s = select(channels(AD), sweeps(), all) + s = select(selrange($c), selchannels(AD), selvis(all)) - data($c, $s) + data($s) The section containing the variable definition can contain empty lines. The first line that is not fulfilling the format for a variable definition is treated as the first line of the formula expression(s) section. Variable definitions can use variables that were defined in a preceding line. @@ -1679,8 +2089,8 @@ of the formula expression(s) section. Variable definitions can use variables tha .. code-block:: bash c = cursors(A,B) - s = select(channels(AD), sweeps(), all) - d = data($c, $s) + s = select(selrange($c), selchannels(AD), selvis(all)) + d = data($s) $d @@ -1694,8 +2104,8 @@ Limitations of the current variable definition concept: # This does NOT work c = cursors(A,B) - s = select(channels(AD), sweeps(), all) - p = $c, $s # p is resolved to a single numerical array + s = select(selrange($c), selchannels(AD), selvis(all)) + p = $s, $s # p is resolved to a single numerical array data($p) # the data operation sees a single argument @@ -1845,10 +2255,10 @@ It also updates the operation stack information. There are two cases where `SFH_ - The operation does not take an input reference wave - The operation returns data through `SF_GetOutputForExecutorSingle` that creates the reference wave. -For operation that do not take an input reference wave that is calculated to an output reference wave the approach is to update the operation stack +For operations that do not take an input reference wave that is calculated to an output reference wave the approach is to update the operation stack meta information directly through `JWN_SetStringInWaveNote(output, SF_META_OPSTACK, AddListItem(SF_OP_OPSHORT, ""))`. If `SF_GetOutputForExecutorSingle` is called then the optional parameter `opStack` should be set to the previous operation stack. For operations like -`sweeps()` there is no previous operation, thus the parameter would be `opStack=""`. +`selsweeps()` there is no previous operation, thus the parameter would be `opStack=""`. It should be noted that there is a difference for parsing a single first argument through `SF_GetArgument` or `SF_GetArgumentTop`. `SF_GetArgument` starts execution for argument 0 specifically at the `/0` JSON path location, whereas `SF_GetArgumentTop` starts execution at `/`. @@ -1894,7 +2304,7 @@ If the following conditions are met then a suggested X-values are set in the met butterworth( integrate( derivative( - data(TP,select(channels(AD0), 4...11,all)) + data(select(selrange(TP), selchannels(AD0), selsweeps(4...11), selvis(all))) ) ) ,4,100,4) @@ -1949,6 +2359,6 @@ need to be shown with a different marker or line style. It also adapts the legen .. code-block:: igorpro - apfrequency(data(ST, select(channels(AD), sweeps(), all)), 3, 100, freq, normoversweepsavg, count) + apfrequency(data(select(selrange(ST), selchannels(AD), selvis(all))), 3, 100, freq, normoversweepsavg, count) with - apfrequency(data(ST, select(channels(AD), sweeps(), all)), 3, 100, time, norminsweepsavg, count) + apfrequency(data(select(selrange(ST), selchannels(AD), selvis(all))), 3, 100, time, norminsweepsavg, count) From 304863d5997f195e77b86289f1652623dd7ba7c9 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 19 Sep 2024 14:38:46 +0200 Subject: [PATCH 29/38] SF: Update SF help notebook --- Packages/MIES/SweepFormulaHelp.ifn | Bin 322186 -> 364236 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Packages/MIES/SweepFormulaHelp.ifn b/Packages/MIES/SweepFormulaHelp.ifn index 5e8a3e3238cefccd2a95c171a774c2836eb763a9..d271660586fb1a06e16e9cc7ea4ec7b8b3d6bace 100644 GIT binary patch delta 37290 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 F{6F?Rc=rGR delta 15374 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 From c17a25dfda61b60e9e72a7f7efd587b2c6ee19ce Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 23 Sep 2024 16:15:48 +0200 Subject: [PATCH 30/38] Tests: Fix speed dependent fail in PS_PB6 test - sometimes PS_PB6 acquires sweep 2 as full sweep if the computer is slow. However the test checked if there are no results. If there was a full sweep acquired it tests now against existing results. --- .../UTF_PatchSeqPipetteInBath.ipf | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Packages/tests/HardwareAnalysisFunctions/UTF_PatchSeqPipetteInBath.ipf b/Packages/tests/HardwareAnalysisFunctions/UTF_PatchSeqPipetteInBath.ipf index 4a0c453f18..84d5c351ff 100644 --- a/Packages/tests/HardwareAnalysisFunctions/UTF_PatchSeqPipetteInBath.ipf +++ b/Packages/tests/HardwareAnalysisFunctions/UTF_PatchSeqPipetteInBath.ipf @@ -608,8 +608,12 @@ static Function PS_PB6_REENTRY([str]) CHECK_EQUAL_WAVES(entries[%resistance], resistanceRef, mode = WAVE_DATA) CHECK_EQUAL_WAVES(entries[%resistancePass], {1, 1, 1}, mode = WAVE_DATA) - CHECK_WAVE(entries[%resultsSweep], NULL_WAVE) - CHECK_WAVE(entries[%resultsResistance], NULL_WAVE) + if(WaveExists(entries[%resultsSweep])) + CHECK_WAVE(entries[%resultsSweep], FREE_WAVE) + CHECK_WAVE(entries[%resultsResistance], FREE_WAVE) + else + CHECK_WAVE(entries[%resultsResistance], NULL_WAVE) + endif CommonAnalysisFunctionChecks(str, sweepNo, entries[%setPass]) CheckPSQChunkTimes(str, {20, 520}) From a3c1e23db1f045420e353b6b7fe3369d5c810c2f Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 22:27:20 +0200 Subject: [PATCH 31/38] SF: Use correct LNBs SFH_GetStimsetName - LNB is handed down from SF_GetSelectData - For the displayed case the LNBs are gathered per trace with TUD data from CreateTiledChannelGraph --- Packages/MIES/MIES_SweepFormula.ipf | 33 ++++++++++++++----- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 6 +--- .../Basic/UTF_SweepFormula_Operations.ipf | 15 +++++---- Packages/tests/UTF_HelperFunctions.ipf | 5 +-- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 60c7ed14ce..429b766a3a 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2291,6 +2291,15 @@ static Function/WAVE SF_MakeSweepPropertiesDisplayed(variable numTraces) return sweepPropertiesDisplayed End +static Function/WAVE SF_MakeSweepLNBsDisplayed(variable numTraces) + + Make/FREE/WAVE/N=(numTraces, 2) wv + SetDimLabel COLS, 0, NUMERICAL, wv + SetDimLabel COLS, 1, TEXTUAL, wv + + return wv +End + /// @brief Use the labnotebook information to return the active channel numbers /// for a given set of sweeps /// @@ -2306,7 +2315,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & variable isSweepBrowser variable dimPosSweep, dimPosChannelNumber, dimPosChannelType, dimPosSweepMapIndex variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType, dimPosTClampMode, dimPosTExpName, dimPosTDevice, dimPosTSweepMapIndex - variable dimPosTNumericalValues + variable dimPosTNumericalValues, dimPosTTextualValues variable numTraces, fromDisplayed, clampCode, smIndexCounter, mapIndex, setCycleCount, setSweepCount, doStimsetMatching string msg, device, singleSweepDFStr, expName, dataFolder variable mapSize = 1 @@ -2344,8 +2353,9 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & WAVE sweeps = sweepsIntersect numSweeps = DimSize(sweeps, ROWS) - WAVE selectDisplayed = SFH_NewSelectDataWave(numTraces, 1) - WAVE sweepPropertiesDisplayed = SF_MakeSweepPropertiesDisplayed(numTraces) + WAVE selectDisplayed = SFH_NewSelectDataWave(numTraces, 1) + WAVE sweepPropertiesDisplayed = SF_MakeSweepPropertiesDisplayed(numTraces) + WAVE/WAVE sweepLNBsDisplayed = SF_MakeSweepLNBsDisplayed(numTraces) dimPosSweep = FindDimLabel(selectDisplayed, COLS, "SWEEP") dimPosChannelType = FindDimLabel(selectDisplayed, COLS, "CHANNELTYPE") dimPosChannelNumber = FindDimLabel(selectDisplayed, COLS, "CHANNELNUMBER") @@ -2358,6 +2368,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & dimPosTDevice = FindDimLabel(traces, COLS, "Device") dimPosTSweepMapIndex = FindDimLabel(traces, COLS, "SweepMapIndex") dimPosTNumericalValues = FindDimLabel(traces, COLS, "numericalValues") + dimPosTTextualValues = FindDimLabel(traces, COLS, "textualValues") for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] for(j = 0; j < numTraces; j += 1) @@ -2374,14 +2385,15 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & channelType = WhichListItem(traces[j][dimPosTChannelType], XOP_CHANNEL_NAMES) channelNumber = str2num(traces[j][dimPosTChannelNumber]) WAVE numericalValues = $traces[j][dimPosTNumericalValues] + WAVE textualValues = $traces[j][dimPosTTextualValues] if(!IsNaN(filter.setCycleCount)) - [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, "Set Cycle Count", channelNumber, channelType, DATA_ACQUISITION_MODE) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Set Cycle Count", channelNumber, channelType, DATA_ACQUISITION_MODE) setCycleCount = WaveExists(setting) ? setting[index] : NaN else setCycleCount = NaN endif if(!IsNaN(filter.setSweepCount)) - [WAVE setting, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, "Set Sweep Count", channelNumber, channelType, DATA_ACQUISITION_MODE) + [WAVE setting, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Set Sweep Count", channelNumber, channelType, DATA_ACQUISITION_MODE) setSweepCount = WaveExists(setting) ? setting[index] : NaN else setSweepCount = NaN @@ -2394,6 +2406,8 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & sweepPropertiesDisplayed[outIndex][SWEEPPROP_CLAMPMODE] = str2num(traces[j][dimPosTClampMode]) sweepPropertiesDisplayed[outIndex][SWEEPPROP_SETCYCLECOUNT] = setCycleCount sweepPropertiesDisplayed[outIndex][SWEEPPROP_SETSWEEPCOUNT] = setSweepCount + sweepLNBsDisplayed[outIndex][%NUMERICAL] = numericalValues + sweepLNBsDisplayed[outIndex][%TEXTUAL] = textualValues outIndex += 1 endif if(outIndex == numTraces) @@ -2406,6 +2420,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & endfor Redimension/N=(outIndex, -1) selectDisplayed Redimension/N=(outIndex, -1) sweepPropertiesDisplayed + Redimension/N=(outIndex, -1) sweepLNBsDisplayed numTraces = outIndex outIndex = 0 @@ -2475,7 +2490,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & for(l = 0; l < numTraces; l += 1) clampCode = SF_MapClampModeToSelectCM(sweepPropertiesDisplayed[l][SWEEPPROP_CLAMPMODE]) - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT], doStimsetMatching)) + if(!SF_IsValidSingleSelection(graph, filter, sweepLNBsDisplayed[l][%NUMERICAL], sweepLNBsDisplayed[l][%TEXTUAL], sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT], doStimsetMatching)) continue endif @@ -2510,7 +2525,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & setSweepCount = WaveExists(setting) ? setting[index] : NaN endif - if(!SF_IsValidSingleSelection(graph, filter, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount, doStimsetMatching)) + if(!SF_IsValidSingleSelection(graph, filter, numericalValues, textualValues, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount, doStimsetMatching)) continue endif @@ -2536,7 +2551,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & return out End -static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount, variable doStimsetMatching) +static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, WAVE numericalValues, WAVE textualValues, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount, variable doStimsetMatching) variable sweepQC, setQC string setName @@ -2558,7 +2573,7 @@ static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParamete endif if(doStimsetMatching) - setName = SFH_GetStimsetName(graph, sweepNo, channelNumber, channelType) + setName = SFH_GetStimsetName(numericalValues, textualValues, sweepNo, channelNumber, channelType) if(!MatchAgainstWildCardPatterns(filter.stimsets, setName)) return 0 endif diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 258a6ff6d2..221300f1ed 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1689,14 +1689,10 @@ Function/WAVE SFH_GetSingleSelect(string graph, string opShort, variable sweepNo return selectDataArray End -Function/S SFH_GetStimsetName(string graph, variable sweepNo, variable channelNumber, variable channelType) +Function/S SFH_GetStimsetName(WAVE numericalValues, WAVE textualValues, variable sweepNo, variable channelNumber, variable channelType) variable index - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z/T textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) - ASSERT(WaveExists(numericalValues) && WaveExists(textualValues), "Could not retrieve LNB") - [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, STIM_WAVE_NAME_KEY, channelNumber, channelType, DATA_ACQUISITION_MODE) ASSERT(WaveExists(settings), "Could not retrieve setName") diff --git a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf index 80f8c687b3..7f14bb7c5e 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf @@ -1871,8 +1871,9 @@ static Function TestOperationData() WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode", "SweepMapIndex"}, \ - {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), StringFromList(j, channelNumberList), num2istr(clampMode), "NaN"}) + WAVE textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "textualValues", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(textualValues, 2), GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), StringFromList(j, channelNumberList), num2istr(clampMode), "NaN"}) endfor endfor @@ -2586,8 +2587,9 @@ static Function TestOperationSelect() WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "SweepMapIndex"}, \ - {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), "NaN"}) + WAVE textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "textualValues", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(textualValues, 2), GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), "NaN"}) endfor endfor @@ -2865,8 +2867,9 @@ static Function TestOperationSelect() WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "AssociatedHeadstage", "SweepMapIndex"}, \ - {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), num2istr(0), "NaN"}) + WAVE textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "textualValues", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "clampMode", "GUIChannelNumber", "AssociatedHeadstage", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(textualValues, 2), GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo), num2istr(clampMode), StringFromList(j, channelNumberList), num2istr(0), "NaN"}) endfor endfor diff --git a/Packages/tests/UTF_HelperFunctions.ipf b/Packages/tests/UTF_HelperFunctions.ipf index 5e51ab2a00..7ca497c6ed 100644 --- a/Packages/tests/UTF_HelperFunctions.ipf +++ b/Packages/tests/UTF_HelperFunctions.ipf @@ -1537,8 +1537,9 @@ Function [variable numSweeps, variable numChannels, WAVE/U/I channels] FillFakeD AppendToGraph/W=$win singleColumnDataWave/TN=$trace WAVE numericalValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNumber) - TUD_SetUserDataFromWaves(win, trace, {"experiment", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode", "SweepMapIndex"}, \ - {"blah", GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(singleColumnDataWave, 2), "Sweep", "0", channelTypeStr, num2str(channelNumber), num2str(sweepNumber), num2istr(channelNumber), num2istr(clampMode), "NaN"}) + WAVE textualValues = BSP_GetLogbookWave(win, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNumber) + TUD_SetUserDataFromWaves(win, trace, {"experiment", "textualValues", "numericalValues", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber", "GUIChannelNumber", "clampMode", "SweepMapIndex"}, \ + {"blah", GetWavesDataFolder(textualValues, 2), GetWavesDataFolder(numericalValues, 2), GetWavesDataFolder(singleColumnDataWave, 2), "Sweep", "0", channelTypeStr, num2str(channelNumber), num2str(sweepNumber), num2istr(channelNumber), num2istr(clampMode), "NaN"}) endfor endfor From 01c0062039aab70cb0d10148b9064a25bb2ba1ff Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 23:02:46 +0200 Subject: [PATCH 32/38] SF: Use LNB directly for sweepQC and setQC filter - LNB handed down from SF_GetSelectData - Uses correct LNB now for SB case --- Packages/MIES/MIES_SweepFormula.ipf | 10 +++---- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 30 +++++++-------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 429b766a3a..2eb1c40a7e 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2490,7 +2490,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & for(l = 0; l < numTraces; l += 1) clampCode = SF_MapClampModeToSelectCM(sweepPropertiesDisplayed[l][SWEEPPROP_CLAMPMODE]) - if(!SF_IsValidSingleSelection(graph, filter, sweepLNBsDisplayed[l][%NUMERICAL], sweepLNBsDisplayed[l][%TEXTUAL], sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT], doStimsetMatching)) + if(!SF_IsValidSingleSelection(filter, sweepLNBsDisplayed[l][%NUMERICAL], sweepLNBsDisplayed[l][%TEXTUAL], sweepNo, channelNumber, channelType, selectDisplayed[l][dimPosSweep], selectDisplayed[l][dimPosChannelNumber], selectDisplayed[l][dimPosChannelType], clampCode, sweepPropertiesDisplayed[l][SWEEPPROP_SETCYCLECOUNT], sweepPropertiesDisplayed[l][SWEEPPROP_SETSWEEPCOUNT], doStimsetMatching)) continue endif @@ -2525,7 +2525,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & setSweepCount = WaveExists(setting) ? setting[index] : NaN endif - if(!SF_IsValidSingleSelection(graph, filter, numericalValues, textualValues, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount, doStimsetMatching)) + if(!SF_IsValidSingleSelection(filter, numericalValues, textualValues, sweepNo, channelNumber, channelType, sweepNo, l, channelType, clampCode, setCycleCount, setSweepCount, doStimsetMatching)) continue endif @@ -2551,7 +2551,7 @@ static Function/WAVE SF_GetSelectData(string graph, STRUCT SF_SelectParameters & return out End -static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParameters &filter, WAVE numericalValues, WAVE textualValues, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount, variable doStimsetMatching) +static Function SF_IsValidSingleSelection(STRUCT SF_SelectParameters &filter, WAVE numericalValues, WAVE textualValues, variable filtSweepNo, variable filtChannelNumber, variable filtChannelType, variable sweepNo, variable channelNumber, variable channelType, variable clampMode, variable setCycleCount, variable setSweepCount, variable doStimsetMatching) variable sweepQC, setQC string setName @@ -2580,14 +2580,14 @@ static Function SF_IsValidSingleSelection(string graph, STRUCT SF_SelectParamete endif if(filter.sweepQC != SF_OP_SELECT_IVSCCSWEEPQC_IGNORE) - sweepQC = SFH_IsSweepQCPassed(graph, sweepNo, channelNumber, channelType) == 1 ? SF_OP_SELECT_IVSCCSWEEPQC_PASSED : SF_OP_SELECT_IVSCCSWEEPQC_FAILED + sweepQC = SFH_IsSweepQCPassed(numericalValues, textualValues, sweepNo, channelNumber, channelType) == 1 ? SF_OP_SELECT_IVSCCSWEEPQC_PASSED : SF_OP_SELECT_IVSCCSWEEPQC_FAILED if(!(filter.sweepQC & sweepQC)) return 0 endif endif if(filter.setQC != SF_OP_SELECT_IVSCCSETQC_IGNORE) - setQC = SFH_IsSetQCPassed(graph, sweepNo, channelNumber, channelType) == 1 ? SF_OP_SELECT_IVSCCSETQC_PASSED : SF_OP_SELECT_IVSCCSETQC_FAILED + setQC = SFH_IsSetQCPassed(numericalValues, textualValues, sweepNo, channelNumber, channelType) == 1 ? SF_OP_SELECT_IVSCCSETQC_PASSED : SF_OP_SELECT_IVSCCSETQC_FAILED if(!(filter.setQC & setQC)) return 0 endif diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 221300f1ed..44f279694d 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1699,48 +1699,42 @@ Function/S SFH_GetStimsetName(WAVE numericalValues, WAVE textualValues, variable return WaveText(settings, row = index) End -Function SFH_IsSetQCPassed(string graph, variable sweepNo, variable channelNumber, variable channelType) +Function SFH_IsSetQCPassed(WAVE numericalValues, WAVE textualValues, variable sweepNo, variable channelNumber, variable channelType) - return SFH_GetIndepPSQEntrySCI(graph, sweepNo, channelNumber, channelType, PSQ_FMT_LBN_SET_PASS) + return SFH_GetIndepPSQEntrySCI(numericalValues, textualValues, sweepNo, channelNumber, channelType, PSQ_FMT_LBN_SET_PASS) End -Function SFH_IsSweepQCPassed(string graph, variable sweepNo, variable channelNumber, variable channelType) +Function SFH_IsSweepQCPassed(WAVE numericalValues, WAVE textualValues, variable sweepNo, variable channelNumber, variable channelType) - return SFH_GetIndepPSQEntry(graph, sweepNo, channelNumber, channelType, PSQ_FMT_LBN_SWEEP_PASS) + return SFH_GetIndepPSQEntry(numericalValues, textualValues, sweepNo, channelNumber, channelType, PSQ_FMT_LBN_SWEEP_PASS) End -static Function SFH_GetIndepPSQEntrySCI(string graph, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) +static Function SFH_GetIndepPSQEntrySCI(WAVE numericalValues, WAVE textualValues, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) variable type, waMode, headstage string key - [type, waMode, headstage] = SFH_GetAnalysisFunctionType(graph, sweepNo, channelNumber, channelType) + [type, waMode, headstage] = SFH_GetAnalysisFunctionType(numericalValues, textualValues, sweepNo, channelNumber, channelType) if(IsNaN(type)) return NaN endif - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - ASSERT(WaveExists(numericalValues), "Could not retrieve LNB") - key = CreateAnaFuncLBNKey(type, psqLNBEntry, query = 1, waMode = waMode) return GetLastSettingIndepSCI(numericalValues, sweepNo, key, headstage, UNKNOWN_MODE, defValue = 0) End -static Function SFH_GetIndepPSQEntry(string graph, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) +static Function SFH_GetIndepPSQEntry(WAVE numericalValues, WAVE textualValues, variable sweepNo, variable channelNumber, variable channelType, string psqLNBEntry) variable type, waMode, headstage, passed string key - [type, waMode, headstage] = SFH_GetAnalysisFunctionType(graph, sweepNo, channelNumber, channelType) + [type, waMode, headstage] = SFH_GetAnalysisFunctionType(numericalValues, textualValues, sweepNo, channelNumber, channelType) if(IsNaN(type)) return NaN endif - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - ASSERT(WaveExists(numericalValues), "Could not retrieve LNB") - if(type == PSQ_RHEOBASE && !CmpStr(psqLNBEntry, PSQ_FMT_LBN_SWEEP_PASS)) - passed = SFH_IsSetQCPassed(graph, sweepNo, channelNumber, channelType) + passed = SFH_IsSetQCPassed(numericalValues, textualValues, sweepNo, channelNumber, channelType) WAVE sweepsSCI = AFH_GetSweepsFromSameSCI(numericalValues, sweepNo, headstage) [WAVE passingSweeps, WAVE failingSweeps] = AFH_GetRheobaseSweepsSCISweepQCSplitted(numericalValues, sweepNo, headstage, sweepsSCI, passed) if(WaveExists(passingSweeps)) @@ -1755,15 +1749,11 @@ static Function SFH_GetIndepPSQEntry(string graph, variable sweepNo, variable ch return GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE, defValue = 0) End -static Function [variable type, variable waMode, variable headstage] SFH_GetAnalysisFunctionType(string graph, variable sweepNo, variable channelNumber, variable channelType) +static Function [variable type, variable waMode, variable headstage] SFH_GetAnalysisFunctionType(WAVE numericalValues, WAVE/T textualValues, variable sweepNo, variable channelNumber, variable channelType) string key, anaFuncName variable index, DAC - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z/T textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) - ASSERT(WaveExists(numericalValues) && WaveExists(textualValues), "Could not retrieve LNB") - [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DAC", channelNumber, channelType, DATA_ACQUISITION_MODE) if(!WaveExists(settings)) return [NaN, NaN, NaN] From fa3060c8b45160d2ec872f46cf3a13944a125d32 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 23:22:47 +0200 Subject: [PATCH 33/38] SF: Moved SFH_GetAnalysisFunctionType as generic helper to MIES_MiesUtilities_Logbook.ipf - use GetHeadStageForChannel to retrieve headstage - Add function documentation - adapt call sites --- Packages/MIES/MIES_MiesUtilities_Logbook.ipf | 41 ++++++++++++++++++++ Packages/MIES/MIES_SweepFormula_Helpers.ipf | 39 +------------------ 2 files changed, 43 insertions(+), 37 deletions(-) diff --git a/Packages/MIES/MIES_MiesUtilities_Logbook.ipf b/Packages/MIES/MIES_MiesUtilities_Logbook.ipf index 97a0be0182..5921212fdc 100644 --- a/Packages/MIES/MIES_MiesUtilities_Logbook.ipf +++ b/Packages/MIES/MIES_MiesUtilities_Logbook.ipf @@ -1976,3 +1976,44 @@ Function GetTotalOnsetDelayFromDevice(string device) return DAG_GetNumericalValue(device, "setvar_DataAcq_OnsetDelayUser") + TPSettingsCalculated[%totalLengthMS] End + +/// @brief Retrieve the analysis function that was run for a given sweep / channelNumber / channelType +/// +/// @param numericalValues numerical labnotebook +/// @param textualValues textual labnotebook +/// @param sweepNo sweep number +/// @param channelNumber channel number +/// @param channelType channelType +/// +/// @retval type analysis function type @ref SpecialAnalysisFunctionTypes +/// @retval waMode bit-mask of possible workarounds for CreateAnaFuncLBNKey() +/// @retval headstage headstage where the analysis function was run on +Function [variable type, variable waMode, variable headstage] GetAnalysisFunctionType(WAVE numericalValues, WAVE/T textualValues, variable sweepNo, variable channelNumber, variable channelType) + + string key, anaFuncName + variable index, DAC + + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DAC", channelNumber, channelType, DATA_ACQUISITION_MODE) + if(!WaveExists(settings)) + return [NaN, NaN, NaN] + endif + DAC = settings[index] + + key = "Generic function" + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, key, DAC, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) + if(!WaveExists(settings)) + return [NaN, NaN, NaN] + endif + anaFuncName = WaveText(settings, row = index) + + headstage = GetHeadStageForChannel(numericalValues, sweepNo, channelType, channelNumber, DATA_ACQUISITION_MODE) + if(IsNaN(headstage)) + return [NaN, NaN, NaN] + endif + + WAVE anaFuncTypes = LBN_GetNumericWave(defValue = INVALID_ANALYSIS_FUNCTION) + anaFuncTypes[headstage] = MapAnaFuncToConstant(anaFuncName) + [type, waMode] = AD_GetAnalysisFunctionType(numericalValues, anaFuncTypes, sweepNo, headstage) + + return [type, waMode, headstage] +End diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 44f279694d..24a3fe8618 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1714,7 +1714,7 @@ static Function SFH_GetIndepPSQEntrySCI(WAVE numericalValues, WAVE textualValues variable type, waMode, headstage string key - [type, waMode, headstage] = SFH_GetAnalysisFunctionType(numericalValues, textualValues, sweepNo, channelNumber, channelType) + [type, waMode, headstage] = GetAnalysisFunctionType(numericalValues, textualValues, sweepNo, channelNumber, channelType) if(IsNaN(type)) return NaN endif @@ -1728,7 +1728,7 @@ static Function SFH_GetIndepPSQEntry(WAVE numericalValues, WAVE textualValues, v variable type, waMode, headstage, passed string key - [type, waMode, headstage] = SFH_GetAnalysisFunctionType(numericalValues, textualValues, sweepNo, channelNumber, channelType) + [type, waMode, headstage] = GetAnalysisFunctionType(numericalValues, textualValues, sweepNo, channelNumber, channelType) if(IsNaN(type)) return NaN endif @@ -1749,41 +1749,6 @@ static Function SFH_GetIndepPSQEntry(WAVE numericalValues, WAVE textualValues, v return GetLastSettingIndep(numericalValues, sweepNo, key, UNKNOWN_MODE, defValue = 0) End -static Function [variable type, variable waMode, variable headstage] SFH_GetAnalysisFunctionType(WAVE numericalValues, WAVE/T textualValues, variable sweepNo, variable channelNumber, variable channelType) - - string key, anaFuncName - variable index, DAC - - [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DAC", channelNumber, channelType, DATA_ACQUISITION_MODE) - if(!WaveExists(settings)) - return [NaN, NaN, NaN] - endif - DAC = settings[index] - - key = "Generic function" - [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, key, DAC, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) - if(!WaveExists(settings)) - return [NaN, NaN, NaN] - endif - anaFuncName = WaveText(settings, row = index) - - WAVE/Z settings = GetLastSetting(numericalValues, sweepNo, "DAC", DATA_ACQUISITION_MODE) - if(!WaveExists(settings)) - return [NaN, NaN, NaN] - endif - WAVE headstageIndices = FindIndizes(settings, var = DAC) - if(DimSize(headstageIndices, ROWS) != 1) - return [NaN, NaN, NaN] - endif - headstage = headstageIndices[0] - - WAVE anaFuncTypes = LBN_GetNumericWave(defValue = INVALID_ANALYSIS_FUNCTION) - anaFuncTypes[headstage] = MapAnaFuncToConstant(anaFuncName) - [type, waMode] = AD_GetAnalysisFunctionType(numericalValues, anaFuncTypes, sweepNo, headstage) - - return [type, waMode, headstage] -End - Function/S SFH_CreateLegendFromRanges(WAVE selectData, WAVE/WAVE ranges) variable i, prefixPerSelect From fd5ee28ee9de40ad8e4c4b9e980192ba72dbd7bd Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 4 Oct 2024 23:33:48 +0200 Subject: [PATCH 34/38] SF: Use correct LNB getter in SF_GetTraceColor --- Packages/MIES/MIES_SweepFormula.ipf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 2eb1c40a7e..5d449f8f3b 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1386,7 +1386,7 @@ End Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE data) - variable i, channelNumber, channelType, sweepNo, headstage, numDoInh, minVal, isAveraged + variable i, channelNumber, channelType, sweepNo, headstage, numDoInh, minVal, isAveraged, mapIndex s.red = 0xFFFF s.green = 0x0000 @@ -1416,12 +1416,13 @@ Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE channelNumber = JWN_GetNumberFromWaveNote(data, SF_META_CHANNELNUMBER) channelType = JWN_GetNumberFromWaveNote(data, SF_META_CHANNELTYPE) isAveraged = JWN_GetNumberFromWaveNote(data, SF_META_ISAVERAGED) + mapIndex = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPMAPINDEX) sweepNo = isAveraged == 1 ? JWN_GetNumberFromWaveNote(data, SF_META_AVERAGED_FIRST_SWEEP) : JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) if(!IsValidSweepNumber(sweepNo)) return [s] endif - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) if(!WaveExists(numericalValues)) return [s] endif From e7248080dd8ec8ffe6ab44df50288e5189b0c244 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 9 Oct 2024 14:46:34 +0200 Subject: [PATCH 35/38] SF: Introduce constant for default select formula --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 24a3fe8618..053effd894 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -13,6 +13,7 @@ static StrConstant SFH_WORKING_DF = "FormulaData" static StrConstant SFH_ARGSETUP_OPERATION_KEY = "Operation" static StrConstant SFH_ARGSETUP_EMPTY_OPERATION_VALUE = "NOOP" +static StrConstant SFH_DEFAULT_SELECT_FORMULA = "select()" threadsafe Function SFH_StringChecker_Prototype(string str) ASSERT_TS(0, "Can't call prototype function") @@ -928,7 +929,7 @@ Function/WAVE SFH_GetArgumentSelect(variable jsonId, string jsonPath, string gra return selectArray endif - WAVE selectComp = SF_ExecuteFormula("select()", graph, useVariables = 0) + WAVE selectComp = SF_ExecuteFormula(SFH_DEFAULT_SELECT_FORMULA, graph, useVariables = 0) Make/FREE/WAVE selectArray = {selectComp} return selectArray @@ -1027,7 +1028,7 @@ Function [WAVE/T keys, WAVE/T values] SFH_CreateResultsWaveWithCode(string graph WAVE/T/Z cursorInfos = GetCursorInfos(graph) - WAVE/WAVE/Z selectData = SF_ExecuteFormula("select()", graph, useVariables = 0) + WAVE/WAVE/Z selectData = SF_ExecuteFormula(SFH_DEFAULT_SELECT_FORMULA, graph, useVariables = 0) if(WaveExists(selectData) && WaveExists(selectData[0])) values[0][%$"Sweep Formula sweeps/channels"][INDEP_HEADSTAGE] = NumericWaveToList(selectData[0], ",", colSep = ";") endif From 528f1f366a87b78f04af9b61f2f10e37b2563caf Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 9 Oct 2024 15:20:57 +0200 Subject: [PATCH 36/38] SF: Add documentation to SF_SelectParameters structure --- Packages/MIES/MIES_Structures.ipf | 38 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index 2ec04be7b7..be82e0798c 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -648,23 +648,25 @@ EndStructure /// @brief Wraps all parameters combined for one SF select call /// When adapting also change @ref SF_DuplicateSelectFilter +/// Initialization with uninitialized state value @see SF_InitSelectFilterUninitalized +/// Initialization with defaults for uninitialized elements after select argument parsing @see SF_SetSelectionFilterDefaults Structure SF_SelectParameters - WAVE selects - WAVE channels - WAVE sweeps - variable sweepsSet // flag that indicates if sweep input was set - string vis - variable clampMode - WAVE/T stimsets - WAVE ranges - variable sweepQC - variable setQC - string experimentName - string device - variable expandSCI - variable expandRAC - variable setCycleCount - variable setSweepCount - variable sciIndex - variable racIndex + WAVE selects // selection wave (SF_DATATYPE_SELECT) or null if no select(...) argument appeared + WAVE channels // result from channels(...) (SF_DATATYPE_CHANNELS), if no argument was given result of "selchannels()" + WAVE sweeps // result from selsweeps(...) (SF_DATATYPE_SWEEPNO), if no argument was given result of "selsweeps()" + variable sweepsSet // 1 if WAVE sweeps was set due to a selsweeps(...) argument, 0 otherwise + string vis // result from selvis(...), if this argument was given one of SF_OP_SELECTVIS_*, if not then if a select argument was given SF_OP_SELECTVIS_ALL, SF_OP_SELECTVIS_DISPLAYED otherwise + variable clampMode // result from selcm(...), if this argument was given a bit combination of SF_OP_SELECT_CLAMPCODE_*, SF_OP_SELECT_CLAMPCODE_ALL otherwise + WAVE/T stimsets // result from setstimset(...), wildcard patterns, if this argument was not given "*" + WAVE ranges // result from selrange(...), if this argument was not given the full range + variable sweepQC // result from selivsccsweepqc(...), if this argument was given SF_OP_SELECT_IVSCCSWEEPQC_PASSED or SF_OP_SELECT_IVSCCSWEEPQC_FAILED, SF_OP_SELECT_IVSCCSWEEPQC_IGNORE otherwise + variable setQC // result from selivsccsetqc(...), if this argument was given SF_OP_SELECT_IVSCCSETQC_PASSED or SF_OP_SELECT_IVSCCSETQC_FAILED, SF_OP_SELECT_IVSCCSETQC_IGNORE otherwise + string experimentName // result from selexp(...), wildcard pattern if this argument was not given, "" otherwise + string device // result from seldev(...), wildcard pattern if this argument was not given, "" otherwise + variable expandSCI // result from selexpandsci(...), 1 if this argument was given, 0 otherwise + variable expandRAC // result from selexpandrac(...), 1 if this argument was given, 0 otherwise + variable setCycleCount // result from selsetcyclecount(x), x if this argument was given, NaN otherwise + variable setSweepCount // result from selsetsweepcount(x), x if this argument was given, NaN otherwise + variable sciIndex // result from selsciindex(x), x if this argument was given, NaN otherwise + variable racIndex // result from selracindex(x), x if this argument was given, NaN otherwise EndStructure From 6bdda347b4679e8264fd613d4e3c3952a2bbaa8d Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Mon, 14 Oct 2024 20:55:18 +0200 Subject: [PATCH 37/38] SFH_NewSelectDataWave: Initialize wave to NaN This avoids having invalid data in there. Missed in the initial implementation in abe808f5e (SF: Add select operation to select sweeps for data, adapted data operation, 2022-02-04). --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 053effd894..01813c8d55 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1093,7 +1093,7 @@ Function/WAVE SFH_NewSelectDataWave(variable numSweeps, variable numChannels) ASSERT(numSweeps >= 0 && numChannels >= 0, "Invalid wave size specified") - Make/FREE/D/N=(numSweeps * numChannels, 4) selectData + Make/FREE/D/N=(numSweeps * numChannels, 4) selectData = NaN SetDimLabel COLS, 0, SWEEP, selectData SetDimLabel COLS, 1, CHANNELTYPE, selectData SetDimLabel COLS, 2, CHANNELNUMBER, selectData From bf10c953aa976c54de0cd9afc2fb0ad5ad9e1db0 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Mon, 14 Oct 2024 21:47:17 +0200 Subject: [PATCH 38/38] MIES_SweepFormula_PSX.ipf: Handle sweepbrowser labnotebooks correctly In the same spirit as d5b560f5f (Bugfix: SF use proper handling of sweepMap for SB, 2024-10-04) but for PSX. We also need to include now the experiment name in the combo key to make it unique. And for the stats equivalence data we also include the sweepmapindex next to the sweepNo. --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 1 + Packages/MIES/MIES_SweepFormula_PSX.ipf | 64 ++++++--- Packages/tests/Basic/UTF_SweepFormula_PSX.ipf | 122 +++++++++++++----- 3 files changed, 133 insertions(+), 54 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 01813c8d55..6d2e349681 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -1120,6 +1120,7 @@ Function [WAVE selectData, WAVE range] SFH_ParseToSelectDataWaveAndRange(WAVE sw selectData[0][%SWEEP] = JWN_GetNumberFromWaveNote(sweepData, SF_META_SWEEPNO) selectData[0][%CHANNELTYPE] = JWN_GetNumberFromWaveNote(sweepData, SF_META_CHANNELTYPE) selectData[0][%CHANNELNUMBER] = JWN_GetNumberFromWaveNote(sweepData, SF_META_CHANNELNUMBER) + selectData[0][%SWEEPMAPINDEX] = JWN_GetNumberFromWaveNote(sweepData, SF_META_SWEEPMAPINDEX) return [selectData, range] End diff --git a/Packages/MIES/MIES_SweepFormula_PSX.ipf b/Packages/MIES/MIES_SweepFormula_PSX.ipf index 0856df564b..a2ef1a036a 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX.ipf @@ -1027,10 +1027,10 @@ static Function/S PSX_BuildSweepEquivKey(variable chanType, variable chanNr) return str End -/// @brief Return the triplett channel number, channel type and sweep number from the sweep equivalence wave located in the given channelNumberType and sweepIndex coordinates -static Function [variable chanNr, variable chanType, variable sweepNo] PSX_GetSweepEquivKeyAndSweep(WAVE sweepEquiv, variable channelNumberType, variable sweepIndex) +/// @brief Return the quartett of channel number, channel type, sweep number and map index from the sweep equivalence wave located in the given channelNumberType and column coordinates +static Function [variable chanNr, variable chanType, variable sweepNo, variable mapIndex] PSX_GetSweepEquivKeyAndValue(WAVE/T sweepEquiv, variable channelNumberType, variable sweepIndex) - string str, chanTypeStr, chanNrStr + string str, chanTypeStr, chanNrStr, entry, sweepNoStr, mapIndexStr str = GetDimLabel(sweepEquiv, ROWS, channelNumberType) ASSERT(strlen(str) > 0, "Unexpected empty channelNumberType label") @@ -1040,9 +1040,25 @@ static Function [variable chanNr, variable chanType, variable sweepNo] PSX_GetSw chanType = str2num(chanTypeStr) chanNr = str2num(chanNrStr) - return [chanNr, chanType, sweepEquiv[channelNumberType][sweepIndex]] + entry = sweepEquiv[channelNumberType][sweepIndex] + + SplitString/E="(?i)SweepNo([[:digit:]]+)_MapIndex([[:digit:]]+|NaN)" entry, sweepNoStr, mapIndexStr + + sweepNo = str2num(sweepNoStr) + mapIndex = str2num(mapIndexStr) + + return [chanNr, chanType, sweepNo, mapIndex] End +/// @brief Build the column data used for the sweep equivalence wave +static Function/S PSX_BuildSweepEquivValue(variable sweepNo, variable mapIndex) + + string str + + sprintf str, "SweepNo%d_MapIndex%g", sweepNo, mapIndex + + return str +End /// @brief Generate the equivalence classes of selectData /// /// All selections which have the same channel number and type are in one equivalence class. @@ -1057,7 +1073,7 @@ static Function/WAVE PSX_GenerateSweepEquiv(WAVE selectData) numSelect = DimSize(selectData, ROWS) ASSERT(numSelect > 0, "Expected at least one entry in sweepData") - Make/N=(numSelect, numSelect)/FREE=1 sweepEquiv = NaN + Make/N=(numSelect, numSelect)/FREE=1/T sweepEquiv for(i = 0; i < numSelect; i += 1) key = PSX_BuildSweepEquivKey(selectData[i][%CHANNELTYPE], selectData[i][%CHANNELNUMBER]) @@ -1069,11 +1085,11 @@ static Function/WAVE PSX_GenerateSweepEquiv(WAVE selectData) nextFreeRow += 1 endif - FindValue/FNAN/RMD=[idx][] sweepEquiv + FindValue/TXOP=4/TEXT=""/RMD=[idx][] sweepEquiv ASSERT(V_col >= 0, "Not enough space") maxCol = max(maxCol, V_col) - sweepEquiv[idx][V_col] = selectData[i][%SWEEP] + sweepEquiv[idx][V_col] = PSX_BuildSweepEquivValue(selectData[i][%SWEEP], selectData[i][%SWEEPMAPINDEX]) endfor ASSERT(nextFreeRow > 0, "Could not build sweep equivalence classes") @@ -1093,8 +1109,7 @@ Function PSX_CollectResolvedRanges(string graph, WAVE range, WAVE singleSelectDa chanType = singleSelectData[0][%CHANNELTYPE] mapIndex = singleSelectData[0][%SWEEPMAPINDEX] - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) SFH_ASSERT(WaveExists(textualValues) && WaveExists(numericalValues), "LBN not found for sweep " + num2istr(sweepNo)) [WAVE resolvedRanges, WAVE/T epochRangeNames] = SFH_GetNumericRangeFromEpoch(graph, numericalValues, textualValues, range, sweepNo, chanType, chanNr, mapIndex) @@ -1147,7 +1162,7 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r string propLabelAxis, comboKey variable numRows, numCols, i, j, k, index, sweepNo, chanNr, chanType, state, numRanges, lowerBoundary, upperBoundary, temp, err - variable refMarker, idx + variable refMarker, idx, mapIndex WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_PSX_STATS, MINIMUM_WAVE_SIZE) @@ -1176,7 +1191,7 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r for(k = 0; k < numCols; k += 1) - [chanNr, chanType, sweepNo] = PSX_GetSweepEquivKeyAndSweep(selectDataEquiv, i, k) + [chanNr, chanType, sweepNo, mapIndex] = PSX_GetSweepEquivKeyAndValue(selectDataEquiv, i, k) if(!IsValidSweepNumber(sweepNo)) break @@ -1187,6 +1202,7 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r singleSelectData[0][%SWEEP] = sweepNo singleSelectData[0][%CHANNELNUMBER] = chanNr singleSelectData[0][%CHANNELTYPE] = chanType + singleSelectData[0][%SWEEPMAPINDEX] = mapIndex comboKey = PSX_GenerateComboKey(graph, singleSelectData, range) @@ -2944,27 +2960,29 @@ End /// to the wave note of `psxEvent`. static Function/S PSX_GenerateComboKey(string graph, WAVE selectData, WAVE range) - variable sweepNo, channel, chanType - string device, key, datafolder, rangeStr + variable sweepNo, channel, chanType, mapIndex, isDataBrowser + string device, key, rangeStr, experiment + + isDataBrowser = BSP_IsDataBrowser(graph) ASSERT(DimSize(selectData, ROWS) == 1, "Expected selectData with only one entry") sweepNo = selectData[0][%SWEEP] channel = selectData[0][%CHANNELNUMBER] chanType = selectData[0][%CHANNELTYPE] + mapIndex = selectData[0][%SWEEPMAPINDEX] - WAVE/T textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) // Introduced in 7e903ed8 (GetSweepSettingsTextWave: Add device as entry, 2023-01-03) device = GetLastSettingTextIndep(textualValues, sweepNo, "Device", DATA_ACQUISITION_MODE) if(IsEmpty(device)) - if(BSP_IsDataBrowser(graph)) + if(isDataBrowser) device = BSP_GetDevice(graph) else - // datafolder looks like: root:MIES:Analysis:my_experiment:ITC18USB_Dev_0:labnotebook - datafolder = GetWavesDataFolder(textualValues, 1) - device = StringFromList(4, datafolder, ":") + WAVE/T sweepMap = SB_GetSweepMap(graph) + device = sweepMap[mapIndex][%Device] endif endif @@ -2980,7 +2998,15 @@ static Function/S PSX_GenerateComboKey(string graph, WAVE selectData, WAVE range ASSERT(0, "Unexpected wave type") endif - sprintf key, "Range[%s], Sweep [%d], Channel [%s%d], Device [%s]", rangeStr, sweepNo, StringFromList(chanType, XOP_CHANNEL_NAMES), channel, device + if(isDataBrowser) + experiment = GetExperimentName() + else + experiment = sweepMap[mapIndex][%FileName] + endif + + ASSERT(!IsEmpty(experiment), "Could not find the experiment of the given selectData") + + sprintf key, "Range[%s], Sweep [%d], Channel [%s%d], Device [%s], Experiment [%s]", rangeStr, sweepNo, StringFromList(chanType, XOP_CHANNEL_NAMES), channel, device, experiment ASSERT(strsearch(key, ":", 0) == -1, "Can't use a colon") return key diff --git a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf index 1f3059bc4e..9ccf61a608 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf @@ -361,44 +361,58 @@ End static Function CheckSweepEquiv() - variable sweepNo, chanType, chanNr + variable sweepNo, chanType, chanNr, mapIndex - WAVE selectData = SFH_NewSelectDataWave(5, 1) + WAVE selectData = SFH_NewSelectDataWave(6, 1) selectData[0][%SWEEP] = 1 selectData[1][%SWEEP] = 2 selectData[2][%SWEEP] = 3 selectData[3][%SWEEP] = 4 selectData[4][%SWEEP] = 5 + selectData[5][%SWEEP] = 5 + + selectData[0][%SWEEPMAPINDEX] = 0 + selectData[1][%SWEEPMAPINDEX] = 1 + selectData[2][%SWEEPMAPINDEX] = 2 + selectData[3][%SWEEPMAPINDEX] = 3 + selectData[4][%SWEEPMAPINDEX] = 4 + // different mapIndex but same sweepNo + selectData[5][%SWEEPMAPINDEX] = 5 selectData[0][%CHANNELNUMBER] = 10 selectData[1][%CHANNELNUMBER] = 30 selectData[2][%CHANNELNUMBER] = 10 selectData[3][%CHANNELNUMBER] = 20 // same sweep but different channel number selectData[4][%CHANNELNUMBER] = 10 + selectData[5][%CHANNELNUMBER] = 10 selectData[0][%CHANNELTYPE] = XOP_CHANNEL_TYPE_ADC selectData[1][%CHANNELTYPE] = XOP_CHANNEL_TYPE_ADC selectData[2][%CHANNELTYPE] = XOP_CHANNEL_TYPE_TTL // same sweep bug different channel type selectData[3][%CHANNELTYPE] = XOP_CHANNEL_TYPE_ADC selectData[4][%CHANNELTYPE] = XOP_CHANNEL_TYPE_ADC + selectData[5][%CHANNELTYPE] = XOP_CHANNEL_TYPE_ADC // sweep 1 and 5 are a group the rest is separate WAVE/Z selectDataEquiv = MIES_PSX#PSX_GenerateSweepEquiv(selectData) - CHECK_WAVE(selectDataEquiv, NUMERIC_WAVE, minorType = FLOAT_WAVE) + CHECK_WAVE(selectDataEquiv, TEXT_WAVE | FREE_WAVE) - Make/FREE ref = {{1, 2, 3, 4}, {5, NaN, NaN, NaN}} + Make/FREE/T ref = {{"SweepNo1_MapIndex0", "SweepNo2_MapIndex1", "SweepNo3_MapIndex2", "SweepNo4_MapIndex3"}, \ + {"SweepNo5_MapIndex4", "", "", ""}, \ + {"SweepNo5_MapIndex5", "", "", ""}} CHECK_EQUAL_WAVES(selectDataEquiv, ref, mode = WAVE_DATA) Make/T/N=(4)/FREE refLabels = MIES_PSX#PSX_BuildSweepEquivKey(selectData[p][%CHANNELTYPE], selectData[p][%CHANNELNUMBER]) Make/T/N=(4)/FREE labels = GetDimLabel(selectDataEquiv, ROWS, p) CHECK_EQUAL_WAVES(refLabels, labels, mode = WAVE_DATA) - [chanNr, chanType, sweepNo] = MIES_PSX#PSX_GetSweepEquivKeyAndSweep(selectDataEquiv, 0, 1) + [chanNr, chanType, sweepNo, mapIndex] = MIES_PSX#PSX_GetSweepEquivKeyAndValue(selectDataEquiv, 0, 1) CHECK_EQUAL_VAR(sweepNo, 5) CHECK_EQUAL_VAR(chanType, XOP_CHANNEL_TYPE_ADC) CHECK_EQUAL_VAR(chanNr, 10) + CHECK_EQUAL_VAR(mapIndex, 4) End Function [WAVE range, WAVE selectData] GetFakeRangeAndSelectData() @@ -410,6 +424,7 @@ Function [WAVE range, WAVE selectData] GetFakeRangeAndSelectData() selectData[0][%SWEEP] = 1 selectData[0][%CHANNELTYPE] = XOP_CHANNEL_TYPE_ADC selectData[0][%CHANNELNUMBER] = 3 + selectData[0][%SWEEPMAPINDEX] = NaN return [range, selectData] End @@ -664,7 +679,7 @@ static Function StatsWorksWithResults([STRUCT IUTF_mData &m]) Redimension/N=(10, -1) psxEvent comboKey = MIES_PSX#PSX_GenerateComboKey(browser, selectData, range) - ref = "Range[100, 200], Sweep [1], Channel [AD3], Device [ITC16_Dev_0]" + sprintf ref, "Range[100, 200], Sweep [1], Channel [AD3], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() CHECK_EQUAL_STR(comboKey, ref) id = "myID" @@ -1139,13 +1154,19 @@ End /// IUTF_TD_GENERATOR v0:GetKernelAmplitude static Function TestOperationPSX([STRUCT IUTF_mData &m]) - string win, device, str + string win, device, str, comboKey variable jsonID, kernelAmp kernelAmp = m.v0 - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=2 combos + + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey + WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) // all decay fits are successfull @@ -1203,10 +1224,13 @@ static Function TestOperationPSX([STRUCT IUTF_mData &m]) End static Function TestOperationPSXTooLargeDecayTau() - string win, device, str + string win, device, str, comboKey variable jsonID - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=1 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(2, combos) // all decay fits are successfull @@ -1307,10 +1331,15 @@ End static Function MouseSelectionPSX() - string browser, device, code, psxPlot, win + string browser, device, code, psxPlot, win, comboKey + + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[][][%$"Fit Result"] = 1 @@ -1465,11 +1494,14 @@ End /// UTF_TD_GENERATOR s0:SupportedPostProcForEventSelection static Function MouseSelectionPSXStats([STRUCT IUTF_mData &m]) - string win, browser, code, psxGraph, psxStatsGraph, postProc + string win, browser, code, psxGraph, psxStatsGraph, postProc, comboKey variable numEvents, logMode - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[][][%$"Fit Result"] = 1 @@ -1553,10 +1585,13 @@ End static Function MouseSelectionStatsPostProcNonFinite() - string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames + string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames, comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[1][%$combos[0]][%$"Fit Result"] = 1 @@ -2122,10 +2157,13 @@ End static Function KeyboardInteractions() - string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames + string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames, comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[][][%$"Fit Result"] = 1 @@ -2338,10 +2376,14 @@ End static Function KeyboardInteractionsStats() - string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames + string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames, comboKey + + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[][][%$"Fit Result"] = 1 @@ -2565,10 +2607,14 @@ End static Function KeyboardInteractionsStatsSpecial() - string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames + string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames, comboKey + + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[][][%$"Fit Result"] = 1 @@ -2631,10 +2677,13 @@ End static Function KeyboardInteractionsStatsPostProcNonFinite() - string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames + string browser, code, psxGraph, win, mainWindow, psxStatsGraph, trace, tracenames, comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[1][%$combos[0]][%$"Fit Result"] = 1 @@ -3006,10 +3055,13 @@ End static Function TestStoreAndLoad() - string browser, code, psxGraph, win, mainWindow, specialEventPanel, extAllGraph, bsPanel + string browser, code, psxGraph, win, mainWindow, specialEventPanel, extAllGraph, bsPanel, comboKey - Make/FREE/T combos = {"Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0]", \ - "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0]"} + Make/FREE/T/N=2 combos + sprintf comboKey, "Range[50, 150], Sweep [0], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[0] = comboKey + sprintf comboKey, "Range[50, 150], Sweep [2], Channel [AD6], Device [ITC16_Dev_0], Experiment [%s]", GetExperimentName() + combos[1] = comboKey WAVE overrideResults = MIES_PSX#PSX_CreateOverrideResults(4, combos) overrideResults[][][%$"Fit Result"] = 1