diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index 5c623e14c5..b731864d57 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -106,45 +106,184 @@ Function TP_StoreTP(device, TPWave, tpMarker, 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(headstage >= 0 && headstage < NUM_HEADSTAGES && IsInteger(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(headstage >= 0 && headstage < NUM_HEADSTAGES && IsInteger(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 9f18ffdd1b..7b48361ee0 100644 --- a/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf +++ b/Packages/tests/HardwareBasic/UTF_TestPulseAndTPDuringDAQ.ipf @@ -1252,6 +1252,115 @@ static Function TPDuringDAQwithPS_PreAcq(device) PGC_SetAndActivateControl(device, "check_settings_show_power", val = 1) End +/// UTF_TD_GENERATOR DeviceNameGeneratorMD1 +static Function GetStoredTPTest([str]) + 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([str]) + 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/WAVE/Z tpADCs = tpData[0] + WAVE/WAVE/Z tpDACs = tpData[1] + WAVE/WAVE/Z 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([str]) string str