blob: 9d36f377aebe0ca8b6e07cfffc16014dd6545b0a [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 android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import android.hardware.Sensor;
import android.hardware.cts.helpers.SensorCtsHelper;
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
import android.util.SparseIntArray;
import com.android.cts.util.StatisticsUtils;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import junit.framework.Assert;
/**
* A {@link ISensorVerification} which verifies that the sensor jitter is in an acceptable range.
*/
public class JitterVerification extends AbstractSensorVerification {
public static final String PASSED_KEY = "jitter_passed";
// sensorType: threshold (% of expected period)
private static final SparseIntArray DEFAULTS = new SparseIntArray(12);
// Max allowed jitter in +/- sense (in percentage).
private static final int GRACE_FACTOR = 2;
private static final int THRESHOLD_PERCENT_FOR_HIFI_SENSORS = 1 * GRACE_FACTOR;
// Margin sample intervals that considered outliers, lower and higher margin is discarded
// before verification
private static final float OUTLIER_MARGIN = 0.025f; //2.5%
static {
// Use a method so that the @deprecation warning can be set for that method only
setDefaults();
}
private final float mOutlierMargin;
private final long mThresholdNs;
private final long mExpectedPeriodNs; // for error message only
private final List<Long> mTimestamps = new LinkedList<Long>();
/**
* Construct a {@link JitterVerification}
*
* @param thresholdAsPercentage the acceptable margin of error as a percentage
*/
public JitterVerification(float outlierMargin, long thresholdNs, long expectedPeriodNs) {
mExpectedPeriodNs = expectedPeriodNs;
mOutlierMargin = outlierMargin;
mThresholdNs = thresholdNs;
}
/**
* Get the default {@link JitterVerification} for a sensor.
*
* @param environment the test environment
* @return the verification or null if the verification does not apply to the sensor.
*/
public static JitterVerification getDefault(TestSensorEnvironment environment) {
int sensorType = environment.getSensor().getType();
int thresholdPercent = DEFAULTS.get(sensorType, -1);
if (thresholdPercent == -1) {
return null;
}
boolean hasHifiSensors = environment.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_HIFI_SENSORS);
if (hasHifiSensors) {
thresholdPercent = THRESHOLD_PERCENT_FOR_HIFI_SENSORS;
}
long expectedPeriodNs = (long) environment.getExpectedSamplingPeriodUs() * 1000;
long jitterThresholdNs = expectedPeriodNs * thresholdPercent * 2 / 100; // *2 is for +/-
return new JitterVerification(OUTLIER_MARGIN, jitterThresholdNs, expectedPeriodNs);
}
/**
* Verify that the 95th percentile of the jitter is in the acceptable range. Add
* {@value #PASSED_KEY} and {@value SensorStats#JITTER_95_PERCENTILE_PERCENT_KEY} keys to
* {@link SensorStats}.
*
* @throws AssertionError if the verification failed.
*/
@Override
public void verify(TestSensorEnvironment environment, SensorStats stats) {
int timestampsCount = mTimestamps.size();
if (timestampsCount < 2 || environment.isSensorSamplingRateOverloaded()) {
// the verification is not reliable in environments under load
stats.addValue(PASSED_KEY, true);
return;
}
List<Long> deltas = getDeltaValues();
float percentiles[] = new float[2];
percentiles[0] = mOutlierMargin;
percentiles[1] = 1 - percentiles[0];
List<Long> percentileValues = SensorCtsHelper.getPercentileValue(deltas, percentiles);
double normalizedRange =
(double)(percentileValues.get(1) - percentileValues.get(0)) / mThresholdNs;
double percentageJitter =
(double)(percentileValues.get(1) - percentileValues.get(0)) /
mExpectedPeriodNs / 2 * 100; //one side variation comparing to sample time
stats.addValue(SensorStats.JITTER_95_PERCENTILE_PERCENT_KEY, percentageJitter);
boolean success = normalizedRange <= 1.0;
stats.addValue(PASSED_KEY, success);
if (!success) {
String message = String.format(
"Jitter out of range: requested period = %dns, " +
"jitter min, max, range (95th percentile) = (%dns, %dns, %dns), " +
"jitter expected range <= %dns",
mExpectedPeriodNs,
percentileValues.get(0), percentileValues.get(1),
percentileValues.get(1) - percentileValues.get(0),
mThresholdNs);
Assert.fail(message);
}
}
/**
* {@inheritDoc}
*/
@Override
public JitterVerification clone() {
return new JitterVerification(mOutlierMargin, mThresholdNs, mExpectedPeriodNs);
}
/**
* {@inheritDoc}
*/
@Override
protected void addSensorEventInternal(TestSensorEvent event) {
mTimestamps.add(event.timestamp);
}
/**
* Get the list of delta values. Exposed for unit testing.
*/
List<Long> getDeltaValues() {
List<Long> deltas = new ArrayList<Long>(mTimestamps.size() - 1);
for (int i = 1; i < mTimestamps.size(); i++) {
deltas.add(mTimestamps.get(i) - mTimestamps.get(i - 1));
}
return deltas;
}
@SuppressWarnings("deprecation")
private static void setDefaults() {
DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_GYROSCOPE, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_ORIENTATION, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_PRESSURE, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_GRAVITY, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR, Integer.MAX_VALUE);
DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, Integer.MAX_VALUE);
}
}