From d1231ba2acbc28f446cedd92b44a0fd5ef7835c9 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 19 Jan 2022 21:27:05 +0100 Subject: [PATCH 01/49] SF: Change SF_GetSweepForFormula to return also data from non-displayed sweeps SF_GetSweepForFormula retrieves the sweep data for the data operation. Previously it relied on the displayed traces in the data browser. Now it retrieves also data from non-displayed sweeps. --- Packages/MIES/MIES_SweepFormula.ipf | 161 ++++++++++++---------------- 1 file changed, 70 insertions(+), 91 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 63794cb364..76dccc4684 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1054,101 +1054,84 @@ static Function SF_GetDAChannel(string graph, variable sweep, variable channelTy return NaN End -static Function/WAVE SF_GetSweepForFormula(graph, range, channels, sweeps) - String graph - WAVE range, channels, sweeps +static Function/WAVE SF_GetSweepForFormula(string graph, WAVE range, WAVE channels, WAVE sweeps) - variable i, j, rangeStart, rangeEnd, pOffset, delta, numRows, DAChannel - string dimLabel - variable channelType = -1 - variable xStart = NaN, xEnd = NaN + variable i, j, rangeStart, rangeEnd, pOffset, delta, numRows, DAChannel, numSweeps, sweepNo + variable chanNr, chanType, numChannels, indexOffset, iStart, iLength, cIndex, isSweepBrowser + string dimLabel, device, dataFolder ASSERT(WindowExists(graph), "graph window does not exist") if(IsTextWave(range)) SF_ASSERT(DimSize(range, ROWS) == 1, "A epoch range must be a single string with the epoch name.") WAVE/T wEpochName = range else - SF_ASSERT(DimSize(range, ROWS) == 2, "A numerical range is of the form [rangeStart, rangeEnd].") + SF_ASSERT(DimSize(range, ROWS) == 2, "A numerical range is must have two rows for range start and end.") endif SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - SF_ASSERT(DimSize(range, COLS) <= 1, "Multidimensional ranges not fully implemented.") + numSweeps = DimSize(sweeps, ROWS) + numChannels = DimSize(channels, ROWS) + if(!numSweeps || !numChannels) + WAVE wv = SF_GetDefaultEmptyWave() + return wv + endif - WAVE/T/Z traces = GetTraceInfos(graph) - if(!WaveExists(traces)) - DebugPrint("No traces found for extracting sweep wave locations.") - return $"" + if(DimSize(range, COLS) > 0) + SF_ASSERT(DimSize(range, COLS) == numSweeps, "Multidimensional range column size must equal number of sweeps.") + SF_ASSERT(DimSize(range, LAYERS) == numChannels, "Multidimensional range layers size must equal number of channels.") endif - SortColumns/A/DIML/KNDX={FindDimLabel(traces, COLS, "channelType"), FindDimLabel(traces, COLS, "channelNumber"), FindDimLabel(traces, COLS, "sweepNumber")} sortWaves=traces + isSweepBrowser = BSP_IsSweepBrowser(graph) - Make/FREE/N=(DimSize(sweeps, ROWS), DimSize(channels, ROWS)) indices = NaN - for(i = 0; i < DimSize(sweeps, ROWS); i += 1) - WAVE/Z sweepListIndex = FindIndizes(traces, colLabel = "sweepNumber", var = sweeps[i]) - if(!WaveExists(sweepListIndex)) - continue - endif - for(j = 0; j < DimSize(channels, ROWS); j += 1) - if(channelType != channels[j][%channelType]) - channelType = channels[j][%channelType] - WAVE/Z channelTypeIndex = FindIndizes(traces, colLabel = "channelType", str = StringFromList(channelType, XOP_CHANNEL_NAMES)) - endif - if(!WaveExists(channelTypeIndex)) - continue - endif - WAVE/Z channelNumberIndex = FindIndizes(traces, colLabel = "channelNumber", var = channels[j][%channelNumber]) - if(!WaveExists(channelNumberIndex)) - continue - endif + if(isSweepBrowser) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + else + SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") + device = BSP_GetDevice(graph) + DFREF deviceDFR = GetDeviceDataPath(device) + endif + + Make/FREE/WAVE/N=(numSweeps, numChannels) sweepRefs + Make/FREE/U/I/N=(numSweeps, numChannels) indexStart, indexEnd - // find matching index in @c traces wave - Concatenate/FREE {sweepListIndex, channelTypeIndex, channelNumberIndex}, index - Redimension/N=(numpnts(index))/E=1 index - Sort index, index - Extract/FREE index, matches, (p > 1 && (index[p] == index[p - 1]) && (index[p] == index[p - 2])) - WaveClear index - if(DimSize(matches, ROWS) == 0) + for(i = 0; i < numSweeps; i += 1) + + sweepNo = sweeps[i] + + if(isSweepBrowser) + cIndex = FindDimLabel(sweepMap, COLS, "Sweep") + FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap + if(V_value == -1) continue endif - SF_ASSERT(DimSize(matches, ROWS) == 1, "More than one matching sweep for this sweepNumber, channelType, and channelNumber combination.") - indices[i][j] = matches[0] - WaveClear matches - endfor - endfor + dataFolder = sweepMap[V_row][%DataFolder] + device = sweepMap[V_row][%Device] + DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) + endif + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) - // ASSERT if sweeps are from different experiments - Duplicate/FREE indices wv - Redimension/N=(numpnts(wv))/E=1 wv - WAVE/z wvReduced = ZapNaNs(wv) - if(!WaveExists(wvReduced)) - DebugPrint("No matching sweep.") - return $"" - endif - Make/FREE/T/N=(DimSize(wv, ROWS)) experiments - experiments[] = traces[wv][%experiment] - WaveClear wv - WAVE/Z uniqueExperiments = GetUniqueEntries(experiments) - SF_ASSERT(DimSize(uniqueExperiments, ROWS) == 1, "Sweep data is from more than one experiment. This is currently not supported.") + for(j = 0; j < numChannels; j += 1) - // get data wave dimensions - Make/FREE/U/I/N=(DimSize(sweeps, ROWS), DimSize(channels, ROWS)) pStart, pEnd - for(i = 0; i < DimSize(sweeps, ROWS); i += 1) - for(j = 0; j < DimSize(channels, ROWS); j += 1) - if(IsNaN(indices[i][j])) + chanNr = channels[j][%channelNumber] + chanType = channels[j][%channelType] + WAVE/Z sweep = GetDAQDataSingleColumnWave(sweepDFR, chanType, chanNr) + if(!WaveExists(sweep)) continue endif - WAVE sweep = $(traces[indices[i][j]][%fullPath]) - SF_ASSERT(DimSize(sweep, COLS) <= 1, "Sweeps need to be one-dimensional.") - delta = max(delta, DimDelta(sweep, ROWS)) - SF_ASSERT(delta == DimDelta(sweep, ROWS), "Sweeps are not equally spaced. Data would need to get resampled.") + if(delta == 0) + delta = DimDelta(sweep, ROWS) + else + SF_ASSERT(delta == DimDelta(sweep, ROWS), "Sweeps have not the same delta.") + endif if(WaveExists(wEpochName)) - DAChannel = SF_GetDAChannel(graph, sweeps[i], channels[j][0], channels[j][1]) - WAVE range = SF_GetRangeFromEpoch(graph, wEpochName[0], sweeps[i], DAChannel) + DAChannel = SF_GetDAChannel(graph, sweepNo, chanType, chanNr) + WAVE range = SF_GetRangeFromEpoch(graph, wEpochName[0], sweepNo, DAChannel) endif - if(DimSize(range, COLS) == DimSize(sweeps, ROWS) && DimSize(range, LAYERS) == DimSize(channels, ROWS)) + if(DimSize(range, COLS) > 0) rangeStart = range[0][i][j] rangeEnd = range[1][i][j] else @@ -1156,37 +1139,33 @@ static Function/WAVE SF_GetSweepForFormula(graph, range, channels, sweeps) rangeEnd = range[1] endif SF_ASSERT(!IsNaN(rangeStart) && !IsNaN(rangeEnd), "Specified range not valid.") + SF_ASSERT(rangeStart == -inf || (IsFinite(rangeStart) && rangeStart >= leftx(sweep) && rangeStart < rightx(sweep)), "Specified starting range not inside sweep " + num2istr(sweepNo) + ".") + SF_ASSERT(rangeEnd == inf || (IsFinite(rangeEnd) && rangeEnd >= leftx(sweep) && rangeEnd < rightx(sweep)), "Specified ending range not inside sweep " + num2istr(sweepNo) + ".") - SF_ASSERT(rangeStart == -inf || (IsFinite(rangeStart) && rangeStart >= leftx(sweep) && rangeStart < rightx(sweep)), "Specified starting range does not lie completely inside the sweep.") - SF_ASSERT(rangeEnd == inf || (IsFinite(rangeEnd) && rangeEnd >= leftx(sweep) && rangeEnd < rightx(sweep)), "Specified ending range does not lie completely inside the sweep.") - - pStart[i][j] = ScaleToIndexWrapper(sweep, rangeStart, ROWS) - pEnd[i][j] = ScaleToIndexWrapper(sweep, rangeEnd, ROWS) - - if(IsNaN(xStart) && IsNaN(xEnd)) - xStart = IndexToScale(sweep, pStart[i][j], ROWS) - xEnd = IndexToScale(sweep, pEnd[i][j], ROWS) - else - xStart = min(IndexToScale(sweep, pStart[i][j], ROWS), xStart) - xEnd = max(IndexToScale(sweep, pEnd[i][j], ROWS), xEnd) - endif + indexStart[i][j] = ScaleToIndexWrapper(sweep, rangeStart, ROWS) + indexEnd[i][j] = ScaleToIndexWrapper(sweep, rangeEnd, ROWS) + sweepRefs[i][j] = sweep endfor endfor - numRows = round((xEnd - xStart) / delta + 1) + indexOffset = WaveMin(indexStart) + numRows = WaveMax(indexEnd) + 1 - indexOffset // combine sweeps to data wave - Make/FREE/D/N=(numRows, DimSize(sweeps, ROWS), DimSize(channels, ROWS)) sweepData = NaN - SetScale/P x, xStart, delta, sweepData - for(i = 0; i < DimSize(sweeps, ROWS); i += 1) - for(j = 0; j < DimSize(channels, ROWS); j += 1) - if(IsNaN(indices[i][j])) + Make/FREE/N=(numRows, numSweeps, numChannels) sweepData = NaN + SetScale/P x, indexOffset * delta, delta, sweepData + for(i = 0; i < numSweeps; i += 1) + for(j = 0; j < numChannels; j += 1) + + WAVE/Z sweep = sweepRefs[i][j] + if(!WaveExists(sweep)) continue endif - WAVE sweep = $(traces[indices[i][j]][%fullPath]) - pOffset = ScaleToIndexWrapper(sweepData, IndexToScale(sweep, pStart[i][j], ROWS), ROWS) - MultiThread sweepData[pOffset, pOffSet + (pEnd[i][j] - pStart[i][j])][i][j] = sweep[pStart[i][j] + (p - pOffset)] + iStart = indexStart[i][j] + iLength = indexEnd[i][j] - iStart + pOffset = iStart - indexOffset + MultiThread sweepData[pOffset, pOffset + iLength][i][j] = sweep[iStart + p - pOffset] if(i == 0) dimLabel = StringFromList(channels[j][%channelType], XOP_CHANNEL_NAMES) + num2istr(channels[j][%channelNumber]) From 8095877e405f54ce10cafcab65bda9b82cf50a05 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 21 Jan 2022 03:21:30 +0100 Subject: [PATCH 02/49] Adapt CreateFakeSweepData to PrepareLBN_Ignore changes PrepareLBN_Ignore creates 4 channels, but CreateFakeSweepData added only two channels, resulting in active channels with missing sweeps. Now properly sweeps for all channels are created. - Added optional parameter sweepNo, that allows to set which sweepNo is created. - Added optional parameter sweepGen, that allows to give a function that generates the sweep data --- Packages/Testing-MIES/UTF_HelperFunctions.ipf | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/Packages/Testing-MIES/UTF_HelperFunctions.ipf b/Packages/Testing-MIES/UTF_HelperFunctions.ipf index 9293a14fc0..36026a43ec 100644 --- a/Packages/Testing-MIES/UTF_HelperFunctions.ipf +++ b/Packages/Testing-MIES/UTF_HelperFunctions.ipf @@ -329,31 +329,53 @@ Function [string key, string keyTxt] PrepareLBN_IGNORE(string device) return [key, keyTxt] End -Function CreateFakeSweepData(string device) - string list, key, keyTxt - variable sweepNo +Function/WAVE FakeSweepDataGeneratorProto(WAVE sweep, variable numChannels) - GetDAQDeviceID(device) + ASSERT(0, "Prototype Function FakeSweepDataGeneratorProto called.") +End - WAVE sweep = GetDAQDataWave(device, DATA_ACQUISITION_MODE) - WAVE config = GetDAQConfigWave(device) +Function/WAVE FakeSweepDataGeneratorDefault(WAVE sweep, variable numChannels) - Redimension/N=(10, 2) sweep + Redimension/N=(10, numChannels) sweep sweep = p + return sweep +End + +Function CreateFakeSweepData(string device, [variable sweepNo, FUNCREF FakeSweepDataGeneratorProto sweepGen]) + + string list, key, keyTxt + variable numChannels + + sweepNo = ParamIsDefault(sweepNo) ? 0: sweepNo + if(ParamIsDefault(sweepGen)) + FUNCREF FakeSweepDataGeneratorProto sweepGen = FakeSweepDataGeneratorDefault + endif + + GetDAQDeviceID(device) + [key, keyTxt] = PrepareLBN_IGNORE(device) + numChannels = 4 // from LBN creation in PrepareLBN_IGNORE -> DA2, AD6, DA3, AD7 - // creates HS 0 with DAC 2 and ADC 6 - Make/FREE/N=(1, 1, LABNOTEBOOK_LAYER_COUNT) values - Make/FREE/N=(1, 1)/T keys = CLAMPMODE_ENTRY_KEY + WAVE sweepTemplate = GetDAQDataWave(device, DATA_ACQUISITION_MODE) + WAVE sweep = sweepGen(sweepTemplate, numChannels) - Redimension/N=(2, -1) config + WAVE config = GetDAQConfigWave(device) + Redimension/N=(4, -1) config + // creates HS 0 with DAC 2 and ADC 6 config[0][%ChannelType] = XOP_CHANNEL_TYPE_DAC config[0][%ChannelNumber] = 2 config[1][%ChannelType] = XOP_CHANNEL_TYPE_ADC config[1][%ChannelNumber] = 6 + // creates HS 1 with DAC 3 and ADC 7 + config[2][%ChannelType] = XOP_CHANNEL_TYPE_DAC + config[2][%ChannelNumber] = 3 + + config[3][%ChannelType] = XOP_CHANNEL_TYPE_ADC + config[3][%ChannelNumber] = 7 + DFREF dfr = GetDeviceDataPath(device) MoveWave sweep, dfr:$GetSweepWaveName(sweepNo) MoveWave config, dfr:$GetConfigWaveName(sweepNo) From 504ba606d07bc887c984ba86812d7c41e034f932 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 21 Jan 2022 04:39:07 +0100 Subject: [PATCH 03/49] SF: Add Tests for data operation Add new tests for changed data operation. Removed old tests that were part of the labnotebook operation test. --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 139 ++++++++++++++++++--- 1 file changed, 121 insertions(+), 18 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 8d88ceeec3..6d3151bce6 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1145,6 +1145,127 @@ static Function TestPlotting() REQUIRE_EQUAL_VAR(WindowExists(win), 1) End +static Function TestDataOperation() + + variable numChannels, sweepNo, rStart, rDelta + string str, epochStr + string win = DATABROWSER_WINDOW_TITLE + variable mode = DATA_ACQUISITION_MODE + variable rangeStart0 = 3 + variable rangeEnd0 = 6 + variable rangeStart1 = 1 + variable rangeEnd1 = 8 + string device = HW_ITC_BuildDeviceString(StringFromList(0, DEVICE_TYPES_ITC), StringFromList(0, DEVICE_NUMBERS)) + Make/FREE/T/N=(1, 1) epochKeys = EPOCHS_ENTRY_KEY + + if(windowExists(win)) + DoWindow/K $win + endif + + Display/N=$win as device + BSP_SetDataBrowser(win) + BSP_SetDevice(win, device) + + sweepNo = 0 + + CreateFakeSweepData(device, sweepNo=sweepNo) + MIES_DB#DB_SplitSweepsIfReq(win, sweepNo) + CreateFakeSweepData(device, sweepNo=sweepNo + 1) + MIES_DB#DB_SplitSweepsIfReq(win, sweepNo + 1) + + epochStr = "0.00" + num2istr(rangeStart0) + ",0.00" + num2istr(rangeEnd0) + ",ShortName=TestEpoch,0" + Make/FREE/T/N=(1, 1, LABNOTEBOOK_LAYER_COUNT) epochInfo = epochStr + ED_AddEntriesToLabnotebook(epochInfo, epochKeys, sweepNo, device, mode) + epochStr = "0.00" + num2istr(rangeStart1) + ",0.00" + num2istr(rangeEnd1) + ",ShortName=TestEpoch,0" + Make/FREE/T/N=(1, 1, LABNOTEBOOK_LAYER_COUNT) epochInfo = epochStr + ED_AddEntriesToLabnotebook(epochInfo, epochKeys, sweepNo + 1, device, mode) + + numChannels = 4 // from LBN creation in CreateFakeSweepData->PrepareLBN_IGNORE -> DA2, AD6, DA3, AD7 + Make/FREE/N=0 sweepTemplate + WAVE sweepRef = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) + + Make/FREE/N=(DimSize(sweepRef, ROWS), 1, numChannels / 2) dataRef + dataRef[][][] = sweepRef[p] + str = "data(cursors(A,B),channels(AD),[" + num2istr(sweepNo) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + Make/FREE/N=(DimSize(sweepRef, ROWS), 1, 1) dataRef + dataRef[][][] = sweepRef[p] + str = "data(cursors(A,B),channels(AD6),[" + num2istr(sweepNo) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + Make/FREE/N=(rangeEnd0 - rangeStart0 + 1, 1, numChannels / 2) dataRef + dataRef[][][] = sweepRef[p + rangeStart0] + str = "data(TestEpoch,channels(AD),[" + num2istr(sweepNo) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + Make/FREE/N=(rangeEnd1 - rangeStart1 + 1, 1, numChannels / 2) dataRef + dataRef[][][] = sweepRef[p + rangeStart1] + str = "data(TestEpoch,channels(AD),[" + num2istr(sweepNo + 1) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + rStart = min(rangeStart0, rangeStart1) + Make/FREE/N=(max(rangeEnd0, rangeEnd1) - rStart + 1, 2, numChannels / 2) dataRef = NaN + dataRef[rangeStart0 - rStart, rangeEnd0 - rStart][0][] = sweepRef[p + rStart] + dataRef[rangeStart1 - rStart, rangeEnd1 - rStart][1][] = sweepRef[p + rStart] + SetDimLabel COLS, 0, sweep0, dataRef + SetDimLabel COLS, 1, sweep1, dataRef + SetDimLabel LAYERS, 0, AD6, dataRef + SetDimLabel LAYERS, 1, AD7, dataRef + str = "data(TestEpoch,channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_LABELS) + + // use a 3-dim range specification + str = "[[[" + num2istr(rangeStart0) + "," + num2istr(rangeStart0) + "],[" + num2istr(rangeStart1) + "," + num2istr(rangeStart1) + "]]," + str = str + "[[" + num2istr(rangeEnd0) + "," + num2istr(rangeEnd0) + "],[" + num2istr(rangeEnd1) + "," + num2istr(rangeEnd1) + "]]]" + str = "data(" + str + ",channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + // FAIL Tests + WAVE dataRef = MIES_SF#SF_GetDefaultEmptyWave() + + // non existing channel + str = "data(TestEpoch,channels(AD4),[" + num2istr(sweepNo) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + // non existing sweep + str = "data(TestEpoch,channels(AD4),[" + num2istr(sweepNo + 2) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + // range begin + str = "data([12, 10],channels(AD),[" + num2istr(sweepNo) + "])" + try + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + + // range end + str = "data([0, 11],channels(AD),[" + num2istr(sweepNo) + "])" + try + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + + // One sweep does not exist + Make/FREE/N=(DimSize(sweepRef, ROWS), 2, numChannels / 2) dataRef = NaN + dataRef[][0][] = sweepRef[p] + str = "data(cursors(A,B),channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 2) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) +End + Function TestLabNotebook() Variable i, j, sweepNumber, channelNumber String str, trace, key, name, epochStr @@ -1177,9 +1298,6 @@ Function TestLabNotebook() Make/U/I/N=(numChannels) connections = {7,5,3,1,0} Make/U/I/N=(numSweeps, numChannels) channels = q * 2 Make/D/FREE/N=(LABNOTEBOOK_LAYER_COUNT) values = NaN - epochStr = "0,0." + num2istr(datasize - 1) + ",ShortName=TestEpoch,0" - Make/FREE/T/N=(1, 1, LABNOTEBOOK_LAYER_COUNT) epochInfo = epochStr - Make/FREE/T/N=(1, 1) epochKeys = EPOCHS_ENTRY_KEY Make/FREE/T/N=(1, 1) dacKeys = "DAC" Make/FREE/N=(dataSize, numSweeps, numChannels) input = q + p^r // + gnoise(1) @@ -1202,8 +1320,6 @@ Function TestLabNotebook() ED_AddEntriesToLabnotebook(values, dacKeys, sweepNumber, device, mode) Redimension/N=(LABNOTEBOOK_LAYER_COUNT)/E=1 values ED_AddEntryToLabnotebook(device, keys[0], values, overrideSweepNo = sweepNumber) - - ED_AddEntriesToLabnotebook(epochInfo, epochKeys, sweepNumber, device, mode) endfor ModifyGraph/W=$win log(left)=1 @@ -1214,19 +1330,6 @@ Function TestLabNotebook() str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + channelTypeC + ",channels(AD),sweeps(),UNKNOWN_MODE)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) - - str = "data(cursors(A,B),channels(AD),sweeps())" - WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) - REQUIRE_EQUAL_WAVES(input, data, mode = WAVE_DATA) - - str = "data(TestEpoch,channels(AD),sweeps())" - WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) - REQUIRE_EQUAL_WAVES(input, data, mode = WAVE_DATA) - - str = "data(TestEpoch,channels(AD4),sweeps())" - WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) - Duplicate/FREE/RMD=[][][2] input, singleChannelData - REQUIRE_EQUAL_WAVES(singleChannelData, data, mode = WAVE_DATA) End /// @brief Test Epoch operation of SweepFormula From abe808f5e2c0420ab46eccdee83143ae240ec626 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Fri, 4 Feb 2022 18:49:45 +0100 Subject: [PATCH 04/49] SF: Add select operation to select sweeps for data, adapted data operation Add a new formula select(array channels, array sweeps, [string mode]) were mode accepts displayed or all and defaults to displayed. select returns a n x 3 wave with sweepNr, channelType, channelNr in the columns The row counts the sweeps. Adapted data to data(array range, array selectData). data returns the same data format as before. Developer note: The function SF_ReCreateOldSweepsChannelLayout was added to convert from input data from the select wave to suitable data for the old data format e.g. for data [sweepData][sweepNr][channelTypeNumber]. This conversion reduces the freedom of the original selectData wave such that all possible channelTypes, channelNumbers are combined in a NxM wave, whereas in the original input specific combinations might have been skipped. This will be resolved in the next iteration of SF. --- Packages/MIES/MIES_SweepFormula.ipf | 421 +++++++++++++++++++++++++++- 1 file changed, 407 insertions(+), 14 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 76dccc4684..2ce9df8188 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -85,6 +85,7 @@ static StrConstant SF_OP_FINDLEVEL = "findlevel" static StrConstant SF_OP_EPOCHS = "epochs" static StrConstant SF_OP_TP = "tp" static StrConstant SF_OP_STORE = "store" +static StrConstant SF_OP_SELECT = "select" static StrConstant SF_OP_EPOCHS_TYPE_RANGE = "range" static StrConstant SF_OP_EPOCHS_TYPE_NAME = "name" @@ -764,6 +765,9 @@ Function/WAVE SF_FormulaExecutor(jsonID, [jsonPath, graph]) case SF_OP_STORE: WAVE out = SF_OperationStore(jsonId, jsonPath, graph) break + case SF_OP_SELECT: + WAVE out = SF_OperationSelect(jsonId, jsonPath, graph) + break default: SF_ASSERT(0, "Undefined Operation", jsonId=jsonId) endswitch @@ -1054,7 +1058,322 @@ static Function SF_GetDAChannel(string graph, variable sweep, variable channelTy return NaN End -static Function/WAVE SF_GetSweepForFormula(string graph, WAVE range, WAVE channels, WAVE sweeps) +static Function/WAVE SF_GetSelectDataForAll(string graph, WAVE activeChannels, WAVE sweeps) + + variable i, j, numSweeps, sweepNo, cIndex, index + variable channelNr, channelType, numChannels, isSweepBrowser + string device, dataFolder + + ASSERT(WindowExists(graph), "graph window does not exist") + SF_ASSERT(DimSize(activeChannels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") + SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") + numSweeps = DimSize(sweeps, ROWS) + numChannels = DimSize(activeChannels, ROWS) + if(!numSweeps || !numChannels) + return $"" + endif + + isSweepBrowser = BSP_IsSweepBrowser(graph) + + if(isSweepBrowser) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + else + SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") + device = BSP_GetDevice(graph) + DFREF deviceDFR = GetDeviceDataPath(device) + endif + + WAVE selectData = SF_NewSelectDataWave(numSweeps, numChannels) + + for(i = 0; i < numSweeps; i += 1) + + sweepNo = sweeps[i] + + if(isSweepBrowser) + cIndex = FindDimLabel(sweepMap, COLS, "Sweep") + FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap + if(V_value == -1) + continue + endif + dataFolder = sweepMap[V_row][%DataFolder] + device = sweepMap[V_row][%Device] + DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) + endif + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + + for(j = 0; j < numChannels; j += 1) + + channelNr = activeChannels[j][%channelNumber] + channelType = activeChannels[j][%channelType] + WAVE/Z sweep = GetDAQDataSingleColumnWave(sweepDFR, channelType, channelNr) + if(!WaveExists(sweep)) + continue + endif + + selectData[index][%SWEEP] = sweepNo + selectData[index][%CHANNELTYPE] = channelType + selectData[index][%CHANNELNUMBER] = channelNr + index += 1 + endfor + endfor + if(!index) + return $"" + endif + Redimension/N=(index, -1) selectData + + return selectData +End + +static Function/WAVE SF_GetSelectDataForDisplayed(string graph, WAVE activeChannels, WAVE sweeps) + + variable i, j, index + variable numSweeps, numChannels + variable sweepNo, channelNr + variable channelType = -1 + + ASSERT(WindowExists(graph), "graph window does not exist") + SF_ASSERT(DimSize(activeChannels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") + SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") + + numSweeps = DimSize(sweeps, ROWS) + numChannels = DimSize(activeChannels, ROWS) + if(!numSweeps || !numChannels) + return $"" + endif + + WAVE/T/Z traces = GetTraceInfos(graph) + if(!WaveExists(traces)) + DebugPrint("No traces found for extracting sweep wave locations.") + return $"" + endif + SortColumns/A/DIML/KNDX={FindDimLabel(traces, COLS, "channelType"), FindDimLabel(traces, COLS, "channelNumber"), FindDimLabel(traces, COLS, "sweepNumber")} sortWaves=traces + + WAVE selectData = SF_NewSelectDataWave(numSweeps, numChannels) + + for(i = 0; i < numSweeps; i += 1) + + sweepNo = sweeps[i] + WAVE/Z sweepListIndex = FindIndizes(traces, colLabel = "sweepNumber", var = sweepNo) + if(!WaveExists(sweepListIndex)) + continue + endif + + for(j = 0; j < numChannels; j += 1) + + if(channelType != activeChannels[j][%channelType]) + channelType = activeChannels[j][%channelType] + WAVE/Z channelTypeIndex = FindIndizes(traces, colLabel = "channelType", str = StringFromList(channelType, XOP_CHANNEL_NAMES)) + endif + if(!WaveExists(channelTypeIndex)) + continue + endif + + channelNr = activeChannels[j][%channelNumber] + WAVE/Z channelNumberIndex = FindIndizes(traces, colLabel = "channelNumber", var = channelNr) + if(!WaveExists(channelNumberIndex)) + continue + endif + + // find matching index in @c traces wave + Concatenate/FREE {sweepListIndex, channelTypeIndex, channelNumberIndex}, indices + Redimension/N=(numpnts(indices))/E=1 indices + Sort indices, indices + Extract/FREE indices, matches, (p > 1 && (indices[p] == indices[p - 1]) && (indices[p] == indices[p - 2])) + WaveClear indices + if(DimSize(matches, ROWS) == 0) + continue + endif + SF_ASSERT(DimSize(matches, ROWS) == 1, "More than one matching sweep for this sweepNumber, channelType, and channelNumber combination.") + WaveClear matches + + selectData[index][%SWEEP] = sweepNo + selectData[index][%CHANNELTYPE] = channelType + selectData[index][%CHANNELNUMBER] = channelNr + index += 1 + endfor + endfor + if(!index) + return $"" + endif + Redimension/N=(index, -1) selectData + + return selectData +End + +static Function/WAVE SF_GetSweepsForFormula(string graph, WAVE range, WAVE selectData) + + variable i, j, rangeStart, rangeEnd, pOffset, delta, numRows, DAChannel, numSweeps, sweepNo + variable chanNr, chanType, numChannels, indexOffset, iStart, iLength, cIndex, isSweepBrowser + variable dimPos, numChannelTypes, numChannelNumbers, index + string dimLabel, device, dataFolder + + ASSERT(WindowExists(graph), "graph window does not exist") + if(IsTextWave(range)) + SF_ASSERT(DimSize(range, ROWS) == 1, "A epoch range must be a single string with the epoch name.") + WAVE/T wEpochName = range + else + SF_ASSERT(DimSize(range, ROWS) == 2, "A numerical range is must have two rows for range start and end.") + endif + if(SF_IsDefaultEmptyWave(selectData)) + return $"" + endif + SF_ASSERT(DimSize(selectData, COLS) == 3, "Select data must have 3 columns.") + + WAVE/Z sweeps + WAVE/Z activeChannels + [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) + + numSweeps = DimSize(sweeps, ROWS) + numChannels = DimSize(activeChannels, ROWS) + + if(DimSize(range, COLS) > 0) + SF_ASSERT(DimSize(range, COLS) == numSweeps, "Multidimensional range column size must equal number of sweeps.") + SF_ASSERT(DimSize(range, LAYERS) == numChannels, "Multidimensional range layers size must equal number of channels.") + endif + + isSweepBrowser = BSP_IsSweepBrowser(graph) + + if(isSweepBrowser) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + else + SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") + device = BSP_GetDevice(graph) + DFREF deviceDFR = GetDeviceDataPath(device) + endif + + Make/FREE/WAVE/N=(numSweeps, numChannels) sweepRefs + Make/FREE/U/I/N=(numSweeps, numChannels) indexStart, indexEnd + + for(i = 0; i < numSweeps; i += 1) + + sweepNo = sweeps[i] + + if(isSweepBrowser) + cIndex = FindDimLabel(sweepMap, COLS, "Sweep") + FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap + if(V_value == -1) + continue + endif + dataFolder = sweepMap[V_row][%DataFolder] + device = sweepMap[V_row][%Device] + DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) + endif + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + + for(j = 0; j < numChannels; j += 1) + + chanNr = activeChannels[j][%channelNumber] + chanType = activeChannels[j][%channelType] + WAVE/Z sweep = GetDAQDataSingleColumnWave(sweepDFR, chanType, chanNr) + if(!WaveExists(sweep)) + continue + endif + + if(delta == 0) + delta = DimDelta(sweep, ROWS) + else + SF_ASSERT(delta == DimDelta(sweep, ROWS), "Sweeps have not the same delta.") + endif + + if(WaveExists(wEpochName)) + DAChannel = SF_GetDAChannel(graph, sweepNo, chanType, chanNr) + WAVE range = SF_GetRangeFromEpoch(graph, wEpochName[0], sweepNo, DAChannel) + endif + + if(DimSize(range, COLS) > 0) + rangeStart = range[0][i][j] + rangeEnd = range[1][i][j] + else + rangeStart = range[0] + rangeEnd = range[1] + endif + SF_ASSERT(!IsNaN(rangeStart) && !IsNaN(rangeEnd), "Specified range not valid.") + SF_ASSERT(rangeStart == -inf || (IsFinite(rangeStart) && rangeStart >= leftx(sweep) && rangeStart < rightx(sweep)), "Specified starting range not inside sweep " + num2istr(sweepNo) + ".") + SF_ASSERT(rangeEnd == inf || (IsFinite(rangeEnd) && rangeEnd >= leftx(sweep) && rangeEnd < rightx(sweep)), "Specified ending range not inside sweep " + num2istr(sweepNo) + ".") + + indexStart[i][j] = ScaleToIndexWrapper(sweep, rangeStart, ROWS) + indexEnd[i][j] = ScaleToIndexWrapper(sweep, rangeEnd, ROWS) + sweepRefs[i][j] = sweep + endfor + endfor + + indexOffset = WaveMin(indexStart) + numRows = WaveMax(indexEnd) + 1 - indexOffset + + // combine sweeps to data wave + Make/FREE/N=(numRows, numSweeps, numChannels) sweepData = NaN + SetScale/P x, indexOffset * delta, delta, sweepData + for(i = 0; i < numSweeps; i += 1) + for(j = 0; j < numChannels; j += 1) + + WAVE/Z sweep = sweepRefs[i][j] + if(!WaveExists(sweep)) + continue + endif + + iStart = indexStart[i][j] + iLength = indexEnd[i][j] - iStart + pOffset = iStart - indexOffset + MultiThread sweepData[pOffset, pOffset + iLength][i][j] = sweep[iStart + p - pOffset] + + if(i == 0) + dimLabel = StringFromList(activeChannels[j][%channelType], XOP_CHANNEL_NAMES) + num2istr(activeChannels[j][%channelNumber]) + SetDimLabel LAYERS, j, $dimLabel, sweepData + endif + endfor + + sprintf dimLabel, "sweep%d", sweeps[i] + SetDimLabel COLS, i, $dimLabel, sweepData + endfor + + return sweepData +End + +/// @brief Returns the Unique numeric entries as free 1d wave from a column of a 2d wave, where the column is identified by its dimension label +static Function/WAVE SF_GetReducedColumn(WAVE w, string dimLabel) + + variable dimPos + + dimPos = FindDimLabel(w, COLS, dimLabel) + SF_ASSERT(dimPos >= 0, "Columns with dimLabel " + dimlabel + " not found.") + Duplicate/FREE/RMD=[][dimPos] w, wTmp + Redimension/N=(-1) wTmp + + WAVE wReduced = GetUniqueEntries(wTmp) + + return wReduced +End + +/// @brief Converts from a 1d wave of selected sweeps/channel type/channel number to a 2d wave that +/// holds the old channel layout where all selected channel type + channel number combinations appear exactly once +/// independent of the sweep number. +/// Converts also from a 1d wave of selected sweeps/channel type/channel to a 1d wave that +/// holds the old sweeps layout, where all selected sweep numbers appear exactly once. +/// Note: The association between sweep and channel type + channel number in the original selectData is lost by this back conversion. +static Function [WAVE sweeps, WAVE/D channels] SF_ReCreateOldSweepsChannelLayout(WAVE selectData) + + variable numSelected, numCombined + variable shift = ceil(log(NUM_AD_CHANNELS) / log(2)) + + WAVE sweepsReduced = SF_GetReducedColumn(selectData, "SWEEP") + + numSelected = DimSize(selectData, ROWS) + Make/FREE/D/N=(numSelected) combined = selectData[p][%CHANNELTYPE] << shift + selectData[p][%CHANNELNUMBER] + WAVE combReduced = GetUniqueEntries(combined) + + numCombined = DimSize(combReduced, ROWS) + WAVE channels = SF_NewChannelsWave(numCombined) + if(numCombined) + channels[][%channelType] = combReduced[p] >> shift + channels[][%channelNumber] = combReduced[p] - channels[p][%channelType] << shift + endif + + return [sweepsReduced, channels] +End + +static Function/WAVE SF_GetSweepForFormulaOld(string graph, WAVE range, WAVE channels, WAVE sweeps) variable i, j, rangeStart, rangeEnd, pOffset, delta, numRows, DAChannel, numSweeps, sweepNo variable chanNr, chanType, numChannels, indexOffset, iStart, iLength, cIndex, isSweepBrowser @@ -1758,7 +2077,7 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra for(j = 0; j < activeChannelCnt; j += 1) singleChannel[0][] = activeChannels[j][q] - WAVE/Z sweepData = SF_GetSweepForFormula(graph, {-Inf, Inf}, singleChannel, {sweep}) + WAVE/Z sweepData = SF_GetSweepForFormulaOld(graph, {-Inf, Inf}, singleChannel, {sweep}) if(!WaveExists(sweepData)) continue endif @@ -2404,12 +2723,60 @@ static Function/WAVE SF_OperationSweeps(variable jsonId, string jsonPath, string return out End -/// `data(array range,array channels,array sweeps)` +/// `select(array channels, array sweeps, [string mode])` /// -/// returns [[sweeps][channel]] for all [sweeps] in list sweepNumbers, grouped by channels +/// returns n x 3 with columns [sweepNr][channelType][channelNr] +static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string graph) + + variable numIndices + string mode + + numIndices = JSON_GetArraySize(jsonID, jsonPath) + + SF_ASSERT(numIndices >= 2 && numIndices <= 3, "Function requires 2 or 3 arguments.") + SF_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + WAVE channels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) + SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") + + WAVE sweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") + + if(numIndices == 3) + WAVE/T wMode = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) + SF_ASSERT(IsTextWave(wMode), "mode parameter can not be a number. Use \"all\" or \"displayed\".") + SF_ASSERT(!DimSize(wMode, COLS) && DimSize(wMode, ROWS) == 1, "mode must be either displayed or all.") + mode = wMode[0] + else + mode = "displayed" + endif + + WAVE activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) + + if(!CmpStr(mode, "all")) + WAVE/Z out = SF_GetSelectDataForAll(graph, activeChannels, sweeps) + else + WAVE/Z out = SF_GetSelectDataForDisplayed(graph, activeChannels, sweeps) + endif + + if(!WaveExists(out)) + DebugPrint("Call to SF_GetSweepNumbersForSelect returned no results") + WAVE out = SF_GetDefaultEmptyWave() + endif + + return out +End + +/// `data(array range, array selectData)` +/// +/// returns [sweepData][sweeps][channelTypeNumber] for all sweeps selected by selectData static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string graph) + variable numIndices + + numIndices = JSON_GetArraySize(jsonID, jsonPath) + SF_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") + SF_ASSERT(numIndices == 2, "Function requires 2 arguments.") WAVE range = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) if(IsTextWave(range)) @@ -2419,18 +2786,17 @@ static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string g range[][][] = !IsNaN(range[p][q][r]) ? range[p][q][r] : (p == 0 ? -1 : 1) * inf endif - WAVE channels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1") - SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - - WAVE sweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - - WAVE activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + if(SF_IsDefaultEmptyWave(selectData)) + return selectData + endif + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") - WAVE/Z out = SF_GetSweepForFormula(graph, range, activeChannels, sweeps) + WAVE/Z out = SF_GetSweepsForFormula(graph, range, selectData) if(!WaveExists(out)) - DebugPrint("Call to SF_GetSweepForFormula returned no results") - Make/FREE/N=1 out = {NaN} + DebugPrint("Call to SF_GetSweepsForFormula returned no results") + WAVE out = SF_GetDefaultEmptyWave() endif return out @@ -2761,6 +3127,21 @@ static Function/WAVE SF_NewChannelsWave(variable size) return out End +/// @brief Create a new selectData wave +/// The row counts the selected combinations of sweep, channel type, channel number +/// The three columns per row store the sweep number, channel type, channel number +static Function/WAVE SF_NewSelectDataWave(variable numSweeps, variable numChannels) + + ASSERT(numSweeps >= 0 && numChannels >= 0, "Invalid wave size specified") + + Make/FREE/D/N=(numSweeps * numChannels, 3) selectData + SetDimLabel COLS, 0, SWEEP, selectData + SetDimLabel COLS, 1, CHANNELTYPE, selectData + SetDimLabel COLS, 2, CHANNELNUMBER, selectData + + return selectData +End + static Function/WAVE SF_GetDefaultEmptyWave() Make/FREE/D/N=1 out = NaN @@ -2768,6 +3149,18 @@ static Function/WAVE SF_GetDefaultEmptyWave() return out End +/// @brief Returns a wave that is the default for SweepFormula for "no return value" +/// The wave is numeric, consists of one element that is {NaN}. +/// This effectively represent a JSON_NULL: {null} +static Function SF_IsDefaultEmptyWave(WAVE w) + + if(IsNumericWave(w) && DimSize(w, ROWS) == 1 && !DimSize(w, COLS)) + return IsNaN(w[0]) + endif + + return 0 +End + static Function/WAVE SF_AverageTPFromSweep(WAVE/T epochMatches, WAVE sweepData) variable numTPEpochs, tpDataSizeMin, tpDataSizeMax, sweepDelta From 77d89a13711f1ae9199e3d780b31a97e5bcc2b31 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 17:16:56 +0200 Subject: [PATCH 05/49] SF: Adapt tp operation to use select instead of sweeps and channels Change of tp operation to: tp(string type, array selectData, [array ignoreTPs]) output format of tp operation did not change --- .../MIES/MIES_AnalysisFunctions_PatchSeq.ipf | 10 +- Packages/MIES/MIES_SweepFormula.ipf | 164 +++--------------- 2 files changed, 27 insertions(+), 147 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf index 6858ee6df2..bebac5ffd4 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf @@ -4465,7 +4465,7 @@ Function PSQ_PipetteInBath(string device, struct AnalysisFunction_V3& s) /// @todo: Rework to use non-displayed sweeps, once https://github.com/AllenInstitute/MIES/pull/1256 is merged /// this also then allows us to remove the OVS fiddling - ReplaceNotebookText(formula_nb, "store(\"Steady state resistance\", tp(ss, channels(AD), sweeps(), [0]))") + ReplaceNotebookText(formula_nb, "store(\"Steady state resistance\", tp(ss, select(channels(AD), sweeps()), [0]))") PGC_SetAndActivateControl(bsPanel, "check_BrowserSettings_SF", val = 1) @@ -5068,15 +5068,15 @@ Function PSQ_SealEvaluation(string device, struct AnalysisFunction_V3& s) // and `tp` takes the *ignored* list switch(testpulseGroupSel) case PSQ_SE_TGS_BOTH: - formula = "store(\"Steady state resistance (group A)\", tp(ss, channels(AD), sweeps(), [0, 4, 5, 6]))\r" + \ + formula = "store(\"Steady state resistance (group A)\", tp(ss, select(channels(AD), sweeps()), [0, 4, 5, 6]))\r" + \ "and\r" + \ - "store(\"Steady state resistance (group B)\", tp(ss, channels(AD), sweeps(), [0, 1, 2, 3]))" + "store(\"Steady state resistance (group B)\", tp(ss, select(channels(AD), sweeps()), [0, 1, 2, 3]))" break case PSQ_SE_TGS_FIRST: - formula = "store(\"Steady state resistance (group A)\", tp(ss, channels(AD), sweeps(), [0]))" + formula = "store(\"Steady state resistance (group A)\", tp(ss, select(channels(AD), sweeps()), [0]))" break case PSQ_SE_TGS_SECOND: - formula = "store(\"Steady state resistance (group B)\", tp(ss, channels(AD), sweeps(), [0]))" + formula = "store(\"Steady state resistance (group B)\", tp(ss, select(channels(AD), sweeps()), [0]))" break default: ASSERT(0, "Invalid testpulseGroupSel: " + num2str(testpulseGroupSel)) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 2ce9df8188..bb97a771ee 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1373,132 +1373,6 @@ static Function [WAVE sweeps, WAVE/D channels] SF_ReCreateOldSweepsChannelLayout return [sweepsReduced, channels] End -static Function/WAVE SF_GetSweepForFormulaOld(string graph, WAVE range, WAVE channels, WAVE sweeps) - - variable i, j, rangeStart, rangeEnd, pOffset, delta, numRows, DAChannel, numSweeps, sweepNo - variable chanNr, chanType, numChannels, indexOffset, iStart, iLength, cIndex, isSweepBrowser - string dimLabel, device, dataFolder - - ASSERT(WindowExists(graph), "graph window does not exist") - if(IsTextWave(range)) - SF_ASSERT(DimSize(range, ROWS) == 1, "A epoch range must be a single string with the epoch name.") - WAVE/T wEpochName = range - else - SF_ASSERT(DimSize(range, ROWS) == 2, "A numerical range is must have two rows for range start and end.") - endif - SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - numSweeps = DimSize(sweeps, ROWS) - numChannels = DimSize(channels, ROWS) - if(!numSweeps || !numChannels) - WAVE wv = SF_GetDefaultEmptyWave() - return wv - endif - - if(DimSize(range, COLS) > 0) - SF_ASSERT(DimSize(range, COLS) == numSweeps, "Multidimensional range column size must equal number of sweeps.") - SF_ASSERT(DimSize(range, LAYERS) == numChannels, "Multidimensional range layers size must equal number of channels.") - endif - - isSweepBrowser = BSP_IsSweepBrowser(graph) - - if(isSweepBrowser) - DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) - WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) - else - SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") - device = BSP_GetDevice(graph) - DFREF deviceDFR = GetDeviceDataPath(device) - endif - - Make/FREE/WAVE/N=(numSweeps, numChannels) sweepRefs - Make/FREE/U/I/N=(numSweeps, numChannels) indexStart, indexEnd - - for(i = 0; i < numSweeps; i += 1) - - sweepNo = sweeps[i] - - if(isSweepBrowser) - cIndex = FindDimLabel(sweepMap, COLS, "Sweep") - FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap - if(V_value == -1) - continue - endif - dataFolder = sweepMap[V_row][%DataFolder] - device = sweepMap[V_row][%Device] - DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) - endif - DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) - - for(j = 0; j < numChannels; j += 1) - - chanNr = channels[j][%channelNumber] - chanType = channels[j][%channelType] - WAVE/Z sweep = GetDAQDataSingleColumnWave(sweepDFR, chanType, chanNr) - if(!WaveExists(sweep)) - continue - endif - - if(delta == 0) - delta = DimDelta(sweep, ROWS) - else - SF_ASSERT(delta == DimDelta(sweep, ROWS), "Sweeps have not the same delta.") - endif - - if(WaveExists(wEpochName)) - DAChannel = SF_GetDAChannel(graph, sweepNo, chanType, chanNr) - WAVE range = SF_GetRangeFromEpoch(graph, wEpochName[0], sweepNo, DAChannel) - endif - - if(DimSize(range, COLS) > 0) - rangeStart = range[0][i][j] - rangeEnd = range[1][i][j] - else - rangeStart = range[0] - rangeEnd = range[1] - endif - SF_ASSERT(!IsNaN(rangeStart) && !IsNaN(rangeEnd), "Specified range not valid.") - SF_ASSERT(rangeStart == -inf || (IsFinite(rangeStart) && rangeStart >= leftx(sweep) && rangeStart < rightx(sweep)), "Specified starting range not inside sweep " + num2istr(sweepNo) + ".") - SF_ASSERT(rangeEnd == inf || (IsFinite(rangeEnd) && rangeEnd >= leftx(sweep) && rangeEnd < rightx(sweep)), "Specified ending range not inside sweep " + num2istr(sweepNo) + ".") - - indexStart[i][j] = ScaleToIndexWrapper(sweep, rangeStart, ROWS) - indexEnd[i][j] = ScaleToIndexWrapper(sweep, rangeEnd, ROWS) - sweepRefs[i][j] = sweep - endfor - endfor - - indexOffset = WaveMin(indexStart) - numRows = WaveMax(indexEnd) + 1 - indexOffset - - // combine sweeps to data wave - Make/FREE/N=(numRows, numSweeps, numChannels) sweepData = NaN - SetScale/P x, indexOffset * delta, delta, sweepData - for(i = 0; i < numSweeps; i += 1) - for(j = 0; j < numChannels; j += 1) - - WAVE/Z sweep = sweepRefs[i][j] - if(!WaveExists(sweep)) - continue - endif - - iStart = indexStart[i][j] - iLength = indexEnd[i][j] - iStart - pOffset = iStart - indexOffset - MultiThread sweepData[pOffset, pOffset + iLength][i][j] = sweep[iStart + p - pOffset] - - if(i == 0) - dimLabel = StringFromList(channels[j][%channelType], XOP_CHANNEL_NAMES) + num2istr(channels[j][%channelNumber]) - SetDimLabel LAYERS, j, $dimLabel, sweepData - endif - endfor - - sprintf dimLabel, "sweep%d", sweeps[i] - SetDimLabel COLS, i, $dimLabel, sweepData - endfor - - return sweepData -End - /// @brief transfer the wave scaling from one wave to another /// /// Note: wave scale transfer requires wave units for the first wave in the array that @@ -1998,6 +1872,8 @@ static Function/WAVE SF_FilterEpochs(WAVE/Z epochs, WAVE/Z ignoreTPs) return epochs End +// tp(string type, array selectData, [array ignoreTPs]) +// returns 3D wave in the layout: result x sweeps x channels static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string graph) variable numArgs, sweepCnt, activeChannelCnt, i, j, channelNr, channelType, dacChannelNr @@ -2008,10 +1884,8 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra string baselineUnit = "" STRUCT TPAnalysisInput tpInput - // tp(string type, array channels, array sweeps, [array ignoreTPs]) - // returns 3D wave in the layout: result x sweeps x channels numArgs = JSON_GetArraySize(jsonID, jsonPath) - SF_ASSERT(numArgs == 3 || numArgs == 4, "tp requires 3 or 4 arguments") + SF_ASSERT(numArgs == 2 || numArgs == 3, "tp requires 2 or 3 arguments") WAVE wType = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) SF_ASSERT(DimSize(wType, ROWS) == 1, "Too many input values for parameter name") @@ -2034,23 +1908,23 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra outType = wType[0] endif - WAVE channels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - SF_ASSERT(IsNumericWave(channels), "channels parameter must be numeric") - - WAVE sweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) - SF_ASSERT(DimSize(sweeps, COLS) < 2, "sweeps must be one-dimensional.") - SF_ASSERT(IsNumericWave(sweeps), "sweeps parameter must be numeric") + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + SF_ASSERT(!SF_IsDefaultEmptyWave(selectData), "No valid sweep/channels combination found.") + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") - if(numArgs == 4) - WAVE ignoreTPs = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/3", graph = graph) + if(numArgs == 3) + WAVE ignoreTPs = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) SF_ASSERT(DimSize(ignoreTPs, COLS) < 2, "ignoreTPs must be one-dimensional.") SF_ASSERT(IsNumericWave(ignoreTPs), "ignoreTPs parameter must be numeric") else WAVE/Z ignoreTPs endif - WAVE activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) + WAVE/Z activeChannels + WAVE/Z sweeps + [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) + sweepCnt = DimSize(sweeps, ROWS) activeChannelCnt = DimSize(activeChannels, ROWS) SF_ASSERT(sweepCnt > 0, "Could not find sweeps from given specification.") @@ -2058,7 +1932,10 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra Make/FREE/D/N=(1, sweepCnt, activeChannelCnt) out = NaN emptyOutput = 1 - WAVE singleChannel = SF_NewChannelsWave(1) + + Duplicate/FREE selectData, singleSelect + Redimension/N=(1, -1) singleSelect + WAVE/Z settings for(i = 0; i < sweepCnt; i += 1) sweep = sweeps[i] @@ -2074,10 +1951,13 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra endif WAVE/Z keyValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_KEYS, sweepNumber=sweep) + singleSelect[0][%SWEEP] = sweep + for(j = 0; j < activeChannelCnt; j += 1) - singleChannel[0][] = activeChannels[j][q] - WAVE/Z sweepData = SF_GetSweepForFormulaOld(graph, {-Inf, Inf}, singleChannel, {sweep}) + singleSelect[0][%CHANNELTYPE] = activeChannels[j][%channelType] + singleSelect[0][%CHANNELNUMBER] = activeChannels[j][%channelNumber] + WAVE/Z sweepData = SF_GetSweepsForFormula(graph, {-Inf, Inf}, singleSelect) if(!WaveExists(sweepData)) continue endif From dc49f1d87a938a2823b2267256513fa529ced090 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 17:56:57 +0200 Subject: [PATCH 06/49] SF: Change labnotebook operation to use select instead of sweeps, channels Operation changes to: labnotebook(string key, array selectData [, string entrySourceType]) The output format does not change. --- Packages/MIES/MIES_SweepFormula.ipf | 43 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index bb97a771ee..a162b14f25 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2682,35 +2682,36 @@ static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string g return out End -/// `labnotebook(string key, array channels, array sweeps [, string entrySourceType])` +/// `labnotebook(string key, array selectData [, string entrySourceType])` /// /// return lab notebook @p key for all @p sweeps that belong to the channels @p channels static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, string graph) - variable numIndices, i, j, mode, JSONtype, index, sweepNo + variable numIndices, i, j, mode, JSONtype, index, sweepNo, numSweeps, numChannels string str SF_ASSERT(!IsEmpty(graph), "Graph not specified.") numIndices = JSON_GetArraySize(jsonID, jsonPath) - SF_ASSERT(numIndices <= 4, "Maximum number of arguments exceeded.") - SF_ASSERT(numIndices >= 3, "At least three arguments are required.") + SF_ASSERT(numIndices <= 3, "Maximum number of three arguments exceeded.") + SF_ASSERT(numIndices >= 2, "At least two arguments are required.") JSONtype = JSON_GetType(jsonID, jsonPath + "/0") SF_ASSERT(JSONtype == JSON_STRING, "first parameter needs to be a string labnotebook key") str = JSON_GetString(jsonID, jsonPath + "/0") - WAVE channels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - - WAVE sweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + if(SF_IsDefaultEmptyWave(selectData)) + return selectData + endif + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") mode = DATA_ACQUISITION_MODE - if(numIndices == 4) - JSONtype = JSON_GetType(jsonID, jsonPath + "/3") + if(numIndices == 3) + JSONtype = JSON_GetType(jsonID, jsonPath + "/2") SF_ASSERT(JSONtype == JSON_STRING, "Last parameter needs to be a string.") - strswitch(JSON_GetString(jsonID, jsonPath + "/3")) + strswitch(JSON_GetString(jsonID, jsonPath + "/2")) case "UNKNOWN_MODE": mode = UNKNOWN_MODE break @@ -2727,13 +2728,17 @@ static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, s endswitch endif - WAVE activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, mode) + WAVE/Z sweeps + WAVE/Z activeChannels + [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) + numSweeps = DimSize(sweeps, ROWS) + numChannels = DimSize(activeChannels, ROWS) WAVE/Z settings - Make/D/FREE/N=(DimSize(sweeps, ROWS), DimSize(activeChannels, ROWS)) outD = NaN - Make/T/FREE/N=(DimSize(sweeps, ROWS), DimSize(activeChannels, ROWS)) outT - for(i = 0; i < DimSize(sweeps, ROWS); i += 1) + Make/D/FREE/N=(numSweeps, numChannels) outD = NaN + Make/T/FREE/N=(numSweeps, numChannels) outT + for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] if(!IsValidSweepNumber(sweepNo)) @@ -2746,7 +2751,7 @@ static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, s continue endif - for(j = 0; j < DimSize(activeChannels, ROWS); j += 1) + for(j = 0; j < numChannels; j += 1) [settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweeps[i], str, activeChannels[j][%channelNumber], activeChannels[j][%channelType], mode) if(!WaveExists(settings)) continue @@ -2764,11 +2769,11 @@ static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, s if(!WaveExists(out)) DebugPrint("labnotebook entry not found.") - Make/FREE/N=1 out = {NaN} + WAVE out = SF_GetDefaultEmptyWave() return out endif - for(i = 0; i < DimSize(activeChannels, ROWS); i += 1) + for(i = 0; i < numChannels; i += 1) str = StringFromList(activeChannels[i][%channelType], XOP_CHANNEL_NAMES) + num2istr(activeChannels[i][%channelNumber]) SetDimLabel COLS, i, $str, out endfor From 2f99f82a0071f05de31efea970771263b503088a Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 18:17:35 +0200 Subject: [PATCH 07/49] SF: Changes epochs operation to use select instead of channels, sweeps Changed to: epochs(string shortName, array selectData, [string type]) The output format of epochs(...) is unchanged. --- Packages/MIES/MIES_SweepFormula.ipf | 36 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index a162b14f25..1a9dee8d10 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2078,31 +2078,30 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra return out End +// epochs(string shortName, array selectData, [string type]) +// returns 2xN wave for type = range except for a single range result static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string graph) variable numArgs, i, j, k, epType, sweepCnt, activeChannelCnt, outCnt, index, numEpochs, sweepNo string str, epName, epShortName - // epochs(string shortName, array channels, array sweeps, [string type]) - // returns 2xN wave for type = range except for a single range result numArgs = JSON_GetArraySize(jsonID, jsonPath) - SF_ASSERT(numArgs >= 3, "epochs requires at least 3 arguments") - SF_ASSERT(numArgs <= 4, "epochs requires at most 4 arguments") + SF_ASSERT(numArgs >= 2, "epochs requires at least 2 arguments") + SF_ASSERT(numArgs <= 3, "epochs requires at most 3 arguments") WAVE/T epochName = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) SF_ASSERT(DimSize(epochName, ROWS) == 1, "Too many input values for parameter name") SF_ASSERT(IsTextWave(epochName), "name parameter must be textual") - WAVE epochChannels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - SF_ASSERT(DimSize(epochChannels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - SF_ASSERT(IsNumericWave(epochChannels), "channels parameter must be numeric") - - WAVE epochSweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) - SF_ASSERT(DimSize(epochSweeps, COLS) < 2, "sweeps must be one-dimensional.") - SF_ASSERT(IsNumericWave(epochSweeps), "sweeps parameter must be numeric") + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + if(SF_IsDefaultEmptyWave(selectData)) + return selectData + endif + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") - if(numArgs == 4) - WAVE/T epochType = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/3", graph = graph) + if(numArgs == 3) + WAVE/T epochType = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) SF_ASSERT(DimSize(epochType, ROWS) == 1, "Too many input values for parameter type") SF_ASSERT(IsTextWave(epochType), "type parameter must be textual") strswitch(epochType[0]) @@ -2125,9 +2124,11 @@ static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string epType = EPOCHS_TYPE_RANGE endif - WAVE activeChannels = SF_GetActiveChannelNumbers(graph, epochChannels, epochSweeps, DATA_ACQUISITION_MODE) + WAVE/Z activeChannels + WAVE/Z sweeps + [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) - sweepCnt = DimSize(epochSweeps, ROWS) + sweepCnt = DimSize(sweeps, ROWS) activeChannelCnt = DimSize(activeChannels, ROWS) if(epType == EPOCHS_TYPE_NAME) @@ -2147,7 +2148,7 @@ static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string outCnt = 0 for(i = 0; i < sweepCnt; i += 1) - sweepNo = epochSweeps[i] + sweepNo = sweeps[i] if(!IsValidSweepNumber(sweepNo)) continue @@ -2190,8 +2191,7 @@ static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string if(outCnt == 1) Redimension/N=2 out elseif(outCnt == 0) - Redimension/N=1 out - out = NaN + WAVE out = SF_GetDefaultEmptyWave() else Redimension/N=(-1, outCnt) out endif From 67c83e12c6ea7d494b7085d50d20b63617255faa Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 21:28:53 +0200 Subject: [PATCH 08/49] SF tests: Adapt Epochs Tests for using select operation --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 6d3151bce6..0fa03cb964 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1397,60 +1397,60 @@ static Function TestOperationEpochs() ED_AddEntriesToLabnotebook(wEpochStr, keysEpochs, sweepNumber, device, mode) endfor - str = "epochs(\"E0_PT_P48\", channels(DA0), 0)" + str = "epochs(\"E0_PT_P48\", select(channels(DA0), 0))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D refData = {500, 510} REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", channels(DA4), 0)" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA4), 0))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D refData = {503, 510} REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", channels(DA4), 0, range)" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA4), 0), range)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D refData = {503, 510} REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", channels(DA4), 0, treelevel)" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA4),0), treelevel)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D refData = {3} REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", channels(DA4), 9, name)" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA4), 9), name)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/T refDataT = {"Epoch=0;Type=Pulse Train;Pulse=48;Baseline;ShortName=E0_PT_P48_B;"} REQUIRE_EQUAL_WAVES(data, refDataT, mode = WAVE_DATA) // works with wrong casing - str = "epochs(\"e0_pt_p48_B\", channels(DA4), 9, name)" + str = "epochs(\"e0_pt_p48_B\", select(channels(DA4), 9), name)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/T refDataT = {"Epoch=0;Type=Pulse Train;Pulse=48;Baseline;ShortName=E0_PT_P48_B;"} REQUIRE_EQUAL_WAVES(data, refDataT, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", channels(DA), sweeps())" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA), sweeps()))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D/N=(2, numSweeps * activeChannelsDA) refData refData = p ? 510 : 503 REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) // channel(s) with no epochs - str = "epochs(\"E0_PT_P48_B\", channels(AD), sweeps())" + str = "epochs(\"E0_PT_P48_B\", select(channels(AD), sweeps()))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) CHECK_EQUAL_WAVES({NaN}, data, mode = WAVE_DATA) // name that does not match any - str = "epochs(\"does_not_exist\", channels(DA), sweeps())" + str = "epochs(\"does_not_exist\", select(channels(DA), sweeps()))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D/N=(2, numSweeps * activeChannelsDA) refData = NaN CHECK_EQUAL_WAVES(refData, data) // invalid sweep - WAVE data = SF_FormulaExecutor(DirectToFormulaParser("epochs(\"E0_PT_P48_B\", channels(DA), -1)"), graph = win) + WAVE data = SF_FormulaExecutor(DirectToFormulaParser("epochs(\"E0_PT_P48_B\", select(channels(DA), -1))"), graph = win) CHECK_EQUAL_WAVES({NaN}, data, mode = WAVE_DATA) // invalid type - str = "epochs(\"E0_PT_P48_B\", channels(DA), sweeps(), invalid_type)" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA), sweeps()), invalid_type)" try WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win); AbortOnRTE FAIL() From af6967906b1b6d88ead5e9136761fa19a2f7d219 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 21:35:59 +0200 Subject: [PATCH 09/49] SF Tests: Adapt labnotebook operation tests using select(...) --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 0fa03cb964..e30feea9ee 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1323,11 +1323,11 @@ Function TestLabNotebook() endfor ModifyGraph/W=$win log(left)=1 - str = "labnotebook(" + channelTypeC + ",channels(AD),sweeps())" + str = "labnotebook(" + channelTypeC + ",select(channels(AD),sweeps()))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) - str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + channelTypeC + ",channels(AD),sweeps(),UNKNOWN_MODE)" + str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + channelTypeC + ",select(channels(AD),sweeps()),UNKNOWN_MODE)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) End From 31317dd6d879ede9ec85ca914d6e6d24857351b1 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 21:55:50 +0200 Subject: [PATCH 10/49] SF tests: Adapt test AvoidAssertingOutWithNoSweeps using select operation --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index e30feea9ee..e7952dd9f7 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1508,9 +1508,9 @@ End static Function/WAVE SweepFormulaFunctionsWithSweepsArgument() - Make/FREE/T wv = {"data(cursors(A,B), channels(AD), sweeps())", \ - "epochs(\"I DONT EXIST\", channels(DA), sweeps())", \ - "labnotebook(\"I DONT EXIST\", channels(DA), sweeps())"} + Make/FREE/T wv = {"data(cursors(A,B), select(channels(AD), sweeps()))", \ + "epochs(\"I DONT EXIST\", select(channels(DA), sweeps()))", \ + "labnotebook(\"I DONT EXIST\", select(channels(DA), sweeps()))"} SetDimensionLabels(wv, "data;epochs;labnotebook", ROWS) From 26689a1859bac13b9c82d1280d88742553d63f97 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 5 Apr 2022 22:16:35 +0200 Subject: [PATCH 11/49] SF Tests: Adapt data operation tests using select The last test for requesting more sweeps than there are existing was adapted due to select returning only existing sweeps. For this very special case this a functionality change of data that uses now select comapred to data with channels, sweeps specified. However returning empty sweeps before for non-existing sweeps was inconvenient and could be considered as wrong. --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index e7952dd9f7..e239a6b4f5 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1186,25 +1186,25 @@ static Function TestDataOperation() Make/FREE/N=(DimSize(sweepRef, ROWS), 1, numChannels / 2) dataRef dataRef[][][] = sweepRef[p] - str = "data(cursors(A,B),channels(AD),[" + num2istr(sweepNo) + "])" + str = "data(cursors(A,B),select(channels(AD),[" + num2istr(sweepNo) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) Make/FREE/N=(DimSize(sweepRef, ROWS), 1, 1) dataRef dataRef[][][] = sweepRef[p] - str = "data(cursors(A,B),channels(AD6),[" + num2istr(sweepNo) + "])" + str = "data(cursors(A,B),select(channels(AD6),[" + num2istr(sweepNo) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) Make/FREE/N=(rangeEnd0 - rangeStart0 + 1, 1, numChannels / 2) dataRef dataRef[][][] = sweepRef[p + rangeStart0] - str = "data(TestEpoch,channels(AD),[" + num2istr(sweepNo) + "])" + str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) Make/FREE/N=(rangeEnd1 - rangeStart1 + 1, 1, numChannels / 2) dataRef dataRef[][][] = sweepRef[p + rangeStart1] - str = "data(TestEpoch,channels(AD),[" + num2istr(sweepNo + 1) + "])" + str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo + 1) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) @@ -1216,14 +1216,14 @@ static Function TestDataOperation() SetDimLabel COLS, 1, sweep1, dataRef SetDimLabel LAYERS, 0, AD6, dataRef SetDimLabel LAYERS, 1, AD7, dataRef - str = "data(TestEpoch,channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "])" + str = "data(TestEpoch,select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_LABELS) // use a 3-dim range specification str = "[[[" + num2istr(rangeStart0) + "," + num2istr(rangeStart0) + "],[" + num2istr(rangeStart1) + "," + num2istr(rangeStart1) + "]]," str = str + "[[" + num2istr(rangeEnd0) + "," + num2istr(rangeEnd0) + "],[" + num2istr(rangeEnd1) + "," + num2istr(rangeEnd1) + "]]]" - str = "data(" + str + ",channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "])" + str = "data(" + str + ",select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) @@ -1231,17 +1231,17 @@ static Function TestDataOperation() WAVE dataRef = MIES_SF#SF_GetDefaultEmptyWave() // non existing channel - str = "data(TestEpoch,channels(AD4),[" + num2istr(sweepNo) + "])" + str = "data(TestEpoch,select(channels(AD4),[" + num2istr(sweepNo) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) // non existing sweep - str = "data(TestEpoch,channels(AD4),[" + num2istr(sweepNo + 2) + "])" + str = "data(TestEpoch,select(channels(AD4),[" + num2istr(sweepNo + 2) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) // range begin - str = "data([12, 10],channels(AD),[" + num2istr(sweepNo) + "])" + str = "data([12, 10],select(channels(AD),[" + num2istr(sweepNo) + "],all))" try WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) FAIL() @@ -1250,7 +1250,7 @@ static Function TestDataOperation() endtry // range end - str = "data([0, 11],channels(AD),[" + num2istr(sweepNo) + "])" + str = "data([0, 11],select(channels(AD),[" + num2istr(sweepNo) + "],all))" try WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) FAIL() @@ -1258,10 +1258,10 @@ static Function TestDataOperation() PASS() endtry - // One sweep does not exist - Make/FREE/N=(DimSize(sweepRef, ROWS), 2, numChannels / 2) dataRef = NaN + // One sweep does not exist, it is not result of select, we end up with one sweep + Make/FREE/N=(DimSize(sweepRef, ROWS), 1, numChannels / 2) dataRef = NaN dataRef[][0][] = sweepRef[p] - str = "data(cursors(A,B),channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 2) + "])" + str = "data(cursors(A,B),select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 2) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) End From 3b695d69949e4382a38f448cc5849507d084b569 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 7 Apr 2022 20:05:58 +0200 Subject: [PATCH 12/49] SF: Adapts tests for tp operation for select introduction --- .../Testing-MIES/UTF_SweepFormulaHardware.ipf | 38 +++++++++---------- .../Testing-MIES/UserAnalysisFunctions.ipf | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf index 1e122e3e7b..32c2f60053 100644 --- a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf @@ -146,7 +146,7 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(unknown_mode, channels(AD), sweeps())" + formula = "tp(unknown_mode, select(channels(AD), sweeps()))" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -154,7 +154,7 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(ss, channels(AD), 3)" + formula = "tp(ss, select(channels(AD), 3))" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -162,7 +162,7 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(ss, channels(unknown), sweeps())" + formula = "tp(ss, select(channels(unknown), sweeps()))" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -170,7 +170,7 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(ss, channels(AD), sweeps(), INVALID)" + formula = "tp(ss, select(channels(AD), sweeps()), INVALID)" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -178,7 +178,7 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(ss, channels(AD), sweeps(), [inf])" + formula = "tp(ss, select(channels(AD), sweeps()), [inf])" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -186,7 +186,7 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(ss, channels(AD), sweeps(), 1)" + formula = "tp(ss, select(channels(AD), sweeps()), 1)" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -194,17 +194,17 @@ static Function TestSweepFormulaTP(string device) PASS() endtry - formula = "tp(ss, channels(AD), sweeps(), 0)" + formula = "tp(ss, select(channels(AD), sweeps()), 0)" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, {NaN}, mode=WAVE_DATA) - formula = "tp(ss, channels(AD), sweeps())" + formula = "tp(ss, select(channels(AD), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) Make/FREE/D/N=(1, 3, 2) wRef CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_SIZES) PGC_SetAndActivateControl(dbPanel, "check_BrowserSettings_DAC", val=1) - formula = "tp(ss, channels(DA), sweeps())" + formula = "tp(ss, select(channels(DA), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) Make/FREE/D/N=(1, 3, 2) wRef = 1000 SetDimLabel COLS, 0, sweep0, wRef @@ -215,45 +215,45 @@ static Function TestSweepFormulaTP(string device) SetScale d, 0, 0, "MΩ", wRef CHECK_EQUAL_WAVES(tpResult, wRef, tol = 1e-12) - formula = "tp(inst, channels(DA), sweeps())" + formula = "tp(inst, select(channels(DA), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, wRef, tol = 1e-12) - formula = "tp(1, channels(DA), sweeps())" + formula = "tp(1, select(channels(DA), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, wRef, tol = 1e-12) - formula = "tp(2, channels(DA), sweeps())" + formula = "tp(2, select(channels(DA), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, wRef, tol = 1e-12) - formula = "tp(base, channels(DA), sweeps())" + formula = "tp(base, select(channels(DA), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) wRef = 0 SetScale d, 0, 0, "", wRef CHECK_EQUAL_WAVES(tpResult, wRef) - formula = "tp(0, channels(DA), sweeps())" + formula = "tp(0, select(channels(DA), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, wRef) Make/FREE/D/N=(1, 1, 1) wRef - formula = "tp(base, channels(DA0), 0)" + formula = "tp(base, select(channels(DA0), 0))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) SetScale d, 0, 0, "pA", wRef CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_UNITS) - formula = "tp(base, channels(DA1), 0)" + formula = "tp(base, select(channels(DA1), 0))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) SetScale d, 0, 0, "mV", wRef CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_UNITS) - formula = "tp(base, channels(AD1), 0)" + formula = "tp(base, select(channels(AD1), 0))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) SetScale d, 0, 0, "mV", wRef CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_UNITS) - formula = "tp(base, channels(AD2), 0)" + formula = "tp(base, select(channels(AD2), 0))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) SetScale d, 0, 0, "pA", wRef CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_UNITS) @@ -303,7 +303,7 @@ static Function TestSweepFormulaCodeResults_REENTRY([string str]) Make/FREE/T/N=(DimSize(indizes, ROWS)) code = textualResultsValues[indizes[p]][%$"Sweep Formula code"][INDEP_HEADSTAGE] - Make/FREE/T/N=(3) ref = {"data(TP, channels(AD), [0])", "data(TP, channels(AD), [1])", "data(TP, channels(AD), [2])"} + Make/FREE/T/N=(3) ref = {"data(TP, select(channels(AD), [0]))", "data(TP, select(channels(AD), [1]))", "data(TP, select(channels(AD), [2]))"} CHECK_EQUAL_TEXTWAVES(ref, code, mode = WAVE_DATA) // set cursors and execute formula again diff --git a/Packages/Testing-MIES/UserAnalysisFunctions.ipf b/Packages/Testing-MIES/UserAnalysisFunctions.ipf index 69977fb9b0..b2d18ccf3e 100644 --- a/Packages/Testing-MIES/UserAnalysisFunctions.ipf +++ b/Packages/Testing-MIES/UserAnalysisFunctions.ipf @@ -983,7 +983,7 @@ Function SetSweepFormula(string device, STRUCT AnalysisFunction_V3& s) case PRE_SWEEP_CONFIG_EVENT: win = DB_FindDataBrowser(device) sweepFormulaNB = BSP_GetSFFormula(win) - sprintf code, "data(TP, channels(AD), [%d])\r", s.sweepNo + sprintf code, "data(TP, select(channels(AD), [%d]))\r", s.sweepNo ReplaceNotebookText(sweepFormulaNB, code) break default: From 80ed7e65e3770a87ce3b044543528902fdfd2bff Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 7 Apr 2022 20:06:41 +0200 Subject: [PATCH 13/49] DB: Adapt default SF notebook template with data using select --- Packages/MIES/MIES_DataBrowser.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_DataBrowser.ipf b/Packages/MIES/MIES_DataBrowser.ipf index 612bd08723..a9ab07b986 100644 --- a/Packages/MIES/MIES_DataBrowser.ipf +++ b/Packages/MIES/MIES_DataBrowser.ipf @@ -223,7 +223,7 @@ Function DB_ResetAndStoreCurrentDBPanel() CheckBox check_limit_x_selected_sweeps WIN = $shPanel, value=0 sfFormula = BSP_GetSFFormula(device) - ReplaceNotebookText(sfFormula, "data(\rcursors(A,B),\rchannels(AD),\rsweeps()\r)") + ReplaceNotebookText(sfFormula, "data(\rcursors(A,B),\rselect(channels(AD),sweeps())\r)") sfJSON = BSP_GetSFJSON(device) ReplaceNotebookText(sfJSON, "") From aa5ab268a5cde90a8922a2bb3c4f92167aff4aa0 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 7 Apr 2022 17:47:53 +0200 Subject: [PATCH 14/49] SF: Add tests for operation select(...) Tests for displayed/all and for various channels and fail conditions. --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 185 +++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index e239a6b4f5..59438cdfd4 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1145,6 +1145,191 @@ static Function TestPlotting() REQUIRE_EQUAL_VAR(WindowExists(win), 1) End +static Function TestOperationSelect() + + variable numChannels, sweepNo + string str, chanList + string win = DATABROWSER_WINDOW_TITLE + string device = HW_ITC_BuildDeviceString(StringFromList(0, DEVICE_TYPES_ITC), StringFromList(0, DEVICE_NUMBERS)) + + variable numSweeps = 2 + variable dataSize = 10 + variable i, j + string trace, name + string channelTypeList = "DA;AD;DA;AD;" + string channelNumberList = "2;6;3;7;" + + if(windowExists(win)) + DoWindow/K $win + endif + + Display/N=$win as device + BSP_SetDataBrowser(win) + BSP_SetDevice(win, device) + + sweepNo = 0 + + CreateFakeSweepData(device, sweepNo=sweepNo) + MIES_DB#DB_SplitSweepsIfReq(win, sweepNo) + CreateFakeSweepData(device, sweepNo=sweepNo + 1) + MIES_DB#DB_SplitSweepsIfReq(win, sweepNo + 1) + + numChannels = 4 // from LBN creation in CreateFakeSweepData->PrepareLBN_IGNORE -> DA2, AD6, DA3, AD7 + Make/FREE/N=0 sweepTemplate + WAVE sweepRef = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) + + Make/FREE/N=(2, 3) dataRef + dataRef[][0] = sweepNo + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7} // AD6, AD7 + str = "select(channels(AD),[" + num2istr(sweepNo) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // non-existing sweeps are ignored + str = "select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 2) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + Make/FREE/N=(4, 3) dataRef + dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} // sweep 0, 1 with 2 AD channels each + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 6, 7} // AD6, AD7, AD6, AD7 + str = "select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + Make/FREE/N=(2, 3) dataRef + dataRef[][0] = {sweepNo, sweepNo + 1} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 6} // AD6, AD6 + str = "select(channels(AD6),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + Make/FREE/N=(6, 3) dataRef + dataRef[][0] = {sweepNo, sweepNo, sweepNo, sweepNo + 1, sweepNo + 1, sweepNo + 1} + chanList = "AD;DA;DA;AD;DA;DA;" + dataRef[][1] = WhichListItem(StringFromList(p, chanList), XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 2, 3, 6, 2, 3} // AD6, DA2, DA3, AD6, DA2, DA3 + str = "select(channels(AD6, DA),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // No existing sweeps + WAVE dataRef = MIES_SF#SF_GetDefaultEmptyWave() + str = "select(channels(AD6, DA),[" + num2istr(sweepNo + 2) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // No existing channels + WAVE dataRef = MIES_SF#SF_GetDefaultEmptyWave() + str = "select(channels(AD0),[" + num2istr(sweepNo) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // Invalid channels + try + str = "select([0, 6],[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + + // Setup graph with equivalent data for displayed parameter + TUD_Clear(win) + + Make/FREE/N=(dataSize, numSweeps, numChannels) input = q + p^r // + gnoise(1) + for(i = 0; i < numSweeps; i += 1) + sweepNo = i + for(j = 0; j < numChannels; j += 1) + name = UniqueName("data", 1, 0) + trace = "trace_" + name + Extract input, $name, q == i && r == j + WAVE wv = $name + AppendToGraph/W=$win wv/TN=$trace + TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber"}, \ + {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo)}) + endfor + endfor + + sweepNo = 0 + Make/FREE/N=(4, 3) dataRef + dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} + dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 6, 7} + str = "select(channels(AD),sweeps(),displayed)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + str = "select(channels(AD),sweeps())" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} + dataRef[][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) + dataRef[][2] = {2, 3, 2, 3} + str = "select(channels(DA),sweeps())" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + Make/FREE/N=(6, 3) dataRef + dataRef[][0] = {sweepNo, sweepNo, sweepNo, sweepNo + 1, sweepNo + 1, sweepNo + 1} + chanList = "AD;AD;DA;AD;AD;DA;" + dataRef[][1] = WhichListItem(StringFromList(p, chanList), XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 2, 6, 7, 2} + str = "select(channels(DA2, AD),sweeps())" // note: channels are sorted AD, DA... + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // No existing sweeps + WAVE dataRef = MIES_SF#SF_GetDefaultEmptyWave() + str = "select(channels(AD6, DA),[" + num2istr(sweepNo + 2) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // No existing channels + WAVE dataRef = MIES_SF#SF_GetDefaultEmptyWave() + str = "select(channels(AD0),[" + num2istr(sweepNo) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + // Invalid channels + try + str = "select([0, 6],[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 1) + "])" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + + str = "select(1)" + try + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + + str = "select(channels(AD), sweeps(), 1)" + try + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + + str = "select(channels(AD), sweeps(), all, 1)" + try + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + FAIL() + catch + PASS() + endtry + +End + static Function TestDataOperation() variable numChannels, sweepNo, rStart, rDelta From 4eac9c0a358aa6830baf62e9d705e7658a2c9dd5 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 21 Jan 2022 22:03:38 +0100 Subject: [PATCH 15/49] Docu: Update SF docu for select and changes of data, labnotebook, epochs, tp --- Packages/doc/SweepFormula.rst | 86 +++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index 5633384177..f47451df70 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -448,20 +448,24 @@ Although being listed near the end, the `data()` function is the core of the .. code-block:: bash - data(array range, array channels, array sweeps) + data(array range, array selectData) - data(string epochShortName, array channels, array sweeps) + data(string epochShortName, array selectData) It returns `[[sweeps][channel]]` for all `[sweeps]` in the array containing the sweep numbers. -The sweeps that you want to return need to be displayed in the graph. Do this -in the OVS tab. +Through the `select` function it can be chosen of data for sweeps is returned for +currently displayed sweeps or for all existing sweeps. The range can be either supplied explicitly using `[100, 300]` which would select `100 ms` to `300 ms` or by using `cursors()`. In case `cursors()` is used but there are no cursors on the graph, the full x-range is used. +A second way to give the range is as 3-dimensional array in the format `[range][sweeps][channels]`. +In this format the row contains two elements as above. This allows to give a separate range +per sweep and channel. + Instead of a numerical range also the short name of an epoch can be given. Then the range is determined from the epoch information of each sweep/channel data iterates through. @@ -477,22 +481,29 @@ When executed by the Formula Executor the data wave has the layout: .. code-block:: bash // Shows the AD channels of all sweeps - data([0, 1000], channels(AD), sweeps()) + data([0, 1000], select(channels(AD), sweeps())) // Shows epoch "E1" range of the AD channels of all sweeps - data("E1", channels(AD), sweeps()) + data("E1", select(channels(AD), sweeps())) + + // Assuming two active AD channels: Shows two AD channels of sweeps 0 and 1 with + // range 10 - 20 ms for the first AD channel and 30 - 40 ms for the second AD channel + data([[[10, 10], [30, 30]], [[20, 20], [40, 40]]], select(channels(AD), [0, 1])) labnotebook """"""""""" -`labnotebook(string key, array channels, array sweeps [, string -entrySourceType])` returns the (case insensitive) `key` entry from the -labnotebook for the given channel and sweep combination. The optional +.. code-block:: bash + + labnotebook(string key, array selectData [, string entrySourceType]) + +The labnotebook function returns the (case insensitive) `key` entry from the +labnotebook for the selected channel and sweep combination(s). The optional `entrySourceType` can be one of the constants `DataAcqModes` for data acquisition modes as defined in `../MIES/MIES_Constants.ipf`. If the `entrySourceType` is omitted it defaults to `DATA_ACQUISITION_MODE`. -The `labnotebook()` function has the same data layouting as the `data()` +The `labnotebook()` function has the same data layout as the `data()` function. It returns the notebook entry in the rows for all `[sweeps]` with the corresponding `[channel]` (`[[sweeps][channel]]`). @@ -508,8 +519,7 @@ corresponding `[channel]` (`[[sweeps][channel]]`). vs labnotebook( "set cycle count", - channels(AD), - sweeps(), + select(channels(AD), sweeps()), DATA_ACQUISITION_MODE ) @@ -548,8 +558,36 @@ seconds of the level and :math:`T` the total x range of the data in seconds. apfrequency([10, 20, 30], 1, 15) -Various -^^^^^^^ +Utility Functions +^^^^^^^^^^^^^^^^^ + +select +"""""" + +The select function allows to choose a set of sweep data from a given list of sweeps and channels. +It is intended to be used with functions like data, labnotebook, epochs and tp. A mode parameter allows to retrieve +the set of sweeps from the `displayed` data or from `all` existing sweeps. + +.. code-block:: bash + + select(array channels, array sweeps[, string mode]) + +The function accepts two or three parameters. If the mode parameter is omitted then select defaults to `displayed`. +To retrieve a correct array of channels the `channels` function should be used. + +If a given channel/sweep combination does not exist it is omitted in the output. + +The output is a N x 3 array where the columns are sweep number, channel type, channel number. + +The order of the output is sweep -> channel type -> channel number. +e.g. for two sweeps numbered 0, 1 that have channels AD0, AD1, DA6, DA7: +`{{0, 0, 0, 0, 1, 1, 1, 1}, {0, 0, 1, 1, 0, 0, 1, 1}, {0, 1, 6, 7, 0, 1, 6, 7}}`. + +.. code-block:: bash + + select(channels(AD), sweeps(), all) + select(channels(AD4, DA), [1, 5]], all) + select(channels(AD2, DA5, AD0, DA6), [0, 1, 3, 7], all) range """"" @@ -581,13 +619,13 @@ The epochs function returns information from epochs. .. code-block:: bash - epochs(string name, array channels, array sweeps[, string type]) + epochs(string name, array selectData[, string type]) type sets what information is returned. Valid types are: `range`, `name`, `treelevel`. If type is not specified then `range` is used as default. range: -The operation returns a 2xN wave with the start and end time of the epoch(s) in [ms] for all active channels. +The operation returns a 2xN wave with the start and end time of the epoch(s) in [ms] for all existing active channels. If only a single epoch is returned then the operation returns a 1D wave with two elements, as the range function. The order of returned ranges for the N dimension is: sweeps( channels ). If a sweep/channel combination does not have epoch information saved `[NaN, NaN]` is returned as range for this combination. @@ -606,16 +644,16 @@ If no matching epoch was found a zero sized wave is returned. .. code-block:: bash // two sweeps acquired with two headstages set with PulseTrain_100Hz_DA_0 and PulseTrain_150Hz_DA_0 from _2017_09_01_192934-compressed.nwb - epochs(ST, channels(AD), sweeps(), range) == [[20, 1376.01], [20, 1342.67], [20, 1376.01], [20, 1342.67]] + epochs(ST, select(channels(AD), sweeps()), range) == [[20, 1376.01], [20, 1342.67], [20, 1376.01], [20, 1342.67]] tp "" -The tp function returns analysis values for test pulses that are part of sweeps. +The tp function returns analysis values for test pulses that are part of selected sweeps. .. code-block:: bash - tp(variant type, array channels, array sweeps, [array ignoreTPs]) + tp(variant type, array selectData[, array ignoreTPs]) type sets what test pulse analysis value is returned. The following types are supported: @@ -629,10 +667,10 @@ The following types are supported: The returned array is 1 x M x N, where M indexes the sweeps and N indexes the channels. Thus, sweep and channel information gets transferred as well. Values for non-existing sweeps and/or channels are set NaN. -If a single sweep contains multiple test pulses than the data from the test +If a single sweep contains multiple test pulses then the data from the test pulse ranges is averaged. The optional parameter ``ignoreTPs`` allows to ignore -some of the found testpulses. The indizes are zero-based and identify the -testpulses by ascending starting time. +some of the found test-pulses. The indices are zero-based and identify the +test-pulses by ascending starting time. The test pulses in the sweep must have the same duration. Test pulses that are part of sweeps are identified through their respective epoch short name, that starts with "TP" or "U_TP". If sweeps and channels can resolve existing single sweeps but none contain @@ -641,10 +679,10 @@ epochs for test pulses then a numeric single element wave is returned with the v .. code-block:: bash // Get steady state resistance from all sweeps and all AD channels - tp(ss, channels(AD), sweeps()) + tp(ss, select(channels(AD), sweeps())) // Get base line level from all sweeps and DA1 channel - tp(static, channels(DA1), sweeps()) + tp(static, select(channels(DA1), sweeps())) merge """"" From 02a63a030a6306129256f6544c059200753925f9 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 7 Apr 2022 19:34:59 +0200 Subject: [PATCH 16/49] SF: Update docu in SF notebook for the new select function. --- Packages/MIES/SweepFormulaHelp.ifn | Bin 68463 -> 69915 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Packages/MIES/SweepFormulaHelp.ifn b/Packages/MIES/SweepFormulaHelp.ifn index de0b67855decb91d263a48e8a1b61c1dd7eb67ac..ba001ccc3528b671632ef0d1b8e50150c221144d 100644 GIT binary patch delta 3016 zcma*peQXqE9tZH>EV!1kTboj#O3??};&z#Cr^~AaO3Rf(%Zm!wJBw8JWwssMompq5 zK$Wogl1oDnDUTAv5z-jXG{zXh5??OnG{zYJxu!>m$DLdcJYR@0AdRm`#P2iHZC9h> zCZEpCGtcvTp7}k$o$aN=lmGJE)VVKl72$eUi9UMXZoW!FdSB zIvjtwz5lIa{g-d$yQN^~o;cA*UZUuOA^=ebCaH|N*X=kT9w6!~lITeUn_eu@JoH(H zqxf9^$fd67homWf(eormT*6WAzdS$N;dvt{Rq~lNv!_~WQjOWo5j#@NADUVtg?Mkx z?LTR43eL)&s`=a_Df~@k`fPVVAl4nxbv0=PnwqO+X6;nfv=yl4P2t(xTel@NpGq)c z9AN`AnvIkmYZ zGApgdw1gIm*qWh-{BHXEw3>ett|)W|i3DVo1tXIJ70xmk`|Vk)Wv=*2Ih*3m#+SS| zi68KIy>28v;494>b@IKXTOEtS5^mqQ{w?4zX67MUqKlr^Rd|7b}7jn;D6*th0n^q*#}>L)G0DrdRZ&qN?de8Oy@Y zlED1@?G=^&Idlylk&tWGi2q~ROx{vkGA-=1qD8bipN0J1wu)&h^Lsn(P5hr#H~Zb4 z8`~vb*HT`z6`N+8n%>1yMqK4{*4Jj9a`GxJ#quqtC7A8xC;w59ow#bNW=hkMBj>?H zC)9(f>UOf1#WgFPjP&OA*kXq1c0-n!x=XWcF{xXQA=VyAYH?=?RvC+!YQ0FNJP{=1 zrwl*eF>U@@JWXrD=_z&y+mXu`X2J^ei81`EhILXvJonZ$loaD^F~n43Mk=N1aWTLU zd+a>kcnyjTb(b|!3r-EVX~Rlp=Vw>!MO84r#mTqgwB+y1N~xQl zS0`tzrr{^6$-j2jBeQw#W91g@HanxbREs4dmaSTL$bV?tEww?X6vyf{w?lZ5EtFYv zlalv3lSRFV_u_5bx2=d5Z}4Wr%ii!vi}xh zPm&h%SuN%Hkv6ncO_Cb9)jGHIm-3kXYwPP$@OEb=I~}mB>&(LUu6sBr|Nidf>-(mS zM_0y!^W*Ew_@i~T{EMkRk&0~bhN9`2O*KSGI01hHIYhJ%24E02)#4Woo`;v6SYGpJO*FFRp?lNUq(0w7Zy;4?1dNvA3;6pQHMTw4&H(f z!MB)bDLeps;8{2c|AB%fM9r`R4#JCY0#3u(CB%l761@*4ci`dRF4ztqLS=o1*zS6w z!!QhI;9DqJMihp=

IA;cGayf+)~{e-Pj^7=`Fcw8IN9qY?MOg#GYhBcU7-ACAFk zxCp*gMDw5{gFiO>9o~gcpydxl8{sIt0<0Oq;bj7uloq1WHALl54|`x0-hyqQ z!^?2671@I2ZHNy;@IH)!zK$rf1ApFu5lF1Z0C*NkHXvPa7>+~PMl1xLfVZLGPOKh= zf!&1#Lpz*+QSjZ3Yd8WYpdYYc06b;cZ9i)ExoW7&;bcH^k@98Ra(OEKHy>W`v G?fDVu897z} delta 1853 zcmajfe@v8h9Ki9BTC|9M%sIx2)IV5Kx!#AcYU;e(>-j!EKA-Qe z&$H*fQ|8}8=0uZ3etGh_RvA@io^C&EPkh}`tVJFrh!!Q%#n~gY=V|YKzrE+ANi(7qX;|thah3R%9u*c(R3fR)qRhGq} z&SckWb`|4%EdHKR%H?!v9`&ZPBx8I1Zjb*?_TuSSBjE9Qwg&5W`W>_6&Z=ignflwA z5}9W>W65w-!lRSZF^vCDLv`fNjppXmY{HJESEIs*R$Ve_F15a30qI6~e?hmN)-ajw zY1+Bf8?bFEzt3vI4J8&WarI3e9|LL;Om3>zkuE$uy z{*6iTtu>h^!;vMj8*ktuZbQ%JWkm>`usTI5P=oy# z!#G}EBI3n0j4hE?Y0eQjj!~?7QY45roWW&`z?v(v5_LF$ljz1zn7veF8ESA8XVHrR zjA4AK7$eI>uEV}uqzDZ-fWMZv(s7;`p*&`dQQU-e1;Hp;A;wkO+oTBWlSLM zDF#O!4&oZdP`py476aJ$G`oXlSXYT0!Vr2|T_SzBhK$uDVjOb|NI-reCBX<9)=&yG zBLr&^aR{Of3D2;DD8?lWp{STGM<1>r=UMu|Ip`%Ktt}h|FagtAR)P>ZF@fB5A{%fT zLl{TwbG!rS#Q>s9nG77z*HaRdU_ahRH*Q1Uz=BYSS`?L0|I#vEE?mX@jnonc(S}a+ z!?uaqq7ymgbb>DQp!|7}YQ$BDB%v9X@df^XY-Wehf{!tPLEPLdhNY7F*Hy9-%y~h? zKrLF(i(!nyTqVZ*Dz*+G4B{qowlEo-MK7*kbc-0~YGQE_gOC>~6@2Kx84P10okLcQ z$U68CtP!J)HiQXi5-k?5NSdkrMx5HWBhKV(P@x@m)A28r;j_&&^Zf0vMgHW|zEFRN zs{`Ngz;4?6t;+so4jHlf!`$_K7YWyAaO(bk`bqRmF3W!qNtx{Zx-U-O^P|YTsj*`9 z7{)*Lv&w0->#4s{cZ*uqn5sv2IR1`$r0HuebO)Q$h+db>tFwA Jc(&#>{R30zuw4KE From 92b04d477931a7429701c4272f055136d9547889 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 8 Apr 2022 16:15:26 +0200 Subject: [PATCH 17/49] SF: Change sweeps operation to use zero parameters and return all sweeps - also adapted in-app SF help notebook - also adapted documentation --- Packages/MIES/MIES_SweepFormula.ipf | 32 +++++----------------------- Packages/MIES/SweepFormulaHelp.ifn | Bin 69915 -> 69551 bytes Packages/doc/SweepFormula.rst | 5 +---- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 1a9dee8d10..53f02ae66d 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2566,38 +2566,16 @@ static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, stri return out End -/// `sweeps([str type])` -/// @p type: `|displayed|all` -/// displayed (default): get (selected) sweeps -/// all: get all possible sweeps +/// `sweeps()` +/// returns all possible sweeps as 1d array static Function/WAVE SF_OperationSweeps(variable jsonId, string jsonPath, string graph) - SF_ASSERT(JSON_GetArraySize(jsonID, jsonPath) <= 1, "Function requires 1 argument at most.") + SF_ASSERT(JSON_GetArraySize(jsonID, jsonPath) == 1 && IsNaN(JSON_GetVariable(jsonID, jsonPath + "/0")), "Sweep function takes no arguments.") SF_ASSERT(!IsEmpty(graph), "Graph not specified.") - if(JSON_GetType(jsonID, jsonPath + "/0") == JSON_NULL) - Make/FREE/T wvT = {"displayed"} - else - WAVE/T wvT = JSON_GetTextWave(jsonID, jsonPath) - endif - - strswitch(wvT[0]) - case "all": - WAVE out = OVS_GetSelectedSweeps(graph, OVS_SWEEP_ALL_SWEEPNO) - break - case "displayed": - WAVE/T/Z sweepNumbers = GetSweepUserData(graph, "sweepNumber") - if(WaveExists(sweepNumbers)) - Make/N=(DimSize(sweepNumbers, ROWS))/FREE sweepNumbersNumeric = str2num(sweepNumbers[p]) - WAVE out = GetUniqueEntries(sweepNumbersNumeric) - endif - break - default: - SF_ASSERT(0, "Undefined argument") - endswitch - + WAVE/Z out = OVS_GetSelectedSweeps(graph, OVS_SWEEP_ALL_SWEEPNO) if(!WaveExists(out)) - Make/N=1/FREE out = {NaN} // simulates [null] + WAVE out = SF_GetDefaultEmptyWave() endif return out diff --git a/Packages/MIES/SweepFormulaHelp.ifn b/Packages/MIES/SweepFormulaHelp.ifn index ba001ccc3528b671632ef0d1b8e50150c221144d..9e65ad40a501b9d20a8222ce11b39b3a9a2b234d 100644 GIT binary patch delta 1550 zcmajfZ%kWN7{~Enn|cQbNDvK~3%SucJ60=W6>-iEH>{H(Iz+?{*;?tswo*yk{c$0y z!Gsu`3#^7W64y<-;^+LE2O8aYPB8+Tab>|5G6>a!NxcX(uH zEP6B;?1-9?VC--t95wx6(;tcWUo(#$h_#uqwxD@4zziR54+JAo^VR*PKNPw(7)kf(YhA|I0C9hL))zL|z?SNaAsus%gR>^s>lOu_dw`8bGfSZmldG$B$${a5JN zcaeZWq)=KbRpUIyQ2dP4hyg63wT^MjVgcS~nH)(h!1WxZ!T^3i1_iqrM;a5zspsSC zslSVk@jRy)HR!?s#&H8i1BVOM2*CS-REJBLLrEhC72P<65qts1iyS_Tz}>_gxQG-Q zUXq$o6!%eY9LHr$;&*5d+l@|KzyzkTyhmnkv$P*+6u(T(5kM!>n85;UEiy}5*k<%& z8q09+Pe3H4;wrA97m1`Z{5T9N9MBfBvoX2fx4jjoWxif5!`__0#VYn`Vp2C@|JA zdj7}Na3hQE*`NM-latNkJJ+Q9Zq9x^RAhYevvkkjYZVx`v;LJ|67F`V(Yi$4a}$;A Qb|a6`*q@`l?QgyR7yGkELjV8( delta 1855 zcmajge@v8h9Ki9<-SDFGFh$ zzn|y(d_K>?XP=~ebUdZ&psU^@9QmT`B5wLF+9kCAJ>ETh%hTo)xoH#eHYHbz)LKRI zywle;^hb}0w9m1~heVm1ZjlAdSxuWf{=YTLo8}(0Jeii9Imn7%X`S777oDxOX6L!m zEy=Oy9$k+_m7&L4j8If*A*C6HwpWGyYU-8>wKRKmBWgM}&5JjOeY$F3j#pQDAoiwi zC@mONVMF->(MV9+tNUE4(yylG1)?exj;Y;&Rz2iYftd2?eyt@Ki>g@o-}5&ngBJV~ zpy1J72>sX|Ha}XX0wKlol~0Rl%4>x8M0LZZrbDr%$Af6CS|F%-gZks)T;}!Er_J9g zGc4ui$Ikh6)iTMEOzz|)Cywn?Bgw0WBXu+8_-4%cV8)z7=2!Xi=4DC-F{cw=7>yZ$ z(C(>BEi~Vnz4&ZyL1$^aq)?;+5p-Y_50JG`Bp0Q)hMRB{i7Y@h>hTWxaRC#!hs;GH zy%@qBB#`bDIfXfkiHofWVqmev7h%I9cZZ3<96fYMEp&iFDj2m#Q5Lty?Xu}Z<;ya|S zBx`6z7y2=P%eb~u)W|B4E68}Bm%|n`;s)}HMeQpVIgHUbr>pn_8LLH>qP>KmxQ2T; z`hrMdDL-)BK?2?v8OKRvm2nRSI?-Pys-m0+;3zI*0**D554DKlbHp!lx`nkbiEKhI zhM+14j?)-{|7Fq#_gZTFibxKM(S|kaC<8(`jjl?vg_0^thY?&s0-^O}4Hq!Bp8ESY zumDGpv5|D)FwP)*6C1&M_yVb$**!*~wy4Mp@B{ zwr$kEhmKLr*Nh=dAoVp;g+>_Y!x@ZVpho083b#{Nbl?OMxR0FIMONSt9w6rpK5XtNk(bE`;(c6&qk+1k3V!tB9Ij%#LDYR(tB-rI zh+WP#n~~;hGt!uDvx-&5!e&vU(^~e8nch@r?r%()wftL=Kly)=Z=1z*_xH&gHXGgi zAIws^PteW&kx{2L?K&e)oBSj)-`agQGa1!x=l=YkMV@&$Vw=r_OMfvhF?BS-Umb_} XWmBF#jnVPHc;ly%muEKw8$SIT)9tf& diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index f47451df70..a1332aa534 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -401,10 +401,7 @@ sweeps `sweeps()` -return an array which holds the sweep numbers of all displayed sweeps. -`sweeps(all)` return an array of all available sweeps. - -Not implemented yet: The not-yet checked sweeps from overlay sweeps are automatically enabled. +return an 1d-array with the sweep numbers of all sweeps. .. code-block:: bash From 5af3c37f3dc87a0d7ad8e8ec7e09c4aee63ce9c2 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 8 Apr 2022 16:17:13 +0200 Subject: [PATCH 18/49] Updated SF example in code docu comment in MIES_AnalysisFunctions.ipf --- Packages/MIES/MIES_AnalysisFunctions.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_AnalysisFunctions.ipf b/Packages/MIES/MIES_AnalysisFunctions.ipf index b3e8657c3c..9d2c93dbd6 100644 --- a/Packages/MIES/MIES_AnalysisFunctions.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions.ipf @@ -1261,7 +1261,7 @@ End /// /// setvar_DataAcq_OnsetDelayUser -> Pre DAQ|20| /// Popup_Settings_FixedFreq -> Pre Sweep|100|Post Sweep|Maximum| -/// sweepFormula_formula -> Pre Set|data(cursors(A,B), channels(AD), sweeps()) +/// sweepFormula_formula -> Pre Set|data(cursors(A,B), select(channels(AD), sweeps())) /// /// \endrst /// From 43d5ed7598943dd36bdf3c3a280c518df35334b3 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 8 Apr 2022 16:46:34 +0200 Subject: [PATCH 19/49] SF: Adapt store operation to replace use of sweeps(displayed) The store operation used sweeps(displayed), which is now replaced through the select operation. The sweeps are now retrieved through calling select. --- Packages/MIES/MIES_SweepFormula.ipf | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 53f02ae66d..a86614e39e 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1742,6 +1742,7 @@ End static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string graph, string code, [WAVE data, string name]) variable numEntries, numOptParams, hasStoreEntry, numCursors, numBasicEntries + variable dimSweep string shPanel, dataFolder, device numOptParams = ParamIsDefault(data) + ParamIsDefault(name) @@ -1773,7 +1774,15 @@ static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string WAVE/T/Z cursorInfos = GetCursorInfos(graph) - WAVE/Z sweeps = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser("sweeps(displayed)")), graph = graph) + WAVE select = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser("select(channels(AD), sweeps(),displayed)")), graph = graph) + dimSweep = FindDimLabel(select, COLS, "SWEEP") + Duplicate/FREE/RMD=[][dimSweep] select, wTmp + Redimension/N=(-1) wTmp + if(DimSize(wTmp, ROWS) > 1) + FindDuplicates/FREE/RN=sweeps wTmp + else + WAVE sweeps = wTmp + endif values[0][%$"Sweep Formula displayed sweeps"][INDEP_HEADSTAGE] = NumericWaveToList(sweeps, ";") // todo: use plain `channels()` once https://github.com/AllenInstitute/MIES/issues/1135 is resolved From beae732ad5a8d5d93afaf44832c44fd44b24f9d4 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 8 Apr 2022 20:56:33 +0200 Subject: [PATCH 20/49] Updated comment for todo in AnalysisFunctions PSQ - with recent changes to SF the targeted adaption at this location changes as well --- Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf index bebac5ffd4..da89b8f446 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf @@ -4463,8 +4463,8 @@ Function PSQ_PipetteInBath(string device, struct AnalysisFunction_V3& s) formula_nb = BSP_GetSFFormula(databrowser) - /// @todo: Rework to use non-displayed sweeps, once https://github.com/AllenInstitute/MIES/pull/1256 is merged - /// this also then allows us to remove the OVS fiddling + /// @todo: The call should work on the last sweep acquired. Once this number is retrieved it can be set directly + /// in the formula string replacing sweeps(). The the OVS disabled/enable procedure can be skipped. ReplaceNotebookText(formula_nb, "store(\"Steady state resistance\", tp(ss, select(channels(AD), sweeps()), [0]))") PGC_SetAndActivateControl(bsPanel, "check_BrowserSettings_SF", val = 1) From aff5340e6185fb7625969c66a7812abfe0b0cea6 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 14 Apr 2022 01:48:53 +0200 Subject: [PATCH 21/49] OVS: Moved DF getter in OVS_GetSelectedSweeps to a later postion This helps easier design of tests, such that this meta information is not required when retrieving all selected sweeps. --- Packages/MIES/MIES_OverlaySweeps.ipf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_OverlaySweeps.ipf b/Packages/MIES/MIES_OverlaySweeps.ipf index 7bc896ceb5..88649394d9 100644 --- a/Packages/MIES/MIES_OverlaySweeps.ipf +++ b/Packages/MIES/MIES_OverlaySweeps.ipf @@ -358,8 +358,6 @@ Function/WAVE OVS_GetSelectedSweeps(win, mode) mode == OVS_SWEEP_SELECTION_SWEEPNO || \ mode == OVS_SWEEP_ALL_SWEEPNO, "Invalid mode") - DFREF dfr = OVS_GetFolder(win) - if(mode == OVS_SWEEP_ALL_SWEEPNO) return GetPlainSweepList(win) endif @@ -369,6 +367,8 @@ Function/WAVE OVS_GetSelectedSweeps(win, mode) return $"" endif + DFREF dfr = OVS_GetFolder(win) + WAVE/T listboxWave = GetOverlaySweepsListWave(dfr) WAVE listboxSelWave = GetOverlaySweepsListSelWave(dfr) From 253b053175204f04931bedf07fb4905c1231bf48 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 14 Apr 2022 01:51:10 +0200 Subject: [PATCH 22/49] SF: Adapt tests for sweeps() operation working on all sweeps now - Specifically in the epochs and labnotebook test the sweep range was specified explicitly as the sweep() operation relies now on OVS that is not setup for these tests. It is setup for the select() operation tests, though. --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 59438cdfd4..bd4577d532 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1508,11 +1508,11 @@ Function TestLabNotebook() endfor ModifyGraph/W=$win log(left)=1 - str = "labnotebook(" + channelTypeC + ",select(channels(AD),sweeps()))" + str = "labnotebook(" + channelTypeC + ",select(channels(AD),0..." + num2istr(numSweeps) + "))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) - str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + channelTypeC + ",select(channels(AD),sweeps()),UNKNOWN_MODE)" + str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + channelTypeC + ",select(channels(AD),0..." + num2istr(numSweeps) + "),UNKNOWN_MODE)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) End @@ -1613,19 +1613,19 @@ static Function TestOperationEpochs() Make/FREE/T refDataT = {"Epoch=0;Type=Pulse Train;Pulse=48;Baseline;ShortName=E0_PT_P48_B;"} REQUIRE_EQUAL_WAVES(data, refDataT, mode = WAVE_DATA) - str = "epochs(\"E0_PT_P48_B\", select(channels(DA), sweeps()))" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA), 0..." + num2istr(numSweeps) + "))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D/N=(2, numSweeps * activeChannelsDA) refData refData = p ? 510 : 503 REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) // channel(s) with no epochs - str = "epochs(\"E0_PT_P48_B\", select(channels(AD), sweeps()))" + str = "epochs(\"E0_PT_P48_B\", select(channels(AD), 0..." + num2istr(numSweeps) + "))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) CHECK_EQUAL_WAVES({NaN}, data, mode = WAVE_DATA) // name that does not match any - str = "epochs(\"does_not_exist\", select(channels(DA), sweeps()))" + str = "epochs(\"does_not_exist\", select(channels(DA), 0..." + num2istr(numSweeps) + "))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D/N=(2, numSweeps * activeChannelsDA) refData = NaN CHECK_EQUAL_WAVES(refData, data) @@ -1635,7 +1635,7 @@ static Function TestOperationEpochs() CHECK_EQUAL_WAVES({NaN}, data, mode = WAVE_DATA) // invalid type - str = "epochs(\"E0_PT_P48_B\", select(channels(DA), sweeps()), invalid_type)" + str = "epochs(\"E0_PT_P48_B\", select(channels(DA), 0..." + num2istr(numSweeps) + "), invalid_type)" try WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win); AbortOnRTE FAIL() From ad770e7942529f405c2192fbc91be1491598a598 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Fri, 8 Apr 2022 23:51:53 +0200 Subject: [PATCH 23/49] SF: bugfix channels operation, now properly returning NaNs - The channels operation did not properly returned {NaN,NaN} for channels(), instead it returned {2, NaN} containing an invalid channelType. - Depending on this fix SF_GetActiveChannelNumbers was completely rewritten to support NaN as channelType targetting all channel types (DAC and ADC) --- Packages/MIES/MIES_SweepFormula.ipf | 165 +++++++++++++++------------- 1 file changed, 86 insertions(+), 79 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index a86614e39e..c3f5e8d61d 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1431,11 +1431,11 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo WAVE channels, sweeps variable entrySourceType - variable i, j, k, channelType, channelNumber, numIndices, sweepNo - string setting, msg + variable i, j, k, l, channelType, channelNumber, index, sweepNo, outIndex, shift + variable numSweeps, numInChannels, numSettings, maxChannels, numUniqueCombinations + string setting, settingList, msg WAVE/Z settings - Variable index ASSERT(windowExists(graph), "DB/SB not specified.") SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") @@ -1448,14 +1448,14 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo entrySourceType == NUMBER_OF_LBN_DAQ_MODES, \ "Undefined labnotebook mode. Use one in group DataAcqModes") - Make/FREE/WAVE/N=2 channelNumbers - Make/FREE/N=(GetNumberFromType(xopVar=XOP_CHANNEL_TYPE_ADC)) channelNumbersAD = NaN - channelNumbers[XOP_CHANNEL_TYPE_ADC] = channelNumbersAD - Make/FREE/N=(GetNumberFromType(xopVar=XOP_CHANNEL_TYPE_DAC)) channelNumbersDA = NaN - channelNumbers[XOP_CHANNEL_TYPE_DAC] = channelNumbersDA - // search sweeps for active channels - for(i = 0; i < DimSize(sweeps, ROWS); i += 1) + shift = ceil(log(NUM_AD_CHANNELS) / log(2)) + numSweeps = DimSize(sweeps, ROWS) + numInChannels = DimSize(channels, ROWS) + + Make/FREE/N=((NUM_DA_TTL_CHANNELS + NUM_AD_CHANNELS) * numSweeps) collect + + for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] if(!IsValidSweepNumber(sweepNo)) @@ -1467,58 +1467,76 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo continue endif - for(j = 0; j < DimSize(channels, ROWS); j += 1) - channelType = channels[j][0] - switch(channelType) - case XOP_CHANNEL_TYPE_DAC: - setting = "DAC" - break - case XOP_CHANNEL_TYPE_ADC: - setting = "ADC" - break - default: - sprintf msg, "Unhandled channel type %g in channels() at position %d", channelType, j - SF_ASSERT(0, msg) - endswitch + for(j = 0; j < numInChannels; j += 1) - channelNumber = channels[j][1] - WAVE wv = channelNumbers[channelType] - for(k = 0; k < DimSize(wv, ROWS); k += 1) - if(!IsNaN(wv[k])) - continue - endif - if(!IsNaN(channelNumber) && channelNumber != k) - continue - endif - [settings, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, setting, k, channelType, entrySourceType) - if(!WaveExists(settings)) - continue - endif - wv[k] = settings[index] + channelType = channels[j][%channelType] + channelNumber = channels[j][%channelNumber] + + if(IsNaN(channelType)) + settingList = "DAC;ADC;" + else + switch(channelType) + case XOP_CHANNEL_TYPE_DAC: + settingList = "DAC;" + break + case XOP_CHANNEL_TYPE_ADC: + settingList = "ADC;" + break + default: + sprintf msg, "Unhandled channel type %g in channels() at position %d", channelType, j + SF_ASSERT(0, msg) + endswitch + endif + + numSettings = ItemsInList(settingList) + for(k = 0; k < numSettings; k += 1) + setting = StringFromList(k, settingList) + strswitch(setting) + case "DAC": + channelType = XOP_CHANNEL_TYPE_DAC + maxChannels = NUM_DA_TTL_CHANNELS + break + case "ADC": + channelType = XOP_CHANNEL_TYPE_ADC + maxChannels = NUM_AD_CHANNELS + break + default: + SF_ASSERT(0, "Unexpected setting entry for channel type resolution.") + break + endswitch + + for(l = 0; l < maxChannels; l += 1) + if(!IsNaN(channelNumber) && channelNumber != l) + continue + endif + [settings, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, setting, l, channelType, entrySourceType) + if(!WaveExists(settings)) + continue + endif + collect[outIndex] = channelType << shift + settings[index] + outIndex += 1 + endfor endfor endfor endfor + Redimension/N=(outIndex, -1) collect - // create channels wave - Make/FREE/N=(NUM_MAX_CHANNELS, 2) out + if(outIndex > 1) + FindDuplicates/FREE/RN=collectReduced collect + Sort collectReduced, collectReduced + else + WAVE collectReduced = collect + endif + numUniqueCombinations = DimSize(collectReduced, ROWS) + + Make/FREE/N=(numUniqueCombinations, 2) out SetDimLabel COLS, 0, channelType, out SetDimLabel COLS, 1, channelNumber, out - numIndices = 0 - for(i = 0; i < DimSize(channelNumbers, ROWS); i += 1) - WAVE wv = channelNumbers[i] - for(j = 0; j < DimSize(wv, ROWS); j += 1) - if(IsNaN(wv[j])) - continue - endif - - out[numIndices][%channelType] = i - out[numIndices][%channelNumber] = wv[j] - numIndices += 1 - endfor - endfor - - Redimension/N=(numIndices, -1) out + if(numUniqueCombinations) + out[][%channelType] = collectReduced[p] >> shift + out[][%channelNumber] = collectReduced[p] - out[p][%channelType] << shift + endif return out End @@ -1785,26 +1803,10 @@ static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string endif values[0][%$"Sweep Formula displayed sweeps"][INDEP_HEADSTAGE] = NumericWaveToList(sweeps, ";") - // todo: use plain `channels()` once https://github.com/AllenInstitute/MIES/issues/1135 is resolved - WAVE/Z channels_AD = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser("channels(AD)")), graph = graph) - WAVE/Z channels_DA = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser("channels(DA)")), graph = graph) - - if(WaveExists(sweeps) && WaveExists(channels_DA) && WaveExists(channels_AD)) - WAVE/Z activeChannels_DA = SF_GetActiveChannelNumbers(graph, channels_DA, sweeps, DATA_ACQUISITION_MODE) - WAVE/Z activeChannels_AD = SF_GetActiveChannelNumbers(graph, channels_AD, sweeps, DATA_ACQUISITION_MODE) - - if(!WaveExists(activeChannels_DA) && !WaveExists(activeChannels_AD)) - WAVE/Z activeChannels - elseif(WaveExists(activeChannels_DA) && WaveExists(activeChannels_AD)) - Concatenate/FREE/NP=(ROWS) {activeChannels_DA, activeChannels_AD}, activeChannels - elseif(WaveExists(activeChannels_DA) && !WaveExists(activeChannels_AD)) - WAVE activeChannels = activeChannels_DA - elseif(!WaveExists(activeChannels_DA) && WaveExists(activeChannels_AD)) - WAVE activeChannels = activeChannels_AD - else - ASSERT(0, "Unexpected case") - endif + WAVE/Z channels = SF_ExecuteFormula(graph, "channels()") + if(WaveExists(sweeps) && WaveExists(channels)) + WAVE/Z activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) values[0][%$"Sweep Formula active channels"][INDEP_HEADSTAGE] = NumericWaveToList(activeChannels, ";") endif @@ -2548,8 +2550,8 @@ End /// returns [[channelName, channelNumber]+] static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, string graph) - variable numIndices, i, JSONtype - string channelName, channelNumber + variable numIndices, i, JSONtype, channelType + string channelName, channelNumber, channelStr string regExp = "^(?i)(" + ReplaceString(";", XOP_CHANNEL_NAMES, "|") + ")([0-9]+)?$" numIndices = JSON_GetArraySize(jsonID, jsonPath) @@ -2561,16 +2563,21 @@ static Function/WAVE SF_OperationChannels(variable jsonId, string jsonPath, stri if(JSONtype == JSON_NUMERIC) out[i][%channelNumber] = JSON_GetVariable(jsonID, jsonPath + "/" + num2istr(i)) elseif(JSONtype == JSON_STRING) - SplitString/E=regExp JSON_GetString(jsonID, jsonPath + "/" + num2istr(i)), channelName, channelNumber + channelStr = JSON_GetString(jsonID, jsonPath + "/" + num2istr(i)) + SplitString/E=regExp channelStr, channelName, channelNumber if(V_flag == 0) - continue + SF_ASSERT(0, "Unknown channel: " + channelStr) endif out[i][%channelNumber] = str2num(channelNumber) endif SF_ASSERT(!isFinite(out[i][%channelNumber]) || out[i][%channelNumber] < NUM_MAX_CHANNELS, "Maximum Number Of Channels exceeded.") - out[i][%channelType] = WhichListItem(channelName, XOP_CHANNEL_NAMES, ";", 0, 0) + if(!IsEmpty(channelName)) + channelType = WhichListItem(channelName, XOP_CHANNEL_NAMES, ";", 0, 0) + if(channelType >= 0) + out[i][%channelType] = channelType + endif + endif endfor - out[][] = out[p][q] < 0 ? NaN : out[p][q] return out End From 4cdf605c766b3d2260b5bc9eb0563005fd68e4af Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 9 Apr 2022 00:05:14 +0200 Subject: [PATCH 24/49] SF: Adapt tests for channels() operation for previous fix - Also added tests to select using the fixed channels() feature --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 37 +++++++++++++++++++--- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index bd4577d532..2a1764ae53 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -595,7 +595,7 @@ static Function merge() REQUIRE_EQUAL_WAVES(SF_FormulaExecutor(jsonID0), SF_FormulaExecutor(jsonID1)) End -static Function MIES_channel() +static Function TestOperationChannels() Make/FREE input = {{0}, {NaN}} SetDimLabel COLS, 0, channelType, input @@ -623,17 +623,28 @@ static Function MIES_channel() WAVE output = SF_FormulaExecutor(DirectToFormulaParser("channels(AD,DA)")) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) - Make/FREE input = {{2}, {1}} + Make/FREE input = {{NaN}, {1}} WAVE output = SF_FormulaExecutor(DirectToFormulaParser("channels(1)")) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) - Make/FREE input = {{2, 2}, {1, 3}} + Make/FREE input = {{NaN, NaN}, {1, 3}} WAVE output = SF_FormulaExecutor(DirectToFormulaParser("channels(1,3)")) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) - Make/FREE input = {{0,1,2},{1,2,3}} + Make/FREE input = {{0,1,NaN},{1,2,3}} WAVE output = SF_FormulaExecutor(DirectToFormulaParser("channels(AD1,DA2,3)")) REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) + + Make/FREE input = {{NaN}, {NaN}} + WAVE output = SF_FormulaExecutor(DirectToFormulaParser("channels()")) + REQUIRE_EQUAL_WAVES(input, output, mode = WAVE_DATA) + + try + SF_FormulaExecutor(DirectToFormulaParser("channels(unknown)")) + FAIL() + catch + PASS() + endtry End static Function testDifferentiales() @@ -1178,6 +1189,24 @@ static Function TestOperationSelect() Make/FREE/N=0 sweepTemplate WAVE sweepRef = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) + Make/FREE/N=(4, 3) dataRef + dataRef[][0] = sweepNo + dataRef[0, 1][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[2, 3][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 2, 3} // AD6, AD7, DA2, DA3 + str = "select(channels(),[" + num2istr(sweepNo) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + + Make/FREE/N=(2, 3) dataRef + dataRef[][0] = sweepNo + dataRef[0][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[1][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 2} // AD6, DA2 + str = "select(channels(2, 6),[" + num2istr(sweepNo) + "],all)" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + Make/FREE/N=(2, 3) dataRef dataRef[][0] = sweepNo dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) From 09f49dbd3016c17117366484e55fbc4a5e5c9e17 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 9 Apr 2022 00:18:19 +0200 Subject: [PATCH 25/49] SF: Docu adapted documentation for the fixed channels operation --- Packages/MIES/SweepFormulaHelp.ifn | Bin 69551 -> 69753 bytes Packages/doc/SweepFormula.rst | 9 +++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/SweepFormulaHelp.ifn b/Packages/MIES/SweepFormulaHelp.ifn index 9e65ad40a501b9d20a8222ce11b39b3a9a2b234d..8cce47f75a26ff33593a2fd58a588444960aa2de 100644 GIT binary patch delta 1853 zcmajge@L8l9Ki9<-Q4B;F-4|xE8>Zrzizjinw@Uh<;t3xQ?pt&r+Mntb)GkOcP?78 zvjt;+EIIx5N69duAyPt?5>m2k$dDmJhKw|^zA%Uc;tY~Xo3APLEaR&FKxCpyKtq8nur<3c63U1~sSu!`5b(FKE+> zgPPwJZ1wvBTF~wCY1P$b&axV-_Nb^K=F}Us_+0^-2N=w|Il317q{T(V*Z3sjYi+J~ z`Hcaa%8c2e!tn`t*JXo4Pj6#GPf_INqlkXD8QEs|cblCCmANZ9E?N@ku$fG{I$55m zE}Ki$SgtAhNi;V@os3RY%UPx^MPgTpDa9&mPF0sv64X*wq`{&7N=e&QooUrLvpDtK z9#{3N`f*#L-cFUIPDfiG&9{HQR_s%jvuq8@Gd4nJWEViDPmLW_i?l!^x{h}kPrfFgVV zqgA94Ui9M%BKL{J<1A)z59{c+@nP`*#{DD#FNQIShbYVxDMc^lv7Q+csmmgJ4C59` zvdIR67=itH5eKee9tU$o!WhR4){yjqNFM6ZfuAvrB}CapGEs|eOkx3&OEMuY7G&p& zX3i6_!i}@&!x-`o@I5;q+6~G@q#hKpp$u-E$IOciitKzc#466bBytV~4ptN+Sj9tl zU*;J?6I|vX1)_BqFacI!ImB|I9&H%JHSBnWrA0n2;u2O6^Qy=xJVZnx?+->{e~pRY zd0oVhFsAXt0~Cec6{&z97w{)4tGLHKtmE{1WQe3={9NNEW{_Xa z-Xn}(a0T|`ysQYK1Kk)tF4}d<^coVuZOp-3%l{=5AjTjvpRyArX-sWOyQx8X?iC=v_wvo(CP5nK5qB z#u}D^PxCKU*N+k1*dk%2%o%Mrw;ChntS&1`B5SvG3+7m{<$7cKUt8bp^?jaup3n2+ zp6B!ZD{TDmEo0wm!`c2dFXq25%g;(#G>BACGio9aQT}Ip|EIq;radJxXA)U#A&SSv zfHMdG{asIeIxMn&l|f!(!VgjmVkPEF%H`+(OZC!EmQ~APV}!qayFqOI^P5Ipns>vB zDallyZJbumTC-He+9WlcRij3Vl2&v}mCCm6DAR+@?x5c6?x517ceoEX>4CQAb#Kez zCVxjun_s`#)8cP`E~s||11FbLYuD zCY6$XDLG%#*iT`h?raOF-?MYo=Hxp@tvTx)p^q~~&SMVdERi(KB95*6_iEH4fXkSL zFu7jF^=Ex2U2oH9B4o{dNGtMA+hJN2)fXN_fWA(MIxO@ z+RPvv#T2w+5(Fiv!x4lriEBu^m;Is&eheT6bBRbM9B`r;?V%FUJ@<*UVh|IU!*v`f zB{NG!PrY9x4_^52Dh6=@>mOiJw4)bATSU$wimnGaQ7od!!AT&3C`{!dIzokH&OLGob)#SKh?Gnhc#lOp?&?&7|23>PqkKOuWLZgk=_CK1EJUeS~H zi9C%cGWK)l@Sqb>gl2ffVQduL(#SF66k=FF@c}l22%?xq{DA1jgT&%IVi32;I`|OA z5N5ChyGNu3J_J3YpP)R2C6C-D1~E&GPsFPA&!;Qhmuk{PlYn1cYqe>YzEWL>a!f{I zKL1ATK4jCP-_l~!&VHv}zmjnyd?lU3Kir5*qWboaH+#}eZ(I@Ccxzg-na)ps7P;$I bi)nfBi@&IoKAYy9R}&P$zef|k Date: Sat, 9 Apr 2022 00:44:00 +0200 Subject: [PATCH 26/49] SF: Add Utility functions to set a formula in SF NB and to execute a formula - Added SF_SetFormula to set a formula in the given databrowser notebook The previous formula is replaced. - Added SF_ExecuteFormula to execute a gormula independent of the SF notebook --- Packages/MIES/MIES_SweepFormula.ipf | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index c3f5e8d61d..5f90da762d 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -3128,3 +3128,28 @@ Function SF_PopMenuProc_OldCode(pa) : PopupMenuControl return 0 End + +// Sets a formula in the SweepFormula notebook of the given data/sweepbrowser +Function SF_SetFormula(string databrowser, string formula) + + string nb = BSP_GetSFFormula(databrowser) + ReplaceNotebookText(nb, formula) +End + +// Executes a given formula without changing the current SweepFormula notebook +Function/WAVE SF_ExecuteFormula(string formula, [string databrowser]) + + variable jsonId + + formula = SF_PreprocessInput(formula) + formula = SF_FormulaPreParser(formula) + jsonId = SF_FormulaParser(formula) + if(ParamIsDefault(databrowser)) + WAVE/Z out = SF_FormulaExecutor(jsonId) + else + WAVE/Z out = SF_FormulaExecutor(jsonId, graph = databrowser) + endif + JSON_Release(jsonId, ignoreErr=1) + + return out +End From 6190f434c5ba3c317be131c341eb4fd69e976193 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 9 Apr 2022 00:58:44 +0200 Subject: [PATCH 27/49] DataBrowser: Use SF_SetFormula in DB and update SF notebook initial string - also raised DataBrowser version --- Packages/MIES/MIES_Constants.ipf | 2 +- Packages/MIES/MIES_DataBrowser.ipf | 5 +- Packages/MIES/MIES_DataBrowser_Macro.ipf | 2300 +++++++++++----------- 3 files changed, 1109 insertions(+), 1198 deletions(-) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 757ea748c2..2bcdc1c3c7 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -638,7 +638,7 @@ Constant HARDWARE_DAC_EXTERNAL_TRIGGER = 0x1 /// Used to upgrade the GuiStateWave as well as the DA Ephys panel Constant DA_EPHYS_PANEL_VERSION = 55 -Constant DATA_SWEEP_BROWSER_PANEL_VERSION = 39 +Constant DATA_SWEEP_BROWSER_PANEL_VERSION = 40 Constant WAVEBUILDER_PANEL_VERSION = 12 Constant ANALYSISBROWSER_PANEL_VERSION = 1 diff --git a/Packages/MIES/MIES_DataBrowser.ipf b/Packages/MIES/MIES_DataBrowser.ipf index a9ab07b986..9d9bb366e5 100644 --- a/Packages/MIES/MIES_DataBrowser.ipf +++ b/Packages/MIES/MIES_DataBrowser.ipf @@ -39,7 +39,7 @@ End /// after GUI editor adapted controls in development process Function DB_ResetAndStoreCurrentDBPanel() string device, bsPanel, scPanel, shPanel, recreationCode - string sfFormula, sfJSON, descNB + string sfJSON, descNB device = GetMainWindow(GetCurrentWindow()) if(!windowExists(device)) @@ -222,8 +222,7 @@ Function DB_ResetAndStoreCurrentDBPanel() // settings history CheckBox check_limit_x_selected_sweeps WIN = $shPanel, value=0 - sfFormula = BSP_GetSFFormula(device) - ReplaceNotebookText(sfFormula, "data(\rcursors(A,B),\rselect(channels(AD),sweeps())\r)") + SF_SetFormula(device, "data(\rcursors(A,B),\rselect(channels(AD),sweeps())\r)") sfJSON = BSP_GetSFJSON(device) ReplaceNotebookText(sfJSON, "") diff --git a/Packages/MIES/MIES_DataBrowser_Macro.ipf b/Packages/MIES/MIES_DataBrowser_Macro.ipf index 4074ed8f08..b654d0221f 100644 --- a/Packages/MIES/MIES_DataBrowser_Macro.ipf +++ b/Packages/MIES/MIES_DataBrowser_Macro.ipf @@ -11,1291 +11,1211 @@ Window DataBrowser() : Graph PauseUpdate; Silent 1 // building window... - Display /W=(487.5,83,918,420.5)/K=1 as "DataBrowser" - Button button_BSP_open,pos={3.00,3.00},size={24.00,24.00},disable=1,proc=DB_ButtonProc_Panel - Button button_BSP_open,title="<<",help={"Open Side Panel"} - Button button_BSP_open,userdata(ResizeControlsInfo)=A"!!,>M!!#8L!!#=#!!#=#z!!#](Aon\"Qzzzzzzzzzzzzzz!!#](Aon\"Qzz" - Button button_BSP_open,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kM!!#8L!!#=#!!#=#z!!#](Aon\"Qzzzzzzzzzzzzzz!!#](Aon\"Qzz" + Button button_BSP_open,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kn!!#BEz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - Slider slider_BrowserSettings_dDAQ,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kZz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_calc,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kn!!#BEz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + Slider slider_BrowserSettings_dDAQ,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kZz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_calc,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#BqJ,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - GroupBox group_properties_sweepFormula,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#N3Bk1ct9jqaR6>q*JDf>[Vzzzzzzzz" - GroupBox group_properties_sweepFormula,userdata(ResizeControlsInfo)+=A"zzz!!#N3Bk1ct9jqaR6>q*8Dfg)>D#aP9zzzzzzzzzz!!!" + GroupBox group_channels,userdata(tabnum)= "0",userdata(tabcontrol)= "Settings" + GroupBox group_channels,userdata(ResizeControlsInfo)= A"!!,D_!!#=;!!#BH!!#?%z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_channels,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#BqJ,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + GroupBox group_properties_sweepFormula,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#N3Bk1ct9jqaR6>q*JDf>[Vzzzzzzzz" + GroupBox group_properties_sweepFormula,userdata(ResizeControlsInfo) += A"zzz!!#N3Bk1ct9jqaR6>q*8Dfg)>D#aP9zzzzzzzzzz!!!" TabControl Settings,pos={0.00,0.00},size={446.00,21.00},proc=ACL_DisplayTab - TabControl Settings,help={"Main navigation tab"},userdata(currenttab)="0" - TabControl Settings,userdata(finalhook)="BSP_MainTabControlFinal" - TabControl Settings,userdata(ResizeControlsInfo)=A"!!*'\"z!!#CD!!#<`z!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAeBE3-fz" - TabControl Settings,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#>Vz!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" - GroupBox group_enable_sweeps,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" - GroupBox group_enable_sweeps,userdata(ResizeControlsInfo)+=A"zzz!!#u:Duafn!,c4SCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - GroupBox group_enable_channels,pos={31.00,25.00},size={380.00,380.00},disable=1 - GroupBox group_enable_channels,title="Channel Selection",userdata(tabnum)="2" - GroupBox group_enable_channels,userdata(tabcontrol)="Settings" - GroupBox group_enable_channels,userdata(ResizeControlsInfo)=A"!!,C\\!!#=+!!#C#!!#C#z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_enable_channels,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#>Vz!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - GroupBox group_enable_artifact,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#>Vz!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" + GroupBox group_enable_sweeps,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" + GroupBox group_enable_sweeps,userdata(ResizeControlsInfo) += A"zzz!!#u:Duafn!,c4SCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + GroupBox group_enable_channels,pos={31.00,25.00},size={380.00,380.00},disable=1,title="Channel Selection" + GroupBox group_enable_channels,userdata(tabnum)= "2" + GroupBox group_enable_channels,userdata(tabcontrol)= "Settings" + GroupBox group_enable_channels,userdata(ResizeControlsInfo)= A"!!,C\\!!#=+!!#C#!!#C#z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_enable_channels,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#>Vz!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + GroupBox group_enable_artifact,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#BqJ,fQL!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" - GroupBox group_properties_sweeps,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#BqJ,fQL!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" + GroupBox group_properties_sweeps,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#BqJ,fQL!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" - GroupBox group_properties_artefact,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k2!!#>>!!#A]z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_channelSel_DA,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#BqJ,fQL!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" + GroupBox group_properties_artefact,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k2!!#>>!!#A]z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_channelSel_DA,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kVz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_enable_pulse,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k6!!#>F!!#A]z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_channelSel_HEADSTAGE,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kVz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_enable_pulse,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k6!!#>F!!#A]z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_channelSel_HEADSTAGE,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k6!!#?;!!#A[z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_channelSel_AD,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k6!!#?;!!#A[z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_channelSel_AD,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kf!!#<`z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - Button button_RemoveRanges,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kf!!#<`z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + Button button_RemoveRanges,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kB!!#B!!#J!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_OVS,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_OVS,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_AR,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_AR,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_PA,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_PA,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kN!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_dDAQ,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kN!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_dDAQ,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kZ!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_TA,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kZ!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_TA,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kN!!#N!!#2!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_Display_VisibleXrange,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k2!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_Display_VisibleXrange,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kj!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_Display_EqualYrange,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kj!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_Display_EqualYrange,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#A.!!#@j!!#!!#A.!!#@j!!#n!!#?!!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_splitTTL,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kn!!#?!!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_splitTTL,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#>Vz!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - GroupBox group_enable_sweepFormula,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#N3Bk1ctAStpcCh5qOGZ8U#zzzzzzzz" - GroupBox group_enable_sweepFormula,userdata(ResizeControlsInfo)+=A"zzz!!#N3Bk1ctAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - SetVariable setvar_sweepFormula_parseResult,pos={28.00,403.00},size={405.00,18.00},disable=1 - SetVariable setvar_sweepFormula_parseResult,userdata(tabnum)="5" - SetVariable setvar_sweepFormula_parseResult,userdata(tabcontrol)="Settings" - SetVariable setvar_sweepFormula_parseResult,userdata(ResizeControlsInfo)=A"!!,CD!!#C.J,hsZJ,hlsz!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - SetVariable setvar_sweepFormula_parseResult,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#?(FEDG!!#>Vz!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + GroupBox group_enable_sweepFormula,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#N3Bk1ctAStpcCh5qOGZ8U#zzzzzzzz" + GroupBox group_enable_sweepFormula,userdata(ResizeControlsInfo) += A"zzz!!#N3Bk1ctAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + SetVariable setvar_sweepFormula_parseResult,pos={28.00,403.00},size={405.00,18.00},disable=3 + SetVariable setvar_sweepFormula_parseResult,userdata(tabnum)= "5" + SetVariable setvar_sweepFormula_parseResult,userdata(tabcontrol)= "Settings" + SetVariable setvar_sweepFormula_parseResult,userdata(ResizeControlsInfo)= A"!!,CD!!#C.J,hsZJ,hlsz!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + SetVariable setvar_sweepFormula_parseResult,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#?(FEDG!!#BlJ,fQL!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" - ListBox list_dashboard,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#BlJ,fQL!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" + ListBox list_dashboard,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_DS,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#N3Bk1ctAStpcCh5qOGZ8U#zzzzzzzz" - CheckBox check_BrowserSettings_DS,userdata(ResizeControlsInfo)+=A"zzz!!#N3Bk1ctAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - CheckBox check_BrowserSettings_DS,value=0 + CheckBox check_BrowserSettings_DS,userdata(tabnum)= "7" + CheckBox check_BrowserSettings_DS,userdata(tabcontrol)= "Settings" + CheckBox check_BrowserSettings_DS,userdata(ResizeControlsInfo)= A"!!,GJ!!#>J!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_DS,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#N3Bk1ctAStpcCh5qOGZ8U#zzzzzzzz" + CheckBox check_BrowserSettings_DS,userdata(ResizeControlsInfo) += A"zzz!!#N3Bk1ctAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + CheckBox check_BrowserSettings_DS,value= 0 GroupBox group_enable_dashboard,pos={5.00,25.00},size={434.00,60.00},disable=1 - GroupBox group_enable_dashboard,userdata(tabnum)="7" - GroupBox group_enable_dashboard,userdata(tabcontrol)="Settings" - GroupBox group_enable_dashboard,userdata(ResizeControlsInfo)=A"!!,?X!!#=+!!#C>!!#?)z!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" - GroupBox group_enable_dashboard,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#?)z!!#](Aon\"Qzzzzzzzzzzzzzz!!#o2B4uAezz" + GroupBox group_enable_dashboard,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k^!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_DB_Passed,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k^!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_DB_Passed,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kn!!#>J!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_DB_Failed,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kn!!#>J!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_DB_Failed,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kJ!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_SF,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#N3Bk1ctAStpcCh5qOGZ8U#zzzzzzzz" - CheckBox check_BrowserSettings_SF,userdata(ResizeControlsInfo)+=A"zzz!!#N3Bk1ctAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - CheckBox check_BrowserSettings_SF,value=0 - CheckBox check_channelSel_HEADSTAGE_ALL,pos={79.00,225.00},size={30.00,15.00},disable=1,proc=BSP_CheckProc_ChangedSetting - CheckBox check_channelSel_HEADSTAGE_ALL,title="All" + CheckBox check_BrowserSettings_SF,userdata(tabnum)= "5" + CheckBox check_BrowserSettings_SF,userdata(tabcontrol)= "Settings" + CheckBox check_BrowserSettings_SF,userdata(ResizeControlsInfo)= A"!!,GJ!!#>J!!#>Z!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_SF,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#N3Bk1ctAStpcCh5qOGZ8U#zzzzzzzz" + CheckBox check_BrowserSettings_SF,userdata(ResizeControlsInfo) += A"zzz!!#N3Bk1ctAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + CheckBox check_BrowserSettings_SF,value= 0 + CheckBox check_channelSel_HEADSTAGE_ALL,pos={79.00,225.00},size={30.00,15.00},disable=1,proc=BSP_CheckProc_ChangedSetting,title="All" CheckBox check_channelSel_HEADSTAGE_ALL,help={"Toggle the display of all headstages"} - CheckBox check_channelSel_HEADSTAGE_ALL,userdata(tabcontrol)="Settings" - CheckBox check_channelSel_HEADSTAGE_ALL,userdata(tabnum)="2" - CheckBox check_channelSel_HEADSTAGE_ALL,userdata(ResizeControlsInfo)=A"!!,EX!!#Ap!!#=S!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_channelSel_HEADSTAGE_ALL,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" - CheckBox check_channelSel_HEADSTAGE_ALL,userdata(ResizeControlsInfo)+=A"zzz!!#u:DuaGlAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - CheckBox check_channelSel_HEADSTAGE_ALL,value=0 - CheckBox check_channelSel_DA_All,pos={143.00,225.00},size={30.00,15.00},disable=1,proc=BSP_CheckProc_ChangedSetting - CheckBox check_channelSel_DA_All,title="All" + CheckBox check_channelSel_HEADSTAGE_ALL,userdata(tabcontrol)= "Settings" + CheckBox check_channelSel_HEADSTAGE_ALL,userdata(tabnum)= "2" + CheckBox check_channelSel_HEADSTAGE_ALL,userdata(ResizeControlsInfo)= A"!!,EX!!#Ap!!#=S!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_channelSel_HEADSTAGE_ALL,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" + CheckBox check_channelSel_HEADSTAGE_ALL,userdata(ResizeControlsInfo) += A"zzz!!#u:DuaGlAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + CheckBox check_channelSel_HEADSTAGE_ALL,value= 0 + CheckBox check_channelSel_DA_All,pos={143.00,225.00},size={30.00,15.00},disable=1,proc=BSP_CheckProc_ChangedSetting,title="All" CheckBox check_channelSel_DA_All,help={"Toggle the display of all DA channels"} - CheckBox check_channelSel_DA_All,userdata(tabcontrol)="Settings" - CheckBox check_channelSel_DA_All,userdata(tabnum)="2" - CheckBox check_channelSel_DA_All,userdata(ResizeControlsInfo)=A"!!,Ft!!#Ap!!#=S!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_channelSel_DA_All,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" - CheckBox check_channelSel_DA_All,userdata(ResizeControlsInfo)+=A"zzz!!#u:DuaGlAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - CheckBox check_channelSel_DA_All,value=0 - CheckBox check_channelSel_AD_All,pos={210.00,225.00},size={30.00,15.00},disable=1,proc=BSP_CheckProc_ChangedSetting - CheckBox check_channelSel_AD_All,title="All" + CheckBox check_channelSel_DA_All,userdata(tabcontrol)= "Settings" + CheckBox check_channelSel_DA_All,userdata(tabnum)= "2" + CheckBox check_channelSel_DA_All,userdata(ResizeControlsInfo)= A"!!,Ft!!#Ap!!#=S!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_channelSel_DA_All,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" + CheckBox check_channelSel_DA_All,userdata(ResizeControlsInfo) += A"zzz!!#u:DuaGlAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + CheckBox check_channelSel_DA_All,value= 0 + CheckBox check_channelSel_AD_All,pos={210.00,225.00},size={30.00,15.00},disable=1,proc=BSP_CheckProc_ChangedSetting,title="All" CheckBox check_channelSel_AD_All,help={"Toggle the display of all AD channels"} - CheckBox check_channelSel_AD_All,userdata(tabcontrol)="Settings" - CheckBox check_channelSel_AD_All,userdata(tabnum)="2" - CheckBox check_channelSel_AD_All,userdata(ResizeControlsInfo)=A"!!,Gb!!#Ap!!#=S!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_channelSel_AD_All,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" - CheckBox check_channelSel_AD_All,userdata(ResizeControlsInfo)+=A"zzz!!#u:DuaGlAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" - CheckBox check_channelSel_AD_All,value=0 - CheckBox check_pulseAver_searchFailedPulses,pos={236.00,340.00},size={119.00,15.00},disable=1,proc=PA_CheckProc_Common - CheckBox check_pulseAver_searchFailedPulses,title="Search failed pulses" + CheckBox check_channelSel_AD_All,userdata(tabcontrol)= "Settings" + CheckBox check_channelSel_AD_All,userdata(tabnum)= "2" + CheckBox check_channelSel_AD_All,userdata(ResizeControlsInfo)= A"!!,Gb!!#Ap!!#=S!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_channelSel_AD_All,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:DuaGlAStpcCh5qOGZ8U#zzzzzzzz" + CheckBox check_channelSel_AD_All,userdata(ResizeControlsInfo) += A"zzz!!#u:DuaGlAStpcCh5qOGX?=jFDl!rzzzzzzzzzz!!!" + CheckBox check_channelSel_AD_All,value= 0 + CheckBox check_pulseAver_searchFailedPulses,pos={236.00,340.00},size={119.00,15.00},disable=1,proc=PA_CheckProc_Common,title="Search failed pulses" CheckBox check_pulseAver_searchFailedPulses,help={"Failed pulses don't have a signal above the given level in the diagonal elements"} - CheckBox check_pulseAver_searchFailedPulses,userdata(tabnum)="4" - CheckBox check_pulseAver_searchFailedPulses,userdata(tabcontrol)="Settings" - CheckBox check_pulseAver_searchFailedPulses,userdata(ResizeControlsInfo)=A"!!,H'!!#Bd!!#@R!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_pulseAver_searchFailedPulses,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#Biz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - GroupBox group_pulseAver_general,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#Biz!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + GroupBox group_pulseAver_general,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]k!!#@4!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_pulseAver_showTraces,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]k!!#@4!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_pulseAver_showTraces,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kn!!#@0!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" - CheckBox check_BrowserSettings_VisEpochs,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kn!!#@0!!#<(z!!#`-A7TLfzzzzzzzzzzzzzz!!#r+D.OhkBk2=!z" + CheckBox check_BrowserSettings_VisEpochs,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - SetWindow kwTopWin,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + SetWindow kwTopWin,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kb7Bf:op@iaf>^JRCF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - SetWindow kwTopWin,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + SetWindow kwTopWin,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - SetWindow kwTopWin,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + SetWindow kwTopWin,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" - SetWindow kwTopWin,userdata(ResizeControlsInfo)+=A"zzzzzzzzzzzz!!#u:Du]kF!!#C#!!#B[J,fQL!!#](Aon#azzzzzzzzzzzzzz!!#o2B4uAeBk2=!z" + SetWindow kwTopWin,userdata(ResizeControlsInfo) += A"zzzzzzzzzzzz!!#u:Du]kjz!!#N3Bk1ctjz!!#N3Bk1ct!!#=k!!#@o!!#!!#=k!!#@o!!# Date: Sat, 9 Apr 2022 01:15:54 +0200 Subject: [PATCH 28/49] SF: use SF_SetFormula and SF_ExecuteFormula in other MIES code - replaced SF_ExecuteFormula in locations where formerly SF_FormulaExecutor was called with a formula string that was parsed. --- Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf | 13 +++++-------- Packages/MIES/MIES_SweepFormula.ipf | 9 +++++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf index da89b8f446..210a1cf2a7 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf @@ -4427,7 +4427,7 @@ Function PSQ_PipetteInBath(string device, struct AnalysisFunction_V3& s) variable multiplier, chunk, baselineQCPassed, ret, DAC, pipetteResistanceQCPassed, samplingFrequencyQCPassed, ovsState variable sweepsInSet, passesInSet, acquiredSweepsInSet, sweepPassed, setPassed, numSweepsFailedAllowed, failsInSet variable maxPipetteResistance, minPipetteResistance, expectedNumTestpulses, numTestPulses, pipetteResistance - string key, ctrl, stimset, msg, databrowser, bsPanel, formula_nb + string key, ctrl, stimset, msg, databrowser, bsPanel switch(s.eventType) case PRE_DAQ_EVENT: @@ -4461,11 +4461,10 @@ Function PSQ_PipetteInBath(string device, struct AnalysisFunction_V3& s) databrowser = DB_GetBoundDataBrowser(device) bsPanel = BSP_GetPanel(databrowser) - formula_nb = BSP_GetSFFormula(databrowser) - /// @todo: The call should work on the last sweep acquired. Once this number is retrieved it can be set directly /// in the formula string replacing sweeps(). The the OVS disabled/enable procedure can be skipped. - ReplaceNotebookText(formula_nb, "store(\"Steady state resistance\", tp(ss, select(channels(AD), sweeps()), [0]))") + /// By using SF_ExecuteFormula instead it can also executed right here. + SF_SetFormula(databrowser, "store(\"Steady state resistance\", tp(ss, select(channels(AD), sweeps()), [0]))") PGC_SetAndActivateControl(bsPanel, "check_BrowserSettings_SF", val = 1) @@ -5019,7 +5018,7 @@ Function PSQ_SealEvaluation(string device, struct AnalysisFunction_V3& s) variable multiplier, chunk, baselineQCPassed, ret, DAC, samplingFrequencyQCPassed, sealResistanceMax variable sweepsInSet, passesInSet, acquiredSweepsInSet, sweepPassed, setPassed, numSweepsFailedAllowed, failsInSet, ovsState variable expectedNumTestpulses, numTestPulses, sealResistanceA, sealResistanceB, sealResistanceQCPassed, testpulseGroupSel, sealThreshold - string key, ctrl, stimset, msg, databrowser, bsPanel, formula, formula_nb, pipetteResistanceStr, sweepStr + string key, ctrl, stimset, msg, databrowser, bsPanel, formula, pipetteResistanceStr, sweepStr string sealResistanceGroupAStr, sealResistanceGroupBStr switch(s.eventType) @@ -5055,8 +5054,6 @@ Function PSQ_SealEvaluation(string device, struct AnalysisFunction_V3& s) bsPanel = BSP_GetPanel(databrowser) - formula_nb = BSP_GetSFFormula(databrowser) - testpulseGroupSel = PSQ_SE_GetTestpulseGroupSelection(s.params) /// @todo: Rework to use non-displayed sweeps, once https://github.com/AllenInstitute/MIES/pull/1256 is merged @@ -5082,7 +5079,7 @@ Function PSQ_SealEvaluation(string device, struct AnalysisFunction_V3& s) ASSERT(0, "Invalid testpulseGroupSel: " + num2str(testpulseGroupSel)) endswitch - ReplaceNotebookText(formula_nb, formula) + SF_SetFormula(databrowser, formula) PGC_SetAndActivateControl(bsPanel, "check_BrowserSettings_SF", val = 1) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 5f90da762d..d67309c61f 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -827,7 +827,7 @@ static Function SF_FormulaPlotter(string graph, string formula, [DFREF dfr, vari WaveClear wvX xFormula = formulaPairs[j][%FORMULA_X] if(!IsEmpty(xFormula)) - WAVE/Z wv = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser(xFormula)), graph = graph) + WAVE/Z wv = SF_ExecuteFormula(xFormula, databrowser = graph) SF_Assert(WaveExists(wv), "Error in x part of formula.") xPoints = DimSize(wv, ROWS) dim1X = max(1, DimSize(wv, COLS)) @@ -844,7 +844,7 @@ static Function SF_FormulaPlotter(string graph, string formula, [DFREF dfr, vari WAVE wvX = GetSweepFormulaX(dfr, j) endif - WAVE/Z wv = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser(formulaPairs[j][%FORMULA_Y])), graph = graph) + WAVE/Z wv = SF_ExecuteFormula(formulaPairs[j][%FORMULA_Y], databrowser = graph) SF_Assert(WaveExists(wv), "Error in y part of formula.") yPoints = DimSize(wv, ROWS) dim1Y = max(1, DimSize(wv, COLS)) @@ -1792,7 +1792,8 @@ static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string WAVE/T/Z cursorInfos = GetCursorInfos(graph) - WAVE select = SF_FormulaExecutor(SF_FormulaParser(SF_FormulaPreParser("select(channels(AD), sweeps(),displayed)")), graph = graph) + WAVE/Z select = SF_ExecuteFormula("select(channels(AD), sweeps(),displayed)", databrowser = graph) + SF_ASSERT(WaveExists(select), "Select did not return data.") dimSweep = FindDimLabel(select, COLS, "SWEEP") Duplicate/FREE/RMD=[][dimSweep] select, wTmp Redimension/N=(-1) wTmp @@ -1803,7 +1804,7 @@ static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string endif values[0][%$"Sweep Formula displayed sweeps"][INDEP_HEADSTAGE] = NumericWaveToList(sweeps, ";") - WAVE/Z channels = SF_ExecuteFormula(graph, "channels()") + WAVE/Z channels = SF_ExecuteFormula("channels()", databrowser = graph) if(WaveExists(sweeps) && WaveExists(channels)) WAVE/Z activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) From c3ccc08295b118362fa2fd7d24a841fa658fb097 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 9 Apr 2022 01:47:37 +0200 Subject: [PATCH 29/49] SF: Allow select operation to be called with no arguments - select uses then as default select(channels(), sweeps()) --- Packages/MIES/MIES_SweepFormula.ipf | 42 ++++++++++++++++++----------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index d67309c61f..d34d6455f1 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2598,31 +2598,35 @@ static Function/WAVE SF_OperationSweeps(variable jsonId, string jsonPath, string return out End -/// `select(array channels, array sweeps, [string mode])` +/// `select([array channels, array sweeps, [string mode]])` /// /// returns n x 3 with columns [sweepNr][channelType][channelNr] static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string graph) variable numIndices - string mode + string mode = "displayed" - numIndices = JSON_GetArraySize(jsonID, jsonPath) - - SF_ASSERT(numIndices >= 2 && numIndices <= 3, "Function requires 2 or 3 arguments.") SF_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") - WAVE channels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) - SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - - WAVE sweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - if(numIndices == 3) - WAVE/T wMode = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) - SF_ASSERT(IsTextWave(wMode), "mode parameter can not be a number. Use \"all\" or \"displayed\".") - SF_ASSERT(!DimSize(wMode, COLS) && DimSize(wMode, ROWS) == 1, "mode must be either displayed or all.") - mode = wMode[0] + numIndices = SF_GetNumberOfArguments(jsonId, jsonPath) + if(!numIndices) + WAVE channels = SF_ExecuteFormula("channels()", databrowser=graph) + WAVE sweeps = SF_ExecuteFormula("sweeps()", databrowser=graph) else - mode = "displayed" + SF_ASSERT(numIndices >= 2 && numIndices <= 3, "Function requires None, 2 or 3 arguments.") + WAVE channels = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) + SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") + + WAVE sweeps = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") + + if(numIndices == 3) + WAVE/T wMode = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) + SF_ASSERT(IsTextWave(wMode), "mode parameter can not be a number. Use \"all\" or \"displayed\".") + SF_ASSERT(!DimSize(wMode, COLS) && DimSize(wMode, ROWS) == 1, "mode must not be an array with multiple options.") + mode = wMode[0] + SF_ASSERT(!CmpStr(mode, "displayed") || !CmpStr(mode, "all"), "mode must be \"all\" or \"displayed\".") + endif endif WAVE activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) @@ -3154,3 +3158,9 @@ Function/WAVE SF_ExecuteFormula(string formula, [string databrowser]) return out End + +// returns number of operation arguments +static Function SF_GetNumberOfArguments(variable jsonId, string jsonPath) + + return JSON_GetType(jsonId, jsonPath + "/0") == JSON_NULL ? 0 : JSON_GetArraySize(jsonID, jsonPath) +End From c96c305b50ffb56b58e5249a87cc660a2c52edbc Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 9 Apr 2022 02:42:41 +0200 Subject: [PATCH 30/49] SF: Add test for select operation without arguments select() --- Packages/MIES/MIES_SweepFormula.ipf | 12 +++++++++--- Packages/Testing-MIES/UTF_SweepFormula.ipf | 12 ++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index d34d6455f1..847a45aa4a 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2645,7 +2645,7 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string return out End -/// `data(array range, array selectData)` +/// `data(array range[, array selectData])` /// /// returns [sweepData][sweeps][channelTypeNumber] for all sweeps selected by selectData static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string graph) @@ -2655,7 +2655,8 @@ static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string g numIndices = JSON_GetArraySize(jsonID, jsonPath) SF_ASSERT(!IsEmpty(graph), "Graph for extracting sweeps not specified.") - SF_ASSERT(numIndices == 2, "Function requires 2 arguments.") + SF_ASSERT(numIndices >= 1, "data function requires at least 1 argument.") + SF_ASSERT(numIndices <= 2, "data function has maximal 2 arguments.") WAVE range = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) if(IsTextWave(range)) @@ -2665,7 +2666,12 @@ static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string g range[][][] = !IsNaN(range[p][q][r]) ? range[p][q][r] : (p == 0 ? -1 : 1) * inf endif - WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + if(numIndices == 2) + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + else + WAVE selectData = SF_ExecuteFormula("select()", databrowser = graph) + endif + if(SF_IsDefaultEmptyWave(selectData)) return selectData endif diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 2a1764ae53..342a743ef0 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1284,6 +1284,18 @@ static Function TestOperationSelect() endfor sweepNo = 0 + Make/FREE/N=(8, 3) dataRef + dataRef[0, 3][0] = sweepNo + dataRef[4, 7][0] = sweepNo + 1 + dataRef[0, 1][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[2, 3][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) + dataRef[4, 5][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) + dataRef[6, 7][1] = WhichListItem("DA", XOP_CHANNEL_NAMES) + dataRef[][2] = {6, 7, 2, 3, 6, 7, 2, 3} + str = "select()" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA | DIMENSION_SIZES) + Make/FREE/N=(4, 3) dataRef dataRef[][0] = {sweepNo, sweepNo, sweepNo + 1, sweepNo + 1} dataRef[][1] = WhichListItem("AD", XOP_CHANNEL_NAMES) From 7cf9d8e4615e19ba8068276b29f0f11e3d54c3d9 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sat, 9 Apr 2022 02:43:21 +0200 Subject: [PATCH 31/49] SF: Add Tests for data without optional arguments. - for data(range) and data(range, select()) --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 37 ++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 342a743ef0..749cc4bf7c 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1371,16 +1371,20 @@ static Function TestOperationSelect() End -static Function TestDataOperation() +static Function TestOperationData() - variable numChannels, sweepNo, rStart, rDelta - string str, epochStr + variable i, j, numChannels, sweepNo, rStart, rDelta + string str, epochStr, name, trace string win = DATABROWSER_WINDOW_TITLE variable mode = DATA_ACQUISITION_MODE + variable numSweeps = 2 + variable dataSize = 10 variable rangeStart0 = 3 variable rangeEnd0 = 6 variable rangeStart1 = 1 variable rangeEnd1 = 8 + string channelTypeList = "DA;AD;DA;AD;" + string channelNumberList = "2;6;3;7;" string device = HW_ITC_BuildDeviceString(StringFromList(0, DEVICE_TYPES_ITC), StringFromList(0, DEVICE_NUMBERS)) Make/FREE/T/N=(1, 1) epochKeys = EPOCHS_ENTRY_KEY @@ -1490,6 +1494,33 @@ static Function TestDataOperation() str = "data(cursors(A,B),select(channels(AD),[" + num2istr(sweepNo) + "," + num2istr(sweepNo + 2) + "],all))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + + // Setup graph with equivalent data + TUD_Clear(win) + + Make/FREE/N=(dataSize, numSweeps, numChannels) input = q + p^r + for(i = 0; i < numSweeps; i += 1) + sweepNo = i + for(j = 0; j < numChannels; j += 1) + name = UniqueName("data", 1, 0) + trace = "trace_" + name + Extract input, $name, q == i && r == j + WAVE wv = $name + AppendToGraph/W=$win wv/TN=$trace + TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber"}, \ + {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", StringFromList(j, channelTypeList), StringFromList(j, channelNumberList), num2istr(sweepNo)}) + endfor + endfor + + Make/FREE/N=(DimSize(sweepRef, ROWS), 2, numChannels) dataRef + dataRef[][][] = sweepRef[p] + str = "data(cursors(A,B),select())" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + str = "data(cursors(A,B))" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(dataRef, data, mode = WAVE_DATA) + End Function TestLabNotebook() From 4607c1e440ac01506098f6f126b17378eaad839e Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 10 Apr 2022 16:30:03 +0200 Subject: [PATCH 32/49] SF: Change in tp operation that select is optional --- Packages/MIES/MIES_SweepFormula.ipf | 36 ++++++++++++++++------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 847a45aa4a..27c3d7a4a3 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1884,7 +1884,7 @@ static Function/WAVE SF_FilterEpochs(WAVE/Z epochs, WAVE/Z ignoreTPs) return epochs End -// tp(string type, array selectData, [array ignoreTPs]) +// tp(string type[, array selectData[, array ignoreTPs]]) // returns 3D wave in the layout: result x sweeps x channels static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string graph) @@ -1896,8 +1896,25 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra string baselineUnit = "" STRUCT TPAnalysisInput tpInput - numArgs = JSON_GetArraySize(jsonID, jsonPath) - SF_ASSERT(numArgs == 2 || numArgs == 3, "tp requires 2 or 3 arguments") + numArgs = SF_GetNumberOfArguments(jsonId, jsonPath) + SF_ASSERT(numArgs >= 1 || numArgs <= 3, "tp requires 1 to 3 arguments") + + if(numArgs == 3) + WAVE ignoreTPs = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) + SF_ASSERT(DimSize(ignoreTPs, COLS) < 2, "ignoreTPs must be one-dimensional.") + SF_ASSERT(IsNumericWave(ignoreTPs), "ignoreTPs parameter must be numeric") + else + WAVE/Z ignoreTPs + endif + + if(numArgs >= 2) + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + else + WAVE selectData = SF_ExecuteFormula("select()", databrowser = graph) + endif + SF_ASSERT(!SF_IsDefaultEmptyWave(selectData), "No valid sweep/channels combination found.") + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") WAVE wType = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) SF_ASSERT(DimSize(wType, ROWS) == 1, "Too many input values for parameter name") @@ -1920,19 +1937,6 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra outType = wType[0] endif - WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - SF_ASSERT(!SF_IsDefaultEmptyWave(selectData), "No valid sweep/channels combination found.") - SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") - SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") - - if(numArgs == 3) - WAVE ignoreTPs = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) - SF_ASSERT(DimSize(ignoreTPs, COLS) < 2, "ignoreTPs must be one-dimensional.") - SF_ASSERT(IsNumericWave(ignoreTPs), "ignoreTPs parameter must be numeric") - else - WAVE/Z ignoreTPs - endif - WAVE/Z activeChannels WAVE/Z sweeps [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) From d478c6eaa548de4e02986768ffebdd30541882ac Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 10 Apr 2022 16:46:28 +0200 Subject: [PATCH 33/49] SF: Add tests for optional select in tp operation --- .../Testing-MIES/UTF_SweepFormulaHardware.ipf | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf index 32c2f60053..137c14180e 100644 --- a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf @@ -138,7 +138,7 @@ static Function TestSweepFormulaTP(string device) PGC_SetAndActivateControl(dbPanel, "check_BrowserSettings_OVS", val = 1) PGC_SetAndActivateControl(dbPanel, "popup_overlaySweeps_select", str = "All") - formula = "tp(0)" + formula = "tp()" try WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) FAIL() @@ -198,14 +198,19 @@ static Function TestSweepFormulaTP(string device) WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, {NaN}, mode=WAVE_DATA) + Make/FREE/D/N=(1, 3, 2) wRef formula = "tp(ss, select(channels(AD), sweeps()))" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) - Make/FREE/D/N=(1, 3, 2) wRef CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_SIZES) - PGC_SetAndActivateControl(dbPanel, "check_BrowserSettings_DAC", val=1) - formula = "tp(ss, select(channels(DA), sweeps()))" + formula = "tp(ss, select())" + WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) + CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_SIZES) + + formula = "tp(ss)" WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) + CHECK_EQUAL_WAVES(tpResult, wRef, mode=DIMENSION_SIZES) + Make/FREE/D/N=(1, 3, 2) wRef = 1000 SetDimLabel COLS, 0, sweep0, wRef SetDimLabel COLS, 1, sweep1, wRef @@ -213,6 +218,9 @@ static Function TestSweepFormulaTP(string device) SetDimLabel LAYERS, 0, DA0, wRef SetDimLabel LAYERS, 1, DA1, wRef SetScale d, 0, 0, "MΩ", wRef + PGC_SetAndActivateControl(dbPanel, "check_BrowserSettings_DAC", val=1) + formula = "tp(ss, select(channels(DA), sweeps()))" + WAVE tpResult = SF_FormulaExecutor(DirectToFormulaParser(formula), graph=graph) CHECK_EQUAL_WAVES(tpResult, wRef, tol = 1e-12) formula = "tp(inst, select(channels(DA), sweeps()))" From e091acd689e27ff0b80b95bd281d60f0900d27d9 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 10 Apr 2022 23:23:04 +0200 Subject: [PATCH 34/49] SF: Change operation epochs to allow select argument to be optional --- Packages/MIES/MIES_SweepFormula.ipf | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 27c3d7a4a3..fd36d3f60d 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2094,27 +2094,15 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra return out End -// epochs(string shortName, array selectData, [string type]) +// epochs(string shortName[, array selectData, [string type]]) // returns 2xN wave for type = range except for a single range result static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string graph) variable numArgs, i, j, k, epType, sweepCnt, activeChannelCnt, outCnt, index, numEpochs, sweepNo string str, epName, epShortName - numArgs = JSON_GetArraySize(jsonID, jsonPath) - SF_ASSERT(numArgs >= 2, "epochs requires at least 2 arguments") - SF_ASSERT(numArgs <= 3, "epochs requires at most 3 arguments") - - WAVE/T epochName = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) - SF_ASSERT(DimSize(epochName, ROWS) == 1, "Too many input values for parameter name") - SF_ASSERT(IsTextWave(epochName), "name parameter must be textual") - - WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - if(SF_IsDefaultEmptyWave(selectData)) - return selectData - endif - SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") - SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") + numArgs = SF_GetNumberOfArguments(jsonID, jsonPath) + SF_ASSERT(numArgs >= 1 && numArgs <= 3, "epochs requires at least 1 and at most 3 arguments") if(numArgs == 3) WAVE/T epochType = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) @@ -2140,6 +2128,22 @@ static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string epType = EPOCHS_TYPE_RANGE endif + if(numArgs >= 2) + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + else + WAVE selectData = SF_ExecuteFormula("select()", databrowser = graph) + endif + + if(SF_IsDefaultEmptyWave(selectData)) + return selectData + endif + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") + + WAVE/T epochName = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) + SF_ASSERT(DimSize(epochName, ROWS) == 1, "Too many input values for parameter name") + SF_ASSERT(IsTextWave(epochName), "name parameter must be textual") + WAVE/Z activeChannels WAVE/Z sweeps [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) From e6e729f5cb0a778207a40989c62b21b97c065bc4 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 10 Apr 2022 23:24:52 +0200 Subject: [PATCH 35/49] SF: Add Tests for operation epochs optional select argument --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 35 ++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 749cc4bf7c..f7258d3f8d 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1630,20 +1630,35 @@ static Function TestOperationEpochs() Make/FREE/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/T wEpochStr wEpochStr = "0.5000000,0.5100000,Epoch=0;Type=Pulse Train;Amplitude=1;Pulse=48;ShortName=E0_PT_P48;,2,:0.5030000,0.5100000,Epoch=0;Type=Pulse Train;Pulse=48;Baseline;ShortName=E0_PT_P48_B;,3," + DFREF dfr = GetDeviceDataPath(device) for(i = 0; i < numSweeps; i += 1) sweepNumber = i + + WAVE sweepTemplate = GetDAQDataWave(device, DATA_ACQUISITION_MODE) + WAVE sweep = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) + WAVE config = GetDAQConfigWave(device) + Redimension/N=(numChannels, -1) config + for(j = 0; j < numChannels; j += 1) name = UniqueName("data", 1, 0) trace = "trace_" + name Extract input, $name, q == i && r == j WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace + channelNumber = channels[i][j] TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber"}, \ - {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", channelType, num2istr(channels[i][j]), num2istr(sweepNumber)}) - values[connections[j]] = channels[i][j] + {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", channelType, num2istr(channelNumber), num2istr(sweepNumber)}) + values[connections[j]] = channelNumber + config[j][%ChannelType] = XOP_CHANNEL_TYPE_DAC + config[j][%ChannelNumber] = channelNumber endfor - // channels setup: 8, 6, NaN, 4, NaN, 2, NaN, 0, NaN - // -> 5 active channels for ADC + + // create sweeps with dummy data for sweeps() operation thats called when omitting select + MoveWave sweep, dfr:$GetSweepWaveName(sweepNumber) + MoveWave config, dfr:$GetConfigWaveName(sweepNumber) + MIES_DB#DB_SplitSweepsIfReq(win, sweepNumber) + + // channels setup DA: 8, 6, NaN, 4, NaN, 2, NaN, 0, NaN // -> 4 active channels for DAC, because DAC knows only 8 channels from 0 to 7. Redimension/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/E=1 values @@ -1654,6 +1669,13 @@ static Function TestOperationEpochs() ED_AddEntriesToLabnotebook(wEpochStr, keysEpochs, sweepNumber, device, mode) endfor + str = "epochs(\"E0_PT_P48\")" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + Make/FREE/D/N=(2, numSweeps * activeChannelsDA) refData + refData[0][] = 500 + refData[1][] = 510 + REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) + str = "epochs(\"E0_PT_P48\", select(channels(DA0), 0))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) Make/FREE/D refData = {500, 510} @@ -1691,10 +1713,11 @@ static Function TestOperationEpochs() refData = p ? 510 : 503 REQUIRE_EQUAL_WAVES(data, refData, mode = WAVE_DATA) + WAVE wRefEmpty = MIES_SF#SF_GetDefaultEmptyWave() // channel(s) with no epochs str = "epochs(\"E0_PT_P48_B\", select(channels(AD), 0..." + num2istr(numSweeps) + "))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) - CHECK_EQUAL_WAVES({NaN}, data, mode = WAVE_DATA) + CHECK_EQUAL_WAVES(wRefEmpty, data, mode = WAVE_DATA) // name that does not match any str = "epochs(\"does_not_exist\", select(channels(DA), 0..." + num2istr(numSweeps) + "))" @@ -1704,7 +1727,7 @@ static Function TestOperationEpochs() // invalid sweep WAVE data = SF_FormulaExecutor(DirectToFormulaParser("epochs(\"E0_PT_P48_B\", select(channels(DA), -1))"), graph = win) - CHECK_EQUAL_WAVES({NaN}, data, mode = WAVE_DATA) + CHECK_EQUAL_WAVES(wRefEmpty, data, mode = WAVE_DATA) // invalid type str = "epochs(\"E0_PT_P48_B\", select(channels(DA), 0..." + num2istr(numSweeps) + "), invalid_type)" From 145f98331a6f8799bf5b7d7be87c10ad1f619987 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Sun, 10 Apr 2022 23:59:44 +0200 Subject: [PATCH 36/49] SF: Changes labnotebook operation for optional select argument --- Packages/MIES/MIES_SweepFormula.ipf | 46 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index fd36d3f60d..7b9eb9222c 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -2695,36 +2695,24 @@ static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string g return out End -/// `labnotebook(string key, array selectData [, string entrySourceType])` +/// `labnotebook(string key[, array selectData [, string entrySourceType]])` /// /// return lab notebook @p key for all @p sweeps that belong to the channels @p channels static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, string graph) variable numIndices, i, j, mode, JSONtype, index, sweepNo, numSweeps, numChannels - string str + string str, lbnKey SF_ASSERT(!IsEmpty(graph), "Graph not specified.") - numIndices = JSON_GetArraySize(jsonID, jsonPath) + numIndices = SF_GetNumberOfArguments(jsonID, jsonPath) SF_ASSERT(numIndices <= 3, "Maximum number of three arguments exceeded.") - SF_ASSERT(numIndices >= 2, "At least two arguments are required.") - - JSONtype = JSON_GetType(jsonID, jsonPath + "/0") - SF_ASSERT(JSONtype == JSON_STRING, "first parameter needs to be a string labnotebook key") - str = JSON_GetString(jsonID, jsonPath + "/0") + SF_ASSERT(numIndices >= 1, "At least one argument is required.") - WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) - if(SF_IsDefaultEmptyWave(selectData)) - return selectData - endif - SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") - SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") - - mode = DATA_ACQUISITION_MODE if(numIndices == 3) - JSONtype = JSON_GetType(jsonID, jsonPath + "/2") - SF_ASSERT(JSONtype == JSON_STRING, "Last parameter needs to be a string.") - strswitch(JSON_GetString(jsonID, jsonPath + "/2")) + WAVE/T wMode = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/2", graph = graph) + SF_ASSERT(IsTextWave(wMode) && DimSize(wMode, ROWS) == 1 && !DimSize(wMode, COLS), "Last parameter needs to be a string.") + strswitch(wMode[0]) case "UNKNOWN_MODE": mode = UNKNOWN_MODE break @@ -2739,8 +2727,26 @@ static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, s default: SF_ASSERT(0, "Undefined labnotebook mode. Use one in group DataAcqModes") endswitch + else + mode = DATA_ACQUISITION_MODE + endif + + if(numIndices >= 2) + WAVE selectData = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/1", graph = graph) + else + WAVE selectData = SF_ExecuteFormula("select()", databrowser = graph) endif + if(SF_IsDefaultEmptyWave(selectData)) + return selectData + endif + SF_ASSERT(DimSize(selectData, COLS) == 3, "A select input has 3 columns.") + SF_ASSERT(IsNumericWave(selectData), "select parameter must be numeric") + + WAVE/T wLbnKey = SF_FormulaExecutor(jsonID, jsonPath = jsonPath + "/0", graph = graph) + SF_ASSERT(IsTextWave(wLbnKey) && DimSize(wLbnKey, ROWS) == 1 && !DimSize(wLbnKey, COLS), "First parameter needs to be a string labnotebook key.") + lbnKey = wLbnKey[0] + WAVE/Z sweeps WAVE/Z activeChannels [sweeps, activeChannels] = SF_ReCreateOldSweepsChannelLayout(selectData) @@ -2765,7 +2771,7 @@ static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, s endif for(j = 0; j < numChannels; j += 1) - [settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweeps[i], str, activeChannels[j][%channelNumber], activeChannels[j][%channelType], mode) + [settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweeps[i], lbnKey, activeChannels[j][%channelNumber], activeChannels[j][%channelType], mode) if(!WaveExists(settings)) continue endif From eab0d993802126fef80dfd7be5d73d8fde55a6a1 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 11 Apr 2022 00:02:17 +0200 Subject: [PATCH 37/49] SF: Add Test for optional select argument of labnotebook operation --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index f7258d3f8d..488c49dcb7 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1523,7 +1523,7 @@ static Function TestOperationData() End -Function TestLabNotebook() +static Function TestOperationLabNotebook() Variable i, j, sweepNumber, channelNumber String str, trace, key, name, epochStr @@ -1559,19 +1559,32 @@ Function TestLabNotebook() Make/FREE/N=(dataSize, numSweeps, numChannels) input = q + p^r // + gnoise(1) + DFREF dfr = GetDeviceDataPath(device) for(i = 0; i < numSweeps; i += 1) sweepNumber = i + WAVE sweepTemplate = GetDAQDataWave(device, DATA_ACQUISITION_MODE) + WAVE sweep = FakeSweepDataGeneratorDefault(sweepTemplate, numChannels) + WAVE config = GetDAQConfigWave(device) + Redimension/N=(numChannels, -1) config for(j = 0; j < numChannels; j += 1) name = UniqueName("data", 1, 0) trace = "trace_" + name Extract input, $name, q == i && r == j WAVE wv = $name AppendToGraph/W=$win wv/TN=$trace + channelNumber = channels[i][j] TUD_SetUserDataFromWaves(win, trace, {"experiment", "fullPath", "traceType", "occurence", "channelType", "channelNumber", "sweepNumber"}, \ - {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", channelType, num2str(channels[i][j]), num2str(sweepNumber)}) - values[connections[j]] = channels[i][j] + {"blah", GetWavesDataFolder(wv, 2), "Sweep", "0", channelType, num2str(channelNumber), num2str(sweepNumber)}) + values[connections[j]] = channelNumber + config[j][%ChannelType] = XOP_CHANNEL_TYPE_ADC + config[j][%ChannelNumber] = channelNumber endfor + // create sweeps with dummy data for sweeps() operation thats called when omitting select + MoveWave sweep, dfr:$GetSweepWaveName(sweepNumber) + MoveWave config, dfr:$GetConfigWaveName(sweepNumber) + MIES_DB#DB_SplitSweepsIfReq(win, sweepNumber) + Redimension/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/E=1 values ED_AddEntriesToLabnotebook(values, keys, sweepNumber, device, mode) ED_AddEntriesToLabnotebook(values, dacKeys, sweepNumber, device, mode) @@ -1580,6 +1593,10 @@ Function TestLabNotebook() endfor ModifyGraph/W=$win log(left)=1 + str = "labnotebook(" + channelTypeC + ")" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) + str = "labnotebook(" + channelTypeC + ",select(channels(AD),0..." + num2istr(numSweeps) + "))" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) @@ -1587,6 +1604,11 @@ Function TestLabNotebook() str = "labnotebook(" + LABNOTEBOOK_USER_PREFIX + channelTypeC + ",select(channels(AD),0..." + num2istr(numSweeps) + "),UNKNOWN_MODE)" WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) REQUIRE_EQUAL_WAVES(data, channels, mode = WAVE_DATA) + + str = "labnotebook(" + channelTypeC + ",select(channels(AD12),-1))" + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) + WAVE wRef = MIES_SF#SF_GetDefaultEmptyWave() + REQUIRE_EQUAL_WAVES(data, wRef, mode = WAVE_DATA) End /// @brief Test Epoch operation of SweepFormula From 53941775a1267d7edfa46f38b9440a92c78da6a6 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 11 Apr 2022 00:38:52 +0200 Subject: [PATCH 38/49] SF: Adapted SweepFormula documentation for optional select argument - for data, tp, labnotebook and epochs operation --- Packages/doc/SweepFormula.rst | 57 +++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index 9bc4bd45a5..5d14a06b04 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -446,19 +446,21 @@ data """" Although being listed near the end, the `data()` function is the core of the -`SweepFormula` library. It returns *MIES* specific data from the current graph. +`SweepFormula` library. It returns sweep data from *MIES*. .. code-block:: bash - data(array range, array selectData) + data(array range[, array selectData]) - data(string epochShortName, array selectData) + data(string epochShortName[, array selectData]) -It returns `[[sweeps][channel]]` for all `[sweeps]` in the array containing the -sweep numbers. +The return format is a 3d array `[sweepData][sweeps][channels]` where the columns +use a dimension label in the form SWEEP where X is the sweep number, e.g. `SWEEP0`. The layers +use dimension labels in the form , e.g. `AD0`. -Through the `select` function it can be chosen of data for sweeps is returned for -currently displayed sweeps or for all existing sweeps. +Through the `select` function it can be chosen for which sweeps and channels sweep data is returned. +`select` also allows to choose either currently displayed sweeps or all existing sweeps as data source. +When the optional select argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. The range can be either supplied explicitly using `[100, 300]` which would select `100 ms` to `300 ms` or by using `cursors()`. In case `cursors()` is @@ -497,7 +499,7 @@ labnotebook .. code-block:: bash - labnotebook(string key, array selectData [, string entrySourceType]) + labnotebook(string key[, array selectData [, string entrySourceType]]) The labnotebook function returns the (case insensitive) `key` entry from the labnotebook for the selected channel and sweep combination(s). The optional @@ -505,9 +507,10 @@ labnotebook for the selected channel and sweep combination(s). The optional acquisition modes as defined in `../MIES/MIES_Constants.ipf`. If the `entrySourceType` is omitted it defaults to `DATA_ACQUISITION_MODE`. -The `labnotebook()` function has the same data layout as the `data()` -function. It returns the notebook entry in the rows for all `[sweeps]` with the -corresponding `[channel]` (`[[sweeps][channel]]`). +When the optional select argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. + +The `labnotebook()` function has a similar data layout as the `data()` +function. It returns the notebook entries as single elements where the rows count the sweeps and the columns the channels: `[sweeps][channel]`. .. code-block:: bash @@ -621,7 +624,13 @@ The epochs function returns information from epochs. .. code-block:: bash - epochs(string name, array selectData[, string type]) + epochs(string name[, array selectData[, string type]]) + +The first argument is the name of the epoch. + +The second argument is a selection of sweeps and channels where the epoch information is retrieved. +It is intended to be specified through the `select` operation. +When the optional second argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. type sets what information is returned. Valid types are: `range`, `name`, `treelevel`. If type is not specified then `range` is used as default. @@ -645,6 +654,9 @@ If no matching epoch was found a zero sized wave is returned. .. code-block:: bash + // get stimset range (epoch ST) from all displayed sweeps and channels + epochs(ST) + // two sweeps acquired with two headstages set with PulseTrain_100Hz_DA_0 and PulseTrain_150Hz_DA_0 from _2017_09_01_192934-compressed.nwb epochs(ST, select(channels(AD), sweeps()), range) == [[20, 1376.01], [20, 1342.67], [20, 1376.01], [20, 1342.67]] @@ -655,7 +667,7 @@ The tp function returns analysis values for test pulses that are part of selecte .. code-block:: bash - tp(variant type, array selectData[, array ignoreTPs]) + tp(variant type[, array selectData[, array ignoreTPs]]) type sets what test pulse analysis value is returned. The following types are supported: @@ -670,9 +682,16 @@ The returned array is 1 x M x N, where M indexes the sweeps and N indexes the ch sweep and channel information gets transferred as well. Values for non-existing sweeps and/or channels are set NaN. If a single sweep contains multiple test pulses then the data from the test -pulse ranges is averaged. The optional parameter ``ignoreTPs`` allows to ignore +pulse ranges is averaged. + +The second argument is a selection of sweeps and channels where the test pulse information is retrieved. +It is intended to be specified through the `select` operation. +When the optional second argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. + +The optional argument ``ignoreTPs`` allows to ignore some of the found test-pulses. The indices are zero-based and identify the test-pulses by ascending starting time. + The test pulses in the sweep must have the same duration. Test pulses that are part of sweeps are identified through their respective epoch short name, that starts with "TP" or "U_TP". If sweeps and channels can resolve existing single sweeps but none contain @@ -680,12 +699,18 @@ epochs for test pulses then a numeric single element wave is returned with the v .. code-block:: bash - // Get steady state resistance from all sweeps and all AD channels + // Get steady state resistance from all displayed sweeps and channels + tp(ss) + + // Get steady state resistance from all displayed sweeps and AD channels tp(ss, select(channels(AD), sweeps())) - // Get base line level from all sweeps and DA1 channel + // Get base line level from all displayed sweeps and DA1 channel tp(static, select(channels(DA1), sweeps())) + // Get base line level from all displayed sweeps and channels ignoring test pulse 0 and 1 + tp(static, select(), [0, 1]) + merge """"" From f36238b283121ede3755b26666fe346363734d2c Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Mon, 11 Apr 2022 01:26:37 +0200 Subject: [PATCH 39/49] SF: Update SweepFormula help notebook with optional select argument description - also added more examples --- Packages/MIES/SweepFormulaHelp.ifn | Bin 69753 -> 72987 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Packages/MIES/SweepFormulaHelp.ifn b/Packages/MIES/SweepFormulaHelp.ifn index 8cce47f75a26ff33593a2fd58a588444960aa2de..79925ba5f1ea1fc472bec8e41a01506bd441d4b8 100644 GIT binary patch delta 4509 zcmc)NTWl2983*uh7I(RL4HRtS#6S)hn_XD1$HW-w8(9hZS4)>Rj6aC;xfqsaLa~29zgfx}7nm>=e@|Q*zXPL>) zAo(p^y+`h>jJG*l9-*0cuiWO+2W7dR1tdfAW%2diW!&NI;jcA23(ZGvDH4%}SVU60 z<#sQ#|I%ee_8WD0-QnTi&n)IOolc*XS~ntLwVTQPnm^PM(jvwVsW0t!TwJk`&n>>W z?HO8zdbNgRrpta!4WNkbXrHVaEUdGzYRGCp4lqMwT{4U6xE%}|A!b z-DB1$A{)_&DhGU7@dTleVKJ`~n&Woy3Zuy6H__GIsNFkjZvN%^lc^XHC| zvpZYRr$i+stl8>!y9_HoyCg06=q4nqW6fbEvSL=MbJ3N}J6+B>)~sz8y{uFeMSC{y zX@7K~W>YuRO-zztMC(gmlSE%kDjXrh9b)~OrpGIcKUMmKbD}N6YX0w%RZaw`qImeX zD;97uCvWMlhx1P9IlErGP4vzjp8)(?Nj^U=cIF4B{*d>I`4eBn3rb6bMVqbUiqxfQ zhTNrTz3#+1&?^tMb9Gg*(1AKFid9=t(u%$dh9kQ1K&VSB@r~Xu`@_K@GZ-Y6k|#rE zn2q+bmql&kd8GuwgA0fq6p0ANTo0$r>y)6Mn6_4UN5b-e8Q=S93yOIBQ0sY&P{RW9 z(2|m8Juw%UlRwBz}+`2tjSIm7wcY8d(tZDq+r3L);%6USg8M=bZU#+fR zZ1u6(yAjzOR3khfj}N)-i~qj*S3;@Rs&D~ou@r+=)A-FL&iU33s3+qW%_Um3w^L?5=kK=V2n)e!B;CW-WkIZ=&Wbu(||`vAr!|-%9MK$$(oiv9~4m zuGFn7XMWbCishIc5qE7I`_@K#3BpFvEu&IKxKGE`UUc1d!7Z&GyOvY!dHmCcMRPK( zflrKHOKWm&#cyu;Sjb&#y)^pW15zX`;fP`k^Zd1MS3 zi*bKc!MSAW;i3OBKITYThcgN17VfJ?h^8N`Icvvftq}@G0{jmCzo31AZRM={M zo1N}kexqTZP{sX?-fZ(An20X3kE}YGISM8Gokm{Gh)Kx_9!TaETnrHwHgQE;{ zYf~A~9{32{tBFKtg$r;AeC0&tun!Kx7@S-~bOu(fC8~f6Fbd`0Avy%-;VRsPc@;!u z&l_EzFQBzPSz!Wi6xj4Glc@Kq6GIBB>c00-bLI0t1lXiW{VOSs;E zrR#`RKs^NDWw^Q?d4jkB&*5`;p%(98S!^RJ02y^eE_eY(;5>-W-~)IKehI&Wj7>x} zuoI5KD0~HZn~9y?jQ2QWxS$qx!#?;iT!b<3ZbhY`70y8H9Dcd#(NZ`F!{FS89)VNv z9;40MYLXX04A-fIV zLk+wM=ipCmF%-~_f2-jr{0)4&iMGI7Fb4Nvdj~2Bzkm;54EA;sy$J8a1sH>~-$P;W zHI(l`WkH40a29UEmyj!A*uW9%LO8)2a0Nbt3O~9L0&oC6g1P|yh8u7j6q(3?89}1C za1kzpuNz|y2jL`~0e1*J2|;)f4#9~KvGcgbo(!WU@FDyWa(j>%_QFv(1|vPhuHahK zi?M}6Z~|_?ZRk{x9$toH3b7Gfi~5Kna0t%8C15Is2zJ9!IHl5Z`U`GI!%J{$#Av-aI6#kw6 z=vtnYd^%p{eQKqff!nNqB;I5@=@wBzGTT|R@Uq|zQGW7?GZTf^e8G<@UdQFH`FX`V NZFH6pzoiWS>_6X~8RP%} delta 2051 zcmajge@K*f9Ki9 zL5$c$`@+~`G@QzqPZ<1B0{!^77-y%!oe0|5z(*7-gmCC*kBL5?)&TW{XWn0 z`9AkNpP!3*+!@uDtbN-)?fs0eC49`1?=2#asP$1kru@(T_8)$&y6zUaYZW=aJ3LXO zoC*7kvG!K#S35<9;x+kL6A8Se$$VlyqYNMaU#d+_NyoI9_;6$oGd4`p#MXXqL8#mk zm!|DiyB4fZ+fcZAiyo*m-8DOF-1-h9Xy`lrwR`lS$JE{Re!uAp>bv|#ok#7Rm87mN z*q@dnQ&=k;nQHj`MziiWe7j7WKGq1BUeg_1#b}z^J26AEtFLUYP5Jkza-`2x4{XD3 zm#kyC!l3RqgY|x2KzF$Ujiy-_sC2nKhRQJzgfN$&;t5s)9LMrkb7ha?vWzC$i0B`gF6&bk_&X9eQ zoYTigu0Y+(OHz*ARu#-DN?7`0&^L>0{|K@;s}hP{3}BT|r_-)!tJP20Gt}?d8Om5R zO*wL`p`OL|n0RL-d@R|iYNTL+7D;!gXhoi;tyLK-i!Andb*Ol%swkKe>Rs8Yg-)!Q zHBQS`-xW?%KNrqsxy;a+^@HO>Ih*^nq@2il!MfB2!(U^t=fUPWQ>B;A3zd}CSwgqU z^0aZqlA@-SzdEkeV=ApIQ(LGq?Q>OW*%57}T3()!Ft%;F;oA}3D>dDd`R-5i}RE|9)`Rb1f$E3(}w`#>g{jYsjzeG<}UsRNb;M3&(S?jX-8(uRxZ$0HyxQHm;2tydgu|n1_Vm?}I{H()8^x>8DB2M&T z2*dCclP=tab%Tfx4Y-Br8%aC5G5;<4aS~mKE@5p{qYER5-9$W2;Or*${~DDM9NEmi zq7T Date: Mon, 11 Apr 2022 01:46:00 +0200 Subject: [PATCH 40/49] SF: Adapted TestSweepFormulaCodeResults test for changed order active channels - The order for active channels is now channelType -> channelNumber The test TestSweepFormulaCodeResults had to be adapted for that. --- Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf index 137c14180e..5f0fba0721 100644 --- a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf @@ -331,7 +331,7 @@ static Function TestSweepFormulaCodeResults_REENTRY([string str]) CHECK_EQUAL_STR(content, contentRef) content = GetLastSettingTextIndep(textualResultsValues, NaN, "Sweep Formula active channels", UNKNOWN_MODE) - contentRef = "1;0;,0;1;," + contentRef = "0;1;,1;0;," CHECK_EQUAL_STR(content, contentRef) content = GetLastSettingTextIndep(textualResultsValues, NaN, "Sweep Formula experiment", UNKNOWN_MODE) From e5058c0a2d3469689630265dcdb57ca3e7a812fd Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 12 Apr 2022 17:33:39 +0200 Subject: [PATCH 41/49] Optimize speed of GetLastSettingInternal - FindIndizes was replaced by GetRowIndex because the returned wave is 1D anyway. - Changed variable naming from idx to headstage, improves readability of code --- Packages/MIES/MIES_MiesUtilities.ipf | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Packages/MIES/MIES_MiesUtilities.ipf b/Packages/MIES/MIES_MiesUtilities.ipf index 6900416e40..ce613d0935 100644 --- a/Packages/MIES/MIES_MiesUtilities.ipf +++ b/Packages/MIES/MIES_MiesUtilities.ipf @@ -880,7 +880,7 @@ End /// @sa GetLastSettingChannel threadsafe static Function [WAVE/Z wv, variable index] GetLastSettingChannelInternal(WAVE numericalValues, WAVE values, variable sweepNo, string setting, variable channelNumber, variable channelType, variable entrySourceType) string entryName - variable idx, indep + variable headstage, indep switch(channelType) case XOP_CHANNEL_TYPE_DAC: @@ -896,12 +896,10 @@ threadsafe static Function [WAVE/Z wv, variable index] GetLastSettingChannelInte WAVE/Z activeChannels = GetLastSetting(numericalValues, sweepNo, entryName, entrySourceType) if(WaveExists(activeChannels)) - WAVE/Z indizes = FindIndizes(activeChannels, col=0, var=channelNumber) + headstage = GetRowIndex(activeChannels, val = channelNumber) - if(WaveExists(indizes)) + if(IsFinite(headstage)) // given channel was associated and active - ASSERT_TS(DimSize(indizes, ROWS) == 1, "Unexpected size") - idx = indizes[0] WAVE/Z settings = GetLastSetting(values, sweepNo, setting, entrySourceType) @@ -914,21 +912,29 @@ threadsafe static Function [WAVE/Z wv, variable index] GetLastSettingChannelInte WAVE/T settingsT = settings if(IsNumericWave(settings)) - if(!IsNaN(settings[idx])) + if(!IsNaN(settings[headstage])) // numerical assoc setting - return [settings, idx] + return [settings, headstage] elseif(!IsNaN(settings[indep])) // ... unassoc ... return [settings, indep] endif + + // happens with querying associated entries + // which are only set for other headstages + // e.g. DA0, DA1 is active and only DA0 has an entry, + // then for DA1 settings[headstage] == NaN, + // but settings exist because for DA0 settings[headstage] != NaN elseif(IsTextWave(settingsT)) - if(cmpstr(settingsT[idx], "")) + if(cmpstr(settingsT[headstage], "")) // textual assoc setting - return [settingsT, idx] + return [settingsT, headstage] elseif(cmpstr(settingsT[indep], "")) // ... unassoc ... return [settingsT, indep] endif + + // same as above else ASSERT_TS(0, "Invalid wave type") endif From 3a62b30878b9104f831aa3e3e8bad1752aedd086 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Tue, 12 Apr 2022 18:27:38 +0200 Subject: [PATCH 42/49] SF: Optimize SF_GetActiveChannelNumbers, no GetLastSettingChannel calls - directly call GetLastSetting and work on the 1D wave basis instead of querying all channels separately --- Packages/MIES/MIES_SweepFormula.ipf | 32 +++++++++++++++++++---------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 7b9eb9222c..91385ae798 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1432,7 +1432,7 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo variable entrySourceType variable i, j, k, l, channelType, channelNumber, index, sweepNo, outIndex, shift - variable numSweeps, numInChannels, numSettings, maxChannels, numUniqueCombinations + variable numSweeps, numInChannels, numSettings, maxChannels, numUniqueCombinations, numActiveChannels, activeChannel string setting, settingList, msg WAVE/Z settings @@ -1505,17 +1505,27 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo break endswitch - for(l = 0; l < maxChannels; l += 1) - if(!IsNaN(channelNumber) && channelNumber != l) - continue - endif - [settings, index] = GetLastSettingChannel(numericalValues, $"", sweepNo, setting, l, channelType, entrySourceType) - if(!WaveExists(settings)) - continue + WAVE/Z activeChannels = GetLastSetting(numericalValues, sweepNo, setting, entrySourceType) + if(!WaveExists(activeChannels)) + continue + endif + if(IsNaN(channelNumber)) + // faster than ZapNaNs due to no mem alloc + numActiveChannels = DimSize(activeChannels, ROWS) + for(l = 0; l < numActiveChannels; l += 1) + activeChannel = activeChannels[l] + if(!IsNaN(activeChannel) && activeChannel < maxChannels) + collect[outIndex] = channelType << shift + activeChannel + outIndex += 1 + endif + endfor + elseif(channelNumber < maxChannels) + FindValue/V=(channelNumber) activeChannels + if(V_Value >= 0) + collect[outIndex] = channelType << shift + channelNumber + outIndex += 1 endif - collect[outIndex] = channelType << shift + settings[index] - outIndex += 1 - endfor + endif endfor endfor endfor From 4920fcd189eb445157c02b9c2b95cd1562b28872 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 13 Apr 2022 12:28:18 +0200 Subject: [PATCH 43/49] SF: Speedup memory fill for larger sweep data in SF_GetSweepFormula --- Packages/MIES/MIES_SweepFormula.ipf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 91385ae798..20fb9603e8 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1303,7 +1303,8 @@ static Function/WAVE SF_GetSweepsForFormula(string graph, WAVE range, WAVE selec numRows = WaveMax(indexEnd) + 1 - indexOffset // combine sweeps to data wave - Make/FREE/N=(numRows, numSweeps, numChannels) sweepData = NaN + Make/FREE/N=(numRows, numSweeps, numChannels) sweepData + MultiThread sweepData = NaN SetScale/P x, indexOffset * delta, delta, sweepData for(i = 0; i < numSweeps; i += 1) for(j = 0; j < numChannels; j += 1) From a1006ae0ebbc7096d941c8e0b5fc6403584a6f1e Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 13 Apr 2022 19:03:10 +0200 Subject: [PATCH 44/49] SF: Remove AbortOnRTE for epochs op fail test as SF uses only Abort - ASSERTs that indicate a programming error should be caught as error by the IUTF --- Packages/Testing-MIES/UTF_SweepFormula.ipf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormula.ipf b/Packages/Testing-MIES/UTF_SweepFormula.ipf index 488c49dcb7..8a2bf05a86 100644 --- a/Packages/Testing-MIES/UTF_SweepFormula.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormula.ipf @@ -1754,10 +1754,9 @@ static Function TestOperationEpochs() // invalid type str = "epochs(\"E0_PT_P48_B\", select(channels(DA), 0..." + num2istr(numSweeps) + "), invalid_type)" try - WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win); AbortOnRTE + WAVE data = SF_FormulaExecutor(DirectToFormulaParser(str), graph = win) FAIL() catch - ClearRTError() PASS() endtry End From abba5d8c519b913d70351fbe0fd631d25d6405d5 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Wed, 13 Apr 2022 19:10:41 +0200 Subject: [PATCH 45/49] SF: Optimization for speed of select operation. Previously we determined all active channels from all sweeps before we resolved the actual active channels of each individial sweep. This is inefficient as we can as well resolve first the actual channels of all sweeps. Then for displayed sweeps we can filter that list based on the trace data. Also in SF_CreateResultsWaveWithCode we can use the channels data determined by the select() execution. Select() returns the data of the displayed sweeps, so the returned channels are also only from the displayed sweeps. --- Packages/MIES/MIES_SweepFormula.ipf | 281 +++++++++++----------------- 1 file changed, 110 insertions(+), 171 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 20fb9603e8..99c2c52c98 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1058,147 +1058,64 @@ static Function SF_GetDAChannel(string graph, variable sweep, variable channelTy return NaN End -static Function/WAVE SF_GetSelectDataForAll(string graph, WAVE activeChannels, WAVE sweeps) - - variable i, j, numSweeps, sweepNo, cIndex, index - variable channelNr, channelType, numChannels, isSweepBrowser - string device, dataFolder - - ASSERT(WindowExists(graph), "graph window does not exist") - SF_ASSERT(DimSize(activeChannels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - numSweeps = DimSize(sweeps, ROWS) - numChannels = DimSize(activeChannels, ROWS) - if(!numSweeps || !numChannels) - return $"" - endif - - isSweepBrowser = BSP_IsSweepBrowser(graph) - - if(isSweepBrowser) - DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) - WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) - else - SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") - device = BSP_GetDevice(graph) - DFREF deviceDFR = GetDeviceDataPath(device) - endif - - WAVE selectData = SF_NewSelectDataWave(numSweeps, numChannels) - - for(i = 0; i < numSweeps; i += 1) - - sweepNo = sweeps[i] - - if(isSweepBrowser) - cIndex = FindDimLabel(sweepMap, COLS, "Sweep") - FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap - if(V_value == -1) - continue - endif - dataFolder = sweepMap[V_row][%DataFolder] - device = sweepMap[V_row][%Device] - DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) - endif - DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) - - for(j = 0; j < numChannels; j += 1) - - channelNr = activeChannels[j][%channelNumber] - channelType = activeChannels[j][%channelType] - WAVE/Z sweep = GetDAQDataSingleColumnWave(sweepDFR, channelType, channelNr) - if(!WaveExists(sweep)) - continue - endif - - selectData[index][%SWEEP] = sweepNo - selectData[index][%CHANNELTYPE] = channelType - selectData[index][%CHANNELNUMBER] = channelNr - index += 1 - endfor - endfor - if(!index) - return $"" - endif - Redimension/N=(index, -1) selectData - - return selectData -End - -static Function/WAVE SF_GetSelectDataForDisplayed(string graph, WAVE activeChannels, WAVE sweeps) +static Function/WAVE SF_FilterSelectDataFromDisplayed(string graph, WAVE selectData) variable i, j, index - variable numSweeps, numChannels - variable sweepNo, channelNr - variable channelType = -1 + variable numEntries, numTraces + variable sweepNo, channelType, channelNumber + variable dimPosSweepNo, dimPosChannelType, dimPosChannelNumber + variable dimPosSelectSweepNo, dimPosSelectChannelType, dimPosSelectChannelNumber ASSERT(WindowExists(graph), "graph window does not exist") - SF_ASSERT(DimSize(activeChannels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - numSweeps = DimSize(sweeps, ROWS) - numChannels = DimSize(activeChannels, ROWS) - if(!numSweeps || !numChannels) - return $"" - endif + numEntries = DimSize(selectData, ROWS) WAVE/T/Z traces = GetTraceInfos(graph) if(!WaveExists(traces)) DebugPrint("No traces found for extracting sweep wave locations.") return $"" endif - SortColumns/A/DIML/KNDX={FindDimLabel(traces, COLS, "channelType"), FindDimLabel(traces, COLS, "channelNumber"), FindDimLabel(traces, COLS, "sweepNumber")} sortWaves=traces + numTraces = DimSize(traces, ROWS) - WAVE selectData = SF_NewSelectDataWave(numSweeps, numChannels) + Duplicate/FREE selectData, selectDataFiltered + dimPosSweepNo = FindDimLabel(traces, COLS, "sweepNumber") + dimPosChannelType = FindDimLabel(traces, COLS, "channelType") + dimPosChannelNumber = FindDimLabel(traces, COLS, "channelNumber") - for(i = 0; i < numSweeps; i += 1) + dimPosSelectSweepNo = FindDimLabel(selectData, COLS, "SWEEP") + dimPosSelectChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") + dimPosSelectChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") - sweepNo = sweeps[i] - WAVE/Z sweepListIndex = FindIndizes(traces, colLabel = "sweepNumber", var = sweepNo) - if(!WaveExists(sweepListIndex)) - continue - endif + WAVE selectDisplayed = SF_NewSelectDataWave(numTraces, 1) + selectDisplayed[][%SWEEP] = str2num(traces[p][dimPosSweepNo]) + selectDisplayed[][%CHANNELTYPE] = WhichListItem(traces[p][dimPosChannelType], XOP_CHANNEL_NAMES) + selectDisplayed[][%CHANNELNUMBER] = str2num(traces[p][dimPosChannelNumber]) - for(j = 0; j < numChannels; j += 1) + for(i = 0; i < numEntries; i += 1) - if(channelType != activeChannels[j][%channelType]) - channelType = activeChannels[j][%channelType] - WAVE/Z channelTypeIndex = FindIndizes(traces, colLabel = "channelType", str = StringFromList(channelType, XOP_CHANNEL_NAMES)) - endif - if(!WaveExists(channelTypeIndex)) - continue - endif + sweepNo = selectData[i][%SWEEP] + channelType = selectData[i][%CHANNELTYPE] + channelNumber = selectData[i][%CHANNELNUMBER] - channelNr = activeChannels[j][%channelNumber] - WAVE/Z channelNumberIndex = FindIndizes(traces, colLabel = "channelNumber", var = channelNr) - if(!WaveExists(channelNumberIndex)) - continue - endif + for(j = 0; j < numTraces; j += 1) - // find matching index in @c traces wave - Concatenate/FREE {sweepListIndex, channelTypeIndex, channelNumberIndex}, indices - Redimension/N=(numpnts(indices))/E=1 indices - Sort indices, indices - Extract/FREE indices, matches, (p > 1 && (indices[p] == indices[p - 1]) && (indices[p] == indices[p - 2])) - WaveClear indices - if(DimSize(matches, ROWS) == 0) - continue - endif - SF_ASSERT(DimSize(matches, ROWS) == 1, "More than one matching sweep for this sweepNumber, channelType, and channelNumber combination.") - WaveClear matches + if(selectDisplayed[j][dimPosSelectSweepNo] == sweepNo && selectDisplayed[j][dimPosSelectChannelType] == channelType && selectDisplayed[j][dimPosSelectChannelNumber] == channelNumber) - selectData[index][%SWEEP] = sweepNo - selectData[index][%CHANNELTYPE] = channelType - selectData[index][%CHANNELNUMBER] = channelNr - index += 1 + selectDataFiltered[index][%SWEEP] = sweepNo + selectDataFiltered[index][%CHANNELTYPE] = channelType + selectDataFiltered[index][%CHANNELNUMBER] = channelNumber + index += 1 + + break + endif endfor endfor if(!index) return $"" endif - Redimension/N=(index, -1) selectData + Redimension/N=(index, -1) selectDataFiltered - return selectData + return selectDataFiltered End static Function/WAVE SF_GetSweepsForFormula(string graph, WAVE range, WAVE selectData) @@ -1425,18 +1342,15 @@ End /// @param sweeps @c SF_FormulaExecutor style @c sweeps() wave /// @param entrySourceType type of the labnotebook entry, one of @ref DataAcqModes. /// If you don't care about the entry source type pass #UNKNOWN_MODE. -/// @return a @c SF_FormulaExecutor style @c channels() wave with two columns -/// containing channelType and channelNumber -static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySourceType) - string graph - WAVE channels, sweeps - variable entrySourceType +/// @return a selectData style wave with three columns +/// containing sweepNumber, channelType and channelNumber +static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE channels, WAVE sweeps, variable entrySourceType) - variable i, j, k, l, channelType, channelNumber, index, sweepNo, outIndex, shift - variable numSweeps, numInChannels, numSettings, maxChannels, numUniqueCombinations, numActiveChannels, activeChannel - string setting, settingList, msg - - WAVE/Z settings + variable i, j, k, l, channelType, channelNumber, sweepNo, outIndex + variable numSweeps, numInChannels, numSettings, maxChannels, activeChannel, numActiveChannels + variable isSweepBrowser, cIndex + variable dimPosSweep, dimPosChannelNumber, dimPosChannelType + string setting, settingList, msg, device, dataFolder, singleSweepDFStr ASSERT(windowExists(graph), "DB/SB not specified.") SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") @@ -1449,12 +1363,26 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo entrySourceType == NUMBER_OF_LBN_DAQ_MODES, \ "Undefined labnotebook mode. Use one in group DataAcqModes") + isSweepBrowser = BSP_IsSweepBrowser(graph) + + if(isSweepBrowser) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + else + SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") + device = BSP_GetDevice(graph) + DFREF deviceDFR = GetDeviceDataPath(device) + endif + // search sweeps for active channels - shift = ceil(log(NUM_AD_CHANNELS) / log(2)) numSweeps = DimSize(sweeps, ROWS) numInChannels = DimSize(channels, ROWS) - Make/FREE/N=((NUM_DA_TTL_CHANNELS + NUM_AD_CHANNELS) * numSweeps) collect + WAVE selectData = SF_NewSelectDataWave(numSweeps, NUM_DA_TTL_CHANNELS + NUM_AD_CHANNELS) + + dimPosSweep = FindDimLabel(selectData, COLS, "SWEEP") + dimPosChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") + dimPosChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] @@ -1463,6 +1391,22 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo continue endif + if(isSweepBrowser) + cIndex = FindDimLabel(sweepMap, COLS, "Sweep") + FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap + if(V_value == -1) + continue + endif + dataFolder = sweepMap[V_row][%DataFolder] + device = sweepMap[V_row][%Device] + DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) + endif + singleSweepDFStr = GetSingleSweepFolderAsString(deviceDFR, sweepNo) + if(!DataFolderExists(singleSweepDFStr)) + continue + endif + DFREF sweepDFR = $singleSweepDFStr + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) if(!WaveExists(numericalValues)) continue @@ -1474,7 +1418,7 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo channelNumber = channels[j][%channelNumber] if(IsNaN(channelType)) - settingList = "DAC;ADC;" + settingList = "ADC;DAC;" else switch(channelType) case XOP_CHANNEL_TYPE_DAC: @@ -1516,40 +1460,48 @@ static Function/WAVE SF_GetActiveChannelNumbers(graph, channels, sweeps, entrySo for(l = 0; l < numActiveChannels; l += 1) activeChannel = activeChannels[l] if(!IsNaN(activeChannel) && activeChannel < maxChannels) - collect[outIndex] = channelType << shift + activeChannel + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = activeChannel outIndex += 1 endif endfor elseif(channelNumber < maxChannels) FindValue/V=(channelNumber) activeChannels if(V_Value >= 0) - collect[outIndex] = channelType << shift + channelNumber + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = channelNumber outIndex += 1 endif endif + endfor endfor endfor - Redimension/N=(outIndex, -1) collect - - if(outIndex > 1) - FindDuplicates/FREE/RN=collectReduced collect - Sort collectReduced, collectReduced - else - WAVE collectReduced = collect + if(!outIndex) + return $"" endif - numUniqueCombinations = DimSize(collectReduced, ROWS) - Make/FREE/N=(numUniqueCombinations, 2) out - SetDimLabel COLS, 0, channelType, out - SetDimLabel COLS, 1, channelNumber, out + Redimension/N=(outIndex, -1) selectData + WAVE out = SF_SortSelectData(selectData) - if(numUniqueCombinations) - out[][%channelType] = collectReduced[p] >> shift - out[][%channelNumber] = collectReduced[p] - out[p][%channelType] << shift + return out +End + +static Function/WAVE SF_SortSelectData(WAVE selectData) + + variable dimPosSweep, dimPosChannelType, dimPosChannelNumber + + if(DimSize(selectData, ROWS) >= 1) + dimPosSweep = FindDimLabel(selectData, COLS, "SWEEP") + dimPosChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") + dimPosChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") + + SortColumns/KNDX={dimPosSweep, dimPosChannelType, dimPosChannelNumber} sortWaves=selectData endif - return out + return selectData End /// @brief Pre process code entered into the notebook @@ -1771,7 +1723,6 @@ End static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string graph, string code, [WAVE data, string name]) variable numEntries, numOptParams, hasStoreEntry, numCursors, numBasicEntries - variable dimSweep string shPanel, dataFolder, device numOptParams = ParamIsDefault(data) + ParamIsDefault(name) @@ -1803,23 +1754,13 @@ static Function [WAVE/T keys, WAVE/T values] SF_CreateResultsWaveWithCode(string WAVE/T/Z cursorInfos = GetCursorInfos(graph) - WAVE/Z select = SF_ExecuteFormula("select(channels(AD), sweeps(),displayed)", databrowser = graph) - SF_ASSERT(WaveExists(select), "Select did not return data.") - dimSweep = FindDimLabel(select, COLS, "SWEEP") - Duplicate/FREE/RMD=[][dimSweep] select, wTmp - Redimension/N=(-1) wTmp - if(DimSize(wTmp, ROWS) > 1) - FindDuplicates/FREE/RN=sweeps wTmp - else - WAVE sweeps = wTmp - endif - values[0][%$"Sweep Formula displayed sweeps"][INDEP_HEADSTAGE] = NumericWaveToList(sweeps, ";") - - WAVE/Z channels = SF_ExecuteFormula("channels()", databrowser = graph) - - if(WaveExists(sweeps) && WaveExists(channels)) - WAVE/Z activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) - values[0][%$"Sweep Formula active channels"][INDEP_HEADSTAGE] = NumericWaveToList(activeChannels, ";") + WAVE selectData = SF_ExecuteFormula("select()", databrowser = graph) + if(!SF_IsDefaultEmptyWave(selectData)) + WAVE/Z sweeps + WAVE/Z channels + [sweeps, channels] = SF_ReCreateOldSweepsChannelLayout(selectData) + values[0][%$"Sweep Formula displayed sweeps"][INDEP_HEADSTAGE] = NumericWaveToList(sweeps, ";") + values[0][%$"Sweep Formula active channels"][INDEP_HEADSTAGE] = NumericWaveToList(channels, ";") endif shPanel = LBV_GetSettingsHistoryPanel(graph) @@ -2648,12 +2589,10 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string endif endif - WAVE activeChannels = SF_GetActiveChannelNumbers(graph, channels, sweeps, DATA_ACQUISITION_MODE) - - if(!CmpStr(mode, "all")) - WAVE/Z out = SF_GetSelectDataForAll(graph, activeChannels, sweeps) - else - WAVE/Z out = SF_GetSelectDataForDisplayed(graph, activeChannels, sweeps) + WAVE/Z out = SF_GetActiveChannelNumbersForSweeps(graph, channels, sweeps, DATA_ACQUISITION_MODE) + if(!CmpStr(mode, "displayed") && WaveExists(out)) + WAVE selectDataAll = out + WAVE/Z out = SF_FilterSelectDataFromDisplayed(graph, selectDataAll) endif if(!WaveExists(out)) From 22e9ebb7792670ac30143f373192e3e0497ade72 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 14 Apr 2022 01:36:11 +0200 Subject: [PATCH 46/49] SF: Test fix TestSweepFormulaCodeResults test with data from store operation Previously TestSweepFormulaCodeResults saved the sweeps from the select operation and the channels from the channels operation. This was wrong, because "select()" works on the displayed sweeps and channels on the given channels. If the displayed sweeps did not contain any of the given channels, channels were saved that did not exist in the sweeps that were saved. Now a single select() operation retrieves both. However, since active channels are saved separately from the sweeps there might be active channels that are not active for a particular sweep (and other channels thar are no active for another sweep). This is summarized in a current issue 1335 --- Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf index 5f0fba0721..1ba287db9a 100644 --- a/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf +++ b/Packages/Testing-MIES/UTF_SweepFormulaHardware.ipf @@ -331,7 +331,7 @@ static Function TestSweepFormulaCodeResults_REENTRY([string str]) CHECK_EQUAL_STR(content, contentRef) content = GetLastSettingTextIndep(textualResultsValues, NaN, "Sweep Formula active channels", UNKNOWN_MODE) - contentRef = "0;1;,1;0;," + contentRef = "0;1;," CHECK_EQUAL_STR(content, contentRef) content = GetLastSettingTextIndep(textualResultsValues, NaN, "Sweep Formula experiment", UNKNOWN_MODE) From 4a9060bc90501837842d87d41a8fcaa640962256 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 14 Apr 2022 15:01:39 +0200 Subject: [PATCH 47/49] GetUniqueEntries: added dontDuplicate optional argument and Tests GetUniqueEntries was very slow if the input wave has only a single element because it created a new free wave. In a lot of application cases it suffices to continue using the input wave instead. For theses cases the duplicate can be prevented. The new optional argument dontDuplicate allows to hint the function that it is ok to return the input wave if it has only a single element preventing the mem alloc overhead of duplicate. --- Packages/MIES/MIES_Utilities.ipf | 46 ++++++++++++++++------------- Packages/Testing-MIES/UTF_Utils.ipf | 24 +++++++++++++++ 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/Packages/MIES/MIES_Utilities.ipf b/Packages/MIES/MIES_Utilities.ipf index 6078da9e83..ffefa6fa70 100644 --- a/Packages/MIES/MIES_Utilities.ipf +++ b/Packages/MIES/MIES_Utilities.ipf @@ -872,13 +872,16 @@ Function CalculateLCMOfWave(wv) End /// @brief Returns an unsorted free wave with all unique entries from wv +/// If dontDuplicate is set, then for a single element input wave no new free wave is created but the input wave is returned. /// /// uses built-in igor function FindDuplicates. Entries are deleted from left to right. /// +/// @param wv wave reference, can be numeric or text +/// @param caseSensitive [optional, default = 1] Indicates whether comparison should be case sensitive. Applies only if the input wave is a text wave +/// @param dontDuplicate [optional, default = 0] for a single element input wave no new free wave is created but the input wave is returned. +/// /// @todo IP9-only Make threadsafe -Function/Wave GetUniqueEntries(wv, [caseSensitive]) - Wave wv - variable caseSensitive +Function/WAVE GetUniqueEntries(WAVE wv[, variable caseSensitive, variable dontDuplicate]) variable numRows @@ -887,21 +890,23 @@ Function/Wave GetUniqueEntries(wv, [caseSensitive]) numRows = DimSize(wv, ROWS) ASSERT(numRows == numpnts(wv), "Wave must be 1D") - if(IsTextWave(wv)) - if(ParamIsDefault(caseSensitive)) - caseSensitive = 1 - else - caseSensitive = !!caseSensitive - endif - - return GetUniqueTextEntries(wv, caseSensitive=caseSensitive) - endif + dontDuplicate = ParamIsDefault(dontDuplicate) ? 0 : !!dontDuplicate if(numRows <= 1) + if(dontDuplicate) + return wv + endif + Duplicate/FREE wv, result return result endif + if(IsTextWave(wv)) + caseSensitive = ParamIsDefault(caseSensitive) ? 1 : !!caseSensitive + + return GetUniqueTextEntries(wv, caseSensitive=caseSensitive) + endif + FindDuplicates/FREE/RN=result wv return result @@ -935,27 +940,26 @@ End /// Duplicates are removed from left to right /// /// @param wv text wave reference -/// @param caseSensitive [optional] Indicates whether comparison should be case sensitive. defaults to True +/// @param caseSensitive [optional, default = 1] Indicates whether comparison should be case sensitive. +/// @param dontDuplicate [optional, default = 0] for a single element input wave no new free wave is created but the input wave is returned. /// /// @return free wave with unique entries /// /// @todo IP9-only make threadsafe -static Function/Wave GetUniqueTextEntries(wv, [caseSensitive]) - Wave/T wv - variable caseSensitive +static Function/WAVE GetUniqueTextEntries(WAVE/T wv[, variable caseSensitive, variable dontDuplicate]) variable numEntries, numDuplicates, i - if(ParamIsDefault(caseSensitive)) - caseSensitive = 1 - else - caseSensitive = !!caseSensitive - endif + dontDuplicate = ParamIsDefault(dontDuplicate) ? 0 : !!dontDuplicate + caseSensitive = ParamIsDefault(caseSensitive) ? 1 : !!caseSensitive numEntries = DimSize(wv, ROWS) ASSERT(numEntries == numpnts(wv), "Wave must be 1D.") if(numEntries <= 1) + if(dontDuplicate) + return wv + endif Duplicate/T/FREE wv result return result endif diff --git a/Packages/Testing-MIES/UTF_Utils.ipf b/Packages/Testing-MIES/UTF_Utils.ipf index 7b23830a33..c8fd2131de 100644 --- a/Packages/Testing-MIES/UTF_Utils.ipf +++ b/Packages/Testing-MIES/UTF_Utils.ipf @@ -2090,6 +2090,14 @@ Function GUE_WorksWithOne() CHECK_EQUAL_WAVES(result, wv) End +static Function GUE_WorksWithOneNoDuplicate() + + Make/N=1 wv + + WAVE/Z result = GetUniqueEntries(wv, dontDuplicate=1) + CHECK(WaveRefsEqual(result, wv)) +End + Function GUE_BailsOutWith2D() Make/N=(1, 2) wv @@ -2119,6 +2127,14 @@ Function GUE_WorksWithTextOne() CHECK_EQUAL_WAVES(result, wv) End +static Function GUE_WorksWithTextOneNoDuplicate() + + Make/T/N=1 wv + + WAVE/Z result = GetUniqueEntries(wv, dontDuplicate=1) + CHECK(WaveRefsEqual(result, wv)) +End + Function GUE_IgnoresCase() Make/T wv = {"a", "A"} @@ -2180,6 +2196,14 @@ Function GUE_ListWorksWithSep() CHECK_EQUAL_STR(result, expected) End +static Function GUTE_WorksWithOneNoDuplicate() + + Make/T/N=1 wv + + WAVE/Z result = MIES_UTILS#GetUniqueTextEntries(wv, dontDuplicate=1) + CHECK(WaveRefsEqual(result, wv)) +End + /// @} /// GetListOfObjects From 85d599e2ac1bf20cc8f4aff67febce785f52b466 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 14 Apr 2022 15:06:52 +0200 Subject: [PATCH 48/49] SF: Use for GetUniqueEntries the dontDuplicate option - this gives a speeds boost if the input wave has only a single element --- Packages/MIES/MIES_SweepFormula.ipf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 99c2c52c98..beca28ede0 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1259,7 +1259,7 @@ static Function/WAVE SF_GetReducedColumn(WAVE w, string dimLabel) Duplicate/FREE/RMD=[][dimPos] w, wTmp Redimension/N=(-1) wTmp - WAVE wReduced = GetUniqueEntries(wTmp) + WAVE wReduced = GetUniqueEntries(wTmp, dontDuplicate=1) return wReduced End @@ -1279,7 +1279,7 @@ static Function [WAVE sweeps, WAVE/D channels] SF_ReCreateOldSweepsChannelLayout numSelected = DimSize(selectData, ROWS) Make/FREE/D/N=(numSelected) combined = selectData[p][%CHANNELTYPE] << shift + selectData[p][%CHANNELNUMBER] - WAVE combReduced = GetUniqueEntries(combined) + WAVE combReduced = GetUniqueEntries(combined, dontDuplicate=1) numCombined = DimSize(combReduced, ROWS) WAVE channels = SF_NewChannelsWave(numCombined) From 8803d9cc13bcfd96cb57dfa8e3bbde8f2c3341b1 Mon Sep 17 00:00:00 2001 From: Michael Huth Date: Thu, 14 Apr 2022 21:09:28 +0200 Subject: [PATCH 49/49] SF: Speed optimization of select operation - included "displayed" handling in SF_GetActiveChannelNumbersForSweeps --- Packages/MIES/MIES_SweepFormula.ipf | 258 ++++++++++++++-------------- 1 file changed, 131 insertions(+), 127 deletions(-) diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index beca28ede0..cd01285823 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -1058,66 +1058,6 @@ static Function SF_GetDAChannel(string graph, variable sweep, variable channelTy return NaN End -static Function/WAVE SF_FilterSelectDataFromDisplayed(string graph, WAVE selectData) - - variable i, j, index - variable numEntries, numTraces - variable sweepNo, channelType, channelNumber - variable dimPosSweepNo, dimPosChannelType, dimPosChannelNumber - variable dimPosSelectSweepNo, dimPosSelectChannelType, dimPosSelectChannelNumber - - ASSERT(WindowExists(graph), "graph window does not exist") - - numEntries = DimSize(selectData, ROWS) - - WAVE/T/Z traces = GetTraceInfos(graph) - if(!WaveExists(traces)) - DebugPrint("No traces found for extracting sweep wave locations.") - return $"" - endif - numTraces = DimSize(traces, ROWS) - - Duplicate/FREE selectData, selectDataFiltered - dimPosSweepNo = FindDimLabel(traces, COLS, "sweepNumber") - dimPosChannelType = FindDimLabel(traces, COLS, "channelType") - dimPosChannelNumber = FindDimLabel(traces, COLS, "channelNumber") - - dimPosSelectSweepNo = FindDimLabel(selectData, COLS, "SWEEP") - dimPosSelectChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") - dimPosSelectChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") - - WAVE selectDisplayed = SF_NewSelectDataWave(numTraces, 1) - selectDisplayed[][%SWEEP] = str2num(traces[p][dimPosSweepNo]) - selectDisplayed[][%CHANNELTYPE] = WhichListItem(traces[p][dimPosChannelType], XOP_CHANNEL_NAMES) - selectDisplayed[][%CHANNELNUMBER] = str2num(traces[p][dimPosChannelNumber]) - - for(i = 0; i < numEntries; i += 1) - - sweepNo = selectData[i][%SWEEP] - channelType = selectData[i][%CHANNELTYPE] - channelNumber = selectData[i][%CHANNELNUMBER] - - for(j = 0; j < numTraces; j += 1) - - if(selectDisplayed[j][dimPosSelectSweepNo] == sweepNo && selectDisplayed[j][dimPosSelectChannelType] == channelType && selectDisplayed[j][dimPosSelectChannelNumber] == channelNumber) - - selectDataFiltered[index][%SWEEP] = sweepNo - selectDataFiltered[index][%CHANNELTYPE] = channelType - selectDataFiltered[index][%CHANNELNUMBER] = channelNumber - index += 1 - - break - endif - endfor - endfor - if(!index) - return $"" - endif - Redimension/N=(index, -1) selectDataFiltered - - return selectDataFiltered -End - static Function/WAVE SF_GetSweepsForFormula(string graph, WAVE range, WAVE selectData) variable i, j, rangeStart, rangeEnd, pOffset, delta, numRows, DAChannel, numSweeps, sweepNo @@ -1340,38 +1280,82 @@ End /// @param graph DataBrowser or SweepBrowser reference graph /// @param channels @c SF_FormulaExecutor style @c channels() wave /// @param sweeps @c SF_FormulaExecutor style @c sweeps() wave -/// @param entrySourceType type of the labnotebook entry, one of @ref DataAcqModes. -/// If you don't care about the entry source type pass #UNKNOWN_MODE. +/// @param fromDisplayed boolean variable, if set the selectdata is determined from the displayed sweeps +/// /// @return a selectData style wave with three columns /// containing sweepNumber, channelType and channelNumber -static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE channels, WAVE sweeps, variable entrySourceType) +static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE channels, WAVE sweeps, variable fromDisplayed) - variable i, j, k, l, channelType, channelNumber, sweepNo, outIndex + variable i, j, k, l, channelType, channelNumber, sweepNo, sweepNoT, outIndex variable numSweeps, numInChannels, numSettings, maxChannels, activeChannel, numActiveChannels variable isSweepBrowser, cIndex variable dimPosSweep, dimPosChannelNumber, dimPosChannelType + variable dimPosTSweep, dimPosTChannelNumber, dimPosTChannelType + variable numTraces string setting, settingList, msg, device, dataFolder, singleSweepDFStr - ASSERT(windowExists(graph), "DB/SB not specified.") - SF_ASSERT(DimSize(channels, COLS) == 2, "A channel input consists of [[channelType, channelNumber]+].") - SetDimLabel COLS, 0, channelType, channels - SetDimLabel COLS, 1, channelNumber, channels - SF_ASSERT(DimSize(sweeps, COLS) < 2, "Sweeps are one-dimensional.") - SF_ASSERT((IsNaN(UNKNOWN_MODE) && IsNaN(entrySourceType)) || \ - entrySourceType == DATA_ACQUISITION_MODE || \ - entrySourceType == TEST_PULSE_MODE || \ - entrySourceType == NUMBER_OF_LBN_DAQ_MODES, \ - "Undefined labnotebook mode. Use one in group DataAcqModes") + if(!DimSize(sweeps, ROWS) || !DimSize(channels, ROWS)) + return $"" + endif - isSweepBrowser = BSP_IsSweepBrowser(graph) + fromDisplayed = !!fromDisplayed - if(isSweepBrowser) - DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) - WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + if(fromDisplayed) + WAVE/T/Z traces = GetTraceInfos(graph) + if(!WaveExists(traces)) + return $"" + endif + numTraces = DimSize(traces, ROWS) + dimPosTSweep = FindDimLabel(traces, COLS, "sweepNumber") + Make/FREE/D/N=(numTraces) displayedSweeps = str2num(traces[p][dimPosTSweep]) + WAVE displayedSweepsUnique = GetUniqueEntries(displayedSweeps, dontDuplicate=1) + MatrixOp/FREE sweepsDP = fp64(sweeps) + WAVE/Z sweepsIntersect = GetSetIntersection(sweepsDP, displayedSweepsUnique) + if(!WaveExists(sweepsIntersect)) + return $"" + endif + WAVE sweeps = sweepsIntersect + numSweeps = DimSize(sweeps, ROWS) + + WAVE selectDisplayed = SF_NewSelectDataWave(numTraces, 1) + dimPosSweep = FindDimLabel(selectDisplayed, COLS, "SWEEP") + dimPosChannelType = FindDimLabel(selectDisplayed, COLS, "CHANNELTYPE") + dimPosChannelNumber = FindDimLabel(selectDisplayed, COLS, "CHANNELNUMBER") + + dimPosTChannelType = FindDimLabel(traces, COLS, "channelType") + dimPosTChannelNumber = FindDimLabel(traces, COLS, "channelNumber") + for(i = 0; i < numSweeps; i += 1) + sweepNo = sweeps[i] + for(j = 0; j < numTraces; j += 1) + sweepNoT = str2num(traces[j][dimPosTSweep]) + if(sweepNo == sweepNoT) + selectDisplayed[outIndex][dimPosSweep] = sweepNo + selectDisplayed[outIndex][dimPosChannelType] = WhichListItem(traces[j][dimPosTChannelType], XOP_CHANNEL_NAMES) + selectDisplayed[outIndex][dimPosChannelNumber] = str2num(traces[j][dimPosTChannelNumber]) + outIndex += 1 + endif + if(outIndex == numTraces) + break + endif + endfor + if(outIndex == numTraces) + break + endif + endfor + Redimension/N=(outIndex, -1) selectDisplayed + numTraces = outIndex + + outIndex = 0 else - SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") - device = BSP_GetDevice(graph) - DFREF deviceDFR = GetDeviceDataPath(device) + isSweepBrowser = BSP_IsSweepBrowser(graph) + if(isSweepBrowser) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + WAVE/T sweepMap = GetSweepBrowserMap(sweepBrowserDFR) + else + SF_ASSERT(BSP_HasBoundDevice(graph), "No device bound.") + device = BSP_GetDevice(graph) + DFREF deviceDFR = GetDeviceDataPath(device) + endif endif // search sweeps for active channels @@ -1379,10 +1363,11 @@ static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE chan numInChannels = DimSize(channels, ROWS) WAVE selectData = SF_NewSelectDataWave(numSweeps, NUM_DA_TTL_CHANNELS + NUM_AD_CHANNELS) - - dimPosSweep = FindDimLabel(selectData, COLS, "SWEEP") - dimPosChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") - dimPosChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") + if(!fromDisplayed) + dimPosSweep = FindDimLabel(selectData, COLS, "SWEEP") + dimPosChannelType = FindDimLabel(selectData, COLS, "CHANNELTYPE") + dimPosChannelNumber = FindDimLabel(selectData, COLS, "CHANNELNUMBER") + endif for(i = 0; i < numSweeps; i += 1) sweepNo = sweeps[i] @@ -1391,25 +1376,26 @@ static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE chan continue endif - if(isSweepBrowser) - cIndex = FindDimLabel(sweepMap, COLS, "Sweep") - FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap - if(V_value == -1) + if(!fromDisplayed) + if(isSweepBrowser) + cIndex = FindDimLabel(sweepMap, COLS, "Sweep") + FindValue/RMD=[][cIndex]/TEXT=num2istr(sweepNo)/TXOP=4 sweepMap + if(V_value == -1) + continue + endif + dataFolder = sweepMap[V_row][%DataFolder] + device = sweepMap[V_row][%Device] + DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) + endif + singleSweepDFStr = GetSingleSweepFolderAsString(deviceDFR, sweepNo) + if(!DataFolderExists(singleSweepDFStr)) + continue + endif + DFREF sweepDFR = $singleSweepDFStr + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + if(!WaveExists(numericalValues)) continue endif - dataFolder = sweepMap[V_row][%DataFolder] - device = sweepMap[V_row][%Device] - DFREF deviceDFR = GetAnalysisSweepPath(dataFolder, device) - endif - singleSweepDFStr = GetSingleSweepFolderAsString(deviceDFR, sweepNo) - if(!DataFolderExists(singleSweepDFStr)) - continue - endif - DFREF sweepDFR = $singleSweepDFStr - - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - if(!WaveExists(numericalValues)) - continue endif for(j = 0; j < numInChannels; j += 1) @@ -1450,29 +1436,52 @@ static Function/WAVE SF_GetActiveChannelNumbersForSweeps(string graph, WAVE chan break endswitch - WAVE/Z activeChannels = GetLastSetting(numericalValues, sweepNo, setting, entrySourceType) - if(!WaveExists(activeChannels)) - continue - endif - if(IsNaN(channelNumber)) - // faster than ZapNaNs due to no mem alloc - numActiveChannels = DimSize(activeChannels, ROWS) - for(l = 0; l < numActiveChannels; l += 1) - activeChannel = activeChannels[l] - if(!IsNaN(activeChannel) && activeChannel < maxChannels) + if(fromDisplayed) + for(l = 0; l < numTraces; l += 1) + if(IsNaN(channelNumber)) + if(sweepNo == selectDisplayed[l][dimPosSweep] && channelType == selectDisplayed[l][dimPosChannelType]) + activeChannel = selectDisplayed[l][dimPosChannelNumber] + if(activeChannel < maxChannels) + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = selectDisplayed[l][dimPosChannelNumber] + outIndex += 1 + endif + endif + else + if(sweepNo == selectDisplayed[l][dimPosSweep] && channelType == selectDisplayed[l][dimPosChannelType] && channelNumber == selectDisplayed[l][dimPosChannelNumber] && channelNumber < maxChannels) + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = channelNumber + outIndex += 1 + endif + endif + endfor + else + WAVE/Z activeChannels = GetLastSetting(numericalValues, sweepNo, setting, DATA_ACQUISITION_MODE) + if(!WaveExists(activeChannels)) + continue + endif + if(IsNaN(channelNumber)) + // faster than ZapNaNs due to no mem alloc + numActiveChannels = DimSize(activeChannels, ROWS) + for(l = 0; l < numActiveChannels; l += 1) + activeChannel = activeChannels[l] + if(!IsNaN(activeChannel) && activeChannel < maxChannels) + selectData[outIndex][dimPosSweep] = sweepNo + selectData[outIndex][dimPosChannelType] = channelType + selectData[outIndex][dimPosChannelNumber] = activeChannel + outIndex += 1 + endif + endfor + elseif(channelNumber < maxChannels) + FindValue/V=(channelNumber) activeChannels + if(V_Value >= 0) selectData[outIndex][dimPosSweep] = sweepNo selectData[outIndex][dimPosChannelType] = channelType - selectData[outIndex][dimPosChannelNumber] = activeChannel + selectData[outIndex][dimPosChannelNumber] = channelNumber outIndex += 1 endif - endfor - elseif(channelNumber < maxChannels) - FindValue/V=(channelNumber) activeChannels - if(V_Value >= 0) - selectData[outIndex][dimPosSweep] = sweepNo - selectData[outIndex][dimPosChannelType] = channelType - selectData[outIndex][dimPosChannelNumber] = channelNumber - outIndex += 1 endif endif @@ -2589,12 +2598,7 @@ static Function/WAVE SF_OperationSelect(variable jsonId, string jsonPath, string endif endif - WAVE/Z out = SF_GetActiveChannelNumbersForSweeps(graph, channels, sweeps, DATA_ACQUISITION_MODE) - if(!CmpStr(mode, "displayed") && WaveExists(out)) - WAVE selectDataAll = out - WAVE/Z out = SF_FilterSelectDataFromDisplayed(graph, selectDataAll) - endif - + WAVE/Z out = SF_GetActiveChannelNumbersForSweeps(graph, channels, sweeps, !CmpStr(mode, "displayed")) if(!WaveExists(out)) DebugPrint("Call to SF_GetSweepNumbersForSelect returned no results") WAVE out = SF_GetDefaultEmptyWave()