|
Tugadar Networking Community (@admin) |
Determining Your Orientation
The orientation sensors are a combination of a built-in compass that provides the yaw (heading) and the accelerometers that help determine pitch and roll.
If you’ve done a bit of trigonometry, you’ve got the skills required to calculate the device orientation based on the accelerometer values along the three axes. If you enjoyed trig as much as I did, you’ll be happy to learn that Android does these calculations for you.
The device orientation is reported along all three dimensions, as illustrated in Figure 10-2:
❑Heading The heading (also bearing or yaw) is the direction the device is facing around the Z-axis, where 0/360 degrees is North, 90 degrees is East, 180 degrees is South, and 270 degrees is West.
❑Pitch Pitch represents the angle of the device around the Y-axis. The tilt angle returned shows 0 degrees when the device is flat on its back, –90 degrees when standing upright (top of device pointing at the ceiling), 90 degrees when the device is upside down, and 180/–180 degrees when the device is face down.
❑Roll The roll represents the device’s sideways tilt between –90 and 90 degrees on the X-axis. The tilt is 0 degrees when the device is flat on its back, –90 degrees when the screen faces left, and 90 degrees when the screen faces right.
As implied by the preceding list, the Sensor Manager considers the device at rest (heading, pitch, roll at 0 degrees) when it is flat on its back. To monitor device orientation, register a Sensor Listener with the Sensor Manager, specifying the SENSOR_ORIENTATION flag, as shown in the following code snippet:
SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE); sm.registerListener(myOrientationListener,
SensorManager.SENSOR_ORIENTATION,
SensorManager.SENSOR_DELAY_NORMAL);
The onSensorChanged method in your SensorListener implementation will receive a float array containing the current orientation, along the three axes described above, whenever the device’s orienta-tion changes.
Within this float array, use the Sensor Manager constants DATA_X, DATA_Y, and DATA_Z to find the roll, pitch, and heading (yaw) respectively. Use the corresponding RAW_DATA_* constants to find the unsmoothed / unfiltered values as shown in the following code snippet:
SensorListener myOrientationListener = new SensorListener() {
public void onSensorChanged(int sensor, float[] values) {
if (sensor == SensorManager.SENSOR_ORIENTATION) { float rollAngle = values[SensorManager.DATA_X]; float pitchAngle = values[SensorManager.DATA_Y]; float headingAngle = values[SensorManager.DATA_Z];
float raw_rollAngle = values[SensorManager.RAW_DATA_X]; float raw_pitchAngle= values[SensorManager.RAW_DATA_Y]; float raw_headingAngle = values[SensorManager.RAW_DATA_Z]; // TODO apply the orientation changes to your application.
}Creating a Compass and Artificial Horizon
In Chapter 4, you created a simple CompassView to experiment with owner-drawn controls. In this example, you’ll extend the functionality of the CompassView to display the device pitch and roll, before using it to display the current device orientation.
Open the Compass project you created in Chapter 4. You will be making changes to the CompassView as well as the Compass Activity used to display it. To ensure that the View and controller remain as decoupled as possible, the CompassView won’t be linked to the sensors directly; instead, it will be updated by the Activity.
Start by adding field variables and get/set methods for pitch and roll to the CompassView.
float pitch = 0;
float roll = 0;
public float getPitch() {
return pitch;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public float getRoll() {
return roll;
}
public void setRoll(float roll) {
this.roll = roll;
}
Update the onDraw method to include two circles that will be used to indicate the pitch and roll values.
@Override
protected void onDraw(Canvas canvas) {
[ … Existing onDraw method … ]
2.1. Create a new circle that’s half-filled and rotates in line with the sideways tilt.
RectF rollOval = new RectF((getMeasuredWidth()/3)-getMeasuredWidth()/7, (getMeasuredHeight()/2)-getMeasuredWidth()/7, (getMeasuredWidth()/3)+getMeasuredWidth()/7, (getMeasuredHeight()/2)+getMeasuredWidth()/7 );
markerPaint.setStyle(Paint.Style.STROKE);
canvas.drawOval(rollOval, markerPaint);
markerPaint.setStyle(Paint.Style.FILL);
canvas.save();
canvas.rotate(roll, getMeasuredWidth()/3, getMeasuredHeight()/2); canvas.drawArc(rollOval, 0, 180, false, markerPaint);
canvas.restore();
2.2. Create a new circle that starts half-filled and varies between full and empty based on the current pitch.
RectF pitchOval = new RectF((2*mMeasuredWidth/3)-mMeasuredWidth/7, (getMeasuredHeight()/2)-getMeasuredWidth()/7, (2*getMeasuredWidth()/3)+getMeasuredWidth()/7, (getMeasuredHeight()/2)+getMeasuredWidth()/7 );
markerPaint.setStyle(Paint.Style.STROKE);
canvas.drawOval(pitchOval, markerPaint);
markerPaint.setStyle(Paint.Style.FILL);
canvas.drawArc(pitchOval, 0-pitch/2, 180+(pitch), false, markerPaint);
markerPaint.setStyle(Paint.Style.STROKE);
}
That completes the changes to the CompassView. If you run the application now, it should appear as shown in Figure 10-3. This screen capture was taken while the device was facing due East, with a 45-degree roll and 10-degree backward pitch.
Now you’ll be updating the Compass Activity to use the Sensor Manager to listen for orienta-tion changes and pass them through to the CompassView. Start by adding local field variables to store the current roll, pitch, and heading as well as references to the CompassView and SensorManager.
float pitch = 0;
float roll = 0;
float heading = 0;
CompassView compassView;
SensorManager sensorManager;
Create a new updateOrientation method that takes new heading, pitch, and roll values to update the field variables and apply them to the CompassView.
private void updateOrientation(float _roll,
float _pitch,
float _heading) {
heading = _heading;
pitch = _pitch;
roll = _roll;
if (compassView!= null) {
compassView.setBearing(heading);
compassView.setPitch(pitch);
compassView.setRoll(roll);
compassView.invalidate();
}
}
Update the onCreate method to get references to the CompassView and SensorManager, as well as initializing the heading, pitch, and roll values.
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.main);
compassView = (CompassView)this.findViewById(R.id.compassView); sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); updateOrientation(0, 0, 0);
}
Create a new field variable that instantiates a new SensorListener implementation that calls the updateOrientation method.
private final SensorListener sensorListener = new SensorListener() {
public void onSensorChanged(int sensor, float[] values) { updateOrientation(values[SensorManager.DATA_X],
values[SensorManager.DATA_Y],
values[SensorManager.DATA_Z]);
}
public void onAccuracyChanged(int sensor, int accuracy) {}
};
Then override the onResume method to register the SensorListener to listen for orientation changes when the Activity is visible. Also override onStop to prevent updates when the Activ-ity has been suspended.
@Override
protected void onResume()
{
super.onResume();
sensorManager.registerListener(sensorListener,
SensorManager.SENSOR_ORIENTATION,
SensorManager.SENSOR_DELAY_FASTEST);
}
@Override
protected void onStop()
{
sensorManager.unregisterListener(sensorListener); super.onStop();
}
If you run the application now, you should see the three face dials update dynamically when the orien-tation of the device changes. Unfortunately, it’s not currently possible to emulate the sensor hardware, so this application will only update when running on supported hardware.