From feab7d865b19d58d1e4f4242260df67c6564757b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 22 Dec 2025 15:14:46 +0100 Subject: [PATCH 01/34] SF: Move part of the argument parsing for operation apfrequency to own function This will allow to parse the same argument block as part of another operation later --- .../MIES/MIES_SweepFormula_Operations.ipf | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index e4dfdf22ff..f2e076be52 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -235,6 +235,25 @@ static Function/WAVE SFO_OperationAnaFuncParamImplAllNames(WAVE/T names, WAVE/WA return GetUniqueEntries(allNames) End +static Function [variable method, variable level, string timeFreq, string normalize, string xAxisType] SFO_GetApFrequencyArguments(STRUCT SF_ExecutionData &exd, string opShort, variable offset) + + method = SFH_GetArgumentAsNumeric(exd, opShort, offset, defValue = SF_APFREQUENCY_FULL, allowedValues = {SF_APFREQUENCY_FULL, SF_APFREQUENCY_INSTANTANEOUS, SF_APFREQUENCY_APCOUNT, SF_APFREQUENCY_INSTANTANEOUS_PAIR}) + level = SFH_GetArgumentAsNumeric(exd, opShort, offset + 1, defValue = 0) + timeFreq = SFH_GetArgumentAsText(exd, opShort, offset + 2, defValue = SF_OP_APFREQUENCY_Y_FREQ, allowedValues = {SF_OP_APFREQUENCY_Y_TIME, SF_OP_APFREQUENCY_Y_FREQ}) + normalize = SFH_GetArgumentAsText(exd, opShort, offset + 3, defValue = SF_OP_APFREQUENCY_NONORM, allowedValues = { \ + SF_OP_APFREQUENCY_NONORM, \ + SF_OP_APFREQUENCY_NORMOVERSWEEPSMIN, \ + SF_OP_APFREQUENCY_NORMOVERSWEEPSMAX, \ + SF_OP_APFREQUENCY_NORMOVERSWEEPSAVG, \ + SF_OP_APFREQUENCY_NORMWITHINSWEEPMIN, \ + SF_OP_APFREQUENCY_NORMWITHINSWEEPMAX, \ + SF_OP_APFREQUENCY_NORMWITHINSWEEPAVG \ + }) + xAxisType = SFH_GetArgumentAsText(exd, opShort, offset + 4, defValue = SF_OP_APFREQUENCY_X_TIME, allowedValues = {SF_OP_APFREQUENCY_X_TIME, SF_OP_APFREQUENCY_X_COUNT}) + + return [method, level, timeFreq, normalize, xAxisType] +End + // apfrequency(data, [frequency calculation method], [spike detection crossing level], [result value type], [normalize], [x-axis type]) Function/WAVE SFO_OperationApFrequency(STRUCT SF_ExecutionData &exd) @@ -249,19 +268,7 @@ Function/WAVE SFO_OperationApFrequency(STRUCT SF_ExecutionData &exd) SFH_ASSERT(numArgs >= numArgsMin, "ApFrequency needs at least " + num2istr(numArgsMin) + " argument(s).") WAVE/WAVE input = SF_ResolveDatasetFromJSON(exd, 0) - method = SFH_GetArgumentAsNumeric(exd, opShort, 1, defValue = SF_APFREQUENCY_FULL, allowedValues = {SF_APFREQUENCY_FULL, SF_APFREQUENCY_INSTANTANEOUS, SF_APFREQUENCY_APCOUNT, SF_APFREQUENCY_INSTANTANEOUS_PAIR}) - level = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 0) - timeFreq = SFH_GetArgumentAsText(exd, opShort, 3, defValue = SF_OP_APFREQUENCY_Y_FREQ, allowedValues = {SF_OP_APFREQUENCY_Y_TIME, SF_OP_APFREQUENCY_Y_FREQ}) - normalize = SFH_GetArgumentAsText(exd, opShort, 4, defValue = SF_OP_APFREQUENCY_NONORM, allowedValues = { \ - SF_OP_APFREQUENCY_NONORM, \ - SF_OP_APFREQUENCY_NORMOVERSWEEPSMIN, \ - SF_OP_APFREQUENCY_NORMOVERSWEEPSMAX, \ - SF_OP_APFREQUENCY_NORMOVERSWEEPSAVG, \ - SF_OP_APFREQUENCY_NORMWITHINSWEEPMIN, \ - SF_OP_APFREQUENCY_NORMWITHINSWEEPMAX, \ - SF_OP_APFREQUENCY_NORMWITHINSWEEPAVG \ - }) - xAxisType = SFH_GetArgumentAsText(exd, opShort, 5, defValue = SF_OP_APFREQUENCY_X_TIME, allowedValues = {SF_OP_APFREQUENCY_X_TIME, SF_OP_APFREQUENCY_X_COUNT}) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 1) WAVE/T argSetup = SFH_GetNewArgSetupWave(numArgsMax - 1) From baff74133b012ab52377d570ad0becc1e60db42e Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 15 Dec 2025 20:47:25 +0100 Subject: [PATCH 02/34] SF: Add operation ivscc_apfrequency - Add plot property support for axisOffsets and axisPercent An operation can set these through SF_META_XAXISOFFSET, SF_META_YAXISOFFSET, SF_META_XAXISPERCENT and SF_META_YAXISPERCENT in the JSON wave note of the result wave. The plotter uses the settings from the last result in a "with" block. e.g. op_that_sets_axisoffet() with 1 would ignore the plot properties set by the first operation, whereas 1 with op_that_sets_axisoffet() would apply it. --- Packages/MIES/MIES_Constants.ipf | 5 + Packages/MIES/MIES_SweepFormula.ipf | 66 ++++++-- Packages/MIES/MIES_SweepFormula_Executor.ipf | 3 + Packages/MIES/MIES_SweepFormula_Helpers.ipf | 30 ++-- .../MIES/MIES_SweepFormula_Operations.ipf | 151 ++++++++++++++++++ Packages/MIES/MIES_WaveDataFolderGetters.ipf | 4 +- 6 files changed, 222 insertions(+), 37 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 5cee83a1eb..720ca96f4f 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2147,6 +2147,10 @@ StrConstant SF_META_DONOTPLOT = "/DoNotPlot" // number, boolean, StrConstant SF_META_WINDOW_HOOK = "/WindowHook" // string StrConstant SF_META_FORMULA = "/Formula" // string StrConstant SF_META_PLOT = "/Plot" // number, boolean, defaults to false (0) +StrConstant SF_META_XAXISOFFSET = "/XAxisOffset" // number +StrConstant SF_META_YAXISOFFSET = "/YAxisOffset" // number +StrConstant SF_META_XAXISPERCENT = "/XAxisPercent" // number +StrConstant SF_META_YAXISPERCENT = "/YAxisPercent" // number /// A color group allows to have matching colors for sweep data with the same channel type/number and sweep. /// It is applied before the matching headstage/average colors in #SF_GetTraceColor(). @@ -2552,6 +2556,7 @@ StrConstant SF_OP_TPINST = "tpinst" StrConstant SF_OP_TPBASE = "tpbase" StrConstant SF_OP_TPFIT = "tpfit" StrConstant SF_OP_EXTRACT = "extract" +StrConstant SF_OP_IVSCCAPFREQUENCY = "ivscc_apfrequency" #ifdef AUTOMATED_TESTING StrConstant SF_OP_TESTOP = "testop" diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index c6c8ed7408..7761daed2d 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -129,7 +129,8 @@ Function/WAVE SF_GetNamedOperations() 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_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT} + SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT, \ + SF_OP_IVSCCAPFREQUENCY} #ifdef AUTOMATED_TESTING Make/FREE/T wtTest = {SF_OP_TESTOP} Concatenate/NP/T {wtTest}, wt @@ -152,19 +153,6 @@ Function/S SF_EscapeJsonPath(string str) return ReplaceString("/", str, "~1") End -/// @brief Retrieves the plot meta data from the JSON wave note or other sources and stores it in the plotMetaData wave -static Function/WAVE SF_FillPlotMetaData(WAVE wvYRef, variable useXLabel, string dataUnits) - - WAVE/T plotMetaData = GetSFPlotMetaData() - plotMetaData[%DATATYPE] = JWN_GetStringFromWaveNote(wvYRef, SF_META_DATATYPE) - plotMetaData[%OPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_OPSTACK) - plotMetaData[%ARGSETUPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_ARGSETUPSTACK) - plotMetaData[%XAXISLABEL] = SelectString(useXLabel, SF_XLABEL_USER, JWN_GetStringFromWaveNote(wvYRef, SF_META_XAXISLABEL)) - plotMetaData[%YAXISLABEL] = JWN_GetStringFromWaveNote(wvYRef, SF_META_YAXISLABEL) + dataUnits - - return plotMetaData -End - /// @brief transfer the wave scaling from one wave to another /// /// Note: wave scale transfer requires wave units for the first wave or second wave @@ -212,6 +200,23 @@ Function SF_FormulaWaveScaleTransfer(WAVE source, WAVE dest, variable dimSource, endswitch End +/// @brief Retrieves the plot meta data from the JSON wave note or other sources and stores it in the plotMetaData wave +static Function/WAVE SF_FillPlotMetaData(WAVE wvYRef, variable useXLabel, string dataUnits) + + WAVE/T plotMetaData = GetSFPlotMetaData() + plotMetaData[%DATATYPE] = JWN_GetStringFromWaveNote(wvYRef, SF_META_DATATYPE) + plotMetaData[%OPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_OPSTACK) + plotMetaData[%ARGSETUPSTACK] = JWN_GetStringFromWaveNote(wvYRef, SF_META_ARGSETUPSTACK) + plotMetaData[%XAXISLABEL] = SelectString(useXLabel, SF_XLABEL_USER, JWN_GetStringFromWaveNote(wvYRef, SF_META_XAXISLABEL)) + plotMetaData[%YAXISLABEL] = JWN_GetStringFromWaveNote(wvYRef, SF_META_YAXISLABEL) + dataUnits + plotMetaData[%XAXISOFFSET] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_XAXISOFFSET), "%f") + plotMetaData[%YAXISOFFSET] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_YAXISOFFSET), "%f") + plotMetaData[%XAXISPERCENT] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_XAXISPERCENT), "%f") + plotMetaData[%YAXISPERCENT] = num2str(JWN_GetNumberFromWaveNote(wvYRef, SF_META_YAXISPERCENT), "%f") + + return plotMetaData +End + static Function [WAVE/WAVE formulaResults, WAVE/T plotMetaData] SF_FillFormulaResults(WAVE/Z/WAVE wvYRef, WAVE/Z/WAVE wvXRef, string yFormula) variable i, numResultsY, numResultsX @@ -1499,6 +1504,29 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode SF_KillOldDataDisplayWindows(graph, winDisplayMode, wList, outputWindows) End +/// @brief Sets axis properties for plots of the SF formula plotter. The properties are stored in the plotMetaData wave. +static Function SF_SetAxisProperties(STRUCT SF_PlotterGraphStruct &pg) + + variable xaxisOffset, yaxisOffset, xaxisPercent, yaxisPercent + + xaxisOffset = str2num(pg.plotMetaData[%XAXISOFFSET]) + if(!IsNaN(xaxisOffset)) + ModifyGraph/W=$pg.win axOffset(bottom)=xaxisOffset + endif + yaxisOffset = str2num(pg.plotMetaData[%YAXISOFFSET]) + if(!IsNaN(yaxisOffset)) + ModifyGraph/W=$pg.win axOffset(left)=yaxisOffset + endif + xaxisPercent = str2num(pg.plotMetaData[%XAXISPERCENT]) + if(!IsNaN(xaxisPercent)) + ModifyGraph/W=$pg.win axisEnab(bottom)={0, xaxisPercent * PERCENT_TO_ONE} + endif + yaxisPercent = str2num(pg.plotMetaData[%YAXISPERCENT]) + if(!IsNaN(yaxisPercent)) + ModifyGraph/W=$pg.win axisEnab(left)={0, yaxisPercent * PERCENT_TO_ONE} + endif +End + static Function SF_FinishPlotWindow(STRUCT SF_PlotterGraphStruct &pg, WAVE/T winGraphs) variable formulasAreDifferent, numTableFormulas @@ -1511,7 +1539,11 @@ static Function SF_FinishPlotWindow(STRUCT SF_PlotterGraphStruct &pg, WAVE/T win endif if(pg.panelsCreated[%GRAPH]) + pg.win = winGraphs[GetNumberFromWaveNote(winGraphs, NOTE_INDEX) - 1] + + SF_SetAxisProperties(pg) + if(pg.showLegend) formulasAreDifferent = SF_AddPlotLegend(pg) endif @@ -2827,3 +2859,9 @@ Function SF_TableWindowHook(STRUCT WMWinHookStruct &s) return 0 End + +/// @brief Adds an expression to a formula string with the proper termination character +Function/S SF_AddExpressionToFormula(string formula, string expr) + + return formula + expr + SF_CHAR_CR +End diff --git a/Packages/MIES/MIES_SweepFormula_Executor.ipf b/Packages/MIES/MIES_SweepFormula_Executor.ipf index 241aad4c5f..ef8a21f198 100644 --- a/Packages/MIES/MIES_SweepFormula_Executor.ipf +++ b/Packages/MIES/MIES_SweepFormula_Executor.ipf @@ -547,6 +547,9 @@ Function/WAVE SFE_FormulaExecutor(STRUCT SF_ExecutionData &exd, [variable srcLoc case SF_OP_TABLE: WAVE out = SFO_OperationTable(exdop) break + case SF_OP_IVSCCAPFREQUENCY: + WAVE out = SFO_OperationIVSCCApFrequency(exdop) + break #ifdef AUTOMATED_TESTING case SF_OP_TESTOP: WAVE out = SFO_OperationTestop(exdop) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 846dcc62e0..e049ed3ba3 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -2178,27 +2178,6 @@ Function/WAVE SFH_GetDatasetArrayAsResolvedWaverefs(STRUCT SF_ExecutionData &exd return dataFromEachGroup End -/// @brief Executes a formula from within an operation with low overhead -/// - the currently active variable storage is used -/// - the formula string is not preprocessed -Function/WAVE SFH_ExecuteFormulaInternal(string graph, string formula) - - STRUCT SF_ExecutionData exd - variable jsonId, srcLocId - - exd.graph = graph - [jsonId, srcLocId] = SFP_ParseFormulaToJSON(formula) - exd.jsonId = jsonId - WAVE dataRef = SFE_FormulaExecutor(exd, srcLocId = srcLocId) - - JSON_Release(exd.jsonId) - JSON_Release(srcLocId) - - WAVE resolved = SF_ResolveDataset(dataRef) - - return resolved -End - /// @brief Adds a variable to the variable storage. If the variable already exists it is overwritten. Function SFH_AddVariableToStorage(string graph, string name, WAVE result) @@ -2220,3 +2199,12 @@ Function SFH_AddVariableToStorage(string graph, string name, WAVE result) JWN_SetNumberInWaveNote(result, SF_VARIABLE_MARKER, 1) varStorage[idx] = result End + +/// @brief Adds a variable to the variable storage from a given formula. If the variable already exists it is overwritten. +Function/WAVE SFH_AddVariableToStorageByFormula(string graph, string name, string formula, string opShort) + + WAVE/WAVE result = SFE_ExecuteFormula(formula, graph, preProcess = 0) + SFH_AddVariableToStorage(graph, name, SFH_GetOutputForExecutor(result, graph, opShort)) + + return result +End diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index f2e076be52..1fe51f12ff 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -30,6 +30,11 @@ static StrConstant SF_OP_APFREQUENCY_NONORM = "nonorm" static StrConstant SF_OP_APFREQUENCY_X_COUNT = "count" static StrConstant SF_OP_APFREQUENCY_X_TIME = "time" +static StrConstant SF_OP_IVSCCAPFREQUENCY_FIRST = "first" +static StrConstant SF_OP_IVSCCAPFREQUENCY_MIN = "min" +static StrConstant SF_OP_IVSCCAPFREQUENCY_MAX = "max" +static StrConstant SF_OP_IVSCCAPFREQUENCY_NONE = "none" + static StrConstant SF_OP_AVG_INSWEEPS = "in" static StrConstant SF_OP_AVG_OVERSWEEPS = "over" static StrConstant SF_OP_AVG_GROUPS = "group" @@ -2509,3 +2514,149 @@ Function/WAVE SFO_OperationTestop(STRUCT SF_ExecutionData &exd) return wv End #endif // AUTOMATED_TESTING + +/// @brief Sets the plot meta data for the ivscc_apfrequency operation +static Function SFO_OperationIVSCCApFrequencySetPlotProperties(WAVE wvY, variable xAxisPercentage, variable yAxisPercentage) + + JWN_SetNumberInWaveNote(wvY, SF_META_XAXISPERCENT, xAxisPercentage) + JWN_SetNumberInWaveNote(wvY, SF_META_YAXISPERCENT, yAxisPercentage) +End + +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage]) +Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_IVSCCAPFREQUENCY + variable numArgsMin = 0 + variable numArgsMax = 9 + string formula, expr, exprPart + variable i, numArgs, col, size, numExp + variable xAxisPercentage, yAxisPercentage + string xaxisOffset, yaxisOffset + variable method, level + string timeFreq, normalize, xAxisType + STRUCT RGBColor s + + SFH_ASSERT(BSP_IsSweepBrowser(exd.graph), "ivscc_apfrequency only works with sweepbrowser") + + numArgs = SFH_GetNumberOfArguments(exd) + SFH_ASSERT(numArgs <= numArgsMax, "ivscc_apfrequency has " + num2istr(numArgsMax) + " arguments at most.") + SFH_ASSERT(numArgs >= numArgsMin, "ivscc_apfrequency needs at least " + num2istr(numArgsMin) + " argument(s).") + + xaxisOffset = SFH_GetArgumentAsText(exd, opShort, 0, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 4) + + WAVE/T sweepMap = SB_GetSweepMap(exd.graph) + col = FindDimlabel(sweepMap, COLS, "FileName") + size = GetNumberFromWaveNote(sweepMap, NOTE_INDEX) + Duplicate/FREE/RMD=[0, size - 1][col] sweepMap, fileNames + WAVE/T uniqueFiles = GetUniqueEntries(fileNames, dontDuplicate = 1) + Sort uniqueFiles, uniqueFiles + numExp = DimSize(uniqueFiles, ROWS) + SFH_ASSERT(numExp > 0, "ivscc_apfrequency: data from at least one experiment has to be loaded") + + Make/FREE/T/N=(numExp) elems + + formula = "sel = select(selsweeps(), selstimset(\"*rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r" + for(i = 0; i < numExp; i += 1) + sprintf expr, "selexpAD%d = select(selexp(\"%s\"), $sel, selchannels(AD0), selrange(E1))", i, uniqueFiles[i] + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "selexpDA%d = select(selexp(\"%s\"), $sel, selchannels(DA0), selrange(E1))", i, uniqueFiles[i] + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "freq%d = apfrequency(data($selexpAD%d), %d, %f, %s, %s, %s)", i, i, method, level, timeFreq, normalize, xAxisType + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "current%d = max(data($selexpDA%d))", i, i + formula = SF_AddExpressionToFormula(formula, expr) + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + sprintf expr, "currentNorm%d = $current%d - extract($current%d, 0)", i, i, i + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + sprintf expr, "currentNorm%d = $current%d - min(merge($current%d))", i, i, i + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + sprintf expr, "currentNorm%d = $current%d - max(merge($current%d))", i, i, i + else // SF_OP_IVSCCAPFREQUENCY_NONE + sprintf expr, "currentNorm%d = $current%d", i, i + endif + formula = SF_AddExpressionToFormula(formula, expr) + endfor + + elems[] = "$freq" + num2istr(p) + expr = "ivsccavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + formula = SF_AddExpressionToFormula(formula, expr) + + elems[] = "$currentNorm" + num2istr(p) + expr = "ivscccurrentavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + formula = SF_AddExpressionToFormula(formula, expr) + + elems[] = "\"" + uniqueFiles[p] + "\"" + expr = "ivscc_apfrequency_explist = [" + TextWaveToList(elems, ",", trailSep = 0) + "]" + formula = SF_AddExpressionToFormula(formula, expr) + + WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) + Duplicate/FREE varStorage, varBackup + SFE_ExecuteVariableAssignments(exd.graph, formula) + + WAVE/WAVE varStorageOp = GetSFVarStorage(exd.graph) + WAVE wvResult = varStorageOp[%ivscc_apfrequency_explist] + + WAVE/WAVE plotAND = SFH_CreateSFRefWave(exd.graph, opShort, 1) + Make/FREE/WAVE/N=(numExp + 1, 2) plotWITH + SetDimlabel COLS, 0, FORMULAX, plotWITH + SetDimlabel COLS, 1, FORMULAY, plotWITH + plotAND[0] = plotWITH + + for(i = 0; i < numExp; i += 1) + if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + sprintf formula, "merge($freq%d - extract($freq%d, 0))", i, i + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + sprintf formula, "merge($freq%d - min(merge($freq%d)))", i, i + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + sprintf formula, "merge($freq%d - max(merge($freq%d)))", i, i + else // SF_OP_IVSCCAPFREQUENCY_NONE + sprintf formula, "merge($freq%d)", i + endif + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + [s] = GetTraceColor(i) + Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + JWN_SetWaveInWaveNote(wvY[0], SF_META_TRACECOLOR, traceColor) + JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 17) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, uniqueFiles[i]) + plotWITH[i][%FORMULAY] = wvY + SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) + sprintf formula, "merge($currentNorm%d)", i + plotWITH[i][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + endfor + + if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + formula = "merge($ivsccavg - extract($ivsccavg, 0))" + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + formula = "merge($ivsccavg - min(merge($ivsccavg)))" + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + formula = "merge($ivsccavg - max(merge($ivsccavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + formula = "merge($ivsccavg)" + endif + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") + plotWITH[i][%FORMULAY] = wvY + SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) + + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + formula = "merge($ivscccurrentavg - min(merge($ivscccurrentavg)))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + formula = "merge($ivscccurrentavg - max(merge($ivscccurrentavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + formula = "merge($ivscccurrentavg)" + endif + plotWITH[i][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + + Duplicate/O varBackup, varStorage + SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) + + JWN_SetNumberInWaveNote(plotAND, SF_META_PLOT, 1) + + return SFH_GetOutputForExecutor(plotAND, exd.graph, opShort) +End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index a656b51a1d..847043051e 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -9367,8 +9367,8 @@ End /// @brief Wave storing sf plot meta information per formularesult, filled in SF_GatherFormulaResults Function/WAVE GetSFPlotMetaData() - Make/FREE/T/N=(5) wv - SetDimensionLabels(wv, "DATATYPE;OPSTACK;ARGSETUPSTACK;XAXISLABEL;YAXISLABEL;", ROWS) + Make/FREE/T/N=(9) wv + SetDimensionLabels(wv, "DATATYPE;OPSTACK;ARGSETUPSTACK;XAXISLABEL;YAXISLABEL;XAXISOFFSET;YAXISOFFSET;XAXISPERCENT;YAXISPERCENT;", ROWS) return wv End From 07c37e3b4222fe1250ef4e25bf044eb8acdd7380 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 19 Dec 2025 16:46:23 +0100 Subject: [PATCH 03/34] SF: bugfix for avg operation with group mode The avg operation in group mode does a wavenote and meta data transfer. This transfer works only if the number of datasets in the output waves are equal to the number of datasets of the input waves. (If there would be less input than output waves, there would be no data for some output waves) The group mode did transfer always from the first group. If the first group contained less datasets than the group with the greatest number of datasets then the transfer failed. As the result of the group averaging always has the same number of datasets as the greatest group the wave note and meta data transfer is now done from the greatest input group. Since: 272c26b0 (SF: operation avg add mode to average over groups of data, 2025-11-12) --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 1fe51f12ff..8b24121ceb 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -525,7 +525,6 @@ Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) elseif(!CmpStr(mode, SF_OP_AVG_GROUPS)) WAVE/WAVE dataFromEachGroup = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 0, resolveSelect = 1) WAVE/WAVE averagedGroup = SFO_OperationAvgImplSweepGroups(dataFromEachGroup, exd.graph, opShort) - SFH_TransferFormulaDataWaveNoteAndMeta(dataFromEachGroup[0], averagedGroup, opShort, SF_DATATYPE_AVG) return SFH_GetOutputForExecutor(averagedGroup, exd.graph, opShort) else @@ -535,7 +534,7 @@ End static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSelection, string graph, string opShort) - variable numData, numMaxSweeps, numGroups, i, j + variable numData, numMaxSweeps, numGroups, i, j, maxIdx STRUCT RGBColor s [s] = GetTraceColorForAverage() @@ -543,7 +542,9 @@ static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSel numGroups = DimSize(sweepsFromEachSelection, ROWS) Make/FREE/D/N=(numGroups) sweepCnts = DimSize(sweepsFromEachSelection[p], ROWS) - numMaxSweeps = WaveMax(sweepCnts) + WaveStats/Q/M=1 sweepCnts + numMaxSweeps = V_max + maxIdx = V_maxRowLoc WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numMaxSweeps) for(i = 0; i < numMaxSweeps; i += 1) Make/FREE/WAVE/N=(numGroups) avgSet @@ -561,6 +562,7 @@ static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSel JWN_SetNumberInWaveNote(output[i], SF_META_TRACETOFRONT, 1) JWN_SetNumberInWaveNote(output[i], SF_META_LINESTYLE, 0) endfor + SFH_TransferFormulaDataWaveNoteAndMeta(sweepsFromEachSelection[maxIdx], output, opShort, SF_DATATYPE_AVG) return output End From d15a8897014f1e53e2c739876002433d8d6f81f4 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 22 Dec 2025 21:14:38 +0100 Subject: [PATCH 04/34] SF: Add transfer of plotting meta data in merge operation --- Packages/MIES/MIES_SweepFormula_Helpers.ipf | 11 +++++++++++ Packages/MIES/MIES_SweepFormula_Operations.ipf | 2 ++ 2 files changed, 13 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index e049ed3ba3..5f81c8d2bc 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -2208,3 +2208,14 @@ Function/WAVE SFH_AddVariableToStorageByFormula(string graph, string name, strin return result End + +/// @brief Copy plot meta data JSON properties from a source to a target wave +Function SFH_CopyPlotMetaData(WAVE input, WAVE output) + + WAVE/Z wv = JWN_GetNumericWaveFromWaveNote(input, SF_META_TRACECOLOR) + if(WaveExists(wv)) + JWN_SetWaveInWaveNote(output, SF_META_TRACECOLOR, wv) + endif + JWN_SetNumberInWaveNote(output, SF_META_TRACETOFRONT, JWN_GetNumberFromWaveNote(input, SF_META_TRACETOFRONT)) + JWN_SetNumberInWaveNote(output, SF_META_LINESTYLE, JWN_GetNumberFromWaveNote(input, SF_META_LINESTYLE)) +End diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 8b24121ceb..07e4792420 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -1579,6 +1579,8 @@ Function/WAVE SFO_OperationMerge(STRUCT SF_ExecutionData &exd) output[0] = content + SFH_CopyPlotMetaData(input[0], output[0]) + return SFH_GetOutputForExecutor(output, exd.graph, SF_OP_MERGE) End From 6c13177c8b37eac4b985733145fc3e54bf3ad5cb Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 23 Dec 2025 20:44:33 +0100 Subject: [PATCH 05/34] SF: Add bins mode to avg operation --- .../MIES/MIES_SweepFormula_Operations.ipf | 110 +++++++++++++++++- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 07e4792420..cc98fe7ab1 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -38,6 +38,7 @@ static StrConstant SF_OP_IVSCCAPFREQUENCY_NONE = "none" static StrConstant SF_OP_AVG_INSWEEPS = "in" static StrConstant SF_OP_AVG_OVERSWEEPS = "over" static StrConstant SF_OP_AVG_GROUPS = "group" +static StrConstant SF_OP_AVG_BINS = "bins" static StrConstant SF_OP_EPOCHS_TYPE_RANGE = "range" static StrConstant SF_OP_EPOCHS_TYPE_NAME = "name" @@ -500,13 +501,13 @@ End Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) - variable numArgs - string mode + variable numArgs, binWidth + string mode string opShort = SF_OP_AVG - numArgs = SFH_CheckArgumentCount(exd, opShort, 1, maxArgs = 2) + numArgs = SFH_CheckArgumentCount(exd, opShort, 1, maxArgs = 5) - mode = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_AVG_INSWEEPS, allowedValues = {SF_OP_AVG_INSWEEPS, SF_OP_AVG_OVERSWEEPS, SF_OP_AVG_GROUPS}) + mode = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_AVG_INSWEEPS, allowedValues = {SF_OP_AVG_INSWEEPS, SF_OP_AVG_OVERSWEEPS, SF_OP_AVG_GROUPS, SF_OP_AVG_BINS}) if(!CmpStr(mode, SF_OP_AVG_INSWEEPS) || !CmpStr(mode, SF_OP_AVG_OVERSWEEPS)) WAVE/WAVE input = SFH_GetArgumentAsWave(exd, opShort, 0, resolveSelect = 1) strswitch(mode) @@ -527,11 +528,108 @@ Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) WAVE/WAVE averagedGroup = SFO_OperationAvgImplSweepGroups(dataFromEachGroup, exd.graph, opShort) return SFH_GetOutputForExecutor(averagedGroup, exd.graph, opShort) - else - FATAL_ERROR("Unhandled avg operation mode") + elseif(!CmpStr(mode, SF_OP_AVG_BINS)) + WAVE/WAVE dataFromEachGroup = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 0, resolveSelect = 1) + WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 2) + WAVE binRange = wTmp[0] + SFH_ASSERT(DimSize(binRange, ROWS) == 2, "Expected range in the form of [start, end]") + SFH_ASSERT(binRange[1] > binRange[0], "The end of the bin range must be greater than the start") + binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 3, checkFunc = IsStrictlyPositiveAndFinite) + WAVE/WAVE binData = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 4, resolveSelect = 1) + SFH_ASSERT(DimSize(dataFromEachGroup, ROWS) == DimSize(binData, ROWS), "input data and bin data must have the same number of groups") + WAVE/WAVE averagedBins = SFO_OperationAvgImplBins(dataFromEachGroup, exd.graph, opShort, binData, binRange, binWidth) + return SFH_GetOutputForExecutor(averagedBins, exd.graph, opShort) endif End +static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, string opShort, WAVE/WAVE binData, WAVE binRange, variable binWidth) + + variable i, j, numBins, binStart, binEnd, numGroups, numDataSets, binValue, binPos, idx + STRUCT RGBColor s + + [s] = GetTraceColorForAverage() + Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + + numGroups = DimSize(input, ROWS) + binStart = binRange[0] + binEnd = binRange[1] + numBins = ceil((binEnd - binStart) / binWidth) + SFH_ASSERT(numBins < 1E6, "Maximum number of bins is 1E6.") + + // Gather + Make/FREE/WAVE/N=(numBins, numGroups) binnedPerGroup + for(i = 0; i < numGroups; i += 1) + WAVE/WAVE dataSets = input[i] + WAVE/WAVE binDataSets = binData[i] + numDataSets = DimSize(dataSets, ROWS) + SFH_ASSERT(numDataSets == DimSize(binDataSets, ROWS), "The number of datasets of the input and bins are not the same for group " + num2istr(i)) + for(j = 0; j < numDataSets; j += 1) + SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value") + binValue = WaveRef(binDataSets, row = j)[0] + if(binValue < binStart || binValue >= binEnd) + continue + endif + binPos = floor((binValue - binStart) / binWidth) + // Add to bin + WAVE/Z/WAVE wavesInBin = binnedPerGroup[binPos][i] + if(!WaveExists(wavesInBin)) + Make/FREE/WAVE wavesInBin = {dataSets[j]} + SetNumberInWaveNote(wavesInBin, NOTE_INDEX, 1) + binnedPerGroup[binPos][i] = wavesInBin + continue + endif + idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) + EnsureLargeEnoughWave(wavesInBin, indexShouldExist = idx) + wavesInBin[idx] = dataSets[j] + SetNumberInWaveNote(wavesInBin, NOTE_INDEX, idx + 1) + endfor + endfor + // avg bins with multiple filling + for(i = 0; i < numBins; i += 1) + for(j = 0; j < numGroups; j += 1) + if(!WaveExists(binnedPerGroup[i][j])) + continue + endif + WAVE/WAVE wavesInBin = binnedPerGroup[i][j] + idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) + Redimension/N=(idx) wavesInBin + if(idx == 1) + continue + endif + WAVE/WAVE avg = MIES_fWaveAverage(wavesInBin, 1, IGOR_TYPE_64BIT_FLOAT) + Redimension/N=(1) wavesInBin + wavesInBin[0] = avg[0] + endfor + endfor + // avg same bins + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numBins) + for(i = 0; i < numBins; i += 1) + Make/FREE/WAVE/N=(numGroups) sameBin + idx = 0 + for(j = 0; j < numGroups; j += 1) + if(!WaveExists(binnedPerGroup[i][j])) + continue + endif + WAVE/WAVE wavesInBin = binnedPerGroup[i][j] + sameBin[idx] = wavesInBin[0] + idx += 1 + endfor + if(idx == 0) + Make/FREE/D tmp = {NaN} + output[i] = tmp + else + Redimension/N=(idx) sameBin + WAVE/WAVE avg = MIES_fWaveAverage(sameBin, 1, IGOR_TYPE_64BIT_FLOAT) + output[i] = avg[0] + endif + JWN_SetWaveInWaveNote(output[i], SF_META_TRACECOLOR, traceColor) + JWN_SetNumberInWaveNote(output[i], SF_META_TRACETOFRONT, 1) + JWN_SetNumberInWaveNote(output[i], SF_META_LINESTYLE, 0) + endfor + + return output +End + static Function/WAVE SFO_OperationAvgImplSweepGroups(WAVE/WAVE sweepsFromEachSelection, string graph, string opShort) variable numData, numMaxSweeps, numGroups, i, j, maxIdx From a5a3911e63b1aee3ee73551a418c66889f79ce21 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 23 Dec 2025 21:12:50 +0100 Subject: [PATCH 06/34] SF: use avg bins mode in ivscc_apfrequency --- .../MIES/MIES_SweepFormula_Operations.ipf | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index cc98fe7ab1..c2bb512bbf 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2634,8 +2634,8 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) variable i, numArgs, col, size, numExp variable xAxisPercentage, yAxisPercentage string xaxisOffset, yaxisOffset - variable method, level - string timeFreq, normalize, xAxisType + variable method, level, binWidth, numBins + string timeFreq, normalize, xAxisType, freqList, currentList, expList, binList STRUCT RGBColor s SFH_ASSERT(BSP_IsSweepBrowser(exd.graph), "ivscc_apfrequency only works with sweepbrowser") @@ -2644,11 +2644,14 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) SFH_ASSERT(numArgs <= numArgsMax, "ivscc_apfrequency has " + num2istr(numArgsMax) + " arguments at most.") SFH_ASSERT(numArgs >= numArgsMin, "ivscc_apfrequency needs at least " + num2istr(numArgsMin) + " argument(s).") - xaxisOffset = SFH_GetArgumentAsText(exd, opShort, 0, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) - yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) - xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) - yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) - [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 4) + xaxisOffset = SFH_GetArgumentAsText(exd, opShort, 0, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) + xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) + WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 4) + WAVE binRange = wTmp[0] + binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 5, checkFunc = IsStrictlyPositiveAndFinite) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 6) WAVE/T sweepMap = SB_GetSweepMap(exd.graph) col = FindDimlabel(sweepMap, COLS, "FileName") @@ -2659,8 +2662,6 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) numExp = DimSize(uniqueFiles, ROWS) SFH_ASSERT(numExp > 0, "ivscc_apfrequency: data from at least one experiment has to be loaded") - Make/FREE/T/N=(numExp) elems - formula = "sel = select(selsweeps(), selstimset(\"*rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r" for(i = 0; i < numExp; i += 1) sprintf expr, "selexpAD%d = select(selexp(\"%s\"), $sel, selchannels(AD0), selrange(E1))", i, uniqueFiles[i] @@ -2683,16 +2684,21 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) formula = SF_AddExpressionToFormula(formula, expr) endfor - elems[] = "$freq" + num2istr(p) - expr = "ivsccavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + Make/FREE/T/N=(numExp) freqs, currents, exps + freqs[] = "$freq" + num2istr(p) + freqList = TextWaveToList(freqs, ",", trailSep = 0) + currents[] = "$currentNorm" + num2istr(p) + currentList = TextWaveToList(currents, ",", trailSep = 0) + exps[] = "\"" + uniqueFiles[p] + "\"" + expList = TextWaveToList(exps, ",", trailSep = 0) + + sprintf expr, "ivsccavg = avg([%s], bins, [%f,%f],%f,[%s])", freqList, binRange[0], binRange[1], binWidth, currentList formula = SF_AddExpressionToFormula(formula, expr) - elems[] = "$currentNorm" + num2istr(p) - expr = "ivscccurrentavg = avg([" + TextWaveToList(elems, ",", trailSep = 0) + "], group)" + sprintf expr, "ivscccurrentavg = avg([%s], bins, [%f,%f],%f,[%s])", currentList, binRange[0], binRange[1], binWidth, currentList formula = SF_AddExpressionToFormula(formula, expr) - elems[] = "\"" + uniqueFiles[p] + "\"" - expr = "ivscc_apfrequency_explist = [" + TextWaveToList(elems, ",", trailSep = 0) + "]" + sprintf expr, "ivscc_apfrequency_explist = [%s]", expList formula = SF_AddExpressionToFormula(formula, expr) WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) @@ -2741,7 +2747,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) endif WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") - plotWITH[i][%FORMULAY] = wvY + plotWITH[numExp][%FORMULAY] = wvY SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) @@ -2753,7 +2759,13 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) else // SF_OP_IVSCCAPFREQUENCY_NONE formula = "merge($ivscccurrentavg)" endif - plotWITH[i][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + + numBins = ceil((binRange[1] - binRange[0]) / binWidth) + Make/FREE/D/N=(numBins) binValues + binValues[] = binRange[0] + p * binWidth + binWidth / 2 + binList = NumericWaveToList(binValues, ",", format = "%f", trailSep = 0) + sprintf formula, "[%s]", binList + plotWITH[numExp][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) From e3e2b2ddd5a8bdebf5e34bfeb8fa67aa07cebd24 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 5 Feb 2026 15:38:28 +0100 Subject: [PATCH 07/34] SFO: Change minus,plus,mult,div operation for zero size datasets on both sides The operations return now a zero sized dataset if on both sides a zero sized dataset is given as input. --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index c2bb512bbf..2bef399f10 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2337,7 +2337,11 @@ static Function/WAVE SFO_IndexOverDataSetsForPrimitiveOperation(STRUCT SF_Execut WAVE/WAVE arg1 = SF_ResolveDatasetFromJSON(exd, 1) dataSetNum0 = DimSize(arg0, ROWS) dataSetNum1 = DimSize(arg1, ROWS) - SFH_ASSERT(dataSetNum0 > 0 && dataSetNum1 > 0, "No input data for " + opShort) + if(dataSetNum0 == 0 && dataSetNum1 == 0) + WAVE/WAVE output = SFH_CreateSFRefWave(exd.graph, opShort, 0) + return output + endif + SFH_ASSERT(dataSetNum0 > 0 && dataSetNum1 > 0, "No input data on one side for " + opShort) if(dataSetNum0 == dataSetNum1) WAVE/WAVE output = SFH_CreateSFRefWave(exd.graph, opShort, dataSetNum0) WAVE/WAVE input = arg0 From 5b2cf30b976be756548853da466c318b64719eed Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 5 Feb 2026 15:42:26 +0100 Subject: [PATCH 08/34] f ivscc_apfrequency - use *LP_Rheo* instead of *rheo* as stimset selection - better resilience where partial results are zero sized datasets --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 2bef399f10..2e21c969a2 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2666,7 +2666,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) numExp = DimSize(uniqueFiles, ROWS) SFH_ASSERT(numExp > 0, "ivscc_apfrequency: data from at least one experiment has to be loaded") - formula = "sel = select(selsweeps(), selstimset(\"*rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r" + formula = "sel = select(selsweeps(), selstimset(\"*LP_Rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r" for(i = 0; i < numExp; i += 1) sprintf expr, "selexpAD%d = select(selexp(\"%s\"), $sel, selchannels(AD0), selrange(E1))", i, uniqueFiles[i] formula = SF_AddExpressionToFormula(formula, expr) @@ -2729,11 +2729,13 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) sprintf formula, "merge($freq%d)", i endif WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) - [s] = GetTraceColor(i) - Make/FREE/W/U traceColor = {s.red, s.green, s.blue} - JWN_SetWaveInWaveNote(wvY[0], SF_META_TRACECOLOR, traceColor) - JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 17) - JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, uniqueFiles[i]) + if(DimSize(wvY, ROWS) > 0) + [s] = GetTraceColor(i) + Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + JWN_SetWaveInWaveNote(wvY[0], SF_META_TRACECOLOR, traceColor) + JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 17) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, uniqueFiles[i]) + endif plotWITH[i][%FORMULAY] = wvY SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) sprintf formula, "merge($currentNorm%d)", i @@ -2750,7 +2752,9 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) formula = "merge($ivsccavg)" endif WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) - JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") + if(DimSize(wvY, ROWS) > 0) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") + endif plotWITH[numExp][%FORMULAY] = wvY SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) From 51086c822278b1f7947b90573f71b5630a01bb5e Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 5 Feb 2026 17:15:49 +0100 Subject: [PATCH 09/34] f ivscc_apfrequency --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 2e21c969a2..b8c82a200e 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2628,13 +2628,13 @@ static Function SFO_OperationIVSCCApFrequencySetPlotProperties(WAVE wvY, variabl JWN_SetNumberInWaveNote(wvY, SF_META_YAXISPERCENT, yAxisPercentage) End -// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage]) +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, binRange, binWidth, method, level, timeFreq, normalize, xAxisType]) Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) string opShort = SF_OP_IVSCCAPFREQUENCY variable numArgsMin = 0 variable numArgsMax = 9 - string formula, expr, exprPart + string formula, expr variable i, numArgs, col, size, numExp variable xAxisPercentage, yAxisPercentage string xaxisOffset, yaxisOffset @@ -2756,7 +2756,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") endif plotWITH[numExp][%FORMULAY] = wvY - SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[i][%FORMULAY], xAxisPercentage, yAxisPercentage) + SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[numExp][%FORMULAY], xAxisPercentage, yAxisPercentage) if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" From d8a58862ab1d53a2b8fd5d21d81015711ae72818 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 6 Feb 2026 10:58:31 +0100 Subject: [PATCH 10/34] SFO: avg with mode bins: skip null wave datasets in gather step --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index b8c82a200e..133135a7bc 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -564,6 +564,9 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str numDataSets = DimSize(dataSets, ROWS) SFH_ASSERT(numDataSets == DimSize(binDataSets, ROWS), "The number of datasets of the input and bins are not the same for group " + num2istr(i)) for(j = 0; j < numDataSets; j += 1) + if(!WaveExists(dataSets[j])) + continue + endif SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value") binValue = WaveRef(binDataSets, row = j)[0] if(binValue < binStart || binValue >= binEnd) From 5904dfb7992931b67898194eee3dbb777fc6093b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 6 Feb 2026 18:50:42 +0100 Subject: [PATCH 11/34] SF Constants: Add meta constants for error bar information --- Packages/MIES/MIES_Constants.ipf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 720ca96f4f..896479091e 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2151,6 +2151,10 @@ StrConstant SF_META_XAXISOFFSET = "/XAxisOffset" // number StrConstant SF_META_YAXISOFFSET = "/YAxisOffset" // number StrConstant SF_META_XAXISPERCENT = "/XAxisPercent" // number StrConstant SF_META_YAXISPERCENT = "/YAxisPercent" // number +StrConstant SF_META_ERRORBARYPLUS = "/ErrorYPlus" // numeric wave +StrConstant SF_META_ERRORBARYMINUS = "/ErrorYMinus" // numeric wave +StrConstant SF_META_ERRORBARXPLUS = "/ErrorXPlus" // numeric wave +StrConstant SF_META_ERRORBARXMINUS = "/ErrorXMinus" // numeric wave /// A color group allows to have matching colors for sweep data with the same channel type/number and sweep. /// It is applied before the matching headstage/average colors in #SF_GetTraceColor(). From b08c9afa6fca090db109d0f01d84641b79fd6c6b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 6 Feb 2026 18:51:56 +0100 Subject: [PATCH 12/34] SFO: Add bins2 mode to avg operation with dbg output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds rank-based binning across experiments - Sort points within each experiment by x (current), low to high. - Build bins by index: bin 1 contains the lowest-x point from each experiment, bin 2 contains the next-lowest point from each experiment, etc. - This yields ~1 point per experiment per bin (e.g., 6 experiments → up to 6 points per bin). - Stopping rule - Stop creating bins once a bin would contain fewer than 2 total data points (i.e., if fewer than 2 experiments contribute a point at that rank). output: Bin y-value = mean frequency across the points in the bin with number of bins datasets, each dataset has a single number mean current across the points in the bin set as x-value in the wave note standard deviation in x and y from each bin is stored as error bar information in the wave note --- .../MIES/MIES_SweepFormula_Operations.ipf | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 133135a7bc..24d380e6c4 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -39,6 +39,7 @@ static StrConstant SF_OP_AVG_INSWEEPS = "in" static StrConstant SF_OP_AVG_OVERSWEEPS = "over" static StrConstant SF_OP_AVG_GROUPS = "group" static StrConstant SF_OP_AVG_BINS = "bins" +static StrConstant SF_OP_AVG_BINS2 = "bins2" static StrConstant SF_OP_EPOCHS_TYPE_RANGE = "range" static StrConstant SF_OP_EPOCHS_TYPE_NAME = "name" @@ -507,7 +508,7 @@ Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) numArgs = SFH_CheckArgumentCount(exd, opShort, 1, maxArgs = 5) - mode = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_AVG_INSWEEPS, allowedValues = {SF_OP_AVG_INSWEEPS, SF_OP_AVG_OVERSWEEPS, SF_OP_AVG_GROUPS, SF_OP_AVG_BINS}) + mode = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_AVG_INSWEEPS, allowedValues = {SF_OP_AVG_INSWEEPS, SF_OP_AVG_OVERSWEEPS, SF_OP_AVG_GROUPS, SF_OP_AVG_BINS, SF_OP_AVG_BINS2}) if(!CmpStr(mode, SF_OP_AVG_INSWEEPS) || !CmpStr(mode, SF_OP_AVG_OVERSWEEPS)) WAVE/WAVE input = SFH_GetArgumentAsWave(exd, opShort, 0, resolveSelect = 1) strswitch(mode) @@ -539,9 +540,140 @@ Function/WAVE SFO_OperationAvg(STRUCT SF_ExecutionData &exd) SFH_ASSERT(DimSize(dataFromEachGroup, ROWS) == DimSize(binData, ROWS), "input data and bin data must have the same number of groups") WAVE/WAVE averagedBins = SFO_OperationAvgImplBins(dataFromEachGroup, exd.graph, opShort, binData, binRange, binWidth) return SFH_GetOutputForExecutor(averagedBins, exd.graph, opShort) + elseif(!CmpStr(mode, SF_OP_AVG_BINS2)) + WAVE/WAVE dataFromEachGroup = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 0, resolveSelect = 1) + WAVE/WAVE binData = SFH_GetDatasetArrayAsResolvedWaverefs(exd, 2, resolveSelect = 1) + SFH_ASSERT(DimSize(dataFromEachGroup, ROWS) == DimSize(binData, ROWS), "input data and bin data must have the same number of groups") + WAVE/WAVE averagedBins = SFO_OperationAvgImplBins2(dataFromEachGroup, exd.graph, opShort, binData) + return SFH_GetOutputForExecutor(averagedBins, exd.graph, opShort) endif End +static Function/WAVE SFO_OperationAvgImplBins2(WAVE/WAVE input, string graph, string opShort, WAVE/WAVE binData) + + variable i, j, maxBins, numGroups, numDataSets, idx, numBins, numEntries, size, xValue, xSdev, ySdev + STRUCT RGBColor s + + [s] = GetTraceColorForAverage() + Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + + numGroups = DimSize(input, ROWS) + + // Sort + Make/FREE/WAVE/N=(numGroups) sortedDatasetGroups, sortedBinDatasets + for(i = 0; i < numGroups; i += 1) + WAVE/WAVE dataSets = input[i] + WAVE/WAVE binDataSets = binData[i] + + numDataSets = DimSize(dataSets, ROWS) + printf "Group %d, num datasets %d\r", i, numDataSets + SFH_ASSERT(numDataSets == DimSize(binDataSets, ROWS), "The number of datasets of the input and bins are not the same for group " + num2istr(i)) + Make/FREE/D/N=(numDataSets) sortedKey + for(j = 0; j < numDataSets; j += 1) + SFH_ASSERT(WaveExists(binDataSets[j]), "A bin dataset is null") + SFH_ASSERT(IsNumericWave(binDataSets[j]), "A bin dataset must be numeric") + SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value") + sortedKey[j] = WaveRef(binDataSets, row = j)[0] + endfor + + Duplicate/FREE/WAVE dataSets, sortedDatasets + Sort sortedKey, sortedKey, sortedDatasets + sortedDatasetGroups[i] = sortedDatasets + sortedBinDatasets[i] = sortedKey + maxBins = max(maxBins, numDataSets) + endfor + + // Gather + Make/FREE/WAVE/N=(maxBins) filledBins, xValuesBin + for(i = 0; i < numGroups; i += 1) + WAVE/WAVE sortedDatasets = sortedDatasetGroups[i] + WAVE binXValues = sortedBinDatasets[i] + numDataSets = DimSize(sortedDatasets, ROWS) + for(j = 0; j < numDataSets; j += 1) + if(!WaveExists(sortedDatasets[j])) + continue + endif + // Add to bin + WAVE/Z/WAVE wavesInBin = filledBins[j] + if(!WaveExists(wavesInBin)) + Make/FREE/WAVE wavesInBin = {sortedDatasets[j]} + SetNumberInWaveNote(wavesInBin, NOTE_INDEX, 1) + filledBins[j] = wavesInBin + Make/FREE/D xValues = {binXValues[j]} + SetNumberInWaveNote(xValues, NOTE_INDEX, 1) + xValuesBin[j] = xValues + continue + endif + idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) + printf "Group %d, Bin %d, add at index %d\r", i, j, idx + WAVE xValues = xValuesBin[j] + EnsureLargeEnoughWave(wavesInBin, indexShouldExist = idx) + EnsureLargeEnoughWave(xValues, indexShouldExist = idx) + wavesInBin[idx] = sortedDatasets[j] + xValues[idx] = binXValues[j] + SetNumberInWaveNote(wavesInBin, NOTE_INDEX, idx + 1) + SetNumberInWaveNote(xValues, NOTE_INDEX, idx + 1) + endfor + endfor + + for(i = 0; i < maxBins; i += 1) + if(WaveExists(filledBins[i])) + printf "Bin %d, filling %d\r", i, GetNumberFromWaveNote(filledBins[i], NOTE_INDEX) + else + printf "Bin %d, filling %d\r", i, 0 + endif + endfor + + // Cutoff + for(i = 0; i < maxBins; i += 1) + if(!WaveExists(filledBins[i])) + break + endif + if(GetNumberFromWaveNote(filledBins[i], NOTE_INDEX) < 2) + break + endif + endfor + numBins = i + printf "Cutoff after bin %d\r", i - 1 + Redimension/N=(numBins) filledBins, xValuesBin + + // avg same bins + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numBins) + for(i = 0; i < numBins; i += 1) + WAVE/WAVE wavesInBin = filledBins[i] + numEntries = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) + Redimension/N=(numEntries) wavesInBin + + WAVE/WAVE avg = MIES_fWaveAverage(wavesInBin, 1, IGOR_TYPE_64BIT_FLOAT) + output[i] = avg[0] + + size = DimSize(wavesInBin, ROWS) + Make/FREE/D/N=(size) valuesFromBin = WaveRef(wavesInBin, row = p)[0] + WaveStats/Q valuesFromBin + ySdev = V_sdev + + WAVE xValues = xValuesBin[i] + Redimension/N=(numEntries) xValues + xValue = mean(xValues) + WaveStats/Q xValues + xSdev = V_sdev + + WAVE wTmp = output[i] + printf "Bin: %d Avg result: %f, xValue: %f, ySdev: %f, xSdev: %f\r", i, wTmp[0], xValue, ySdev, xSdev + + JWN_SetWaveInWaveNote(output[i], SF_META_TRACECOLOR, traceColor) + JWN_SetNumberInWaveNote(output[i], SF_META_TRACETOFRONT, 1) + JWN_SetNumberInWaveNote(output[i], SF_META_LINESTYLE, 0) + JWN_SetWaveInWaveNote(output[i], SF_META_XVALUES, {xValue}) + JWN_SetWaveInWaveNote(output[i], SF_META_ERRORBARYPLUS, {ySdev}) + JWN_SetWaveInWaveNote(output[i], SF_META_ERRORBARYMINUS, {ySdev}) + JWN_SetWaveInWaveNote(output[i], SF_META_ERRORBARXPLUS, {xSdev}) + JWN_SetWaveInWaveNote(output[i], SF_META_ERRORBARXMINUS, {xSdev}) + endfor + + return output +End + static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, string opShort, WAVE/WAVE binData, WAVE binRange, variable binWidth) variable i, j, numBins, binStart, binEnd, numGroups, numDataSets, binValue, binPos, idx From ac242088887f143c13c5c620b9d9297b8973fae2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 9 Feb 2026 16:15:50 +0100 Subject: [PATCH 13/34] SF: Add two more checks for avg bin mode for bin datasets --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 24d380e6c4..79688d9bcc 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -699,6 +699,8 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str if(!WaveExists(dataSets[j])) continue endif + SFH_ASSERT(WaveExists(binDataSets[j]), "A bin dataset is null") + SFH_ASSERT(IsNumericWave(binDataSets[j]), "A bin dataset must be numeric") SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value") binValue = WaveRef(binDataSets, row = j)[0] if(binValue < binStart || binValue >= binEnd) From bbd52b3efde1e64da131e23c4f74e6beba334538 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 9 Feb 2026 16:16:43 +0100 Subject: [PATCH 14/34] debug printouts --- .../MIES/MIES_SweepFormula_Operations.ipf | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 79688d9bcc..bdcfc8d74a 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -730,12 +730,20 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str WAVE/WAVE wavesInBin = binnedPerGroup[i][j] idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) Redimension/N=(idx) wavesInBin + printf "Bin %d, Group %d, NumWaves %d:\r", i, j, idx if(idx == 1) + WAVE wTmp = wavesInBin[0] + print "Result:", wTmp[0] + continue endif WAVE/WAVE avg = MIES_fWaveAverage(wavesInBin, 1, IGOR_TYPE_64BIT_FLOAT) Redimension/N=(1) wavesInBin wavesInBin[0] = avg[0] + + WAVE wTmp = avg[0] + print "Result:", wTmp[0] + endfor endfor // avg same bins @@ -751,6 +759,7 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str sameBin[idx] = wavesInBin[0] idx += 1 endfor + printf "Bin %d, NumWaves/TotalGroups %d/%d:\r", i, idx, numGroups if(idx == 0) Make/FREE/D tmp = {NaN} output[i] = tmp @@ -758,6 +767,9 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str Redimension/N=(idx) sameBin WAVE/WAVE avg = MIES_fWaveAverage(sameBin, 1, IGOR_TYPE_64BIT_FLOAT) output[i] = avg[0] + + WAVE wTmp = output[i] + printf "Avg result: %f\r", wTmp[0] endif JWN_SetWaveInWaveNote(output[i], SF_META_TRACECOLOR, traceColor) JWN_SetNumberInWaveNote(output[i], SF_META_TRACETOFRONT, 1) @@ -2833,7 +2845,8 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) exps[] = "\"" + uniqueFiles[p] + "\"" expList = TextWaveToList(exps, ",", trailSep = 0) - sprintf expr, "ivsccavg = avg([%s], bins, [%f,%f],%f,[%s])", freqList, binRange[0], binRange[1], binWidth, currentList + // sprintf expr, "ivsccavg = avg([%s], bins, [%f,%f],%f,[%s])", freqList, binRange[0], binRange[1], binWidth, currentList + sprintf expr, "ivsccavg = avg([%s], bins2, [%s])", freqList, currentList formula = SF_AddExpressionToFormula(formula, expr) sprintf expr, "ivscccurrentavg = avg([%s], bins, [%f,%f],%f,[%s])", currentList, binRange[0], binRange[1], binWidth, currentList @@ -2912,6 +2925,15 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) sprintf formula, "[%s]", binList plotWITH[numExp][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + WAVE/WAVE wTmp = SF_ResolveDataset(varStorage[%ivsccavg]) + printf "ivsccavg: " + for(WAVE wElem : wTmp) + printf "%f, ", wElem[0] + endfor + printf "\r" + WAVE wTmp = SF_ResolveDataset(varStorage[%ivscccurrentavg]) + print "ivscccurrentavg: \r", wTmp + Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) From 6c6c659ae8f97a17dd69c2d5b51d2e5100b17d8b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 10 Feb 2026 16:44:05 +0100 Subject: [PATCH 15/34] SF: Extend merge operation to merge xValues and errorbar information --- .../MIES/MIES_SweepFormula_Operations.ipf | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index bdcfc8d74a..f9f67d41eb 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -1792,6 +1792,7 @@ End Function/WAVE SFO_OperationMerge(STRUCT SF_ExecutionData &exd) variable numElements, numOutputDatasets, wvType + string errorTag SFH_CheckArgumentCount(exd, SF_OP_MERGE, 1, maxArgs = 1) WAVE/WAVE inputWithNull = SF_ResolveDatasetFromJSON(exd, 0) @@ -1824,6 +1825,18 @@ Function/WAVE SFO_OperationMerge(STRUCT SF_ExecutionData &exd) contentTxt[] = WaveText(WaveRef(input[p]), row = 0) endif + WAVE/Z mergedX = SFO_OperationMergeXValues(input) + if(WaveExists(mergedX)) + JWN_SetWaveInWaveNote(content, SF_META_XVALUES, mergedX) + endif + Make/FREE/T errorBarTags = {SF_META_ERRORBARYPLUS, SF_META_ERRORBARYMINUS, SF_META_ERRORBARXPLUS, SF_META_ERRORBARXMINUS} + for(string errorTag : errorBarTags) + WAVE/Z errorbarMerged = SFO_OperationMergeErrorBars(input, errorTag) + if(WaveExists(errorbarMerged)) + JWN_SetWaveInWaveNote(content, errorTag, errorbarMerged) + endif + endfor + output[0] = content SFH_CopyPlotMetaData(input[0], output[0]) @@ -1831,6 +1844,58 @@ Function/WAVE SFO_OperationMerge(STRUCT SF_ExecutionData &exd) return SFH_GetOutputForExecutor(output, exd.graph, SF_OP_MERGE) End +static Function/WAVE SFO_OperationMergeXValues(WAVE/WAVE input) + + variable numElements, i + + numElements = DimSize(input, ROWS) + + SFH_ASSERT(DimSize(input, ROWS) > 0, "input must have at least one dataset") + WAVE/Z xWave = JWN_GetNumericWaveFromWaveNote(input[0], SF_META_XVALUES) + if(!WaveExists(xWave)) + return $"" + endif + + SFH_ASSERT(DimSize(xWave, ROWS) == 1, "xValues must be only a one element") + Make/FREE/D/N=(numElements) mergedX + mergedX[0] = xWave[0] + if(numElements > 1) + for(i = 1; i < numElements; i += 1) + WAVE/Z xWave = JWN_GetNumericWaveFromWaveNote(input[i], SF_META_XVALUES) + SFH_ASSERT(WaveExists(xWave), "Can not merge xValues because only some datasets have xValues") + SFH_ASSERT(DimSize(xWave, ROWS) == 1, "xValues must be only a one element") + mergedX[i] = xWave[0] + endfor + endif + + return mergedX +End + +static Function/WAVE SFO_OperationMergeErrorBars(WAVE/WAVE input, string metaTag) + + variable numElements, i + + numElements = DimSize(input, ROWS) + + SFH_ASSERT(DimSize(input, ROWS) > 0, "input must have at least one dataset") + + Make/FREE/D/N=(numElements) mergedError + FastOp mergedError = (NaN) + for(i = 0; i < numElements; i += 1) + + WAVE/Z errorbar = JWN_GetNumericWaveFromWaveNote(input[i], metaTag) + if(WaveExists(errorbar)) + SFH_ASSERT(DimSize(errorbar, ROWS) == 1, "errorBar wave must be only a one element") + mergedError[i] = errorbar[0] + endif + endfor + if(!HasOneFiniteEntry(mergedError)) + return $"" + endif + + return mergedError +End + Function/WAVE SFO_OperationMin(STRUCT SF_ExecutionData &exd) WAVE/WAVE input = SFO_GetNumericVarArgs(exd, SF_OP_MIN) From f488eb666b840d556b1cf4b06fb1d48155c0d6b6 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 10 Feb 2026 23:49:45 +0100 Subject: [PATCH 16/34] SF: Add support to plot errorbars on traces in SF plotter --- Packages/MIES/MIES_SweepFormula.ipf | 83 +++++++++++++++++++- Packages/MIES/MIES_WaveDataFolderGetters.ipf | 35 +++++++++ 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 7761daed2d..d76db5c8ce 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -973,6 +973,7 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable STRUCT RGBColor color variable numTraces, yPoints, xPoints, yMxN, xMxN, idx, splitTraces variable i, isCategoryAxis, splitX, splitY + variable rangeBeginX, rangeEndX, rangeBeginY, rangeEndY string info WAVE/Z wvX = $"" @@ -1040,6 +1041,7 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i]) endfor elseif((xMxN == 1) && (yMxN == 1)) // 1D if(yPoints == 1) // 0D vs 1D @@ -1051,6 +1053,7 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][0]/TN=$traces[i] vs wvX[i][] annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i], rangeBeginX = i, rangeEndX = i) endfor elseif(xPoints == 1) // 1D vs 0D numTraces = yPoints @@ -1061,8 +1064,10 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[i][]/TN=$traces[i] vs wvX[][0] annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i], rangeBeginY = i, rangeEndY = i) endfor else // 1D vs 1D + splitTraces = min(yPoints, xPoints) numTraces = floor(max(yPoints, xPoints) / splitTraces) SF_CheckNumTraces(pg.graph, numTraces) @@ -1091,10 +1096,16 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable endif SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) - splitY = SF_SplitPlotting(wvY, ROWS, i, splitTraces) - splitX = SF_SplitPlotting(wvX, ROWS, i, splitTraces) - AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[splitY, splitY + splitTraces - 1][0]/TN=$traces[i] vs wvX[splitX, splitX + splitTraces - 1][0] + splitY = SF_SplitPlotting(wvY, ROWS, i, splitTraces) + splitX = SF_SplitPlotting(wvX, ROWS, i, splitTraces) + rangeBeginX = splitX + rangeEndX = splitX + splitTraces - 1 + rangeBeginY = splitY + rangeEndY = splitY + splitTraces - 1 + AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[rangeBeginY, rangeEndY][0]/TN=$traces[i] vs wvX[rangeBeginX, rangeEndX][0] annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i], rangeBeginX = rangeBeginX, rangeEndX = rangeEndX, rangeBeginY = rangeBeginY, rangeEndY = rangeEndY) endfor endif elseif(yMxN == 1) // 1D vs 2D @@ -1106,6 +1117,7 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][0]/TN=$traces[i] vs wvX[][i] annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i]) endfor elseif(xMxN == 1) // 2D vs 1D or 0D if(xPoints == 1) // 2D vs 0D -> extend X to 1D with constant value @@ -1121,6 +1133,7 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable SF_CollectTraceData(gdIndex, plotFormData, traces[i], wvX, wvY) AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] vs wvX annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i]) endfor else // 2D vs 2D numTraces = WaveExists(wvX) ? max(1, max(yMxN, xMxN)) : max(1, yMxN) @@ -1141,6 +1154,7 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable AppendTograph/W=$pg.win/C=(color.red, color.green, color.blue) wvY[][i]/TN=$traces[i] endif annotation += SF_GetMetaDataAnnotationText(pg.plotMetaData, wvResultY, traces[i]) + SF_AddErrorBars(pg.graph, pg.win, wvY, traces[i]) endfor endif @@ -1151,6 +1165,69 @@ static Function [variable dataCnt, variable gdIndex, string annotation, variable return [dataCnt, gdIndex, annotation, formulaAddedOncePerDataset] End +static Function SF_AddErrorBars(string graph, string win, WAVE wvY, string traceName, [variable rangeBeginX, variable rangeEndX, variable rangeBeginY, variable rangeEndY]) + + rangeBeginX = ParamIsDefault(rangeBeginX) ? 0 : rangeBeginX + rangeEndX = ParamIsDefault(rangeEndX) ? Inf : rangeEndX + rangeBeginY = ParamIsDefault(rangeBeginY) ? 0 : rangeBeginY + rangeEndY = ParamIsDefault(rangeEndY) ? Inf : rangeEndY + + DFREF dfr = SF_GetBrowserDF(graph) + + WAVE/Z errorbarYPlus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARYPLUS) + if(WaveExists(errorbarYPlus)) + WAVE wvYplusPermanent = GetSweepFormulaErrorbar(dfr, traceName, SF_META_ERRORBARYPLUS) + Duplicate/O errorbarYPlus, wvYplusPermanent + endif + WAVE/Z errorbarYMinus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARYMINUS) + if(WaveExists(errorbarYMinus)) + WAVE wvYminusPermanent = GetSweepFormulaErrorbar(dfr, traceName, SF_META_ERRORBARYMINUS) + Duplicate/O errorbarYMinus, wvYminusPermanent + endif + WAVE/Z errorbarXPlus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARXPLUS) + if(WaveExists(errorbarXPlus)) + WAVE wvXplusPermanent = GetSweepFormulaErrorbar(dfr, traceName, SF_META_ERRORBARXPLUS) + Duplicate/O errorbarXPlus, wvXplusPermanent + endif + WAVE/Z errorbarXMinus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARXMINUS) + if(WaveExists(errorbarXMinus)) + WAVE wvXminusPermanent = GetSweepFormulaErrorbar(dfr, traceName, SF_META_ERRORBARXMINUS) + Duplicate/O errorbarXMinus, wvXminusPermanent + endif + + if(!WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && !WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + return NaN + endif + + if(WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(wvXplusPermanent[rangeBeginX, rangeEndX], wvXminusPermanent[rangeBeginX, rangeEndX]), wave=(wvYplusPermanent[rangeBeginY, rangeEndY], wvYminusPermanent[rangeBeginY, rangeEndY]) + elseif(WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(wvXplusPermanent[rangeBeginX, rangeEndX],), wave=(wvYplusPermanent[rangeBeginY, rangeEndY], wvYminusPermanent[rangeBeginY, rangeEndY]) + elseif(WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && !WaveExists(errorbarXPlus) && WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(, wvXminusPermanent[rangeBeginX, rangeEndX]), wave=(wvYplusPermanent[rangeBeginY, rangeEndY], wvYminusPermanent[rangeBeginY, rangeEndY]) + elseif(WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && !WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, Y, wave=(wvYplusPermanent[rangeBeginY, rangeEndY], wvYminusPermanent[rangeBeginY, rangeEndY]) + elseif(WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(wvXplusPermanent[rangeBeginX, rangeEndX], wvXminusPermanent[rangeBeginX, rangeEndX]), wave=(wvYplusPermanent[rangeBeginY, rangeEndY],) + elseif(WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(wvXplusPermanent[rangeBeginX, rangeEndX],), wave=(wvYplusPermanent[rangeBeginY, rangeEndY],) + elseif(WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && !WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, Y, wave=(wvYplusPermanent[rangeBeginY, rangeEndY],) + elseif(!WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(wvXplusPermanent[rangeBeginX, rangeEndX], wvXminusPermanent[rangeBeginX, rangeEndX]), wave=(, wvYminusPermanent[rangeBeginY, rangeEndY]) + elseif(!WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, XY, wave=(wvXplusPermanent[rangeBeginX, rangeEndX],), wave=(, wvYminusPermanent[rangeBeginY, rangeEndY]) + elseif(!WaveExists(errorbarYPlus) && WaveExists(errorbarYMinus) && !WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, Y, wave=(wvYminusPermanent[rangeBeginY, rangeEndY],) + elseif(!WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, X, wave=(wvXplusPermanent[rangeBeginX, rangeEndX], wvXminusPermanent[rangeBeginX, rangeEndX]) + elseif(!WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && WaveExists(errorbarXPlus) && !WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, X, wave=(wvXplusPermanent[rangeBeginX, rangeEndX],) + elseif(!WaveExists(errorbarYPlus) && !WaveExists(errorbarYMinus) && !WaveExists(errorbarXPlus) && WaveExists(errorbarXMinus)) + ErrorBars/W=$win $traceName, X, wave=(, wvXminusPermanent[rangeBeginX, rangeEndX]) + endif +End + static Function [variable dataCnt] SF_CreateTracesForResults(STRUCT SF_PlotterGraphStruct &pg) variable i, idx, showInTable, numData, formulaAddedOncePerDataset diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index 847043051e..4af044aeb2 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -7664,6 +7664,41 @@ Function/WAVE GetSweepFormulaY(DFREF dfr, variable graphNr) return wv End +/// @brief Return a permanent wave for errorBars +Function/WAVE GetSweepFormulaErrorbar(DFREF dfr, string traceName, string tagName) + + string wName, suffix + + strswitch(tagName) + case SF_META_ERRORBARYPLUS: + suffix = "yplus" + break + case SF_META_ERRORBARYMINUS: + suffix = "yminus" + break + case SF_META_ERRORBARXPLUS: + suffix = "xplus" + break + case SF_META_ERRORBARXMINUS: + suffix = "xminus" + break + default: + FATAL_ERROR("Unknown errorbar tag") + endswitch + + wName = "sf_errorbar_" + traceName + "_" + suffix + + WAVE/Z/D/SDFR=dfr wv = $wName + + if(WaveExists(wv)) + return wv + endif + + Make/N=0/D dfr:$wName/WAVE=wv + + return wv +End + /// @brief Return the global temporary wave for extended popup menu Function/WAVE GetPopupExtMenuWave() From ac9853ebc59c0b12d240358c9f8889e1ee803f79 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 11 Feb 2026 14:37:55 +0100 Subject: [PATCH 17/34] fix avg bins2 mode xvalues --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index f9f67d41eb..dc276c7ab9 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -552,6 +552,7 @@ End static Function/WAVE SFO_OperationAvgImplBins2(WAVE/WAVE input, string graph, string opShort, WAVE/WAVE binData) variable i, j, maxBins, numGroups, numDataSets, idx, numBins, numEntries, size, xValue, xSdev, ySdev + string unit STRUCT RGBColor s [s] = GetTraceColorForAverage() @@ -574,6 +575,9 @@ static Function/WAVE SFO_OperationAvgImplBins2(WAVE/WAVE input, string graph, st SFH_ASSERT(IsNumericWave(binDataSets[j]), "A bin dataset must be numeric") SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value") sortedKey[j] = WaveRef(binDataSets, row = j)[0] + if(IsNull(unit)) + unit = WaveUnits(binDataSets[j], ROWS) + endif endfor Duplicate/FREE/WAVE dataSets, sortedDatasets @@ -671,6 +675,11 @@ static Function/WAVE SFO_OperationAvgImplBins2(WAVE/WAVE input, string graph, st JWN_SetWaveInWaveNote(output[i], SF_META_ERRORBARXMINUS, {xSdev}) endfor + if(IsEmpty(unit)) + unit = "x" + endif + JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, unit) + return output End From b8f55201013ed68988d5e9f8ce28df40a329a9eb Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 11 Feb 2026 14:42:39 +0100 Subject: [PATCH 18/34] SF: Extend xValues operation to return xValues from SF meta data if present --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index dc276c7ab9..9c8fdbd23e 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2540,6 +2540,15 @@ static Function/WAVE SFO_OperationXValuesImpl(WAVE/Z input) return $"" endif + WAVE/Z xValues = JWN_GetNumericWaveFromWaveNote(input, SF_META_XVALUES) + if(WaveExists(xValues)) + return xValues + endif + WAVE/Z/T xValuesT = JWN_GetTextWaveFromWaveNote(input, SF_META_XVALUES) + if(WaveExists(xValuesT)) + return xValuesT + endif + Make/FREE/D/N=(DimSize(input, ROWS), DimSize(input, COLS), DimSize(input, LAYERS), DimSize(input, CHUNKS)) output offset = DimOffset(input, ROWS) delta = DimDelta(input, ROWS) From 3d9be2405fff678235eaec8e961185ac55dd9ef3 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 11 Feb 2026 16:02:21 +0100 Subject: [PATCH 19/34] Add some more debug output to avg --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 9c8fdbd23e..5c28f1fda3 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -555,6 +555,8 @@ static Function/WAVE SFO_OperationAvgImplBins2(WAVE/WAVE input, string graph, st string unit STRUCT RGBColor s + printf "avg in bins2 mode:\r" + [s] = GetTraceColorForAverage() Make/FREE/W/U traceColor = {s.red, s.green, s.blue} @@ -606,6 +608,7 @@ static Function/WAVE SFO_OperationAvgImplBins2(WAVE/WAVE input, string graph, st Make/FREE/D xValues = {binXValues[j]} SetNumberInWaveNote(xValues, NOTE_INDEX, 1) xValuesBin[j] = xValues + printf "Group %d, Bin %d, add at index %d\r", i, j, 0 continue endif idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) @@ -688,6 +691,8 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str variable i, j, numBins, binStart, binEnd, numGroups, numDataSets, binValue, binPos, idx STRUCT RGBColor s + printf "avg in bins mode:\r" + [s] = GetTraceColorForAverage() Make/FREE/W/U traceColor = {s.red, s.green, s.blue} @@ -730,6 +735,7 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str SetNumberInWaveNote(wavesInBin, NOTE_INDEX, idx + 1) endfor endfor + printf "Averaging bins:\r" // avg bins with multiple filling for(i = 0; i < numBins; i += 1) for(j = 0; j < numGroups; j += 1) @@ -739,7 +745,7 @@ static Function/WAVE SFO_OperationAvgImplBins(WAVE/WAVE input, string graph, str WAVE/WAVE wavesInBin = binnedPerGroup[i][j] idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX) Redimension/N=(idx) wavesInBin - printf "Bin %d, Group %d, NumWaves %d:\r", i, j, idx + printf "Bin %d, Group %d, NumWaves %d, ", i, j, idx if(idx == 1) WAVE wTmp = wavesInBin[0] print "Result:", wTmp[0] From 81611f8319b3d8821ce030f6d7c86619342dd451 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 11 Feb 2026 16:03:38 +0100 Subject: [PATCH 20/34] SFO ivscc_apfrequency add bins2 mode --- .../MIES/MIES_SweepFormula_Operations.ipf | 119 ++++++++++++------ 1 file changed, 81 insertions(+), 38 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 5c28f1fda3..327264c032 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2866,18 +2866,22 @@ static Function SFO_OperationIVSCCApFrequencySetPlotProperties(WAVE wvY, variabl JWN_SetNumberInWaveNote(wvY, SF_META_YAXISPERCENT, yAxisPercentage) End -// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, binRange, binWidth, method, level, timeFreq, normalize, xAxisType]) +// for avgMode: bins +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, avgMode, binRange, binWidth, method, level, timeFreq, normalize, xAxisType]) +// +// for avgMode: bins2 +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, avgMode, method, level, timeFreq, normalize, xAxisType]) Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) string opShort = SF_OP_IVSCCAPFREQUENCY variable numArgsMin = 0 - variable numArgsMax = 9 + variable numArgsMax = 12 string formula, expr variable i, numArgs, col, size, numExp variable xAxisPercentage, yAxisPercentage string xaxisOffset, yaxisOffset variable method, level, binWidth, numBins - string timeFreq, normalize, xAxisType, freqList, currentList, expList, binList + string timeFreq, normalize, xAxisType, freqList, currentList, expList, binList, avgMode STRUCT RGBColor s SFH_ASSERT(BSP_IsSweepBrowser(exd.graph), "ivscc_apfrequency only works with sweepbrowser") @@ -2890,11 +2894,19 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) - WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 4) - WAVE binRange = wTmp[0] - binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 5, checkFunc = IsStrictlyPositiveAndFinite) - [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 6) + avgMode = SFH_GetArgumentAsText(exd, opShort, 4, defValue = SF_OP_AVG_BINS, allowedValues = {SF_OP_AVG_BINS, SF_OP_AVG_BINS2}) + if(!CmpStr(avgMode, SF_OP_AVG_BINS)) + WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 5) + WAVE binRange = wTmp[0] + binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 6, checkFunc = IsStrictlyPositiveAndFinite) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 7) + else + // SF_OP_AVG_BINS2 + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 5) + endif + + // create and evaluate variables WAVE/T sweepMap = SB_GetSweepMap(exd.graph) col = FindDimlabel(sweepMap, COLS, "FileName") size = GetNumberFromWaveNote(sweepMap, NOTE_INDEX) @@ -2934,20 +2946,27 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) exps[] = "\"" + uniqueFiles[p] + "\"" expList = TextWaveToList(exps, ",", trailSep = 0) - // sprintf expr, "ivsccavg = avg([%s], bins, [%f,%f],%f,[%s])", freqList, binRange[0], binRange[1], binWidth, currentList - sprintf expr, "ivsccavg = avg([%s], bins2, [%s])", freqList, currentList - formula = SF_AddExpressionToFormula(formula, expr) - - sprintf expr, "ivscccurrentavg = avg([%s], bins, [%f,%f],%f,[%s])", currentList, binRange[0], binRange[1], binWidth, currentList - formula = SF_AddExpressionToFormula(formula, expr) - sprintf expr, "ivscc_apfrequency_explist = [%s]", expList formula = SF_AddExpressionToFormula(formula, expr) + if(!CmpStr(avgMode, SF_OP_AVG_BINS)) + sprintf expr, "ivsccavg = avg([%s], bins, [%f,%f],%f,[%s])", freqList, binRange[0], binRange[1], binWidth, currentList + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "ivscccurrentavg = avg([%s], bins, [%f,%f],%f,[%s])", currentList, binRange[0], binRange[1], binWidth, currentList + formula = SF_AddExpressionToFormula(formula, expr) + else + // SF_OP_AVG_BINS2 + sprintf expr, "ivsccavg = avg([%s], bins2, [%s])", freqList, currentList + formula = SF_AddExpressionToFormula(formula, expr) + sprintf expr, "ivsccavg_xvalues = xvalues(merge($ivsccavg))" + formula = SF_AddExpressionToFormula(formula, expr) + endif + WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) Duplicate/FREE varStorage, varBackup SFE_ExecuteVariableAssignments(exd.graph, formula) + // build plot tree WAVE/WAVE varStorageOp = GetSFVarStorage(exd.graph) WAVE wvResult = varStorageOp[%ivscc_apfrequency_explist] @@ -2957,6 +2976,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) SetDimlabel COLS, 1, FORMULAY, plotWITH plotAND[0] = plotWITH + // apfrequency traces for(i = 0; i < numExp; i += 1) if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) sprintf formula, "merge($freq%d - extract($freq%d, 0))", i, i @@ -2972,7 +2992,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) [s] = GetTraceColor(i) Make/FREE/W/U traceColor = {s.red, s.green, s.blue} JWN_SetWaveInWaveNote(wvY[0], SF_META_TRACECOLOR, traceColor) - JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 17) + JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 19) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, uniqueFiles[i]) endif plotWITH[i][%FORMULAY] = wvY @@ -2981,6 +3001,8 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) plotWITH[i][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) endfor + // avg trace + if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) formula = "merge($ivsccavg - extract($ivsccavg, 0))" elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) @@ -2992,36 +3014,57 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) endif WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) if(DimSize(wvY, ROWS) > 0) - JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency average") + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency avg " + avgMode) endif + JWN_SetStringInWaveNote(wvY, SF_META_XAXISLABEL, "x value") // activates x values plotWITH[numExp][%FORMULAY] = wvY SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[numExp][%FORMULAY], xAxisPercentage, yAxisPercentage) - if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) - formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" - elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) - formula = "merge($ivscccurrentavg - min(merge($ivscccurrentavg)))" - elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) - formula = "merge($ivscccurrentavg - max(merge($ivscccurrentavg)))" - else // SF_OP_IVSCCAPFREQUENCY_NONE - formula = "merge($ivscccurrentavg)" - endif + if(!CmpStr(avgMode, SF_OP_AVG_BINS)) + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + formula = "merge($ivscccurrentavg - min(merge($ivscccurrentavg)))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + formula = "merge($ivscccurrentavg - max(merge($ivscccurrentavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + formula = "merge($ivscccurrentavg)" + endif - numBins = ceil((binRange[1] - binRange[0]) / binWidth) - Make/FREE/D/N=(numBins) binValues - binValues[] = binRange[0] + p * binWidth + binWidth / 2 - binList = NumericWaveToList(binValues, ",", format = "%f", trailSep = 0) - sprintf formula, "[%s]", binList + // numBins = ceil((binRange[1] - binRange[0]) / binWidth) + // Make/FREE/D/N=(numBins) binValues + // binValues[] = binRange[0] + p * binWidth + binWidth / 2 + // binList = NumericWaveToList(binValues, ",", format = "%f", trailSep = 0) + // sprintf formula, "[%s]", binList + else + // SF_OP_AVG_BINS2 + + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + // formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + formula = "$ivsccavg_xvalues - min($ivsccavg_xvalues)" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + formula = "$ivsccavg_xvalues - max($ivsccavg_xvalues)" + else // SF_OP_IVSCCAPFREQUENCY_NONE + formula = "$ivsccavg_xvalues" + endif + endif plotWITH[numExp][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) - WAVE/WAVE wTmp = SF_ResolveDataset(varStorage[%ivsccavg]) - printf "ivsccavg: " - for(WAVE wElem : wTmp) - printf "%f, ", wElem[0] - endfor - printf "\r" - WAVE wTmp = SF_ResolveDataset(varStorage[%ivscccurrentavg]) - print "ivscccurrentavg: \r", wTmp + if(!CmpStr(avgMode, SF_OP_AVG_BINS)) + WAVE/WAVE wTmp = SF_ResolveDataset(varStorage[%ivsccavg]) + printf "ivsccavg bins mode: " + for(WAVE wElem : wTmp) + printf "%f, ", wElem[0] + endfor + printf "\r" + printf "ivscccurrentavg: " + WAVE/WAVE wTmp = SF_ResolveDataset(varStorage[%ivscccurrentavg]) + for(WAVE wElem : wTmp) + printf "%f, ", wElem[0] + endfor + printf "\r" + endif Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) From a75c608ce014e4cc62e0d19cba9c48a4ea5e435b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 16 Feb 2026 16:25:05 +0100 Subject: [PATCH 21/34] Util: Add function to return a non-HS color for traces By definition the first NUM_HEADSTAGES colors are used to distinguish data from headstages. The following color is used for averaged traces. The new function GetTraceColorNonHeadstage returns a color that is not part of this pool. Also fixed a typo in the description for GetTraceColorForAverage --- Packages/MIES/MIES_Constants.ipf | 7 +++++++ Packages/MIES/MIES_GuiUtilities.ipf | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 896479091e..2783b50ba8 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2623,3 +2623,10 @@ StrConstant SF_WINNAME_SUFFIX_TABLE = "table" Constant SF_DISPLAYTYPE_GRAPH = 0 Constant SF_DISPLAYTYPE_TABLE = 1 ///@} + +/// @name Maximum number of colors for traces defined +/// +/// @anchor TraceColorMax +///@{ +Constant TRACECOLORMAX = 21 +///@} diff --git a/Packages/MIES/MIES_GuiUtilities.ipf b/Packages/MIES/MIES_GuiUtilities.ipf index cab833cefc..28bf71b9fc 100644 --- a/Packages/MIES/MIES_GuiUtilities.ipf +++ b/Packages/MIES/MIES_GuiUtilities.ipf @@ -650,7 +650,7 @@ End /// where the color white has been removed. Function [STRUCT RGBColor s] GetTraceColor(variable index) - index = mod(index, 21) + index = mod(index, TRACECOLORMAX) switch(index) case 0: s.red = 7967; s.green = 7710; s.blue = 7710 @@ -742,11 +742,21 @@ Function [STRUCT RGBColor s] GetTraceColor(variable index) endswitch End -/// @brief Returns the trace color used for avergae type traces +/// @brief Returns the trace color used for average type traces Function [STRUCT RGBColor s] GetTraceColorForAverage() [s] = GetTraceColor(NUM_HEADSTAGES + 1) +End + +/// @brief Returns a color that is not attributed to a headstage and not the average color +Function [STRUCT RGBColor s] GetTraceColorNonHeadstage(variable idx) + + variable range + + range = TRACECOLORMAX - (NUM_HEADSTAGES + 2) + idx = mod(idx, range) + [s] = GetTraceColor(idx + NUM_HEADSTAGES + 2) End /// @brief Get colors from alternative color scheme From ad44c085b3025d33b8650df47015c6732df149e1 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 16 Feb 2026 16:28:20 +0100 Subject: [PATCH 22/34] ivscc_apfrequency op: use 0.2 opacity for apfrequency traces and improve trace colors --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 327264c032..685ae0f3d1 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -65,6 +65,8 @@ static Constant SF_POWERSPECTRUM_RATIO_GAUSS_NUMCOEFS = 4 static StrConstant SF_AVERAGING_NONSWEEPDATA_LBL = "NOSWEEPDATA" +static Constant SF_IVSCC_APFREQUENCY_OPACITY = 13107 // 0.2 * 65535 + Function/WAVE SFO_OperationAnaFuncParam(STRUCT SF_ExecutionData &exd) SFH_CheckArgumentCount(exd, SF_OP_ANAFUNCPARAM, 0, maxArgs = 2) @@ -2989,8 +2991,8 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) endif WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) if(DimSize(wvY, ROWS) > 0) - [s] = GetTraceColor(i) - Make/FREE/W/U traceColor = {s.red, s.green, s.blue} + [s] = GetTraceColorNonHeadstage(i) + Make/FREE/W/U traceColor = {s.red, s.green, s.blue, SF_IVSCC_APFREQUENCY_OPACITY} JWN_SetWaveInWaveNote(wvY[0], SF_META_TRACECOLOR, traceColor) JWN_SetNumberInWaveNote(wvY[0], SF_META_MOD_MARKER, 19) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, uniqueFiles[i]) From 89292128bc9f88cf4eec3c918c13e1019c1ca759 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 19 Feb 2026 17:37:33 +0100 Subject: [PATCH 23/34] Util: Add utility functions IsIntegratedFitFunction and GetIntegratedFitFunctionCoefficientNumber The functions allow to programmatically retrieve information about Igors integrated fit functions. IsIntegratedFitFunction allows to check a fit function name if it matches an Igor integrated fit function. GetIntegratedFitFunctionCoefficientNumber returns the number of coefficients required by an integrated fit function. The wave getter GetSFIgorFitProperties can be extended with more information in additional columns if needed. --- Packages/MIES/MIES_Utilities_Checks.ipf | 33 ++++++++++++++++ Packages/MIES/MIES_WaveDataFolderGetters.ipf | 41 ++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/Packages/MIES/MIES_Utilities_Checks.ipf b/Packages/MIES/MIES_Utilities_Checks.ipf index 1bb8a291db..21498c6ce5 100644 --- a/Packages/MIES/MIES_Utilities_Checks.ipf +++ b/Packages/MIES/MIES_Utilities_Checks.ipf @@ -526,3 +526,36 @@ Function ListHasOnlyOneUniqueEntry(string list, [string sep]) return 1 End + +/// @brief Returns 1 if the given fitFuncName is an Igor integrated fit function, 0 otherwise +Function IsIntegratedFitFunction(string fitFuncName) + + variable col + + ASSERT(!IsEmpty(fitFuncName), "Expected non-empty fit func name") + + WAVE/T fitProps = GetSFIgorFitProperties() + + col = FindDimLabel(fitProps, COLS, "FITFUNC") + ASSERT(col >= 0, "Column not found") + FindValue/Z/TEXT=(fitFuncName)/TXOP=4/RMD=[][col] fitProps + + return V_value >= 0 +End + +/// @brief Returns the number of coefficients required for an integrated fit function +Function GetIntegratedFitFunctionCoefficientNumber(string fitFuncName) + + variable col + + ASSERT(!IsEmpty(fitFuncName), "Expected non-empty fit func name") + + WAVE/T fitProps = GetSFIgorFitProperties() + + col = FindDimLabel(fitProps, COLS, "FITFUNC") + ASSERT(col >= 0, "Column not found") + FindValue/Z/TEXT=(fitFuncName)/TXOP=4/RMD=[][col] fitProps + ASSERT(V_value >= 0, "Given fitFuncName is not an integrated Igor fit function") + + return str2num(fitProps[V_row][%NUMCOEFS]) +End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index 4af044aeb2..aee4930809 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -9440,3 +9440,44 @@ Function/WAVE GetFullPlottingWITH(variable size) return plotWITH End + +/// @brief Returns a wave with all Igor integrated fit functions and their respective number of coefficients +/// For poly functions upto 4th order is included (Igor Pro supports up to 20th order) +Function/WAVE GetSFIgorFitProperties() + + Make/FREE/T/N=(28, 2) wv + + wv[0][] = {{"gauss"}, {"4"}} + wv[1][] = {{"lor"}, {"4"}} + wv[2][] = {{"Voigt"}, {"5"}} + wv[3][] = {{"exp"}, {"3"}} + wv[4][] = {{"dblexp"}, {"5"}} + wv[5][] = {{"exp_XOffset"}, {"3"}} + wv[6][] = {{"dblexp_XOffset"}, {"5"}} + wv[7][] = {{"dblexp_peak"}, {"5"}} + wv[8][] = {{"sin"}, {"4"}} + wv[9][] = {{"line"}, {"2"}} + wv[10][] = {{"poly 1"}, {"1"}} + wv[11][] = {{"poly 2"}, {"2"}} + wv[12][] = {{"poly 3"}, {"3"}} + wv[13][] = {{"poly 4"}, {"4"}} + wv[14][] = {{"poly_XOffset 1"}, {"1"}} + wv[15][] = {{"poly_XOffset 2"}, {"2"}} + wv[16][] = {{"poly_XOffset 3"}, {"3"}} + wv[17][] = {{"poly_XOffset 4"}, {"4"}} + wv[18][] = {{"hillequation"}, {"4"}} + wv[19][] = {{"sigmoid"}, {"4"}} + wv[20][] = {{"power"}, {"3"}} + wv[21][] = {{"lognormal"}, {"4"}} + wv[22][] = {{"log"}, {"2"}} + wv[23][] = {{"gauss2D"}, {"7"}} + wv[24][] = {{"poly2D 1"}, {"3"}} + wv[25][] = {{"poly2D 2"}, {"6"}} + wv[26][] = {{"poly2D 3"}, {"10"}} + wv[27][] = {{"poly2D 4"}, {"15"}} + + SetDimLabel COLS, 0, FITFUNC, wv + SetDimLabel COLS, 1, NUMCOEFS, wv + + return wv +End From 98437b2a1a14fea6cd351357daebc21eaa194bf9 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 18 Feb 2026 17:25:23 +0100 Subject: [PATCH 24/34] SFP: Allow greater/lesser characters in strings in parser This allows to specify fit constraints in the form "K0 > 3" in a sweep formula. --- Packages/MIES/MIES_SweepFormula_Parser.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Parser.ipf b/Packages/MIES/MIES_SweepFormula_Parser.ipf index 764e8f59e5..5a57ebdc4f 100644 --- a/Packages/MIES/MIES_SweepFormula_Parser.ipf +++ b/Packages/MIES/MIES_SweepFormula_Parser.ipf @@ -42,7 +42,7 @@ static StrConstant SF_PARSER_REGEX_SIGNED_NUMBER = "^(?i)[+-]?[0-9]+(?:\.[0 static StrConstant SF_PARSER_REGEX_QUOTED_STRING = "^\".*\"$" static StrConstant SF_PARSER_REGEX_SIGNED_PARENTHESIS = "^(?i)[+-]?\\([\s\S]*$" static StrConstant SF_PARSER_REGEX_SIGNED_FUNCTION = "^(?i)[+-]?[A-Za-z]+" -static StrConstant SF_PARSER_REGEX_OTHER_VALID_CHARS = "[A-Za-z0-9_\.:;=!$]" +static StrConstant SF_PARSER_REGEX_OTHER_VALID_CHARS = "[A-Za-z0-9_\.:;=!$<>]" // The structure stores data that is required and gathered when a SF formula is parsed static Structure SF_ParserData From 4219fcf0c28c841fb30ee4235912127309c3429a Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 16 Feb 2026 21:21:08 +0100 Subject: [PATCH 25/34] SFO: Add operation PrepareFit The PrepareFit operation allows to gather information for fitting input data. The function returns a wave reference wave that stores all gathered information. The format of this wave is designed to allow further extensions for e.g. masking, weights etc. This is a preparation operation for a fit operation that implements the actual fit. PrepareFit allows to use the same fit setup for different fits. --- Packages/MIES/MIES_Constants.ipf | 10 +++ Packages/MIES/MIES_SweepFormula.ipf | 2 +- Packages/MIES/MIES_SweepFormula_Executor.ipf | 3 + .../MIES/MIES_SweepFormula_Operations.ipf | 83 +++++++++++++++++++ Packages/MIES/MIES_Utilities_SFHCheckers.ipf | 16 ++++ Packages/MIES/MIES_WaveDataFolderGetters.ipf | 16 ++++ 6 files changed, 129 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 2783b50ba8..c59b4953ea 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2213,6 +2213,7 @@ StrConstant SF_DATATYPE_SELECTSETSWEEPCOUNT = "SelectSetSweepCount" StrConstant SF_DATATYPE_SELECTSCIINDEX = "SelectSCIIndex" StrConstant SF_DATATYPE_SELECTRACINDEX = "SelectRACIndex" StrConstant SF_DATATYPE_ANAFUNCPARAM = "AnaFunc" +StrConstant SF_DATATYPE_PREPAREFIT = "PrepareFit" StrConstant SF_WREF_MARKER = "\"WREF@\":" StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric @@ -2561,6 +2562,7 @@ StrConstant SF_OP_TPBASE = "tpbase" StrConstant SF_OP_TPFIT = "tpfit" StrConstant SF_OP_EXTRACT = "extract" StrConstant SF_OP_IVSCCAPFREQUENCY = "ivscc_apfrequency" +StrConstant SF_OP_PREPAREFIT = "preparefit" #ifdef AUTOMATED_TESTING StrConstant SF_OP_TESTOP = "testop" @@ -2630,3 +2632,11 @@ Constant SF_DISPLAYTYPE_TABLE = 1 ///@{ Constant TRACECOLORMAX = 21 ///@} + +/// @name Characters allowed in the SF PrepareFit operation to hold a coefficient +/// +/// @anchor SFPrepareFitHoldStringCharacters +///@{ +StrConstant SF_PREPAREFIT_HOLDCHAR_HOLD = "X" +StrConstant SF_PREPAREFIT_HOLDCHAR_FREE = "O" +///@} diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index d76db5c8ce..eb105cb04a 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -130,7 +130,7 @@ Function/WAVE SF_GetNamedOperations() 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_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT, \ - SF_OP_IVSCCAPFREQUENCY} + SF_OP_IVSCCAPFREQUENCY, SF_OP_PREPAREFIT} #ifdef AUTOMATED_TESTING Make/FREE/T wtTest = {SF_OP_TESTOP} Concatenate/NP/T {wtTest}, wt diff --git a/Packages/MIES/MIES_SweepFormula_Executor.ipf b/Packages/MIES/MIES_SweepFormula_Executor.ipf index ef8a21f198..bc67d11c95 100644 --- a/Packages/MIES/MIES_SweepFormula_Executor.ipf +++ b/Packages/MIES/MIES_SweepFormula_Executor.ipf @@ -550,6 +550,9 @@ Function/WAVE SFE_FormulaExecutor(STRUCT SF_ExecutionData &exd, [variable srcLoc case SF_OP_IVSCCAPFREQUENCY: WAVE out = SFO_OperationIVSCCApFrequency(exdop) break + case SF_OP_PREPAREFIT: + WAVE out = SFO_OperationPrepareFit(exdop) + break #ifdef AUTOMATED_TESTING case SF_OP_TESTOP: WAVE out = SFO_OperationTestop(exdop) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 685ae0f3d1..50c76db764 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -67,6 +67,8 @@ static StrConstant SF_AVERAGING_NONSWEEPDATA_LBL = "NOSWEEPDATA" static Constant SF_IVSCC_APFREQUENCY_OPACITY = 13107 // 0.2 * 65535 +static StrConstant SF_PREPAREFIT_NOFUNC = "none" + Function/WAVE SFO_OperationAnaFuncParam(STRUCT SF_ExecutionData &exd) SFH_CheckArgumentCount(exd, SF_OP_ANAFUNCPARAM, 0, maxArgs = 2) @@ -3075,3 +3077,84 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) return SFH_GetOutputForExecutor(plotAND, exd.graph, opShort) End + +Function SFO_OperationPrepareFit_PROTO(WAVE w, variable x) + FATAL_ERROR("Prototype function called") +End + +/// preparefit([fitFuncName, coefs, holdStr, range, constraints]) +Function/WAVE SFO_OperationPrepareFit(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_PREPAREFIT + variable numArgs, numCoefs + string fitfuncName, holdStr, checkStr + + SFH_CheckArgumentCount(exd, opShort, 0, maxArgs = 4) + + WAVE/WAVE output = SFH_CreateSFRefWave(exd.graph, opShort, 1) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_PREPAREFIT) + + WAVE/WAVE prepFit = GetSFPrepareFitWave() + output[0] = prepFit + + WAVE/T fitArgs = prepFit[%FITARGS] + + numArgs = SFH_GetNumberOfArguments(exd) + if(numArgs == 0) + fitArgs[%FITFUNCNAME] = SF_PREPAREFIT_NOFUNC + return SFH_GetOutputForExecutor(output, exd.graph, opShort) + endif + + fitfuncName = SFH_GetArgumentAsText(exd, opShort, 0) + if(IsIntegratedFitFunction(fitFuncName)) + WAVE/Z/WAVE coefsArray = SFH_GetArgumentAsWave(exd, opShort, 1, defWave=$"") + if(WaveExists(coefsArray)) + SFH_ASSERT(SFH_IsArray(coefsArray), "Expected array at coefs arg position") + WAVE coefs = coefsArray[0] + else + WAVE/Z coefs = $"" + numCoefs = GetIntegratedFitFunctionCoefficientNumber(fitFuncName) + endif + else + FUNCREF SFO_OperationPrepareFit_PROTO func = $fitfuncName + SFH_ASSERT(FuncRefIsAssigned(FuncRefInfo(func)), "The specified user fit function does not exist: " + fitfuncName) + WAVE/WAVE coefsArray = SFH_GetArgumentAsWave(exd, opShort, 1) + SFH_ASSERT(SFH_IsArray(coefsArray), "Expected array at coefs arg position") + WAVE coefs = coefsArray[0] + endif + fitArgs[%FITFUNCNAME] = fitfuncName + + if(WaveExists(coefs)) + SFH_ASSERT(IsNumericWave(coefs), "Expected numeric wave for coefs argument") + numCoefs = DimSize(coefs, ROWS) + endif + prepFit[%COEFS] = coefs + + holdStr = PadString("", numCoefs, char2num("O")) + holdStr = SFH_GetArgumentAsText(exd, opShort, 2, defValue = holdStr, checkFunc = IsSFPrepareFitHoldString) + SFH_ASSERT(strlen(holdStr) == numCoefs, "Number of coefficients does not match number of characters in hold string.") + holdStr = ReplaceString(SF_PREPAREFIT_HOLDCHAR_HOLD, holdStr, "1") + holdStr = ReplaceString(SF_PREPAREFIT_HOLDCHAR_FREE, holdStr, "0") + fitArgs[%HOLDSTR] = holdStr + + WAVE/Z/WAVE rangeArray = SFH_GetArgumentAsWave(exd, opShort, 3, defWave = $"") + if(WaveExists(rangeArray)) + SFH_ASSERT(SFH_IsArray(rangeArray), "Expected array at range arg position") + WAVE range = rangeArray[0] + SFH_ASSERT(IsNumericWave(range), "Expected numeric wave for range argument") + SFH_ASSERT(DimSize(range, ROWS) == 2, "Expected range wave to have two entries") + SFH_ASSERT(range[0] >= 0, "First range element must be >= 0") + SFH_ASSERT(range[1] <= 0, "Second range element must be <= 0") + prepFit[%RANGE] = range + endif + + WAVE/Z/WAVE constraintsArray = SFH_GetArgumentAsWave(exd, opShort, 4, defWave = $"") + if(WaveExists(constraintsArray)) + SFH_ASSERT(SFH_IsArray(constraintsArray), "Expected array at constraints arg position") + WAVE/T constraints = constraintsArray[0] + SFH_ASSERT(IsTextWave(constraints), "Expected text wave for constraints argument") + prepFit[%CONSTRAINTS] = constraints + endif + + return SFH_GetOutputForExecutor(output, exd.graph, opShort) +End diff --git a/Packages/MIES/MIES_Utilities_SFHCheckers.ipf b/Packages/MIES/MIES_Utilities_SFHCheckers.ipf index 9a6961dbc2..e05a967a28 100644 --- a/Packages/MIES/MIES_Utilities_SFHCheckers.ipf +++ b/Packages/MIES/MIES_Utilities_SFHCheckers.ipf @@ -78,3 +78,19 @@ threadsafe Function BetweenZeroAndOneHoundred(variable val) return val >= 0.0 && val <= 100.0 End + +/// @brief Return the truth if `holdStr` is non-empty and contains only SF_PREPAREFIT_HOLDCHAR_HOLD or SF_PREPAREFIT_HOLDCHAR_FREE +/// The function DOES NOT check if the length is correct +/// +/// UTF_NOINSTRUMENTATION +threadsafe Function IsSFPrepareFitHoldString(string holdStr) + + if(IsEmpty(holdStr)) + return 0 + endif + + holdStr = ReplaceString(SF_PREPAREFIT_HOLDCHAR_HOLD, holdStr, "") + holdStr = ReplaceString(SF_PREPAREFIT_HOLDCHAR_FREE, holdStr, "") + + return IsEmpty(holdStr) +End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index aee4930809..81ffce760c 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -9481,3 +9481,19 @@ Function/WAVE GetSFIgorFitProperties() return wv End + +/// @brief The returned wave reference wave encapsulates the information gathered by the SF operation PrepareFit +Function/WAVE GetSFPrepareFitWave() + + Make/FREE/T/N=2 txtInfo + SetDimLabel ROWS, 0, FITFUNCNAME, txtInfo + SetDimLabel ROWS, 1, HOLDSTR, txtInfo + + Make/FREE/WAVE wv = {txtInfo, $"", $"", $""} + SetDimLabel ROWS, 0, FITARGS, wv + SetDimLabel ROWS, 1, COEFS, wv + SetDimLabel ROWS, 2, RANGE, wv + SetDimLabel ROWS, 3, CONSTRAINTS, wv + + return wv +End From a91627d1ec861d407305da4bfa0d22cb53f68864 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 17 Feb 2026 15:27:21 +0100 Subject: [PATCH 26/34] Constants: Move some SF_OP constants to SF_OP block --- Packages/MIES/MIES_Constants.ipf | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index c59b4953ea..c00ac05575 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2451,11 +2451,6 @@ Constant PSX_DECONV_FILTER_DEF_ORDER = 4 StrConstant PSX_JWN_COMBO_KEYS_NAME = "ComboKeys" -StrConstant SF_OP_MERGE = "merge" -StrConstant SF_OP_FIT = "fit" -StrConstant SF_OP_FITLINE = "fitline" -StrConstant SF_OP_DATASET = "dataset" - StrConstant SWEEP_NOTE_KEY_ORIGCREATIONTIME_UTC = "OriginalCreationTimeInUTC" StrConstant DF_NAME_FREE = "freeroot" @@ -2563,6 +2558,10 @@ StrConstant SF_OP_TPFIT = "tpfit" StrConstant SF_OP_EXTRACT = "extract" StrConstant SF_OP_IVSCCAPFREQUENCY = "ivscc_apfrequency" StrConstant SF_OP_PREPAREFIT = "preparefit" +StrConstant SF_OP_MERGE = "merge" +StrConstant SF_OP_FIT = "fit" +StrConstant SF_OP_FITLINE = "fitline" +StrConstant SF_OP_DATASET = "dataset" #ifdef AUTOMATED_TESTING StrConstant SF_OP_TESTOP = "testop" From 36b529efaa89d0fab3a144a8b31a5a13371c91a8 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 17 Feb 2026 18:39:08 +0100 Subject: [PATCH 27/34] SFO: Add operation fit2 --- Packages/MIES/MIES_Constants.ipf | 110 ++- Packages/MIES/MIES_SweepFormula.ipf | 2 +- Packages/MIES/MIES_SweepFormula_Executor.ipf | 3 + Packages/MIES/MIES_SweepFormula_Helpers.ipf | 6 + .../MIES/MIES_SweepFormula_Operations.ipf | 671 ++++++++++++++++++ 5 files changed, 752 insertions(+), 40 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index c00ac05575..8727bbc376 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2116,45 +2116,56 @@ StrConstant CO_DAP_DIFF_FALLBACK_NI = "DiffFallbackNI" /// @name Constants for SweepFormula Meta data in JSON format /// @anchor SFMetaDataConstants ///@{ -StrConstant SF_META_DATATYPE = "/DataType" // string -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_XVALUES = "/XValues" // numeric wave -StrConstant SF_META_XTICKLABELS = "/XTickLabels" // text wave -StrConstant SF_META_XTICKPOSITIONS = "/XTickPositions" // numeric wave -StrConstant SF_META_YTICKLABELS = "/YTickLabels" // text wave -StrConstant SF_META_YTICKPOSITIONS = "/YTickPositions" // numeric wave -StrConstant SF_META_XAXISLABEL = "/XAxisLabel" // string -StrConstant SF_META_YAXISLABEL = "/YAxisLabel" // string -StrConstant SF_META_LEGEND_LINE_PREFIX = "/LegendLinePrefix" // string -StrConstant SF_META_TAG_TEXT = "/TagText" // string -StrConstant SF_META_OPSTACK = "/OperationStack" // string -StrConstant SF_META_MOD_MARKER = "/Marker" // numeric wave (per point) or number (all points) -StrConstant SF_META_SHOW_LEGEND = "/ShowLegend" // numeric, boolean, defaults to true (1) -StrConstant SF_META_CUSTOM_LEGEND = "/CustomLegend" // string with custom legend text, honours /ShowLegend -StrConstant SF_META_ARGSETUPSTACK = "/ArgSetupStack" // string -StrConstant SF_META_TRACECOLOR = "/TraceColor" // numeric wave, applies to markers and lines -StrConstant SF_META_LINESTYLE = "/LineStyle" // number -StrConstant SF_META_TRACE_MODE = "/TraceMode" // number, one of @ref TraceDisplayTypes -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_WINDOW_HOOK = "/WindowHook" // string -StrConstant SF_META_FORMULA = "/Formula" // string -StrConstant SF_META_PLOT = "/Plot" // number, boolean, defaults to false (0) -StrConstant SF_META_XAXISOFFSET = "/XAxisOffset" // number -StrConstant SF_META_YAXISOFFSET = "/YAxisOffset" // number -StrConstant SF_META_XAXISPERCENT = "/XAxisPercent" // number -StrConstant SF_META_YAXISPERCENT = "/YAxisPercent" // number -StrConstant SF_META_ERRORBARYPLUS = "/ErrorYPlus" // numeric wave -StrConstant SF_META_ERRORBARYMINUS = "/ErrorYMinus" // numeric wave -StrConstant SF_META_ERRORBARXPLUS = "/ErrorXPlus" // numeric wave -StrConstant SF_META_ERRORBARXMINUS = "/ErrorXMinus" // numeric wave +StrConstant SF_META_DATATYPE = "/DataType" // string +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_XVALUES = "/XValues" // numeric wave +StrConstant SF_META_XTICKLABELS = "/XTickLabels" // text wave +StrConstant SF_META_XTICKPOSITIONS = "/XTickPositions" // numeric wave +StrConstant SF_META_YTICKLABELS = "/YTickLabels" // text wave +StrConstant SF_META_YTICKPOSITIONS = "/YTickPositions" // numeric wave +StrConstant SF_META_XAXISLABEL = "/XAxisLabel" // string +StrConstant SF_META_YAXISLABEL = "/YAxisLabel" // string +StrConstant SF_META_LEGEND_LINE_PREFIX = "/LegendLinePrefix" // string +StrConstant SF_META_TAG_TEXT = "/TagText" // string +StrConstant SF_META_OPSTACK = "/OperationStack" // string +StrConstant SF_META_MOD_MARKER = "/Marker" // numeric wave (per point) or number (all points) +StrConstant SF_META_SHOW_LEGEND = "/ShowLegend" // numeric, boolean, defaults to true (1) +StrConstant SF_META_CUSTOM_LEGEND = "/CustomLegend" // string with custom legend text, honours /ShowLegend +StrConstant SF_META_ARGSETUPSTACK = "/ArgSetupStack" // string +StrConstant SF_META_TRACECOLOR = "/TraceColor" // numeric wave, applies to markers and lines +StrConstant SF_META_LINESTYLE = "/LineStyle" // number +StrConstant SF_META_TRACE_MODE = "/TraceMode" // number, one of @ref TraceDisplayTypes +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_WINDOW_HOOK = "/WindowHook" // string +StrConstant SF_META_FORMULA = "/Formula" // string +StrConstant SF_META_PLOT = "/Plot" // number, boolean, defaults to false (0) +StrConstant SF_META_XAXISOFFSET = "/XAxisOffset" // number +StrConstant SF_META_YAXISOFFSET = "/YAxisOffset" // number +StrConstant SF_META_XAXISPERCENT = "/XAxisPercent" // number +StrConstant SF_META_YAXISPERCENT = "/YAxisPercent" // number +StrConstant SF_META_ERRORBARYPLUS = "/ErrorYPlus" // numeric wave +StrConstant SF_META_ERRORBARYMINUS = "/ErrorYMinus" // numeric wave +StrConstant SF_META_ERRORBARXPLUS = "/ErrorXPlus" // numeric wave +StrConstant SF_META_ERRORBARXMINUS = "/ErrorXMinus" // numeric wave +StrConstant SF_META_FITCOEFS = "/FitCoefs" // numeric wave +StrConstant SF_META_FITERROR = "/FitError" // number +StrConstant SF_META_FITQUITREASON = "/FitQuitReason" // number +StrConstant SF_META_FITNUMITERS = "/FitNumIters" // number +StrConstant SF_META_FITSTATUSMESSAGE = "/FitStatusMessage" // string +StrConstant SF_META_FITWSIGMA = "/FitWSigma" // numeric wave +StrConstant SF_META_FITMCOVAR = "/FitMCovar" // numeric wave +StrConstant SF_META_FITMFITCONSTRAINT = "/FitMFitConstraint" // numeric wave +StrConstant SF_META_FITWFITCONSTRAINT = "/FitWFitConstraint" // numeric wave +StrConstant SF_META_FITCHISQUARE = "/FitChiSquare" // number +StrConstant SF_META_FITFUNC = "/FitFunc" // string /// A color group allows to have matching colors for sweep data with the same channel type/number and sweep. /// It is applied before the matching headstage/average colors in #SF_GetTraceColor(). @@ -2562,6 +2573,7 @@ StrConstant SF_OP_MERGE = "merge" StrConstant SF_OP_FIT = "fit" StrConstant SF_OP_FITLINE = "fitline" StrConstant SF_OP_DATASET = "dataset" +StrConstant SF_OP_FIT2 = "fit2" #ifdef AUTOMATED_TESTING StrConstant SF_OP_TESTOP = "testop" @@ -2639,3 +2651,23 @@ Constant TRACECOLORMAX = 21 StrConstant SF_PREPAREFIT_HOLDCHAR_HOLD = "X" StrConstant SF_PREPAREFIT_HOLDCHAR_FREE = "O" ///@} + +/// @name Bits for Fit Errors +/// @anchor FitErrors +///@{ +Constant FIT_ERROR_ANY = 1 +Constant FIT_ERROR_SINGULARMATRIX = 2 +Constant FIT_ERROR_OUTOFMEMORY = 4 +Constant FIT_ERROR_RETURNEDNANORINF = 8 +Constant FIT_ERROR_FUNCREQUESTEDSTOP = 16 +Constant FIT_ERROR_REENTRANT_FIT = 32 +///@} + +/// @name Codes for Fit Quit Reasons +/// @anchor FitQuitReason +///@{ +Constant FIT_QUITREASON_OK = 0 +Constant FIT_QUITREASON_ITERATIONLIMITREACHED = 1 +Constant FIT_QUITREASON_STOPPEDBYUSER = 2 +Constant FIT_QUITREASON_NOCHISQUAREDECREASE = 3 +///@} diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index eb105cb04a..0adb09e820 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -130,7 +130,7 @@ Function/WAVE SF_GetNamedOperations() 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_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT, \ - SF_OP_IVSCCAPFREQUENCY, SF_OP_PREPAREFIT} + SF_OP_IVSCCAPFREQUENCY, SF_OP_PREPAREFIT, SF_OP_FIT2} #ifdef AUTOMATED_TESTING Make/FREE/T wtTest = {SF_OP_TESTOP} Concatenate/NP/T {wtTest}, wt diff --git a/Packages/MIES/MIES_SweepFormula_Executor.ipf b/Packages/MIES/MIES_SweepFormula_Executor.ipf index bc67d11c95..7bd2acaddb 100644 --- a/Packages/MIES/MIES_SweepFormula_Executor.ipf +++ b/Packages/MIES/MIES_SweepFormula_Executor.ipf @@ -553,6 +553,9 @@ Function/WAVE SFE_FormulaExecutor(STRUCT SF_ExecutionData &exd, [variable srcLoc case SF_OP_PREPAREFIT: WAVE out = SFO_OperationPrepareFit(exdop) break + case SF_OP_FIT2: + WAVE out = SFO_OperationFit2(exdop) + break #ifdef AUTOMATED_TESTING case SF_OP_TESTOP: WAVE out = SFO_OperationTestop(exdop) diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 5f81c8d2bc..77b5a4ecaf 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -2219,3 +2219,9 @@ Function SFH_CopyPlotMetaData(WAVE input, WAVE output) JWN_SetNumberInWaveNote(output, SF_META_TRACETOFRONT, JWN_GetNumberFromWaveNote(input, SF_META_TRACETOFRONT)) JWN_SetNumberInWaveNote(output, SF_META_LINESTYLE, JWN_GetNumberFromWaveNote(input, SF_META_LINESTYLE)) End + +Function SFH_SetTraceStyleForFit(WAVE fitData) + + JWN_SetWaveInWaveNote(fitData, SF_META_TRACECOLOR, {0, 0, 0}) // black + JWN_SetNumberInWaveNote(fitData, SF_META_TRACE_MODE, TRACE_DISPLAY_MODE_LINES) +End diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 50c76db764..340bd50b85 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -69,6 +69,8 @@ static Constant SF_IVSCC_APFREQUENCY_OPACITY = 13107 // 0.2 * 65535 static StrConstant SF_PREPAREFIT_NOFUNC = "none" +static Constant SF_FIT2_MAX_ITERATIONS = 40 + Function/WAVE SFO_OperationAnaFuncParam(STRUCT SF_ExecutionData &exd) SFH_CheckArgumentCount(exd, SF_OP_ANAFUNCPARAM, 0, maxArgs = 2) @@ -3158,3 +3160,672 @@ Function/WAVE SFO_OperationPrepareFit(STRUCT SF_ExecutionData &exd) return SFH_GetOutputForExecutor(output, exd.graph, opShort) End + +/// fit2(wvY, wvX, preparefit(...)) +Function/WAVE SFO_OperationFit2(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_FIT2 + string dataType + variable numDatasets + + SFH_CheckArgumentCount(exd, opShort, 2, maxArgs = 3) + WAVE/WAVE datasetsWvY = SFH_GetArgumentAsWave(exd, opShort, 0) + WAVE/WAVE datasetsWvX = SFH_GetArgumentAsWave(exd, opShort, 1) + numDatasets = DimSize(datasetsWvY, ROWS) + dataType = JWN_GetStringFromWaveNote(datasetsWvX, SF_META_DATATYPE) + if(!CmpStr(dataType, SF_DATATYPE_PREPAREFIT)) + WAVE/WAVE prepFit = datasetsWvX[0] + Make/WAVE/FREE/N=(numDatasets) datasetsWvX + else + SFH_ASSERT(numDatasets == DimSize(datasetsWvX, ROWS), "number of datasets for wvY must equal number of datasets for wvX") + WAVE/WAVE prepFitRef = SFH_GetArgumentAsWave(exd, opShort, 2) + dataType = JWN_GetStringFromWaveNote(prepFitRef, SF_META_DATATYPE) + SFH_ASSERT(!CmpStr(dataType, SF_DATATYPE_PREPAREFIT), "Expected argument of type preparefit") + WAVE/WAVE prepFit = prepFitRef[0] + endif + + WAVE/WAVE output = SFH_CreateSFRefWave(exd.graph, opShort, numDatasets) + + output[] = SFO_OperationFit2Impl(datasetsWvY[p], datasetsWvX[p], prepFit) + + JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "x value") // activates x values + + return SFH_GetOutputForExecutor(output, exd.graph, opShort) +End + +static Function/S SFO_OperationFit2GetFitStatusMessage(variable fitError, variable fitQuitReason, variable fitNumIters) + + string msg = "" + + if(FitError == 0) + msg += "Fit Ok.\r" + endif + if((FitError & FIT_ERROR_ANY) == FIT_ERROR_ANY) + msg += "Fit Error.\r" + endif + if((FitError & FIT_ERROR_SINGULARMATRIX) == FIT_ERROR_SINGULARMATRIX) + msg += "Singular Matrix.\r" + endif + if((FitError & FIT_ERROR_OUTOFMEMORY) == FIT_ERROR_OUTOFMEMORY) + msg += "Out of memory.\r" + endif + if((FitError & FIT_ERROR_RETURNEDNANORINF) == FIT_ERROR_RETURNEDNANORINF) + msg += "Fit function returned NaN or Inf.\r" + endif + if((FitError & FIT_ERROR_FUNCREQUESTEDSTOP) == FIT_ERROR_FUNCREQUESTEDSTOP) + msg += "Function requested stop.\r" + endif + if((FitError & FIT_ERROR_REENTRANT_FIT) == FIT_ERROR_REENTRANT_FIT) + msg += "Reentrant fit function call encountered.\r" + endif + if((FitQuitReason & FIT_QUITREASON_ITERATIONLIMITREACHED) == FIT_QUITREASON_ITERATIONLIMITREACHED) + msg += "Iteration limit reached.\r" + endif + if((FitQuitReason & FIT_QUITREASON_STOPPEDBYUSER) == FIT_QUITREASON_STOPPEDBYUSER) + msg += "User stopped fit.\r" + endif + if((FitQuitReason & FIT_QUITREASON_NOCHISQUAREDECREASE) == FIT_QUITREASON_NOCHISQUAREDECREASE) + msg += "chiSquare did not decrease in last 9 iterations.\r" + endif + msg += "Iterations run: " + num2istr(fitNumIters) + + return msg +End + +static Function/WAVE SFO_OperationFit2CalculateWeights(WAVE/Z wMinus, WAVE/Z wPlus) + + if(!WaveExists(wMinus) && !WaveExists(wPlus)) + return $"" + endif + + if(!WaveExists(wPlus)) + Make/FREE/D/N=(DimSize(wMinus, ROWS)) weight + MultiThread weight[] = abs(wMinus[p]) + elseif(!WaveExists(wMinus)) + Make/FREE/D/N=(DimSize(wPlus, ROWS)) weight + MultiThread weight[] = abs(wPlus[p]) + else + Make/FREE/D/N=(DimSize(wMinus, ROWS)) weight + MultiThread weight[] = (abs(wPlus[p]) + abs(wMinus[p])) / 2 + endif + + return weight +End + +static Function/WAVE SFO_OperationFit2Impl(WAVE wvY, WAVE/Z wvX, WAVE/WAVE prepFit) + + string fitFuncName, holdStr, fitStatus + variable V_FitError, V_FitQuitReason, V_FitNumIters, V_ChiSq, V_FitMaxIters + variable numPoints, xErrorsOut, haveConstraints + + V_FitMaxIters = SF_FIT2_MAX_ITERATIONS + + numPoints = DimSize(wvY, ROWS) + + if(WaveExists(wvX)) + WAVE/D xWave = wvX + else + WAVE/Z/D xWave = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_XVALUES) + if(WaveExists(xWave)) + SFH_ASSERT(IsNumericWave(xWave), "Require wvX to be numeric") + else + Make/FREE/D/N=(numPoints) xWave + Multithread xWave[] = IndexToScale(wvY, p, ROWS) + endif + endif + SFH_ASSERT(numPoints == DimSize(xWave, ROWS), "Number of points in xWave must equal number of points in y-wave") + + WAVE/T fitArgs = prepFit[%FITARGS] + fitFuncName = fitArgs[%FITFUNCNAME] + if(!CmpStr(fitFuncName, SF_PREPAREFIT_NOFUNC)) + return $"" + endif + + holdStr = fitArgs[%HOLDSTR] + WAVE/Z coefs = prepFit[%COEFS] + WAVE/Z/T constraints = prepFit[%CONSTRAINTS] + haveConstraints = WaveExists(constraints) + if(!haveConstraints) + // use always a constraints wave to reduce flag combinations for CurveFit + Make/FREE/T/N=0 constraints + endif + + WAVE/Z/D errorbarYPlus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARYPLUS) + if(WaveExists(errorbarYPlus)) + SFH_ASSERT(numPoints == DimSize(errorbarYPlus, ROWS), "number of points in errorbar must equal the number of points from y-wave") + endif + WAVE/Z/D errorbarYMinus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARYMINUS) + if(WaveExists(errorbarYMinus)) + SFH_ASSERT(numPoints == DimSize(errorbarYMinus, ROWS), "number of points in errorbar must equal the number of points from y-wave") + endif + WAVE/Z/D errorbarXPlus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARXPLUS) + if(WaveExists(errorbarXPlus)) + SFH_ASSERT(numPoints == DimSize(errorbarXPlus, ROWS), "number of points in errorbar must equal the number of points from y-wave") + endif + WAVE/Z/D errorbarXMinus = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_ERRORBARXMINUS) + if(WaveExists(errorbarXMinus)) + SFH_ASSERT(numPoints == DimSize(errorbarXMinus, ROWS), "number of points in errorbar must equal the number of points from y-wave") + endif + + WAVE/Z/D weightY = SFO_OperationFit2CalculateWeights(errorbarYMinus, errorbarYPlus) + WAVE/Z/D weightX = SFO_OperationFit2CalculateWeights(errorbarXMinus, errorbarXPlus) + xErrorsOut = WaveExists(weightX) + + if(!WaveExists(weightY)) + // use always a weight wave for Y to reduce flag combinations for CurveFit + Make/FREE/D/N=(numPoints) weightY + FastOp weightY = 1 + endif + + Make/FREE/D/N=(numPoints) fitOutput, yResiduals, xResiduals, xOutput, mask + + FastOp mask = 1 + WAVE/Z range = prepFit[%RANGE] + if(WaveExists(range)) + if(range[0] > 0) + mask[0, limit(range[0] - 1, 0, numPoints - 1)] = 0 + endif + if(range[1] < 0) + mask[limit(numPoints - 1 - range[1], 0, numPoints - 1), Inf] = 0 + endif + endif + + DFREF dfrSave = GetDataFolderDFR() + DFREF dfr = NewFreeDataFolder() + SetDataFolder dfr + + if(!IsIntegratedFitFunction(fitFuncName)) + // FuncFit + ASSERT(WaveExists(coefs), "For user fit function coefs must exist") + if(xErrorsOut) + // ODR fit + FuncFit/C/Q/M=2/H=holdStr $fitFuncName, coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + FuncFit/C/Q/M=2/H=holdStr $fitFuncName, coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + // CurveFit + strswitch(fitFuncName) + case "gauss": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 gauss, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr gauss, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 gauss, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr gauss, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "lor": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 lor, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr lor, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 lor, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr lor, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "Voigt": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 Voigt, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr Voigt, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 Voigt, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr Voigt, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "exp": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 exp, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr exp, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 exp, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr exp, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "dblexp": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 dblexp, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr dblexp, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 dblexp, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr dblexp, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "exp_XOffset": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 exp_XOffset, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr exp_XOffset, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 exp_XOffset, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr exp_XOffset, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "dblexp_XOffset": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 dblexp_XOffset, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr dblexp_XOffset, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 dblexp_XOffset, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr dblexp_XOffset, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "dblexp_peak": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 dblexp_peak, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr dblexp_peak, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 dblexp_peak, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr dblexp_peak, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "sin": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 sin, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr sin, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 sin, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr sin, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "line": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 line, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr line, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 line, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr line, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly 1": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 1, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 1, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 1, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 1, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly 2": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 2, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 2, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 2, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 2, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly 3": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 3, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 3, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 3, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 3, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly 4": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 4, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 4, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly 4, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly 4, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly_XOffset 1": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 1, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 1, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 1, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 1, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly_XOffset 2": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 2, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 2, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 2, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 2, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly_XOffset 3": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 3, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 3, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 3, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 3, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly_XOffset 4": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 4, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 4, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly_XOffset 4, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly_XOffset 4, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "hillequation": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 hillequation, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr hillequation, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 hillequation, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr hillequation, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "sigmoid": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 sigmoid, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr sigmoid, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 sigmoid, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr sigmoid, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "power": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 power, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr power, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 power, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr power, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "lognormal": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 lognormal, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr lognormal, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 lognormal, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr lognormal, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "log": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 log, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr log, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 log, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr log, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "gauss2D": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 gauss2D, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr gauss2D, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 gauss2D, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr gauss2D, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly2D 1": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 1, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 1, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 1, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 1, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly2D 2": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 2, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 2, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 2, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 2, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly2D 3": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 3, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 3, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 3, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 3, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + case "poly2D 4": + if(WaveExists(coefs)) + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 4, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 4, kwCWave=coefs, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + else + if(xErrorsOut) + CurveFit/C/Q/M=2/H=holdStr/ODR=2 poly2D 4, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/XW=weightX/XD=xOutput/XR=xResiduals/I=1/M=mask + else + CurveFit/C/Q/M=2/H=holdStr poly2D 4, wvY/X=xWave/D=fitOutput/C=constraints/W=weightY/A=0/R=yResiduals/I=1/M=mask + endif + endif + break + default: + SFH_FATAL_ERROR("Unsupported integrated fit function") + endswitch + endif + if(!WaveExists(coefs)) + WAVE coefs = W_coef + endif + if(haveConstraints) + WAVE/Z mFitConstraints = M_FitConstraint + WAVE/Z wFitConstraints = W_FitConstraint + endif + + WAVE/Z wSigma = W_Sigma + WAVE/Z mCovar = M_Covar + + SetDataFolder dfrSave + + fitStatus = SFO_OperationFit2GetFitStatusMessage(V_FitError, V_FitQuitReason, V_FitNumIters) + + if(V_FitError) + Make/FREE/D/N=0 fitOutput + JWN_SetNumberInWaveNote(fitOutput, SF_META_FITERROR, V_FitError) + JWN_SetNumberInWaveNote(fitOutput, SF_META_FITQUITREASON, V_FitQuitReason) + JWN_SetNumberInWaveNote(fitOutput, SF_META_FITNUMITERS, V_FitNumIters) + endif + + JWN_SetStringInWaveNote(fitOutput, SF_META_FITSTATUSMESSAGE, fitStatus) + JWN_SetStringInWaveNote(fitOutput, SF_META_FITFUNC, fitFuncName) + + if(V_FitError) + return fitOutput + endif + + JWN_SetWaveInWaveNote(fitOutput, SF_META_FITCOEFS, coefs) + JWN_SetWaveInWaveNote(fitOutput, SF_META_ERRORBARYPLUS, yResiduals) + JWN_SetWaveInWaveNote(fitOutput, SF_META_ERRORBARYMINUS, yResiduals) + if(xErrorsOut) + JWN_SetWaveInWaveNote(fitOutput, SF_META_ERRORBARXPLUS, xResiduals) + JWN_SetWaveInWaveNote(fitOutput, SF_META_ERRORBARXMINUS, xResiduals) + JWN_SetWaveInWaveNote(fitOutput, SF_META_XVALUES, xOutput) + else + JWN_SetWaveInWaveNote(fitOutput, SF_META_XVALUES, xWave) + endif + + // Possible more fit data: V_npnts, V_nterms, V_nheld + JWN_SetNumberInWaveNote(fitOutput, SF_META_FITCHISQUARE, V_ChiSq) + if(WaveExists(wSigma)) + JWN_SetWaveInWaveNote(fitOutput, SF_META_FITWSIGMA, wSigma) + endif + if(WaveExists(mCovar)) + JWN_SetWaveInWaveNote(fitOutput, SF_META_FITMCOVAR, mCovar) + endif + if(WaveExists(mFitConstraints)) + JWN_SetWaveInWaveNote(fitOutput, SF_META_FITMFITCONSTRAINT, mFitConstraints) + endif + if(WaveExists(wFitConstraints)) + JWN_SetWaveInWaveNote(fitOutput, SF_META_FITWFITCONSTRAINT, wFitConstraints) + endif + + SFH_SetTraceStyleForFit(fitOutput) + + return fitOutput +End From 7602c35ee71cd0e0eb93e0282fe5174959f35cc2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 18 Feb 2026 16:45:27 +0100 Subject: [PATCH 28/34] f remove debug output ivscc_apfrequency --- .../MIES/MIES_SweepFormula_Operations.ipf | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 340bd50b85..f9b7081b15 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -3036,17 +3036,11 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) else // SF_OP_IVSCCAPFREQUENCY_NONE formula = "merge($ivscccurrentavg)" endif - - // numBins = ceil((binRange[1] - binRange[0]) / binWidth) - // Make/FREE/D/N=(numBins) binValues - // binValues[] = binRange[0] + p * binWidth + binWidth / 2 - // binList = NumericWaveToList(binValues, ",", format = "%f", trailSep = 0) - // sprintf formula, "[%s]", binList else // SF_OP_AVG_BINS2 if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) - // formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" + // formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0, 0))" elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) formula = "$ivsccavg_xvalues - min($ivsccavg_xvalues)" elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) @@ -3057,21 +3051,6 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) endif plotWITH[numExp][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) - if(!CmpStr(avgMode, SF_OP_AVG_BINS)) - WAVE/WAVE wTmp = SF_ResolveDataset(varStorage[%ivsccavg]) - printf "ivsccavg bins mode: " - for(WAVE wElem : wTmp) - printf "%f, ", wElem[0] - endfor - printf "\r" - printf "ivscccurrentavg: " - WAVE/WAVE wTmp = SF_ResolveDataset(varStorage[%ivscccurrentavg]) - for(WAVE wElem : wTmp) - printf "%f, ", wElem[0] - endfor - printf "\r" - endif - Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) From 10d95b3ef83962cf1f0dd684c76894986f33bdf2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 18 Feb 2026 18:29:33 +0100 Subject: [PATCH 29/34] f ivscc_apfrequency Use allowEmptyCode for SFE_ExecuteVariableAssignments call --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index f9b7081b15..cd2c2f6d69 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2970,7 +2970,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) Duplicate/FREE varStorage, varBackup - SFE_ExecuteVariableAssignments(exd.graph, formula) + SFE_ExecuteVariableAssignments(exd.graph, formula, allowEmptyCode=1) // build plot tree WAVE/WAVE varStorageOp = GetSFVarStorage(exd.graph) From 603fa8f9dd1507bf038e3ff6d6cdc547ec0d9e02 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 18 Feb 2026 18:31:30 +0100 Subject: [PATCH 30/34] f Add fit to ivscc_apfrequency --- .../MIES/MIES_SweepFormula_Operations.ipf | 101 ++++++++++-------- 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index cd2c2f6d69..7168278302 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -2873,15 +2873,15 @@ static Function SFO_OperationIVSCCApFrequencySetPlotProperties(WAVE wvY, variabl End // for avgMode: bins -// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, avgMode, binRange, binWidth, method, level, timeFreq, normalize, xAxisType]) +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, prepFit, avgMode, binRange, binWidth, method, level, timeFreq, normalize, xAxisType]) // // for avgMode: bins2 -// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, avgMode, method, level, timeFreq, normalize, xAxisType]) +// ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, prepFit, avgMode, method, level, timeFreq, normalize, xAxisType]) Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) string opShort = SF_OP_IVSCCAPFREQUENCY variable numArgsMin = 0 - variable numArgsMax = 12 + variable numArgsMax = 13 string formula, expr variable i, numArgs, col, size, numExp variable xAxisPercentage, yAxisPercentage @@ -2900,22 +2900,25 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) yaxisOffset = SFH_GetArgumentAsText(exd, opShort, 1, defValue = SF_OP_IVSCCAPFREQUENCY_MIN, allowedValues = {SF_OP_IVSCCAPFREQUENCY_FIRST, SF_OP_IVSCCAPFREQUENCY_MIN, SF_OP_IVSCCAPFREQUENCY_MAX, SF_OP_IVSCCAPFREQUENCY_NONE}) xAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) yAxisPercentage = SFH_GetArgumentAsNumeric(exd, opShort, 3, defValue = 100, checkFunc = BetweenZeroAndOneHoundred) - avgMode = SFH_GetArgumentAsText(exd, opShort, 4, defValue = SF_OP_AVG_BINS, allowedValues = {SF_OP_AVG_BINS, SF_OP_AVG_BINS2}) + WAVE/WAVE prepFit = SFH_GetArgumentAsWave(exd, opShort, 4, defOp = "preparefit()") + + avgMode = SFH_GetArgumentAsText(exd, opShort, 5, defValue = SF_OP_AVG_BINS, allowedValues = {SF_OP_AVG_BINS, SF_OP_AVG_BINS2}) if(!CmpStr(avgMode, SF_OP_AVG_BINS)) - WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 5) + WAVE/WAVE wTmp = SFH_GetArgumentAsWave(exd, opShort, 6) WAVE binRange = wTmp[0] - binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 6, checkFunc = IsStrictlyPositiveAndFinite) - [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 7) + binWidth = SFH_GetArgumentAsNumeric(exd, opShort, 7, checkFunc = IsStrictlyPositiveAndFinite) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 8) else // SF_OP_AVG_BINS2 - [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 5) + [method, level, timeFreq, normalize, xAxisType] = SFO_GetApFrequencyArguments(exd, opShort, 6) endif // create and evaluate variables WAVE/T sweepMap = SB_GetSweepMap(exd.graph) col = FindDimlabel(sweepMap, COLS, "FileName") size = GetNumberFromWaveNote(sweepMap, NOTE_INDEX) + SFH_ASSERT(size > 0, "No sweep data loaded") Duplicate/FREE/RMD=[0, size - 1][col] sweepMap, fileNames WAVE/T uniqueFiles = GetUniqueEntries(fileNames, dontDuplicate = 1) Sort uniqueFiles, uniqueFiles @@ -2960,24 +2963,58 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) formula = SF_AddExpressionToFormula(formula, expr) sprintf expr, "ivscccurrentavg = avg([%s], bins, [%f,%f],%f,[%s])", currentList, binRange[0], binRange[1], binWidth, currentList formula = SF_AddExpressionToFormula(formula, expr) + + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + expr = "ivsccavg_norm_x = merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + expr = "ivsccavg_norm_x = merge($ivscccurrentavg - min(merge($ivscccurrentavg)))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + expr = "ivsccavg_norm_x = merge($ivscccurrentavg - max(merge($ivscccurrentavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + expr = "ivsccavg_norm_x = merge($ivscccurrentavg)" + endif + formula = SF_AddExpressionToFormula(formula, expr) else // SF_OP_AVG_BINS2 sprintf expr, "ivsccavg = avg([%s], bins2, [%s])", freqList, currentList formula = SF_AddExpressionToFormula(formula, expr) - sprintf expr, "ivsccavg_xvalues = xvalues(merge($ivsccavg))" + expr = "ivsccavg_xvalues = xvalues(merge($ivsccavg))" + formula = SF_AddExpressionToFormula(formula, expr) + + if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + // expr = "ivsccavg_norm_x = merge($ivscccurrentavg - extract($ivscccurrentavg, 0, 0))" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + expr = "ivsccavg_norm_x = $ivsccavg_xvalues - min($ivsccavg_xvalues)" + elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + expr = "ivsccavg_norm_x = $ivsccavg_xvalues - max($ivsccavg_xvalues)" + else // SF_OP_IVSCCAPFREQUENCY_NONE + expr = "ivsccavg_norm_x = $ivsccavg_xvalues" + endif formula = SF_AddExpressionToFormula(formula, expr) endif + if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) + expr = "ivsccavg_norm_y = merge($ivsccavg - extract($ivsccavg, 0))" + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) + expr = "ivsccavg_norm_y = merge($ivsccavg - min(merge($ivsccavg)))" + elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) + expr = "ivsccavg_norm_y = merge($ivsccavg - max(merge($ivsccavg)))" + else // SF_OP_IVSCCAPFREQUENCY_NONE + expr = "ivsccavg_norm_y = merge($ivsccavg)" + endif + formula = SF_AddExpressionToFormula(formula, expr) WAVE/WAVE varStorage = GetSFVarStorage(exd.graph) Duplicate/FREE varStorage, varBackup SFE_ExecuteVariableAssignments(exd.graph, formula, allowEmptyCode=1) + SFH_AddVariableToStorage(exd.graph, "pfit", SFH_GetOutputForExecutor(prepFit, exd.graph, opShort)) + // build plot tree WAVE/WAVE varStorageOp = GetSFVarStorage(exd.graph) WAVE wvResult = varStorageOp[%ivscc_apfrequency_explist] WAVE/WAVE plotAND = SFH_CreateSFRefWave(exd.graph, opShort, 1) - Make/FREE/WAVE/N=(numExp + 1, 2) plotWITH + Make/FREE/WAVE/N=(numExp + 2, 2) plotWITH SetDimlabel COLS, 0, FORMULAX, plotWITH SetDimlabel COLS, 1, FORMULAY, plotWITH plotAND[0] = plotWITH @@ -3008,16 +3045,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) endfor // avg trace - - if(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) - formula = "merge($ivsccavg - extract($ivsccavg, 0))" - elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) - formula = "merge($ivsccavg - min(merge($ivsccavg)))" - elseif(!CmpStr(yaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) - formula = "merge($ivsccavg - max(merge($ivsccavg)))" - else // SF_OP_IVSCCAPFREQUENCY_NONE - formula = "merge($ivsccavg)" - endif + formula = "$ivsccavg_norm_y" WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) if(DimSize(wvY, ROWS) > 0) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency avg " + avgMode) @@ -3026,30 +3054,17 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) plotWITH[numExp][%FORMULAY] = wvY SFO_OperationIVSCCApFrequencySetPlotProperties(plotWITH[numExp][%FORMULAY], xAxisPercentage, yAxisPercentage) - if(!CmpStr(avgMode, SF_OP_AVG_BINS)) - if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) - formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0))" - elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) - formula = "merge($ivscccurrentavg - min(merge($ivscccurrentavg)))" - elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) - formula = "merge($ivscccurrentavg - max(merge($ivscccurrentavg)))" - else // SF_OP_IVSCCAPFREQUENCY_NONE - formula = "merge($ivscccurrentavg)" - endif - else - // SF_OP_AVG_BINS2 + formula = "$ivsccavg_norm_x" + plotWITH[numExp][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) - if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) - // formula = "merge($ivscccurrentavg - extract($ivscccurrentavg, 0, 0))" - elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MIN)) - formula = "$ivsccavg_xvalues - min($ivsccavg_xvalues)" - elseif(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_MAX)) - formula = "$ivsccavg_xvalues - max($ivsccavg_xvalues)" - else // SF_OP_IVSCCAPFREQUENCY_NONE - formula = "$ivsccavg_xvalues" - endif + numExp += 1 + // fit trace + formula = "fit2($ivsccavg_norm_y, $ivsccavg_norm_x, $pfit)" + WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + if(DimSize(wvY, ROWS) > 0) + JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency fit") endif - plotWITH[numExp][%FORMULAX] = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) + plotWITH[numExp][%FORMULAY] = wvY Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) @@ -3305,7 +3320,7 @@ static Function/WAVE SFO_OperationFit2Impl(WAVE wvY, WAVE/Z wvX, WAVE/WAVE prepF mask[0, limit(range[0] - 1, 0, numPoints - 1)] = 0 endif if(range[1] < 0) - mask[limit(numPoints - 1 - range[1], 0, numPoints - 1), Inf] = 0 + mask[limit(numPoints + range[1], 0, numPoints - 1), Inf] = 0 endif endif From 4b9f22bf783b960022fb6480c9a28fb1423ba181 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 20 Feb 2026 10:21:14 +0100 Subject: [PATCH 31/34] f ivscc_apfrequency expose fitresult in topmost varStorage - this allows the user to use getmeta($ivscc_apfrequency_fit) --- Packages/MIES/MIES_SweepFormula_Operations.ipf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 7168278302..d0f64a1c8f 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -3008,6 +3008,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) SFE_ExecuteVariableAssignments(exd.graph, formula, allowEmptyCode=1) SFH_AddVariableToStorage(exd.graph, "pfit", SFH_GetOutputForExecutor(prepFit, exd.graph, opShort)) + WAVE/WAVE fitResult = SFH_AddVariableToStorageByFormula(exd.graph, "ivscc_apfrequency_fit", "fit2($ivsccavg_norm_y, $ivsccavg_norm_x, $pfit)", opShort) // build plot tree WAVE/WAVE varStorageOp = GetSFVarStorage(exd.graph) @@ -3059,7 +3060,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) numExp += 1 // fit trace - formula = "fit2($ivsccavg_norm_y, $ivsccavg_norm_x, $pfit)" + formula = "$ivscc_apfrequency_fit" WAVE/WAVE wvY = SFE_ExecuteFormula(formula, exd.graph, preProcess = 0) if(DimSize(wvY, ROWS) > 0) JWN_SetStringInWaveNote(wvY[0], SF_META_LEGEND_LINE_PREFIX, "ivscc_apfrequency fit") @@ -3068,6 +3069,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) + SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_fit", fitResult) JWN_SetNumberInWaveNote(plotAND, SF_META_PLOT, 1) From d59148e5d9a462d43ac73f5cea6aaf2aea1716e2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 20 Feb 2026 12:36:29 +0100 Subject: [PATCH 32/34] JWN: Add function to return all keys at a given path --- Packages/MIES/MIES_JSONWaveNotes.ipf | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Packages/MIES/MIES_JSONWaveNotes.ipf b/Packages/MIES/MIES_JSONWaveNotes.ipf index 57a3442359..87a127ef8e 100644 --- a/Packages/MIES/MIES_JSONWaveNotes.ipf +++ b/Packages/MIES/MIES_JSONWaveNotes.ipf @@ -318,3 +318,20 @@ threadsafe Function/WAVE JWN_CreatePath(WAVE wv, string jsonPath) JSON_AddTreeObject(jsonID, jsonPath) JWN_WriteWaveNote(wv, JWN_GetWaveNoteHeader(wv), jsonID) End + +/// @brief Returns a text wave with the json keys available at path, null wave if path does not exist +/// +/// @param wv wave reference where the WaveNote is taken from +/// @param jsonPath path to create as object +threadsafe Function/WAVE JWN_GetKeysAt(WAVE wv, string jsonPath) + + variable jsonID + + ASSERT_TS(!IsEmpty(jsonPath), "Empty jsonPath") + + jsonID = JWN_GetWaveNoteAsJSON(wv) + WAVE/Z keys = JSON_GetKeys(jsonID, jsonPath, ignoreErr = 1) + JSON_Release(jsonID) + + return keys +End From 015a268524cf3d835d4a4b76fddc704dd4856733 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 20 Feb 2026 11:44:08 +0100 Subject: [PATCH 33/34] SFO: Add operation getmeta --- Packages/MIES/MIES_Constants.ipf | 2 + Packages/MIES/MIES_JSONWaveNotes.ipf | 2 - Packages/MIES/MIES_SweepFormula.ipf | 2 +- Packages/MIES/MIES_SweepFormula_Executor.ipf | 3 + .../MIES/MIES_SweepFormula_Operations.ipf | 79 +++++++++++++++++++ 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 8727bbc376..84e14984ac 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2231,6 +2231,7 @@ StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric StrConstant SF_ANNOTATION_NAME = "metadata" StrConstant SF_VARNAME_REGEXP = "[A-Z]{1}[A-Z0-9_]*" +StrConstant SF_SERIALIZE = "/serial" // path prefix ///@} /// @name Constants for SweepFormula Clampmode codes returned by operation selcm() @@ -2574,6 +2575,7 @@ StrConstant SF_OP_FIT = "fit" StrConstant SF_OP_FITLINE = "fitline" StrConstant SF_OP_DATASET = "dataset" StrConstant SF_OP_FIT2 = "fit2" +StrConstant SF_OP_GETMETA = "getmeta" #ifdef AUTOMATED_TESTING StrConstant SF_OP_TESTOP = "testop" diff --git a/Packages/MIES/MIES_JSONWaveNotes.ipf b/Packages/MIES/MIES_JSONWaveNotes.ipf index 87a127ef8e..282f808a80 100644 --- a/Packages/MIES/MIES_JSONWaveNotes.ipf +++ b/Packages/MIES/MIES_JSONWaveNotes.ipf @@ -327,8 +327,6 @@ threadsafe Function/WAVE JWN_GetKeysAt(WAVE wv, string jsonPath) variable jsonID - ASSERT_TS(!IsEmpty(jsonPath), "Empty jsonPath") - jsonID = JWN_GetWaveNoteAsJSON(wv) WAVE/Z keys = JSON_GetKeys(jsonID, jsonPath, ignoreErr = 1) JSON_Release(jsonID) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 0adb09e820..63dac59d89 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -130,7 +130,7 @@ Function/WAVE SF_GetNamedOperations() 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_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM, SF_OP_CONCAT, SF_OP_TABLE, SF_OP_EXTRACT, \ - SF_OP_IVSCCAPFREQUENCY, SF_OP_PREPAREFIT, SF_OP_FIT2} + SF_OP_IVSCCAPFREQUENCY, SF_OP_PREPAREFIT, SF_OP_FIT2, SF_OP_GETMETA} #ifdef AUTOMATED_TESTING Make/FREE/T wtTest = {SF_OP_TESTOP} Concatenate/NP/T {wtTest}, wt diff --git a/Packages/MIES/MIES_SweepFormula_Executor.ipf b/Packages/MIES/MIES_SweepFormula_Executor.ipf index 7bd2acaddb..8526b9a05d 100644 --- a/Packages/MIES/MIES_SweepFormula_Executor.ipf +++ b/Packages/MIES/MIES_SweepFormula_Executor.ipf @@ -556,6 +556,9 @@ Function/WAVE SFE_FormulaExecutor(STRUCT SF_ExecutionData &exd, [variable srcLoc case SF_OP_FIT2: WAVE out = SFO_OperationFit2(exdop) break + case SF_OP_GETMETA: + WAVE out = SFO_OperationGetMeta(exdop) + break #ifdef AUTOMATED_TESTING case SF_OP_TESTOP: WAVE out = SFO_OperationTestop(exdop) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index d0f64a1c8f..4fe061e6d8 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -3825,3 +3825,82 @@ static Function/WAVE SFO_OperationFit2Impl(WAVE wvY, WAVE/Z wvX, WAVE/WAVE prepF return fitOutput End + +/// getmeta(data, [keyStr, datasetNumber] +Function/WAVE SFO_OperationGetMeta(STRUCT SF_ExecutionData &exd) + + string opShort = SF_OP_GETMETA + string key, str, path, serStr + variable dsNum, numDatasets, val + + SFH_CheckArgumentCount(exd, opShort, 0, maxArgs = 3) + WAVE/WAVE datasets = SFH_GetArgumentAsWave(exd, opShort, 0) + key = SFH_GetArgumentAsText(exd, opShort, 1, defValue="") + dsNum = SFH_GetArgumentAsNumeric(exd, opShort, 2, defValue=0) + + numDatasets = DimSize(datasets, ROWS) + SFH_ASSERT(dsNum < numDatasets, "Dataset with given number does not exist in input data") + WAVE/Z data = datasets[dsNum] + + WAVE/WAVE output = SFH_CreateSFRefWave(exd.graph, opShort, 1) + if(!WaveExists(data)) + return SFH_GetOutputForExecutor(output, exd.graph, opShort) + endif + + if(IsEmpty(key)) + WAVE/Z/T keys = JWN_GetKeysAt(data, SF_SERIALIZE) + serStr = "" + for(string key : keys) + path = SF_SERIALIZE + "/" + key + val = JWN_GetNumberFromWaveNote(data, path) + if(!IsNaN(val)) + serStr += key + ": " + num2str(val, "%f") + "\r" + continue + endif + str = JWN_GetStringFromWaveNote(data, path) + if(!IsEmpty(str)) + serStr += key + ":\r" + str + "\r" + continue + endif + WAVE/Z wvn = JWN_GetNumericWaveFromWaveNote(data, path) + if(WaveExists(wvn)) + str = NumericWaveToList(wvn, "\r") + serStr += key + ":\r" + str + continue + endif + WAVE/Z/T wt = JWN_GetTextWaveFromWaveNote(data, path) + if(WaveExists(wt)) + str = TextWaveToList(wt, "\r") + serStr += key + ":\r" + str + continue + endif + endfor + serStr = RemoveEnding(serStr, "\r") + Make/FREE/T wvt = {serStr} + output[0] = wvt + else + path = SF_SERIALIZE + "/" + key + val = JWN_GetNumberFromWaveNote(data, path) + if(!IsNaN(val)) + Make/FREE/D wv = {val} + SetDimLabel ROWS, 0, $key, wv + output[0] = wv + endif + str = JWN_GetStringFromWaveNote(data, path) + if(!IsEmpty(str)) + Make/FREE/T wvt = {str} + SetDimLabel ROWS, 0, $key, wvt + output[0] = wvt + endif + WAVE/Z wvn = JWN_GetNumericWaveFromWaveNote(data, path) + if(WaveExists(wvn)) + output[0] = wvn + endif + WAVE/Z/T wvt = JWN_GetTextWaveFromWaveNote(data, path) + if(WaveExists(wvt)) + output[0] = wvt + endif + endif + + return SFH_GetOutputForExecutor(output, exd.graph, opShort) +End From 13a200cb557316410512fff5c403682a99014c87 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 20 Feb 2026 11:44:40 +0100 Subject: [PATCH 34/34] f ivscc_apfrequency move meta data to meta data JWN block --- .../MIES/MIES_SweepFormula_Operations.ipf | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula_Operations.ipf b/Packages/MIES/MIES_SweepFormula_Operations.ipf index 4fe061e6d8..a1794b9adf 100644 --- a/Packages/MIES/MIES_SweepFormula_Operations.ipf +++ b/Packages/MIES/MIES_SweepFormula_Operations.ipf @@ -3069,7 +3069,7 @@ Function/WAVE SFO_OperationIVSCCApFrequency(STRUCT SF_ExecutionData &exd) Duplicate/O varBackup, varStorage SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_explist", wvResult) - SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_fit", fitResult) + SFH_AddVariableToStorage(exd.graph, "ivscc_apfrequency_fit", SFH_GetOutputForExecutor(fitResult, exd.graph, opShort)) JWN_SetNumberInWaveNote(plotAND, SF_META_PLOT, 1) @@ -3781,21 +3781,22 @@ static Function/WAVE SFO_OperationFit2Impl(WAVE wvY, WAVE/Z wvX, WAVE/WAVE prepF fitStatus = SFO_OperationFit2GetFitStatusMessage(V_FitError, V_FitQuitReason, V_FitNumIters) + JWN_CreatePath(fitOutput, SF_SERIALIZE) if(V_FitError) Make/FREE/D/N=0 fitOutput - JWN_SetNumberInWaveNote(fitOutput, SF_META_FITERROR, V_FitError) - JWN_SetNumberInWaveNote(fitOutput, SF_META_FITQUITREASON, V_FitQuitReason) - JWN_SetNumberInWaveNote(fitOutput, SF_META_FITNUMITERS, V_FitNumIters) + JWN_SetNumberInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITERROR, V_FitError) + JWN_SetNumberInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITQUITREASON, V_FitQuitReason) + JWN_SetNumberInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITNUMITERS, V_FitNumIters) endif - JWN_SetStringInWaveNote(fitOutput, SF_META_FITSTATUSMESSAGE, fitStatus) - JWN_SetStringInWaveNote(fitOutput, SF_META_FITFUNC, fitFuncName) + JWN_SetStringInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITSTATUSMESSAGE, fitStatus) + JWN_SetStringInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITFUNC, fitFuncName) if(V_FitError) return fitOutput endif - JWN_SetWaveInWaveNote(fitOutput, SF_META_FITCOEFS, coefs) + JWN_SetWaveInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITCOEFS, coefs) JWN_SetWaveInWaveNote(fitOutput, SF_META_ERRORBARYPLUS, yResiduals) JWN_SetWaveInWaveNote(fitOutput, SF_META_ERRORBARYMINUS, yResiduals) if(xErrorsOut) @@ -3807,18 +3808,18 @@ static Function/WAVE SFO_OperationFit2Impl(WAVE wvY, WAVE/Z wvX, WAVE/WAVE prepF endif // Possible more fit data: V_npnts, V_nterms, V_nheld - JWN_SetNumberInWaveNote(fitOutput, SF_META_FITCHISQUARE, V_ChiSq) + JWN_SetNumberInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITCHISQUARE, V_ChiSq) if(WaveExists(wSigma)) - JWN_SetWaveInWaveNote(fitOutput, SF_META_FITWSIGMA, wSigma) + JWN_SetWaveInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITWSIGMA, wSigma) endif if(WaveExists(mCovar)) - JWN_SetWaveInWaveNote(fitOutput, SF_META_FITMCOVAR, mCovar) + JWN_SetWaveInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITMCOVAR, mCovar) endif if(WaveExists(mFitConstraints)) - JWN_SetWaveInWaveNote(fitOutput, SF_META_FITMFITCONSTRAINT, mFitConstraints) + JWN_SetWaveInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITMFITCONSTRAINT, mFitConstraints) endif if(WaveExists(wFitConstraints)) - JWN_SetWaveInWaveNote(fitOutput, SF_META_FITWFITCONSTRAINT, wFitConstraints) + JWN_SetWaveInWaveNote(fitOutput, SF_SERIALIZE + SF_META_FITWFITCONSTRAINT, wFitConstraints) endif SFH_SetTraceStyleForFit(fitOutput)