From 3dd0dcf0e219f1b1cc3c8f9256e7cfbe975653b0 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 22 Aug 2024 13:19:55 +0200 Subject: [PATCH 1/7] Util: Add two conversion function for DA/AD unit string return Added GetADChannelUnit, GetDAChannelUnit that return the unit string depending on clamp mode. Adapted GetChanAmpAssignUnit to use the new functions. --- Packages/MIES/MIES_MiesUtilities_Conversion.ipf | 12 ++++++++++++ Packages/MIES/MIES_WaveDataFolderGetters.ipf | 8 ++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Packages/MIES/MIES_MiesUtilities_Conversion.ipf b/Packages/MIES/MIES_MiesUtilities_Conversion.ipf index 92e3c2e513..917f8ca3ce 100644 --- a/Packages/MIES/MIES_MiesUtilities_Conversion.ipf +++ b/Packages/MIES/MIES_MiesUtilities_Conversion.ipf @@ -263,3 +263,15 @@ Function MapAnaFuncToConstant(string anaFunc) #endif endswitch End + +/// @brief returns the unit string for the AD channel depending on clampmode +threadsafe Function/S GetADChannelUnit(variable clampMode) + + return SelectString(clampMode == V_CLAMP_MODE, "mV", "pA") +End + +/// @brief returns the unit string for the DA channel depending on clampmode +threadsafe Function/S GetDAChannelUnit(variable clampMode) + + return SelectString(clampMode == V_CLAMP_MODE, "pA", "mV") +End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index 4ef248ae38..f07b836879 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -162,10 +162,10 @@ Function/WAVE GetChanAmpAssignUnit(string device) Make/T/N=(4, NUM_HEADSTAGES) dfr:ChanAmpAssignUnit/WAVE=wv wv = "" - wv[0][] = "mV" - wv[1][] = "pA" - wv[2][] = "pA" - wv[3][] = "mV" + wv[0][] = GetDAChannelUnit(V_CLAMP_MODE) + wv[1][] = GetADChannelUnit(V_CLAMP_MODE) + wv[2][] = GetDAChannelUnit(I_CLAMP_MODE) + wv[3][] = GetADChannelUnit(I_CLAMP_MODE) endif SetDimLabel ROWS, 0, VC_DAUnit, wv From 92a4275ad72f4fd6bcf0c36c7f2b3615fab9bf10 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 22 Aug 2024 13:25:14 +0200 Subject: [PATCH 2/7] TP: Add more information that is transferred to the TP analysis thread - extended the TPAnalysisInput structure This is a preparation commit for adding zeromq publishers that include some of that information. --- Packages/MIES/MIES_Oscilloscope.ipf | 45 ++++++++++++++++++----------- Packages/MIES/MIES_Structures.ipf | 13 +++++++-- Packages/MIES/MIES_SweepFormula.ipf | 7 +++-- Packages/MIES/MIES_TestPulse.ipf | 13 +++++++-- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/Packages/MIES/MIES_Oscilloscope.ipf b/Packages/MIES/MIES_Oscilloscope.ipf index f3d4f0da79..5ffdf40a3b 100644 --- a/Packages/MIES/MIES_Oscilloscope.ipf +++ b/Packages/MIES/MIES_Oscilloscope.ipf @@ -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 @@ -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) @@ -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] @@ -566,6 +573,10 @@ Function SCOPE_UpdateOscilloscopeData(string device, variable dataAcqOrTP, [vari hsList = "" endif + // Use same time for all headstages + tpInput.timeStamp = DateTime + tpInput.timeStampUTC = DateTimeInUTC() + for(j = 0; j < numADCs; j += 1) if(ADCmode[j] == DAQ_CHANNEL_TYPE_TP) @@ -608,7 +619,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 diff --git a/Packages/MIES/MIES_Structures.ipf b/Packages/MIES/MIES_Structures.ipf index e3919d424f..bb04e8814b 100644 --- a/Packages/MIES/MIES_Structures.ipf +++ b/Packages/MIES/MIES_Structures.ipf @@ -299,14 +299,23 @@ Structure TPAnalysisInput WAVE data variable clampAmp variable clampMode - variable duration // [points] + variable tpLengthPointsADC + variable pulseLengthPointsADC + variable pulseStartPointsADC + variable samplingIntervalADC + variable tpLengthPointsDAC + variable pulseLengthPointsDAC + variable pulseStartPointsDAC + variable samplingIntervalDAC variable baselineFrac - variable tpLengthPoints variable readTimeStamp variable hsIndex string device variable measurementMarker variable activeADCs + variable timeStamp + variable timeStampUTC + variable cycleId EndStructure /// @brief Helper structure for GetPlotArea() diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index d13da2d04e..610b399580 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -3472,9 +3472,10 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel // Assemble TP data WAVE tpInput.data = SF_AverageTPFromSweep(epochMatches, sweepData) - tpInput.tpLengthPoints = DimSize(tpInput.data, ROWS) - tpInput.duration = (str2num(epochTPPulse[0][EPOCH_COL_ENDTIME]) - str2num(epochTPPulse[0][EPOCH_COL_STARTTIME])) * ONE_TO_MILLI / DimDelta(sweepData, ROWS) - tpInput.baselineFrac = TP_CalculateBaselineFraction(tpInput.duration, tpInput.duration + 2 * tpBaseLinePoints) + tpInput.tpLengthPointsADC = DimSize(tpInput.data, ROWS) + tpInput.samplingIntervalADC = DimDelta(tpInput.data, ROWS) + tpInput.pulseLengthPointsADC = (str2num(epochTPPulse[0][EPOCH_COL_ENDTIME]) - str2num(epochTPPulse[0][EPOCH_COL_STARTTIME])) * ONE_TO_MILLI / DimDelta(sweepData, ROWS) + tpInput.baselineFrac = TP_CalculateBaselineFraction(tpInput.pulseLengthPointsADC, tpInput.pulseLengthPointsADC + 2 * tpBaseLinePoints) [WAVE settings, settingsIndex] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, CLAMPMODE_ENTRY_KEY, dacChannelNr, XOP_CHANNEL_TYPE_DAC, DATA_ACQUISITION_MODE) SFH_ASSERT(WaveExists(settings), "Failed to retrieve TP Clamp Mode from LBN") diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index eaa16c0317..40c4129bc6 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -1501,14 +1501,23 @@ Function/DF TP_PrepareAnalysisDF(string device, STRUCT TPAnalysisInput &tpInput) ASYNC_AddParam(threadDF, w = tpInput.data) ASYNC_AddParam(threadDF, var = tpInput.clampAmp) ASYNC_AddParam(threadDF, var = tpInput.clampMode) - ASYNC_AddParam(threadDF, var = tpInput.duration) + ASYNC_AddParam(threadDF, var = tpInput.pulseLengthPointsADC) ASYNC_AddParam(threadDF, var = tpInput.baselineFrac) - ASYNC_AddParam(threadDF, var = tpInput.tpLengthPoints) + ASYNC_AddParam(threadDF, var = tpInput.tpLengthPointsADC) ASYNC_AddParam(threadDF, var = tpInput.readTimeStamp) ASYNC_AddParam(threadDF, var = tpInput.hsIndex) ASYNC_AddParam(threadDF, str = tpInput.device) ASYNC_AddParam(threadDF, var = tpInput.measurementMarker) ASYNC_AddParam(threadDF, var = tpInput.activeADCs) + ASYNC_AddParam(threadDF, var = tpInput.timeStamp) + ASYNC_AddParam(threadDF, var = tpInput.timeStampUTC) + ASYNC_AddParam(threadDF, var = tpInput.cycleId) + ASYNC_AddParam(threadDF, var = tpInput.pulseStartPointsADC) + ASYNC_AddParam(threadDF, var = tpInput.samplingIntervalADC) + ASYNC_AddParam(threadDF, var = tpInput.tpLengthPointsDAC) + ASYNC_AddParam(threadDF, var = tpInput.pulseLengthPointsDAC) + ASYNC_AddParam(threadDF, var = tpInput.pulseStartPointsDAC) + ASYNC_AddParam(threadDF, var = tpInput.samplingIntervalDAC) return threadDF End From a10846d5c3f74f0755ff5ba14179566dfb2261eb Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 25 Aug 2024 19:52:02 +0200 Subject: [PATCH 3/7] PUB: Preparation to add four zeromq publishers for TP data - the data is published from the TP analysis thread including additional information available in the thread through the previous commit. - The additional values are also returned by the thread and collected in the async buffer as well then in TPResult and in TPStorage. - The involved waves and their respective getters were adapted with new elements that the additional data can be stored. - As most of the elements store the same information, thus a constant was introduced with a dimension label list that is used as helper for the wave creation in the getter functions. --- Packages/MIES/MIES_Constants.ipf | 7 + Packages/MIES/MIES_SweepFormula.ipf | 2 +- Packages/MIES/MIES_TestPulse.ipf | 260 ++++++++++--------- Packages/MIES/MIES_WaveDataFolderGetters.ipf | 94 ++++--- 4 files changed, 212 insertions(+), 151 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 74eb6a2f9a..094e1daec7 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2378,3 +2378,10 @@ Constant ABORTCODE_ABORT = -3 Constant ABORTCODE_STACKOVERFLOW = -2 Constant ABORTCODE_USERABORT = -1 ///@} + +// 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;" diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 610b399580..b27648546c 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -3490,7 +3490,7 @@ static Function/WAVE SF_OperationTPImpl(string graph, WAVE/WAVE mode, WAVE/Z sel DFREF dfrTPAnalysis = TP_PrepareAnalysisDF(graph, tpInput) DFREF dfrTPAnalysisInput = dfrTPAnalysis:input DFREF dfr = TP_TSAnalysis(dfrTPAnalysisInput) - WAVE tpOutData = dfr:outData + WAVE tpOutData = dfr:tpData // handle waves sent out when TP_ANALYSIS_DEBUGGING is defined if(WaveExists(dfr:data) && WaveExists(dfr:colors)) diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index 40c4129bc6..2dc27e98b2 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -178,10 +178,9 @@ End /// @param ar ASYNC_ReadOutStruct structure with dfr with input data Function TP_ROAnalysis(STRUCT ASYNC_ReadOutStruct &ar) - variable i, j, bufSize - variable posMarker, posAsync, tpBufferSize - variable posBaseline, posSSRes, posInstRes - variable posElevSS, posElevInst + variable i, j, bufSize, headstage, marker + variable posMarker, posAsync + string lbl if(ar.rtErr || ar.abortCode) ASSERT(!ar.rtErr, "TP analysis thread encountered RTE " + ar.rtErrMsg) @@ -190,23 +189,16 @@ Function TP_ROAnalysis(STRUCT ASYNC_ReadOutStruct &ar) DFREF dfr = ar.dfr - WAVE/SDFR=dfr inData = outData - NVAR/SDFR=dfr now = now - NVAR/SDFR=dfr hsIndex = hsIndex - SVAR/SDFR=dfr device = device - NVAR/SDFR=dfr marker = marker - NVAR/SDFR=dfr activeADCs = activeADCs + WAVE/SDFR=dfr inData = tpData + SVAR/SDFR=dfr device = device + headstage = inData[%HEADSTAGE] + marker = inData[%MARKER] WAVE asyncBuffer = GetTPResultAsyncBuffer(device) - bufSize = DimSize(asyncBuffer, ROWS) - posMarker = FindDimLabel(asyncBuffer, LAYERS, "MARKER") - posAsync = FindDimLabel(asyncBuffer, COLS, "ASYNCDATA") - posBaseline = FindDimLabel(asyncBuffer, COLS, "BASELINE") - posSSRes = FindDimLabel(asyncBuffer, COLS, "STEADYSTATERES") - posInstRes = FindDimLabel(asyncBuffer, COLS, "INSTANTRES") - posElevSS = FindDimLabel(asyncBuffer, COLS, "ELEVATED_SS") - posElevInst = FindDimLabel(asyncBuffer, COLS, "ELEVATED_INST") + bufSize = DimSize(asyncBuffer, ROWS) + posMarker = FindDimLabel(asyncBuffer, LAYERS, "MARKER") + posAsync = FindDimLabel(asyncBuffer, COLS, "ASYNCDATA") FindValue/RMD=[][posAsync][posMarker, posMarker]/V=(marker)/T=0 asyncBuffer i = V_Value >= 0 ? V_Row : bufSize @@ -218,26 +210,41 @@ Function TP_ROAnalysis(STRUCT ASYNC_ReadOutStruct &ar) asyncBuffer[bufSize][posAsync][posMarker] = marker endif - asyncBuffer[i][posBaseline][hsIndex] = inData[%BASELINE] - asyncBuffer[i][posSSRes][hsIndex] = inData[%STEADYSTATERES] - asyncBuffer[i][posInstRes][hsIndex] = inData[%INSTANTRES] - asyncBuffer[i][posElevSS][hsIndex] = inData[%ELEVATED_SS] - asyncBuffer[i][posElevInst][hsIndex] = inData[%ELEVATED_INST] - - asyncBuffer[i][posAsync][%NOW] = now + WAVE/T dimLabels = ListToTextWave(TP_ANALYSIS_DATA_LABELS, ";") + for(lbl : dimLabels) + asyncBuffer[i][%$lbl][headstage] = inData[%$lbl] + endfor asyncBuffer[i][posAsync][%REC_CHANNELS] += 1 // got one set of results ready - if(asyncBuffer[i][posAsync][%REC_CHANNELS] == activeADCs) + if(asyncBuffer[i][posAsync][%REC_CHANNELS] == inData[%NUMBER_OF_TP_CHANNELS]) WAVE TPResults = GetTPResults(device) WAVE TPSettings = GetTPSettings(device) - MultiThread TPResults[%BaselineSteadyState][] = asyncBuffer[i][posBaseline][q] - MultiThread TPResults[%ResistanceSteadyState][] = asyncBuffer[i][posSSRes][q] - MultiThread TPResults[%ResistanceInst][] = asyncBuffer[i][posInstRes][q] - MultiThread TPResults[%ElevatedSteadyState][] = asyncBuffer[i][posElevSS][q] - MultiThread TPResults[%ElevatedInst][] = asyncBuffer[i][posElevInst][q] + MultiThread TPResults[%BaselineSteadyState][] = asyncBuffer[i][%BASELINE][q] + MultiThread TPResults[%ResistanceSteadyState][] = asyncBuffer[i][%STEADYSTATERES][q] + MultiThread TPResults[%ResistanceInst][] = asyncBuffer[i][%INSTANTRES][q] + MultiThread TPResults[%ElevatedSteadyState][] = asyncBuffer[i][%ELEVATED_SS][q] + MultiThread TPResults[%ElevatedInst][] = asyncBuffer[i][%ELEVATED_INST][q] + MultiThread TPResults[%NOW][] = asyncBuffer[i][%NOW][q] + MultiThread TPResults[%HEADSTAGE][] = asyncBuffer[i][%HEADSTAGE][q] + MultiThread TPResults[%MARKER][] = asyncBuffer[i][%MARKER][q] + MultiThread TPResults[%NUMBER_OF_TP_CHANNELS][] = asyncBuffer[i][%NUMBER_OF_TP_CHANNELS][q] + MultiThread TPResults[%TIMESTAMP][] = asyncBuffer[i][%TIMESTAMP][q] + MultiThread TPResults[%TIMESTAMPUTC][] = asyncBuffer[i][%TIMESTAMPUTC][q] + MultiThread TPResults[%CLAMPMODE][] = asyncBuffer[i][%CLAMPMODE][q] + MultiThread TPResults[%CLAMPAMP][] = asyncBuffer[i][%CLAMPAMP][q] + MultiThread TPResults[%BASELINEFRAC][] = asyncBuffer[i][%BASELINEFRAC][q] + MultiThread TPResults[%CYCLEID][] = asyncBuffer[i][%CYCLEID][q] + MultiThread TPResults[%TPLENGTHPOINTSADC][] = asyncBuffer[i][%TPLENGTHPOINTSADC][q] + MultiThread TPResults[%PULSELENGTHPOINTSADC][] = asyncBuffer[i][%PULSELENGTHPOINTSADC][q] + MultiThread TPResults[%PULSESTARTPOINTSADC][] = asyncBuffer[i][%PULSESTARTPOINTSADC][q] + MultiThread TPResults[%SAMPLINGINTERVALADC][] = asyncBuffer[i][%SAMPLINGINTERVALADC][q] + MultiThread TPResults[%TPLENGTHPOINTSDAC][] = asyncBuffer[i][%TPLENGTHPOINTSDAC][q] + MultiThread TPResults[%PULSELENGTHPOINTSDAC][] = asyncBuffer[i][%PULSELENGTHPOINTSDAC][q] + MultiThread TPResults[%PULSESTARTPOINTSDAC][] = asyncBuffer[i][%PULSESTARTPOINTSDAC][q] + MultiThread TPResults[%SAMPLINGINTERVALDAC][] = asyncBuffer[i][%SAMPLINGINTERVALDAC][q] // Remove finished results from buffer DeletePoints i, 1, asyncBuffer @@ -259,7 +266,7 @@ Function TP_ROAnalysis(STRUCT ASYNC_ReadOutStruct &ar) TP_AutoAmplitudeAndBaseline(device, TPResults, marker) DQ_ApplyAutoBias(device, TPResults) - TP_RecordTP(device, TPResults, now, marker) + TP_RecordTP(device, TPResults, inData[%NOW]) endif End @@ -882,22 +889,31 @@ End threadsafe Function/DF TP_TSAnalysis(DFREF dfrInp) variable evalRange, refTime, refPoint, tpStartPoint - variable sampleInt + variable jsonId variable avgBaselineSS, avgTPSS, instVal, evalOffsetPointsCorrected, instPoint DFREF dfrOut = NewFreeDataFolder() - WAVE data = dfrInp:param0 - NVAR/SDFR=dfrInp clampAmp = param1 - NVAR/SDFR=dfrInp clampMode = param2 - NVAR/SDFR=dfrInp duration = param3 - NVAR/SDFR=dfrInp baselineFrac = param4 - NVAR/SDFR=dfrInp lengthTPInPoints = param5 - NVAR/SDFR=dfrInp now = param6 - NVAR/SDFR=dfrInp hsIndex = param7 - SVAR/SDFR=dfrInp device = param8 - NVAR/SDFR=dfrInp marker = param9 - NVAR/SDFR=dfrInp activeADCs = param10 + WAVE data = ASYNC_FetchWave(dfrInp, "data") + variable clampAmp = ASYNC_FetchVariable(dfrInp, "clampAmp") + variable clampMode = ASYNC_FetchVariable(dfrInp, "clampMode") + variable pulseLengthPointsADC = ASYNC_FetchVariable(dfrInp, "pulseLengthPointsADC") + variable baselineFrac = ASYNC_FetchVariable(dfrInp, "baselineFrac") + variable tpLengthPointsADC = ASYNC_FetchVariable(dfrInp, "tpLengthPointsADC") + variable now = ASYNC_FetchVariable(dfrInp, "now") + variable headstage = ASYNC_FetchVariable(dfrInp, "headstage") + string device = ASYNC_FetchString(dfrInp, "device") + variable marker = ASYNC_FetchVariable(dfrInp, "marker") + variable activeADCs = ASYNC_FetchVariable(dfrInp, "numTPChannels") + variable timeStamp = ASYNC_FetchVariable(dfrInp, "timeStamp") + variable timeStampUTC = ASYNC_FetchVariable(dfrInp, "timeStampUTC") + variable cycleId = ASYNC_FetchVariable(dfrInp, "cycleId") + variable pulseStartPointsADC = ASYNC_FetchVariable(dfrInp, "pulseStartPointsADC") + variable samplingIntervalADC = ASYNC_FetchVariable(dfrInp, "samplingIntervalADC") + variable tpLengthPointsDAC = ASYNC_FetchVariable(dfrInp, "tpLengthPointsDAC") + variable pulseLengthPointsDAC = ASYNC_FetchVariable(dfrInp, "pulseLengthPointsDAC") + variable pulseStartPointsDAC = ASYNC_FetchVariable(dfrInp, "pulseStartPointsDAC") + variable samplingIntervalDAC = ASYNC_FetchVariable(dfrInp, "samplingIntervalDAC") #if defined(TP_ANALYSIS_DEBUGGING) DEBUGPRINT_TS("Marker: ", var = marker) @@ -908,43 +924,32 @@ threadsafe Function/DF TP_TSAnalysis(DFREF dfrInp) colors[0, lengthTPInPoints - 1] = 100 #endif - // Rows: - // 0: base line level - // 1: steady state resistance - // 2: instantaneous resistance - // 3: averaged elevated level (steady state) - // 4: averaged elevated level (instantaneous) - Make/N=5/D dfrOut:outData/WAVE=outData - SetDimLabel ROWS, 0, BASELINE, outData - SetDimLabel ROWS, 1, STEADYSTATERES, outData - SetDimLabel ROWS, 2, INSTANTRES, outData - SetDimLabel ROWS, 3, ELEVATED_SS, outData - SetDimLabel ROWS, 4, ELEVATED_INST, outData - - sampleInt = DimDelta(data, ROWS) - tpStartPoint = baseLineFrac * lengthTPInPoints - evalRange = min(5 / sampleInt, min(duration * 0.2, tpStartPoint * 0.2)) * sampleInt + WAVE tpData = GetTPAnalysisDataWave() + MoveWave tpData, dfrOut + + tpStartPoint = baseLineFrac * tpLengthPointsADC + evalRange = min(5 / samplingIntervalADC, min(pulseLengthPointsADC * 0.2, tpStartPoint * 0.2)) * samplingIntervalADC // correct TP_EVAL_POINT_OFFSET for the non-standard sampling interval - evalOffsetPointsCorrected = (TP_EVAL_POINT_OFFSET / sampleInt) * HARDWARE_ITC_MIN_SAMPINT + evalOffsetPointsCorrected = (TP_EVAL_POINT_OFFSET / samplingIntervalADC) * HARDWARE_ITC_MIN_SAMPINT - refTime = (tpStartPoint - evalOffsetPointsCorrected) * sampleInt + refTime = (tpStartPoint - evalOffsetPointsCorrected) * samplingIntervalADC AvgBaselineSS = mean(data, refTime - evalRange, refTime) #if defined(TP_ANALYSIS_DEBUGGING) // color BASE - variable refpt = refTime / sampleInt - colors[refpt - evalRange / sampleInt, refpt] = 50 - DEBUGPRINT_TS("SampleInt: ", var = sampleInt) + variable refpt = refTime / samplingIntervalADC + colors[refpt - evalRange / samplingIntervalADC, refpt] = 50 + DEBUGPRINT_TS("SampleInt: ", var = samplingIntervalADC) DEBUGPRINT_TS("tpStartPoint: ", var = tpStartPoint) DEBUGPRINT_TS("evalRange (ms): ", var = evalRange) - DEBUGPRINT_TS("evalRange in points: ", var = evalRange / sampleInt) + DEBUGPRINT_TS("evalRange in points: ", var = evalRange / samplingIntervalADC) DEBUGPRINT_TS("Base range begin (ms): ", var = refTime - evalRange) DEBUGPRINT_TS("Base range eng (ms): ", var = refTime) DEBUGPRINT_TS("average BaseLine: ", var = AvgBaselineSS) #endif - refTime = (lengthTPInPoints - tpStartPoint - evalOffsetPointsCorrected) * sampleInt + refTime = (tpLengthPointsADC - tpStartPoint - evalOffsetPointsCorrected) * samplingIntervalADC avgTPSS = mean(data, refTime - evalRange, refTime) #if defined(TP_ANALYSIS_DEBUGGING) @@ -952,16 +957,16 @@ threadsafe Function/DF TP_TSAnalysis(DFREF dfrInp) DEBUGPRINT_TS("steady state range eng (ms): ", var = refTime) DEBUGPRINT_TS("steady state average: ", var = avgTPSS) // color steady state - refpt = lengthTPInPoints - tpStartPoint - evalOffsetPointsCorrected - colors[refpt - evalRange / sampleInt, refpt] = 50 + refpt = lengthTPInPoints - tpStartPoint - evalOffsetPointsCorrected + colors[refpt - evalRange / samplingIntervalADC, refpt] = 50 // color instantaneous - refpt = tpStartPoint + evalOffsetPointsCorrected - colors[refpt, refpt + 0.25 / sampleInt] = 50 + refpt = tpStartPoint + evalOffsetPointsCorrected + colors[refpt, refpt + 0.25 / samplingIntervalADC] = 50 #endif refPoint = tpStartPoint + evalOffsetPointsCorrected // as data is always too small for threaded execution, the values of V_minRowLoc/V_maxRowLoc are reproducible - WaveStats/P/Q/M=1/R=[refPoint, refPoint + 0.25 / sampleInt] data + WaveStats/P/Q/M=1/R=[refPoint, refPoint + 0.25 / samplingIntervalADC] data instPoint = (clampAmp < 0) ? V_minRowLoc : V_maxRowLoc if(instPoint == -1) // all wave data is NaN @@ -977,27 +982,41 @@ threadsafe Function/DF TP_TSAnalysis(DFREF dfrInp) #endif if(clampMode == I_CLAMP_MODE) - outData[1] = (avgTPSS - avgBaselineSS) * MILLI_TO_ONE / (clampAmp * PICO_TO_ONE) * ONE_TO_MEGA - outData[2] = (instVal - avgBaselineSS) * MILLI_TO_ONE / (clampAmp * PICO_TO_ONE) * ONE_TO_MEGA + tpData[%STEADYSTATERES] = (avgTPSS - avgBaselineSS) * MILLI_TO_ONE / (clampAmp * PICO_TO_ONE) * ONE_TO_MEGA + tpData[%INSTANTRES] = (instVal - avgBaselineSS) * MILLI_TO_ONE / (clampAmp * PICO_TO_ONE) * ONE_TO_MEGA else - outData[1] = (clampAmp * MILLI_TO_ONE) / ((avgTPSS - avgBaselineSS) * PICO_TO_ONE) * ONE_TO_MEGA - outData[2] = (clampAmp * MILLI_TO_ONE) / ((instVal - avgBaselineSS) * PICO_TO_ONE) * ONE_TO_MEGA + tpData[%STEADYSTATERES] = (clampAmp * MILLI_TO_ONE) / ((avgTPSS - avgBaselineSS) * PICO_TO_ONE) * ONE_TO_MEGA + tpData[%INSTANTRES] = (clampAmp * MILLI_TO_ONE) / ((instVal - avgBaselineSS) * PICO_TO_ONE) * ONE_TO_MEGA endif - outData[0] = avgBaselineSS - outData[3] = avgTPSS - outData[4] = instVal + tpData[%BASELINE] = avgBaselineSS + tpData[%ELEVATED_SS] = avgTPSS + tpData[%ELEVATED_INST] = instVal #if defined(TP_ANALYSIS_DEBUGGING) - DEBUGPRINT_TS("instantaneous resistance: ", var = outData[2]) - DEBUGPRINT_TS("steady state resistance: ", var = outData[1]) + DEBUGPRINT_TS("instantaneous resistance: ", var = tpData[%INSTANTRES]) + DEBUGPRINT_TS("steady state resistance: ", var = tpData[%STEADYSTATERES]) #endif // additional data copy - variable/G dfrOut:now = now - variable/G dfrOut:hsIndex = hsIndex - string/G dfrOut:device = device - variable/G dfrOut:marker = marker - variable/G dfrOut:activeADCs = activeADCs + string/G dfrOut:device = device + tpData[%NOW] = now + tpData[%HEADSTAGE] = headstage + tpData[%MARKER] = marker + tpData[%NUMBER_OF_TP_CHANNELS] = activeADCs + tpData[%TIMESTAMP] = timestamp + tpData[%TIMESTAMPUTC] = timestampUTC + tpData[%CLAMPMODE] = clampMode + tpData[%CLAMPAMP] = clampAmp + tpData[%BASELINEFRAC] = baselineFrac + tpData[%CYCLEID] = cycleId + tpData[%TPLENGTHPOINTSADC] = tpLengthPointsADC + tpData[%PULSELENGTHPOINTSADC] = pulseLengthPointsADC + tpData[%PULSESTARTPOINTSADC] = pulseStartPointsADC + tpData[%SAMPLINGINTERVALADC] = samplingIntervalADC + tpData[%TPLENGTHPOINTSDAC] = tpLengthPointsDAC + tpData[%PULSELENGTHPOINTSDAC] = pulseLengthPointsDAC + tpData[%PULSESTARTPOINTSDAC] = pulseStartPointsDAC + tpData[%SAMPLINGINTERVALDAC] = samplingIntervalDAC return dfrOut End @@ -1030,9 +1049,9 @@ End /// @brief Records values from TPResults into TPStorage at defined intervals. /// /// Used for analysis of TP over time. -static Function TP_RecordTP(string device, WAVE TPResults, variable now, variable tpMarker) +static Function TP_RecordTP(string device, WAVE TPResults, variable now) - variable delta, i, ret, lastPressureCtrl, timestamp, cycleID + variable delta, i, ret, lastPressureCtrl WAVE TPStorage = GetTPStorage(device) WAVE hsProp = GetHSProperties(device) variable count = GetNumberFromWaveNote(TPStorage, NOTE_INDEX) @@ -1072,14 +1091,9 @@ static Function TP_RecordTP(string device, WAVE TPResults, variable now, variabl : TPStorage[count][q][%HoldingCmd_IC] endif - TPStorage[count][][%TimeInSeconds] = now - - // store the current time in a variable first - // so that all columns have the same timestamp - timestamp = DateTime - TPStorage[count][][%TimeStamp] = timestamp - timestamp = DateTimeInUTC() - TPStorage[count][][%TimeStampSinceIgorEpochUTC] = timestamp + TPStorage[count][][%TimeInSeconds] = TPResults[%NOW][q] + TPStorage[count][][%TimeStamp] = TPResults[%TIMESTAMP][q] + TPStorage[count][][%TimeStampSinceIgorEpochUTC] = TPResults[%TIMESTAMPUTC][q] TPStorage[count][][%PeakResistance] = min(TPResults[%ResistanceInst][q], TP_MAX_VALID_RESISTANCE) TPStorage[count][][%SteadyStateResistance] = min(TPResults[%ResistanceSteadyState][q], TP_MAX_VALID_RESISTANCE) @@ -1090,21 +1104,29 @@ static Function TP_RecordTP(string device, WAVE TPResults, variable now, variabl TPStorage[count][][%ADC] = hsProp[q][%ADC] TPStorage[count][][%Headstage] = hsProp[q][%Enabled] ? q : NaN TPStorage[count][][%ClampMode] = hsProp[q][%ClampMode] + TPStorage[count][][%CLAMPAMP] = TPResults[%CLAMPAMP][q] TPStorage[count][][%Baseline_VC] = hsProp[q][%ClampMode] == V_CLAMP_MODE ? TPResults[%BaselineSteadyState][q] : NaN TPStorage[count][][%Baseline_IC] = hsProp[q][%ClampMode] == I_CLAMP_MODE ? TPResults[%BaselineSteadyState][q] : NaN TPStorage[count][][%DeltaTimeInSeconds] = count > 0 ? now - TPStorage[0][0][%TimeInSeconds] : 0 - TPStorage[count][][%TPMarker] = tpMarker + TPStorage[count][][%TPMarker] = TPResults[%MARKER][q] - cycleID = ROVAR(GetTestpulseCycleID(device)) - TPStorage[count][][%TPCycleID] = cycleID + TPStorage[count][][%TPCycleID] = TPResults[%CYCLEID][q] TPStorage[count][][%AutoTPAmplitude] = TPResults[%AutoTPAmplitude][q] TPStorage[count][][%AutoTPBaseline] = TPResults[%AutoTPBaseline][q] TPStorage[count][][%AutoTPBaselineRangeExceeded] = TPResults[%AutoTPBaselineRangeExceeded][q] TPStorage[count][][%AutoTPBaselineFitResult] = TPResults[%AutoTPBaselineFitResult][q] TPStorage[count][][%AutoTPDeltaV] = TPResults[%AutoTPDeltaV][q] + TPStorage[count][][%TPLENGTHPOINTSADC] = TPResults[%TPLENGTHPOINTSADC][q] + TPStorage[count][][%PULSELENGTHPOINTSADC] = TPResults[%PULSELENGTHPOINTSADC][q] + TPStorage[count][][%PULSESTARTPOINTSADC] = TPResults[%PULSESTARTPOINTSADC][q] + TPStorage[count][][%SAMPLINGINTERVALADC] = TPResults[%SAMPLINGINTERVALADC][q] + TPStorage[count][][%TPLENGTHPOINTSDAC] = TPResults[%TPLENGTHPOINTSDAC][q] + TPStorage[count][][%PULSELENGTHPOINTSDAC] = TPResults[%PULSELENGTHPOINTSDAC][q] + TPStorage[count][][%PULSESTARTPOINTSDAC] = TPResults[%PULSESTARTPOINTSDAC][q] + TPStorage[count][][%SAMPLINGINTERVALDAC] = TPResults[%SAMPLINGINTERVALDAC][q] WAVE TPSettings = GetTPSettings(device) TPStorage[count][][%AutoTPCycleID] = hsProp[q][%Enabled] ? TPSettings[%autoTPCycleID][q] : NaN @@ -1498,26 +1520,26 @@ Function/DF TP_PrepareAnalysisDF(string device, STRUCT TPAnalysisInput &tpInput) wlName = GetWorkLoadName(WORKLOADCLASS_TP, device) DFREF threadDF = ASYNC_PrepareDF("TP_TSAnalysis", "TP_ROAnalysis", wlName, inOrder = 0) - ASYNC_AddParam(threadDF, w = tpInput.data) - ASYNC_AddParam(threadDF, var = tpInput.clampAmp) - ASYNC_AddParam(threadDF, var = tpInput.clampMode) - ASYNC_AddParam(threadDF, var = tpInput.pulseLengthPointsADC) - ASYNC_AddParam(threadDF, var = tpInput.baselineFrac) - ASYNC_AddParam(threadDF, var = tpInput.tpLengthPointsADC) - ASYNC_AddParam(threadDF, var = tpInput.readTimeStamp) - ASYNC_AddParam(threadDF, var = tpInput.hsIndex) - ASYNC_AddParam(threadDF, str = tpInput.device) - ASYNC_AddParam(threadDF, var = tpInput.measurementMarker) - ASYNC_AddParam(threadDF, var = tpInput.activeADCs) - ASYNC_AddParam(threadDF, var = tpInput.timeStamp) - ASYNC_AddParam(threadDF, var = tpInput.timeStampUTC) - ASYNC_AddParam(threadDF, var = tpInput.cycleId) - ASYNC_AddParam(threadDF, var = tpInput.pulseStartPointsADC) - ASYNC_AddParam(threadDF, var = tpInput.samplingIntervalADC) - ASYNC_AddParam(threadDF, var = tpInput.tpLengthPointsDAC) - ASYNC_AddParam(threadDF, var = tpInput.pulseLengthPointsDAC) - ASYNC_AddParam(threadDF, var = tpInput.pulseStartPointsDAC) - ASYNC_AddParam(threadDF, var = tpInput.samplingIntervalDAC) + ASYNC_AddParam(threadDF, w = tpInput.data, name = "data") + ASYNC_AddParam(threadDF, var = tpInput.clampAmp, name = "clampAmp") + ASYNC_AddParam(threadDF, var = tpInput.clampMode, name = "clampMode") + ASYNC_AddParam(threadDF, var = tpInput.pulseLengthPointsADC, name = "pulseLengthPointsADC") + ASYNC_AddParam(threadDF, var = tpInput.baselineFrac, name = "baselineFrac") + ASYNC_AddParam(threadDF, var = tpInput.tpLengthPointsADC, name = "tpLengthPointsADC") + ASYNC_AddParam(threadDF, var = tpInput.readTimeStamp, name = "now") + ASYNC_AddParam(threadDF, var = tpInput.hsIndex, name = "headstage") + ASYNC_AddParam(threadDF, str = tpInput.device, name = "device") + ASYNC_AddParam(threadDF, var = tpInput.measurementMarker, name = "marker") + ASYNC_AddParam(threadDF, var = tpInput.activeADCs, name = "numTPChannels") + ASYNC_AddParam(threadDF, var = tpInput.timeStamp, name = "timeStamp") + ASYNC_AddParam(threadDF, var = tpInput.timeStampUTC, name = "timeStampUTC") + ASYNC_AddParam(threadDF, var = tpInput.cycleId, name = "cycleId") + ASYNC_AddParam(threadDF, var = tpInput.pulseStartPointsADC, name = "pulseStartPointsADC") + ASYNC_AddParam(threadDF, var = tpInput.samplingIntervalADC, name = "samplingIntervalADC") + ASYNC_AddParam(threadDF, var = tpInput.tpLengthPointsDAC, name = "tpLengthPointsDAC") + ASYNC_AddParam(threadDF, var = tpInput.pulseLengthPointsDAC, name = "pulseLengthPointsDAC") + ASYNC_AddParam(threadDF, var = tpInput.pulseStartPointsDAC, name = "pulseStartPointsDAC") + ASYNC_AddParam(threadDF, var = tpInput.samplingIntervalDAC, name = "samplingIntervalDAC") return threadDF End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index f07b836879..d84c1024da 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -483,21 +483,14 @@ End /// - Layers: /// - 0: marker /// - 1: received channels -/// - 2: now -/// Column 1: baseline level -/// Column 2: steady state res -/// Column 3: instantaneous res -/// Column 4: baseline position -/// Column 5: steady state res position -/// Column 6: instantaneous res position -/// Column 7: average elevated level (steady state) -/// Column 8: average elevated level (instantaneous) +/// Column 1+: defined through TP_ANALYSIS_DATA_LABELS /// /// Layers: -/// - NUM_HEADSTAGES positions with value entries at hsIndex +/// - NUM_HEADSTAGES positions with value entries at headstage Function/WAVE GetTPResultAsyncBuffer(string device) - variable versionOfNewWave = 1 + variable versionOfNewWave = 2 + variable numCols = ItemsInList(TP_ANALYSIS_DATA_LABELS) + 1 DFREF dfr = GetDeviceTestPulse(device) @@ -505,25 +498,22 @@ Function/WAVE GetTPResultAsyncBuffer(string device) if(ExistsWithCorrectLayoutVersion(wv, versionOfNewWave)) return wv - elseif(WaveExists(wv)) - Redimension/N=(-1, 9, -1) wv else - Make/N=(0, 9, NUM_HEADSTAGES)/D dfr:TPResultAsyncBuffer/WAVE=wv + numCols = ItemsInList(TP_ANALYSIS_DATA_LABELS) + 1 + if(WaveExists(wv)) + Redimension/N=(-1, numCols, -1) wv + else + Make/N=(0, numCols, NUM_HEADSTAGES)/D dfr:TPResultAsyncBuffer/WAVE=wv + endif + + FastOp wv = (NaN) endif SetDimLabel COLS, 0, ASYNCDATA, wv - SetDimLabel COLS, 1, BASELINE, wv - SetDimLabel COLS, 2, STEADYSTATERES, wv - SetDimLabel COLS, 3, INSTANTRES, wv - SetDimLabel COLS, 4, BASELINE_POS, wv - SetDimLabel COLS, 5, STEADYSTATERES_POS, wv - SetDimLabel COLS, 6, INSTANTRES_POS, wv - SetDimLabel COLS, 7, ELEVATED_SS, wv - SetDimLabel COLS, 8, ELEVATED_INST, wv + SetDimensionLabels(wv, TP_ANALYSIS_DATA_LABELS, COLS, startPos = 1) SetDimLabel LAYERS, 0, MARKER, wv SetDimLabel LAYERS, 1, REC_CHANNELS, wv - SetDimLabel LAYERS, 2, NOW, wv SetWaveVersion(wv, versionOfNewWave) @@ -3029,17 +3019,27 @@ End /// state is switched (aka on->off or off->on) /// - 29: Auto TP Baseline Fit result: One of @ref TPBaselineFitResults /// - 30: Auto TP Delta V [mV] +/// - 31: Clamp Amplitude [mV] or [pA] depending on Clamp Mode +/// - 32: Testpulse full length in points for AD channel [points] +/// - 33: Testpulse pulse length in points for AD channel [points] +/// - 34: Point index of pulse start for AD channel [point] +/// - 35: Sampling interval for AD channel [ms] +/// - 36: Testpulse full length in points for DA channel [points] +/// - 37: Testpulse pulse length in points for DA channel [points] +/// - 38: Point index of pulse start for DA channel [point] +/// - 39: Sampling interval for DA channel [ms] Function/WAVE GetTPStorage(string device) DFREF dfr = GetDeviceTestPulse(device) - variable versionOfNewWave = 15 + variable versionOfNewWave = 16 + variable numLayers = 40 WAVE/Z/D/SDFR=dfr wv = TPStorage if(ExistsWithCorrectLayoutVersion(wv, versionOfNewWave)) return wv elseif(WaveExists(wv)) - Redimension/N=(-1, NUM_HEADSTAGES, 31)/D wv + Redimension/N=(-1, NUM_HEADSTAGES, numLayers)/D wv if(WaveVersionIsSmaller(wv, 10)) wv[][][17] = NaN @@ -3058,8 +3058,11 @@ Function/WAVE GetTPStorage(string device) if(WaveVersionIsSmaller(wv, 15)) wv[][][25, 30] = NaN endif + if(WaveVersionIsSmaller(wv, 16)) + wv[][][31, numLayers - 1] = NaN + endif else - Make/N=(MINIMUM_WAVE_SIZE_LARGE, NUM_HEADSTAGES, 31)/D dfr:TPStorage/WAVE=wv + Make/N=(MINIMUM_WAVE_SIZE_LARGE, NUM_HEADSTAGES, numLayers)/D dfr:TPStorage/WAVE=wv wv = NaN @@ -3099,6 +3102,18 @@ Function/WAVE GetTPStorage(string device) SetDimLabel LAYERS, 28, AutoTPCycleID, wv SetDimLabel LAYERS, 29, AutoTPBaselineFitResult, wv SetDimLabel LAYERS, 30, AutoTPDeltaV, wv + // Dimlabels starting from here are taken from TP_ANALYSIS_DATA_LABELS + // This is not required but convenient because in @ref TP_RecordTP data from TPResults (@ref GetTPResults) + // is transferred to tpStorage and TPResults also uses dimlabels from TP_ANALYSIS_DATA_LABELS partially. + SetDimLabel LAYERS, 31, CLAMPAMP, wv + SetDimLabel LAYERS, 32, TPLENGTHPOINTSADC, wv + SetDimLabel LAYERS, 33, PULSELENGTHPOINTSADC, wv + SetDimLabel LAYERS, 34, PULSESTARTPOINTSADC, wv + SetDimLabel LAYERS, 35, SAMPLINGINTERVALADC, wv + SetDimLabel LAYERS, 36, TPLENGTHPOINTSDAC, wv + SetDimLabel LAYERS, 37, PULSELENGTHPOINTSDAC, wv + SetDimLabel LAYERS, 38, PULSESTARTPOINTSDAC, wv + SetDimLabel LAYERS, 39, SAMPLINGINTERVALDAC, wv SetNumberInWaveNote(wv, AUTOBIAS_LAST_INVOCATION_KEY, 0) SetNumberInWaveNote(wv, DIMENSION_SCALING_LAST_INVOC, 0) @@ -3279,12 +3294,18 @@ End /// - 7: Auto TP Baseline range exceeded: True/False /// - 8: Auto TP Baseline fit result: One of @ref TPBaselineFitResults /// - 9: Auto TP Delta V: [mV] -/// +/// - 10+: partial dim labels from TP_ANALYSIS_DATA_LABELS, originally filled in TP_TSAnalysis /// Columns: /// - NUM_HEADSTAGES Function/WAVE GetTPResults(string device) - variable version = 3 + variable version = 4 + + string labels = "ResistanceInst;BaselineSteadyState;ResistanceSteadyState;ElevatedSteadyState;ElevatedInst;" + \ + "AutoTPAmplitude;AutoTPBaseline;AutoTPBaselineRangeExceeded;AutoTPBaselineFitResult;AutoTPDeltaV;" + \ + "NOW;HEADSTAGE;MARKER;NUMBER_OF_TP_CHANNELS;TIMESTAMP;TIMESTAMPUTC;CLAMPMODE;CLAMPAMP;BASELINEFRAC;" + \ + "CYCLEID;TPLENGTHPOINTSADC;PULSELENGTHPOINTSADC;PULSESTARTPOINTSADC;SAMPLINGINTERVALADC;TPLENGTHPOINTSDAC;" + \ + "PULSELENGTHPOINTSDAC;PULSESTARTPOINTSDAC;SAMPLINGINTERVALDAC;" DFREF dfr = GetDeviceTestPulse(device) WAVE/Z/D/SDFR=dfr wv = results @@ -3292,10 +3313,10 @@ Function/WAVE GetTPResults(string device) if(ExistsWithCorrectLayoutVersion(wv, version)) return wv elseif(WaveExists(wv)) - Redimension/D/N=(10, NUM_HEADSTAGES) wv + Redimension/D/N=(ItemsInList(labels), NUM_HEADSTAGES) wv wv = NaN else - Make/D/N=(10, NUM_HEADSTAGES) dfr:results/WAVE=wv + Make/D/N=(ItemsInList(labels), NUM_HEADSTAGES) dfr:results/WAVE=wv wv = NaN // initialize with the old 1D waves @@ -3311,7 +3332,7 @@ Function/WAVE GetTPResults(string device) KillOrMoveToTrash(wv = SSResistance) endif - SetDimensionLabels(wv, "ResistanceInst;BaselineSteadyState;ResistanceSteadyState;ElevatedSteadyState;ElevatedInst;AutoTPAmplitude;AutoTPBaseline;AutoTPBaselineRangeExceeded;AutoTPBaselineFitResult;AutoTPDeltaV", ROWS) + SetDimensionLabels(wv, labels, ROWS) SetWaveVersion(wv, version) @@ -8707,3 +8728,14 @@ Function/WAVE GetSFSelectDataComp(string graph, string opShort) return selectDataComp End + +/// @brief Get a free wave that can store TP analysis data +/// +/// @returns a named (tpData) free wave +threadsafe Function/WAVE GetTPAnalysisDataWave() + + Make/FREE=1/D/N=(ItemsInList(TP_ANALYSIS_DATA_LABELS)) tpData + SetDimensionLabels(tpData, TP_ANALYSIS_DATA_LABELS, ROWS) + + return tpData +End From c57a13a8d60c8093ce79fd514f96cdf9428c005b Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 23 Aug 2024 13:55:03 +0200 Subject: [PATCH 4/7] PUB: Add four publishers to publish TP data - The four publishers publish the same json, just with a different period. There is a filter for live, 1s, 5s and 10s publishing interval. - See PUB_TPResult for JSON description - publisher is called from TP_TSAnalysis thread --- Packages/MIES/MIES_Constants.ipf | 4 + .../MIES/MIES_ForeignFunctionInterface.ipf | 3 +- Packages/MIES/MIES_Publish.ipf | 185 +++++++++++++++++- Packages/MIES/MIES_TestPulse.ipf | 2 + Packages/tests/Basic/UTF_ZeroMQPublishing.ipf | 141 +++++++++++++ .../UTF_TestPulseAndTPDuringDAQ.ipf | 141 +++++++++++++ Packages/tests/UTF_DataGenerators.ipf | 8 + 7 files changed, 479 insertions(+), 5 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 094e1daec7..ad2979b6e6 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -1857,6 +1857,10 @@ 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 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" diff --git a/Packages/MIES/MIES_ForeignFunctionInterface.ipf b/Packages/MIES/MIES_ForeignFunctionInterface.ipf index fb2974e277..2cb2be93c2 100644 --- a/Packages/MIES/MIES_ForeignFunctionInterface.ipf +++ b/Packages/MIES/MIES_ForeignFunctionInterface.ipf @@ -67,7 +67,8 @@ Function/WAVE FFI_GetAvailableMessageFilters() 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} + ANALYSIS_FUNCTION_AR, ZMQ_FILTER_TPRESULT_NOW, ZMQ_FILTER_TPRESULT_1S, \ + ZMQ_FILTER_TPRESULT_5S, ZMQ_FILTER_TPRESULT_10S} Note/K wv, "Heartbeat is sent every 5 seconds." diff --git a/Packages/MIES/MIES_Publish.ipf b/Packages/MIES/MIES_Publish.ipf index 8443df668e..056bb2c4b9 100644 --- a/Packages/MIES/MIES_Publish.ipf +++ b/Packages/MIES/MIES_Publish.ipf @@ -25,20 +25,23 @@ static Function PUB_GetJSONTemplate(string device, variable headstage) End /// @brief Publish the given message as given by the JSON and the filter -static Function PUB_Publish(variable jsonID, string messageFilter) +threadsafe Function PUB_Publish(variable jsonID, string messageFilter, [variable releaseJSON]) variable err string payload - payload = JSON_Dump(jsonID) - JSON_Release(jsonID) + releaseJSON = ParamIsDefault(releaseJSON) ? 1 : !!releaseJSON + payload = JSON_Dump(jsonID) + if(releaseJSON) + JSON_Release(jsonID) + endif AssertOnAndClearRTError() try zeromq_pub_send(messageFilter, payload); AbortOnRTE catch err = ClearRTError() - BUG("Could not publish " + messageFilter + " due to: " + num2str(err)) + BUG_TS("Could not publish " + messageFilter + " due to: " + num2str(err)) endtry End @@ -655,3 +658,177 @@ Function PUB_AccessResistanceSmoke(string device, variable sweepNo, variable hea PUB_Publish(jsonID, ANALYSIS_FUNCTION_AR) End + +threadsafe static Function PUB_AddTPResultEntry(variable jsonId, string path, variable value, string unit) + + if(IsEmpty(unit)) + JSON_AddVariable(jsonID, path, value) + else + JSON_AddTreeObject(jsonID, path) + JSON_AddVariable(jsonID, path + "/value", value) + JSON_AddString(jsonID, path + "/unit", unit) + endif +End + +/// Filter: #ZMQ_FILTER_TPRESULT_NOW +/// Filter: #ZMQ_FILTER_TPRESULT_1S +/// Filter: #ZMQ_FILTER_TPRESULT_5S +/// Filter: #ZMQ_FILTER_TPRESULT_10S +/// +/// Example: +/// +/// \rst +/// .. code-block:: json +/// +/// { +/// "properties": { +/// "baseline fraction": { +/// "unit": "%", +/// "value": 35 +/// }, +/// "clamp amplitude": { +/// "unit": "mV", +/// "value": 10 +/// }, +/// "clamp mode": 0, +/// "device": "TestDevice", +/// "headstage": 1, +/// "pulse duration ADC": { +/// "unit": "points", +/// "value": 500 +/// }, +/// "pulse duration DAC": { +/// "unit": "points", +/// "value": 600 +/// }, +/// "pulse start point ADC": { +/// "unit": "point", +/// "value": 500 +/// }, +/// "pulse start point DAC": { +/// "unit": "point", +/// "value": 600 +/// }, +/// "sample interval ADC": { +/// "unit": "ms", +/// "value": 0.002 +/// }, +/// "sample interval DAC": { +/// "unit": "ms", +/// "value": 0.002 +/// }, +/// "time of tp acquisition": { +/// "unit": "s", +/// "value": 1000000 +/// }, +/// "timestamp": { +/// "unit": "s", +/// "value": 2000000 +/// }, +/// "timestampUTC": { +/// "unit": "s", +/// "value": 3000000 +/// }, +/// "tp cycle id": 456, +/// "tp length ADC": { +/// "unit": "points", +/// "value": 1500 +/// }, +/// "tp length DAC": { +/// "unit": "points", +/// "value": 1800 +/// }, +/// "tp marker": 1234 +/// }, +/// "results": { +/// "average baseline steady state": { +/// "unit": "pA", +/// "value": 2 +/// }, +/// "average tp steady state": { +/// "unit": "pA", +/// "value": 10 +/// }, +/// "instantaneous": { +/// "unit": "pA", +/// "value": 11 +/// }, +/// "instantaneous resistance": { +/// "unit": "MΩ", +/// "value": 2345 +/// }, +/// "steady state resistance": { +/// "unit": "MΩ", +/// "value": 1234 +/// } +/// } +/// } +/// +/// \endrst +threadsafe Function PUB_TPResult(string device, WAVE tpData) + + string path + variable jsonId = JSON_New() + string adUnit = GetADChannelUnit(tpData[%CLAMPMODE]) + string daUnit = GetDAChannelUnit(tpData[%CLAMPMODE]) + + path = "properties" + JSON_AddTreeObject(jsonID, path) + JSON_AddString(jsonID, path + "/device", device) + JSON_AddVariable(jsonID, path + "/tp marker", tpData[%MARKER]) + JSON_AddVariable(jsonID, path + "/headstage", tpData[%HEADSTAGE]) + JSON_AddVariable(jsonID, path + "/clamp mode", tpData[%CLAMPMODE]) + + PUB_AddTPResultEntry(jsonId, path + "/time of tp acquisition", tpData[%NOW], "s") + PUB_AddTPResultEntry(jsonId, path + "/clamp amplitude", tpData[%CLAMPAMP], daUnit) + PUB_AddTPResultEntry(jsonId, path + "/tp length ADC", tpData[%TPLENGTHPOINTSADC], "points") + PUB_AddTPResultEntry(jsonId, path + "/pulse duration ADC", tpData[%PULSELENGTHPOINTSADC], "points") + PUB_AddTPResultEntry(jsonId, path + "/pulse start point ADC", tpData[%PULSESTARTPOINTSADC], "point") + PUB_AddTPResultEntry(jsonId, path + "/sample interval ADC", tpData[%SAMPLINGINTERVALADC], "ms") + PUB_AddTPResultEntry(jsonId, path + "/tp length DAC", tpData[%TPLENGTHPOINTSDAC], "points") + PUB_AddTPResultEntry(jsonId, path + "/pulse duration DAC", tpData[%PULSELENGTHPOINTSDAC], "points") + PUB_AddTPResultEntry(jsonId, path + "/pulse start point DAC", tpData[%PULSESTARTPOINTSDAC], "point") + PUB_AddTPResultEntry(jsonId, path + "/sample interval DAC", tpData[%SAMPLINGINTERVALDAC], "ms") + PUB_AddTPResultEntry(jsonId, path + "/baseline fraction", tpData[%BASELINEFRAC] * ONE_TO_PERCENT, "%") + PUB_AddTPResultEntry(jsonId, path + "/timestamp", tpData[%TIMESTAMP], "s") + PUB_AddTPResultEntry(jsonId, path + "/timestampUTC", tpData[%TIMESTAMPUTC], "s") + PUB_AddTPResultEntry(jsonId, path + "/tp cycle id", tpData[%CYCLEID], "") + + path = "results" + JSON_AddTreeObject(jsonID, path) + PUB_AddTPResultEntry(jsonId, path + "/average baseline steady state", tpData[%BASELINE], adUnit) + PUB_AddTPResultEntry(jsonId, path + "/average tp steady state", tpData[%ELEVATED_SS], adUnit) + PUB_AddTPResultEntry(jsonId, path + "/instantaneous", tpData[%ELEVATED_INST], adUnit) + PUB_AddTPResultEntry(jsonId, path + "/steady state resistance", tpData[%STEADYSTATERES], "MΩ") + PUB_AddTPResultEntry(jsonId, path + "/instantaneous resistance", tpData[%INSTANTRES], "MΩ") + + PUB_Publish(jsonID, ZMQ_FILTER_TPRESULT_NOW, releaseJSON = 0) + if(PUB_CheckPublishingTime(ZMQ_FILTER_TPRESULT_1S, 1)) + PUB_Publish(jsonID, ZMQ_FILTER_TPRESULT_1S, releaseJSON = 0) + endif + if(PUB_CheckPublishingTime(ZMQ_FILTER_TPRESULT_5S, 5)) + PUB_Publish(jsonID, ZMQ_FILTER_TPRESULT_5S, releaseJSON = 0) + endif + if(PUB_CheckPublishingTime(ZMQ_FILTER_TPRESULT_10S, 10)) + PUB_Publish(jsonID, ZMQ_FILTER_TPRESULT_10S, releaseJSON = 0) + endif + JSON_Release(jsonID) +End + +/// @brief Updates the publishing timestamp in the TUFXOP storage and returns 1 if an update is due (0 otherwise) +threadsafe static Function PUB_CheckPublishingTime(string pubFilter, variable period) + + variable lastTime + variable curTime = DateTime + + TUFXOP_AcquireLock/N=(pubFilter) + lastTime = TSDS_ReadVar(pubFilter, defValue = 0, create = 1) + if(lastTime + period < curTime) + TSDS_WriteVar(pubFilter, curTime + period) + TUFXOP_ReleaseLock/N=(pubFilter) + return 1 + endif + TUFXOP_ReleaseLock/N=(pubFilter) + + return 0 +End diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index 2dc27e98b2..813311de76 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -1018,6 +1018,8 @@ threadsafe Function/DF TP_TSAnalysis(DFREF dfrInp) tpData[%PULSESTARTPOINTSDAC] = pulseStartPointsDAC tpData[%SAMPLINGINTERVALDAC] = samplingIntervalDAC + PUB_TPResult(device, tpData) + return dfrOut End diff --git a/Packages/tests/Basic/UTF_ZeroMQPublishing.ipf b/Packages/tests/Basic/UTF_ZeroMQPublishing.ipf index f0d7c80c99..f3d95fb8a7 100644 --- a/Packages/tests/Basic/UTF_ZeroMQPublishing.ipf +++ b/Packages/tests/Basic/UTF_ZeroMQPublishing.ipf @@ -479,3 +479,144 @@ static Function CheckAccessResSmoke() JSON_Release(jsonID) End + +static Function/WAVE PrepareTPData() + + WAVE tpData = GetTPAnalysisDataWave() + tpData[%NOW] = 1E6 + tpData[%HEADSTAGE] = 1 + tpData[%MARKER] = 1234 + tpData[%NUMBER_OF_TP_CHANNELS] = 2 + tpData[%TIMESTAMP] = 2E6 + tpData[%TIMESTAMPUTC] = 3E6 + tpData[%CLAMPMODE] = V_CLAMP_MODE + tpData[%CLAMPAMP] = 10 + tpData[%BASELINEFRAC] = 0.35 + tpData[%CYCLEID] = 456 + tpData[%TPLENGTHPOINTSADC] = 1500 + tpData[%PULSELENGTHPOINTSADC] = 500 + tpData[%PULSESTARTPOINTSADC] = 500 + tpData[%SAMPLINGINTERVALADC] = 0.002 + tpData[%TPLENGTHPOINTSDAC] = 1800 + tpData[%PULSELENGTHPOINTSDAC] = 600 + tpData[%PULSESTARTPOINTSDAC] = 600 + tpData[%SAMPLINGINTERVALDAC] = 0.002 + + tpData[%BASELINE] = 2 + tpData[%ELEVATED_SS] = 10 + tpData[%ELEVATED_INST] = 11 + tpData[%STEADYSTATERES] = 1234 + tpData[%INSTANTRES] = 2345 + + return tpData +End + +static Function CheckTPData(variable jsonId) + + variable var + string stv, daUnit, adUnit + variable clampMode = V_CLAMP_MODE + + daUnit = GetDAChannelUnit(clampMode) + adUnit = GetADChannelUnit(clampMode) + + var = JSON_GetVariable(jsonID, "/properties/tp marker") + CHECK_EQUAL_VAR(var, 1234) + var = JSON_GetVariable(jsonID, "/properties/headstage") + CHECK_EQUAL_VAR(var, 1) + stv = JSON_GetString(jsonID, "/properties/device") + CHECK_EQUAL_STR(stv, "TestDevice") + var = JSON_GetVariable(jsonID, "/properties/clamp mode") + CHECK_EQUAL_VAR(var, clampMode) + var = JSON_GetVariable(jsonID, "/properties/time of tp acquisition/value") + CHECK_EQUAL_VAR(var, 1E6) + stv = JSON_GetString(jsonID, "/properties/time of tp acquisition/unit") + CHECK_EQUAL_STR(stv, "s") + var = JSON_GetVariable(jsonID, "/properties/clamp amplitude/value") + CHECK_EQUAL_VAR(var, 10) + stv = JSON_GetString(jsonID, "/properties/clamp amplitude/unit") + CHECK_EQUAL_STR(stv, daUnit) + var = JSON_GetVariable(jsonID, "/properties/tp length ADC/value") + CHECK_EQUAL_VAR(var, 1500) + stv = JSON_GetString(jsonID, "/properties/tp length ADC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse duration ADC/value") + CHECK_EQUAL_VAR(var, 500) + stv = JSON_GetString(jsonID, "/properties/pulse duration ADC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse start point ADC/value") + CHECK_EQUAL_VAR(var, 500) + stv = JSON_GetString(jsonID, "/properties/pulse start point ADC/unit") + CHECK_EQUAL_STR(stv, "point") + var = JSON_GetVariable(jsonID, "/properties/sample interval ADC/value") + CHECK_EQUAL_VAR(var, 0.002) + stv = JSON_GetString(jsonID, "/properties/sample interval ADC/unit") + CHECK_EQUAL_STR(stv, "ms") + var = JSON_GetVariable(jsonID, "/properties/tp length DAC/value") + CHECK_EQUAL_VAR(var, 1800) + stv = JSON_GetString(jsonID, "/properties/tp length DAC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse duration DAC/value") + CHECK_EQUAL_VAR(var, 600) + stv = JSON_GetString(jsonID, "/properties/pulse duration DAC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse start point DAC/value") + CHECK_EQUAL_VAR(var, 600) + stv = JSON_GetString(jsonID, "/properties/pulse start point DAC/unit") + CHECK_EQUAL_STR(stv, "point") + var = JSON_GetVariable(jsonID, "/properties/sample interval DAC/value") + CHECK_EQUAL_VAR(var, 0.002) + stv = JSON_GetString(jsonID, "/properties/sample interval DAC/unit") + CHECK_EQUAL_STR(stv, "ms") + var = JSON_GetVariable(jsonID, "/properties/baseline fraction/value") + CHECK_EQUAL_VAR(var, 35) + stv = JSON_GetString(jsonID, "/properties/baseline fraction/unit") + CHECK_EQUAL_STR(stv, "%") + var = JSON_GetVariable(jsonID, "/properties/timestamp/value") + CHECK_EQUAL_VAR(var, 2E6) + stv = JSON_GetString(jsonID, "/properties/timestamp/unit") + CHECK_EQUAL_STR(stv, "s") + var = JSON_GetVariable(jsonID, "/properties/timestampUTC/value") + CHECK_EQUAL_VAR(var, 3E6) + stv = JSON_GetString(jsonID, "/properties/timestampUTC/unit") + CHECK_EQUAL_STR(stv, "s") + + var = JSON_GetVariable(jsonID, "/properties/tp cycle id") + CHECK_EQUAL_VAR(var, 456) + + var = JSON_GetVariable(jsonID, "/results/average baseline steady state/value") + CHECK_EQUAL_VAR(var, 2) + stv = JSON_GetString(jsonID, "/results/average baseline steady state/unit") + CHECK_EQUAL_STR(stv, adUnit) + var = JSON_GetVariable(jsonID, "/results/average tp steady state/value") + CHECK_EQUAL_VAR(var, 10) + stv = JSON_GetString(jsonID, "/results/average tp steady state/unit") + CHECK_EQUAL_STR(stv, adUnit) + var = JSON_GetVariable(jsonID, "/results/instantaneous/value") + CHECK_EQUAL_VAR(var, 11) + stv = JSON_GetString(jsonID, "/results/instantaneous/unit") + CHECK_EQUAL_STR(stv, adUnit) + var = JSON_GetVariable(jsonID, "/results/steady state resistance/value") + CHECK_EQUAL_VAR(var, 1234) + stv = JSON_GetString(jsonID, "/results/steady state resistance/unit") + CHECK_EQUAL_STR(stv, "MΩ") + var = JSON_GetVariable(jsonID, "/results/instantaneous resistance/value") + CHECK_EQUAL_VAR(var, 2345) + stv = JSON_GetString(jsonID, "/results/instantaneous resistance/unit") + CHECK_EQUAL_STR(stv, "MΩ") +End + +// IUTF_TD_GENERATOR DataGenerators#PUB_TPFilters +static Function CheckTPPublishing([string str]) + + variable jsonId + + WAVE tpData = PrepareTPData() + + TUFXOP_Clear/Z/N=(str) + PUB_TPResult("TestDevice", tpData) + + jsonId = FetchAndParseMessage(str) + CheckTPData(jsonId) + JSON_Release(jsonID) +End diff --git a/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf b/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf index 34df5f7218..448ea336fd 100644 --- a/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf +++ b/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf @@ -1305,3 +1305,144 @@ static Function TPZerosDAC_REENTRY([STRUCT IUTF_MDATA &md]) CHECK_LE_VAR(HW_ReadADC(hardwareType, deviceID, ADC), 0.01) End + +/// UTF_TD_GENERATOR DeviceNameGeneratorMD1 +static Function TestTPPublishing([string str]) + + TUFXOP_Clear/Z/N=(ZMQ_FILTER_TPRESULT_1S) + TUFXOP_Clear/Z/N=(ZMQ_FILTER_TPRESULT_5S) + TUFXOP_Clear/Z/N=(ZMQ_FILTER_TPRESULT_10S) + + STRUCT DAQSettings s + InitDAQSettingsFromString(s, "MD1_RA0_I0_L0_BKG1_STP1_TP1" + \ + "__HS0_DA0_AD0_CM:IC:_ST:TestPulse:" + \ + "__HS1_DA1_AD1_CM:VC:_ST:TestPulse:") + + AcquireData_NG(s, str) + + PrepareForPublishTest() + + CtrlNamedBackGround StopTPAfterFiveSeconds, start=(ticks + TP_DURATION_S * 60), period=1, proc=StopTPAfterFiveSeconds_IGNORE +End + +static Function TestTPPublishing_REENTRY([string str]) + + variable sweepNo, jsonId, var, index, dimMarker, headstage + string msg, filter, stv, adUnit, daUnit + + CHECK_EQUAL_VAR(GetSetVariable(str, "SetVar_Sweep"), 0) + + sweepNo = AFH_GetLastSweepAcquired(str) + CHECK_EQUAL_VAR(sweepNo, NaN) + + WaitAndCheckStoredTPs_IGNORE(str, 2) + + WAVE tpStorage = GetTPStorage(str) + dimMarker = FindDimLabel(tpStorage, LAYERS, "TPMarker") + + WAVE/T filters = DataGenerators#PUB_TPFilters() + for(filter : filters) + msg = FetchPublishedMessage(filter) + jsonId = JSON_Parse(msg) + + var = JSON_GetVariable(jsonID, "/properties/tp marker") + CHECK_NEQ_VAR(var, NaN) + + FindValue/RMD=[][0][dimMarker]/V=(var) tpStorage + ASSERT(V_row >= 0, "Inconsistent TP data") + index = V_row + + var = JSON_GetVariable(jsonID, "/properties/headstage") + CHECK_GE_VAR(var, 0) + CHECK_LE_VAR(var, 1) + headstage = var + + stv = JSON_GetString(jsonID, "/properties/device") + CHECK_EQUAL_STR(stv, str) + var = JSON_GetVariable(jsonID, "/properties/clamp mode") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%ClampMode]) + daUnit = GetDAChannelUnit(var) + adUnit = GetADChannelUnit(var) + + var = JSON_GetVariable(jsonID, "/properties/time of tp acquisition/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%TimeInSeconds]) + stv = JSON_GetString(jsonID, "/properties/time of tp acquisition/unit") + CHECK_EQUAL_STR(stv, "s") + var = JSON_GetVariable(jsonID, "/properties/clamp amplitude/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%CLAMPAMP]) + stv = JSON_GetString(jsonID, "/properties/clamp amplitude/unit") + CHECK_EQUAL_STR(stv, daUnit) + var = JSON_GetVariable(jsonID, "/properties/tp length ADC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%TPLENGTHPOINTSADC]) + stv = JSON_GetString(jsonID, "/properties/tp length ADC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse duration ADC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%PULSELENGTHPOINTSADC]) + stv = JSON_GetString(jsonID, "/properties/pulse duration ADC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse start point ADC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%PULSESTARTPOINTSADC]) + stv = JSON_GetString(jsonID, "/properties/pulse start point ADC/unit") + CHECK_EQUAL_STR(stv, "point") + var = JSON_GetVariable(jsonID, "/properties/sample interval ADC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%SAMPLINGINTERVALADC]) + stv = JSON_GetString(jsonID, "/properties/sample interval ADC/unit") + CHECK_EQUAL_STR(stv, "ms") + var = JSON_GetVariable(jsonID, "/properties/tp length DAC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%TPLENGTHPOINTSDAC]) + stv = JSON_GetString(jsonID, "/properties/tp length DAC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse duration DAC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%PULSELENGTHPOINTSDAC]) + stv = JSON_GetString(jsonID, "/properties/pulse duration DAC/unit") + CHECK_EQUAL_STR(stv, "points") + var = JSON_GetVariable(jsonID, "/properties/pulse start point DAC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%PULSESTARTPOINTSDAC]) + stv = JSON_GetString(jsonID, "/properties/pulse start point DAC/unit") + CHECK_EQUAL_STR(stv, "point") + var = JSON_GetVariable(jsonID, "/properties/sample interval DAC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%SAMPLINGINTERVALDAC]) + stv = JSON_GetString(jsonID, "/properties/sample interval DAC/unit") + CHECK_EQUAL_STR(stv, "ms") + var = JSON_GetVariable(jsonID, "/properties/baseline fraction/value") + CHECK_EQUAL_VAR(var, 35) + stv = JSON_GetString(jsonID, "/properties/baseline fraction/unit") + CHECK_EQUAL_STR(stv, "%") + var = JSON_GetVariable(jsonID, "/properties/timestamp/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%TimeStamp]) + stv = JSON_GetString(jsonID, "/properties/timestamp/unit") + CHECK_EQUAL_STR(stv, "s") + var = JSON_GetVariable(jsonID, "/properties/timestampUTC/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%TimeStampSinceIgorEpochUTC]) + stv = JSON_GetString(jsonID, "/properties/timestampUTC/unit") + CHECK_EQUAL_STR(stv, "s") + + var = JSON_GetVariable(jsonID, "/properties/tp cycle id") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%TPCycleID]) + + var = JSON_GetVariable(jsonID, "/results/average baseline steady state/value") + if(tpStorage[index][headstage][%ClampMode] == V_CLAMP_MODE) + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%Baseline_VC]) + else + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%Baseline_IC]) + endif + stv = JSON_GetString(jsonID, "/results/average baseline steady state/unit") + CHECK_EQUAL_STR(stv, adUnit) + var = JSON_GetVariable(jsonID, "/results/average tp steady state/value") + CHECK_NEQ_VAR(var, NaN) + stv = JSON_GetString(jsonID, "/results/average tp steady state/unit") + CHECK_EQUAL_STR(stv, adUnit) + var = JSON_GetVariable(jsonID, "/results/instantaneous/value") + CHECK_NEQ_VAR(var, NaN) + stv = JSON_GetString(jsonID, "/results/instantaneous/unit") + CHECK_EQUAL_STR(stv, adUnit) + var = JSON_GetVariable(jsonID, "/results/steady state resistance/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%SteadyStateResistance]) + stv = JSON_GetString(jsonID, "/results/steady state resistance/unit") + CHECK_EQUAL_STR(stv, "MΩ") + var = JSON_GetVariable(jsonID, "/results/instantaneous resistance/value") + CHECK_EQUAL_VAR(var, tpStorage[index][headstage][%PeakResistance]) + stv = JSON_GetString(jsonID, "/results/instantaneous resistance/unit") + CHECK_EQUAL_STR(stv, "MΩ") + endfor +End diff --git a/Packages/tests/UTF_DataGenerators.ipf b/Packages/tests/UTF_DataGenerators.ipf index 0d976fb097..0971e7fc28 100644 --- a/Packages/tests/UTF_DataGenerators.ipf +++ b/Packages/tests/UTF_DataGenerators.ipf @@ -958,3 +958,11 @@ static Function/WAVE SF_TestOperationSelNoArg() return wt End + +static Function/WAVE PUB_TPFilters() + + Make/FREE/T wv = {ZMQ_FILTER_TPRESULT_NOW, ZMQ_FILTER_TPRESULT_1S, ZMQ_FILTER_TPRESULT_5S, ZMQ_FILTER_TPRESULT_10S} + SetDimensionLabels(wv, "period_now;period_1s;period_5s;period_10s", ROWS) + + return wv +End From 22a9ef305ca60b2eddbe4cc8d72b3fe4f4934d04 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 25 Aug 2024 19:37:20 +0200 Subject: [PATCH 5/7] TP: Rename TP_GetStoredTPs to TP_GetConsecutiveTPsUptoMarker This prevent misleading naming and it more fitting to the functionality the function actually implements --- Packages/MIES/MIES_TestPulse.ipf | 4 ++-- Packages/tests/Basic/UTF_Testpulse.ipf | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index 813311de76..aba644f6bf 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -107,7 +107,7 @@ End /// identified by tpMarker. /// /// The wave reference wave will have as many columns as active headstages were used. -Function/WAVE TP_GetStoredTPs(string device, variable tpMarker, variable number) +Function/WAVE TP_GetConsecutiveTPsUptoMarker(string device, variable tpMarker, variable number) variable numEntries @@ -310,7 +310,7 @@ End /// - Active headstages static Function/WAVE TP_GetTPWaveForAutoTP(string device, variable marker) - WAVE/Z/WAVE TPs = TP_GetStoredTPs(device, marker, 2) + WAVE/Z/WAVE TPs = TP_GetConsecutiveTPsUptoMarker(device, marker, 2) if(!WaveExists(TPs)) return $"" diff --git a/Packages/tests/Basic/UTF_Testpulse.ipf b/Packages/tests/Basic/UTF_Testpulse.ipf index 34381aca3d..c9f949421f 100644 --- a/Packages/tests/Basic/UTF_Testpulse.ipf +++ b/Packages/tests/Basic/UTF_Testpulse.ipf @@ -41,29 +41,29 @@ static Function FetchingTestpulsesWorks() string device = "myDevice" // emty stored TPs - WAVE/Z result = TP_GetStoredTPs(device, 0xA, 1) + WAVE/Z result = TP_GetConsecutiveTPsUptoMarker(device, 0xA, 1) CHECK_WAVE(result, NULL_WAVE) TP_StoreTP(device, {1}, 0xA, "I_DONT_CARE") // not found - WAVE/Z result = TP_GetStoredTPs(device, 0xB, 1) + WAVE/Z result = TP_GetConsecutiveTPsUptoMarker(device, 0xB, 1) CHECK_WAVE(result, NULL_WAVE) // requested too many - WAVE/Z result = TP_GetStoredTPs(device, 0xA, 2) + WAVE/Z result = TP_GetConsecutiveTPsUptoMarker(device, 0xA, 2) CHECK_WAVE(result, NULL_WAVE) TP_StoreTP(device, {2}, 0xB, "I_DONT_CARE") // works with fetching one TP - WAVE/Z/WAVE result = TP_GetStoredTPs(device, 0xA, 1) + WAVE/Z/WAVE result = TP_GetConsecutiveTPsUptoMarker(device, 0xA, 1) CHECK_WAVE(result, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(result, ROWS), 1) CHECK_EQUAL_WAVES(WaveRef(result, row = 0), {{1}}, mode = WAVE_DATA) // works with fetching two TPs - WAVE/Z/WAVE result = TP_GetStoredTPs(device, 0xB, 2) + WAVE/Z/WAVE result = TP_GetConsecutiveTPsUptoMarker(device, 0xB, 2) CHECK_WAVE(result, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(result, ROWS), 2) CHECK_EQUAL_WAVES(WaveRef(result, row = 0), {{1}}, mode = WAVE_DATA) @@ -76,12 +76,12 @@ static Function FetchingTestpulsesWorks() SetNumberInWaveNote(storedTPs[2], "TPCycleID", 4711) // fetching one works - WAVE/Z/WAVE result = TP_GetStoredTPs(device, 0xC, 1) + WAVE/Z/WAVE result = TP_GetConsecutiveTPsUptoMarker(device, 0xC, 1) CHECK_WAVE(result, WAVE_WAVE) CHECK_EQUAL_VAR(DimSize(result, ROWS), 1) CHECK_EQUAL_WAVES(WaveRef(result, row = 0), {{3}}, mode = WAVE_DATA) // but two not because 0xB has a different cycle id - WAVE/Z/WAVE result = TP_GetStoredTPs(device, 0xC, 2) + WAVE/Z/WAVE result = TP_GetConsecutiveTPsUptoMarker(device, 0xC, 2) CHECK_WAVE(result, NULL_WAVE) End From cc95e5c812ffb29233ae11bb7414a99d64ef8a4d Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 22 Aug 2024 13:46:25 +0200 Subject: [PATCH 6/7] TP: Added two functions that allow to retrieve info about TPs Added TP_GetStoredTP and TP_GetStoredTPsFromCycle that allow to get information about a TP by tpMarker or TPs by cycle id and headstage. - both functions allow to recreate the DA wave for the TPs with the flag includeDAC - the returned data includes the AD, DA data as well as metadata for each returned TP (from TPStorage). - These TP functions use the same TP utility function. --- Packages/MIES/MIES_TestPulse.ipf | 173 ++++++++++++++++-- .../UTF_TestPulseAndTPDuringDAQ.ipf | 108 +++++++++++ 2 files changed, 264 insertions(+), 17 deletions(-) diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index aba644f6bf..3a834462bf 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -103,45 +103,184 @@ Function TP_StoreTP(string device, WAVE TPWave, variable tpMarker, string hsList SetNumberInWaveNote(storedTP, NOTE_INDEX, index) End -/// @brief Return a number of consecutive test pulses ending with the TP -/// identified by tpMarker. -/// -/// The wave reference wave will have as many columns as active headstages were used. -Function/WAVE TP_GetConsecutiveTPsUptoMarker(string device, variable tpMarker, variable number) +static Function TP_GetStoredTPIndex(string device, variable tpMarker) variable numEntries WAVE/WAVE storedTP = GetStoredTestPulseWave(device) numEntries = GetNumberFromWaveNote(storedTP, NOTE_INDEX) - if(numEntries == 0) - return $"" + return NaN endif Make/FREE/N=(numEntries) matches - Multithread matches[0, numEntries - 1] = GetNumberFromWaveNote(storedTP[p], "TPMarker") == tpMarker - FindValue/V=1 matches - if(V_row == -1) + return NaN + endif + + return V_row +End + +static Function/WAVE TP_RecreateDACWave(variable tpLengthPointsDAC, variable pulseStartPointsDAC, variable pulseLengthPointsDAC, variable clampMode, variable clampAmp, variable samplingInterval) + + Make/FREE/D/N=0 tpDAC + TP_CreateTestPulseWaveImpl(tpDAC, tpLengthPointsDAC, pulseStartPointsDAC, pulseLengthPointsDAC) + tpDAC *= clampAmp + SetScale/P x, 0, samplingInterval, "ms", tpDAC + SetScale d, 0, 0, GetADChannelUnit(clampMode), tpDAC + + return tpDAC +End + +static Function/WAVE TP_GetTPMetaData(WAVE tpStorage, variable tpMarker, variable headstage) + + variable i + variable numEntries = GetNumberFromWaveNote(tpStorage, NOTE_INDEX) + variable numlayers = DimSize(tpStorage, LAYERS) + variable dimMarker = FindDimLabel(tpStorage, LAYERS, "TPMarker") + + FindValue/RMD=[][headstage][dimMarker]/V=(tpMarker) tpStorage + ASSERT(V_row >= 0, "Inconsistent TP data") + Duplicate/FREE/RMD=[V_row][headstage][] tpStorage, tpResult + Redimension/E=1/N=(numLayers) tpResult + for(i = 0; i < numLayers; i += 1) + SetDimLabel ROWS, i, $GetDimLabel(tpStorage, LAYERS, i), tpResult + endfor + + return tpResult +End + +/// @brief Returns data about a stored TestPulse from a given tpMarker +/// +/// Returns a wave reference wave with 3 entries: +/// 0 : numeric wave with the acquired AD data of the test pulse (signal) in the format as created by @ref TP_StoreTP +/// 1 : numeric wave with the recreated DA data of the test pulse (command) +/// 2 : Additional information for the test pulse from creation and analysis in the format described for @ref GetTPStorage +/// As the information is for a single TP only, the wave contains a single slice (1 row) +/// +/// @param device device name +/// @param tpMarker testpulse marker +/// @param headstage headstage number +/// @param includeDAC flag, when set the DAC wave of the testpulse is recreated +Function/WAVE TP_GetStoredTP(string device, variable tpMarker, variable headstage, variable includeDAC) + + variable tpIndex + variable i, numlayers + + ASSERT(IsValidHeadstage(headstage), "Invalid headstage number") + includeDAC = !!includeDAC + + tpIndex = TP_GetStoredTPIndex(device, tpMarker) + if(IsNaN(tpIndex)) return $"" endif - Make/FREE/N=(number)/WAVE result + WAVE/WAVE tpStored = GetStoredTestPulseWave(device) + WAVE tpADC = tpStored[tpIndex] + + WAVE tpStorage = GetTPStorage(device) + WAVE tpResult = TP_GetTPMetaData(tpStorage, tpMarker, headstage) - if(number > V_row + 1) + if(includeDAC) + WAVE tpDAC = TP_RecreateDACWave(tpResult[%TPLENGTHPOINTSDAC], tpResult[%PULSESTARTPOINTSDAC], tpResult[%PULSELENGTHPOINTSDAC], tpResult[%ClampMode], tpResult[%CLAMPAMP], tpResult[%SAMPLINGINTERVALADC]) + else + WAVE/Z tpDAC = $"" + endif + + Make/FREE/WAVE tpAll = {tpADC, tpDAC, tpResult} + + return tpAll +End + +/// @brief Returns data about stored TestPulses from a given cycle id +/// +/// Returns a wave reference wave with 3 entries: +/// 0 : wave ref wave that stores numeric waves with the acquired AD data of the test pulse (signal) in the format as created by @ref TP_StoreTP +/// The number of elements in the wave ref wave equals the number of test pulses in the cycle. +/// 1 : numeric wave with the recreated DA data of the test pulse (command) +/// Note: here only a single wave is recreated because the DA data for all test pulses of that cycle is identical +/// 2 : wave ref wave that stores additional information for the test pulses from creation and analysis in the format described for @ref GetTPStorage +/// The number of elements in the wave ref wave equals the number of test pulses in the cycle and has the same order as the signal waves from index 0. +/// Each element is a single slice (1 row) of tpStorage. +/// +/// If no test pulses exist for the given cycle id a null wave is returned. +/// +/// @param device device name +/// @param cycleId test pulse cycle id +/// @param headstage headstage number +/// @param includeDAC flag, when set the DAC wave of the testpulse is recreated +Function/WAVE TP_GetStoredTPsFromCycle(string device, variable cycleId, variable headstage, variable includeDAC) + + variable i, numStored, numIndices + + ASSERT(IsValidHeadstage(headstage), "Invalid headstage number") + includeDAC = !!includeDAC + + WAVE/WAVE tpStored = GetStoredTestPulseWave(device) + numStored = GetNumberFromWaveNote(tpStored, NOTE_INDEX) + Make/FREE/D/N=(numStored) matchCycleId + matchCycleId[] = cycleId == GetNumberFromWaveNote(tpStored[p], "TPCycleID") ? p : NaN + WAVE/Z tpIndices = ZapNaNs(matchCycleId) + if(!WaveExists(tpIndices)) + return $"" + endif + + numIndices = DimSize(tpIndices, ROWS) + Make/FREE/WAVE/N=(numIndices) tpsADC, tpsresult + for(i = 0; i < numIndices; i += 1) + Duplicate/FREE/RMD=[][headstage] tpStored[tpIndices[i]], tpADCSliced + Redimension/N=(-1) tpADCSliced + tpsADC[i] = tpADCSliced + endfor + + Make/FREE/D/N=(numIndices) tpMarkers + tpMarkers[] = GetNumberFromWaveNote(tpsADC[p], "TPMarker") + + WAVE tpStorage = GetTPStorage(device) + tpsResult[] = TP_GetTPMetaData(tpStorage, tpMarkers[p], headstage) + + if(includeDAC) + Make/FREE/WAVE/N=(numIndices) tpsDAC + tpsDAC[] = TP_RecreateDACWave(WaveRef(tpsResult[p])[%TPLENGTHPOINTSDAC], WaveRef(tpsResult[p])[%PULSESTARTPOINTSDAC], WaveRef(tpsResult[p])[%PULSELENGTHPOINTSDAC], WaveRef(tpsResult[p])[%ClampMode], WaveRef(tpsResult[p])[%CLAMPAMP], WaveRef(tpsResult[p])[%SAMPLINGINTERVALADC]) + else + WAVE/Z tpsDAC = $"" + endif + + Make/FREE/WAVE tpAll = {tpsADC, tpsDAC, tpsResult} + + return tpAll +End + +/// @brief Return a number of consecutive test pulses ending with the TP +/// identified by tpMarker. +/// +/// The wave reference wave will have as many columns as active headstages were used. +Function/WAVE TP_GetConsecutiveTPsUptoMarker(string device, variable tpMarker, variable number) + + variable tpIndex, tpCycleId + + tpIndex = TP_GetStoredTPIndex(device, tpMarker) + if(IsNaN(tpIndex)) + return $"" + endif + + if(number > tpIndex + 1) // too few TPs available return $"" endif - result[] = storedTP[V_row - number + 1 + p] + Make/FREE/N=(number)/WAVE result - // check that they all belong to the same TP cycle - Redimension/N=(number) matches - matches[] = GetNumberFromWaveNote(result[0], "TPCycleID") == GetNumberFromWaveNote(result[p], "TPCycleID") + WAVE/WAVE storedTP = GetStoredTestPulseWave(device) + result[] = storedTP[tpIndex - number + 1 + p] - if(Sum(matches) < number) + // check that they all belong to the same TP cycle + Make/FREE/N=(number) matches + tpCycleId = GetNumberFromWaveNote(result[0], "TPCycleID") + matches[] = tpCycleId == GetNumberFromWaveNote(result[p], "TPCycleID") + if(sum(matches) < number) return $"" endif diff --git a/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf b/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf index 448ea336fd..75a386a286 100644 --- a/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf +++ b/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf @@ -1240,6 +1240,114 @@ static Function TPDuringDAQwithPS_PreAcq(string device) PGC_SetAndActivateControl(device, "check_settings_show_power", val = 1) End +/// UTF_TD_GENERATOR DeviceNameGeneratorMD1 +static Function GetStoredTPTest([string str]) + + STRUCT DAQSettings s + InitDAQSettingsFromString(s, "MD1_RA0_I0_L0_BKG1_STP1_TP1" + \ + "__HS0_DA0_AD0_CM:IC:_ST:TestPulse:" + \ + "__HS1_DA1_AD1_CM:VC:_ST:TestPulse:") + + AcquireData_NG(s, str) + + CtrlNamedBackGround StopTPAfterFiveSeconds, start=(ticks + TP_DURATION_S * 60), period=1, proc=StopTPAfterFiveSeconds_IGNORE +End + +static Function GetStoredTPTest_REENTRY([string str]) + + variable i, sweepNo, marker, cycleId, headstage, numHS + + Make/FREE/D headstages = {0, 1} + numHS = DimSize(headstages, ROWS) + + CHECK_EQUAL_VAR(GetSetVariable(str, "SetVar_Sweep"), 0) + + sweepNo = AFH_GetLastSweepAcquired(str) + CHECK_EQUAL_VAR(sweepNo, NaN) + + WaitAndCheckStoredTPs_IGNORE(str, numHS) + + WAVE/Z TPStorage = GetTPStorage(str) + CHECK_WAVE(TPStorage, NUMERIC_WAVE) + marker = TPStorage[0][0][%TPMarker] + CHECK_NEQ_VAR(marker, NaN) + + // Check GetStoredTP + WAVE/Z storedTPData = TP_GetStoredTP(str, NaN, 0, 1) + CHECK_WAVE(storedTPData, NULL_WAVE) + + try + WAVE/Z storedTPData = TP_GetStoredTP(str, marker, NaN, 1) + FAIL() + catch + PASS() + endtry + + Make/FREE/WAVE/N=(numHS) tpDACsRef, tpResultsRef + for(headstage : headstages) + WAVE/Z storedTPData = TP_GetStoredTP(str, marker, headstage, 1) + CHECK_WAVE(storedTPData, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(storedTPData, ROWS), 3) + + WAVE/WAVE tpData = storedTPData + WAVE/Z tpADC = tpData[0] + WAVE/Z tpDAC = tpData[1] + WAVE/Z tpInfo = tpData[2] + + CHECK_WAVE(tpADC, NUMERIC_WAVE) + CHECK_GE_VAR(DimSize(tpADC, ROWS), 1) + CHECK_WAVE(tpDAC, NUMERIC_WAVE) + CHECK_GE_VAR(DimSize(tpDAC, ROWS), 1) + + WAVE tpResult = MIES_TP#TP_GetTPMetaData(tpStorage, marker, headstage) + CHECK_EQUAL_WAVES(tpInfo, tpResult) + + tpDACsRef[i] = tpDAC + tpResultsRef[i] = tpInfo + i += 1 + endfor + + // Check TP_GetStoredTPsFromCycle + cycleId = TPStorage[0][0][%TPCycleID] + CHECK_NEQ_VAR(cycleId, NaN) + + WAVE/Z storedTPData = TP_GetStoredTPsFromCycle(str, NaN, 0, 1) + CHECK_WAVE(storedTPData, NULL_WAVE) + + try + WAVE/Z storedTPData = TP_GetStoredTPsFromCycle(str, cycleId, NaN, 1) + FAIL() + catch + PASS() + endtry + + i = 0 + for(headstage : headstages) + WAVE/Z storedTPData = TP_GetStoredTPsFromCycle(str, cycleId, headstage, 1) + CHECK_WAVE(storedTPData, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(storedTPData, ROWS), 3) + + WAVE/WAVE tpData = storedTPData + WAVE/Z/WAVE tpADCs = tpData[0] + WAVE/Z/WAVE tpDACs = tpData[1] + WAVE/Z/WAVE tpInfoWref = tpData[2] + + CHECK_WAVE(tpADCs, WAVE_WAVE) + CHECK_GE_VAR(DimSize(tpADCs, ROWS), 1) + CHECK_WAVE(tpDACs, WAVE_WAVE) + CHECK_GE_VAR(DimSize(tpDACs, ROWS), 1) + CHECK_WAVE(tpInfoWref, WAVE_WAVE) + CHECK_EQUAL_VAR(DimSize(tpADCs, ROWS), DimSize(tpInfoWref, ROWS)) + CHECK_EQUAL_VAR(DimSize(tpADCs, ROWS), DimSize(tpDACs, ROWS)) + + WAVE tpInfo0 = tpInfoWref[0] + CHECK_EQUAL_WAVES(tpInfo0, tpResultsRef[i]) + WAVE tpDAC0 = tpDACs[0] + CHECK_EQUAL_WAVES(tpDAC0, tpDACsRef[i]) + i += 1 + endfor +End + // UTF_TD_GENERATOR DeviceNameGeneratorMD1 static Function TPDuringDAQwithPS([string str]) From fc209363d72cb557db02f8cb6f4cdd8d34888a43 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 4 Sep 2024 15:08:42 +0200 Subject: [PATCH 7/7] Tests: Adapt FetchPublishedMessage for more tries With a running TP adding zeromq publishing messages for each TP it appears that we have to look through more than the last 100 messages after a test to find the requested. - split logic into two parts: either read out upto 10000 existing messages or wait up to 10 seconds (100 trys with 100 ms sleep) if no message is available --- Packages/tests/UTF_HelperFunctions.ipf | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Packages/tests/UTF_HelperFunctions.ipf b/Packages/tests/UTF_HelperFunctions.ipf index 76add5d9cb..49f954eabc 100644 --- a/Packages/tests/UTF_HelperFunctions.ipf +++ b/Packages/tests/UTF_HelperFunctions.ipf @@ -215,17 +215,32 @@ End Function/S FetchPublishedMessage(string expectedFilter) - variable i + variable msgCnt, waitCnt string msg, filter - for(i = 0; i < 100; i += 1) + variable MAX_WAITS = 100 + variable MAX_MESSAGES = 10000 + + for(;;) msg = zeromq_sub_recv(filter) if(!cmpstr(filter, expectedFilter)) break endif - Sleep/S 0.1 + if(IsEmpty(msg)) + waitCnt += 1 + if(waitCnt == MAX_WAITS) + break + endif + Sleep/S 0.1 + else + msgCnt += 1 + if(msgCnt == MAX_MESSAGES) + break + endif + endif + endfor CHECK_EQUAL_STR(filter, expectedFilter)