#region File Description
//-----------------------------------------------------------------------------
// RollingAverage.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
namespace NetworkPrediction
{
///
/// To compensate for network latency, we need to know exactly how late each
/// packet is. Trouble is, there is no guarantee that the clock will be set the
/// same on every machine! The sender can include packet data indicating what
/// time their clock showed when they sent the packet, but this is meaningless
/// unless our local clock is in sync with theirs. To compensate for any clock
/// skew, we maintain a rolling average of the send times from the last 100
/// incoming packets. If this average is, say, 50 milliseconds, but one specific
/// packet arrives with a time difference of 70 milliseconds, we can deduce this
/// particular packet was delivered 20 milliseconds later than usual.
///
class RollingAverage
{
#region Fields
// Array holding the N most recent sample values.
float[] sampleValues;
// Counter indicating how many of the sampleValues have been filled up.
int sampleCount;
// Cached sum of all the valid sampleValues.
float valueSum;
// Write position in the sampleValues array. When this reaches the end,
// it wraps around, so we overwrite the oldest samples with newer data.
int currentPosition;
#endregion
///
/// Constructs a new rolling average object that will track
/// the specified number of sample values.
///
public RollingAverage(int sampleCount)
{
sampleValues = new float[sampleCount];
}
///
/// Adds a new value to the rolling average, automatically
/// replacing the oldest existing entry.
///
public void AddValue(float newValue)
{
// To avoid having to recompute the sum from scratch every time
// we add a new sample value, we just subtract out the value that
// we are replacing, then add in the new value.
valueSum -= sampleValues[currentPosition];
valueSum += newValue;
// Store the new sample value.
sampleValues[currentPosition] = newValue;
// Increment the write position.
currentPosition++;
// Track how many of the sampleValues elements are filled with valid data.
if (currentPosition > sampleCount)
sampleCount = currentPosition;
// If we reached the end of the array, wrap back to the beginning.
if (currentPosition >= sampleValues.Length)
{
currentPosition = 0;
// The trick we used at the top of this method to update the sum
// without having to recompute it from scratch works pretty well to
// keep the average efficient, but over time, floating point rounding
// errors could accumulate enough to cause problems. To prevent that,
// we recalculate from scratch each time the counter wraps.
valueSum = 0;
foreach (float value in sampleValues)
{
valueSum += value;
}
}
}
///
/// Gets the current value of the rolling average.
///
public float AverageValue
{
get
{
if (sampleCount == 0)
return 0;
return valueSum / sampleCount;
}
}
}
}