The Accelerometer
’s CurrentValueChanged
event is raised, by default, 50 times a second and reflects the raw hardware sensor readings. Updating UI elements based on the raw values from the accelerometer can make elements appear jittery, much like the effects of Brownian motion under a microscope. You can allow your app to appear more stable by smoothing the readings received by the accelerometer through ignoring small changes in acceleration.
Some apps may benefit from some degree of smoothing, yet may need to react quickly to sudden fluctuations in reading values, such as games, which usually need input to be as direct as possible. This section looks at applying various data smoothing techniques to reduce jitter while also minimizing latency.
Much of the code and theory in this section is based on an article on the Windows Team Blog by Dave Edson at http://bit.ly/cXJ2EC. It is recommended that you take a look at the article to better understand the theory behind the smoothing algorithms employed in the sample code. The advantage of the sample code provided here is that you also have a number of other features included that you can take and use immediately in your own apps.
The example code for this section is located in the Devices/Sensors directory of the WPUnleashed project in the downloadable sample code.
In the example, a custom class called EnhancedAccelerometer
is used in place of the built-in Accelerometer
. The EnhancedAccelerometer
class uses an Accelerometer
, allowing the application of smoothing algorithms based on previous readings. EnhancedAccelerometer
has a number of features that the Accelerometer
does not, such as calibration support, shake detection, and data smoothing.
Like the Accelerometer
, the EnhancedAccelerometer
is IDisposable
. When the EnhancedAccelerometer
is disposed, it in turn disposes the built-in Accelerometer
. So, too, EnhancedAccelerometer
is started by calling its Start
method, which instantiates an Accelerometer
, subscribes to its CurrentValueChanged
event, and starts the Accelerometer
as shown:
public void Start()
{
if (accelerometer == null)
{
lock (accelerometerLock)
{
if (accelerometer == null)
{
accelerometer = new Accelerometer();
accelerometer.CurrentValueChanged
+= HandleSensorValueChanged;
accelerometer.Start();
}
}
}
}
EnhancedAccelerometer
has a Reading
property of type EnhancedAccelerometerReading
, which not only supplies the raw value supplied by the Accelerometer
, but also the following three smoothed reading values:
AverageAcceleration
LowPassFilteredAcceleration
Each value is the result of the application of a particular smoothing algorithm. The first, AverageAcceleration
, has the highest latency (lag) of the three, while the other two attempt to combat the latency by responding to large reading variations immediately so that you get smoothing as well as responsiveness. These properties are examined in greater detail in the following sections.
The AverageAcceleration
property provides an average value of the last 25 readings. As stated, this approach has the highest latency; a large change in acceleration is less evident as the previous readings average it out. This approach may suit an app that requires input to be very steady—a spirit level app for example—but would not be suitable for a game that needs to respond quickly to user input.
The LowPassFilteredAcceleration
property is calculated using the current reading and the previously calculated output. This approach has less latency than averaging, yet still does not respond to large changes in acceleration immediately.
The EnhancedAccelerometer.LowPassFilterCoefficient
property allows you to adjust the level of smoothing applied to the output value. Each reading dimension value is calculated like so:
double newOutputValue = priorOutputValue
+ LowPassFilterCoefficient * (newInputValue - priorOutputValue);
By decreasing the LowPassFilterCoefficient
, the more smoothing is applied.
The OptimallyFilteredAcceleration
property uses a low pass filter in conjunction with a threshold value, which causes the output value to be set to the raw reading immediately if the reading exceeds the threshold value. This approach eliminates the latency of the pure low pass approach for sharp changes in acceleration.
If the difference between a new reading and the previous output is greater than the noise threshold, the raw value is used, as shown in the following excerpt:
double ApplyLowPassFilterWithNoiseThreshold(
double newInput, double previousOutput)
{
double newOutputValue = newInput;
if (Math.Abs(newInput - previousOutput) <= NoiseThreshold)
{
/* A simple low-pass filter. */
newOutputValue = previousOutput
+ LowPassFilterCoefficient * (newInput - previousOutput);
}
return newOutputValue;
}
The noise threshold of the filter can be adjusted using the EnhancedAccelerometer.NoiseThreshold
property. Its default value is 0.05, and by increasing the value you increase the level of acceleration needed to produce an immediate response.
3.22.60.167