Skip to content

Commit

Permalink
Merge pull request #2015 from AllenInstitute/feature/2015-create_data…
Browse files Browse the repository at this point in the history
…confiuration_from_lnb

Recreate DataConfigurationresult Structure from Labnotebook
  • Loading branch information
t-b authored Mar 24, 2024
2 parents ad735dd + 2400409 commit ee7e65f
Show file tree
Hide file tree
Showing 12 changed files with 799 additions and 118 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ jobs:
experiment: Packages/tests/Compilation/CompilationTester.pxp
installer_flags: "-s git"
artifact_name: Compilation-Each-Commit-assets
timeout_minutes: 60
timeout_minutes: 120

Documentation:
name: 👷 Documentation
Expand Down
490 changes: 467 additions & 23 deletions Packages/MIES/MIES_DataConfigurator.ipf

Large diffs are not rendered by default.

93 changes: 85 additions & 8 deletions Packages/MIES/MIES_Epochs.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -1066,12 +1066,11 @@ End
/// - Descending ending time
/// - Ascending tree level
///
/// @param[in] device title of device panel
static Function EP_SortEpochs(string device)
/// @param[in] epochWave epoch wave
Function EP_SortEpochs(WAVE/T epochWave)

variable channel, channelCnt, epochCnt, channelType

WAVE/T epochWave = GetEpochsWave(device)
channelCnt = DimSize(epochWave, LAYERS)
for(channelType = 0; channelType < XOP_CHANNEL_TYPE_COUNT; channelType += 1)
for(channel = 0; channel < channelCnt; channel += 1)
Expand Down Expand Up @@ -1162,7 +1161,7 @@ End
static Function EP_AddEpoch(WAVE/T epochWave, variable channel, variable channelType, variable epBegin, variable epEnd, string epTags, string epShortName, variable level, [variable lowerlimit, variable upperlimit])

variable i, j, numEpochs, pos
string entry, startTimeStr, endTimeStr
string entry, startTimeStr, endTimeStr, msg

lowerlimit = ParamIsDefault(lowerlimit) ? -Inf : lowerlimit
upperlimit = ParamIsDefault(upperlimit) ? Inf : upperlimit
Expand Down Expand Up @@ -1197,6 +1196,9 @@ static Function EP_AddEpoch(WAVE/T epochWave, variable channel, variable channel
epochWave[i][%EndTime][channel][channelType] = endTimeStr
epochWave[i][%Tags][channel][channelType] = epTags
epochWave[i][%TreeLevel][channel][channelType] = num2str(level)

sprintf msg, "AddEpoch (chan, chanType, Lvl, Start, End, Tags): %d %d %d %s %s %s\r", channel, channelType, level, startTimeStr, endTimeStr, epTags
DEBUGPRINT(msg)
End

/// @brief Write the epoch info into the sweep settings wave
Expand All @@ -1213,7 +1215,8 @@ Function EP_WriteEpochInfoIntoSweepSettings(string device, variable sweepNo, var

EP_AdaptEpochInfo(device, configWave, acquiredTime, plannedTime)

EP_SortEpochs(device)
WAVE/T epochWave = GetEpochsWave(device)
EP_SortEpochs(epochWave)

WAVE DACList = GetDACListFromConfig(configWave)
numDACEntries = DimSize(DACList, ROWS)
Expand Down Expand Up @@ -1259,6 +1262,7 @@ threadsafe Function/WAVE EP_EpochStrToWave(string epochStr)

ASSERT_TS(!IsEmpty(epochStr), "No information in epochStr")
WAVE/T epochWave = ListToTextWaveMD(epochStr, 2, rowSep = EPOCH_LIST_ROW_SEP, colSep = EPOCH_LIST_COL_SEP)
SetEpochsDimensionLabelsSingleChannel(epochWave)

return epochWave
End
Expand Down Expand Up @@ -1314,12 +1318,26 @@ End

static Function EP_AdaptEpochInfoChannel(string device, variable channelNumber, variable channelType, variable acquiredTime, variable plannedTime)

variable epochCnt, epoch, startTime, endTime, samplingInterval, lastValidIndex, acquiredEpochsEndTime
string tags
variable samplingInterval

WAVE/T epochWave = GetEpochsWave(device)

samplingInterval = DAP_GetSampInt(device, DATA_ACQUISITION_MODE, channelType)
EP_AdaptEpochInfoChannelImpl(epochWave, channelNumber, channelType, samplingInterval, acquiredTime, plannedTime)
End

/// @brief Device independent implementation of EP_AdaptEpochInfoChannel
/// @param epochWave epoch wave (4d)
/// @param channelNumber GUI channel number
/// @param channelType channel type
/// @param samplingInterval sampling interval of channel type
/// @param acquiredTime acquiredTime in [s]
/// @param plannedTime plannedTime in [s]
Function EP_AdaptEpochInfoChannelImpl(WAVE/T epochWave, variable channelNumber, variable channelType, variable samplingInterval, variable acquiredTime, variable plannedTime)

variable epochCnt, epoch, startTime, endTime
variable acquiredEpochsEndTime, lastValidIndex
string tags

epochCnt = EP_GetEpochCount(epochWave, channelNumber, channelType)
if(IsNaN(acquiredTime))
acquiredEpochsEndTime = plannedTime
Expand Down Expand Up @@ -1602,3 +1620,62 @@ Function EP_GetEpochAmplitude(string epochTag)

return NumberByKey(EPOCH_AMPLITUDE_KEY, epochTag, STIMSETKEYNAME_SEP, EPOCHNAME_SEP)
End

/// @brief Recreate DA epochs from loaded data. The following must be loaded: LabNotebook, Sweep data of sweepNo, Stimsets used in the sweep
/// User epochs are not recreated !
///
/// @param numericalValues numerical LabNotebook
/// @param textualValues textual LabNotebook
/// @param sweepDFR single sweep folder, e.g. for measurement with a device this wold be DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo)
/// @param sweepNo sweep number
/// @returns recreated 4D epoch wave
Function/WAVE EP_RecreateEpochsFromLoadedData(WAVE numericalValues, WAVE/T textualValues, DFREF sweepDFR, variable sweepNo)

STRUCT DataConfigurationResult s
variable channelNr, plannedTime, acquiredTime, adSize, firstUnacquiredIndex

[s] = DC_RecreateDataConfigurationResultFromLNB(numericalValues, textualValues, sweepDFR, sweepNo)

WAVE/T recEpochWave = GetEpochsWaveAsFree()
EP_CollectEpochInfoDA(recEpochWave, s)

WAVE/Z channelDA = GetDAQDataSingleColumnWaveNG(numericalValues, textualValues, sweepNo, sweepDFR, XOP_CHANNEL_TYPE_DAC, s.DACList[0])
ASSERT(WaveExists(channelDA), "Could not retrieve first DA sweep")
WAVE/Z channelAD = GetDAQDataSingleColumnWaveNG(numericalValues, textualValues, sweepNo, sweepDFR, XOP_CHANNEL_TYPE_ADC, s.ADCList[0])
ASSERT(WaveExists(channelAD), "Could not retrieve first AD sweep")
adSize = DimSize(channelAD, ROWS)
firstUnacquiredIndex = FindFirstNaNIndex(channelAD)
if(IsNaN(firstUnacquiredIndex))
firstUnacquiredIndex = adSize
endif
[plannedTime, acquiredTime] = SWS_DeterminePlannedAndAcquiredTime(channelDA, channelAD, adSize, firstUnacquiredIndex)
for(channelNr : s.DACList)
EP_AdaptEpochInfoChannelImpl(recEpochWave, channelNr, XOP_CHANNEL_TYPE_DAC, s.samplingInterval, acquiredTime, plannedTime)
endfor
EP_SortEpochs(recEpochWave)

return recEpochWave
End

/// @brief Fetches a single epoch channel from a recreated epoch wave.
/// The returned epoch channel wave has the same form as epoch information that was stored in the LNB returned by @ref EP_FetchEpochs
///
/// @param epochWave 4d epoch wave
/// @param channelNumber GUI channel number
/// @param channelType channel type, one of @ref XopChannelConstants
/// @returns epoch channel wave (2d)
Function/WAVE EP_FetchEpochsFromRecreated(WAVE epochWave, variable channelNumber, variable channelType)

string epList

epList = EP_EpochWaveToStr(epochWave, channelNumber, channelType)
if(IsEmpty(epList))
return $""
endif
WAVE epChannel = EP_EpochStrToWave(epList)
if(!DimSize(epChannel, ROWS))
return $""
endif

return epChannel
End
46 changes: 24 additions & 22 deletions Packages/MIES/MIES_OptimzedOverlapDistributedAcquisition.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -346,37 +346,39 @@ End
static Function/Wave OOD_CreateStimSet(params)
STRUCT OOdDAQParams &params

variable i, numSets, length
variable offset, cutoff, column
variable level = 1e-3
variable numSets

numSets = DimSize(params.stimSets, ROWS)
ASSERT(numSets == DimSize(params.offsets, ROWS), "Mismatched offsets wave size")

Make/WAVE/FREE/N=(numSets) stimSetsWithOffset

for(i = 0; i < numSets ; i += 1)
WAVE stimSet = params.stimSets[i]
offset = params.offsets[i]
ASSERT(offset >= 0 , "Invalid offset")
length = DimSize(stimSet, ROWS) + offset
column = params.setColumns[i]
Make/FREE/N=(length) acc
stimSetsWithOffset[] = OOD_OffsetStimSetColAndCutoff(params.stimSets[p], params.setColumns[p], params.offsets[p], params.postFeaturePoints)

Multithread acc[offset, *] = stimSet[p - offset][column]
CopyScales/P stimSet, acc
Note acc, note(stimSet)
return stimSetsWithOffset
End

// remove empty space beyond `postFeatureTime` at the end
FindLevel/P/EDGE=2/Q/R=[DimSize(acc, ROWS) - 1, 0] acc, level
Function/WAVE OOD_OffsetStimSetColAndCutoff(WAVE stimSet, variable column, variable offset, variable postFeaturePoints)

if(!V_flag && acc[length - 1] < level)
cutoff = V_levelX + params.postFeaturePoints
Redimension/N=(cutoff) acc
endif
variable length, cutoff
variable level = 1e-3

stimSetsWithOffset[i] = acc
endfor
ASSERT(offset >= 0 , "Invalid offset")
length = DimSize(stimSet, ROWS) + offset

return stimSetsWithOffset
Make/FREE/N=(length) acc

Multithread acc[offset, *] = stimSet[p - offset][column]
CopyScales/P stimSet, acc
Note acc, note(stimSet)

// remove empty space beyond `postFeatureTime` at the end
FindLevel/P/EDGE=2/Q/R=[DimSize(acc, ROWS) - 1, 0] acc, level

if(!V_flag && acc[length - 1] < level)
cutoff = V_levelX + postFeaturePoints
Redimension/N=(cutoff) acc
endif

return acc
End
50 changes: 38 additions & 12 deletions Packages/MIES/MIES_SweepSaving.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -87,27 +87,29 @@ End
/// @retval acquiredTime if acquisition was aborted early, time of last acquired point in AD wave [s], NaN otherwise
static Function [variable plannedTime, variable acquiredTime] SWS_ProcessDATTLChannelsOnEarlyAcqStop(string device, WAVE/WAVE scaledDataWave, WAVE config)

variable i, numChannels, firstUnAcquiredIndex, adcSize
variable firstUnAcquiredADIndex
variable firstUnAcquiredIndex, adSize

NVAR fifoPosGlobal = $GetFifoPosition(device)
ASSERT(!IsNaN(fifoPosGlobal), "Invalid fifoPosGlobal")

adcSize = HW_GetEffectiveADCWaveLength(device, DATA_ACQUISITION_MODE)
adSize = HW_GetEffectiveADCWaveLength(device, DATA_ACQUISITION_MODE)

WAVE channelDA = scaledDataWave[0]
WAVE channelAD = scaledDataWave[GetFirstADCChannelIndex(config)]
firstUnAcquiredADIndex = FindFirstNaNIndex(channelAD)
if(IsNaN(firstUnAcquiredADIndex))
firstUnAcquiredADIndex = adcSize
endif
ASSERT(firstUnAcquiredADIndex == fifoPosGlobal, "Mismatch of NaN boundary in ADC channel to last fifo position")

plannedTime = IndexToScale(channelDA, DimSize(channelDA, ROWS), ROWS) * MILLI_TO_ONE
if(fifoPosGlobal == adcSize)
return [plannedTime, NaN]
[plannedTime, acquiredTime] = SWS_DeterminePlannedAndAcquiredTime(channelDA, channelAD, adSize, fifoPosGlobal)
if(!IsNaN(acquiredTime))
SWS_SetUnacquiredTimeInADCToNaN(config, scaledDataWave, acquiredTime)
endif

acquiredTime = fifoPosGlobal ? IndexToScale(channelAD, max(fifoPosGlobal - 1, 0), ROWS) * MILLI_TO_ONE : 0
return [plannedTime, acquiredTime]
End

/// @brief Sets the data points in the AD channels of the unacquired time interval to NaN
static Function SWS_SetUnacquiredTimeInADCToNaN(WAVE config, WAVE/WAVE scaledDataWave, variable acquiredTime)

variable i, numChannels, firstUnAcquiredIndex

numChannels = DimSize(config, ROWS)
for(i = 0; i < numChannels; i += 1)
if(!(config[i][%ChannelType] == XOP_CHANNEL_TYPE_DAC || config[i][%ChannelType] == XOP_CHANNEL_TYPE_TTL))
Expand All @@ -120,6 +122,30 @@ static Function [variable plannedTime, variable acquiredTime] SWS_ProcessDATTLCh
endif
MultiThread channel[firstUnAcquiredIndex, Inf] = NaN
endfor
End

/// @brief Determines the acquired and planned time
///
/// @param channelDA DA channel, as all DA channels run synchroneously, the first DA channel is good enough
/// @param channelAD AD channel, as all AD channels run synchroneously, the first AD channel is good enough
/// @param adSize effective size of the AD channel, where data was sampled (for ITC the AD wave can be longer)
/// @param lastFifoPos at this index - 1 was the last data point sampled
Function [variable plannedTime, variable acquiredTime] SWS_DeterminePlannedAndAcquiredTime(WAVE channelDA, WAVE channelAD, variable adSize, variable lastFifoPos)

variable firstUnAcquiredADIndex

firstUnAcquiredADIndex = FindFirstNaNIndex(channelAD)
if(IsNaN(firstUnAcquiredADIndex))
firstUnAcquiredADIndex = adSize
endif
ASSERT(firstUnAcquiredADIndex == lastFifoPos, "Mismatch of NaN boundary in ADC channel to last fifo position")

plannedTime = IndexToScale(channelDA, DimSize(channelDA, ROWS), ROWS) * MILLI_TO_ONE
if(lastFifoPos == adSize)
return [plannedTime, NaN]
endif

acquiredTime = lastFifoPos ? IndexToScale(channelAD, max(lastFifoPos - 1, 0), ROWS) * MILLI_TO_ONE : 0

return [plannedTime, acquiredTime]
End
Expand Down
82 changes: 49 additions & 33 deletions Packages/MIES/MIES_TestPulse.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -1488,18 +1488,22 @@ Function TP_CreateTestPulseWave(device, dataAcqOrTP)
variable totalLengthPoints, pulseStartPoints, pulseLengthPoints

WAVE TestPulse = GetTestPulse()
WAVE TPSettingsCalc = GetTPsettingsCalculated(device)

[totalLengthPoints, pulseStartPoints, pulseLengthPoints] = TP_GetCreationPropertiesInPoints(TPSettingsCalc, dataAcqOrTP)
TP_CreateTestPulseWaveImpl(TestPulse, totalLengthPoints, pulseStartPoints, pulseLengthPoints)
End

[totalLengthPoints, pulseStartPoints, pulseLengthPoints] = TP_GetCreationPropertiesInPoints(device, dataAcqOrTP)
/// @brief device independent test pulse wave creation
Function TP_CreateTestPulseWaveImpl(WAVE tp, variable totalLength, variable pulseStart, variable pulseLength)

Redimension/N=(totalLengthPoints) TestPulse
FastOp TestPulse = 0
Redimension/N=(totalLength) tp
FastOp tp = 0

TestPulse[pulseStartPoints, pulseStartPoints + pulseLengthPoints] = 1
MultiThread tp[pulseStart, pulseStart + pulseLength] = 1
End

Function [variable totalLengthPoints, variable pulseStartPoints, variable pulseLengthPoints] TP_GetCreationPropertiesInPoints(string device, variable dataAcqOrTP)

WAVE TPSettingsCalc = GetTPsettingsCalculated(device)
Function [variable totalLengthPoints, variable pulseStartPoints, variable pulseLengthPoints] TP_GetCreationPropertiesInPoints(WAVE TPSettingsCalc, variable dataAcqOrTP)

totalLengthPoints = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%totalLengthPointsTP] : TPSettingsCalc[%totalLengthPointsDAQ]
pulseStartPoints = (dataAcqOrTP == TEST_PULSE_MODE) ? TPSettingsCalc[%pulseStartPointsTP] : TPSettingsCalc[%pulseStartPointsDAQ]
Expand Down Expand Up @@ -1547,39 +1551,51 @@ End

Function TP_UpdateTPSettingsCalculated(string device)

variable interTPDAC, interDAQDAC, interTPADC, interDAQADC, factorTP, factorDAQ

WAVE TPSettings = GetTPSettings(device)
WAVE calculated = GetTPSettingsCalculated(device)
calculated = NaN
WAVE samplingIntervals = GetNewSamplingIntervalsAsFree()
samplingIntervals[%SI_TP_DAC] = DAP_GetSampInt(device, TEST_PULSE_MODE, XOP_CHANNEL_TYPE_DAC)
samplingIntervals[%SI_DAQ_DAC] = DAP_GetSampInt(device, DATA_ACQUISITION_MODE, XOP_CHANNEL_TYPE_DAC)
samplingIntervals[%SI_TP_ADC] = DAP_GetSampInt(device, TEST_PULSE_MODE, XOP_CHANNEL_TYPE_ADC)
samplingIntervals[%SI_DAQ_ADC] = DAP_GetSampInt(device, DATA_ACQUISITION_MODE, XOP_CHANNEL_TYPE_ADC)

TP_UpdateTPSettingsCalculatedImpl(TPSettings, samplingIntervals, calculated)
End

/// @brief Device and globals independent calculcation of TPSettingsCalculated
Function TP_UpdateTPSettingsCalculatedImpl(WAVE TPSettings, WAVE samplingIntervals, WAVE tpCalculated)

variable interTPDAC, interDAQDAC, interTPADC, interDAQADC, factorTP, factorDAQ

tpCalculated = NaN

interTPDAC = DAP_GetSampInt(device, TEST_PULSE_MODE, XOP_CHANNEL_TYPE_DAC) * MICRO_TO_MILLI
interDAQDAC = DAP_GetSampInt(device, DATA_ACQUISITION_MODE, XOP_CHANNEL_TYPE_DAC) * MICRO_TO_MILLI
interTPADC = DAP_GetSampInt(device, TEST_PULSE_MODE, XOP_CHANNEL_TYPE_ADC) * MICRO_TO_MILLI
interDAQADC = DAP_GetSampInt(device, DATA_ACQUISITION_MODE, XOP_CHANNEL_TYPE_ADC) * MICRO_TO_MILLI
interTPDAC = samplingIntervals[%SI_TP_DAC] * MICRO_TO_MILLI
interDAQDAC = samplingIntervals[%SI_DAQ_DAC] * MICRO_TO_MILLI
interTPADC = samplingIntervals[%SI_TP_ADC] * MICRO_TO_MILLI
interDAQADC = samplingIntervals[%SI_DAQ_ADC] * MICRO_TO_MILLI
factorTP = interTPDAC / interTPADC
factorDAQ = interDAQDAC / interDAQADC

// update the calculated values
calculated[%baselineFrac] = TPSettings[%baselinePerc][INDEP_HEADSTAGE] * PERCENT_TO_ONE

calculated[%pulseLengthMS] = TPSettings[%durationMS][INDEP_HEADSTAGE] // here for completeness
calculated[%pulseLengthPointsTP] = trunc(TPSettings[%durationMS][INDEP_HEADSTAGE] / interTPDAC)
calculated[%pulseLengthPointsDAQ] = trunc(TPSettings[%durationMS][INDEP_HEADSTAGE] / interDAQDAC)
calculated[%pulseLengthPointsTP_ADC] = trunc(calculated[%pulseLengthPointsTP] * factorTP)
calculated[%pulseLengthPointsDAQ_ADC] = trunc(calculated[%pulseLengthPointsDAQ] * factorDAQ)

calculated[%totalLengthMS] = TP_CalculateTestPulseLength(calculated[%pulseLengthMS], calculated[%baselineFrac])
calculated[%totalLengthPointsTP] = trunc(TP_CalculateTestPulseLength(calculated[%pulseLengthPointsTP], calculated[%baselineFrac]))
calculated[%totalLengthPointsDAQ] = trunc(TP_CalculateTestPulseLength(calculated[%pulseLengthPointsDAQ], calculated[%baselineFrac]))
calculated[%totalLengthPointsTP_ADC] = trunc(calculated[%totalLengthPointsTP] * factorTP)
calculated[%totalLengthPointsDAQ_ADC] = trunc(calculated[%totalLengthPointsDAQ] * factorDAQ)

calculated[%pulseStartMS] = calculated[%baselineFrac] * calculated[%totalLengthMS]
calculated[%pulseStartPointsTP] = trunc(calculated[%baselineFrac] * calculated[%totalLengthPointsTP])
calculated[%pulseStartPointsDAQ] = trunc(calculated[%baselineFrac] * calculated[%totalLengthPointsDAQ])
calculated[%pulseStartPointsTP_ADC] = trunc(calculated[%pulseStartPointsTP] * factorTP)
calculated[%pulseStartPointsDAQ_ADC] = trunc(calculated[%pulseStartPointsDAQ] * factorDAQ)
tpCalculated[%baselineFrac] = TPSettings[%baselinePerc][INDEP_HEADSTAGE] * PERCENT_TO_ONE

tpCalculated[%pulseLengthMS] = TPSettings[%durationMS][INDEP_HEADSTAGE] // here for completeness
tpCalculated[%pulseLengthPointsTP] = trunc(TPSettings[%durationMS][INDEP_HEADSTAGE] / interTPDAC)
tpCalculated[%pulseLengthPointsDAQ] = trunc(TPSettings[%durationMS][INDEP_HEADSTAGE] / interDAQDAC)
tpCalculated[%pulseLengthPointsTP_ADC] = trunc(tpCalculated[%pulseLengthPointsTP] * factorTP)
tpCalculated[%pulseLengthPointsDAQ_ADC] = trunc(tpCalculated[%pulseLengthPointsDAQ] * factorDAQ)

tpCalculated[%totalLengthMS] = TP_CalculateTestPulseLength(tpCalculated[%pulseLengthMS], tpCalculated[%baselineFrac])
tpCalculated[%totalLengthPointsTP] = trunc(TP_CalculateTestPulseLength(tpCalculated[%pulseLengthPointsTP], tpCalculated[%baselineFrac]))
tpCalculated[%totalLengthPointsDAQ] = trunc(TP_CalculateTestPulseLength(tpCalculated[%pulseLengthPointsDAQ], tpCalculated[%baselineFrac]))
tpCalculated[%totalLengthPointsTP_ADC] = trunc(tpCalculated[%totalLengthPointsTP] * factorTP)
tpCalculated[%totalLengthPointsDAQ_ADC] = trunc(tpCalculated[%totalLengthPointsDAQ] * factorDAQ)

tpCalculated[%pulseStartMS] = tpCalculated[%baselineFrac] * tpCalculated[%totalLengthMS]
tpCalculated[%pulseStartPointsTP] = trunc(tpCalculated[%baselineFrac] * tpCalculated[%totalLengthPointsTP])
tpCalculated[%pulseStartPointsDAQ] = trunc(tpCalculated[%baselineFrac] * tpCalculated[%totalLengthPointsDAQ])
tpCalculated[%pulseStartPointsTP_ADC] = trunc(tpCalculated[%pulseStartPointsTP] * factorTP)
tpCalculated[%pulseStartPointsDAQ_ADC] = trunc(tpCalculated[%pulseStartPointsDAQ] * factorDAQ)
End

/// @brief Convert from row names of GetTPSettings()/GetTPSettingsCalculated() to GetTPSettingsLBN() column names.
Expand Down
Loading

0 comments on commit ee7e65f

Please sign in to comment.