The other day on the MSDN forums, someone asked about how to detect a shaking motion on Windows Phone 7. I've been playing with the accelerometer lately so I took great joy in answering this along with providing a working implementation. The question was asking about shaking motion in a left-right direction. I made a class that detects left-right and up-down motion (totally ignoring the Z-axis all together for now). Though extending it to consider the Z-axis wouldn't be hard.
The code for detecting the motion has been abstracted in a class called ShakeDetector. The algorithm used has a few variables/constants defined that can be modified to tune the behaviour of the class. The classes constructor accepts an [optional] parameter of how many times the phone should be shaken before the motion is considered acceptable. <codeminimumaccelerationmagnitude> can be raised or lowered to control how hard the device needs to be shaken to be considered acceptable. And MinimumShakeTime takes a time span that defines the maximum length of time over which a shake sequence must occur to be considered acceptable. Once the user moves the phone in a way that meets the requirements for the type of shake we wanted to detect a ShakeDetected event is raised.
I've reduced the direction in which the device is moving to one of 8 directions (North, East, South, West, and the directions in between those). I could have kept the direction as an angle and just ensured that there was atleast a minimum difference between the angles but I thought using the directions on a map would make it easier for someone else to understand.
void</span /> _accelerometer_ReadingChanged(object</span /> sender, AccelerometerReadingEventArgs e)
{
//</span />Does the current acceleration vector meet the minimum magnitude that we
</span /> //</span />care about?
</span /> if</span /> ((e.X*e.X + e.Y*e.Y) > MinimumAccelerationMagnitudeSquared)
{
//</span />I prefer to work in radians. For the sake of those reading this code
</span /> //</span />I will work in degrees. In the following direction will contain the direction
</span /> //</span /> in which the device was accelerating in degrees.
</span /> double</span /> degrees = 180</span />.0*Math.Atan2(e.Y, e.X)/Math.PI;
Direction direction = DegreesToDirection(degrees);
//</span />If the shake detected is in the same direction as the last one then ignore it
</span /> if</span /> ((direction & _shakeRecordList[_shakeRecordIndex].ShakeDirection)
!= Direction.None)
return</span />;
//</span />This is a shake we care about. save in in our list
</span /> ShakeRecord record = new</span /> ShakeRecord();
record.EventTime = DateTime.Now;
record.ShakeDirection = direction;
_shakeRecordIndex = (_shakeRecordIndex + 1</span />)%_minimumShakes;
_shakeRecordList[_shakeRecordIndex] = record;
CheckForShakes();
}
}
void</span /> CheckForShakes()
{
int</span /> startIndex = (_shakeRecordIndex - 1</span />);
if</span /> (startIndex < 0</span />) startIndex = _minimumShakes - 1</span />;
int</span /> endIndex = _shakeRecordIndex;
if</span /> ((_shakeRecordList[endIndex].EventTime.Subtract
(_shakeRecordList[startIndex].EventTime)) <= MinimumShakeTime)
{
OnShakeEvent();
}
}
The example code can be found in my SkyDrive account here. If you want to see the program in action, there is a video on YouTube.
CodeProject