MIES icon indicating copy to clipboard operation
MIES copied to clipboard

MIES freezes for 10-15s when changing clampMode

Open t-b opened this issue 8 months ago • 3 comments

Megan Koch reported an issue with https://github.com/AllenInstitute/MIES/pull/2230 where changing the clamp mode takes way too long (10-15s).

I've tried reproducing that with 5b0bbddb9 (tests/Basic: Ensure that we don't get unexpected ZeroMQ messages, 2025-04-11) of branch feature/2230-Add_zeromq_pub_filters_for_TP with an ITC18 and a real MCC. But failed.

See also the following screencast.

https://github.com/user-attachments/assets/bd9feab0-8dd1-489d-850b-f564f8a4ef6c

t-b avatar Apr 11 '25 22:04 t-b

@t-b

EDIT: These are from running the TP, not the clamp mode switch. I noticed the TP is running about 10X slower than it normally does.

Total time: 50.4178, Time in Function code: 15.4583 (30.7%)
Top function percentages:
Function MIES_TestPulse.ipf TP_GetValuesFromTPStorage: 27%
Function MIES_IVSCC.ipf IVS_SaveExperiment: 20%
Function MIES_TestPulse.ipf TP_AutoFitBaseline: 15%
Function MIES_DAC-Hardware.ipf HW_NI_ReadDigital: 7%
Function MIES_ThreadsafeUtilities.ipf TS_GetNewestFromThreadQueue: 5%
Function MIES_DAC-Hardware.ipf HW_NI_ReadAnalogSingleAndSlow: 4%
Function MIES_DAC-Hardware.ipf HW_NI_WriteAnalogSingleAndSlow: 3%

Annotated Top Functions:

*******************************************************************************************
Function: MIES_TestPulse.ipf TP_GetValuesFromTPStorage; Percent total 27%
*******************************************************************************************
[00]*         	|Function/WAVE TP_GetValuesFromTPStorage(WAVE TPStorage, variable headstage, string entry, variable numReqEntries, [variable options])
[00]          	|
[00]          	|	variable i, idx, value, entryLayer, lastValidEntry, numValidEntries, currentAutoTPCycleID, latestAutoTPCycleID
[00]          	|
[00]*         	|	if(ParamIsDefault(options))
[00]*         	|		options = TP_GETVALUES_DEFAULT
[00]          	|	else
[00]*         	|		ASSERT(options == TP_GETVALUES_DEFAULT || options == TP_GETVALUES_LATEST_AUTOTPCYCLE, "Invalid option")
[00]          	|	endif
[00]          	|
[00]          	|	// NOTE_INDEX gives the next free index and therefore also the number of valid entries
[00]*         	|	numValidEntries = GetNumberFromWaveNote(TPStorage, NOTE_INDEX)
[00]          	|
[00]*         	|	if(numValidEntries <= 0)
[00]*         	|		return $""
[00]*         	|	endif
[00]          	|
[00]*         	|	lastValidEntry = numValidEntries - 1
[00]          	|
[00]*         	|	latestAutoTPCycleID = TPStorage[lastValidEntry][headstage][%autoTPCycleID]
[00]          	|
[00]*         	|	if(numReqEntries == Inf)
[00]          	|		entryLayer = FindDimLabel(TPstorage, LAYERS, entry)
[00]          	|		ASSERT(entryLayer >= 0, "Invalid entry")
[00]          	|		Duplicate/FREE/RMD=[0, lastValidEntry][headstage][entryLayer] TPStorage, slice
[00]          	|		Redimension/N=(numpnts(slice))/E=1 slice
[00]          	|
[00]          	|		if(options == TP_GETVALUES_LATEST_AUTOTPCYCLE)
[00]          	|			// filter all entries which don't have the latest ID out
[00]          	|			slice[] = (TPStorage[p][headstage][%autoTPCycleID] == latestAutoTPCycleID) ? slice[p] : NaN
[00]          	|		endif
[00]          	|
[00]*         	|		return ZapNaNs(slice)
[00]*         	|	endif
[00]          	|
[00]*         	|	ASSERT(IsInteger(numReqEntries) && numReqEntries > 0, "Number of required entries must be larger than zero")
[00]          	|
[00]*         	|	if(numReqEntries > numValidEntries)
[00]          	|		return $""
[00]*         	|	endif
[00]          	|
[00]*         	|	Make/FREE/D/N=(numReqEntries) result = NaN
[00]          	|
[00]          	|	// take the last finite values
[03]*         	|	for(i = lastValidEntry; i >= 0; i -= 1)
[00]*         	|		if(idx == numReqEntries)
[00]*         	|			break
[00]*         	|		endif
[00]          	|
[19]**        	|		value = TPStorage[i][headstage][%$entry]
[00]          	|
[05]*         	|		if(!IsFinite(value))
[00]*         	|			continue
[00]*         	|		endif
[00]          	|
[00]*         	|		if(options == TP_GETVALUES_LATEST_AUTOTPCYCLE)
[00]*         	|			currentAutoTPCycleID = TPStorage[i][headstage][%autoTPCycleID]
[00]          	|
[00]*         	|			if(currentAutoTPCycleID != latestAutoTPCycleID)
[00]          	|				continue
[00]          	|			endif
[00]*         	|		endif
[00]          	|
[00]*         	|		result[idx++] = value
[00]*         	|	endfor
[00]          	|
[00]*         	|	if(idx == numReqEntries)
[00]*         	|		ASSERT(!IsNaN(Sum(result)), "Expected non-nan sum")
[00]*         	|		return result
[00]          	|	endif
[00]          	|
[00]*         	|	return $""
[00]*         	|End

*******************************************************************************************
Function: MIES_IVSCC.ipf IVS_SaveExperiment; Percent total 20%
*******************************************************************************************
[00]          	|Function IVS_SaveExperiment(string filename)
[00]          	|
[00]          	|	variable err
[00]          	|
[00]          	|	AssertOnAndClearRTError()
[00]          	|	try
[20]**        	|		SaveExperiment/C/F={1, "", 2}/P=home as filename + ".pxp"; AbortOnRTE
[00]          	|	catch
[00]          	|		err = ClearRTError()
[00]          	|		ASSERT(0, "Could not save experiment due to code: " + num2istr(err))
[00]          	|	endtry
[00]          	|End


*******************************************************************************************
Function: MIES_TestPulse.ipf TP_AutoFitBaseline; Percent total 15%
*******************************************************************************************
[00]*         	|static Function [variable result, variable tau, variable baseline] TP_AutoFitBaseline(WAVE data, variable pulseLengthMS)
[00]          	|
[00]          	|	variable first, last, firstPnt, lastPnt, totalLength, debugEnabled, fitQuality, referenceThreshold
[00]          	|	variable V_FitQuitReason, V_FitOptions, V_FitError, V_AbortCode
[00]          	|	string msg, win
[00]          	|
[00]*         	|	ASSERT(IsFloatingPointWave(data), "Expected floating point wave")
[00]*         	|	ASSERT(DimSize(data, COLS) <= 1, "Invalid data dimensions")
[00]*         	|	ASSERT(DimOffset(data, ROWS) == 0, "Invalid dimension offset")
[00]*         	|	ASSERT(pulseLengthMS > 0, "Expected valid pulse length")
[00]          	|
[00]          	|	totalLength = DimDelta(data, ROWS) * (DimSize(data, ROWS) - 1)
[00]          	|
[00]*         	|	first = 1 / 4 * totalLength + 1 / 2 * pulseLengthMS
[00]*         	|	last  = 3 / 4 * totalLength - 1 / 2 * pulseLengthMS
[00]*         	|	ASSERT(first < last, "Invalid first/last")
[00]          	|
[00]*         	|	baseline = last - first
[00]          	|
[00]          	|	first += TP_BASELINE_FITTING_INSET
[00]          	|	last  -= TP_BASELINE_FITTING_INSET
[00]          	|
[00]          	|#ifdef DEBUGGING_ENABLED
[00]          	|	if(DP_DebuggingEnabledForCaller())
[00]          	|		debugEnabled = 1
[00]          	|
[00]          	|		Duplicate/O data, root:AutoTPDebuggingData/WAVE=displayedData
[00]          	|		Duplicate/O data, root:Res_AutoTPDebuggingData/WAVE=residuals
[00]          	|
[00]          	|		if(!WindowExists("AutoTPDebugging"))
[00]          	|			Display/K=1/N=AutoTPDebugging displayedData
[00]          	|
[00]          	|			AppendToGraph/W=AutoTPDebugging/L=res residuals
[00]          	|			AppendToGraph/W=AutoTPDebugging displayedData
[00]          	|			ModifyGraph/W=AutoTPDebugging rgb(Res_AutoTPDebuggingData)=(0, 0, 0), rgb(AutoTPDebuggingData)=(655355, 0, 0)
[00]          	|			ModifyGraph axisEnab(left)={0, 0.65}, axisEnab(res)={0.7, 1}, freePos(res)=0
[00]          	|		endif
[00]          	|
[00]          	|		Cursor/W=AutoTPDebugging A, $NameOfWave(displayedData), first
[00]          	|		Cursor/W=AutoTPDebugging B, $NameOfWave(displayedData), last
[00]          	|
[00]          	|		WAVE data      = root:AutoTPDebuggingData
[00]          	|		WAVE residuals = root:Res_AutoTPDebuggingData
[00]          	|	endif
[00]          	|#endif // DEBUGGING_ENABLED
[00]          	|
[00]*         	|	if(!debugEnabled)
[00]*         	|		Duplicate/FREE data, residuals
[00]          	|	endif
[00]          	|
[01]*         	|	residuals = NaN
[00]          	|
[00]*         	|	Make/FREE/D/N=3 coefWave
[00]          	|
[00]          	|	V_FitOptions = 4
[00]          	|
[00]*         	|	AssertOnAndClearRTError()
[00]          	|	try
[00]          	|		V_FitError  = 0
[00]          	|		V_AbortCode = 0
[00]          	|
[00]*         	|		firstPnt = ScaleToIndex(data, first, ROWS)
[00]*         	|		lastPnt  = ScaleToIndex(data, last, ROWS)
[13]*         	|		CurveFit/Q=(!debugEnabled)/N=(!debugEnabled)/NTHR=1/M=0/W=2 exp_XOffset, kwCWave=coefWave, data[firstPnt, lastPnt]/A=(debugEnabled)/R=residuals; AbortOnRTE
[00]*         	|	catch
[00]          	|		msg = GetRTErrMessage()
[00]          	|		ClearRTError()
[00]          	|
[00]          	|		ASSERT(0, "CurveFit failed with error: " + msg)
[00]          	|	endtry
[00]          	|
[00]*         	|	MakeWaveFree($"W_Sigma")
[00]*         	|	MakeWaveFree($"W_FitConstants")
[00]          	|
[00]*         	|	sprintf msg, "Fit result: tau %g, V_FitError %g, V_FitQuitReason %g\r", coefWave[2], V_FitError, V_FitQuitReason
[00]*         	|	DEBUGPRINT(msg)
[00]          	|
[00]*         	|	if(V_FitError || V_FitQuitReason)
[00]          	|		return [TP_BASELINE_FIT_RESULT_ERROR, NaN, baseline]
[00]          	|	endif
[00]          	|
[00]          	|	// @todo check coefficient sign, range, etc
[00]          	|
[00]          	|	// detect residuals being too large
[00]*         	|	Multithread residuals = residuals[p]^2
[00]*         	|	fitQuality         = Sum(residuals, first, last) / (lastPnt - firstPnt)
[00]*         	|	referenceThreshold = 0.25 * abs(WaveMax(data, first, last))
[00]          	|
[00]*         	|	sprintf msg, "fitQuality %g, referenceThreshold %g\r", fitQuality, referenceThreshold
[00]*         	|	DEBUGPRINT(msg)
[00]          	|
[00]*         	|	ASSERT(IsFinite(fitQuality), "Invalid fit quality")
[00]          	|
[00]*         	|	if(fitQuality > referenceThreshold)
[00]*         	|		return [TP_BASELINE_FIT_RESULT_TOO_NOISY, NaN, baseline]
[00]          	|	endif
[00]          	|
[00]*         	|	return [TP_BASELINE_FIT_RESULT_OK, coefWave[2], baseline]
[00]*         	|End

*******************************************************************************************
Function: MIES_DAC-Hardware.ipf HW_NI_ReadDigital; Percent total 07%
*******************************************************************************************
[00]*         	|Function HW_NI_ReadDigital(string device, [variable DIOPort, variable DIOLine, variable flags])
[00]          	|
[00]          	|	variable taskID, ret, result, lineGrouping
[00]          	|	string line
[00]          	|
[00]*         	|	DEBUGPRINTSTACKINFO()
[00]          	|
[00]*         	|	if(ParamIsDefault(DIOPort))
[00]*         	|		DIOPort = 0
[00]          	|	endif
[00]          	|
[00]*         	|	if(ParamIsDefault(DIOLine))
[00]          	|		sprintf line, "/%s/port%d", device, DIOPort
[00]*         	|		lineGrouping = 0
[00]          	|	else
[00]*         	|		lineGrouping = 1
[02]*         	|		ASSERT(DIOline <= fDAQmx_DIO_PortWidth(device, DIOport), "Line does not exist in port")
[00]*         	|		sprintf line, "/%s/port%d/line%d", device, DIOPort, DIOline
[00]*         	|	endif
[00]          	|
[00]*         	|	AssertOnAndClearRTError()
[04]*         	|	DAQmx_DIO_Config/DEV=device/DIR=1/LGRP=(lineGrouping) line
[00]          	|
[00]*         	|	if(ClearRTError())
[00]          	|		print fDAQmx_ErrorString()
[00]          	|		ControlWindowToFront()
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error calling DAQmx_DIO_Config")
[00]          	|		endif
[00]*         	|		return NaN
[00]*         	|	endif
[00]          	|
[00]*         	|	taskID = V_DAQmx_DIO_TaskNumber
[00]          	|
[01]*         	|	result = fDAQmx_DIO_Read(device, taskID)
[00]          	|
[01]*         	|	ret = fDAQmx_DIO_Finished(device, taskID)
[00]*         	|	if(ret)
[00]          	|		print fDAQmx_ErrorString()
[00]          	|		printf "Error %d: fDAQmx_DIO_Finished\r", ret
[00]          	|		ControlWindowToFront()
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error calling fDAQmx_DIO_Finished")
[00]*         	|		endif
[00]          	|	endif
[00]          	|
[00]*         	|	return result
[00]*         	|End

*******************************************************************************************
Function: MIES_ThreadsafeUtilities.ipf TS_GetNewestFromThreadQueue; Percent total 05%
*******************************************************************************************
[00]*         	|Function TS_GetNewestFromThreadQueue(variable tgID, string varName, [variable timeout_default, variable timeout_tries])
[00]          	|
[00]          	|	variable var, err, i
[00]          	|
[00]*         	|	ASSERT_TS(!isEmpty(varName), "varName must not be empty")
[00]          	|
[00]*         	|	if(IsNaN(tgID))
[00]*         	|		return NaN
[00]*         	|	endif
[00]          	|
[00]*         	|	if(ParamIsDefault(timeout_default))
[00]*         	|		timeout_default = NaN
[00]*         	|	endif
[00]          	|
[00]*         	|	if(ParamIsDefault(timeout_tries))
[00]*         	|		timeout_tries = Inf
[00]          	|	else
[00]*         	|		ASSERT(IsInteger(timeout_tries) && timeout_tries > 0, "Invalid timeout_tries")
[00]          	|	endif
[00]          	|
[00]          	|	var = NaN
[00]          	|
[00]*         	|	for(i = 0; i < timeout_tries; i += 1)
[00]*         	|		AssertOnAndClearRTError()
[05]*         	|		DFREF dfr = ThreadGroupGetDFR(tgID, TS_GET_REPEAT_TIMEOUT_IN_MS); err = GetRTError(1)
[00]          	|
[00]*         	|		if(err)
[00]          	|			ASSERT(err == TS_ERROR_INVALID_TGID, "Unexpected error value of " + num2str(err))
[00]*         	|			return NaN
[00]*         	|		endif
[00]          	|
[00]*         	|		if(!DataFolderRefStatus(dfr))
[00]*         	|			if(IsFinite(var))
[00]*         	|				return var
[00]*         	|			elseif(TS_ThreadGroupFinished(tgID))
[00]*         	|				return NaN
[00]          	|			else
[00]*         	|				continue
[00]*         	|			endif
[00]          	|		endif
[00]          	|
[00]*         	|		NVAR/Z/SDFR=dfr var_thread = $varName
[00]          	|
[00]*         	|		ASSERT_TS(NVAR_Exists(var_thread), "Expected variable from thread does not exist: " + varName)
[00]          	|
[00]          	|		// overwrite old values
[00]*         	|		var = var_thread
[00]          	|	endfor
[00]          	|
[00]          	|	return timeout_default
[00]*         	|End

*******************************************************************************************
Function: MIES_DAC-Hardware.ipf HW_NI_ReadAnalogSingleAndSlow; Percent total 04%
*******************************************************************************************
[00]*         	|Function HW_NI_ReadAnalogSingleAndSlow(string device, variable channel, [variable flags])
[00]          	|
[00]          	|	variable value
[00]          	|
[00]          	|	DEBUGPRINTSTACKINFO()
[00]          	|
[04]*         	|	value = fDAQmx_ReadChan(device, channel, HW_NI_MIN_VOLTAGE, HW_NI_MAX_VOLTAGE, HW_NI_DIFFERENTIAL_SETUP)
[00]          	|
[00]*         	|	if(!IsFinite(value))
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error " + fDAQmx_ErrorString())
[00]          	|		else
[00]          	|			DEBUGPRINT("Error: ", str = fDAQmx_ErrorString())
[00]*         	|		endif
[00]          	|	endif
[00]          	|
[00]*         	|	return value
[00]*         	|End

*******************************************************************************************
Function: MIES_DAC-Hardware.ipf HW_NI_WriteAnalogSingleAndSlow; Percent total 03%
*******************************************************************************************
[00]*         	|Function HW_NI_WriteAnalogSingleAndSlow(string device, variable channel, variable value, [variable flags])
[00]          	|
[00]          	|	variable ret
[00]          	|
[00]*         	|	DEBUGPRINTSTACKINFO()
[00]          	|
[00]*         	|	ASSERT(value < HW_NI_MAX_VOLTAGE && value > HW_NI_MIN_VOLTAGE, "Value to set is out of range")
[03]*         	|	ret = fDAQmx_WriteChan(device, channel, value, HW_NI_MIN_VOLTAGE, HW_NI_MAX_VOLTAGE)
[00]          	|
[00]*         	|	if(ret)
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error: " + fDAQmx_ErrorString())
[00]          	|		else
[00]          	|			DEBUGPRINT("Error: ", str = fDAQmx_ErrorString())
[00]*         	|		endif
[00]*         	|	endif
[00]          	|
[00]*         	|	return ret
[00]*         	|End

timjarsky avatar Apr 15 '25 20:04 timjarsky

@t-b

profiling results with zeromq_stop(), after MIES DA_ephys GUI clamp-mode switch, and TP in MIES was noticeably slow.

Total time: 12.6324, Time in Function code: 0.473828 (3.75%)
Top function percentages:
Function MIES_ThreadsafeUtilities.ipf TS_GetNewestFromThreadQueue: 33%
Function MIES_DAC-Hardware.ipf HW_NI_ReadDigital: 27%
Function MIES_DAC-Hardware.ipf HW_NI_WriteAnalogSingleAndSlow: 9%
Function MIES_Oscilloscope.ipf SCOPE_ITC_UpdateOscilloscope: 4%
Function MIES_Oscilloscope.ipf SCOPE_UpdateGraph: 3%
Function MIES_TestPulse.ipf TP_ROAnalysis: 3%
Function MIES_Oscilloscope.ipf SCOPE_UpdateOscilloscopeData: 2%

Annotated Top Functions:

*******************************************************************************************
Function: MIES_ThreadsafeUtilities.ipf TS_GetNewestFromThreadQueue; Percent total 33%
*******************************************************************************************
[00]*         	|Function TS_GetNewestFromThreadQueue(variable tgID, string varName, [variable timeout_default, variable timeout_tries])
[00]          	|
[00]          	|	variable var, err, i
[00]          	|
[00]*         	|	ASSERT_TS(!isEmpty(varName), "varName must not be empty")
[00]          	|
[00]*         	|	if(IsNaN(tgID))
[00]          	|		return NaN
[00]*         	|	endif
[00]          	|
[00]*         	|	if(ParamIsDefault(timeout_default))
[00]*         	|		timeout_default = NaN
[00]          	|	endif
[00]          	|
[00]*         	|	if(ParamIsDefault(timeout_tries))
[00]          	|		timeout_tries = Inf
[00]          	|	else
[00]*         	|		ASSERT(IsInteger(timeout_tries) && timeout_tries > 0, "Invalid timeout_tries")
[00]          	|	endif
[00]          	|
[00]*         	|	var = NaN
[00]          	|
[00]*         	|	for(i = 0; i < timeout_tries; i += 1)
[00]*         	|		AssertOnAndClearRTError()
[33]***       	|		DFREF dfr = ThreadGroupGetDFR(tgID, TS_GET_REPEAT_TIMEOUT_IN_MS); err = GetRTError(1)
[00]          	|
[00]*         	|		if(err)
[00]          	|			ASSERT(err == TS_ERROR_INVALID_TGID, "Unexpected error value of " + num2str(err))
[00]          	|			return NaN
[00]*         	|		endif
[00]          	|
[00]*         	|		if(!DataFolderRefStatus(dfr))
[00]*         	|			if(IsFinite(var))
[00]*         	|				return var
[00]          	|			elseif(TS_ThreadGroupFinished(tgID))
[00]          	|				return NaN
[00]          	|			else
[00]          	|				continue
[00]          	|			endif
[00]          	|		endif
[00]          	|
[00]*         	|		NVAR/Z/SDFR=dfr var_thread = $varName
[00]          	|
[00]*         	|		ASSERT_TS(NVAR_Exists(var_thread), "Expected variable from thread does not exist: " + varName)
[00]          	|
[00]          	|		// overwrite old values
[00]          	|		var = var_thread
[00]*         	|	endfor
[00]          	|
[00]*         	|	return timeout_default
[00]*         	|End

*******************************************************************************************
Function: MIES_DAC-Hardware.ipf HW_NI_ReadDigital; Percent total 27%
*******************************************************************************************
[00]*         	|Function HW_NI_ReadDigital(string device, [variable DIOPort, variable DIOLine, variable flags])
[00]          	|
[00]          	|	variable taskID, ret, result, lineGrouping
[00]          	|	string line
[00]          	|
[00]          	|	DEBUGPRINTSTACKINFO()
[00]          	|
[00]*         	|	if(ParamIsDefault(DIOPort))
[00]          	|		DIOPort = 0
[00]          	|	endif
[00]          	|
[00]          	|	if(ParamIsDefault(DIOLine))
[00]          	|		sprintf line, "/%s/port%d", device, DIOPort
[00]*         	|		lineGrouping = 0
[00]          	|	else
[00]*         	|		lineGrouping = 1
[06]*         	|		ASSERT(DIOline <= fDAQmx_DIO_PortWidth(device, DIOport), "Line does not exist in port")
[00]*         	|		sprintf line, "/%s/port%d/line%d", device, DIOPort, DIOline
[00]          	|	endif
[00]          	|
[00]*         	|	AssertOnAndClearRTError()
[13]*         	|	DAQmx_DIO_Config/DEV=device/DIR=1/LGRP=(lineGrouping) line
[00]          	|
[00]*         	|	if(ClearRTError())
[00]          	|		print fDAQmx_ErrorString()
[00]          	|		ControlWindowToFront()
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error calling DAQmx_DIO_Config")
[00]          	|		endif
[00]*         	|		return NaN
[00]*         	|	endif
[00]          	|
[00]*         	|	taskID = V_DAQmx_DIO_TaskNumber
[00]          	|
[05]*         	|	result = fDAQmx_DIO_Read(device, taskID)
[00]          	|
[02]*         	|	ret = fDAQmx_DIO_Finished(device, taskID)
[00]*         	|	if(ret)
[00]          	|		print fDAQmx_ErrorString()
[00]          	|		printf "Error %d: fDAQmx_DIO_Finished\r", ret
[00]          	|		ControlWindowToFront()
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error calling fDAQmx_DIO_Finished")
[00]*         	|		endif
[00]          	|	endif
[00]          	|
[00]*         	|	return result
[00]*         	|End

*******************************************************************************************
Function: MIES_DAC-Hardware.ipf HW_NI_WriteAnalogSingleAndSlow; Percent total 09%
*******************************************************************************************
[00]*         	|Function HW_NI_WriteAnalogSingleAndSlow(string device, variable channel, variable value, [variable flags])
[00]          	|
[00]          	|	variable ret
[00]          	|
[00]*         	|	DEBUGPRINTSTACKINFO()
[00]          	|
[00]*         	|	ASSERT(value < HW_NI_MAX_VOLTAGE && value > HW_NI_MIN_VOLTAGE, "Value to set is out of range")
[09]*         	|	ret = fDAQmx_WriteChan(device, channel, value, HW_NI_MIN_VOLTAGE, HW_NI_MAX_VOLTAGE)
[00]          	|
[00]*         	|	if(ret)
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error: " + fDAQmx_ErrorString())
[00]          	|		else
[00]          	|			DEBUGPRINT("Error: ", str = fDAQmx_ErrorString())
[00]          	|		endif
[00]          	|	endif
[00]          	|
[00]*         	|	return ret
[00]*         	|End

*******************************************************************************************
Function: MIES_DAC-Hardware.ipf HW_NI_WriteAnalogSingleAndSlow; Percent total 09%
*******************************************************************************************
[00]*         	|Function HW_NI_WriteAnalogSingleAndSlow(string device, variable channel, variable value, [variable flags])
[00]          	|
[00]          	|	variable ret
[00]          	|
[00]*         	|	DEBUGPRINTSTACKINFO()
[00]          	|
[00]*         	|	ASSERT(value < HW_NI_MAX_VOLTAGE && value > HW_NI_MIN_VOLTAGE, "Value to set is out of range")
[09]*         	|	ret = fDAQmx_WriteChan(device, channel, value, HW_NI_MIN_VOLTAGE, HW_NI_MAX_VOLTAGE)
[00]          	|
[00]*         	|	if(ret)
[00]          	|		if(flags & HARDWARE_ABORT_ON_ERROR)
[00]          	|			ASSERT(0, "Error: " + fDAQmx_ErrorString())
[00]          	|		else
[00]          	|			DEBUGPRINT("Error: ", str = fDAQmx_ErrorString())
[00]          	|		endif
[00]          	|	endif
[00]          	|
[00]*         	|	return ret
[00]*         	|End

*******************************************************************************************
Function: MIES_Oscilloscope.ipf SCOPE_ITC_UpdateOscilloscope; Percent total 04%
*******************************************************************************************
[00]*         	|static Function SCOPE_ITC_UpdateOscilloscope(string device, variable dataAcqOrTP, variable chunk, variable fifoPos)
[00]          	|
[00]          	|	WAVE OscilloscopeData = GetOscilloscopeWave(device)
[00]          	|	variable length, first, last
[00]          	|	variable startOfADColumns, endOfADColumns, decMethod, decFactor, i
[00]          	|	string msg
[00]*         	|	WAVE/WAVE scaledDataWave = GetScaledDataWave(device)
[00]          	|	WAVE      DAQDataWave    = GetDAQDataWave(device, dataAcqOrTP)
[00]          	|	WAVE      DAQConfigWave  = GetDAQConfigWave(device)
[00]*         	|	WAVE      ADCs           = GetADCListFromConfig(DAQConfigWave)
[00]*         	|	WAVE      DACs           = GetDACListFromConfig(DAQConfigWave)
[00]          	|	startOfADColumns = DimSize(DACs, ROWS)
[00]*         	|	endOfADColumns   = startOfADColumns + DimSize(ADCs, ROWS)
[00]          	|
[00]*         	|	WAVE allGain = SWS_GETChannelGains(device, timing = GAIN_AFTER_DAQ)
[00]          	|
[00]          	|	if(dataAcqOrTP == TEST_PULSE_MODE)
[00]*         	|		WAVE TPSettingsCalc = GetTPSettingsCalculated(device)
[00]*         	|		length = TPSettingsCalc[%totalLengthPointsTP_ADC]
[00]          	|		first  = chunk * length
[00]*         	|		last   = first + length - 1
[00]*         	|		ASSERT(first >= 0 && last < DimSize(DAQDataWave, ROWS) && first < last, "Invalid wave subrange")
[00]          	|
[00]          	|#ifdef DEBUGGING_ENABLED
[00]          	|		if(DP_DebuggingEnabledForCaller())
[00]          	|
[00]          	|			DAQDataWave[0][0] += 0
[00]          	|			if(!WindowExists("DAQDataWaveTPMD"))
[00]          	|				Display/N=DAQDataWaveTPMD DAQDataWave[][1]
[00]          	|			endif
[00]          	|
[00]          	|			Cursor/W=DAQDataWaveTPMD/H=2/P A, $NameOfWave(DAQDataWave), first
[00]          	|			Cursor/W=DAQDataWaveTPMD/H=2/P B, $NameOfWave(DAQDataWave), last
[00]          	|		endif
[00]          	|#endif // DEBUGGING_ENABLED
[00]          	|
[03]*         	|		Multithread OscilloscopeData[][startOfADColumns, endOfADColumns - 1] = DAQDataWave[first + p][q] / allGain[q]
[00]*         	|		for(i = startOfADColumns; i < endOfADColumns; i += 1)
[00]*         	|			WAVE scaledChannel = scaledDataWave[i]
[01]*         	|			Multithread scaledChannel[] = OscilloscopeData[p][i]
[00]          	|		endfor
[00]          	|
[00]*         	|		Make/FREE/N=(DimSize(ADCs, ROWS)) tpColumns
[00]*         	|		tpColumns[] = startOfADColumns + p
[00]          	|		SCOPE_UpdatePowerSpectrum(device, tpColumns)
[00]          	|
[00]          	|	elseif(dataAcqOrTP == DATA_ACQUISITION_MODE)
[00]          	|
[00]          	|		if(fifopos <= 0)
[00]          	|			return NaN
[00]          	|		endif
[00]          	|
[00]          	|		NVAR fifoPosGlobal = $GetFifoPosition(device)
[00]          	|
[00]          	|		if(fifoPosGlobal == fifoPos)
[00]          	|			return NaN
[00]          	|		endif
[00]          	|
[00]          	|		AssertOnAndClearRTError()
[00]          	|		try
[00]          	|			for(i = startOfADColumns; i < endOfADColumns; i += 1)
[00]          	|				WAVE scaledChannel = scaledDataWave[i]
[00]          	|				Multithread scaledChannel[fifoPosGlobal, fifoPos - 1] = DAQDataWave[p][i] / allGain[i]; AbortOnRTE
[00]          	|			endfor
[00]          	|		catch
[00]          	|			sprintf msg, "Writing scaledDataWave failed, please save the experiment and file a bug report: fifoPosGlobal %g, fifoPos %g, Channel Index %d, scaledChannelWave rows %g, stopCollectionPoint %g\r", fifoPosGlobal, fifoPos, i, DimSize(scaledChannel, ROWS), ROVAR(GetStopCollectionPoint(device))
[00]          	|			ASSERT(0, msg)
[00]          	|		endtry
[00]          	|
[00]          	|		decMethod = GetNumberFromWaveNote(OscilloscopeData, "DecimationMethod")
[00]          	|		decFactor = GetNumberFromWaveNote(OscilloscopeData, "DecimationFactor")
[00]          	|
[00]          	|		switch(decMethod)
[00]          	|			case DECIMATION_NONE:
[00]          	|				Multithread OscilloscopeData[fifoPosGlobal, fifoPos - 1][startOfADColumns, endOfADColumns - 1] = DAQDataWave[p][q] / allGain[q]
[00]          	|				break
[00]          	|			default:
[00]          	|				Duplicate/FREE/RMD=[startOfADColumns, endOfADColumns - 1] allGain, gain
[00]          	|				gain[] = 1 / gain[p]
[00]          	|				DecimateWithMethod(DAQDataWave, OscilloscopeData, decFactor, decMethod, firstRowInp = fifoPosGlobal, lastRowInp = fifoPos - 1, firstColInp = startOfADColumns, lastColInp = endOfADColumns - 1, factor = gain)
[00]          	|		endswitch
[00]          	|	else
[00]*         	|		ASSERT(0, "Invalid dataAcqOrTP value")
[00]*         	|	endif
[00]          	|End

*******************************************************************************************
Function: MIES_Oscilloscope.ipf SCOPE_UpdateGraph; Percent total 03%
*******************************************************************************************
[00]*         	|Function SCOPE_UpdateGraph(string device, variable dataAcqOrTP)
[00]          	|
[00]          	|	variable i, numADCs, range, numDACs, statsMin, statsMax
[00]          	|	variable axisMin, axisMax, spacing, additionalSpacing
[00]          	|	variable showSteadyStateResistance, showPeakResistance, showPowerSpectrum
[00]          	|	variable updateInt, now
[00]          	|	string graph, leftAxis
[00]          	|
[00]*         	|	NVAR timestamp = $GetLastAcqHookCallTimeStamp(device)
[00]*         	|	updateInt = DAG_GetNumericalValue(device, "setvar_Settings_OsciUpdInt")
[00]*         	|	now       = DateTime
[00]*         	|	if((now - timestamp) < (updateInt * MILLI_TO_ONE))
[00]          	|		return 0
[00]          	|	endif
[00]*         	|	timestamp = now
[00]          	|
[00]          	|	graph = SCOPE_GetGraph(device)
[00]*         	|	if(SCOPE_GetTPTopAxisStart(device, axisMin))
[00]          	|		SetAxis/W=$graph $AXIS_SCOPE_TP_TIME, axisMin, axisMin + SCOPE_TIMEAXIS_RESISTANCE_RANGE
[00]*         	|	endif
[00]          	|
[00]*         	|	if(DAG_GetNumericalValue(device, "Popup_Settings_OsciUpdMode") != GUI_SETTING_OSCI_SCALE_INTERVAL)
[00]          	|		return 0
[00]*         	|	endif
[00]          	|
[00]*         	|	[showSteadyStateResistance, showPeakResistance, showPowerSpectrum] = SCOPE_GetCheckBoxesForAddons(device, dataAcqOrTP)
[00]          	|
[00]*         	|	if(showPowerSpectrum)
[00]          	|		return NaN
[00]          	|	endif
[00]          	|
[00]          	|	if(!GotTPChannelsOnADCs(device))
[00]          	|		return NaN
[00]          	|	endif
[00]          	|
[00]*         	|	WAVE config  = GetDAQConfigWave(device)
[00]*         	|	WAVE ADCmode = GetADCTypesFromConfig(config)
[00]*         	|	WAVE ADCs    = GetADCListFromConfig(config)
[00]*         	|	WAVE DACs    = GetDACListFromConfig(config)
[00]*         	|	numADCs = DimSize(ADCs, ROWS)
[00]*         	|	numDACs = DimSize(DACs, ROWS)
[00]          	|
[00]*         	|	if(dataAcqOrTP == DATA_ACQUISITION_MODE)
[00]          	|		WAVE TPData = GetTPOscilloscopeWave(device)
[00]          	|	else
[00]*         	|		WAVE TPData = GetOscilloscopeWave(device)
[00]*         	|	endif
[00]          	|
[00]*         	|	additionalSpacing = DAG_GetNumericalValue(device, "setvar_Settings_OsciUpdExt") * PERCENT_TO_ONE
[00]          	|
[00]          	|	// scale the left AD axes
[00]*         	|	for(i = 0; i < numADCs; i += 1)
[00]          	|
[00]*         	|		if(ADCmode[i] != DAQ_CHANNEL_TYPE_TP)
[00]          	|			continue
[00]*         	|		endif
[00]          	|
[00]*         	|		leftAxis = AXIS_SCOPE_AD + num2str(ADCs[i])
[00]          	|
[03]*         	|		WaveStats/M=1/Q/RMD=[][numDACs + i] TPData
[00]          	|
[00]*         	|		statsMin = V_min
[00]*         	|		statsMax = V_max
[00]          	|
[00]          	|		// data is propably just zero, skip the axis
[00]*         	|		if(statsMin == statsMax || (IsNaN(statsMin) && IsNaN(statsMax)))
[00]          	|			continue
[00]          	|		endif
[00]          	|
[00]*         	|		GetAxis/Q/W=$graph $leftAxis
[00]*         	|		ASSERT(!V_Flag, "Expected axis does not exist")
[00]          	|
[00]          	|		axisMin = V_min
[00]          	|		axisMax = V_max
[00]          	|
[00]*         	|		if(axisMax == axisMin || (axisMin == -1 && axisMax == 1))
[00]          	|			spacing = (statsMax - statsMin) * additionalSpacing
[00]          	|		else
[00]*         	|			spacing = (axisMax - axisMin) * additionalSpacing
[00]          	|		endif
[00]          	|
[00]*         	|		if(axisMin < statsMin && abs(statsMin - axisMin) < spacing)
[00]*         	|			if(axisMax > statsMax && abs(statsMax - axisMax) < spacing)
[00]          	|				continue
[00]          	|			endif
[00]          	|		endif
[00]          	|
[00]          	|		SetAxis/W=$graph $leftAxis, statsMin - spacing / 2.0, statsMax + spacing / 2.0
[00]*         	|	endfor
[00]          	|End
*******************************************************************************************
Function: MIES_TestPulse.ipf TP_ROAnalysis; Percent total 03%
*******************************************************************************************
[00]*         	|Function TP_ROAnalysis(STRUCT ASYNC_ReadOutStruct &ar)
[00]          	|
[00]          	|	variable i, j, bufSize, headstage, marker
[00]          	|	variable posMarker, posAsync
[00]          	|	string lbl
[00]          	|
[00]*         	|	if(ar.rtErr || ar.abortCode)
[00]          	|		ASSERT(!ar.rtErr, "TP analysis thread encountered RTE " + ar.rtErrMsg)
[00]          	|		ASSERT(!ar.abortCode, "TP analysis thread aborted with code: " + GetErrMessage(ar.abortCode))
[00]          	|	endif
[00]          	|
[00]*         	|	DFREF dfr = ar.dfr
[00]          	|
[00]*         	|	WAVE/SDFR=dfr inData = tpData
[00]*         	|	SVAR/SDFR=dfr device = device
[00]*         	|	headstage = inData[%HEADSTAGE]
[00]*         	|	marker    = inData[%MARKER]
[00]          	|
[00]*         	|	WAVE asyncBuffer = GetTPResultAsyncBuffer(device)
[00]          	|
[00]*         	|	bufSize   = DimSize(asyncBuffer, ROWS)
[00]*         	|	posMarker = FindDimLabel(asyncBuffer, LAYERS, "MARKER")
[00]*         	|	posAsync  = FindDimLabel(asyncBuffer, COLS, "ASYNCDATA")
[00]          	|
[00]*         	|	FindValue/RMD=[][posAsync][posMarker, posMarker]/V=(marker)/T=0 asyncBuffer
[00]*         	|	i = (V_Value >= 0) ? V_Row : bufSize
[00]          	|
[00]*         	|	if(i == bufSize)
[00]*         	|		Redimension/N=(bufSize + 1, -1, -1) asyncBuffer
[00]*         	|		asyncBuffer[bufSize][][]                      = NaN
[00]*         	|		asyncBuffer[bufSize][posAsync][%REC_CHANNELS] = 0
[00]*         	|		asyncBuffer[bufSize][posAsync][posMarker]     = marker
[00]          	|	endif
[00]          	|
[00]*         	|	WAVE/T dimLabels = ListToTextWave(TP_ANALYSIS_DATA_LABELS, ";")
[00]*         	|	for(lbl : dimLabels)
[00]*         	|		asyncBuffer[i][%$lbl][headstage] = inData[%$lbl]
[00]          	|	endfor
[00]*         	|	asyncBuffer[i][posAsync][%REC_CHANNELS] += 1
[00]          	|
[00]          	|	// got one set of results ready
[00]*         	|	if(asyncBuffer[i][posAsync][%REC_CHANNELS] == inData[%NUMBER_OF_TP_CHANNELS])
[00]          	|
[00]*         	|		WAVE TPResults  = GetTPResults(device)
[00]*         	|		WAVE TPSettings = GetTPSettings(device)
[00]          	|
[00]*         	|		MultiThread TPResults[%BaselineSteadyState][] = asyncBuffer[i][%BASELINE][q]
[00]*         	|		MultiThread TPResults[%ResistanceSteadyState][] = asyncBuffer[i][%STEADYSTATERES][q]
[00]*         	|		MultiThread TPResults[%ResistanceInst][] = asyncBuffer[i][%INSTANTRES][q]
[00]*         	|		MultiThread TPResults[%ElevatedSteadyState][] = asyncBuffer[i][%ELEVATED_SS][q]
[00]*         	|		MultiThread TPResults[%ElevatedInst][] = asyncBuffer[i][%ELEVATED_INST][q]
[00]*         	|		MultiThread TPResults[%NOW][] = asyncBuffer[i][%NOW][q]
[00]*         	|		MultiThread TPResults[%HEADSTAGE][] = asyncBuffer[i][%HEADSTAGE][q]
[00]*         	|		MultiThread TPResults[%MARKER][] = asyncBuffer[i][%MARKER][q]
[00]*         	|		MultiThread TPResults[%NUMBER_OF_TP_CHANNELS][] = asyncBuffer[i][%NUMBER_OF_TP_CHANNELS][q]
[00]*         	|		MultiThread TPResults[%TIMESTAMP][] = asyncBuffer[i][%TIMESTAMP][q]
[00]*         	|		MultiThread TPResults[%TIMESTAMPUTC][] = asyncBuffer[i][%TIMESTAMPUTC][q]
[00]*         	|		MultiThread TPResults[%CLAMPMODE][] = asyncBuffer[i][%CLAMPMODE][q]
[00]*         	|		MultiThread TPResults[%CLAMPAMP][] = asyncBuffer[i][%CLAMPAMP][q]
[00]*         	|		MultiThread TPResults[%BASELINEFRAC][] = asyncBuffer[i][%BASELINEFRAC][q]
[00]*         	|		MultiThread TPResults[%CYCLEID][] = asyncBuffer[i][%CYCLEID][q]
[00]*         	|		MultiThread TPResults[%TPLENGTHPOINTSADC][] = asyncBuffer[i][%TPLENGTHPOINTSADC][q]
[00]*         	|		MultiThread TPResults[%PULSELENGTHPOINTSADC][] = asyncBuffer[i][%PULSELENGTHPOINTSADC][q]
[00]*         	|		MultiThread TPResults[%PULSESTARTPOINTSADC][] = asyncBuffer[i][%PULSESTARTPOINTSADC][q]
[00]*         	|		MultiThread TPResults[%SAMPLINGINTERVALADC][] = asyncBuffer[i][%SAMPLINGINTERVALADC][q]
[00]*         	|		MultiThread TPResults[%TPLENGTHPOINTSDAC][] = asyncBuffer[i][%TPLENGTHPOINTSDAC][q]
[00]*         	|		MultiThread TPResults[%PULSELENGTHPOINTSDAC][] = asyncBuffer[i][%PULSELENGTHPOINTSDAC][q]
[00]*         	|		MultiThread TPResults[%PULSESTARTPOINTSDAC][] = asyncBuffer[i][%PULSESTARTPOINTSDAC][q]
[00]*         	|		MultiThread TPResults[%SAMPLINGINTERVALDAC][] = asyncBuffer[i][%SAMPLINGINTERVALDAC][q]
[00]          	|
[00]          	|		// Remove finished results from buffer
[00]*         	|		DeletePoints i, 1, asyncBuffer
[00]          	|		if(!DimSize(asyncBuffer, ROWS))
[00]*         	|			KillOrMoveToTrash(wv = asyncBuffer)
[00]          	|		endif
[00]          	|
[00]*         	|		if(TPSettings[%bufferSize][INDEP_HEADSTAGE] > 1)
[00]          	|			WAVE TPResultsBuffer = GetTPResultsBuffer(device)
[00]          	|			TP_CalculateAverage(TPResultsBuffer, TPResults)
[00]*         	|		endif
[00]          	|
[00]*         	|		TPResults[%AutoTPAmplitude][]             = NaN
[00]*         	|		TPResults[%AutoTPBaseline][]              = NaN
[00]*         	|		TPResults[%AutoTPBaselineRangeExceeded][] = NaN
[00]*         	|		TPResults[%AutoTPBaselineFitResult][]     = NaN
[00]          	|
[00]*         	|		MultiThread TPResults[%AutoTPDeltaV][] = TPResults[%ElevatedSteadyState][q] - TPResults[%BaselineSteadyState][q]
[00]          	|
[00]*         	|		TP_AutoAmplitudeAndBaseline(device, TPResults, marker)
[00]*         	|		DQ_ApplyAutoBias(device, TPResults)
[00]*         	|		TP_RecordTP(device, TPResults, inData[%NOW])
[00]*         	|	endif
[00]          	|End
*******************************************************************************************
Function: MIES_Oscilloscope.ipf SCOPE_UpdateOscilloscopeData; Percent total 02%
*******************************************************************************************
[00]*         	|Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [variable chunk, variable fifoPos, variable deviceID])
[00]          	|
[00]          	|	STRUCT TPAnalysisInput tpInput
[00]          	|	variable i, j
[00]          	|	variable tpChannels, numADCs, numDACs, tpLengthPointsADC, tpStart, tpEnd, tpStartPos
[00]          	|	variable TPChanIndex, saveTP, clampAmp
[00]          	|	variable headstage, fifoLatest, channelIndex
[00]          	|	string hsList
[00]          	|
[00]*         	|	variable hardwareType = GetHardwareType(device)
[00]*         	|	switch(hardwareType)
[00]          	|		case HARDWARE_ITC_DAC:
[00]*         	|			if(dataAcqOrTP == TEST_PULSE_MODE)
[00]          	|				if(ParamIsDefault(chunk))
[00]          	|					chunk = 0
[00]          	|				endif
[00]*         	|				ASSERT(ParamIsDefault(fifoPos), "optional parameter fifoPos is not possible with TEST_PULSE_MODE")
[00]          	|			elseif(dataAcqOrTP == DATA_ACQUISITION_MODE)
[00]          	|				ASSERT(!ParamIsDefault(fifoPos), "optional parameter fifoPos missing")
[00]          	|				ASSERT(ParamIsDefault(chunk), "optional parameter chunk is not possible with DATA_ACQUISITION_MODE")
[00]          	|				fifopos = SCOPE_ITC_AdjustFIFOPos(device, fifopos)
[00]*         	|				ASSERT(IsFinite(fifopos), "Invalid fifo position")
[00]          	|			endif
[00]*         	|			SCOPE_ITC_UpdateOscilloscope(device, dataAcqOrTP, chunk, fifoPos)
[00]*         	|			break
[00]          	|		case HARDWARE_NI_DAC:
[00]          	|			ASSERT(!ParamIsDefault(deviceID), "optional parameter deviceID missing (required for NI devices in TP mode)")
[00]          	|			SCOPE_NI_UpdateOscilloscope(device, dataAcqOrTP, deviceID, fifoPos)
[00]          	|			break
[00]          	|		case HARDWARE_SUTTER_DAC:
[00]          	|			if(dataAcqOrTP == TEST_PULSE_MODE)
[00]          	|				ASSERT(!ParamIsDefault(chunk), "optional parameter chunk is missing with TEST_PULSE_MODE")
[00]          	|				ASSERT(ParamIsDefault(fifoPos), "optional parameter fifoPos is not possible with TEST_PULSE_MODE")
[00]          	|			elseif(dataAcqOrTP == DATA_ACQUISITION_MODE)
[00]          	|				ASSERT(!ParamIsDefault(fifoPos), "optional parameter fifoPos missing")
[00]          	|				ASSERT(ParamIsDefault(chunk), "optional parameter chunk is not possible with DATA_ACQUISITION_MODE")
[00]          	|			endif
[00]          	|			SCOPE_SU_UpdateOscilloscope(device, dataAcqOrTP, chunk, fifoPos)
[00]          	|			break
[00]          	|		default:
[00]*         	|			ASSERT(0, "Unsupported hardware type")
[00]          	|	endswitch
[00]          	|
[00]*         	|	WAVE config  = GetDAQConfigWave(device)
[00]          	|	WAVE ADCmode = GetADCTypesFromConfig(config)
[00]*         	|	tpChannels = GetNrOfTypedChannels(ADCmode, DAQ_CHANNEL_TYPE_TP)
[00]          	|
[00]          	|	// send data to TP Analysis if TP present
[00]*         	|	NVAR fifoPosGlobal = $GetFifoPosition(device)
[00]          	|
[00]*         	|	if(tpChannels)
[00]          	|		saveTP = DAG_GetNumericalValue(device, "check_Settings_TP_SaveTP")
[00]          	|		WAVE TPSettings     = GetTPSettings(device)
[00]*         	|		WAVE TPSettingsCalc = GetTPSettingsCalculated(device)
[00]          	|
[00]*         	|		tpLengthPointsADC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%totalLengthPointsTP_ADC] : TPSettingsCalc[%totalLengthPointsDAQ_ADC]
[00]          	|
[00]          	|		// use a 'virtual' end position for fifoLatest for TP Mode since the input data contains one TP only
[00]*         	|		fifoLatest = (dataAcqOrTP == TEST_PULSE_MODE) ? tpLengthPointsADC : fifoPos
[00]          	|
[00]*         	|		WAVE ADCs   = GetADCListFromConfig(config)
[00]*         	|		WAVE DACs   = GetDACListFromConfig(config)
[00]*         	|		WAVE hsProp = GetHSProperties(device)
[00]          	|
[00]*         	|		WAVE/WAVE scaledDataWave = GetScaledDataWave(device)
[00]*         	|		numDACs = DimSize(DACs, ROWS)
[00]          	|		numADCs = DimSize(ADCs, ROWS)
[00]          	|
[00]          	|		// note: currently this works for multiplier = 1 only, see DC_PlaceDataInDAQDataWave
[00]*         	|		Make/FREE/N=(tpLengthPointsADC) channelData
[00]*         	|		WAVE tpInput.data = channelData
[00]          	|
[00]*         	|		tpInput.device               = device
[00]*         	|		tpInput.tpLengthPointsADC    = tpLengthPointsADC
[00]*         	|		tpInput.pulseLengthPointsADC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseLengthPointsTP_ADC] : TPSettingsCalc[%pulseLengthPointsDAQ_ADC]
[00]*         	|		tpInput.pulseStartPointsADC  = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseStartPointsTP_ADC] : TPSettingsCalc[%pulseStartPointsDAQ_ADC]
[00]*         	|		tpInput.samplingIntervalADC  = DimDelta(scaledDataWave[numDACs], ROWS)
[00]*         	|		tpInput.tpLengthPointsDAC    = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%totalLengthPointsTP] : TPSettingsCalc[%totalLengthPointsDAQ]
[00]*         	|		tpInput.pulseLengthPointsDAC = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseLengthPointsTP] : TPSettingsCalc[%pulseLengthPointsDAQ]
[00]*         	|		tpInput.pulseStartPointsDAC  = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseStartPointsTP] : TPSettingsCalc[%pulseStartPointsDAQ]
[00]*         	|		tpInput.samplingIntervalDAC  = DimDelta(scaledDataWave[0], ROWS)
[00]*         	|		tpInput.baselineFrac         = TPSettingsCalc[%baselineFrac]
[00]*         	|		tpInput.readTimeStamp        = ticks * TICKS_TO_SECONDS
[00]          	|		tpInput.activeADCs           = tpChannels
[00]          	|		tpInput.cycleId              = ROVAR(GetTestpulseCycleID(device))
[00]          	|
[00]*         	|		tpStart = trunc(fifoPosGlobal / tpLengthPointsADC)
[00]          	|		tpEnd   = trunc(fifoLatest / tpLengthPointsADC)
[00]*         	|		ASSERT(tpStart <= tpEnd, "New fifopos is smaller than previous fifopos")
[00]*         	|		Make/FREE/D/N=(tpEnd - tpStart) tpMarker
[00]*         	|		NewRandomSeed()
[00]*         	|		tpMarker[] = GetUniqueInteger()
[00]          	|
[00]*         	|		DEBUGPRINT("tpChannels: ", var = tpChannels)
[00]          	|		DEBUGPRINT("tpLength: ", var = tpLengthPointsADC)
[00]          	|
[00]          	|		for(i = tpStart; i < tpEnd; i += 1)
[00]          	|
[00]*         	|			tpInput.measurementMarker = tpMarker[i - tpStart]
[00]*         	|			tpStartPos                = i * tpLengthPointsADC
[00]          	|
[00]          	|			if(saveTP)
[00]          	|				Make/FREE/N=(tpLengthPointsADC, tpChannels) StoreTPWave
[00]          	|				for(j = 0; j < tpChannels; j += 1)
[00]          	|					WAVE scaledChannel = scaledDataWave[numDACs + j]
[00]          	|					Multithread StoreTPWave[][j] = scaledChannel[tpStartPos + p]
[00]          	|				endfor
[00]          	|				CopyScales/P scaledChannel, StoreTPWave
[00]          	|				TPChanIndex = 0
[00]*         	|				hsList      = ""
[00]          	|			endif
[00]          	|
[00]          	|			// Use same time for all headstages
[00]*         	|			tpInput.timeStamp     = DateTime
[00]*         	|			tpInput.timeStampUTC  = DateTimeInUTC()
[00]*         	|			tpInput.sendTPMessage = 1
[00]          	|
[00]*         	|			for(j = 0; j < numADCs; j += 1)
[00]          	|				if(ADCmode[j] == DAQ_CHANNEL_TYPE_TP)
[00]          	|
[00]*         	|					WAVE scaledChannel = scaledDataWave[numDACs + j]
[01]*         	|					MultiThread channelData[] = scaledChannel[tpStartPos + p]
[00]*         	|					CopyScales/P scaledChannel, channelData
[00]          	|
[00]*         	|					headstage = AFH_GetHeadstageFromADC(device, ADCs[j])
[00]*         	|					if(hsProp[headstage][%ClampMode] == I_CLAMP_MODE)
[00]          	|						clampAmp = TPSettings[%amplitudeIC][headstage]
[00]          	|					else
[00]*         	|						clampAmp = TPSettings[%amplitudeVC][headstage]
[00]          	|					endif
[00]          	|					tpInput.clampAmp  = clampAmp
[00]*         	|					tpInput.clampMode = hsProp[headstage][%ClampMode]
[00]*         	|					tpInput.headstage = headstage
[00]          	|
[00]*         	|					DEBUGPRINT("headstage: ", var = headstage)
[00]*         	|					DEBUGPRINT("channel: ", var = numDACs + j)
[00]          	|
[00]*         	|					TP_SendToAnalysis(device, tpInput)
[00]          	|
[00]*         	|					if(saveTP)
[00]          	|						hsList = AddListItem(num2str(headstage), hsList, ",", Inf)
[00]          	|						if(TPChanIndex != j)
[00]          	|							MultiThread StoreTPWave[][TPChanIndex] = channelData[p]
[00]          	|						endif
[00]*         	|						TPChanIndex += 1
[00]          	|					endif
[00]          	|
[00]          	|				endif
[00]          	|			endfor
[00]          	|
[00]          	|			if(saveTP)
[00]          	|				DEBUGPRINT("Storing TP with marker: ", var = tpInput.measurementMarker)
[00]          	|				TP_StoreTP(device, StoreTPWave, tpInput.measurementMarker, hsList)
[00]          	|				WaveClear StoreTPWave
[00]          	|			endif
[00]          	|
[00]          	|		endfor
[00]          	|
[00]          	|		if(dataAcqOrTP == DATA_ACQUISITION_MODE && tpEnd > tpStart)
[00]          	|			tpStartPos = (tpEnd - 1) * tpLengthPointsADC
[00]          	|			if(DAG_GetNumericalValue(device, "check_settings_show_power"))
[00]          	|				WAVE tpOsciForPowerSpectrum = GetScaledTPTempWave(device)
[00]          	|				Make/FREE/D/N=(numADCs) tpColumns
[00]          	|				j = 0
[00]          	|				for(i = 0; i < numADCs; i += 1)
[00]          	|					if(ADCmode[i] == DAQ_CHANNEL_TYPE_TP)
[00]          	|						channelIndex = numDACs + i
[00]          	|						WAVE scaledChannel = scaledDataWave[channelIndex]
[00]          	|						MultiThread tpOsciForPowerSpectrum[][channelIndex] = scaledChannel[tpStartPos + p]
[00]          	|						tpColumns[j] = channelIndex
[00]          	|						j           += 1
[00]          	|					endif
[00]          	|					CopyScales/P scaledChannel, tpOsciForPowerSpectrum
[00]          	|				endfor
[00]          	|				Redimension/N=(j) tpColumns
[00]          	|				SCOPE_UpdatePowerSpectrum(device, tpColumns, osci = tpOsciForPowerSpectrum)
[00]          	|			else
[00]          	|				WAVE TPOscilloscopeData = GetTPOscilloscopeWave(device)
[00]          	|				for(i = 0; i < numADCs; i += 1)
[00]          	|					if(ADCmode[i] == DAQ_CHANNEL_TYPE_TP)
[00]          	|						channelIndex = numDACs + i
[00]          	|						WAVE scaledChannel = scaledDataWave[channelIndex]
[00]          	|						MultiThread TPOscilloscopeData[][channelIndex] = scaledChannel[tpStartPos + p]
[00]          	|					endif
[00]          	|				endfor
[00]          	|			endif
[00]          	|		endif
[00]          	|	endif
[00]          	|
[00]          	|	// Sync fifo position
[00]*         	|	fifoPosGlobal = fifoPos
[00]          	|
[00]*         	|	ASYNC_ThreadReadOut()
[00]          	|End

timjarsky avatar Apr 15 '25 21:04 timjarsky

Notes from live debug session (16th April 2025)

  • Could reproduce the slowness but it was not consistent slow
  • It was slow with either 5b0bbddb9 (tests/Basic: Ensure that we don't get unexpected ZeroMQ messages, 2025-04-11) (ZeroMQ TP message PR) or 88ffd8766 (Merge pull request #2380 from AllenInstitute/bugfix/2380-sf_make_variable_readonly, 2025-04-05) (Two PRs before)
  • Nothing big changed in WSE or ACQ4

DebugLogs.zip

t-b avatar Apr 16 '25 22:04 t-b

As mentioned in a recent one-to-one chat with Tim: We can also try to figure out the reason for the slowness by viewding the diff of the involved applications. For this I would need access to the source code of [ ] WSE/ [x] MIES/ [x] ACQ4 and the exact git revision where the bug does occur and where not.

t-b avatar Jun 08 '25 16:06 t-b

@t-b I need to be careful to check that the TP slowness reported above isn't due to the %baseline being set to max (49%).

timjarsky avatar Aug 11 '25 18:08 timjarsky

ExecuteScriptText is very slow on these machines (a factor of 7 slower than on my laptop). Reported to WM as #issue 7583.

t-b avatar Oct 02 '25 18:10 t-b