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

Add three new ZMQ publishers for TP results / Retrieve TP results #2230

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
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
147 changes: 140 additions & 7 deletions Packages/MIES/MIES_AmplifierInteraction.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -715,13 +715,13 @@ Function AI_UpdateChanAmpAssign(string device, variable headStage, variable clam
End

/// @brief Assert on invalid clamp modes, does nothing otherwise
Function AI_AssertOnInvalidClampMode(variable clampMode)
threadsafe Function AI_AssertOnInvalidClampMode(variable clampMode)

ASSERT(AI_IsValidClampMode(clampMode), "invalid clamp mode")
ASSERT_TS(AI_IsValidClampMode(clampMode), "invalid clamp mode")
End

/// @brief Return true if the given clamp mode is valid
Function AI_IsValidClampMode(variable clampMode)
threadsafe Function AI_IsValidClampMode(variable clampMode)

return clampMode == V_CLAMP_MODE || clampMode == I_CLAMP_MODE || clampMode == I_EQUAL_ZERO_MODE
End
Expand Down Expand Up @@ -842,7 +842,7 @@ static Function/S AI_GetMCCWinFilePath()
End

/// @brief Map from amplifier control names to @ref AI_SendToAmpConstants constants and clamp mode
Function [variable func, variable clampMode] AI_MapControlNameToFunctionConstant(string ctrl)
threadsafe Function [variable func, variable clampMode] AI_MapControlNameToFunctionConstant(string ctrl)

strswitch(ctrl)
// begin VC controls
Expand Down Expand Up @@ -904,7 +904,7 @@ Function [variable func, variable clampMode] AI_MapControlNameToFunctionConstant
return [MCC_PIPETTEOFFSET_FUNC, I_CLAMP_MODE]
// end IC controls
default:
ASSERT(0, "Unknown control " + ctrl)
ASSERT_TS(0, "Unknown control " + ctrl)
break
endswitch
End
Expand Down Expand Up @@ -999,7 +999,7 @@ Function/S AI_MapFunctionConstantToControl(variable func, variable clampMode)
End

/// @brief Map constants from @ref AI_SendToAmpConstants to human readable names
Function/S AI_MapFunctionConstantToName(variable func, variable clampMode)
threadsafe Function/S AI_MapFunctionConstantToName(variable func, variable clampMode)

AI_AssertOnInvalidClampMode(clampMode)

Expand Down Expand Up @@ -1097,7 +1097,7 @@ Function/S AI_MapFunctionConstantToName(variable func, variable clampMode)
return "AutoBiasEnable"
// end others
default:
ASSERT(0, "Invalid func: " + num2str(func))
ASSERT_TS(0, "Invalid func: " + num2str(func))
endswitch
End

Expand Down Expand Up @@ -1207,6 +1207,139 @@ static Function/S AI_AmpStorageControlToRowLabel(string ctrl)
endswitch
End

/// @brief Return the unit with prefix of the given function constant and clampMode
///
/// This uses the MIES internal units i.e. with prefixes.
threadsafe Function/S AI_GetUnitForFunctionConstant(variable func, variable clampMode)

AI_AssertOnInvalidClampMode(clampMode)

switch(func)
// begin AmpStorageWave row labels
case MCC_HOLDING_FUNC:

if(clampMode == V_CLAMP_MODE)
return "mV"
endif

return "pA"
case MCC_HOLDINGENABLE_FUNC:
return "On/Off"
case MCC_WHOLECELLCOMPCAP_FUNC:
return "pF"
case MCC_WHOLECELLCOMPRESIST_FUNC:
return "MΩ"
case MCC_WHOLECELLCOMPENABLE_FUNC:
return "On/Off"
case MCC_RSCOMPCORRECTION_FUNC:
return "%"
case MCC_RSCOMPPREDICTION_FUNC:
return "%"
case MCC_RSCOMPENABLE_FUNC:
return "On/Off"
case MCC_PIPETTEOFFSET_FUNC:
return "mV"
case MCC_AUTOFASTCOMP_FUNC:
return "a.u."
case MCC_AUTOSLOWCOMP_FUNC:
return "a.u."
case MCC_AUTOBRIDGEBALANCE_FUNC:
return "a.u."
case MCC_BRIDGEBALRESIST_FUNC:
return "MΩ"
case MCC_BRIDGEBALENABLE_FUNC:
return "On/Off"
case MCC_NEUTRALIZATIONCAP_FUNC:
return "pF"
case MCC_NEUTRALIZATIONENABL_FUNC:
return "On/Off"
// end AmpStorageWave row labels
// begin others
case MCC_AUTOWHOLECELLCOMP_FUNC:
return "a.u."
case MCC_RSCOMPBANDWIDTH_FUNC:
return "kHz"
case MCC_OSCKILLERENABLE_FUNC:
return "On/Off"
case MCC_AUTOPIPETTEOFFSET_FUNC:
return "a.u."
case MCC_FASTCOMPCAP_FUNC:
return "pF"
case MCC_FASTCOMPTAU_FUNC:
return "μs"
case MCC_SLOWCOMPCAP_FUNC:
return "pF"
case MCC_SLOWCOMPTAU_FUNC:
return "μs"
case MCC_SLOWCOMPTAUX20ENAB_FUNC:
return "On/Off"
case MCC_SLOWCURRENTINJENABL_FUNC:
return "On/Off"
case MCC_SLOWCURRENTINJLEVEL_FUNC:
return "mV"
case MCC_SLOWCURRENTINJSETLT_FUNC:
return "ms"
case MCC_PRIMARYSIGNALGAIN_FUNC:
return "a.u."
case MCC_SECONDARYSIGNALGAIN_FUNC:
return "a.u."
case MCC_PRIMARYSIGNALHPF_FUNC:
return "kHz"
case MCC_PRIMARYSIGNALLPF_FUNC:
return "kHz"
case MCC_SECONDARYSIGNALLPF_FUNC:
return "kHz"
case MCC_NO_AMPCHAIN_FUNC:
return "On/Off"
case MCC_NO_AUTOBIAS_V_FUNC:
return "mV"
case MCC_NO_AUTOBIAS_VRANGE_FUNC:
return "mV"
case MCC_NO_AUTOBIAS_IBIASMAX_FUNC:
return "pA"
case MCC_NO_AUTOBIAS_ENABLE_FUNC:
return "On/Off"
// end others
default:
ASSERT_TS(0, "Invalid func: " + num2str(func))
endswitch
End

/// @brief Return a wave with all function constants for the given clamp mode
threadsafe Function/WAVE AI_GetFunctionConstantForClampMode(variable clampMode)

string list, ctrl
variable func, clampModeRet, numEntries, i

AI_AssertOnInvalidClampMode(clampMode)

switch(clampMode)
case V_CLAMP_MODE:
list = AMPLIFIER_CONTROLS_VC
break
case I_CLAMP_MODE:
list = AMPLIFIER_CONTROLS_IC
break
default:
ASSERT_TS(0, "Invalid clamp mode")
endswitch

numEntries = ItemsInList(list)
Make/FREE/N=(numEntries) funcs
for(i = 0; i < numEntries; i += 1)
ctrl = StringFromList(i, list)
[func, clampModeRet] = AI_MapControlNameToFunctionConstant(ctrl)

ASSERT_TS(clampMode == clampModeRet, "Non-matching clamp mode")

funcs[i] = func
endfor

WAVE uniqueFuncs = GetUniqueEntries(funcs)

return uniqueFuncs
End

#ifdef AMPLIFIER_XOPS_PRESENT

///@brief Returns the holding command of the amplifier
Expand Down
42 changes: 27 additions & 15 deletions Packages/MIES/MIES_Constants.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -1853,18 +1853,23 @@ StrConstant LOGBOOK_WAVE_TEMP_FOLDER = "Temp"
/// @name All available ZeroMQ message filters
/// @anchor ZeroMQMessageFilters
///@{
StrConstant IVS_PUB_FILTER = "ivscc"
StrConstant PRESSURE_STATE_FILTER = "pressure:state"
StrConstant PRESSURE_SEALED_FILTER = "pressure:sealed"
StrConstant PRESSURE_BREAKIN_FILTER = "pressure:break in"
StrConstant AUTO_TP_FILTER = "testpulse:autotune result"
StrConstant AMPLIFIER_CLAMP_MODE_FILTER = "amplifier:clamp mode"
StrConstant AMPLIFIER_AUTO_BRIDGE_BALANCE = "amplifier:auto bridge balance"
StrConstant ANALYSIS_FUNCTION_PB = "analysis function:pipette in bath"
StrConstant ANALYSIS_FUNCTION_SE = "analysis function:seal evaluation"
StrConstant ANALYSIS_FUNCTION_VM = "analysis function:true resting membrane potential"
StrConstant DAQ_TP_STATE_CHANGE_FILTER = "data acquisition:state change"
StrConstant ANALYSIS_FUNCTION_AR = "analysis function:access resistance smoke"
StrConstant IVS_PUB_FILTER = "ivscc"
StrConstant PRESSURE_STATE_FILTER = "pressure:state"
StrConstant PRESSURE_SEALED_FILTER = "pressure:sealed"
StrConstant PRESSURE_BREAKIN_FILTER = "pressure:break in"
StrConstant AUTO_TP_FILTER = "testpulse:autotune result"
StrConstant ZMQ_FILTER_TPRESULT_NOW = "testpulse:results live"
StrConstant ZMQ_FILTER_TPRESULT_1S = "testpulse:results 1s update"
StrConstant ZMQ_FILTER_TPRESULT_5S = "testpulse:results 5s update"
StrConstant ZMQ_FILTER_TPRESULT_10S = "testpulse:results 10s update"
StrConstant ZMQ_FILTER_TPRESULT_NOW_WITH_DATA = "testpulse:results live with data"
StrConstant AMPLIFIER_CLAMP_MODE_FILTER = "amplifier:clamp mode"
StrConstant AMPLIFIER_AUTO_BRIDGE_BALANCE = "amplifier:auto bridge balance"
StrConstant ANALYSIS_FUNCTION_PB = "analysis function:pipette in bath"
StrConstant ANALYSIS_FUNCTION_SE = "analysis function:seal evaluation"
StrConstant ANALYSIS_FUNCTION_VM = "analysis function:true resting membrane potential"
StrConstant DAQ_TP_STATE_CHANGE_FILTER = "data acquisition:state change"
StrConstant ANALYSIS_FUNCTION_AR = "analysis function:access resistance smoke"
///@}

/// which is sufficient to represent each sample point time with a distinctive number up to rates of 10 MHz.
Expand Down Expand Up @@ -1999,9 +2004,9 @@ StrConstant DEFAULT_LIST_SEP = ";"
/// \endrst
///
/// From: 9th edition of the SI Brochure (2019), https://www.bipm.org/en/publications/si-brochure
StrConstant PREFIX_SHORT_LIST = ";Y;Z;E;P;T;G;M;k;h;da;d;c;m;mu;n;p;f;a;z;y"
StrConstant PREFIX_LONG_LIST = "one;yotta;zetta;exa;peta;tera;giga;mega;kilo;hecto;deca;deci;centi;milli;micro;nano;pico;femto;atto;zepto;yocto"
StrConstant PREFIX_VALUE_LIST = "1;1e24;1e21;1e18;1e15;1e12;1e9;1e6;1e3;1e2;1e1;1e-1;1e-2;1e-3;1e-6;1e-9;1e-12;1e-15;1e-18;1e-21;1e-24"
StrConstant PREFIX_SHORT_LIST = ";Y;Z;E;P;T;G;M;k;h;da;d;c;m;mu;μ;n;p;f;a;z;y"
StrConstant PREFIX_LONG_LIST = "one;yotta;zetta;exa;peta;tera;giga;mega;kilo;hecto;deca;deci;centi;milli;micro;micro;nano;pico;femto;atto;zepto;yocto"
StrConstant PREFIX_VALUE_LIST = "1;1e24;1e21;1e18;1e15;1e12;1e9;1e6;1e3;1e2;1e1;1e-1;1e-2;1e-3;1e-6;1e-6;1e-9;1e-12;1e-15;1e-18;1e-21;1e-24"

/// @name Possible return values for PSQ_DetermineSweepQCResults()
/// @anchor DetermineSweepQCReturns
Expand Down Expand Up @@ -2385,3 +2390,10 @@ Constant ABORTCODE_USERABORT = -1
///@}

StrConstant CONF_DEFAULT_SAVE_LOCATION = "C:MiesSave"

// If this constant with dimLabels is changed the following functions should be verified:
//
// TP_TSAnalysis
// GetTPResultAsyncBuffer
// GetTPResults (reuses same dimlabels partially)
StrConstant TP_ANALYSIS_DATA_LABELS = "BASELINE;STEADYSTATERES;INSTANTRES;ELEVATED_SS;ELEVATED_INST;NOW;HEADSTAGE;MARKER;NUMBER_OF_TP_CHANNELS;TIMESTAMP;TIMESTAMPUTC;CLAMPMODE;CLAMPAMP;BASELINEFRAC;CYCLEID;TPLENGTHPOINTSADC;PULSELENGTHPOINTSADC;PULSESTARTPOINTSADC;SAMPLINGINTERVALADC;TPLENGTHPOINTSDAC;PULSELENGTHPOINTSDAC;PULSESTARTPOINTSDAC;SAMPLINGINTERVALDAC;"
11 changes: 6 additions & 5 deletions Packages/MIES/MIES_ForeignFunctionInterface.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,12 @@ End
/// @sa PUB_GetJSONTemplate
Function/WAVE FFI_GetAvailableMessageFilters()

Make/FREE/T wv = {ZeroMQ_HEARTBEAT, IVS_PUB_FILTER, PRESSURE_STATE_FILTER, PRESSURE_SEALED_FILTER, \
PRESSURE_BREAKIN_FILTER, AUTO_TP_FILTER, AMPLIFIER_CLAMP_MODE_FILTER, \
AMPLIFIER_AUTO_BRIDGE_BALANCE, ANALYSIS_FUNCTION_PB, ANALYSIS_FUNCTION_SE, \
ANALYSIS_FUNCTION_VM, DAQ_TP_STATE_CHANGE_FILTER, \
ANALYSIS_FUNCTION_AR}
Make/FREE/T wv = {ZeroMQ_HEARTBEAT, IVS_PUB_FILTER, PRESSURE_STATE_FILTER, PRESSURE_SEALED_FILTER, \
PRESSURE_BREAKIN_FILTER, AUTO_TP_FILTER, AMPLIFIER_CLAMP_MODE_FILTER, \
AMPLIFIER_AUTO_BRIDGE_BALANCE, ANALYSIS_FUNCTION_PB, ANALYSIS_FUNCTION_SE, \
ANALYSIS_FUNCTION_VM, DAQ_TP_STATE_CHANGE_FILTER, \
ANALYSIS_FUNCTION_AR, ZMQ_FILTER_TPRESULT_NOW, ZMQ_FILTER_TPRESULT_1S, \
ZMQ_FILTER_TPRESULT_5S, ZMQ_FILTER_TPRESULT_10S, ZMQ_FILTER_TPRESULT_NOW_WITH_DATA}

Note/K wv, "Heartbeat is sent every 5 seconds."

Expand Down
46 changes: 29 additions & 17 deletions Packages/MIES/MIES_Oscilloscope.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [vari

STRUCT TPAnalysisInput tpInput
variable i, j
variable tpChannels, numADCs, numDACs, tpLengthPoints, tpStart, tpEnd, tpStartPos
variable tpChannels, numADCs, numDACs, tpLengthPointsADC, tpStart, tpEnd, tpStartPos
variable TPChanIndex, saveTP, clampAmp
variable headstage, fifoLatest, channelIndex
string hsList
Expand Down Expand Up @@ -516,10 +516,10 @@ Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [vari
WAVE TPSettings = GetTPSettings(device)
WAVE TPSettingsCalc = GetTPSettingsCalculated(device)

tpLengthPoints = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%totalLengthPointsTP_ADC] : TPSettingsCalc[%totalLengthPointsDAQ_ADC]
tpLengthPointsADC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%totalLengthPointsTP_ADC] : TPSettingsCalc[%totalLengthPointsDAQ_ADC]

// use a 'virtual' end position for fifoLatest for TP Mode since the input data contains one TP only
fifoLatest = (dataAcqOrTP == TEST_PULSE_MODE) ? tpLengthPoints : fifoPos
fifoLatest = (dataAcqOrTP == TEST_PULSE_MODE) ? tpLengthPointsADC : fifoPos

WAVE ADCs = GetADCListFromConfig(config)
WAVE DACs = GetDACListFromConfig(config)
Expand All @@ -530,33 +530,40 @@ Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [vari
numADCs = DimSize(ADCs, ROWS)

// note: currently this works for multiplier = 1 only, see DC_PlaceDataInDAQDataWave
Make/FREE/N=(tpLengthPoints) channelData
Make/FREE/N=(tpLengthPointsADC) channelData
WAVE tpInput.data = channelData

tpInput.device = device
tpInput.duration = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseLengthPointsTP_ADC] : TPSettingsCalc[%pulseLengthPointsDAQ_ADC]
tpInput.baselineFrac = TPSettingsCalc[%baselineFrac]
tpInput.tpLengthPoints = tpLengthPoints
tpInput.readTimeStamp = ticks * TICKS_TO_SECONDS
tpInput.activeADCs = tpChannels

tpStart = trunc(fifoPosGlobal / tpLengthPoints)
tpEnd = trunc(fifoLatest / tpLengthPoints)
tpInput.device = device
tpInput.tpLengthPointsADC = tpLengthPointsADC
tpInput.pulseLengthPointsADC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseLengthPointsTP_ADC] : TPSettingsCalc[%pulseLengthPointsDAQ_ADC]
tpInput.pulseStartPointsADC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseStartPointsTP_ADC] : TPSettingsCalc[%pulseStartPointsDAQ_ADC]
tpInput.samplingIntervalADC = DimDelta(scaledDataWave[numDACs], ROWS)
tpInput.tpLengthPointsDAC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%totalLengthPointsTP] : TPSettingsCalc[%totalLengthPointsDAQ]
tpInput.pulseLengthPointsDAC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseLengthPointsTP] : TPSettingsCalc[%pulseLengthPointsDAQ]
tpInput.pulseStartPointsDAC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseStartPointsTP] : TPSettingsCalc[%pulseStartPointsDAQ]
tpInput.samplingIntervalDAC = DimDelta(scaledDataWave[0], ROWS)
tpInput.baselineFrac = TPSettingsCalc[%baselineFrac]
tpInput.readTimeStamp = ticks * TICKS_TO_SECONDS
tpInput.activeADCs = tpChannels
tpInput.cycleId = ROVAR(GetTestpulseCycleID(device))

tpStart = trunc(fifoPosGlobal / tpLengthPointsADC)
tpEnd = trunc(fifoLatest / tpLengthPointsADC)
ASSERT(tpStart <= tpEnd, "New fifopos is smaller than previous fifopos")
Make/FREE/D/N=(tpEnd - tpStart) tpMarker
NewRandomSeed()
tpMarker[] = GetUniqueInteger()

DEBUGPRINT("tpChannels: ", var = tpChannels)
DEBUGPRINT("tpLength: ", var = tpLengthPoints)
DEBUGPRINT("tpLength: ", var = tpLengthPointsADC)

for(i = tpStart; i < tpEnd; i += 1)

tpInput.measurementMarker = tpMarker[i - tpStart]
tpStartPos = i * tpLengthPoints
tpStartPos = i * tpLengthPointsADC

if(saveTP)
Make/FREE/N=(tpLengthPoints, tpChannels) StoreTPWave
Make/FREE/N=(tpLengthPointsADC, tpChannels) StoreTPWave
for(j = 0; j < tpChannels; j += 1)
WAVE scaledChannel = scaledDataWave[numDACs + j]
Multithread StoreTPWave[][j] = scaledChannel[tpStartPos + p]
Expand All @@ -566,6 +573,11 @@ Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [vari
hsList = ""
endif

// Use same time for all headstages
tpInput.timeStamp = DateTime
tpInput.timeStampUTC = DateTimeInUTC()
tpInput.sendTPMessage = 1

for(j = 0; j < numADCs; j += 1)
if(ADCmode[j] == DAQ_CHANNEL_TYPE_TP)

Expand Down Expand Up @@ -608,7 +620,7 @@ Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [vari
endfor

if(dataAcqOrTP == DATA_ACQUISITION_MODE && tpEnd > tpStart)
tpStartPos = (tpEnd - 1) * tpLengthPoints
tpStartPos = (tpEnd - 1) * tpLengthPointsADC
if(DAG_GetNumericalValue(device, "check_settings_show_power"))
WAVE tpOsciForPowerSpectrum = GetScaledTPTempWave(device)
Make/FREE/D/N=(numADCs) tpColumns
Expand Down
Loading