1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.sensors; 18 19 import com.android.cts.verifier.R; 20 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity; 21 import com.android.cts.verifier.sensors.renderers.GLArrowSensorTestRenderer; 22 23 import junit.framework.Assert; 24 25 import android.content.Context; 26 import android.hardware.Sensor; 27 import android.hardware.SensorEvent; 28 import android.hardware.SensorEventListener; 29 import android.hardware.SensorManager; 30 import android.hardware.cts.helpers.SensorNotSupportedException; 31 import android.hardware.cts.helpers.SensorTestStateNotSupportedException; 32 import android.os.Bundle; 33 import android.util.Log; 34 35 import java.util.concurrent.TimeUnit; 36 37 /** 38 * This test verifies that mobile device can detect it's orientation in space and after device 39 * movement in space it correctly detects original (reference) position. 40 * All three rotation vectors are tested: 41 * - ROTATION_VECTOR, 42 * - GEOMAGNETIC_ROTATION_VECTOR, 43 * - GAME_ROTATION_VECTOR. 44 */ 45 public class RotationVectorTestActivity 46 extends SensorCtsVerifierTestActivity 47 implements SensorEventListener { 48 public RotationVectorTestActivity() { 49 super(RotationVectorTestActivity.class); 50 } 51 52 private SensorManager mSensorManager; 53 private SensorEventListener mListener; 54 55 /** 56 * Defines the thresholds for each rotation vector in degrees. 57 */ 58 private static final double[] MAX_DEVIATION_DEGREES = { 59 10.0, // ROTATION_VECTOR 60 10.0, // GEOMAGNETIC ROTATION_VECTOR 61 40.0, // GAME_ROTATION_VECTOR 62 }; 63 64 private static final int MAX_SENSORS_AVAILABLE = 3; 65 private static final int ROTATION_VECTOR_INDEX = 0; 66 private static final int GEOMAGNETIC_ROTATION_VECTOR_INDEX = 1; 67 private static final int GAME_ROTATION_VECTOR_INDEX = 2; 68 69 private float[][] mLastEvent = new float[3][5]; 70 private final float[][] mReference = new float[3][16]; 71 private final float[][] mAngularChange = new float[3][3]; 72 private final Sensor[] mSensor = new Sensor[3]; 73 74 /** 75 * The activity setup collects all the required data for test cases. 76 * This approach allows to test all sensors at once. 77 */ 78 @Override 79 protected void activitySetUp() throws InterruptedException { 80 if (mSensor[ROTATION_VECTOR_INDEX] == null 81 && mSensor[GEOMAGNETIC_ROTATION_VECTOR_INDEX] == null 82 && mSensor[GAME_ROTATION_VECTOR_INDEX] == null) { 83 // if none of the sensors is supported, skip the test by throwing an exception 84 throw new SensorTestStateNotSupportedException("Rotation vectors are not supported."); 85 } 86 87 // TODO: take reference value automatically when device is 'still' 88 clearText(); 89 appendText(R.string.snsr_rotation_vector_set_reference); 90 waitForUserToContinue(); 91 92 clearText(); 93 for (int i = 0; i < MAX_SENSORS_AVAILABLE; ++i) { 94 SensorManager.getRotationMatrixFromVector(mReference[i], mLastEvent[i].clone()); 95 } 96 97 // TODO: check the user actually moved the device during the test 98 appendText(R.string.snsr_rotation_vector_reference_set); 99 appendText(R.string.snsr_rotation_vector_move_info); 100 appendText(R.string.snsr_test_play_sound); 101 Thread.sleep(TimeUnit.SECONDS.toMillis(30)); 102 playSound(); 103 104 // TODO: take final value automatically when device becomes 'still' at the end 105 clearText(); 106 appendText(R.string.snsr_rotation_vector_set_final); 107 waitForUserToContinue(); 108 109 clearText(); 110 closeGlSurfaceView(); 111 112 float[] finalVector = new float[16]; 113 for (int i = 0; i < MAX_SENSORS_AVAILABLE; ++i) { 114 SensorManager.getRotationMatrixFromVector(finalVector, mLastEvent[i].clone()); 115 SensorManager.getAngleChange(mAngularChange[i], mReference[i], finalVector); 116 } 117 } 118 119 /** 120 * Verifies that a given 'Rotation Vector' sensor does not drift over time. 121 * The test takes in consideration a reference measurement, and a final measurement. It then 122 * calculates its angular change. 123 */ 124 private String verifyVector(int sensorIndex, int sensorType) 125 throws Throwable { 126 Sensor sensor = mSensor[sensorIndex]; 127 if (sensor == null) { 128 throw new SensorNotSupportedException(sensorType); 129 } 130 131 float[] angularChange = mAngularChange[sensorIndex]; 132 double maxDeviationDegrees = MAX_DEVIATION_DEGREES[sensorIndex]; 133 double maxComponentDegrees = findMaxComponentDegrees(angularChange); 134 String message = getString( 135 R.string.snsr_rotation_vector_verification, 136 Math.toDegrees(angularChange[0]), 137 Math.toDegrees(angularChange[1]), 138 Math.toDegrees(angularChange[2]), 139 maxComponentDegrees, 140 maxDeviationDegrees); 141 142 Assert.assertEquals(message, 0, maxComponentDegrees, maxDeviationDegrees); 143 return message; 144 } 145 146 /** 147 * Test cases. 148 */ 149 public String testRotationVector() throws Throwable { 150 return verifyVector(ROTATION_VECTOR_INDEX, Sensor.TYPE_ROTATION_VECTOR); 151 } 152 153 public String testGeomagneticRotationVector() throws Throwable { 154 return verifyVector( 155 GEOMAGNETIC_ROTATION_VECTOR_INDEX, 156 Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR); 157 } 158 159 public String testGameRotationVector() throws Throwable { 160 return verifyVector(GAME_ROTATION_VECTOR_INDEX, Sensor.TYPE_GAME_ROTATION_VECTOR); 161 } 162 163 @Override 164 protected void onCreate(Bundle savedInstanceState) { 165 // set up sensors first, so activitySetUp has the state in place 166 mSensorManager = (SensorManager) getApplicationContext().getSystemService( 167 Context.SENSOR_SERVICE); 168 mSensor[ROTATION_VECTOR_INDEX] = 169 mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); 170 mSensor[GEOMAGNETIC_ROTATION_VECTOR_INDEX] = 171 mSensorManager.getDefaultSensor(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR); 172 mSensor[GAME_ROTATION_VECTOR_INDEX] = 173 mSensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR); 174 175 super.onCreate(savedInstanceState); 176 177 GLArrowSensorTestRenderer renderer = 178 new GLArrowSensorTestRenderer(this, Sensor.TYPE_ROTATION_VECTOR); 179 mListener = renderer; 180 181 initializeGlSurfaceView(renderer); 182 } 183 184 @Override 185 protected void onPause() { 186 super.onPause(); 187 mSensorManager.unregisterListener(mListener); 188 mSensorManager.unregisterListener(this); 189 } 190 191 @Override 192 protected void onResume() { 193 super.onResume(); 194 195 // listener for rendering 196 boolean renderListenerRegistered = false; 197 for (int i = 0; (!renderListenerRegistered && i < MAX_SENSORS_AVAILABLE); ++i) { 198 Sensor sensor = mSensor[i]; 199 if (sensor != null) { 200 renderListenerRegistered = mSensorManager 201 .registerListener(mListener, sensor, SensorManager.SENSOR_DELAY_GAME); 202 Log.v(LOG_TAG, "Renderer using sensor: " + sensor.getName()); 203 } 204 } 205 206 // listeners for testing 207 for (int i = 0; i < MAX_SENSORS_AVAILABLE; ++i) { 208 mSensorManager.registerListener(this, mSensor[i], SensorManager.SENSOR_DELAY_GAME); 209 } 210 } 211 212 @Override 213 public void onSensorChanged(SensorEvent event) { 214 if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { 215 mLastEvent[ROTATION_VECTOR_INDEX] = event.values.clone(); 216 } 217 if (event.sensor.getType() == Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR) { 218 mLastEvent[GEOMAGNETIC_ROTATION_VECTOR_INDEX] = event.values.clone(); 219 } 220 if (event.sensor.getType() == Sensor.TYPE_GAME_ROTATION_VECTOR) { 221 mLastEvent[GAME_ROTATION_VECTOR_INDEX] = event.values.clone(); 222 } 223 } 224 225 @Override 226 public void onAccuracyChanged(Sensor sensor, int accuracy) { 227 } 228 229 private static double findMaxComponentDegrees(float[] vec) { 230 float maxComponent = 0; 231 for (int i = 0; i < vec.length; i++) { 232 float absComp = Math.abs(vec[i]); 233 if (maxComponent < absComp) { 234 maxComponent = absComp; 235 } 236 } 237 return Math.toDegrees(maxComponent); 238 } 239 } 240