Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1687)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java

Issue 40393002: Android: fix the computation of device orientation angles in accordance with spec. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixed comment Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | content/public/android/javatests/src/org/chromium/content/browser/DeviceMotionAndOrientationTest.java » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java b/content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
index eb9cf0ec596f90bf1b888bc7ea6ba3be984ec85b..cd3492d8c538d99263ade34571f35ac631790d89 100644
--- a/content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
+++ b/content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
@@ -209,8 +209,7 @@ class DeviceMotionAndOrientation implements SensorEventListener {
if (mMagneticFieldVector == null) {
mMagneticFieldVector = new float[3];
}
- System.arraycopy(values, 0, mMagneticFieldVector, 0,
- mMagneticFieldVector.length);
+ System.arraycopy(values, 0, mMagneticFieldVector, 0, mMagneticFieldVector.length);
if (mDeviceOrientationIsActive) {
getOrientationUsingGetRotationMatrix();
}
@@ -221,45 +220,106 @@ class DeviceMotionAndOrientation implements SensorEventListener {
}
}
+ /**
+ * Returns orientation angles from a rotation matrix, such that the angles are according
+ * to spec {@link http://dev.w3.org/geo/api/spec-source-orientation.html}.
+ * <p>
+ * It is assumed the rotation matrix transforms a 3D column vector from device coordinate system
+ * to the world's coordinate system, as e.g. computed by {@see SensorManager.getRotationMatrix}.
+ * <p>
+ * In particular we compute the decomposition of a given rotation matrix R such that <br>
+ * R = Rz(alpha) * Rx(beta) * Ry(gamma), <br>
+ * where Rz, Rx and Ry are rotation matrices around Z, X and Y axes in the world coordinate
+ * reference frame respectively. The reference frame consists of three orthogonal axes X, Y, Z
+ * where X points East, Y points north and Z points upwards perpendicular to the ground plane.
+ * The computed angles alpha, beta and gamma are in radians and clockwise-positive when viewed
+ * along the positive direction of the corresponding axis. Except for the special case when the
+ * beta angle is +-pi/2 these angles uniquely define the orientation of a mobile device in 3D
+ * space. The alpha-beta-gamma representation resembles the yaw-pitch-roll convention used in
+ * vehicle dynamics, however it does not exactly match it. One of the differences is that the
+ * 'pitch' angle beta is allowed to be within [-pi, pi). A mobile device with pitch angle
+ * greater than pi/2 could correspond to a user lying down and looking upward at the screen.
+ *
+ * <p>
+ * Upon return the array values is filled with the result,
+ * <ul>
+ * <li>values[0]: rotation around the Z axis, alpha in [0, 2*pi)</li>
+ * <li>values[1]: rotation around the X axis, beta in [-pi, pi)</li>
+ * <li>values[2]: rotation around the Y axis, gamma in [-pi/2, pi/2)</li>
+ * </ul>
+ * <p>
+ *
+ * @param R
+ * a 3x3 rotation matrix {@see SensorManager.getRotationMatrix}.
+ *
+ * @param values
+ * an array of 3 doubles to hold the result.
+ *
+ * @return the array values passed as argument.
+ */
+ @VisibleForTesting
+ public static double[] computeDeviceOrientationFromRotationMatrix(float[] R, double[] values) {
+ /*
+ * 3x3 (length=9) case:
+ * / R[ 0] R[ 1] R[ 2] \
+ * | R[ 3] R[ 4] R[ 5] |
+ * \ R[ 6] R[ 7] R[ 8] /
+ *
+ */
+ if (R.length != 9)
+ return values;
+
+ if (R[8] > 0) { // cos(beta) > 0
+ values[0] = Math.atan2(-R[1], R[4]);
+ values[1] = Math.asin(R[7]); // beta (-pi/2, pi/2)
+ values[2] = Math.atan2(-R[6], R[8]); // gamma (-pi/2, pi/2)
+ } else if (R[8] < 0) { // cos(beta) < 0
+ values[0] = Math.atan2(R[1], -R[4]);
+ values[1] = -Math.asin(R[7]);
+ values[1] += (values[1] >= 0) ? -Math.PI : Math.PI; // beta [-pi,-pi/2) U (pi/2,pi)
+ values[2] = Math.atan2(R[6], -R[8]); // gamma (-pi/2, pi/2)
+ } else { // R[8] == 0
+ if (R[6] > 0) { // cos(gamma) == 0, cos(beta) > 0
+ values[0] = Math.atan2(-R[1], R[4]);
+ values[1] = Math.asin(R[7]); // beta [-pi/2, pi/2]
+ values[2] = -Math.PI / 2; // gamma = -pi/2
+ } else if (R[6] < 0) { // cos(gamma) == 0, cos(beta) < 0
+ values[0] = Math.atan2(R[1], -R[4]);
+ values[1] = -Math.asin(R[7]);
+ values[1] += (values[1] >= 0) ? -Math.PI : Math.PI; // beta [-pi,-pi/2) U (pi/2,pi)
+ values[2] = -Math.PI / 2; // gamma = -pi/2
+ } else { // R[6] == 0, cos(beta) == 0
+ // gimbal lock discontinuity
+ values[0] = Math.atan2(R[3], R[0]);
+ values[1] = (R[7] > 0) ? Math.PI / 2 : -Math.PI / 2; // beta = +-pi/2
+ values[2] = 0; // gamma = 0
+ }
+ }
+
+ // alpha is in [-pi, pi], make sure it is in [0, 2*pi).
+ if (values[0] < 0)
+ values[0] += 2 * Math.PI; // alpha [0, 2*pi)
+
+ return values;
+ }
+
private void getOrientationUsingGetRotationMatrix() {
if (mAccelerationIncludingGravityVector == null || mMagneticFieldVector == null) {
return;
}
- // Get the rotation matrix.
- // The rotation matrix that transforms from the body frame to the earth
- // frame.
float[] deviceRotationMatrix = new float[9];
if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null,
mAccelerationIncludingGravityVector, mMagneticFieldVector)) {
return;
}
- // Convert rotation matrix to rotation angles.
- // Assuming that the rotations are appied in the order listed at
- // http://developer.android.com/reference/android/hardware/SensorEvent.html#values
- // the rotations are applied about the same axes and in the same order as required by the
- // API. The only conversions are sign changes as follows. The angles are in radians
-
- float[] rotationAngles = new float[3];
- SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
-
- double alpha = Math.toDegrees(-rotationAngles[0]);
- while (alpha < 0.0) {
- alpha += 360.0; // [0, 360)
- }
-
- double beta = Math.toDegrees(-rotationAngles[1]);
- while (beta < -180.0) {
- beta += 360.0; // [-180, 180)
- }
-
- double gamma = Math.toDegrees(rotationAngles[2]);
- while (gamma < -90.0) {
- gamma += 360.0; // [-90, 90)
- }
+ double[] rotationAngles = new double[3];
+ computeDeviceOrientationFromRotationMatrix(deviceRotationMatrix, rotationAngles);
- gotOrientation(alpha, beta, gamma);
+ gotOrientation(Math.toDegrees(rotationAngles[0]),
+ Math.toDegrees(rotationAngles[1]),
+ Math.toDegrees(rotationAngles[2]));
}
private SensorManagerProxy getSensorManagerProxy() {
« no previous file with comments | « no previous file | content/public/android/javatests/src/org/chromium/content/browser/DeviceMotionAndOrientationTest.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698