Skip to content

Commit

Permalink
Merge pull request #2184 from AllenInstitute/feature/2184-avoid-setti…
Browse files Browse the repository at this point in the history
…ng-out-of-scale-stim-scale-factor

Avoid setting out of range DAScale values
  • Loading branch information
t-b authored Jan 21, 2025
2 parents 86bdadf + db6e32c commit be804f4
Show file tree
Hide file tree
Showing 28 changed files with 2,074 additions and 312 deletions.
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

0 comments on commit be804f4

Please sign in to comment.