using System; using TBF.BenchControl.GenericDevices; namespace TBF.BenchControl.Sequences { public class Statistics { public readonly int DefaultSkippedSamplesCount; /// 0 = do not skip any samples public readonly int FilterSize; /// 0 = no filter public readonly bool MedianFilter; /// true - median filter instead of average public readonly IPlotter plotter; bool recordingInProgress; double[] fifo; double[] sorted; double first; double last; double min; double max; double sum; int skippedSamplesCount; int totalCount; /// Number of samples passed to Update() int count; /// Number of processed and filtered samples int fifoCount; /// Number of samples inserted into FIFO int graphId; public bool RecordingIProgress { get { return recordingInProgress; } } public double First { get { return first; } } public double Last { get { return last; } } public double Min { get { return (count > 0) ? min : 0; } } /// 0 when there were no samples public double Max { get { return (count > 0) ? max : 0; } } /// 0 when there were no samples public double Average { get { return (count > 0) ? (sum / (double)count) : 0; } } public int Count { get { return count; } } public double Sum { get { return sum; } } public Statistics(int defaultSkippedSamplesCount, int filterSize, bool medianFilter, IPlotter plotter) { this.DefaultSkippedSamplesCount = defaultSkippedSamplesCount; this.FilterSize = filterSize; this.MedianFilter = medianFilter; this.plotter = plotter; if (filterSize > 0) { fifo = new double[FilterSize]; sorted = new double[FilterSize]; } recordingInProgress = false; } public Statistics(int defaultSkippedSamplesCount, int filterSize, bool medianFilter) : this(defaultSkippedSamplesCount, filterSize, medianFilter, new DummyPlotter()) { } public Statistics(IPlotter plotter) : this(0, 0, false, plotter) { } public Statistics() : this(0, 0, false, new DummyPlotter()) { } /// /// Resets statistics and start collecting /// public void Start(int batchNr, string testName, int repetition) { Start(batchNr, testName, repetition, DefaultSkippedSamplesCount); } /// /// Resets statistics and start collecting, 4th arument specifies skipped samples count /// public void Start(int batchNr, string testName, int repetition, int skippedSamplesCount) { this.skippedSamplesCount = skippedSamplesCount; sum = 0; min = double.MaxValue; max = double.MinValue; first = 0; last = 0; totalCount = 0; count = 0; fifoCount = 0; recordingInProgress = true; graphId = plotter.StartGraph(batchNr, testName, repetition); } /// /// Resets statistics /// public void Stop() { if (recordingInProgress) { recordingInProgress = false; plotter.StopGraph(graphId, (float)min, (float)max); } } /// /// Updates statistics /// /// New boxed value public void Update(TBF.Boxes.DoubleBox box) { Update(box.Val); } /// /// Updates statistics /// /// New boxed value public void Update(TBF.Boxes.FloatBox box) { Update(box.Val); } /// /// All samples are passed to this function /// /// New value public void Update(double value) { if (recordingInProgress) { if (totalCount >= skippedSamplesCount) { Process(value); } totalCount++; } } /// /// Only samples after first 'SkippedSamplesCount' samples are passed to this function. /// Any filtering is done inside this function. /// /// New value void Process(double value) { double filteredValue; if (ApplyFilter(value, out filteredValue)) { sum += filteredValue; if (count == 0) first = filteredValue; last = filteredValue; if (filteredValue < min) min = filteredValue; if (filteredValue > max) max = filteredValue; plotter.UpdateGraph(graphId, (float)(count + skippedSamplesCount + FilterSize / 2), (float)filteredValue); count++; } } /// /// Inserts values to FIFO and calculates filetered value when FIFO is full. /// Returns true when FIFO is full and calculated values are valid. /// /// Input value /// Output filtered value /// true when 'fileterdValue' is valid bool ApplyFilter(double value, out double filteredValue) { if (FilterSize == 0) { /// No filter => Pass the input value to the output filteredValue = value; return true; } /// Shift values in FIFO buffer, calculate the sum double sum1 = 0; for (int i = FilterSize - 1; i > 0; i--) { fifo[i] = fifo[i - 1]; sum1 += fifo[i]; } /// Insert the current value into FIFO, add it to the sum fifo[0] = value; sum1 += value; fifoCount++; if (fifoCount < FilterSize) { /// FIFO not filled yet => Pass the input value to the output filteredValue = value; return false; } else { /// FIFO already filled => Apply filter if (MedianFilter) { for (int i = 0; i < FilterSize; i++) sorted[i] = fifo[i]; Array.Sort(sorted); #if false filteredValue = sorted[FilterSize / 2]; #else double sum2 = 0; for (int j = 1; j < FilterSize - 1; j++) sum2 += sorted[j]; filteredValue = sum2 / (FilterSize - 2); #endif } else { filteredValue = sum1 / FilterSize; } return true; } } } class DummyPlotter : IPlotter { public DummyPlotter() { } public int StartGraph(int batchNr, string testName, int repetition) { return 1; } public void UpdateGraph(int graphId, float x, float y) { } public void StopGraph(int graphId, float ymin, float ymax) { } } }