xref: /aosp_15_r20/cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RotationVectorTestActivity.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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 {
RotationVectorTestActivity()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
activitySetUp()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      */
verifyVector(int sensorIndex, int sensorType)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      */
testRotationVector()149     public String testRotationVector() throws Throwable {
150         return verifyVector(ROTATION_VECTOR_INDEX, Sensor.TYPE_ROTATION_VECTOR);
151     }
152 
testGeomagneticRotationVector()153     public String testGeomagneticRotationVector() throws Throwable {
154         return verifyVector(
155                 GEOMAGNETIC_ROTATION_VECTOR_INDEX,
156                 Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR);
157     }
158 
testGameRotationVector()159     public String testGameRotationVector() throws Throwable {
160         return verifyVector(GAME_ROTATION_VECTOR_INDEX, Sensor.TYPE_GAME_ROTATION_VECTOR);
161     }
162 
163     @Override
onCreate(Bundle savedInstanceState)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
onPause()185     protected void onPause() {
186         super.onPause();
187         mSensorManager.unregisterListener(mListener);
188         mSensorManager.unregisterListener(this);
189     }
190 
191     @Override
onResume()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
onSensorChanged(SensorEvent event)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
onAccuracyChanged(Sensor sensor, int accuracy)226     public void onAccuracyChanged(Sensor sensor, int accuracy) {
227     }
228 
findMaxComponentDegrees(float[] vec)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