示例#1
0
        private double computeTapValue()
        {
            double modifiedAcc = getModifiedAcc();

            // Assume SS for non-stream parts
            double accOnStreams = 1 - (1 - modifiedAcc) * totalHits / Attributes.StreamNoteCount;

            double mashLevel = 1 - SpecialFunctions.Logistic((accOnStreams - 0.75) / 0.1) / SpecialFunctions.Logistic(2.5);

            double tapSkill = LinearSpline.InterpolateSorted(Attributes.MashLevels, Attributes.TapSkills)
                              .Interpolate(mashLevel);

            double tapValue = tapSkillToPP(tapSkill);

            // Buff high acc
            double accBuff = Math.Exp((accOnStreams - 1) * 60) * tapValue * 0.2f;

            tapValue += accBuff;

            // Penalize misses exponentially
            tapValue *= Math.Pow(0.93f, countMiss);

            double approachRateFactor = 1.0f;

            if (Attributes.ApproachRate > 10.33f)
            {
                approachRateFactor += 0.2f * (Attributes.ApproachRate - 10.33f);
            }
            tapValue *= approachRateFactor;

            return(tapValue);
        }
        /// <summary>
        /// Creates a curve.  The curve will be flat from the anchor date to the first date and from the last date in dates until maximumDate
        /// </summary>
        /// <param name="currency"></param>
        /// <param name="anchorDate">Date from which the curve applies.  Interpolation before this date won't work.</param>
        /// <param name="dates">Must be sorted in increasing order.</param>
        /// <param name="rates">The rates.  If the curve is going to be used to supply discount factors then these rates must be continuously compounded.</param>
        /// <param name="maximumDate">The date beyond which interpolation will not be allowed.  If it is null or left out then the last date in dates will be used.</param>
        public DatesAndRates(Currency currency, Date anchorDate, Date[] dates, double[] rates, Date maximumDate = null)
        {
            this.anchorDate = anchorDate;
            for (int i = 1; i < dates.Length; i++)
            {
                if (dates[i].value <= dates[i - 1].value)
                {
                    throw new ArgumentException("Dates must be strictly increasing");
                }
            }
            List <double> datesList = new List <double>(dates.GetValues());
            List <double> ratesList = new List <double>(rates.Clone() as double[]);

            if (dates[0] > anchorDate)
            {
                datesList.Insert(0, anchorDate.value);
                ratesList.Insert(0, rates[0]);
            }
            if (maximumDate != null)
            {
                datesList.Add(maximumDate);
                ratesList.Add(rates.Last());
            }
            this.anchorDateValue = anchorDate;
            this.dates           = datesList.ToArray();
            this.rates           = ratesList.ToArray();
            this.currency        = currency;
            spline = LinearSpline.InterpolateSorted(this.dates, this.rates);
        }
示例#3
0
        public PiecewiseLinearInjectWithdrawConstraint([NotNull] IEnumerable <InjectWithdrawRangeByInventory> injectWithdrawRanges)
        {
            if (injectWithdrawRanges == null)
            {
                throw new ArgumentNullException(nameof(injectWithdrawRanges));
            }

            _injectWithdrawRanges = injectWithdrawRanges.OrderBy(injectWithdrawRange => injectWithdrawRange.Inventory)
                                    .ToArray();

            if (_injectWithdrawRanges.Length < 2)
            {
                throw new ArgumentException("Inject/withdraw ranges collection must contain at least two elements.", nameof(injectWithdrawRanges));
            }

            double[] inventories = _injectWithdrawRanges.Select(injectWithdrawRange => injectWithdrawRange.Inventory)
                                   .ToArray();

            double[] maxInjectWithdrawRates = _injectWithdrawRanges
                                              .Select(injectWithdrawRange => injectWithdrawRange.InjectWithdrawRange.MaxInjectWithdrawRate)
                                              .ToArray();

            double[] minInjectWithdrawRates = _injectWithdrawRanges
                                              .Select(injectWithdrawRange => injectWithdrawRange.InjectWithdrawRange.MinInjectWithdrawRate)
                                              .ToArray();

            _maxInjectWithdrawLinear = LinearSpline.InterpolateSorted(inventories, maxInjectWithdrawRates);
            _minInjectWithdrawLinear = LinearSpline.InterpolateSorted(inventories, minInjectWithdrawRates);
        }
        private IInterpolation interpolation_U(UV point, Index index)
        {
            IndexRange horizontalExpansion = this.horizontalRange(index);

            //getting all vertical ranges of the horizontal
            IndexRange[] verticals = new IndexRange[horizontalExpansion.Length];
            Index        next      = horizontalExpansion.Min.Copy();

            for (int i = 0; i < horizontalExpansion.Length; i++)
            {
                verticals[i] = this.verticalRange(next);
                next.I++;
            }
            //getting interpolators for u values
            IInterpolation[] verticalsU = new IInterpolation[horizontalExpansion.Length];
            for (int i = 0; i < verticals.Length; i++)
            {
                verticalsU[i] = this.toInterpolation(verticals[i]);
            }
            //generating u interpolation
            double[] x = new double[horizontalExpansion.Length];
            double[] y = new double[horizontalExpansion.Length];
            next = horizontalExpansion.Min.Copy();
            for (int i = 0; i < horizontalExpansion.Length; i++)
            {
                x[i] = this._cellularFloor.FindCell(next).U;
                y[i] = verticalsU[i].Interpolate(point.V);
                next.I++;
            }
            IInterpolation interpolatorU = null;

            switch (Activity.InterpolationMethod)
            {
            case SpatialAnalysis.FieldUtility.InterpolationMethod.CubicSpline:
                if (x.Length > 1)
                {
                    interpolatorU = CubicSpline.InterpolateBoundariesSorted(x, y,
                                                                            SplineBoundaryCondition.Natural, 0,
                                                                            SplineBoundaryCondition.Natural, 0);
                }
                else
                {
                    interpolatorU = LinearSpline.InterpolateSorted(x, y);
                }
                break;

            //case FieldUtility.InterpolationMethod.Barycentric:
            //    interpolatorU = Barycentric.InterpolatePolynomialEquidistantSorted(x, y);
            //    break;
            case SpatialAnalysis.FieldUtility.InterpolationMethod.Linear:
                interpolatorU = LinearSpline.InterpolateSorted(x, y);
                break;

            default:
                break;
            }
            return(interpolatorU);
        }
 public void SetRate(int index, double rate)
 {
     if (dateOffset == 1 && index == 0)
     {
         rates[0] = rate;
     }
     rates[index + dateOffset] = rate;
     spline = LinearSpline.InterpolateSorted(dateValues, rates);
 }
        public override void Calculate()
        {
            Times = Generate.LinearSpaced(base.Samples, base.Start, base.Start + base.Duration);

            LinearSpline interpolator = (UserTimes.Length < 2)
                                ? LinearSpline.InterpolateSorted(new double[] { UserTimes[0] + 1, UserTimes[0] }, new double[] { UserValues[0], UserValues[0] })
                                : LinearSpline.InterpolateSorted(UserTimes, UserValues);

            values = Times.Select(t => interpolator.Interpolate(t)).ToArray();
        }
 /// <summary>
 /// Interpolate the curve.
 /// </summary>
 /// <param name="date">The date at which the rate is required.</param>
 /// <returns></returns>
 public double InterpAtDate(Date date)
 {
     if (spline == null)
     {
         spline = LinearSpline.InterpolateSorted(this.dates, this.rates);
     }
     if (date.value < anchorDateValue)
     {
         throw new ArgumentException("Interpolation date  (" + date.ToString() + ") is before the anchor date of the curve.(" + (new Date(anchorDateValue)).ToString() + ")");
     }
     if (date.value > dates.Last())
     {
         throw new ArgumentException("Interpolation date (" + date.ToString() + ") is after the last date on the curve.(" + (new Date(dates.Last())).ToString() + ")");
     }
     return(spline.Interpolate(date));
 }
 public void SetDates(Date[] dates)
 {
     if (dates[0] > anchorDate)
     {
         List <Date> dateList = dates.ToList();
         dateList.Insert(0, anchorDate);
         this.dates = dateList.ToArray();
         dateOffset = 1;
     }
     else
     {
         this.dates = dates;
     }
     dateValues = this.dates.GetValues();
     rates      = Vector.Ones(this.dates.Length).Multiply(0.02);
     spline     = LinearSpline.InterpolateSorted(dateValues, rates);
 }
示例#9
0
        public static object[,] InterpLinear([ExcelArgument(Description = "A vector of x values.  Must be in increasing order")] double[] knownX,
                                             [ExcelArgument(Description = "A vector of y values.  Must be the same length as knownX")] Double[] knownY,
                                             [ExcelArgument(Description = "x values at which interpolation is required.")] Double[,] requiredX)
        {
            LinearSpline spline = LinearSpline.InterpolateSorted(knownX, knownY);

            object[,] result = new object[requiredX.GetLength(0), requiredX.GetLength(1)];

            for (int x = 0; x < requiredX.GetLength(0); x += 1)
            {
                for (int y = 0; y < requiredX.GetLength(1); y += 1)
                {
                    result[x, y] = spline.Interpolate(requiredX[x, y]);
                }
            }
            return(result);
        }
示例#10
0
        private static IInterpolation GetInterpolator(ColorInterpolation interpolation, double[] x, double[] y)
        {
            switch (interpolation)
            {
            case ColorInterpolation.Akima:
                return(CubicSpline.InterpolateAkimaSorted(x, y));

            case ColorInterpolation.Spline:
                return(CubicSpline.InterpolateNaturalSorted(x, y));

            case ColorInterpolation.Linear:
                return(LinearSpline.InterpolateSorted(x, y));

            default:
                throw new ArgumentException("interpolation");
            }
        }
示例#11
0
文件: OsuMovement.cs 项目: tjgtll/osu
        private static void prepareInterp(double[] ds, double[] ks, double[] scales, double[,,] coeffs,
                                          ref LinearSpline kInterp, ref LinearSpline scaleInterp, ref LinearSpline[,] coeffsInterps)
        {
            kInterp     = LinearSpline.InterpolateSorted(ds, ks);
            scaleInterp = LinearSpline.InterpolateSorted(ds, scales);

            coeffsInterps = new LinearSpline[coeffs.GetLength(0), numCoeffs];
            for (int i = 0; i < coeffs.GetLength(0); i++)
            {
                for (int j = 0; j < numCoeffs; j++)
                {
                    double[] coeff_ij = new double[coeffs.GetLength(2)];
                    for (int k = 0; k < coeffs.GetLength(2); k++)
                    {
                        coeff_ij[k] = coeffs[i, j, k];
                    }
                    coeffsInterps[i, j] = LinearSpline.InterpolateSorted(ds, coeff_ij);
                }
            }
        }
示例#12
0
        private double computeTapValue()
        {
            double modifiedAcc = getModifiedAcc();

            // Assume SS for non-stream parts
            double accOnStreams = 1 - (1 - modifiedAcc) * totalHits / Attributes.StreamNoteCount;

            // accOnStreams can be negative. The formula below ensures a positive acc while
            // preserving the value when accOnStreams is close to 1
            double accOnStreamsPositive = Math.Exp(accOnStreams - 1);

            double urOnStreams = 10 * (greatWindow) / (Math.Sqrt(2) * SpecialFunctions.ErfInv(accOnStreamsPositive));

            double mashLevel = SpecialFunctions.Logistic(((urOnStreams * Attributes.BurstStrain) - 4000) / 1000);

            double tapSkill = LinearSpline.InterpolateSorted(Attributes.MashLevels, Attributes.TapSkills)
                              .Interpolate(mashLevel);

            double tapValue = tapSkillToPP(tapSkill);

            // Buff high acc
            double accBuff = Math.Exp((accOnStreams - 1) * 60) * tapValue * 0.2f;

            tapValue += accBuff;

            // Penalize misses exponentially
            tapValue *= Math.Pow(0.93f, countMiss);

            double approachRateFactor = 1.0f;

            if (Attributes.ApproachRate > 10.33f)
            {
                approachRateFactor += 0.2f * (Attributes.ApproachRate - 10.33f);
            }
            tapValue *= approachRateFactor;

            return(tapValue);
        }
示例#13
0
 public InterpolatedCurve(double[] xVals, double[] yVals)
 {
     _spline = LinearSpline.InterpolateSorted(xVals, yVals);
 }
示例#14
0
        private double computeAimValue()
        {
            // Guess the number of misaims from combo
            int effectiveMissCount = Math.Max(countMiss, (int)(Math.Floor((beatmapMaxCombo - 0.1 * countSliders) / scoreMaxCombo)));


            // Get player's throughput according to combo
            int comboTPCount     = Attributes.ComboTPs.Length;
            var comboPercentages = Generate.LinearSpaced(comboTPCount, 1.0 / comboTPCount, 1);

            double scoreComboPercentage = ((double)scoreMaxCombo) / beatmapMaxCombo;
            double comboTP = LinearSpline.InterpolateSorted(comboPercentages, Attributes.ComboTPs)
                             .Interpolate(scoreComboPercentage);


            // Get player's throughput according to miss count
            double missTP;

            if (effectiveMissCount == 0)
            {
                missTP = Attributes.MissTPs[0];
            }
            else
            {
                missTP = LinearSpline.InterpolateSorted(Attributes.MissCounts, Attributes.MissTPs)
                         .Interpolate(effectiveMissCount);
                missTP = Math.Max(missTP, 0);
            }

            // Combine combo based throughput and miss count based throughput
            double tp = Math.Pow(comboTP, comboWeight) * Math.Pow(missTP, 1 - comboWeight);


            double modifiedAcc = getModifiedAcc();

            double accOnCheeseNotes = 1 - (1 - modifiedAcc) * Math.Sqrt(totalHits / Attributes.CheeseNoteCount);

            // accOnCheeseNotes can be negative. The formula below ensures a positive acc while
            // preserving the value when accOnCheeseNotes is close to 1
            double accOnCheeseNotesPositive = Math.Exp(accOnCheeseNotes - 1);

            double urOnCheeseNotes = 10 * greatWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(accOnCheeseNotesPositive));

            double cheeseLevel = SpecialFunctions.Logistic(((urOnCheeseNotes * Attributes.AimDiff) - 4300) / 2000);

            double cheeseFactor = LinearSpline.InterpolateSorted(Attributes.CheeseLevels, Attributes.CheeseFactors)
                                  .Interpolate(cheeseLevel);


            if (mods.Any(m => m is OsuModTouchDevice))
            {
                tp = Math.Pow(tp, 0.8);
            }

            double aimValue = tpToPP(tp * cheeseFactor);

            // penalize misses
            aimValue *= Math.Pow(0.96f, effectiveMissCount);

            // Buff very high AR and low AR
            double approachRateFactor = 1.0f;

            if (Attributes.ApproachRate > 10.33f)
            {
                approachRateFactor += 0.2f * (Attributes.ApproachRate - 10.33f);
            }
            else if (Attributes.ApproachRate < 8.0f)
            {
                approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
            }

            aimValue *= approachRateFactor;

            // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
            if (mods.Any(h => h is OsuModHidden))
            {
                aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
            }

            if (mods.Any(h => h is OsuModFlashlight))
            {
                // Apply object-based bonus for flashlight.
                aimValue *= 1.0f + 0.35f * Math.Min(1.0f, totalHits / 200.0f) +
                            (totalHits > 200
                                ? 0.3f * Math.Min(1.0f, (totalHits - 200) / 300.0f) +
                             (totalHits > 500 ? (totalHits - 500) / 2000.0f : 0.0f)
                                : 0.0f);
            }


            // Scale the aim value down slightly with accuracy
            double accLeniency = greatWindow * Attributes.AimDiff / 300;
            double accPenalty  = (SpecialFunctions.Logistic((accuracy - 0.97f) / (-0.01f)) - SpecialFunctions.Logistic(-3)) *
                                 (accLeniency + 1.5f) * 0.1f;

            aimValue *= Math.Exp(-accPenalty);

            return(aimValue);
        }
示例#15
0
        private double computeAimValue()
        {
            if (Attributes.TotalObjectCount <= 1)
            {
                return(0);
            }

            // Get player's throughput according to combo
            int comboTpCount     = Attributes.ComboThroughputs.Length;
            var comboPercentages = Generate.LinearSpaced(comboTpCount, 1.0 / comboTpCount, 1);

            double scoreComboPercentage = ((double)scoreMaxCombo) / Attributes.MaxCombo;
            double comboTp = LinearSpline.InterpolateSorted(comboPercentages, Attributes.ComboThroughputs)
                             .Interpolate(scoreComboPercentage);

            // Get player's throughput according to miss count
            double missTp = LinearSpline.InterpolateSorted(Attributes.MissCounts, Attributes.MissThroughputs)
                            .Interpolate(effectiveMissCount);

            missTp = Math.Max(missTp, 0);

            // Combine combo based throughput and miss count based throughput
            double tp = PowerMean.Of(comboTp, missTp, 20);

            // Hidden mod
            if (mods.Any(h => h is OsuModHidden))
            {
                double hiddenFactor = Attributes.AimHiddenFactor;

                // the buff starts decreasing at AR9.75 and reaches 0 at AR10.75
                if (Attributes.ApproachRate > 10.75)
                {
                    hiddenFactor = 1;
                }
                else if (Attributes.ApproachRate > 9.75)
                {
                    hiddenFactor = 1 + (1 - Math.Pow(Math.Sin((Attributes.ApproachRate - 9.75) * Math.PI / 2), 2)) * (hiddenFactor - 1);
                }

                tp *= hiddenFactor;
            }

            // Account for cheesing
            double modifiedAcc      = getModifiedAcc();
            double accOnCheeseNotes = 1 - (1 - modifiedAcc) * Math.Sqrt(totalHits / Attributes.CheeseNoteCount);

            // accOnCheeseNotes can be negative. The formula below ensures a positive acc while
            // preserving the value when accOnCheeseNotes is close to 1
            double accOnCheeseNotesPositive = Math.Exp(accOnCheeseNotes - 1);
            double urOnCheeseNotes          = 10 * greatWindow / (Math.Sqrt(2) * SpecialFunctions.ErfInv(accOnCheeseNotesPositive));
            double cheeseLevel  = SpecialFunctions.Logistic(((urOnCheeseNotes * Attributes.AimDifficulty) - 3200) / 2000);
            double cheeseFactor = LinearSpline.InterpolateSorted(Attributes.CheeseLevels, Attributes.CheeseFactors)
                                  .Interpolate(cheeseLevel);

            if (mods.Any(m => m is OsuModTouchDevice))
            {
                tp = Math.Min(tp, 1.47 * Math.Pow(tp, 0.8));
            }

            double aimValue = tpToPP(tp * cheeseFactor);

            // penalize misses
            aimValue *= Math.Pow(0.96, Math.Max(effectiveMissCount - miss_count_leniency, 0));

            // Buff long maps
            aimValue *= 1 + (SpecialFunctions.Logistic((totalHits - 2800) / 500.0) - SpecialFunctions.Logistic(-2800 / 500.0)) * 0.22;

            // Buff very high AR and low AR
            double approachRateFactor = 1.0;

            if (Attributes.ApproachRate > 10)
            {
                approachRateFactor += (0.05 + 0.35 * Math.Pow(Math.Sin(Math.PI * Math.Min(totalHits, 1250) / 2500), 1.7)) *
                                      Math.Pow(Attributes.ApproachRate - 10, 2);
            }
            else if (Attributes.ApproachRate < 8.0)
            {
                approachRateFactor += 0.01 * (8.0 - Attributes.ApproachRate);
            }

            aimValue *= approachRateFactor;

            if (mods.Any(h => h is OsuModFlashlight))
            {
                // Apply object-based bonus for flashlight.
                aimValue *= 1.0 + 0.35 * Math.Min(1.0, totalHits / 200.0) +
                            (totalHits > 200
                                ? 0.3 * Math.Min(1.0, (totalHits - 200) / 300.0) +
                             (totalHits > 500 ? (totalHits - 500) / 2000.0 : 0.0)
                                : 0.0);
            }

            // Scale the aim value down with accuracy
            double accLeniency = greatWindow * Attributes.AimDifficulty / 300;
            double accPenalty  = (0.09 / (accuracy - 1.3) + 0.3) * (accLeniency + 1.5);

            aimValue *= 0.2 + SpecialFunctions.Logistic(-((accPenalty - 0.24953) / 0.18));

            return(aimValue);
        }
        private IInterpolation toInterpolation(IndexRange range)
        {
            double[] x    = new double[range.Length];
            double[] y    = new double[x.Length];
            Index    next = range.Min.Copy();

            switch (range.Direction)
            {
            case Direction.Horizontal:
                for (int i = 0; i < x.Length; i++)
                {
                    Cell cell = this._cellularFloor.FindCell(next);
                    x[i]    = cell.U;
                    y[i]    = this.Potentials[cell];
                    next.I += 1;
                }
                break;

            case Direction.Vertical:
                for (int i = 0; i < x.Length; i++)
                {
                    Cell cell = this._cellularFloor.FindCell(next);
                    x[i]    = cell.V;
                    y[i]    = this.Potentials[cell];
                    next.J += 1;
                }
                break;

            default:
                break;
            }
            IInterpolation interpolator = null;

            switch (Activity.InterpolationMethod)
            {
            case SpatialAnalysis.FieldUtility.InterpolationMethod.CubicSpline:
                if (x.Length > 1)
                {
                    interpolator = CubicSpline.InterpolateBoundariesSorted(x, y,
                                                                           SplineBoundaryCondition.Natural, 0,
                                                                           SplineBoundaryCondition.Natural, 0);
                }
                else
                {
                    interpolator = LinearSpline.InterpolateSorted(x, y);
                }
                break;

            //case FieldUtility.InterpolationMethod.Barycentric:
            //    interpolator = Barycentric.InterpolatePolynomialEquidistantSorted(x, y);
            //    break;
            case SpatialAnalysis.FieldUtility.InterpolationMethod.Linear:
                interpolator = LinearSpline.InterpolateSorted(x, y);
                break;

            default:
                break;
            }
            x    = null;
            y    = null;
            next = null;
            return(interpolator);
        }
示例#17
0
        public PairDouble evaluateResonanceFrequency(ChartTypes type, SignalUnits targetUnits, double approximationResonanceFrequency = 0.0)
        {
            int kNumSteps = 4096;
            // Only acceleration data is used to estimate the resonance frequency
            SignalUnits baseUnits = SignalUnits.UNKNOWN;

            if (data[SignalUnits.METERS_PER_SECOND2] != null)
            {
                baseUnits = SignalUnits.METERS_PER_SECOND2;
            }
            else if (data.ContainsKey(SignalUnits.METERS_PER_SECOND2_PER_FORCE) && data[SignalUnits.METERS_PER_SECOND2_PER_FORCE] != null)
            {
                baseUnits = SignalUnits.METERS_PER_SECOND2_PER_FORCE;
            }
            if (baseUnits == SignalUnits.UNKNOWN)
            {
                return(null);
            }
            double[,] complexData = data[baseUnits];
            int nData = frequency.Length;

            double[] realPart = new double[nData];
            double[] imagPart = new double[nData];
            for (int i = 0; i != nData; ++i)
            {
                realPart[i] = complexData[i, 0];
                imagPart[i] = complexData[i, 1];
            }
            LinearSpline          splineRealPart = LinearSpline.InterpolateSorted(frequency, realPart);
            LinearSpline          splineImagPart = LinearSpline.InterpolateSorted(frequency, imagPart);
            List <double>         resFrequencies = null;
            Func <double, double> fun            = null;
            Func <double, double> diffFun        = null;

            switch (type)
            {
            case ChartTypes.REAL_FREQUENCY:
                fun     = x => splineRealPart.Interpolate(x);
                diffFun = x => splineRealPart.Differentiate(x);
                break;

            case ChartTypes.IMAG_FREQUENCY:
                fun     = x => splineImagPart.Differentiate(x);
                diffFun = x => splineImagPart.Differentiate2(x);
                break;
            }
            if (fun == null || diffFun == null)
            {
                return(null);
            }
            double startFrequency = frequency[0];
            double endFrequency   = frequency[nData - 1];

            resFrequencies = findAllRootsBisection(fun, startFrequency, endFrequency, kNumSteps);
            if (resFrequencies == null || resFrequencies.Count == 0)
            {
                return(null);
            }
            double distance;
            double minDistance         = Double.MaxValue;
            int    indClosestResonance = 0;
            int    numFrequencies      = resFrequencies.Count;

            for (int i = 0; i != numFrequencies; ++i)
            {
                try
                {
                    resFrequencies[i] = NewtonRaphson.FindRootNearGuess(fun, diffFun, resFrequencies[i], startFrequency, endFrequency);
                }
                catch
                {
                }
                distance = Math.Abs(resFrequencies[i] - approximationResonanceFrequency);
                if (distance < minDistance)
                {
                    minDistance         = distance;
                    indClosestResonance = i;
                }
            }
            double resonanceFrequency = resFrequencies[indClosestResonance];

            // Evaluate the amplitude value by using data of the target type
            if (baseUnits != targetUnits)
            {
                complexData = data[targetUnits];
                for (int i = 0; i != nData; ++i)
                {
                    realPart[i] = complexData[i, 0];
                    imagPart[i] = complexData[i, 1];
                }
                splineRealPart = LinearSpline.InterpolateSorted(frequency, realPart);
                splineImagPart = LinearSpline.InterpolateSorted(frequency, imagPart);
            }
            double realPeak = splineRealPart.Interpolate(resonanceFrequency);
            double imagPeak = splineImagPart.Interpolate(resonanceFrequency);
            double ampPeak  = Math.Sqrt(Math.Pow(realPeak, 2.0) + Math.Pow(imagPeak, 2.0));

            return(new PairDouble(resonanceFrequency, ampPeak));
        }