Add ivscc_apfrequency operation#2599
Conversation
75c4b24 to
bd0beed
Compare
|
@timjarsky This is a first version to play around. There are still a few things I have to add, that I discuss farther below.
xaxisOffset and yaxisOffset are strings that can be General Plotting BehaviorThe operation itself returns internally a full plotting specification that is inserted by the formula plotter at the location where the operation appears in the notebook code. The operation creates only traces that are separated by
Thus, the plotter applies the 10% for x and y-axis when used like this, where the formula setting the plot properties is last in the chain: but not for this: because Operation ArgumentsCurrently:
with The (later) final arguments should also expose arguments from The default for On the basis of the experiment avgMethodTesting2.pxp the generated code is: I added a
for
for I need to add a Therefore, the An additional task from the issue is to add a variable that contains the names of the experiments. I can create this variable in the operation and add it to the variable storage of the formula notebook. It would be available then after the operation ran. |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
bd0beed to
d228f41
Compare
d228f41 to
5e60339
Compare
This comment was marked as outdated.
This comment was marked as outdated.
0b71dcf to
dc98aba
Compare
|
Thanks for handling the metadata management for mismatched sweep numbers across experiments. A few points for discussion:
|
dc98aba to
79f149a
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
79f149a to
95732dc
Compare
|
@timjarsky I have added support for the apfrequency argument block after the first four argument for ivscc_apfrequency. The arguments are now: The last four arguments are "forwarded" to apfrequency. |
|
@MichaelHuth, Are failing sweeps included or filtered out? If included, please update to use only passing sweeps. |
|
13a200c to
c855eb2
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 17 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| WAVE/Z/T keys = JWN_GetKeys(wv, "") | ||
| CHECK_EQUAL_TEXTWAVES(keys, {"a", "b"}) | ||
|
|
||
| WAVE/Z/T keys = JWN_GetKeys(wv, "I_DONT_EXIST") |
There was a problem hiding this comment.
This test redeclares the local variable keys a second time (WAVE/Z/T keys = ...) in the same function scope, which is not allowed and will cause a compile error. Reuse the existing keys variable (assignment without redeclaration) or use a different variable name for the second call.
| WAVE/Z/T keys = JWN_GetKeys(wv, "I_DONT_EXIST") | |
| keys = JWN_GetKeys(wv, "I_DONT_EXIST") |
| StrConstant SF_OP_MERGE = "merge" | ||
| StrConstant SF_OP_FIT = "fit" | ||
| StrConstant SF_OP_FITLINE = "fitline" | ||
| StrConstant SF_OP_DATASET = "dataset" |
There was a problem hiding this comment.
SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, and SF_OP_DATASET are defined twice in this block, which will cause a compile-time error due to duplicate StrConstant definitions. Remove the duplicated second set (lines 2577–2580) and keep only a single definition per operation constant.
| StrConstant SF_OP_MERGE = "merge" | |
| StrConstant SF_OP_FIT = "fit" | |
| StrConstant SF_OP_FITLINE = "fitline" | |
| StrConstant SF_OP_DATASET = "dataset" |
| variable numArgs, numCoefs | ||
| string fitfuncName, holdStr, checkStr | ||
|
|
||
| SFH_CheckArgumentCount(exd, opShort, 0, maxArgs = 4) |
There was a problem hiding this comment.
preparefit is documented/implemented to accept up to 5 arguments (including constraints at position 4), but SFH_CheckArgumentCount(..., maxArgs = 4) rejects any call that supplies the constraints argument. Update the max argument count to allow the constraints parameter.
| SFH_CheckArgumentCount(exd, opShort, 0, maxArgs = 4) | |
| SFH_CheckArgumentCount(exd, opShort, 0, maxArgs = 5) |
| 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 |
There was a problem hiding this comment.
SFO_OperationGetMeta calls JWN_GetKeysAt(...), but there is no such function added/defined in the repo (the new helper is JWN_GetKeys). This will fail to compile; replace this call with the correct API (and handle the null-wave case if the path does not exist).
| 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 | |
| WAVE/Z/T keys = JWN_GetKeys(data, SF_SERIALIZE) | |
| serStr = "" | |
| if(WaveExists(keys)) | |
| 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 | |
| endif |
| 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 | ||
|
|
||
| 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) |
There was a problem hiding this comment.
SFO_OperationIVSCCApFrequency constructs a SweepFormula string by interpolating experiment file names from uniqueFiles directly into expressions like selexp("%s") and into the ivscc_apfrequency_explist array without any escaping. On platforms where file names can contain " or other special characters, an attacker who controls experiment file names (e.g. via a crafted NWB/PXP on disk) can inject additional SweepFormula operations by breaking out of the quoted string (for example with a name like evil");otherOp()), which will then be executed under the user’s context. To avoid this injection vector, validate file/experiment names to a strict safe character set or escape them appropriately for the SweepFormula string literal context before inserting them into formula via sprintf/SF_AddExpressionToFormula.
c855eb2 to
9e61b08
Compare
- 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. - use *LP_Rheo* instead of *rheo* as stimset selection - better resilience where partial results are zero sized datasets
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.
- this allows the user to use getmeta($ivscc_apfrequency_fit)
We can just use SetDimensionLabelsFromWaveContents with cleanup for that.
The defaults for the prefix was switched.
9e61b08 to
3196644
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| formula = SF_AddExpressionToFormula(formula, expr) | ||
|
|
||
| if(!CmpStr(xaxisOffset, SF_OP_IVSCCAPFREQUENCY_FIRST)) | ||
| // expr = "ivsccavg_norm_x = merge($ivscccurrentavg - extract($ivscccurrentavg, 0, 0))" |
| 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) |
| // 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 |
| 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) |
| 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") |
| /// 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) |
| variable numArgs, numCoefs | ||
| string fitfuncName, holdStr, checkStr | ||
|
|
||
| numArgs = SFH_CheckArgumentCount(exd, opShort, 0, maxArgs = 4) |



close #2581
TBs new todos:
else // SF_OP_IVSCCAPFREQUENCY_NONEbranches and add fatal errorclose #2628