The gyroscope sensor is not a required component of Windows Phone devices. It is therefore important to verify that it is supported before attempting to use it. For this, the static Gyroscope.IsSupported
property is used, as demonstrated in the GyroscopeViewModel
class (see Listing 16.9). The Start
method of the GyroscopeViewModel
oversees the creation of the Gyroscope
instance.
The default value of the Gyroscope
’s TimeBetweenUpdates
property is 20 milliseconds, which on my test device was the minimum allowed value.
As with all sensor types, the CurrentValueChanged
event is used to receive periodic updates from the sensor.
public void Start()
{
if (!Gyroscope.IsSupported)
{
MessageService.ShowMessage(
"Gyroscope is not supported on this device.");
return;
}
gyroscope = new Gyroscope();
gyroscope.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
gyroscope.CurrentValueChanged += HandleGyroscopeCurrentValueChanged;
gyroscope.Start();
}
When the CurrentValueChanged
event is raised, the event handler receives a SensorReadingEventArgs<GyroscopeReading>
object. GyroscopeReading
contains two properties: a Timestamp
property of type DateTimeOffset
, which indicates when the sensor reading was taken, and a RotationRate
property of type Vector3
, which retrieves the rotational velocity around each axis of the device (X, Y, and Z) in radians per second.
The handler for the compass’s CurrentValueChanged
event in the GyroscopeViewModel
converts the RotationRate
property of the event arguments, which is of type Vector3
, to a custom ThreeDimensionalVector
instance and assigns it to the viewmodel’s RotationRate
property (see Listing 16.10).
The viewmodel tracks the angular rotation of the device by accumulating the value of each reading into a ThreeDimensionalVector
called cumulativeRotationRadians
. The amount of rotation since the last CurrentValueChanged
event is calculated by multiplying the rotation rate by the time since the last event was raised. Put simply: (radians/second) * secondsSinceLastReading = radiansSinceLastReading.
void HandleGyroscopeCurrentValueChanged(
object sender, SensorReadingEventArgs<GyroscopeReading> e)
{
GyroscopeReading reading = e.SensorReading;
Vector3 rate = reading.RotationRate;
RotationRate = new ThreeDimensionalVector(rate.X, rate.Y, rate.Z);
if (lastUpdateTime.Equals(DateTimeOffset.MinValue))
{
lastUpdateTime = e.SensorReading.Timestamp;
cumulativeRotationRadians = rotationRate;
return;
}
TimeSpan timeSinceLastUpdate = e.SensorReading.Timestamp - lastUpdateTime;
cumulativeRotationRadians
+= rotationRate * (timeSinceLastUpdate.TotalSeconds);
CumulativeRotation = new RotationAngles(
MathHelper.ToDegrees((float)cumulativeRotationRadians.X),
MathHelper.ToDegrees((float)cumulativeRotationRadians.Y),
MathHelper.ToDegrees((float)cumulativeRotationRadians.Z));
lastUpdateTime = e.SensorReading.Timestamp;
}
The viewmodel’s CumulativeRotation
property is of type RotationAngle
, a simple custom class with three read-only properties: X, Y, and Z, all of which are of type double
.
As with most of the examples in this chapter, the page calls the viewmodel’s Start
method within its OnNavigatedTo
method and calls the Stop
method within its OnNavigatedFrom
method.
The Stop
method of the viewmodel disposes the Gyroscope
instance, as shown:
public void Stop()
{
if (gyroscope == null)
{
return;
}
gyroscope.Stop();
gyroscope.CurrentValueChanged -= HandleGyroscopeCurrentValueChanged;
gyroscope.Dispose();
gyroscope = null;
}
The GyroscopeView
page contains TextBlock
elements that display both the raw reading values in radians/second and the accumulated angular rotation values in degrees.
The cumulative rotation values serve to rotate colored rectangles. As the device is rotated, a RotateTransform
is applied to each rectangle, as shown:
<Canvas Width="400" Height="400" Margin="0,50,0,0">
<Ellipse Width="400" Height="400" StrokeThickness="2"
Stroke="{StaticResource PhoneSubtleBrush}" />
<Rectangle Canvas.Left="195" Canvas.Top="0"
Width="10" Height="200" Fill="Honeydew">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding CumulativeRotation.X}"
CenterX="5" CenterY="200" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Left="195" Canvas.Top="0"
Width="10" Height="200" Fill="Red">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding CumulativeRotation.Y}"
CenterX="5" CenterY="200" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Canvas.Left="195" Canvas.Top="0"
Width="10" Height="200" Fill="Orange">
<Rectangle.RenderTransform>
<RotateTransform Angle="{Binding CumulativeRotation.Z}"
CenterX="5" CenterY="200" />
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
Figure 16.7 shows the GyroscopeView page with the rotation of the device across three dimensions indicated by the three colored lines.
You may notice, while running the sample, that the cumulative rotation drifts over time. This drift is indicative of the main disadvantage of the gyroscope; the gyroscope measures changes in angular rotation, and not the absolute angular rotation of the device. This is where the Accelerometer
has an advantage, because it can provide the same rotational information without drift. In the case of the accelerometer, however, the magnitude of the signal is biased by gravity. This is not the case with the gyroscope.
The next section explores the Motion
class, which promises to overcome the disadvantages of the individual sensors by unifying them into a single software virtual sensor.
18.221.76.234