2
0

RollingAverage.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // RollingAverage.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. namespace NetworkPrediction
  10. {
  11. /// <summary>
  12. /// To compensate for network latency, we need to know exactly how late each
  13. /// packet is. Trouble is, there is no guarantee that the clock will be set the
  14. /// same on every machine! The sender can include packet data indicating what
  15. /// time their clock showed when they sent the packet, but this is meaningless
  16. /// unless our local clock is in sync with theirs. To compensate for any clock
  17. /// skew, we maintain a rolling average of the send times from the last 100
  18. /// incoming packets. If this average is, say, 50 milliseconds, but one specific
  19. /// packet arrives with a time difference of 70 milliseconds, we can deduce this
  20. /// particular packet was delivered 20 milliseconds later than usual.
  21. /// </summary>
  22. class RollingAverage
  23. {
  24. #region Fields
  25. // Array holding the N most recent sample values.
  26. float[] sampleValues;
  27. // Counter indicating how many of the sampleValues have been filled up.
  28. int sampleCount;
  29. // Cached sum of all the valid sampleValues.
  30. float valueSum;
  31. // Write position in the sampleValues array. When this reaches the end,
  32. // it wraps around, so we overwrite the oldest samples with newer data.
  33. int currentPosition;
  34. #endregion
  35. /// <summary>
  36. /// Constructs a new rolling average object that will track
  37. /// the specified number of sample values.
  38. /// </summary>
  39. public RollingAverage(int sampleCount)
  40. {
  41. sampleValues = new float[sampleCount];
  42. }
  43. /// <summary>
  44. /// Adds a new value to the rolling average, automatically
  45. /// replacing the oldest existing entry.
  46. /// </summary>
  47. public void AddValue(float newValue)
  48. {
  49. // To avoid having to recompute the sum from scratch every time
  50. // we add a new sample value, we just subtract out the value that
  51. // we are replacing, then add in the new value.
  52. valueSum -= sampleValues[currentPosition];
  53. valueSum += newValue;
  54. // Store the new sample value.
  55. sampleValues[currentPosition] = newValue;
  56. // Increment the write position.
  57. currentPosition++;
  58. // Track how many of the sampleValues elements are filled with valid data.
  59. if (currentPosition > sampleCount)
  60. sampleCount = currentPosition;
  61. // If we reached the end of the array, wrap back to the beginning.
  62. if (currentPosition >= sampleValues.Length)
  63. {
  64. currentPosition = 0;
  65. // The trick we used at the top of this method to update the sum
  66. // without having to recompute it from scratch works pretty well to
  67. // keep the average efficient, but over time, floating point rounding
  68. // errors could accumulate enough to cause problems. To prevent that,
  69. // we recalculate from scratch each time the counter wraps.
  70. valueSum = 0;
  71. foreach (float value in sampleValues)
  72. {
  73. valueSum += value;
  74. }
  75. }
  76. }
  77. /// <summary>
  78. /// Gets the current value of the rolling average.
  79. /// </summary>
  80. public float AverageValue
  81. {
  82. get
  83. {
  84. if (sampleCount == 0)
  85. return 0;
  86. return valueSum / sampleCount;
  87. }
  88. }
  89. }
  90. }