From 82f8d9e4c34dbaab3e626a448086404104a1b068 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Tue, 23 Jan 2024 20:45:00 +0100 Subject: [PATCH 1/5] Tests/GenerateAnalysisFunctionTable: Make the table output stable By sorting the function names we always get the same output. --- Packages/MIES/analysis_function_parameters.itx | 6 +++--- Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Packages/MIES/analysis_function_parameters.itx b/Packages/MIES/analysis_function_parameters.itx index c35e3a4878..be353705a8 100644 --- a/Packages/MIES/analysis_function_parameters.itx +++ b/Packages/MIES/analysis_function_parameters.itx @@ -14,8 +14,8 @@ BEGIN "BaselineRMSShortThreshold" "variable" "optional" "AR, CR, DA, PB, RA, RB, SE, VM" "Threshold value in mV for the short RMS baseline QC check (defaults to 0.07)" "BaselineTargetVThreshold" "variable" "optional" "CR, DA, RA, RB" "Threshold value in mV for the target V baseline QC check (defaults to 1)" "BoundsEvaluationMode" "string" "required" "CR" "Select the bounds evaluation mode: Symmetric (Lower and Upper), Depolarized (Upper) or Hyperpolarized (Lower)" - "DAScaleModifier" "variable" "mixed" "CR, DA, SC" "Modifier value to the DA Scale of headstages for too few spikes\rPercentage how the DAScale value is adapted if it is outside of the MinimumSpikeCount\"/\"MaximumSpikeCount\" band. Only for \"Supra\".\rModifier value to the DA Scale of headstages with spikes during chirp" - "DAScaleOperator" "string" "mixed" "CR, SC" "Set the math operator to use for combining the DAScale and the too few spikes modifier. Valid strings are \"+\" (addition) and \"*\" (multiplication).\rSet the math operator to use for combining the DAScale and the modifier. Valid strings are \"+\" (addition) and \"*\" (multiplication)." + "DAScaleModifier" "variable" "mixed" "CR, DA, SC" "Modifier value to the DA Scale of headstages with spikes during chirp\rPercentage how the DAScale value is adapted if it is outside of the MinimumSpikeCount\"/\"MaximumSpikeCount\" band. Only for \"Supra\".\rModifier value to the DA Scale of headstages for too few spikes" + "DAScaleOperator" "string" "mixed" "CR, SC" "Set the math operator to use for combining the DAScale and the modifier. Valid strings are \"+\" (addition) and \"*\" (multiplication).\rSet the math operator to use for combining the DAScale and the too few spikes modifier. Valid strings are \"+\" (addition) and \"*\" (multiplication)." "DAScales" "wave" "required" "DA, DS" "DA Scale Factors in pA" "DAScaleSpikePositionModifier" "variable" "required" "SC" "Modifier value to the DA Scale of headstages with failing spike positions." "DAScaleSpikePositionOperator" "string" "required" "SC" "Set the math operator to use for combining the DAScale and the spike position modifier. Valid strings are \"+\" (addition) and \"*\" (multiplication)." @@ -51,7 +51,7 @@ BEGIN "PostDAQDAScaleForFailedHS" "variable" "required" "FR" "Failed headstages will be set to this DAScale value [pA] in the post set event" "PostDAQDAScaleMinOffset" "variable" "required" "FR" "Mininum absolute offset value applied to the found DAScale [pA]" "RelativeVoltageDiff" "variable" "required" "VM" "Maximum relative allowed difference of the baseline membrane potentials [%].\r Set to `inf` to disable this check." - "SamplingFrequency" "variable" "optional" "AR, CR, DA, PB, RA, RB, SE, SP, VM" "Required sampling frequency for the acquired data [kHz]. Defaults to ITC:50 NI:50.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:200 NI:250.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:200 NI:250.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:200 NI:250.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:50 NI:50." + "SamplingFrequency" "variable" "optional" "AR, CR, DA, PB, RA, RB, SE, SP, VM" "Required sampling frequency for the acquired data [kHz]. Defaults to ITC:50 NI:50.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:200 NI:250.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:50 NI:50.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:50 NI:50.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:200 NI:250.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:50 NI:50.\rRequired sampling frequency for the acquired data [kHz]. Defaults to ITC:200 NI:250." "SamplingMultiplier" "variable" "required" "AR, CR, DA, FR, PB, RA, RB, SE, SP, VM" "Sampling multiplier, use 1 for no multiplier" "SealThreshold" "variable" "required" "SE" "Minimum required seal threshold [GΩ]" "ShowPlot" "variable" "optional" "DA" "Show the resistance (\"Sub\") or the f-I (\"Supra\") plot, defaults to true." diff --git a/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf b/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf index 4af0684707..d38e511622 100644 --- a/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf +++ b/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf @@ -764,6 +764,8 @@ static Function/WAVE GetAnalysisFunctions() SetDimensionLabels(wv, funcs, ROWS) + Sort/DIML wv, wv + return wv End @@ -928,7 +930,7 @@ static Function GenerateAnalysisFunctionTable() // if this test fails and the CRC changes // commit the file `Packages/MIES/analysis_function_parameters.itx` // and check that the changes therein are intentional - CHECK_EQUAL_VAR(WaveCRC(0, output, 0), 303837295) + CHECK_EQUAL_VAR(WaveCRC(0, output, 0), 2280254664) StoreWaveOnDisk(output, "analysis_function_parameters") End From 060a01481d7cae28f57a6e4008a054dda56a448e Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Sat, 2 Mar 2024 00:22:40 +0100 Subject: [PATCH 2/5] SF_OperationLabnotebook: Use headstage trace colors --- Packages/MIES/MIES_SweepFormula.ipf | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 2cf92ca180..ff4aad509c 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1217,6 +1217,8 @@ static Function/S SF_GetAnnotationPrefix(string dataType) return "" case SF_DATATYPE_TP: return "TP " + case SF_DATATYPE_LABNOTEBOOK: + return "LB " default: ASSERT(0, "Invalid dataType") endswitch @@ -1232,7 +1234,8 @@ static Function/S SF_GetTraceAnnotationText(STRUCT SF_PlotMetaData& plotMetaData strswitch(plotMetaData.dataType) case SF_DATATYPE_EPOCHS: // fallthrough - case SF_DATATYPE_SWEEP: // fallthrough + case SF_DATATYPE_SWEEP: // fallthrough + case SF_DATATYPE_LABNOTEBOOK: // fallthrough case SF_DATATYPE_TP: sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) annotationPrefix = SF_GetAnnotationPrefix(plotMetaData.dataType) @@ -1288,7 +1291,7 @@ Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE s.blue = 0x0000 Make/FREE/T stopInheritance = {SF_OPSHORT_MINUS, SF_OPSHORT_PLUS, SF_OPSHORT_DIV, SF_OPSHORT_MULT} - Make/FREE/T doInheritance = {SF_OP_DATA, SF_OP_TP, SF_OP_PSX, SF_OP_PSX_STATS, SF_OP_EPOCHS} + Make/FREE/T doInheritance = {SF_OP_DATA, SF_OP_TP, SF_OP_PSX, SF_OP_PSX_STATS, SF_OP_EPOCHS, SF_OP_LABNOTEBOOK} WAVE/T opStackW = ListToTextWave(opStack, ";") numDoInh = DimSize(doInheritance, ROWS) From 57430b547f8c54a10e622ffbafa55b8e234c716a Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Wed, 13 Mar 2024 17:32:42 +0100 Subject: [PATCH 3/5] AFH_GetAnalaysisParameterAsText: Factor it out --- .../MIES/MIES_AnalysisFunctionHelpers.ipf | 44 +++++++++++++ Packages/MIES/MIES_WaveBuilderPanel.ipf | 30 +-------- .../Basic/UTF_AnalysisFunctionParameters.ipf | 64 +++++++++++++++++++ 3 files changed, 110 insertions(+), 28 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf b/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf index da9127c777..8ddcb0a6c9 100644 --- a/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf +++ b/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf @@ -935,6 +935,50 @@ Function AFH_AddAnalysisParameter(string setName, string name, [variable var, st endif End +/// @brief Return a stringified version of the analysis parameter value +/// +/// @param name name of the parameter +/// @param params serialized parameters, usually just #AnalysisFunction_V3.params +Function/S AFH_GetAnalysisParameterAsText(string name, string params) + + string type + variable numericValue + + ASSERT(AFH_IsValidAnalysisParameter(name), "Name is not a legal non-liberal igor object name") + + type = AFH_GetAnalysisParamType(name, params, typeCheck = 0) + + strswitch(type) + case "variable": + numericValue = AFH_GetAnalysisParamNumerical(name, params) + if(!IsNan(numericValue)) + return num2str(numericValue) + endif + break + case "string": + return AFH_GetAnalysisParamTextual(name, params) + break + case "wave": + WAVE/Z wv = AFH_GetAnalysisParamWave(name, params) + if(WaveExists(wv)) + return NumericWaveToList(wv, ";") + endif + break + case "textwave": + WAVE/Z wv = AFH_GetAnalysisParamTextWave(name, params) + if(WaveExists(wv)) + return TextWaveToList(wv, ";") + endif + break + case "": // unknown name + break + default: + ASSERT(0, "invalid type") + endswitch + + return "" +End + /// @brief Return the headstage from the given active AD count /// /// @param statusADC channel status as returned by GetLastSetting() diff --git a/Packages/MIES/MIES_WaveBuilderPanel.ipf b/Packages/MIES/MIES_WaveBuilderPanel.ipf index 704a503f2e..78c70b5dc7 100644 --- a/Packages/MIES/MIES_WaveBuilderPanel.ipf +++ b/Packages/MIES/MIES_WaveBuilderPanel.ipf @@ -2016,36 +2016,10 @@ Function WBP_ListBoxProc_AnalysisParams(lba) : ListBoxControl params = WBP_GetAnalysisParameters() name = listWave[row][%Name] - value = "" + value = AFH_GetAnalysisParameterAsText(name, params) type = listWave[row][%Type] - if(!IsEmpty(type)) - strswitch(type) - case "variable": - numericValue = AFH_GetAnalysisParamNumerical(name, params) - if(!IsNan(numericValue)) - value = num2str(numericValue) - endif - break - case "string": - value = AFH_GetAnalysisParamTextual(name, params) - break - case "wave": - WAVE/Z wv = AFH_GetAnalysisParamWave(name, params) - if(WaveExists(wv)) - value = NumericWaveToList(wv, ";") - endif - break - case "textwave": - WAVE/Z wv = AFH_GetAnalysisParamTextWave(name, params) - if(WaveExists(wv)) - value = TextWaveToList(wv, ";") - endif - break - default: - ASSERT(0, "invalid type") - break - endswitch + if(!IsEmpty(type)) SetPopupMenuString(win, "popup_param_types", type) endif diff --git a/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf b/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf index d38e511622..e6fcf9f026 100644 --- a/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf +++ b/Packages/tests/Basic/UTF_AnalysisFunctionParameters.ipf @@ -959,3 +959,67 @@ static Function GenerateAnalysisFunctionLegend() CHECK_EQUAL_VAR(WaveCRC(0, output, 0), 2579934075) StoreWaveOnDisk(output, "analysis_function_abrev_legend") End + +/// @name AFH_GetAnalysisParameterAsText +/// @{ +Function GAPasT_AbortsWithEmptyName() + + try + AFH_GetAnalysisParameterAsText("", "name:textwave=0") + FAIL() + catch + PASS() + endtry +End + +Function GAPasT_AbortsWithIllegalName() + + try + AFH_GetAnalysisParameterAsText("123", "name:textwave=0") + FAIL() + catch + PASS() + endtry +End + +Function GAPasT_AbortsWithIllegalType() + + try + AFH_GetAnalysisParameterAsText("name", "name:invalidType=0") + FAIL() + catch + PASS() + endtry +End + +static Function/WAVE GetAnalysisParameterValues() + + Make/FREE/N=(5)/WAVE waves + + Make/FREE/T wv0 = {"var", "123"} + waves[0] = wv0 + + Make/FREE/T wv1 = {"str", "abcd"} + waves[1] = wv1 + + Make/FREE/T wv2 = {"wv", "1;2;"} + waves[2] = wv2 + + Make/FREE/T wv3 = {"txtwv", "a;b;"} + waves[3] = wv3 + + Make/FREE/T wv4 = {"i_dont_exist", ""} + waves[4] = wv4 + + return waves +End + +/// UTF_TD_GENERATOR GetAnalysisParameterValues +Function GAPasT_Works([WAVE/T wv]) + + string result + + result = AFH_GetAnalysisParameterAsText(wv[0], "var:variable=123,str:string=abcd,wv:wave=1|2,txtwv:textwave=a|b") + CHECK_EQUAL_STR(result, wv[1]) +End +/// @} From 2d6d6746688c538a6be7e6e151a562a4b6c5ca8f Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Wed, 13 Mar 2024 18:49:09 +0100 Subject: [PATCH 4/5] SFH_GetArgumentAsWave: Extend it The newly added optional parameters `defWave` allow to return a default wave, which makes the defOp = "wave()" workaround superfluous and also allows to check the returned wave types with `expectedWaveType`. --- Packages/MIES/MIES_SweepFormula.ipf | 2 +- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 37 +++++++++++++++++---- Packages/MIES/MIES_SweepFormula_PSX.ipf | 2 +- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index ff4aad509c..857572620b 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -5462,7 +5462,7 @@ Function/WAVE SF_OperationFitLine(variable jsonId, string jsonPath, string graph SFH_CheckArgumentCount(jsonId, jsonPath, SF_OP_FITLINE, 0, maxArgs = 1) - WAVE/T/Z constraints = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, SF_OP_FITLINE, 0, defOp = "wave()", singleResult = 1) + WAVE/T/Z constraints = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, SF_OP_FITLINE, 0, defWave = $"", singleResult = 1) [WAVE holdWave, WAVE initialValues] = SF_ParseFitConstraints(constraints, 2) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 07f2f36f3d..ea447322cd 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -215,7 +215,7 @@ End /// /// opShort = "countBirds" /// WAVE/D birds = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, opShort, 0, singleResult = 1) -/// WAVE/T types = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, opShort, 1, defOp = "birdTypes()", singleResult = 1) +/// WAVE/T types = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, opShort, 1, defOp = "birdTypes()", singleResult = 1, expectedWaveType = IGOR_TYPE_TEXT_WAVE) /// /// \endrst /// @@ -223,18 +223,24 @@ End /// unpacks the outer wave reference wave container. It should always be passed if you only expect one wave to be /// returned. /// -/// The second argument `birdTypes` is optional, if not present the operation `birdTypes()` is called and its result returned. -Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [string defOp, variable singleResult]) +/// The second argument `birdTypes` is optional, if not present the operation `birdTypes()` is called and its result returned. Alternatively `defWave` can be supplied which is then returned if the argument is not present. +Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string graph, string opShort, variable argNum, [string defOp, WAVE/Z defWave, variable singleResult, variable expectedWaveType]) - variable checkExist, numArgs + variable checkExist, numArgs, checkWaveType, realWaveType string msg - if(ParamIsDefault(defOp)) + if(ParamIsDefault(defOp) && ParamIsDefault(defWave)) checkExist = 1 else checkExist = 0 endif + if(ParamIsDefault(expectedWaveType)) + checkWaveType = 0 + else + checkWaveType = !!checkWaveType + endif + if(ParamIsDefault(singleResult)) singleResult = 0 else @@ -252,8 +258,23 @@ Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string gra WAVE/Z data = input[0] SFH_CleanUpInput(input) + + Make/FREE types = {WaveType(data)} else WAVE data = input + Make/FREE/N=(DimSize(input, ROWS)) types = WaveType(input[p]) + endif + + if(checkWaveType) + if(expectedWaveType == IGOR_TYPE_TEXT_WAVE) + // we are using selector 0 for WaveType + realWaveType = 0 + else + realWaveType = expectedWaveType + endif + + sprintf msg, "Argument #%d of operation %s: Expected wave type %d", argNum, opShort, expectedWaveType + SFH_ASSERT(IsConstant(types, realWaveType), msg) endif return data @@ -262,7 +283,11 @@ Function/WAVE SFH_GetArgumentAsWave(variable jsonId, string jsonPath, string gra sprintf msg, "Argument #%d of operation %s is mandatory", argNum, opShort SFH_ASSERT(!checkExist, msg) - return SF_ExecuteFormula(defOp, graph, singleResult = singleResult, useVariables=0) + if(!ParamIsDefault(defOp)) + return SF_ExecuteFormula(defOp, graph, singleResult = singleResult, useVariables=0) + endif + + return defWave End /// @brief Assertion for sweep formula diff --git a/Packages/MIES/MIES_SweepFormula_PSX.ipf b/Packages/MIES/MIES_SweepFormula_PSX.ipf index e2d9a345cf..8e84a98427 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX.ipf @@ -4222,7 +4222,7 @@ Function/WAVE PSX_Operation(variable jsonId, string jsonPath, string graph) sweepFilterLow = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX, 3, defValue = PSX_DEFAULT_FILTER_LOW, checkFunc = IsNullOrPositiveAndFinite) sweepFilterHigh = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX, 4, defValue = PSX_DEFAULT_FILTER_HIGH, checkFunc = IsNullOrPositiveAndFinite) maxTauFactor = SFH_GetArgumentAsNumeric(jsonID, jsonPath, graph, SF_OP_PSX, 5, defValue = PSX_DEFAULT_MAX_TAU_FACTOR, checkFunc = IsStrictlyPositiveAndFinite) - WAVE riseTime = SFH_GetArgumentAsWave(jsonID, jsonPath, graph, SF_OP_PSX, 6, defOp = "psxRiseTime()", singleResult = 1) + WAVE riseTime = SFH_GetArgumentAsWave(jsonID, jsonPath, graph, SF_OP_PSX, 6, defOp = "psxRiseTime()", expectedWaveType = IGOR_TYPE_64BIT_FLOAT, singleResult = 1) ASSERT(IsNumericWave(riseTime), "Invalid return from psxRiseTime") WAVE deconvFilter = SFH_GetArgumentAsWave(jsonID, jsonPath, graph, SF_OP_PSX, 7, defOp = "psxDeconvFilter()", singleResult = 1) From a578cf131ccf992c96ea35b2c1b5e363783112a5 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Wed, 13 Mar 2024 18:53:35 +0100 Subject: [PATCH 5/5] SF_FormulaPlotter: Make trace colors work with swapped axis When plotting text waves we plot them using the category axis feature including swapping X and Y waves. But if we do that we also need to query the opposite wave for the trace colors. Bug present since e1854b0c (SF: Change average operation to support two modes, 2023-04-05). --- Packages/MIES/MIES_SweepFormula.ipf | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 857572620b..2dcbec1711 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1903,7 +1903,16 @@ static Function SF_FormulaPlotter(string graph, string formula, [DFREF dfr, vari WAVE wvY = dataInGraph[l][%WAVEY] trace = tracesInGraph[l] - WAVE/Z traceColor = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_TRACECOLOR) + info = AxisInfo(win, "left") + isCategoryAxis = (NumberByKey("ISCAT", info) == 1) + + if(isCategoryAxis) + WAVE traceColorHolder = wvX + else + WAVE traceColorHolder = wvY + endif + + WAVE/Z traceColor = JWN_GetNumericWaveFromWaveNote(traceColorHolder, SF_META_TRACECOLOR) if(WaveExists(traceColor)) ASSERT(DimSize(traceColor, ROWS) == 3, "Need 3-element wave for color specification.") ModifyGraph/W=$win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2])