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 android.hardware.cts.helpers.sensorverification;
18 
19 import junit.framework.Assert;
20 
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.hardware.Sensor;
24 import android.hardware.cts.helpers.SensorStats;
25 import android.hardware.cts.helpers.TestSensorEnvironment;
26 import android.hardware.cts.helpers.TestSensorEvent;
27 import android.hardware.cts.helpers.SensorCtsHelper;
28 import android.util.Log;
29 
30 import java.util.concurrent.TimeUnit;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /**
35  * A {@link ISensorVerification} which verifies that the standard deviations is within the expected
36  * range.
37  */
38 public class StandardDeviationVerification extends AbstractSensorVerification {
39     public static final String PASSED_KEY = "standard_deviation_passed";
40 
41     // sensorType: threshold
42     private static final Map<Integer, float[]> DEFAULTS = new HashMap<Integer, float[]>(12);
43     static {
44         // Use a method so that the @deprecation warning can be set for that method only
setDefaults()45         setDefaults();
46     }
47 
48     private final float[] mThreshold;
49 
50     private float[] mMeans = null;
51     private float[] mM2s = null;
52     private int mCount = 0;
53 
54     /**
55      * Construct a {@link StandardDeviationVerification}
56      *
57      * @param threshold the thresholds
58      */
StandardDeviationVerification(float[] threshold)59     public StandardDeviationVerification(float[] threshold) {
60         mThreshold = threshold;
61     }
62 
63     /**
64      * Get the default {@link StandardDeviationVerification} for a sensor.
65      *
66      * @param environment the test environment
67      * @return the verification or null if the verification does not apply to the sensor.
68      */
getDefault(TestSensorEnvironment environment)69     public static StandardDeviationVerification getDefault(TestSensorEnvironment environment) {
70         int sensorType = environment.getSensor().getType();
71         float graceFactorAccelGyro = 2.0f;
72         float graceFactorMagPressure = 4.0f;
73         float currOperatingFreq = (float) environment.getFrequencyHz();
74         float maxBandWidth = (float)SensorCtsHelper.getFrequency(
75                 environment.getSensor().getMinDelay(), TimeUnit.MICROSECONDS);
76         float minBandWidth = (float) SensorCtsHelper.getFrequency(
77                 environment.getSensor().getMaxDelay(), TimeUnit.MICROSECONDS);
78 
79         if (Float.isInfinite(currOperatingFreq)) {
80             currOperatingFreq = maxBandWidth;
81         }
82 
83         if (currOperatingFreq > maxBandWidth && !Float.isInfinite(maxBandWidth)) {
84             currOperatingFreq = maxBandWidth;
85         }
86 
87         if (currOperatingFreq < minBandWidth && !Float.isInfinite(minBandWidth)) {
88             currOperatingFreq = minBandWidth;
89         }
90 
91         float mAccelNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
92                 (9.81 * 0.0004));
93         float mGyroNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
94                 (Math.PI/180.0 * 0.014));
95         float mMagNoise = (float)((graceFactorMagPressure) * 0.5); // Allow extra grace for mag
96         float mPressureNoise = (float)(graceFactorMagPressure * 0.02 *
97                 (float)Math.sqrt(currOperatingFreq)); // Allow extra grace for pressure
98 
99         if (!DEFAULTS.containsKey(sensorType)) {
100             return null;
101         }
102         boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
103                 PackageManager.FEATURE_HIFI_SENSORS);
104 
105         if (hasHifiSensors) {
106 
107             DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{mAccelNoise, mAccelNoise, mAccelNoise});
108             // Max gyro deviation: 0.014°/s/√Hz
109             DEFAULTS.put(Sensor.TYPE_GYROSCOPE,
110                     new float[]{mGyroNoise, mGyroNoise, mGyroNoise});
111             // Max magnetometer deviation: 0.1uT/√Hz
112             DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, new float[]{mMagNoise, mMagNoise, mMagNoise});
113             // Max pressure deviation: 2Pa/√Hz
114             DEFAULTS.put(Sensor.TYPE_PRESSURE, new float[]{mPressureNoise});
115         }
116         return new StandardDeviationVerification(DEFAULTS.get(sensorType));
117     }
118 
119     /**
120      * Verify that the standard deviation is in the acceptable range. Add {@value #PASSED_KEY} and
121      * {@value SensorStats#STANDARD_DEVIATION_KEY} keys to {@link SensorStats}.
122      *
123      * @throws AssertionError if the verification failed.
124      */
125     @Override
verify(TestSensorEnvironment environment, SensorStats stats)126     public void verify(TestSensorEnvironment environment, SensorStats stats) {
127         verify(stats);
128     }
129 
130     /**
131      * Visible for unit tests only.
132      */
verify(SensorStats stats)133     void verify(SensorStats stats) {
134         if (mCount < 2) {
135             stats.addValue(PASSED_KEY, true);
136             return;
137         }
138 
139         float[] stdDevs = new float[mM2s.length];
140         for (int i = 0; i < mM2s.length; i++) {
141             stdDevs[i] = (float) Math.sqrt(mM2s[i] / (mCount - 1));
142         }
143 
144         boolean failed = false;
145         StringBuilder stddevSb = new StringBuilder();
146         StringBuilder expectedSb = new StringBuilder();
147 
148         if (stdDevs.length > 1) {
149             stddevSb.append("(");
150             expectedSb.append("(");
151         }
152         for (int i = 0; i < stdDevs.length; i++) {
153             if (stdDevs[i] > mThreshold[i]) {
154                 failed = true;
155             }
156             stddevSb.append(String.format("%.6f", stdDevs[i]));
157             if (i != stdDevs.length - 1) stddevSb.append(", ");
158             expectedSb.append(String.format("<%.6f", mThreshold[i]));
159             if (i != stdDevs.length - 1) expectedSb.append(", ");
160         }
161         if (stdDevs.length > 1) {
162             stddevSb.append(")");
163             expectedSb.append(")");
164         }
165 
166         stats.addValue(PASSED_KEY, !failed);
167         stats.addValue(SensorStats.STANDARD_DEVIATION_KEY, stdDevs);
168 
169         if (failed) {
170             Assert.fail(String.format("Standard deviation out of range: stddev=%s (expected %s)",
171                     stddevSb.toString(), expectedSb.toString()));
172         }
173     }
174 
175     /**
176      * {@inheritDoc}
177      */
178     @Override
clone()179     public StandardDeviationVerification clone() {
180         return new StandardDeviationVerification(mThreshold);
181     }
182 
183     /**
184      * {@inheritDoc}
185      * <p>
186      * Computes the standard deviation using
187      * <a href="http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm">
188      * Welford's algorith</a>.
189      * </p>
190      */
191     @Override
addSensorEventInternal(TestSensorEvent event)192     protected void addSensorEventInternal(TestSensorEvent event) {
193         if (mMeans == null || mM2s == null) {
194             mMeans = new float[event.values.length];
195             mM2s = new float[event.values.length];
196         }
197 
198         Assert.assertEquals(mMeans.length, event.values.length);
199         Assert.assertEquals(mM2s.length, event.values.length);
200 
201         mCount++;
202 
203         for (int i = 0; i < event.values.length; i++) {
204             float delta = event.values[i] - mMeans[i];
205             mMeans[i] += delta / mCount;
206             mM2s[i] += delta * (event.values[i] - mMeans[i]);
207         }
208     }
209 
210     @SuppressWarnings("deprecation")
setDefaults()211     private static void setDefaults() {
212         DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{1.0f, 1.0f, 1.0f});
213         DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new float[]{0.5f, 0.5f, 0.5f});
214         // Sensors that we don't want to test at this time but still want to record the values.
215         DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD,
216                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
217         DEFAULTS.put(Sensor.TYPE_ORIENTATION,
218                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
219         DEFAULTS.put(Sensor.TYPE_PRESSURE,
220                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
221         DEFAULTS.put(Sensor.TYPE_GRAVITY,
222                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
223         DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION,
224                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
225         DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR,
226                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
227                 Float.MAX_VALUE});
228         DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
229                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
230                 Float.MAX_VALUE, Float.MAX_VALUE});
231         DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
232                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
233                 Float.MAX_VALUE});
234         DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
235                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
236                 Float.MAX_VALUE, Float.MAX_VALUE});
237         DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR,
238                 new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
239                 Float.MAX_VALUE});
240     }
241 }
242