From 7a4e92706db49705ec7080d07bb0db3535ab3e36 Mon Sep 17 00:00:00 2001 From: Thomas Braun Date: Tue, 21 Jan 2025 18:55:52 +0100 Subject: [PATCH] changes --- .../MIES/MIES_AnalysisFunctions_PatchSeq.ipf | 205 ++++++++++++------ Packages/MIES/MIES_Constants.ipf | 5 + 2 files changed, 143 insertions(+), 67 deletions(-) diff --git a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf index 5a37418893..3a91f4258d 100644 --- a/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf +++ b/Packages/MIES/MIES_AnalysisFunctions_PatchSeq.ipf @@ -180,19 +180,22 @@ static Constant PSQ_DEFAULT_SAMPLING_MULTIPLIER = 4 static Constant PSQ_RHEOBASE_DURATION = 500 static Constant PSQ_DA_FALLBACK_DASCALE_RANGE_FAC = 1.5 +static Constant PSQ_DA_FALLBACK_DASCALE_NEG_SLOPE_PERC = 50 /// @name Type constants for PSQ_DS_GetLabnotebookData /// @anchor PSQDAScaleAdaptiveLBNTypeConstants ///@{ static Constant PSQ_DS_FI_SLOPE = 0x1 -static Constant PSQ_DS_FI_SLOPE_REACHED_PASS = 0x2 -static Constant PSQ_DS_FI_NEG_SLOPE_PASS = 0x3 -static Constant PSQ_DS_FI_OFFSET = 0x4 -static Constant PSQ_DS_SWEEP_PASS = 0x5 -static Constant PSQ_DS_SWEEP = 0x6 -static Constant PSQ_DS_APFREQ = 0x7 -static Constant PSQ_DS_DASCALE = 0x8 -static Constant PSQ_DS_SWEEP_WO_PP_BL_PASS = 0x9 +static Constant PSQ_DS_FI_SLOPE_DASCALE = 0x2 +static Constant PSQ_DS_FI_SLOPE_REACHED_PASS = 0x3 +static Constant PSQ_DS_FI_NEG_SLOPE_PASS = 0x4 +static Constant PSQ_DS_FI_OFFSET = 0x5 +static Constant PSQ_DS_FI_OFFSET_DASCALE = 0x6 +static Constant PSQ_DS_SWEEP_PASS = 0x7 +static Constant PSQ_DS_SWEEP = 0x8 +static Constant PSQ_DS_APFREQ = 0x9 +static Constant PSQ_DS_DASCALE = 0xA +// static Constant PSQ_DS_SWEEP_WO_PP_BL_PASS = 0xc ///@} /// @brief Fills `s` according to the analysis function type @@ -2109,7 +2112,7 @@ End /// @retval DAScalesFiltered DScales values with intact indices /// @retval deletedIndizes Possibly non-existing wave with the removed indizes /// @retval numPoints Number of points in the apfreq/DAScales waves after filtering -static Function [WAVE apfreqFiltered, WAVE DAScalesFiltered, WAVE deletedIndizes, variable numPoints] PSQ_DS_FilterFrequencyCurrentData(WAVE apfreq, WAVE DAScales) +static Function [WAVE apfreqFiltered, WAVE DAScalesFiltered, WAVE deletedIndizes, variable numPoints] PSQ_DS_FilterFrequencyCurrentData(WAVE apfreq, WAVE DAScales, variable mode) numPoints = DimSize(apfreq, ROWS) ASSERT(numPoints == DimSize(DaScales, ROWS), "Non-matching wave sizes") @@ -2118,19 +2121,24 @@ static Function [WAVE apfreqFiltered, WAVE DAScalesFiltered, WAVE deletedIndizes return [$"", $"", $"", 0] endif - WAVE/Z indizes = FindNeighbourWithPredicate(apfreq, EqualValuesOrBothNaN) + if(mode == PSQ_DS_FIT_MODE_IGN_DUPS) + WAVE/Z indizes = FindNeighbourWithPredicate(apfreq, EqualValuesOrBothNaN) + elseif(mode == PSQ_DS_FIT_MODE_IGN_DUPS_AND_NEG) + WAVE/Z indizes = FindNeighbourWithPredicate(apfreq, LargerEqualValuesOrBothNaN) + else + ASSERT(0, "Invalid mode") + endif if(!WaveExists(indizes)) return [apfreq, DAScales, $"", numPoints] endif if(DimSize(indizes, ROWS) == (numPoints - 1)) - // we can't do any fitting with only a single point left - // but as the original data was all the same we can - // just take the first PSQ_DA_NUM_POINTS_LINE_FIT points - // TODO - Duplicate/FREE/RMD=[0, PSQ_DA_NUM_POINTS_LINE_FIT - 1] apFreq, apFreqFiltered - Duplicate/FREE/RMD=[0, PSQ_DA_NUM_POINTS_LINE_FIT - 1] DAScales, DAScalesFiltered + // we can't do any fitting with only a single point left, + // so let's just use the first point for all PSQ_DA_NUM_POINTS_LINE_FIT entries + Make/FREE/N=(PSQ_DA_NUM_POINTS_LINE_FIT) apFreqFiltered, DAScalesFiltered + apFreqFiltered[] = apFreq[0] + DAScalesFiltered[] = DAScales[0] Redimension/N=(numPoints - PSQ_DA_NUM_POINTS_LINE_FIT) indizes indizes[] = p + PSQ_DA_NUM_POINTS_LINE_FIT @@ -2149,6 +2157,13 @@ static Function [WAVE apfreqFiltered, WAVE DAScalesFiltered, WAVE deletedIndizes return [apfreqFiltered, DASCalesFiltered, indizes, numPoints] End +/// @name Available modes for #PSQ_DS_FitFrequencyCurrentData() +///@anchor FitFrequencyRemovalModes +///@{ +static Constant PSQ_DS_FIT_MODE_IGN_DUPS = 0x1 +static Constant PSQ_DS_FIT_MODE_IGN_DUPS_AND_NEG = 0x2 +///@} + /// @brief Fit the AP frequency and DAScale data with a line fit and return the fit results (offsets and slopes) /// /// @param device DAC hardware device @@ -2156,11 +2171,12 @@ End /// @param apfreq AP frequency data /// @param DAScales DAScale data /// @param singleFit [optional, defaults to false] if only a single fit should be done +/// @param mode One of @ref FitFrequencyRemovalModes /// /// @retval fitOffset offsets of all line fits /// @retval fitSlope slopes of all line fits /// @retval errMsg error message if both `fitOffset` and `fitSlope` are null -static Function [WAVE/D fitOffset, WAVE/D fitSlope, string errMsg] PSQ_DS_FitFrequencyCurrentData(string device, variable sweepNo, WAVE/Z apfreq, WAVE/Z DAScales, [variable singleFit, variable ignorefINegSlope]) +static Function [WAVE/D fitOffset, WAVE/D fitSlope, string errMsg] PSQ_DS_FitFrequencyCurrentData(string device, variable sweepNo, WAVE/Z apfreq, WAVE/Z DAScales, variable mode, [variable singleFit]) variable i, numPoints, numFits, first, last, hasEnoughPoints, initialFit, idx, elem string line, databrowser, key @@ -2172,12 +2188,6 @@ static Function [WAVE/D fitOffset, WAVE/D fitSlope, string errMsg] PSQ_DS_FitFre singleFit = !!singleFit endif - if(ParamIsDefault(ignorefINegSlope)) - ignorefINegSlope = 0 - else - ignorefINegSlope = !!ignorefINegSlope - endif - if(!WaveExists(apFreq) && !WaveExists(DAScales)) return [$"", $"", "No apfreq/DAScales data"] endif @@ -2192,20 +2202,9 @@ static Function [WAVE/D fitOffset, WAVE/D fitSlope, string errMsg] PSQ_DS_FitFre // DAScale: {50, 100, 130, 200} // deletedIndizes: {3} // numPoints = 4 - [WAVE apfreqFiltered, WAVE DAScalesFiltered, WAVE deletedIndizes, numPoints] = PSQ_DS_FilterFrequencyCurrentData(apfreq, DAScales) - // WAVE apfre - // WaveClear apfreq, DAScales - // - // if(ignorefINegSlope) - // [WAVE apFreqFilteredTwice, WAVE DAScalesFilteredTwice, WAVE deletedIndizesTwice] = PSQ_DS_FilterNegativefISlopes(apFreqFiltered, DAScFreqFiltered) - // - // if(WaveExists(deletedIndizesTwice)) - // Concatenate/FREE/NP=(ROWS) - // endif - // endif - // - hasEnoughPoints = (numPoints >= PSQ_DA_NUM_POINTS_LINE_FIT) + [WAVE apfreqFiltered, WAVE DAScalesFiltered, WAVE deletedIndizes, numPoints] = PSQ_DS_FilterFrequencyCurrentData(apfreq, DAScales, mode) + hasEnoughPoints = (numPoints >= PSQ_DA_NUM_POINTS_LINE_FIT) if(singleFit && hasEnoughPoints) DeletePoints/M=(ROWS) 0, (numPoints - PSQ_DA_NUM_POINTS_LINE_FIT), apFreqFiltered, DAScalesFiltered numPoints = PSQ_DA_NUM_POINTS_LINE_FIT @@ -2530,6 +2529,29 @@ static Function/WAVE PSQ_DS_GatherDAScaleFillin(STRUCT PSQ_DS_DAScaleParams &cdp return results End +/// @brief Calculate the new DAScale value after a negative fI slope +/// +/// @param cdp calculation parameters +/// @param type one of @ref FutureDAScaleReason +/// @param perc percentage to use of the maximum possible DAScale +/// @param x DAScale of last acquired sweep [pA] +/// @param y frequency value of last acquired sweep [Hz] +static Function/WAVE PSQ_DS_CalculateDAScaleForNegSlope(STRUCT PSQ_DS_DAScaleParams &cdp, string type, variable perc, variable x, variable y) + + variable absDAScaleStepMin, absDAScaleStepMax, xp + + ASSERT(PSQ_DS_DAScaleParamsAreFinite(cdp), "Invalid cdp values") + + absDAScaleStepMin = cdp.daScaleStepMinNorm * y + absDAScaleStepMax = cdp.daScaleStepMaxNorm * y + + xp = x + absDAScaleStepMax * perc + + Make/T/FREE DAScaleWithType = {PSQ_DS_AD_BuildFutureDAScaleEntry(type, xp)} + + return DAScaleWithType +End + /// @brief Calculate the new DAScale value for adaptive threshold using linear extrapolation /// /// @param cdp calculation parameters @@ -2706,6 +2728,32 @@ static Function PSQ_DS_IsValidFitSlopePosition(WAVE fitSlopes, WAVE DAScales, va return DAScales[idxSlope] > DAScales[idxMax] End +static Function [variable fitOffset, variable fitSlope] PSQ_DS_StoreFitOffsetAndSlope(string device, variable sweepNo, variable headstage, WAVE fitOffsetAll, string lbnKeyOffset, WAVE fitSlopeAll, string lbnKeySlope) + + string key + + if(WaveExists(fitOffsetAll) && WaveExists(fitSlopeAll)) + ASSERT(DimSize(fitOffsetAll, ROWS) == 1 && DimSize(fitSlopeAll, ROWS) == 1, "Expected only one fit slope/offset") + fitOffset = fitOffsetAll[0] + fitSlope = fitSlopeAll[0] + + WAVE fitOffsetLBN = LBN_GetNumericWave() + fitOffsetLBN[headstage] = fitOffset + key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, lbnKeyOffset) + ED_AddEntryToLabnotebook(device, key, fitOffsetLBN, overrideSweepNo = sweepNo, unit = "Hz") + + WAVE fitSlopeLBN = LBN_GetNumericWave() + fitSlopeLBN[headstage] = fitSlope + key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, lbnKeySlope) + ED_AddEntryToLabnotebook(device, key, fitSlopeLBN, overrideSweepNo = sweepNo, unit = "% of Hz/pA") + else + fitOffset = NaN + fitSlope = NaN + endif + + return [fitOffset, fitSlope] +End + /// @brief Evaluate the complete adaptive supra threshold sweep /// /// - Gather AP frequency and DAscale data @@ -2717,7 +2765,7 @@ End static Function [WAVE/T futureDAScales] PSQ_DS_EvaluateAdaptiveThresholdSweep(string device, variable sweepNo, variable headstage, STRUCT PSQ_DS_DAScaleParams &cdp) string key, errMsg - variable maxSlope, validFit, fitOffset, fitSlope + variable maxSlope, validFit, fitOffset, fitSlope, fitOffsetForDAScale, fitSlopeForDAScale WAVE textualValues = GetLBTextualValues(device) WAVE numericalValues = GetLBNumericalValues(device) @@ -2726,28 +2774,13 @@ static Function [WAVE/T futureDAScales] PSQ_DS_EvaluateAdaptiveThresholdSweep(st [WAVE/T futureDAScales, WAVE apfreqs, WAVE DAScales] = PSQ_DS_GatherFutureDAScalesAndFrequency(device, sweepNo, headstage, cdp) - [WAVE fitOffsetAll, WAVE fitSlopeAll, errMsg] = PSQ_DS_FitFrequencyCurrentData(device, sweepNo, apfreqs, DAScales, singleFit = 1) + [WAVE fitOffsetAll, WAVE fitSlopeAll, errMsg] = PSQ_DS_FitFrequencyCurrentData(device, sweepNo, apfreqs, DAScales, PSQ_DS_FIT_MODE_IGN_DUPS, singleFit = 1) + [fitOffset, fitSlope] = PSQ_DS_StoreFitOffsetAndSlope(device, sweepNo, headstage, fitOffsetAll, PSQ_FMT_LBN_DA_AT_FI_OFFSET, fitSlopeAll, PSQ_FMT_LBN_DA_AT_FI_SLOPE) validFit = PSQ_DS_AreFitResultsValid(device, sweepNo, headstage, fitOffsetAll, fitSlopeAll) - if(WaveExists(fitOffsetAll) && WaveExists(fitSlopeAll)) - ASSERT(DimSize(fitOffsetAll, ROWS) == 1 && DimSize(fitSlopeAll, ROWS) == 1, "Expected only one fit slope/offset") - fitOffset = fitOffsetAll[0] - fitSlope = fitSlopeAll[0] - - WAVE fitOffsetLBN = LBN_GetNumericWave() - fitOffsetLBN[headstage] = fitOffset - key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_AT_FI_OFFSET) - ED_AddEntryToLabnotebook(device, key, fitOffsetLBN, overrideSweepNo = sweepNo, unit = "Hz") - - WAVE fitSlopeLBN = LBN_GetNumericWave() - fitSlopeLBN[headstage] = fitSlope - key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_FI_SLOPE) - ED_AddEntryToLabnotebook(device, key, fitSlopeLBN, overrideSweepNo = sweepNo, unit = "% of Hz/pA") - else - fitOffset = NaN - fitSlope = NaN - endif + [WAVE fitOffsetForDAScaleAll, WAVE fitSlopeForDAScaleAll, errMsg] = PSQ_DS_FitFrequencyCurrentData(device, sweepNo, apfreqs, DAScales, PSQ_DS_FIT_MODE_IGN_DUPS_AND_NEG, singleFit = 1) + [fitOffsetForDAScale, fitSlopeForDAScale] = PSQ_DS_StoreFitOffsetAndSlope(device, sweepNo, headstage, fitOffsetForDAScaleAll, PSQ_FMT_LBN_DA_AT_FI_OFFSET_DASCALE, fitSlopeForDAScaleAll, PSQ_FMT_LBN_DA_AT_FI_SLOPE_DASCALE) [maxSlope, WAVE fitSlopesAll, WAVE DAScalesAll] = PSQ_DS_CalculateMaxSlopeAndWriteToLabnotebook(device, sweepNo, headstage, fitSlope) @@ -2828,13 +2861,17 @@ static Function [string currentSCI, string RhSuAd, variable headstageContingency switch(type) case PSQ_DS_FI_SLOPE: - return [PSQ_FMT_LBN_DA_fI_SLOPE, PSQ_FMT_LBN_DA_AT_RSA_fI_SLOPES, HCM_DEPEND] + return [PSQ_FMT_LBN_DA_AT_FI_SLOPE, PSQ_FMT_LBN_DA_AT_RSA_fI_SLOPES, HCM_DEPEND] + case PSQ_DS_FI_SLOPE_DASCALE: + return [PSQ_FMT_LBN_DA_AT_FI_SLOPE_DASCALE, PSQ_FMT_LBN_DA_AT_RSA_FI_SLOPES_DASCALE, HCM_DEPEND] case PSQ_DS_FI_SLOPE_REACHED_PASS: return [PSQ_FMT_LBN_DA_fI_SLOPE_REACHED_PASS, PSQ_FMT_LBN_DA_AT_RSA_fI_SLOPES_PASS, HCM_INDEP] case PSQ_DS_FI_NEG_SLOPE_PASS: return [PSQ_FMT_LBN_DA_AT_FI_NEG_SLOPE_PASS, PSQ_FMT_LBN_DA_AT_RSA_FI_NEG_SLOPES_PASS, HCM_INDEP] case PSQ_DS_FI_OFFSET: return [PSQ_FMT_LBN_DA_AT_FI_OFFSET, PSQ_FMT_LBN_DA_AT_RSA_FI_OFFSETS, HCM_DEPEND] + case PSQ_DS_FI_OFFSET_DASCALE: + return [PSQ_FMT_LBN_DA_AT_FI_OFFSET_DASCALE, PSQ_FMT_LBN_DA_AT_RSA_FI_OFFSETS_DASCALE, HCM_DEPEND] case PSQ_DS_SWEEP_PASS: return [PSQ_FMT_LBN_SWEEP_PASS, PSQ_FMT_LBN_DA_AT_RSA_SWEEPS, HCM_INDEP] case PSQ_DS_SWEEP: @@ -3062,8 +3099,13 @@ static Function PSQ_DS_AdaptiveIsFinished(string device, variable sweepNo, varia // TODO [x] new lbn entries: negative fI slope QC // TODO [x] new passing criteria 1: two negative slopes in a row // TODO [x] a FI slope QC passing and one negative slope in a row (in this order) + // TODO [ ] add another entry into futureDASCales if it passed due to 1. and 2. that + // splits the difference between the last da scale with a positive slope + // and the first with a negative one // TODO [ ] For all sweeps with negative slope we don't care about post pulse baseline QC - // TODO [ ] ignore sweeps with negative slopes for DAScale estimation + // TODO [x] ignore sweeps with negative slopes for DAScale estimation + // TODO [ ] DAScaleNegativeSlopePercent (1-100% of maxDASCaleStep) is + // applied after each negative slope to set the DA scale [WAVE sweepPassed, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_SWEEP_PASS, fromRhSuAd = fromRhSuAd) @@ -3422,9 +3464,9 @@ static Function [variable fitOffset, variable fitSlope, variable DAScale, variab [WAVE apfreqAll, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_APFREQ, filterPassing = 1) ASSERT(!emptySCI, "Unexpected emptySCI") - [WAVE fitSlopeAll, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_FI_SLOPE, filterPassing = 1) + [WAVE fitSlopeAll, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_FI_SLOPE_DASCALE, filterPassing = 1) ASSERT(!emptySCI, "Unexpected emptySCI") - [WAVE fitOffsetAll, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_FI_OFFSET, filterPassing = 1) + [WAVE fitOffsetAll, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_FI_OFFSET_DASCALE, filterPassing = 1) ASSERT(!emptySCI, "Unexpected emptySCI") [WAVE DAScaleAll, emptySCI] = PSQ_DS_GetLabnotebookData(numericalValues, textualValues, sweepNo, headstage, PSQ_DS_DASCALE, filterPassing = 1) ASSERT(!emptySCI, "Unexpected emptySCI") @@ -3924,7 +3966,7 @@ Function PSQ_DAScale(string device, STRUCT AnalysisFunction_V3 &s) variable sweepPassed, setPassed, length, minLength, reachedFinalSlope, fitOffset, fitSlope, apfreq, enoughFIPointsPassedQC variable minimumSpikeCount, maximumSpikeCount, daScaleModifierParam, measuredAllFutureDAScales, fallbackDAScaleRangeFac variable sweepsInSet, passesInSet, acquiredSweepsInSet, multiplier, asyncAlarmPassed, supraStimsetCycle, sweepPassedWoPPBaselineQC - variable daScaleStepMinNorm, daScaleStepMaxNorm, maxSlope, validFit, emptySCI, oorDAScaleQC, limitCheck + variable daScaleStepMinNorm, daScaleStepMaxNorm, maxSlope, validFit, emptySCI, oorDAScaleQC, limitCheck, dascaleNegativeSlopePercent, negSlopePassed string msg, stimset, key, opMode, offsetOp, textboxString, str, errMsg, type variable daScaleOffset variable finalSlopePercent = NaN @@ -3974,6 +4016,7 @@ Function PSQ_DAScale(string device, STRUCT AnalysisFunction_V3 &s) dascaleStepWidthMinMaxRatio = AFH_GetAnalysisParamNumerical("DaScaleStepWidthMinMaxRatio", s.params, defValue = PSQ_DA_DASCALE_STEP_WITH_MIN_MAX_FACTOR) absFrequencyMinDistance = AFH_GetAnalysisParamNumerical("AbsFrequencyMinDistance", s.params, defValue = PSQ_DA_ABS_FREQUENCY_MIN_DISTANCE) fallbackDAScaleRangeFac = AFH_GetAnalysisParamNumerical("DAScaleRangeFactor", s.params, defValue = PSQ_DA_FALLBACK_DASCALE_RANGE_FAC) + dascaleNegativeSlopePercent = AFH_GetAnalysisParamNumerical("DAScaleNegativeSlopePercent", s.params, defValue = PSQ_DA_FALLBACK_DASCALE_NEG_SLOPE_PERC) STRUCT PSQ_DS_DAScaleParams cdp PSQ_DS_InitDAScaleParams(cdp) @@ -4086,8 +4129,9 @@ Function PSQ_DAScale(string device, STRUCT AnalysisFunction_V3 &s) key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_AT_RSA_DASCALE) ED_AddEntryToLabnotebook(device, key, DAScalesTextLBN, overrideSweepNo = s.sweepNo) - [WAVE fitOffsetFromRhSuAd, WAVE fitSlopeFromRhSuAd, errMsg] = PSQ_DS_FitFrequencyCurrentData(device, s.sweepNo, \ - apfreqRhSuAd, DAScalesRhSuAd) + [WAVE fitOffsetFromRhSuAd, WAVE fitSlopeFromRhSuAd, errMsg] = PSQ_DS_FitFrequencyCurrentData(device, s.sweepNo, \ + apfreqRhSuAd, DAScalesRhSuAd, \ + PSQ_DS_FIT_MODE_IGN_DUPS) if(!WaveExists(fitOffsetFromRhSuAd) || !WaveExists(fitSlopeFromRhSuAd)) printf "The f-I fit of the rheobase/supra data failed due to: \"%s\"\r", errMsg @@ -4137,6 +4181,20 @@ Function PSQ_DAScale(string device, STRUCT AnalysisFunction_V3 &s) key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_AT_RSA_FI_NEG_SLOPES_PASS) ED_AddEntryToLabnotebook(device, key, negFitSlopeQCLBN, overrideSweepNo = s.sweepNo, unit = LABNOTEBOOK_BINARY_UNIT) + // generate slopes and offsets for DAScale estimation + [WAVE fitOffsetForDAScaleFromRhSuAd, WAVE fitSlopeForDAScaleFromRhSuAd, errMsg] = PSQ_DS_FitFrequencyCurrentData(device, s.sweepNo, \ + apfreqRhSuAd, DAScalesRhSuAd, \ + PSQ_DS_FIT_MODE_IGN_DUPS_AND_NEG) + WAVE/T fitOffsetForDAScaleLBN = LBN_GetTextWave() + fitOffsetForDAScaleLBN[s.headstage] = NumericWaveToList(fitOffsetForDAScaleFromRhSuAd, ";", format = "%.15g") + key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_AT_RSA_FI_OFFSETS_DASCALE) + ED_AddEntryToLabnotebook(device, key, fitOffsetForDAScaleLBN, overrideSweepNo = s.sweepNo, unit = "Hz") + + WAVE/T fitSlopeForDAScaleLBN = LBN_GetTextWave() + fitSlopeForDAScaleLBN[s.headstage] = NumericWaveToList(fitSlopeForDAScaleFromRhSuAd, ";", format = "%.15g") + key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_AT_RSA_FI_SLOPES_DASCALE) + ED_AddEntryToLabnotebook(device, key, fitSlopeForDAScaleLBN, overrideSweepNo = s.sweepNo, unit = "% of Hz/pA") + if(validFit) if(!WaveExists(futureDAScales)) @@ -4149,7 +4207,11 @@ Function PSQ_DAScale(string device, STRUCT AnalysisFunction_V3 &s) endif endif - WAVE/T DAScaleNew = PSQ_DS_CalculateDAScale(cdp, PSQ_DS_AD_REGULAR_RHSUAD, fitOffsetFromRhSuAd[Inf], fitSlopeFromRhSuAd[Inf], DAScalesRhSuAd[Inf], apfreqRhSuAd[Inf]) + if(negFitSlopeQCfromRhSuAd[inf]) + WAVE/T DAScaleNew = PSQ_DS_CalculateDAScaleForNegSlope(cdp, PSQ_DS_AD_REGULAR_RHSUAD, dascaleNegativeSlopePercent, DAScalesRhSuAd[Inf], apfreqRhSuAd[Inf]) + else + WAVE/T DAScaleNew = PSQ_DS_CalculateDAScale(cdp, PSQ_DS_AD_REGULAR_RHSUAD, fitOffsetForDAScaleFromRhSuAd[Inf], fitSlopeForDAScaleFromRhSuAd[Inf], DAScalesRhSuAd[Inf], apfreqRhSuAd[Inf]) + endif Concatenate/FREE/NP=(ROWS)/T {DAScaleNew}, futureDAScales else @@ -4419,10 +4481,19 @@ Function PSQ_DAScale(string device, STRUCT AnalysisFunction_V3 &s) ASSERT(IsFinite(measuredAllFutureDAScales), "Invalid measuredAllFutureDAScales") if(sweepPassed) - if(measuredAllFutureDAScales) - [fitOffset, fitSlope, DAScale, apfreq] = PSQ_DS_GetValuesOfLargestDAScale(numericalValues, textualValues, s.sweepNo, s.headstage) + key = CreateAnaFuncLBNKey(PSQ_DA_SCALE, PSQ_FMT_LBN_DA_AT_FI_NEG_SLOPE_PASS, query = 1) + negSlopePassed = GetLastSettingIndep(numericalValues, s.sweepNo, key, UNKNOWN_MODE) + + if(measuredAllFutureDAScales || negSlopePassed) + if(measuredAllFutureDAScales) + [fitOffset, fitSlope, DAScale, apfreq] = PSQ_DS_GetValuesOfLargestDAScale(numericalValues, textualValues, s.sweepNo, s.headstage) + WAVE/T DAScaleWithType = PSQ_DS_CalculateDAScaleForNegSlope(cdp, PSQ_DS_AD_REGULAR, dascaleNegativeSlopePercent, DAScale, apfreq) + elseif(negSlopePassed) + WAVE/T DAScaleWithType = PSQ_DS_CalculateDAScale(cdp, PSQ_DS_AD_REGULAR, fitOffset, fitSlope, DAScale, apfreq) + else + ASSERT(0, "Impossible case") + endif - WAVE/T DAScaleWithType = PSQ_DS_CalculateDAScale(cdp, PSQ_DS_AD_REGULAR, fitOffset, fitSlope, DAScale, apfreq) Concatenate/NP=(ROWS)/T/FREE {DAScaleWithType}, futureDAScales PSQ_DS_CalcFutureDAScalesAndStoreInLBN(device, s.sweepNo, s.headstage, futureDAScales) diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 9c0f7f9654..56198d6292 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -1167,6 +1167,9 @@ StrConstant PSQ_FMT_LBN_RB_LIMITED_RES = "%s limited resolut." StrConstant PSQ_FMT_LBN_DA_FI_SLOPE = "%s f-I slope" StrConstant PSQ_FMT_LBN_DA_AT_FI_NEG_SLOPE_PASS = "%s f-I neg. slope QC" StrConstant PSQ_FMT_LBN_DA_AT_FI_OFFSET = "%s f-I offset" +StrConstant PSQ_FMT_LBN_DA_AT_FI_SLOPE = "%s f-I slope" +StrConstant PSQ_FMT_LBN_DA_AT_FI_OFFSET_DASCALE = "%s f-I offset for DAScale estimation" +StrConstant PSQ_FMT_LBN_DA_AT_FI_SLOPE_DASCALE = "%s f-I slope for DAScale estimation" StrConstant PSQ_FMT_LBN_DA_FI_SLOPE_REACHED_PASS = "%s f-I slope QC" StrConstant PSQ_FMT_LBN_DA_OPMODE = "%s operation mode" StrConstant PSQ_FMT_LBN_DA_AT_ENOUGH_FI_POINTS_PASS = "%s enough f-I pairs for line fit QC" @@ -1179,6 +1182,8 @@ StrConstant PSQ_FMT_LBN_DA_AT_MIN_DASCALE_NORM = "%s Min. norm. DAScale StrConstant PSQ_FMT_LBN_DA_AT_RSA_DASCALE = "%s DAScale from rheobase, supra, adaptive" StrConstant PSQ_FMT_LBN_DA_AT_RSA_FI_OFFSETS = "%s f-I offsets from rheobase, supra, adaptive" StrConstant PSQ_FMT_LBN_DA_AT_RSA_FI_SLOPES = "%s f-I slopes from rheobase, supra, adaptive" +StrConstant PSQ_FMT_LBN_DA_AT_RSA_FI_OFFSETS_DASCALE = "%s f-I offsets for DAScale estimation from rheobase, supra, adaptive" +StrConstant PSQ_FMT_LBN_DA_AT_RSA_FI_SLOPES_DASCALE = "%s f-I slopes for DAScale estimation from rheobase, supra, adaptive" StrConstant PSQ_FMT_LBN_DA_AT_RSA_FI_NEG_SLOPES_PASS = "%s f-I neg. slopes QC from rheobase, supra, adaptive" StrConstant PSQ_FMT_LBN_DA_AT_RSA_FI_SLOPES_PASS = "%s f-I slope QCs from rheobase, supra, adaptive" StrConstant PSQ_FMT_LBN_DA_AT_RSA_FREQ = "%s AP frequency from rheobase, supra, adaptive"