blob: 1b66e6a4fbda88d38234916559d3dab712ab7ea7 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.cts.helpers.sensorverification;
import junit.framework.Assert;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
import android.hardware.cts.helpers.SensorCtsHelper;
import android.util.Log;
import java.util.concurrent.TimeUnit;
import java.util.HashMap;
import java.util.Map;
/**
* A {@link ISensorVerification} which verifies that the standard deviations is within the expected
* range.
*/
public class StandardDeviationVerification extends AbstractSensorVerification {
public static final String PASSED_KEY = "standard_deviation_passed";
// sensorType: threshold
private static final Map<Integer, float[]> DEFAULTS = new HashMap<Integer, float[]>(12);
static {
// Use a method so that the @deprecation warning can be set for that method only
setDefaults();
}
private final float[] mThreshold;
private float[] mMeans = null;
private float[] mM2s = null;
private int mCount = 0;
/**
* Construct a {@link StandardDeviationVerification}
*
* @param threshold the thresholds
*/
public StandardDeviationVerification(float[] threshold) {
mThreshold = threshold;
}
/**
* Get the default {@link StandardDeviationVerification} for a sensor.
*
* @param environment the test environment
* @return the verification or null if the verification does not apply to the sensor.
*/
public static StandardDeviationVerification getDefault(TestSensorEnvironment environment) {
int sensorType = environment.getSensor().getType();
float graceFactorAccelGyro = 2.0f;
float graceFactorMagPressure = 4.0f;
float currOperatingFreq = (float) environment.getFrequencyHz();
float maxBandWidth = (float)SensorCtsHelper.getFrequency(
environment.getSensor().getMinDelay(), TimeUnit.MICROSECONDS);
float minBandWidth = (float) SensorCtsHelper.getFrequency(
environment.getSensor().getMaxDelay(), TimeUnit.MICROSECONDS);
if (Float.isInfinite(currOperatingFreq)) {
currOperatingFreq = maxBandWidth;
}
if (currOperatingFreq > maxBandWidth && !Float.isInfinite(maxBandWidth)) {
currOperatingFreq = maxBandWidth;
}
if (currOperatingFreq < minBandWidth && !Float.isInfinite(minBandWidth)) {
currOperatingFreq = minBandWidth;
}
float mAccelNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
(9.81 * 0.0004));
float mGyroNoise = (float)(graceFactorAccelGyro * Math.sqrt(currOperatingFreq) *
(Math.PI/180.0 * 0.014));
float mMagNoise = (float)((graceFactorMagPressure) * 0.5); // Allow extra grace for mag
float mPressureNoise = (float)(graceFactorMagPressure * 0.02 *
(float)Math.sqrt(currOperatingFreq)); // Allow extra grace for pressure
if (!DEFAULTS.containsKey(sensorType)) {
return null;
}
boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_HIFI_SENSORS);
if (hasHifiSensors) {
DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{mAccelNoise, mAccelNoise, mAccelNoise});
// Max gyro deviation: 0.014°/s/√Hz
DEFAULTS.put(Sensor.TYPE_GYROSCOPE,
new float[]{mGyroNoise, mGyroNoise, mGyroNoise});
// Max magnetometer deviation: 0.1uT/√Hz
DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, new float[]{mMagNoise, mMagNoise, mMagNoise});
// Max pressure deviation: 2Pa/√Hz
DEFAULTS.put(Sensor.TYPE_PRESSURE, new float[]{mPressureNoise,mPressureNoise,mPressureNoise});
}
return new StandardDeviationVerification(DEFAULTS.get(sensorType));
}
/**
* Verify that the standard deviation is in the acceptable range. Add {@value #PASSED_KEY} and
* {@value SensorStats#STANDARD_DEVIATION_KEY} keys to {@link SensorStats}.
*
* @throws AssertionError if the verification failed.
*/
@Override
public void verify(TestSensorEnvironment environment, SensorStats stats) {
verify(stats);
}
/**
* Visible for unit tests only.
*/
void verify(SensorStats stats) {
if (mCount < 2) {
stats.addValue(PASSED_KEY, true);
return;
}
float[] stdDevs = new float[mM2s.length];
for (int i = 0; i < mM2s.length; i++) {
stdDevs[i] = (float) Math.sqrt(mM2s[i] / (mCount - 1));
}
boolean failed = false;
StringBuilder stddevSb = new StringBuilder();
StringBuilder expectedSb = new StringBuilder();
if (stdDevs.length > 1) {
stddevSb.append("(");
expectedSb.append("(");
}
for (int i = 0; i < stdDevs.length; i++) {
if (stdDevs[i] > mThreshold[i]) {
failed = true;
}
stddevSb.append(String.format("%.6f", stdDevs[i]));
if (i != stdDevs.length - 1) stddevSb.append(", ");
expectedSb.append(String.format("<%.6f", mThreshold[i]));
if (i != stdDevs.length - 1) expectedSb.append(", ");
}
if (stdDevs.length > 1) {
stddevSb.append(")");
expectedSb.append(")");
}
stats.addValue(PASSED_KEY, !failed);
stats.addValue(SensorStats.STANDARD_DEVIATION_KEY, stdDevs);
if (failed) {
Assert.fail(String.format("Standard deviation out of range: stddev=%s (expected %s)",
stddevSb.toString(), expectedSb.toString()));
}
}
/**
* {@inheritDoc}
*/
@Override
public StandardDeviationVerification clone() {
return new StandardDeviationVerification(mThreshold);
}
/**
* {@inheritDoc}
* <p>
* Computes the standard deviation using
* <a href="http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm">
* Welford's algorith</a>.
* </p>
*/
@Override
protected void addSensorEventInternal(TestSensorEvent event) {
if (mMeans == null || mM2s == null) {
mMeans = new float[event.values.length];
mM2s = new float[event.values.length];
}
Assert.assertEquals(mMeans.length, event.values.length);
Assert.assertEquals(mM2s.length, event.values.length);
mCount++;
for (int i = 0; i < event.values.length; i++) {
float delta = event.values[i] - mMeans[i];
mMeans[i] += delta / mCount;
mM2s[i] += delta * (event.values[i] - mMeans[i]);
}
}
@SuppressWarnings("deprecation")
private static void setDefaults() {
DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{1.0f, 1.0f, 1.0f});
DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new float[]{0.5f, 0.5f, 0.5f});
// Sensors that we don't want to test at this time but still want to record the values.
DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_ORIENTATION,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_PRESSURE,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_GRAVITY,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
Float.MAX_VALUE, Float.MAX_VALUE});
DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR,
new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
Float.MAX_VALUE});
}
}