/// <summary> /// /// </summary> /// <param name="peakDetector"></param> /// <param name="scanNumbers"></param> /// <param name="peakData"></param> /// <param name="peakFinderOptions"></param> /// <returns>Detected peaks will be in the peakData object</returns> private bool FindPeaksWork( PeakFinder peakDetector, IList <int> scanNumbers, PeakDataContainer peakData, udtSICPeakFinderOptionsType peakFinderOptions) { const float sngPeakMaximum = 0; bool validPeakFound; // Smooth the Y data, and store in peakData.SmoothedYData // Note that if using a Butterworth filter, then we increase peakData.PeakWidthPointsMinimum if too small, compared to 1/SamplingFrequency var peakWidthPointsMinimum = peakData.PeakWidthPointsMinimum; var dataIsSmoothed = SmoothData(peakData.YData, peakData.DataCount, peakFinderOptions, ref peakWidthPointsMinimum, out var smoothedYData, out var errorMessage); // peakWidthPointsMinimum may have been auto-updated peakData.PeakWidthPointsMinimum = peakWidthPointsMinimum; // Store the smoothed data in the data container peakData.SetSmoothedData(smoothedYData); var peakDetectIntensityThresholdPercentageOfMaximum = (int)Math.Round(peakFinderOptions.IntensityThresholdFractionMax * 100); const int peakWidthInSigma = 2; const bool useValleysForPeakWidth = true; const bool movePeakLocationToMaxIntensity = true; if (peakFinderOptions.FindPeaksOnSmoothedData && dataIsSmoothed) { peakData.Peaks = peakDetector.DetectPeaks( peakData.XData, peakData.SmoothedYData, peakFinderOptions.IntensityThresholdAbsoluteMinimum, peakData.PeakWidthPointsMinimum, peakDetectIntensityThresholdPercentageOfMaximum, peakWidthInSigma, useValleysForPeakWidth, movePeakLocationToMaxIntensity); } else { // Look for the peaks, using peakData.PeakWidthPointsMinimum as the minimum peak width peakData.Peaks = peakDetector.DetectPeaks( peakData.XData, peakData.YData, peakFinderOptions.IntensityThresholdAbsoluteMinimum, peakData.PeakWidthPointsMinimum, peakDetectIntensityThresholdPercentageOfMaximum, peakWidthInSigma, useValleysForPeakWidth, movePeakLocationToMaxIntensity); } if (peakData.Peaks == null) { // Fatal error occurred while finding peaks return(false); } if (peakData.PeakWidthPointsMinimum == MINIMUM_PEAK_WIDTH) { // Testing the minimum peak width; run some checks ExamineNarrowPeaks(peakData, peakFinderOptions); } if (peakData.Peaks.Count <= 0) { // No peaks were found return(false); } foreach (var peak in peakData.Peaks) { peak.IsValid = false; // Find the center and boundaries of this peak // Make sure peak.LocationIndex is between peak.LeftEdge and peak.RightEdge if (peak.LeftEdge > peak.LocationIndex) { Console.WriteLine("peak.LeftEdge is > peak.LocationIndex; this is probably a programming error"); peak.LeftEdge = peak.LocationIndex; } if (peak.RightEdge < peak.LocationIndex) { Console.WriteLine("peak.RightEdge is < peak.LocationIndex; this is probably a programming error"); peak.RightEdge = peak.LocationIndex; } // See if the peak boundaries (left and right edges) need to be narrowed or expanded // Do this by stepping left or right while the intensity is decreasing. If an increase is found, but the // next point after the increasing point is less than the current point, then possibly keep stepping; the // test for whether to keep stepping is that the next point away from the increasing point must be less // than the current point. If this is the case, replace the increasing point with the average of the // current point and the point two points away // // Use smoothed data for this step // Determine the smoothing window based on peakData.PeakWidthPointsMinimum // If peakData.PeakWidthPointsMinimum <= 4 then do not filter if (!dataIsSmoothed) { // Need to smooth the data now peakWidthPointsMinimum = peakData.PeakWidthPointsMinimum; dataIsSmoothed = SmoothData( peakData.YData, peakData.DataCount, peakFinderOptions, ref peakWidthPointsMinimum, out smoothedYData, out errorMessage); // peakWidthPointsMinimum may have been auto-updated peakData.PeakWidthPointsMinimum = peakWidthPointsMinimum; // Store the smoothed data in the data container peakData.SetSmoothedData(smoothedYData); } // First see if we need to narrow the peak by looking for decreasing intensities moving toward the peak center // We'll use the unsmoothed data for this while (peak.LeftEdge < peak.LocationIndex - 1) { if (peakData.YData[peak.LeftEdge] > peakData.YData[peak.LeftEdge + 1]) { // OrElse (usedSmoothedDataForPeakDetection AndAlso peakData.SmoothedYData[peak.LeftEdge) < 0) Then peak.LeftEdge += 1; } else { break; } } while (peak.RightEdge > peak.LocationIndex + 1) { if (peakData.YData[peak.RightEdge - 1] < peakData.YData[peak.RightEdge]) { // OrElse (usedSmoothedDataForPeakDetection AndAlso peakData.SmoothedYData[peak.RightEdge) < 0) Then peak.RightEdge -= 1; } else { break; } } // Now see if we need to expand the peak by looking for decreasing intensities moving away from the peak center, // but allowing for small increases // We'll use the smoothed data for this; if we encounter negative values in the smoothed data, we'll keep going until we reach the low point since huge peaks can cause some odd behavior with the Butterworth filter // Keep track of the number of times we step over an increased value ExpandPeakLeftEdge(peakData, peakFinderOptions, peak, sngPeakMaximum, dataIsSmoothed); ExpandPeakRightEdge(peakData, peakFinderOptions, peak, sngPeakMaximum, dataIsSmoothed); peak.IsValid = true; if (!peakFinderOptions.ReturnClosestPeak) { continue; } // If peakData.OriginalPeakLocationIndex is not between peak.LeftEdge and peak.RightEdge, then check // if the scan number for peakData.OriginalPeakLocationIndex is within .MaxDistanceScansNoOverlap scans of // either of the peak edges; if not, then mark the peak as invalid since it does not contain the // scan for the parent ion if (peakData.OriginalPeakLocationIndex < peak.LeftEdge) { if ( Math.Abs(scanNumbers[peakData.OriginalPeakLocationIndex] - scanNumbers[peak.LeftEdge]) > peakFinderOptions.MaxDistanceScansNoOverlap) { peak.IsValid = false; } } else if (peakData.OriginalPeakLocationIndex > peak.RightEdge) { if ( Math.Abs(scanNumbers[peakData.OriginalPeakLocationIndex] - scanNumbers[peak.RightEdge]) > peakFinderOptions.MaxDistanceScansNoOverlap) { peak.IsValid = false; } } } // Find the peak with the largest area that has peakData.PeakIsValid = True peakData.BestPeak = null; var bestPeakArea = double.MinValue; foreach (var peak in peakData.Peaks) { if (peak.IsValid) { if (peak.Area > bestPeakArea) { peakData.BestPeak = peak; bestPeakArea = peak.Area; } } } if (peakData.BestPeak != null) { validPeakFound = true; } else { validPeakFound = false; } return(validPeakFound); }