RollingAverage.cs 3.8 KB

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