1 2 package com.android.cts.verifier.sensors; 3 4 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity; 5 6 import junit.framework.Assert; 7 8 import android.annotation.TargetApi; 9 import android.content.Context; 10 import android.hardware.Sensor; 11 import android.hardware.SensorEvent; 12 import android.hardware.SensorEventListener; 13 import android.hardware.SensorManager; 14 import android.hardware.cts.helpers.TestSensorEvent; 15 import android.os.Build; 16 17 import java.util.ArrayList; 18 import java.util.List; 19 20 /** 21 * Test cross-sensor timestamp alignment by detecting major change in each 22 * sensor and comparing timestamps of that change. 23 */ 24 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 25 public class SensorSynchronizationTestActivity 26 extends SensorCtsVerifierTestActivity 27 implements SensorEventListener { SensorSynchronizationTestActivity()28 public SensorSynchronizationTestActivity() { 29 super(SensorSynchronizationTestActivity.class); 30 } 31 32 private final double NANOS_PER_MILLI = 1e6; 33 private final int DATA_COLLECTION_TIME_IN_MS = 5000; 34 private final int RATE_100HZ_IN_US = 10000; 35 private final int MAX_CROSS_SENSOR_DELAY_MILLIS = 125; 36 private final double THRESH_DEGREES = 10.0; 37 private final double THRESH_RPS = 1.0; 38 39 private SensorManager mSensorManager = null; 40 private List<TestSensorEvent> mSensorEvents = new ArrayList<TestSensorEvent>(); 41 startDataCollection()42 private void startDataCollection() { 43 mSensorEvents.clear(); 44 45 mSensorManager = (SensorManager) getApplicationContext() 46 .getSystemService(Context.SENSOR_SERVICE); 47 mSensorManager.registerListener(this, 48 mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 49 RATE_100HZ_IN_US); 50 mSensorManager.registerListener(this, 51 mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), 52 RATE_100HZ_IN_US); 53 mSensorManager.registerListener(this, 54 mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), 55 RATE_100HZ_IN_US); 56 } 57 stopDataCollection()58 private void stopDataCollection() { 59 mSensorManager.unregisterListener(this); 60 } 61 analyzeData()62 private void analyzeData() { 63 int numberOfCollectedEvents = mSensorEvents.size(); 64 Assert.assertTrue("No sensor events collected", numberOfCollectedEvents > 2); 65 66 boolean accMovementDetected = false; 67 boolean magMovementDetected = false; 68 boolean gyrMovementDetected = false; 69 long accMovementTimestamp = 0, magMovementTimestamp = 0, gyrMovementTimestamp = 0; 70 float[] accInitValues = null, magInitValues = null, gyrInitValues = null; 71 72 for (int i = 0; i < numberOfCollectedEvents; i++) { 73 TestSensorEvent event = mSensorEvents.get(i); 74 75 if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { 76 if (accInitValues == null) { 77 accInitValues = event.values.clone(); 78 } else if (angleBetweenVecsDegrees(accInitValues, event.values) > THRESH_DEGREES 79 && !accMovementDetected) { 80 accMovementDetected = true; 81 accMovementTimestamp = event.timestamp; 82 } 83 } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { 84 if (magInitValues == null) { 85 magInitValues = event.values.clone(); 86 } else if (angleBetweenVecsDegrees(magInitValues, event.values) > THRESH_DEGREES 87 && !magMovementDetected) { 88 magMovementDetected = true; 89 magMovementTimestamp = event.timestamp; 90 } 91 } 92 if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { 93 if (gyrInitValues == null) { 94 gyrInitValues = event.values.clone(); 95 } else if (normVec(event.values) > THRESH_RPS && !gyrMovementDetected) { 96 gyrMovementDetected = true; 97 gyrMovementTimestamp = event.timestamp; 98 } 99 } 100 101 if (accMovementDetected && magMovementDetected && gyrMovementDetected) { 102 double maxTimestamp = Math.max(accMovementTimestamp, 103 magMovementTimestamp); 104 maxTimestamp = Math.max(gyrMovementTimestamp, maxTimestamp); 105 106 double minTimestamp = Math.min(accMovementTimestamp, 107 magMovementTimestamp); 108 minTimestamp = Math.min(gyrMovementTimestamp, minTimestamp); 109 110 double timeDifferenceBetweenMovementMillis = 111 (maxTimestamp - minTimestamp) / NANOS_PER_MILLI; 112 113 appendText(String.format("\nSensor | Relative Timestamp (msec)\n" 114 + "Accelerometer | %4.1f\nMagnetometer | %4.1f\nGyroscope | %4.1f\n", 115 (accMovementTimestamp - minTimestamp) / NANOS_PER_MILLI, 116 (magMovementTimestamp - minTimestamp) / NANOS_PER_MILLI, 117 (gyrMovementTimestamp - minTimestamp) / NANOS_PER_MILLI)); 118 Assert.assertEquals(String.format( 119 "Cross sensor timestamp alignment off by more than %d msec.", 120 MAX_CROSS_SENSOR_DELAY_MILLIS), 121 0, timeDifferenceBetweenMovementMillis, MAX_CROSS_SENSOR_DELAY_MILLIS); 122 appendText(String.format( 123 "Maximum cross sensor time between movement: %4.1f msec is within " 124 + "required tolerance of %4.1f msec", 125 timeDifferenceBetweenMovementMillis, 126 (float) MAX_CROSS_SENSOR_DELAY_MILLIS)); 127 break; 128 } 129 } 130 131 Assert.assertTrue("Accelerometer did not detect any movement", accMovementDetected); 132 Assert.assertTrue("Magnetometer did not detect any movement", magMovementDetected); 133 Assert.assertTrue("Gyroscope did not detect any movement", gyrMovementDetected); 134 } 135 testCrossSensorSynchronization()136 public String testCrossSensorSynchronization() throws Throwable { 137 appendText("This test provides a rough indication of cross-sensor timestamp synchronization."); 138 appendText("Hold device still in hand and click 'Next'"); 139 waitForUserToBegin(); 140 clearText(); 141 appendText("Quickly twist device upside-down and back"); 142 143 startDataCollection(); 144 Thread.sleep(DATA_COLLECTION_TIME_IN_MS); 145 146 stopDataCollection(); 147 analyzeData(); 148 return null; 149 } 150 angleBetweenVecsDegrees(float[] vec1, float[] vec2)151 protected double angleBetweenVecsDegrees(float[] vec1, float[] vec2) { 152 return Math.toDegrees(Math.acos((vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2]) 153 / normVec(vec1) / normVec(vec2))); 154 } 155 normVec(float[] vec1)156 protected double normVec(float[] vec1) { 157 return Math.sqrt(vec1[0] * vec1[0] + vec1[1] * vec1[1] + vec1[2] * vec1[2]); 158 } 159 160 @Override onSensorChanged(SensorEvent sensorEvent)161 public void onSensorChanged(SensorEvent sensorEvent) { 162 mSensorEvents.add(new TestSensorEvent(sensorEvent)); 163 } 164 165 @Override onAccuracyChanged(Sensor sensor, int accuracy)166 public void onAccuracyChanged(Sensor sensor, int accuracy) { 167 } 168 } 169