blob: d1013e0439516292cca4f9cbb2d41edc56434ed6 [file] [log] [blame]
/*
* Copyright (C) 2013 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Set of static helper methods to verify sensor CTS tests.
*/
public class SensorVerificationHelper {
private static final int MESSAGE_LENGTH = 3;
/**
* Class which holds results from the verification.
*/
public static class VerificationResult {
private boolean mFailed = false;
private String mMessage = null;
private Map<String, Object> mValueMap = new HashMap<String, Object>();
public void fail(String messageFormat, Object ... args) {
mFailed = true;
mMessage = String.format(messageFormat, args);
}
public boolean isFailed() {
return mFailed;
}
public String getFailureMessage() {
return mMessage;
}
public void putValue(String key, Object value) {
mValueMap.put(key, value);
}
public Object getValue(String key) {
return mValueMap.get(key);
}
}
/**
* Private constructor for static class.
*/
private SensorVerificationHelper() {}
/**
* Verify that the events are in the correct order.
*
* @param events The array of {@link TestSensorEvent}
* @return a {@link VerificationResult} containing the verification info including the keys
* "count" which is the number of events out of order and "positions" which contains an
* array of indexes that were out of order.
* @throws IllegalStateException if number of events less than 1.
*/
public static VerificationResult verifyEventOrdering(TestSensorEvent[] events) {
VerificationResult result = new VerificationResult();
List<Integer> indices = new ArrayList<Integer>();
long maxTimestamp = events[0].timestamp;
for (int i = 1; i < events.length; i++) {
long currentTimestamp = events[i].timestamp;
if (currentTimestamp < maxTimestamp) {
indices.add(i);
} else if (currentTimestamp > maxTimestamp) {
maxTimestamp = currentTimestamp;
}
}
result.putValue("count", indices.size());
result.putValue("positions", indices);
if (indices.size() > 0) {
StringBuilder sb = new StringBuilder();
sb.append(indices.size()).append(" events out of order: ");
for (int i = 0; i < Math.min(indices.size(), MESSAGE_LENGTH); i++) {
int index = indices.get(i);
sb.append(String.format("position=%d, previous=%d, timestamp=%d; ", index,
events[index - 1].timestamp, events[index].timestamp));
}
if (indices.size() > MESSAGE_LENGTH) {
sb.append(indices.size() - MESSAGE_LENGTH).append(" more");
} else {
// Delete the "; "
sb.delete(sb.length() - 2, sb.length());
}
result.fail(sb.toString());
}
return result;
}
/**
* Verify that the sensor frequency matches the expected frequency.
*
* @param events The array of {@link TestSensorEvent}
* @param expected The expected frequency in Hz
* @param threshold The acceptable margin of error in Hz
* @return a {@link VerificationResult} containing the verification info including the key
* "frequency" which is the computed frequency of the events in Hz.
* @throws IllegalStateException if number of events less than 1.
*/
public static VerificationResult verifyFrequency(TestSensorEvent[] events, double expected,
double threshold) {
VerificationResult result = new VerificationResult();
List<Long> timestampDelayValues = SensorCtsHelper.getTimestampDelayValues(events);
double frequency = SensorCtsHelper.getFrequency(
SensorCtsHelper.getMean(timestampDelayValues), TimeUnit.NANOSECONDS);
result.putValue("frequency", frequency);
if (Math.abs(frequency - expected) > threshold) {
result.fail("Frequency out of range: frequency=%.2fHz, expected=%.2f+/-%.2fHz",
frequency, expected, threshold);
}
return result;
}
/**
* Verify that the jitter is in an acceptable range
*
* @param events The array of {@link TestSensorEvent}
* @param threshold The acceptable margin of error in nanoseconds
* @return a {@link VerificationResult} containing the verification info including the keys
* "jitter" which is the list of computed jitter values and "jitter95Percentile" which is
* 95th percentile of the jitter values.
* @throws IllegalStateException if number of events less than 2.
*/
public static VerificationResult verifyJitter(TestSensorEvent[] events, double threshold) {
VerificationResult result = new VerificationResult();
List<Double> jitterValues = SensorCtsHelper.getJitterValues(events);
double jitter95Percentile = SensorCtsHelper.get95PercentileValue(jitterValues);
result.putValue("jitter", jitterValues);
result.putValue("jitter95Percentile", jitter95Percentile);
if (jitter95Percentile > threshold) {
result.fail("Jitter out of range: jitter at 95th percentile=%.0fns, expected=<%.0fns",
jitter95Percentile, threshold);
}
return result;
}
/**
* Verify that the means matches the expected measurement.
*
* @param events The array of {@link TestSensorEvent}
* @param expected The array of expected values
* @param threshold The array of thresholds
* @return a {@link VerificationResult} containing the verification info including the key
* "mean" which is the computed means for each value of the sensor.
* @throws IllegalStateException if number of events less than 1.
*/
public static VerificationResult verifyMean(TestSensorEvent[] events, double[] expected,
double[] threshold) {
VerificationResult result = new VerificationResult();
double[] means = SensorCtsHelper.getMeans(events);
result.putValue("means", means);
boolean failed = false;
StringBuilder meanSb = new StringBuilder();
StringBuilder expectedSb = new StringBuilder();
if (means.length > 1) {
meanSb.append("(");
expectedSb.append("(");
}
for (int i = 0; i < means.length && !failed; i++) {
if (Math.abs(means[i] - expected[i]) > threshold[i]) {
failed = true;
}
meanSb.append(String.format("%.2f", means[i]));
if (i != means.length - 1) meanSb.append(", ");
expectedSb.append(String.format("%.2f+/-%.2f", expected[i], threshold[i]));
if (i != means.length - 1) expectedSb.append(", ");
}
if (means.length > 1) {
meanSb.append(")");
expectedSb.append(")");
}
if (failed) {
result.fail("Mean out of range: mean=%s, expected=%s",
meanSb.toString(), expectedSb.toString());
}
return result;
}
/**
* Verify that the mean of the magnitude of the sensors vector is within the expected range.
*
* @param events The array of {@link TestSensorEvent}
* @param expected The expected value
* @param threshold The threshold
* @return a {@link VerificationResult} containing the verification info including the key
* "magnitude" which is the mean of the computed magnitude of the sensor values.
* @throws IllegalStateException if number of events less than 1.
*/
public static VerificationResult verifyMagnitude(TestSensorEvent[] events, double expected,
double threshold) {
VerificationResult result = new VerificationResult();
Collection<Double> magnitudes = new ArrayList<Double>(events.length);
for (TestSensorEvent event : events) {
double norm = 0;
for (int i = 0; i < event.values.length; i++) {
norm += event.values[i] * event.values[i];
}
magnitudes.add(Math.sqrt(norm));
}
double mean = SensorCtsHelper.getMean(magnitudes);
result.putValue("magnitude", mean);
if (Math.abs(mean - expected) > threshold) {
result.fail(String.format("Magnitude mean out of range: mean=%s, expected=%s+/-%s",
mean, expected, threshold));
}
return result;
}
/**
* Verify that the sign of each of the sensor values is correct.
* <p>
* If the value of the measurement is in [-threshold, threshold], the sign is considered 0. If
* it is less than -threshold, it is considered -1. If it is greater than threshold, it is
* considered 1.
* </p>
*
* @param events
* @param threshold The threshold that needs to be crossed to consider a measurement nonzero
* @return a {@link VerificationResult} containing the verification info including the key
* "mean" which is the computed means for each value of the sensor.
* @throws IllegalStateException if number of events less than 1.
*/
public static VerificationResult verifySignum(TestSensorEvent[] events, int[] expected,
double[] threshold) {
VerificationResult result = new VerificationResult();
for (int i = 0; i < expected.length; i++) {
if (!(expected[i] == -1 || expected[i] == 0 || expected[i] == 1)) {
throw new IllegalArgumentException("Expected value must be -1, 0, or 1");
}
}
double[] means = SensorCtsHelper.getMeans(events);
result.putValue("means", means);
boolean failed = false;
StringBuilder meanSb = new StringBuilder();
StringBuilder expectedSb = new StringBuilder();
if (means.length > 1) {
meanSb.append("(");
expectedSb.append("(");
}
for (int i = 0; i < means.length; i++) {
meanSb.append(String.format("%.2f", means[i]));
if (i != means.length - 1) meanSb.append(", ");
if (expected[i] == 0) {
if (Math.abs(means[i]) > threshold[i]) {
failed = true;
}
expectedSb.append(String.format("[%.2f, %.2f]", -threshold[i], threshold[i]));
} else {
if (expected[i] > 0) {
if (means[i] <= threshold[i]) {
failed = true;
}
expectedSb.append(String.format("(%.2f, inf)", threshold[i]));
} else {
if (means[i] >= -1 * threshold[i]) {
failed = true;
}
expectedSb.append(String.format("(-inf, %.2f)", -1 * threshold[i]));
}
}
if (i != means.length - 1) expectedSb.append(", ");
}
if (means.length > 1) {
meanSb.append(")");
expectedSb.append(")");
}
if (failed) {
result.fail("Signum out of range: mean=%s, expected=%s",
meanSb.toString(), expectedSb.toString());
}
return result;
}
/**
* Verify that the standard deviations is within the expected range.
*
* @param events The array of {@link TestSensorEvent}
* @param threshold The array of thresholds
* @return a {@link VerificationResult} containing the verification info including the key
* "stddevs" which is the computed standard deviations for each value of the sensor.
* @throws IllegalStateException if number of events less than 1.
*/
public static VerificationResult verifyStandardDeviation(TestSensorEvent[] events,
double[] threshold) {
VerificationResult result = new VerificationResult();
double[] standardDeviations = SensorCtsHelper.getStandardDeviations(events);
result.putValue("stddevs", standardDeviations);
boolean failed = false;
StringBuilder meanSb = new StringBuilder();
StringBuilder expectedSb = new StringBuilder();
if (standardDeviations.length > 1) {
meanSb.append("(");
expectedSb.append("(");
}
for (int i = 0; i < standardDeviations.length && !failed; i++) {
if (standardDeviations[i] > threshold[i]) {
failed = true;
}
meanSb.append(String.format("%.2f", standardDeviations[i]));
if (i != standardDeviations.length - 1) meanSb.append(", ");
expectedSb.append(String.format("0+/-%.2f", threshold[i]));
if (i != standardDeviations.length - 1) expectedSb.append(", ");
}
if (standardDeviations.length > 1) {
meanSb.append(")");
expectedSb.append(")");
}
if (failed) {
result.fail("Standard deviation out of range: mean=%s, expected=%s",
meanSb.toString(), expectedSb.toString());
}
return result;
}
}