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() { |