Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid setting out of range DAScale values #2184

Merged
merged 17 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 171 additions & 12 deletions Packages/MIES/MIES_AnalysisFunctions.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -853,8 +853,8 @@ Function FitResistance(string device, variable headstage, [variable showPlot, va
endif
End

/// @brief Helper for setting the DAScale
Function SetDAScaleModOp(string device, variable headstage, variable modifier, string operator, [variable invert, variable roundTopA])
/// @brief Helper for setting the DAScale, see also SetDAScale()
Function SetDAScaleModOp(string device, variable sweepNo, variable headstage, variable modifier, string operator, [variable invert, variable roundTopA, variable limitCheck])

if(ParamIsDefault(invert))
invert = 0
Expand All @@ -868,13 +868,17 @@ Function SetDAScaleModOp(string device, variable headstage, variable modifier, s
roundTopA = !!roundTopA
endif

if(ParamIsDefault(limitCheck))
limitCheck = 1
else
limitCheck = !!limitCheck
endif

strswitch(operator)
case "+":
SetDAScale(device, headstage, offset = invert ? -modifier : modifier, roundTopA = roundTopA)
break
return SetDAScale(device, sweepNo, headstage, offset = invert ? -modifier : modifier, roundTopA = roundTopA, limitCheck = limitCheck)
case "*":
SetDAScale(device, headstage, relative = invert ? 1 / modifier : modifier, roundTopA = roundTopA)
break
return SetDAScale(device, sweepNo, headstage, relative = invert ? 1 / modifier : modifier, roundTopA = roundTopA, limitCheck = limitCheck)
default:
ASSERT(0, "Invalid operator")
break
Expand All @@ -883,16 +887,22 @@ End

/// @brief Set the DAScale value of the given headstage
///
/// @param device device
/// The limit check assumes that the next sweep is the next sweep of the current stimset (aka has a set count + 1).
///
/// @param device device
/// @param headstage MIES headstage
/// @param sweepNo Sweep number
/// @param absolute (optional) DAScale value in `A` (Amperes)
/// @param relative (optional) relative DAScale modifier
/// @param offset (optional) offset DAScale value
/// @param roundTopA (optional, defaults to false) round the set DAScale to integer pA values
Function SetDAScale(string device, variable headstage, [variable absolute, variable relative, variable offset, variable roundTopA])
/// @param limitCheck (optional, defaults to true) check if the new DAScale value would be out of range
///
/// @return 0 on sucessful limits check, 1 on out-of-range and NaN if the limits check was skipped
Function SetDAScale(string device, variable sweepNo, variable headstage, [variable absolute, variable relative, variable offset, variable roundTopA, variable limitCheck])

variable amps, DAC
string DAUnit, ctrl, lbl
variable amps, DAC, nextStimsetColumn, DAScaleLimit, skipCountExisting, setCount
string DAUnit, ctrl, lbl, stimSetName

ASSERT(ParamIsDefault(absolute) + ParamIsDefault(relative) + ParamIsDefault(offset) == 2, "One of absolute, relative or offset has to be present")

Expand All @@ -902,6 +912,12 @@ Function SetDAScale(string device, variable headstage, [variable absolute, varia
roundTopA = !!roundTopA
endif

if(ParamIsDefault(limitCheck))
limitCheck = 1
else
limitCheck = !!limitCheck
endif

DAC = AFH_GetDACFromHeadstage(device, headstage)
ASSERT(IsFinite(DAC), "Does not work with unassociated DA channels")

Expand All @@ -924,7 +940,27 @@ Function SetDAScale(string device, variable headstage, [variable absolute, varia

amps = roundTopA ? round(amps) : amps
ASSERT(IsFinite(amps), "Invalid non-finite value")

if(limitCheck)
stimSetName = AFH_GetStimSetNameForHeadstage(device, headstage)
WAVE numericalValues = GetLBNumericalValues(device)

nextStimsetColumn = AFH_GetNextSweepSetCount(numericalValues, sweepNo, headstage)

DAScaleLimit = DAP_GetDAScaleMax(device, headstage, stimsetName, nextStimsetColumn)
ASSERT(IsFinite(DAScaleLimit), "Unsupported return value from DAP_GetDataLimits")

// the border value is not valid, see DC_CheckIfDataWaveHasBorderVals
if(amps >= DAScaleLimit)
return 1
endif
endif

PGC_SetAndActivateControl(device, ctrl, val = amps)

if(limitCheck)
return 0
endif
End

/// @brief Return a list of required parameters
Expand Down Expand Up @@ -974,6 +1010,36 @@ Function/S ReachTargetVoltage_CheckParam(string name, STRUCT CheckParametersStru
endswitch
End

/// #ReachTargetVoltage:
///
/// Rows:
/// - Headstage
///
/// Cols:
/// - 0: Resistance [MΩ]
static Function/WAVE CreateOverrideResults()

variable numRows, numCols

numRows = LABNOTEBOOK_LAYER_COUNT
numCols = 1

WAVE/Z/D wv = GetOverrideResults()

if(WaveExists(wv))
ASSERT(IsNumericWave(wv), "overrideResults wave must be numeric here")
Redimension/D/N=(numRows, numCols) wv
else
Make/D/N=(numRows, numCols) root:overrideResults/WAVE=wv
endif

wv[] = 0

SetDimensionLabels(wv, "Resistance", COLS)

return wv
End

/// @brief Analysis function to experimentally determine the cell resistance by sweeping
/// through a wave of target voltages.
///
Expand Down Expand Up @@ -1016,7 +1082,7 @@ Function ReachTargetVoltage(string device, STRUCT AnalysisFunction_V3 &s)
return 1
endif

SetDAScale(device, i, absolute = -20e-12)
SetDAScale(device, s.sweepNo, i, absolute = -20e-12, limitCheck = 0)

autoBiasCheck = ampParam[%AutoBiasEnable][0][i]
holdingPotential = ampParam[%AutoBiasVcom][0][i]
Expand Down Expand Up @@ -1109,6 +1175,7 @@ Function ReachTargetVoltage(string device, STRUCT AnalysisFunction_V3 &s)
WAVE deltaV = LBN_GetNumericWave()
WAVE deltaI = LBN_GetNumericWave()
WAVE resistance = LBN_GetNumericWave()
WAVE oorDAScale = LBN_GetNumericWave()

CalculateTPLikePropsFromSweep(numericalValues, textualValues, s.scaledDACWave, deltaI, deltaV, resistance)

Expand All @@ -1120,6 +1187,14 @@ Function ReachTargetVoltage(string device, STRUCT AnalysisFunction_V3 &s)
WAVE/Z resistanceFitted = GetLastSetting(numericalValues, s.sweepNo, LABNOTEBOOK_USER_PREFIX + LBN_RESISTANCE_FIT, UNKNOWN_MODE)
ASSERT(WaveExists(resistanceFitted), "Expected fitted resistance data")

#ifdef AUTOMATED_TESTING
WAVE/Z overrideResults = GetOverrideResults()

if(WaveExists(overrideResults))
resistanceFitted[] = overrideResults[p][%Resistance] * MEGA_TO_ONE
endif
#endif

for(i = 0; i < NUM_HEADSTAGES; i += 1)

if(!statusHS[i])
Expand Down Expand Up @@ -1155,8 +1230,10 @@ Function ReachTargetVoltage(string device, STRUCT AnalysisFunction_V3 &s)
sprintf msg, "(%s, %d): ΔR = %.0W1PΩ, V_target = %.0W1PV, I = %.0W1PA", device, i, resistanceFitted[i], targetV, amps
DEBUGPRINT(msg)

SetDAScale(device, i, absolute = amps)
oorDAScale[i] = SetDAScale(device, s.sweepNo, i, absolute = amps)
endfor

ReportOutOfRangeDAScale(device, s.sweepNo, INVALID_ANALYSIS_FUNCTION, oorDAScale)
break
case POST_SET_EVENT:
if(!DAG_HeadstageIsHighestActive(device, s.headstage))
Expand Down Expand Up @@ -1196,6 +1273,88 @@ Function ReachTargetVoltage(string device, STRUCT AnalysisFunction_V3 &s)
endswitch
End

/// @brief Report a future out of range DAScale value to the user and the labnotebook
///
/// Usage for a single headstage:
///
/// \rst
/// .. code-block:: igorpro
///
/// WAVE oorDAScale = LBN_GetNumericWave()
/// oorDAScale[s.headstage] = SetDAScale(...)
///
/// if(oorDAScale[s.headstage])
/// ReportOutOfRangeDAScale(...)
/// endif
/// \endrst
Function ReportOutOfRangeDAScale(string device, variable sweepNo, variable anaFuncType, WAVE oorDAScale)

variable i
string key

ASSERT(GetHardwareType(device) != HARDWARE_SUTTER_DAC, "Missing support for Sutter amplifier")

switch(anaFuncType)
case PSQ_CHIRP:
case PSQ_RAMP:
case PSQ_DA_SCALE:
case PSQ_SQUARE_PULSE:
case PSQ_RHEOBASE:
key = CreateAnaFuncLBNKey(anaFuncType, PSQ_FMT_LBN_DASCALE_OOR)
ED_AddEntryToLabnotebook(device, key, oorDAScale, overrideSweepNo = sweepNo, unit = LABNOTEBOOK_BINARY_UNIT)
break
case MSQ_FAST_RHEO_EST:
case MSQ_DA_SCALE:
case SC_SPIKE_CONTROL:
key = CreateAnaFuncLBNKey(anaFuncType, MSQ_FMT_LBN_DASCALE_OOR)
ED_AddEntryToLabnotebook(device, key, oorDAScale, overrideSweepNo = sweepNo, unit = LABNOTEBOOK_BINARY_UNIT)
break
case INVALID_ANALYSIS_FUNCTION: // ReachTargetVoltage
ED_AddEntryToLabnotebook(device, LBN_DASCALE_OUT_OF_RANGE, oorDAScale, unit = LABNOTEBOOK_BINARY_UNIT)
break
default:
ASSERT(0, "Unknown analysis function")
endswitch

WAVE statusHS = DAG_GetChannelState(device, CHANNEL_TYPE_HEADSTAGE)

Make/FREE/N=(NUM_HEADSTAGES) failedHS = statusHS[p] && oorDAScale[p] == 1

if(Sum(failedHS) == 0)
return NaN
endif

printf "(%s) The DAScale value could not be set as it is out-of-range.\r", GetRTStackInfo(2)
printf "Please adjust the \"External Command Sensitivity\" in the MultiClamp Commander application and try again.\r"
ControlWindowToFront()

for(i = 0; i < NUM_HEADSTAGES; i += 1)

if(!failedHS[i])
continue
endif

ForceSetEvent(device, i)
endfor

RA_SkipSweeps(device, Inf, SWEEP_SKIP_AUTO)
End

/// @brief Manually force the pre/post set events
///
/// Required to do before skipping sweeps.
/// @todo this hack must go away.
static Function ForceSetEvent(string device, variable headstage)

variable DAC

WAVE setEventFlag = GetSetEventFlag(device)
DAC = AFH_GetDACFromHeadstage(device, headstage)

setEventFlag[DAC][%PRE_SET_EVENT] = 1
setEventFlag[DAC][%POST_SET_EVENT] = 1
End

Function/S SetControlInEvent_CheckParam(string name, STRUCT CheckParametersStruct &s)

string type, event
Expand Down
Loading