diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 8dda37a6fc..fee0a05de4 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -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 diff --git a/Packages/MIES/MIES_DataConfigurator.ipf b/Packages/MIES/MIES_DataConfigurator.ipf index 8709ccc2b4..f33a56669a 100644 --- a/Packages/MIES/MIES_DataConfigurator.ipf +++ b/Packages/MIES/MIES_DataConfigurator.ipf @@ -284,12 +284,21 @@ End /// @param device panel title /// @param dataAcqOrTP acquisition mode, one of #DATA_ACQUISITION_MODE or #TEST_PULSE_MODE static Function DC_CalculateDAQDataWaveLength(string device, variable dataAcqOrTP) + variable hardwareType = GetHardwareType(device) NVAR stopCollectionPoint = $GetStopCollectionPoint(device) + return DC_CalculateDAQDataWaveLengthImpl(stopCollectionPoint, hardwareType, dataAcqOrTP) +end + +/// @brief device independent implkementation for @ref DC_CalculateDAQDataWaveLength +Function DC_CalculateDAQDataWaveLengthImpl(variable dataLength, variable hardwareType, variable dataAcqOrTP) + + variable exponent + switch(hardwareType) case HARDWARE_ITC_DAC: - variable exponent = FindNextPower(stopCollectionPoint, 2) + exponent = FindNextPower(dataLength, 2) if(dataAcqOrTP == DATA_ACQUISITION_MODE) exponent += 1 @@ -298,13 +307,13 @@ static Function DC_CalculateDAQDataWaveLength(string device, variable dataAcqOrT exponent = max(MINIMUM_ITCDATAWAVE_EXPONENT, exponent) return 2^exponent - break case HARDWARE_NI_DAC: - return stopCollectionPoint - break + + return dataLength endswitch + return NaN -end +End /// @brief Create the DAQConfigWave used to configure the DAQ device /// @@ -847,11 +856,18 @@ End /// /// @param device device /// @param dataAcqOrTP one of #DATA_ACQUISITION_MODE or #TEST_PULSE_MODE -static Function DC_GetDecimationFactor(device, dataAcqOrTP) - string device - variable dataAcqOrTP +static Function DC_GetDecimationFactor(string device, variable dataAcqOrTP) + + variable samplingInterval = DAP_GetSampInt(device, dataAcqOrTP, XOP_CHANNEL_TYPE_DAC) + + return DC_GetDecimationFactorCalc(samplingInterval) +End + +/// @brief Calculcate decimation factor device independent +/// @param samplingInterval sampling interval in microseconds +static Function DC_GetDecimationFactorCalc(variable samplingInterval) - return DAP_GetSampInt(device, dataAcqOrTP, XOP_CHANNEL_TYPE_DAC) / (WAVEBUILDER_MIN_SAMPINT * MILLI_TO_MICRO) + return samplingInterval / (WAVEBUILDER_MIN_SAMPINT * MILLI_TO_MICRO) End /// @brief Returns the longest sweep in a stimulus set across the given channel type @@ -891,16 +907,11 @@ static Function DC_CalculateGeneratedDataSize(device, dataAcqOrTP, genLength) string device variable dataAcqOrTP, genLength - variable decimationFactor = DC_GetDecimationFactor(device, dataAcqOrTP) + variable decimationFactor - // note: the decimationFactor is the factor between the hardware sample rate and the sample rate of the generated waveform in singleStimSet - // The ratio of the source to target wave sizes is however limited by the integer size of both waves - // While ideally srcLength == tgtLength the floor(...) limits the real data wave length such that - // when decimationFactor * index of real data wave is applied as index of the generated data wave it never exceeds its size - // Also if decimationFactor >= 2 the last point of the generated data wave is never transferred - // e.g. generated data with 10 points and decimationFactor == 2 copies index 0, 2, 4, 6, 8 to the real data wave of size 5 if(dataAcqOrTP == DATA_ACQUISITION_MODE) - return floor(genLength / decimationFactor) + IndexAfterDecimation(0, decimationFactor) + decimationFactor = DC_GetDecimationFactor(device, dataAcqOrTP) + return DC_CalculateGeneratedDataSizeDAQMode(genLength, decimationFactor) elseif(dataAcqOrTP == TEST_PULSE_MODE) return genLength else @@ -908,6 +919,18 @@ static Function DC_CalculateGeneratedDataSize(device, dataAcqOrTP, genLength) endif End +static Function DC_CalculateGeneratedDataSizeDAQMode(variable genLength, variable decimationFactor) + + // note: the decimationFactor is the factor between the hardware sample rate and the sample rate of the generated waveform in singleStimSet + // The ratio of the source to target wave sizes is however limited by the integer size of both waves + // While ideally srcLength == tgtLength the floor(...) limits the real data wave length such that + // when decimationFactor * index of real data wave is applied as index of the generated data wave it never exceeds its size + // Also if decimationFactor >= 2 the last point of the generated data wave is never transferred + // e.g. generated data with 10 points and decimationFactor == 2 copies index 0, 2, 4, 6, 8 to the real data wave of size 5 + + return floor(genLength / decimationFactor) + IndexAfterDecimation(0, decimationFactor) +End + /// @brief Places data from appropriate DA and TTL stimulus set(s) into DAQDataWave. /// Also records certain DA_Ephys GUI settings into sweepDataLNB and sweepDataTxTLNB /// @param device panel title @@ -1491,7 +1514,8 @@ static Function [STRUCT DataConfigurationResult s] DC_GetConfiguration(string de WAVE s.testPulse = GetTestPulse() // test pulse length is calculated for dataAcqOrTP @ref TP_CreateTestPulseWave - [testPulseLength, tpPulseStartPoint, tpPulseLengthPoints] = TP_GetCreationPropertiesInPoints(device, dataAcqOrTP) + WAVE TPSettingsCalc = GetTPsettingsCalculated(device) + [testPulseLength, tpPulseStartPoint, tpPulseLengthPoints] = TP_GetCreationPropertiesInPoints(TPSettingsCalc, dataAcqOrTP) s.testPulseLength = testPulseLength s.tpPulseStartPoint = tpPulseStartPoint s.tpPulseLengthPoints = tpPulseLengthPoints @@ -1664,11 +1688,17 @@ static Function [STRUCT DataConfigurationResult s] DC_GetConfiguration(string de s.distributedDAQDelay = round(DAG_GetNumericalValue(device, "setvar_DataAcq_dDAQDelay") / (s.samplingInterval * MICRO_TO_MILLI)) s.onsetDelay = s.onsetDelayUser + s.onsetDelayAuto - if(s.distributedDAQ) - s.insertStart[] = s.onsetDelay + (sum(s.statusHS, 0, s.headstageDAC[p]) - 1) * (s.distributedDAQDelay + s.setLength[p]) - else - s.insertStart[] = s.onsetDelay - endif + DC_CalculateInsertStart(s) + endif +End + +/// @brief Calculate s.insertStart for DATA_ACQUISITION_MODE +static Function DC_CalculateInsertStart(STRUCT DataConfigurationResult &s) + + if(s.distributedDAQ) + s.insertStart[] = s.onsetDelay + (sum(s.statusHS, 0, s.headstageDAC[p]) - 1) * (s.distributedDAQDelay + s.setLength[p]) + else + s.insertStart[] = s.onsetDelay endif End @@ -2230,3 +2260,417 @@ Function DC_GetChannelTypefromHS(device, headstage) ASSERT(IsFinite(row), "Invalid column") return config[row][%DAQChannelType] End + +// @brief Recreates DataConfigurationResult structure from LabNotebook for DATA_ACQUISITION_MODE +// Requirements: Load sweeps and stimsets before call +Function [STRUCT DataConfigurationResult s] DC_RecreateDataConfigurationResultFromLNB(WAVE numericalValues, WAVE/T textualValues, DFREF sweepDFR, variable sweepNo) + + variable index, i, idx, hwType + + s.dataAcqOrTP = DATA_ACQUISITION_MODE + DC_RecreateDataConfigurationResultFromLNB_Indep(s, numericalValues, textualValues, sweepNo) + + WAVE/Z settings = GetLastSetting(numericalValues, sweepNo, "Headstage Active", s.dataAcqOrTP) + if(WaveExists(settings)) + Redimension/N=(NUM_HEADSTAGES) settings + WAVE s.statusHS = settings + else + DEBUGPRINT("LNB entry not found: Headstage Active") + endif + + [WAVE daGains] = DC_RecreateDataConfigurationResultFromLNB_DAC(s, numericalValues, textualValues, sweepNo) + DC_CalculateInsertStart(s) + + [WAVE adGains] = DC_RecreateDataConfigurationResultFromLNB_ADC(s, numericalValues, textualValues, sweepNo) + DC_RecreateDataConfigurationResultFromLNB_TTL(s, numericalValues, textualValues, sweepNo) + + s.numActiveChannels = s.numDACEntries + s.numADCEntries + s.numTTLEntries + + Make/FREE/N=(s.numTTLEntries) ttlGains + ttlGains = 1 + Concatenate/NP {adGains, ttlGains}, daGains + daGains[] = 1 / daGains[p] + WAVE s.gains = daGains + + ASSERT(DimSize(s.DACList, ROWS), "Could not find any active DA channel.") + WAVE/Z sweep = GetDAQDataSingleColumnWaveNG(numericalValues, textualValues, sweepNo, sweepDFR, XOP_CHANNEL_TYPE_DAC, s.DACList[0]) + ASSERT(WaveExists(sweep), "Could not retrieve sweep for DataConfigurationResult recreation (required for stopCollectionPoint)") + hwType = GetLastSettingIndep(numericalValues, sweepNo, "Digitizer Hardware Type", s.dataAcqOrTP) + if(IsNaN(hwType)) + DEBUGPRINT("LNB entry not found: Digitizer Hardware Type, defaulting to ITC") + hwType = HARDWARE_ITC_DAC + endif + s.stopCollectionPoint = DC_CalculateDAQDataWaveLengthImpl(DimSize(sweep, ROWS), hwType, s.dataAcqOrTP) + + if(!IsNaN(s.baselineFrac)) + DC_RecreateDataConfigurationResultFromLNB_TP(s, numericalValues, sweepNo) + endif + +// TODO: Save in LNB and restore here +// s.joinedTTLStimsetSize +// s.powerSpectrum + +End + +static Function DC_RecreateDataConfigurationResultFromLNB_TTL(STRUCT DataConfigurationResult &s, WAVE numericalValues, WAVE/T textualValues, variable sweepNo) + + variable index, indep_hs_text + + indep_hs_text = GetIndexForHeadstageIndepData(textualValues) + + // TTL value entries, fallbacks for missing data not implemented for TTL + Make/FREE/D/N=(NUM_DA_TTL_CHANNELS) s.TTLsetLength, s.TTLsetColumn, s.TTLcycleCount + Make/FREE/N=(NUM_DA_TTL_CHANNELS) s.statusTTLFiltered + Make/FREE/T/N=(NUM_DA_TTL_CHANNELS) s.TTLsetName + Make/FREE/WAVE/N=(NUM_DA_TTL_CHANNELS) s.TTLstimSet + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "channels", NaN, XOP_CHANNEL_TYPE_TTL, s.dataAcqOrTP) + if(WaveExists(settings)) + WAVE/T settingsT = settings + WAVE TTLList = ListToNumericWave(settingsT[indep_hs_text], ";") + ASSERT(DimSize(TTLList, ROWS) == NUM_DA_TTL_CHANNELS, "Unexpected number of TTL channels from LNB.") + s.statusTTLFiltered[] = !IsNaN(TTLList[p]) + WAVE s.TTLList = ZapNaNs(TTLList) + s.numTTLEntries = DimSize(s.TTLList, ROWS) + else + s.numTTLEntries = 0 + Make/FREE/N=(0) s.TTLList + DEBUGPRINT("LNB entry not found for XOP_CHANNEL_TYPE_TTL: channels") + endif + + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "stim sets", NaN, XOP_CHANNEL_TYPE_TTL, s.dataAcqOrTP) + if(WaveExists(settings)) + WAVE/T settingsT = settings + WAVE/T s.TTLsetName = ListToTextWave(settingsT[indep_hs_text], ";") + ASSERT(DimSize(s.TTLsetName, ROWS) == NUM_DA_TTL_CHANNELS, "Got unexpected LNB entry format") + else + DEBUGPRINT("LNB entry not found for XOP_CHANNEL_TYPE_TTL: stim sets") + endif + + s.TTLstimSet[] = WB_CreateAndGetStimSet(s.TTLsetName[p]) + + // for TTL the setLength was not saved in the LNB, so recalculate. The result may differ from the actual TTLsetLength originally used when acquired + s.TTLsetLength[] = WaveExists(s.TTLstimSet[p]) ? DC_CalculateGeneratedDataSizeDAQMode(DimSize(s.TTLstimSet[p], ROWS), s.decimationFactor) : 0 + + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "TTL set sweep counts", NaN, XOP_CHANNEL_TYPE_TTL, s.dataAcqOrTP) + if(WaveExists(settings)) + WAVE/T settingsT = settings + WAVE s.TTLsetColumn = ListToNumericWave(settingsT[indep_hs_text], ";") + ASSERT(DimSize(s.TTLsetColumn, ROWS) == NUM_DA_TTL_CHANNELS, "Unexpected number of TTL channels from LNB.") + else + DEBUGPRINT("LNB entry not found for XOP_CHANNEL_TYPE_TTL: TTL set sweep counts") + endif + + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "TTL set cycle counts", NaN, XOP_CHANNEL_TYPE_TTL, s.dataAcqOrTP) + if(WaveExists(settings)) + WAVE/T settingsT = settings + WAVE s.TTLcycleCount = ListToNumericWave(settingsT[indep_hs_text], ";") + ASSERT(DimSize(s.TTLcycleCount, ROWS) == NUM_DA_TTL_CHANNELS, "Unexpected number of TTL channels from LNB.") + else + DEBUGPRINT("LNB entry not found for XOP_CHANNEL_TYPE_TTL: TTL set cycle counts") + endif +End + +static Function [WAVE/D adGains] DC_RecreateDataConfigurationResultFromLNB_ADC(STRUCT DataConfigurationResult &s, WAVE numericalValues, WAVE/T textualValues, variable sweepNo) + + variable index, i, idx + + Make/FREE/N=(NUM_DA_TTL_CHANNELS) s.ADCList + for(i = 0; i < NUM_DA_TTL_CHANNELS; i += 1) + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "ADC", i, XOP_CHANNEL_TYPE_ADC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.ADCList[idx] = settings[index] + idx += 1 + endif + endfor + Redimension/N=(idx) s.ADCList + s.numADCEntries = idx + ASSERT(s.numADCEntries > 0, "No active AD channel found") + + Make/FREE/D/N=(s.numADCEntries) s.headstageADC = NaN + WAVE/Z settings = GetLastSetting(numericalValues, sweepNo, "ADC", s.dataAcqOrTP) + if(WaveExists(settings)) + for(i = 0; i < NUM_HEADSTAGES; i += 1) + if(IsFinite(settings[i])) + FindValue/V=(settings[i]) s.DACList + if(V_row >= 0) + s.headstageADC[V_row] = i + endif + endif + endfor + else + DEBUGPRINT("LNB entry not found: ADC") + endif + + Make/FREE/D/N=(s.numADCEntries) adGains + for(i = 0; i < s.numADCEntries; i += 1) + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "AD GAIN", s.ADCList[i], XOP_CHANNEL_TYPE_ADC, s.dataAcqOrTP) + if(WaveExists(settings)) + adGains[i] = settings[index] + endif + endfor + + return [adGains] +End + +static Function [WAVE/D daGains] DC_RecreateDataConfigurationResultFromLNB_DAC(STRUCT DataConfigurationResult &s, WAVE numericalValues, WAVE/T textualValues, variable sweepNo) + + variable index, i, idx, clampMode, wbOodDAQOffset, postFeaturePoints + string key + + Make/FREE/N=(NUM_DA_TTL_CHANNELS) s.DACList + for(i = 0; i < NUM_DA_TTL_CHANNELS; i += 1) + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DAC", i, XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.DACList[idx] = settings[index] + idx += 1 + endif + endfor + Redimension/N=(idx) s.DACList + s.numDACEntries = idx + ASSERT(s.numDACEntries > 0, "No active DA channel found") + + Make/FREE/D/N=(s.numDACEntries) s.headstageDAC = NaN + WAVE/Z settings = GetLastSetting(numericalValues, sweepNo, "DAC", s.dataAcqOrTP) + if(WaveExists(settings)) + for(i = 0; i < NUM_HEADSTAGES; i += 1) + if(IsFinite(settings[i])) + FindValue/V=(settings[i]) s.DACList + if(V_row >= 0) + s.headstageDAC[V_row] = i + endif + endif + endfor + else + DEBUGPRINT("LNB entry not found: DAC") + endif + + Make/FREE/D/N=(s.numDACEntries) s.setCycleCount, s.setColumn, s.insertStart, daGains, daqChannelType, s.setLength + Make/FREE/N=(s.numDACEntries) s.offsets + Make/FREE/T/N=(s.numDACEntries) s.regions, s.setName + Make/FREE/WAVE/N=(s.numDACEntries) s.stimSet + WAVE/D s.DACAmp = GetDACAmplitudes(s.numDACEntries) + for(i = 0; i < s.numDACEntries; i += 1) + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Set Cycle Count", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.setCycleCount[i] = settings[index] + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Set Sweep Count", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.setColumn[i] = settings[index] + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Delay onset oodDAQ", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.offsets[i] = settings[index] + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "oodDAQ regions", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + WAVE/T settingsT = settings + s.regions[i] = settingsT[index] + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DA GAIN", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + daGains[i] = settings[index] + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "DA ChannelType", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + daqChannelType[i] = settings[index] + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, STIMSET_SCALE_FACTOR_KEY, s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + if(daqChannelType[i] == DAQ_CHANNEL_TYPE_DAQ) + s.DACAmp[i][%DASCALE] = settings[index] + else + s.DACAmp[i][%TPAMP] = settings[index] + endif + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, STIM_WAVE_NAME_KEY, s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + WAVE/T settingsT = settings + s.setName[i] = settingsT[index] + s.stimSet[i] = WB_CreateAndGetStimSet(s.setName[i]) + if(s.offsets[i]) + WAVE stimSet = s.stimSet[i] + wbOodDAQOffset = round(s.offsets[i] / WAVEBUILDER_MIN_SAMPINT) + postFeaturePoints = s.distributedDAQOptPost / WAVEBUILDER_MIN_SAMPINT // as in @ref InitOOdDAQParams + WAVE stimCol = OOD_OffsetStimSetColAndCutoff(stimSet, s.setColumn[i], wbOodDAQOffset, postFeaturePoints) + Duplicate/FREE stimSet, stimSetOffsetted + Redimension/N=(DimSize(stimCol, ROWS), -1) stimSetOffsetted + MultiThread stimSetOffsetted[][s.setColumn[i]] = stimCol[p] + s.stimSet[i] = stimSetOffsetted + endif + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Stim set length", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.setLength[i] = settings[index] + elseif(WaveExists(s.stimSet[i])) + s.setLength[i] = DC_CalculateGeneratedDataSizeDAQMode(DimSize(s.stimSet[i], ROWS), s.decimationFactor) + endif + + if(daqChannelType[i] == DAQ_CHANNEL_TYPE_DAQ) + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, "Clamp Mode", s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + clampMode = settings[index] + if(clampMode == V_CLAMP_MODE) + key = TP_AMPLITUDE_VC_ENTRY_KEY + elseif(clampMode == I_CLAMP_MODE) + key = TP_AMPLITUDE_IC_ENTRY_KEY + elseif(clampMode == I_EQUAL_ZERO_MODE) + s.DACAmp[i][%DASCALE] = 0 + s.DACAmp[i][%TPAMP] = 0 + continue + else + ASSERT(0, "Unknown clamp mode") + endif + [WAVE settings, index] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, key, s.DACList[i], XOP_CHANNEL_TYPE_DAC, s.dataAcqOrTP) + if(WaveExists(settings)) + s.DACAmp[i][%TPAMP] = settings[index] + endif + endif + endif + endfor + + return [daGains] +End + +static Function DC_RecreateDataConfigurationResultFromLNB_Indep(STRUCT DataConfigurationResult &s, WAVE numericalValues, WAVE/T textualValues, variable sweepNo) + + variable onsetDelayUserTime, onsetDelayAutoTime, distributedDAQDelayTime, terminationDelayTime + string device + + s.globalTPInsert = GetLastSettingIndep(numericalValues, sweepNo, "TP Insert Checkbox", s.dataAcqOrTP) + if(IsNaN(s.globalTPInsert)) + DEBUGPRINT("LNB entry not found: TP Insert Checkbox") + endif + + s.scalingZero = GetLastSettingIndep(numericalValues, sweepNo, "Scaling zero", s.dataAcqOrTP) + if(IsNaN(s.scalingZero)) + DEBUGPRINT("LNB entry not found: Scaling zero") + endif + + // the field s.indexingLocked is not set or used in DC? + s.indexingLocked = GetLastSettingIndep(numericalValues, sweepNo, "Locked indexing", s.dataAcqOrTP) + if(IsNaN(s.indexingLocked)) + DEBUGPRINT("LNB entry not found: Locked indexing") + endif + + s.indexing = GetLastSettingIndep(numericalValues, sweepNo, "Indexing", s.dataAcqOrTP) + if(IsNaN(s.indexing)) + DEBUGPRINT("LNB entry not found: Indexing") + endif + + s.distributedDAQ = GetLastSettingIndep(numericalValues, sweepNo, "Distributed DAQ", s.dataAcqOrTP) + if(IsNaN(s.distributedDAQ)) + DEBUGPRINT("LNB entry not found: Distributed DAQ") + endif + + s.distributedDAQOptOv = GetLastSettingIndep(numericalValues, sweepNo, "Optimized Overlap dDAQ", s.dataAcqOrTP) + if(IsNaN(s.distributedDAQOptOv)) + DEBUGPRINT("LNB entry not found: Optimized Overlap dDAQ") + endif + + s.distributedDAQOptPre = GetLastSettingIndep(numericalValues, sweepNo, "oodDAQ Pre Feature", s.dataAcqOrTP) + if(IsNaN(s.distributedDAQOptPre)) + DEBUGPRINT("LNB entry not found: oodDAQ Pre Feature") + endif + + s.distributedDAQOptPost = GetLastSettingIndep(numericalValues, sweepNo, "oodDAQ Post Feature", s.dataAcqOrTP) + if(IsNaN(s.distributedDAQOptPost)) + DEBUGPRINT("LNB entry not found: oodDAQ Post Feature") + endif + + s.multiDevice = GetLastSettingIndep(numericalValues, sweepNo, "Multi Device mode", s.dataAcqOrTP) + if(IsNaN(s.multiDevice)) + DEBUGPRINT("LNB entry not found: Multi Device mode") + endif + + s.baselineFrac = GetLastSettingIndep(numericalValues, sweepNo, "TP Baseline Fraction", s.dataAcqOrTP) + if(IsNaN(s.baselineFrac)) + DEBUGPRINT("LNB entry not found: TP Baseline Fraction") + endif + + s.samplingInterval = GetLastSettingIndep(numericalValues, sweepNo, "Sampling interval", s.dataAcqOrTP) + if(IsNaN(s.samplingInterval)) + s.decimationFactor = NaN + DEBUGPRINT("LNB entry not found: Sampling interval") + else + s.samplingInterval *= MILLI_TO_MICRO + s.decimationFactor = DC_GetDecimationFactorCalc(s.samplingInterval) + endif + + device = GetLastSettingTextIndep(textualValues, sweepNo, "Device", s.dataAcqOrTP) + if(IsEmpty(device)) + s.hardwareType = NaN + DEBUGPRINT("LNB entry not found: Device") + else + s.hardwareType = GetHardwareType(device) + endif + + onsetDelayUserTime = GetLastSettingIndep(numericalValues, sweepNo, "Delay onset user", s.dataAcqOrTP) + if(IsNaN(onsetDelayUserTime)) + s.onsetDelayUser = NaN + DEBUGPRINT("LNB entry not found: Delay onset user") + else + s.onsetDelayUser = round(onsetDelayUserTime * MILLI_TO_MICRO / s.samplingInterval) + endif + + onsetDelayAutoTime = GetLastSettingIndep(numericalValues, sweepNo, "Delay onset auto", s.dataAcqOrTP) + if(IsNaN(onsetDelayAutoTime)) + s.onsetDelayAuto = NaN + DEBUGPRINT("LNB entry not found: Delay onset auto") + else + s.onsetDelayAuto = round(onsetDelayAutoTime * MILLI_TO_MICRO / s.samplingInterval) + endif + + s.onsetDelay = s.onsetDelayUser + s.onsetDelayAuto + + distributedDAQDelayTime = GetLastSettingIndep(numericalValues, sweepNo, "Delay distributed DAQ", s.dataAcqOrTP) + if(IsNaN(distributedDAQDelayTime)) + s.distributedDAQDelay = NaN + DEBUGPRINT("LNB entry not found: Delay distributed DAQ") + else + s.distributedDAQDelay = round(distributedDAQDelayTime * MILLI_TO_MICRO / s.samplingInterval) + endif + + terminationDelayTime = GetLastSettingIndep(numericalValues, sweepNo, "Delay termination", s.dataAcqOrTP) + if(IsNaN(terminationDelayTime)) + s.terminationDelay = NaN + DEBUGPRINT("LNB entry not found: Delay termination") + else + s.terminationDelay = round(terminationDelayTime * MILLI_TO_MICRO / s.samplingInterval) + endif + + s.skipAhead = GetLastSettingIndep(numericalValues, sweepNo, "Skip Ahead", s.dataAcqOrTP) + if(IsNaN(s.skipAhead)) + DEBUGPRINT("LNB entry not found: Skip Ahead") + endif +End + +static Function DC_RecreateDataConfigurationResultFromLNB_TP(STRUCT DataConfigurationResult &s, WAVE numericalValues, variable sweepNo) + + variable pulseDurationTime, totalLengthPoints, pulseStartPoints, pulseLengthPoints + + pulseDurationTime = GetLastSettingIndep(numericalValues, sweepNo, "TP Pulse Duration", s.dataAcqOrTP) + if(IsNaN(pulseDurationTime)) + DEBUGPRINT("LNB entry not found: TP Pulse Duration") + endif + + WAVE samplingIntervals = GetNewSamplingIntervalsAsFree() + samplingIntervals[%SI_TP_DAC] = NaN + samplingIntervals[%SI_DAQ_DAC] = s.samplingInterval + samplingIntervals[%SI_TP_ADC] = NaN + samplingIntervals[%SI_DAQ_ADC] = NaN + WAVE tpSettings = GetTPSettingsFree() + TPSettings[%baselinePerc][INDEP_HEADSTAGE] = s.baselineFrac * ONE_TO_PERCENT + TPSettings[%durationMS][INDEP_HEADSTAGE] = pulseDurationTime + WAVE tpCalculated = GetTPSettingsCalculatedAsFree() + + TP_UpdateTPSettingsCalculatedImpl(TPSettings, samplingIntervals, tpCalculated) + + [totalLengthPoints, pulseStartPoints, pulseLengthPoints] = TP_GetCreationPropertiesInPoints(tpCalculated, s.dataAcqOrTP) + s.testPulseLength = totalLengthPoints + s.tpPulseStartPoint = pulseStartPoints + s.tpPulseLengthPoints = pulseLengthPoints + + WAVE s.testPulse = GetTestPulseAsFree() + TP_CreateTestPulseWaveImpl(s.testPulse, s.testPulseLength, s.tpPulseStartPoint, s.tpPulseLengthPoints) +End diff --git a/Packages/MIES/MIES_Epochs.ipf b/Packages/MIES/MIES_Epochs.ipf index e1441abb74..07a000472d 100644 --- a/Packages/MIES/MIES_Epochs.ipf +++ b/Packages/MIES/MIES_Epochs.ipf @@ -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) @@ -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 @@ -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 @@ -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) @@ -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 @@ -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 @@ -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 diff --git a/Packages/MIES/MIES_OptimzedOverlapDistributedAcquisition.ipf b/Packages/MIES/MIES_OptimzedOverlapDistributedAcquisition.ipf index 70962d75fc..33a53c4571 100644 --- a/Packages/MIES/MIES_OptimzedOverlapDistributedAcquisition.ipf +++ b/Packages/MIES/MIES_OptimzedOverlapDistributedAcquisition.ipf @@ -346,37 +346,39 @@ End static Function/Wave OOD_CreateStimSet(params) STRUCT OOdDAQParams ¶ms - 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 diff --git a/Packages/MIES/MIES_SweepSaving.ipf b/Packages/MIES/MIES_SweepSaving.ipf index f8ff88d657..3e2bb63a58 100644 --- a/Packages/MIES/MIES_SweepSaving.ipf +++ b/Packages/MIES/MIES_SweepSaving.ipf @@ -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)) @@ -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 diff --git a/Packages/MIES/MIES_TestPulse.ipf b/Packages/MIES/MIES_TestPulse.ipf index 6be8b9a6d1..4112af0e4e 100644 --- a/Packages/MIES/MIES_TestPulse.ipf +++ b/Packages/MIES/MIES_TestPulse.ipf @@ -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] @@ -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. diff --git a/Packages/MIES/MIES_Utilities.ipf b/Packages/MIES/MIES_Utilities.ipf index 8d198babd4..a662f440d1 100644 --- a/Packages/MIES/MIES_Utilities.ipf +++ b/Packages/MIES/MIES_Utilities.ipf @@ -3238,19 +3238,23 @@ End /// Counterpart @see NumericWaveToList(). /// @see TextWaveToList /// -/// @param list list with numeric entries -/// @param sep separator -/// @param type [optional, defaults to double precision float (`IGOR_TYPE_64BIT_FLOAT`)] type of the created numeric wave -threadsafe Function/WAVE ListToNumericWave(list, sep, [type]) - string list, sep - variable type +/// @param list list with numeric entries +/// @param sep separator +/// @param type [optional, defaults to double precision float (`IGOR_TYPE_64BIT_FLOAT`)] type of the created numeric wave +/// @param ignoreErr [optional, defaults 0] when this flag is set conversion errors are ignored, the value placed is NaN (-9223372036854775808 for int type) +threadsafe Function/WAVE ListToNumericWave(string list, string sep, [variable type, variable ignoreErr]) if(ParamIsDefault(type)) type = IGOR_TYPE_64BIT_FLOAT endif + ignoreErr = ParamIsDefault(ignoreErr) ? 0 : !!ignoreErr Make/FREE/Y=(type)/N=(ItemsInList(list, sep)) wv - MultiThread wv = str2num(StringFromList(p, list, sep)) + if(ignoreErr) + MultiThread wv = str2numSafe(StringFromList(p, list, sep)) + else + MultiThread wv = str2num(StringFromList(p, list, sep)) + endif return wv End diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index 1305727acd..9796db846f 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -3725,7 +3725,15 @@ Function/WAVE GetTestPulse() endif /// create dummy wave - Make/R/N=(0) dfr:TestPulse/Wave=wv + WAVE wv = GetTestPulseAsFree() + MoveWave wv, dfr:TestPulse + + return wv +End + +Function/WAVE GetTestPulseAsFree() + + Make/FREE/R/N=0 wv return wv End @@ -7649,18 +7657,18 @@ Function/WAVE GetTPSettings(string device) return wv End +static Constant TP_SETTINGSCALCULATED_WAVE_VERSION = 3 + /// @brief Return the calculated/derived TP settings /// /// The entries in this wave are only valid during DAQ/TP and are updated via DC_UpdateGlobals(). Function/WAVE GetTPSettingsCalculated(string device) - variable versionOfNewWave = 3 - DFREF dfr = GetDeviceTestPulse(device) WAVE/Z/SDFR=dfr/D wv = settingsCalculated - if(ExistsWithCorrectLayoutVersion(wv, versionOfNewWave)) + if(ExistsWithCorrectLayoutVersion(wv, TP_SETTINGSCALCULATED_WAVE_VERSION)) return wv elseif(WaveExists(wv)) if(!IsWaveVersioned(wv)) @@ -7683,18 +7691,34 @@ Function/WAVE GetTPSettingsCalculated(string device) wv[%pulseStartPointsTP_ADC] = wv[%pulseStartPointsTP] wv[%pulseStartPointsDAQ_ADC] = wv[%pulseStartPointsDAQ] endif - else - Make/N=(16)/D dfr:settingsCalculated/WAVE=wv - wv = NaN + + SetTPSettingsCalculatedProperties(wv) + + return wv endif - SetDimensionLabels(wv, "baselineFrac;pulseLengthMS;pulseLengthPointsTP;pulseLengthPointsDAQ;totalLengthMS;totalLengthPointsTP;totalLengthPointsDAQ;pulseStartMS;pulseStartPointsTP;pulseStartPointsDAQ;pulseLengthPointsTP_ADC;pulseLengthPointsDAQ_ADC;totalLengthPointsTP_ADC;totalLengthPointsDAQ_ADC;pulseStartPointsTP_ADC;pulseStartPointsDAQ_ADC;", ROWS) + WAVE/D wv = GetTPSettingsCalculatedAsFree() + MoveWave wv, dfr:settingsCalculated - SetWaveVersion(wv, versionOfNewWave) + return wv +End + +Function/WAVE GetTPSettingsCalculatedAsFree() + + Make/FREE/D/N=(16) wv + wv = NaN + + SetTPSettingsCalculatedProperties(wv) return wv End +static Function SetTPSettingsCalculatedProperties(WAVE wv) + + SetDimensionLabels(wv, "baselineFrac;pulseLengthMS;pulseLengthPointsTP;pulseLengthPointsDAQ;totalLengthMS;totalLengthPointsTP;totalLengthPointsDAQ;pulseStartMS;pulseStartPointsTP;pulseStartPointsDAQ;pulseLengthPointsTP_ADC;pulseLengthPointsDAQ_ADC;totalLengthPointsTP_ADC;totalLengthPointsDAQ_ADC;pulseStartPointsTP_ADC;pulseStartPointsDAQ_ADC;", ROWS) + SetWaveVersion(wv, TP_SETTINGSCALCULATED_WAVE_VERSION) +End + static Constant TP_SETTINGS_WAVE_VERSION = 2 /// @brief Returns a wave reference to the TP settings key wave @@ -8238,3 +8262,16 @@ Function/WAVE GetLogFileNames() return files End + +/// @brief Used as temporary wave to store various sampling intervals in ms +Function/WAVE GetNewSamplingIntervalsAsFree() + + Make/FREE/D/N=4 wv + + SetDimLabel ROWS, 0, SI_TP_DAC, wv + SetDimLabel ROWS, 1, SI_DAQ_DAC, wv + SetDimLabel ROWS, 2, SI_TP_ADC, wv + SetDimLabel ROWS, 3, SI_DAQ_ADC, wv + + return wv +End diff --git a/Packages/tests/Basic/UTF_EpochswoHardware.ipf b/Packages/tests/Basic/UTF_EpochswoHardware.ipf index 0a300fd568..71cd16f8bc 100644 --- a/Packages/tests/Basic/UTF_EpochswoHardware.ipf +++ b/Packages/tests/Basic/UTF_EpochswoHardware.ipf @@ -279,7 +279,8 @@ static Function EP_TestSortEpochs() Make/FREE/D/N=(numEpochs) order = str2num(epochsWave[p][EPOCH_COL_TAGS][0][0]) wfprintf orderStr, "%d\r", order - MIES_EP#EP_SortEpochs(EP_DUMMY_DEVICE) + WAVE/T epochWave = GetEpochsWave(EP_DUMMY_DEVICE) + MIES_EP#EP_SortEpochs(epochWave) WAVE/T epochWave = GetEpochsWave(EP_DUMMY_DEVICE) diff --git a/Packages/tests/Basic/UTF_Utils.ipf b/Packages/tests/Basic/UTF_Utils.ipf index b00d38b9cc..44e135ba90 100644 --- a/Packages/tests/Basic/UTF_Utils.ipf +++ b/Packages/tests/Basic/UTF_Utils.ipf @@ -4245,6 +4245,34 @@ Function LTNWRoundtripsWithNumericWaveToList() CHECK_EQUAL_WAVES(expected, actual, mode = WAVE_DATA) End +static Function LTNInvalidInput() + + Execute/Z "SetIgorOption DisableThreadsafe=?" + NVAR threadingDisabled = V_flag + if(threadingDisabled == 1) + WAVE wv = ListToNumericWave("1;totallyLegitNumber;1;", ";") + CHECK_RTE(1001) // Str2num;expected number + CHECK_WAVE(wv, NUMERIC_WAVE, minorType = DOUBLE_WAVE) + CHECK_EQUAL_WAVES(wv, {1, NaN, 1}, mode = WAVE_DATA) + else + PASS() + endif +End + +static Function LTNInvalidInputIgnored() + + Execute/Z "SetIgorOption DisableThreadsafe=?" + NVAR threadingDisabled = V_flag + if(threadingDisabled == 1) + WAVE wv = ListToNumericWave("1;totallyLegitNumber;1;", ";", ignoreErr=1) + CHECK_NO_RTE() + CHECK_WAVE(wv, NUMERIC_WAVE, minorType = DOUBLE_WAVE) + CHECK_EQUAL_WAVES(wv, {1, NaN, 1}, mode = WAVE_DATA) + else + PASS() + endif +End + /// @} /// Backup functions diff --git a/Packages/tests/HardwareBasic/UTF_Epochs.ipf b/Packages/tests/HardwareBasic/UTF_Epochs.ipf index f86f660838..7f168ef001 100644 --- a/Packages/tests/HardwareBasic/UTF_Epochs.ipf +++ b/Packages/tests/HardwareBasic/UTF_Epochs.ipf @@ -464,6 +464,7 @@ static Function TestEpochsGeneric(string device) endfor endfor + TestEpochReceation(device) End static Function TestUnacquiredEpoch(WAVE sweep, WAVE epochChannel) @@ -1212,3 +1213,48 @@ static Function EP_EpochTest18_REENTRY([STRUCT IUTF_mData &mData]) TestEpochsGeneric(mData.s0) End + +static Function TestEpochReceationRemoveUserEpochs(WAVE/T epochChannel) + + variable i, epochCnt + + epochCnt = DimSize(epochChannel, ROWS) + Make/FREE/T/N=(epochCnt) shortnames = EP_GetShortName(epochChannel[p][EPOCH_COL_TAGS]) + for(i = epochCnt - 1; i >= 0; i -= 1) + if(GrepString(shortnames[i], "^" + EPOCH_SHORTNAME_USER_PREFIX)) + DeleteWavePoint(epochChannel, ROWS, i) + endif + endfor +End + +static Function TestEpochReceation(string device) + + variable channelNumber + variable sweepNo = 0 + + WAVE/Z numericalValues = GetLBNumericalValues(device) + WAVE/Z textualValues = GetLBTextualValues(device) + DFREF deviceDFR = GetDeviceDataPath(device) + DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) + + WAVE epochWave = EP_RecreateEpochsFromLoadedData(numericalValues, textualValues, sweepDFR, sweepNo) + + WAVE/Z activeChannels = GetActiveChannels(numericalValues, textualValues, sweepNo, XOP_CHANNEL_TYPE_DAC) + CHECK_WAVE(activeChannels, FREE_WAVE | NUMERIC_WAVE) + for(channelNumber = 0; channelNumber < NUM_DA_TTL_CHANNELS; channelNumber += 1) + + if(IsNaN(activeChannels[channelNumber])) + continue + endif + WAVE/Z/T epochChannelRef = EP_FetchEpochs(numericalValues, textualValues, sweepNo, channelNumber, XOP_CHANNEL_TYPE_DAC) + WAVE/Z/T epochChannelRec = EP_FetchEpochsFromRecreated(epochWave, channelNumber, XOP_CHANNEL_TYPE_DAC) + + if(WaveExists(epochChannelRef)) + TestEpochReceationRemoveUserEpochs(epochChannelRef) + // also TP channels can be active but have no epochs + CHECK_EQUAL_WAVES(epochChannelRec, epochChannelRef) + else + CHECK_WAVE(epochChannelRec, NULL_WAVE) + endif + endfor +End diff --git a/Packages/tests/UTF_DataGenerators.ipf b/Packages/tests/UTF_DataGenerators.ipf index 01466b1ef6..4021fe987d 100644 --- a/Packages/tests/UTF_DataGenerators.ipf +++ b/Packages/tests/UTF_DataGenerators.ipf @@ -273,7 +273,7 @@ static Function/WAVE EpochTestSamplingFrequency_Gen() string frequencies = DAP_GetSamplingFrequencies() - WAVE wTemp = ListToNumericWave(frequencies, ";") + WAVE wTemp = ListToNumericWave(frequencies, ";", ignoreErr = 1) WAVE w = ZapNaNs(wTemp) SetDimensionLabelsFromWaveContents(w, prefix = "f_", suffix = "_kHz") @@ -285,7 +285,7 @@ static Function/WAVE EpochTestSamplingFrequencyTTL_Gen() string frequencies = DAP_GetSamplingFrequencies() - WAVE wTemp = ListToNumericWave(frequencies, ";") + WAVE wTemp = ListToNumericWave(frequencies, ";", ignoreErr = 1) #ifdef TESTS_WITH_ITC18USB_HARDWARE wTemp[] = wTemp[p] == 100 ? NaN : wTemp[p]