blob: d8075f0ff8d8af93b00e6a61eb7e19907ab3e671 [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 com.android.cts.verifier.sensors;
import com.android.cts.verifier.R;
import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
import junit.framework.Assert;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.cts.helpers.SensorNotSupportedException;
import android.hardware.cts.helpers.TestSensorEvent;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
import android.hardware.cts.helpers.sensorverification.ISensorVerification;
import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
import android.os.SystemClock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Activity that verifies sensor event quality. Sensors will be verified with different requested
* data rates. Also sensor events will be verified while other sensors are also active.
*/
public class SensorValueAccuracyActivity
extends SensorCtsVerifierTestActivity
implements SensorEventListener {
public SensorValueAccuracyActivity() {
super(SensorValueAccuracyActivity.class);
}
private static final int EVENTS_TO_COLLECT = 100;
private static final int SENSOR_RATE = SensorManager.SENSOR_DELAY_FASTEST;
private static final float MAGNETIC_FIELD_CALIBRATED_UNCALIBRATED_THRESHOLD_UT = 1f;
private static final float GYROSCOPE_CALIBRATED_UNCALIBRATED_THRESHOLD_RAD_SEC = 0.01f;
private static final float RANGE_ATMOSPHERIC_PRESSURE = 35f;
private static final float AMBIENT_TEMPERATURE_AVERAGE = 22.5f;
private static final float AMBIENT_TEMPERATURE_THRESHOLD = 7.5f;
private static final float PROXIMITY_AVERAGE = 50f;
private static final float PROXIMITY_THRESHOLD = 50f;
private static final double ONE_HUNDRED_EIGHTY_DEGREES = 180.0f;
private static final double GYROSCOPE_INTEGRATION_THRESHOLD_DEGREES = 10.0f;
private SensorManager mSensorManager;
private final List<TestSensorEvent> mSensorEvents = new ArrayList<TestSensorEvent>();
@Override
protected void activitySetUp() {
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
}
// TODO: move tests without interaction to CTS
public String testPressure() throws Throwable {
return verifySensorNorm(
Sensor.TYPE_PRESSURE,
R.string.snsr_no_interaction,
SensorManager.PRESSURE_STANDARD_ATMOSPHERE,
RANGE_ATMOSPHERIC_PRESSURE);
}
public String testAmbientTemperature() throws Throwable {
return verifySensorNorm(
Sensor.TYPE_AMBIENT_TEMPERATURE,
R.string.snsr_no_interaction,
AMBIENT_TEMPERATURE_AVERAGE,
AMBIENT_TEMPERATURE_THRESHOLD);
}
// TODO: add support for proximity and light to test operations and add test cases here
// TODO: remove from here, refactor and merge with gyroscope and magnetic field tests
public String testMagneticFieldCalibratedUncalibrated() throws Throwable {
return verifyCalibratedUncalibrated(
Sensor.TYPE_MAGNETIC_FIELD,
Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
MAGNETIC_FIELD_CALIBRATED_UNCALIBRATED_THRESHOLD_UT);
}
public String testGyroscopeCalibratedUncalibrated() throws Throwable {
appendText(R.string.snsr_keep_device_rotating_clockwise);
return verifyCalibratedUncalibrated(
Sensor.TYPE_GYROSCOPE,
Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
GYROSCOPE_CALIBRATED_UNCALIBRATED_THRESHOLD_RAD_SEC);
}
/**
* Verifies that the measurements of the gyroscope correspond to predefined angular positions.
* The test uses a routine to integrate gyroscope's readings on a predefined rotation to
* ensure that it corresponds to the expected angular position.
*/
// TODO: refactor the integration routine into a SensorTestVerification
// TODO: use the new verification in GyroscopeMeasurement tests
public String testGyroscopeIntegration() throws Throwable {
Sensor gyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
if (gyroscope == null) {
throw new SensorNotSupportedException(Sensor.TYPE_GYROSCOPE);
}
appendText(R.string.snsr_no_interaction);
String rotationInstructions = getString(
R.string.snsr_gyro_rotate_clockwise_integration,
ONE_HUNDRED_EIGHTY_DEGREES);
appendText(rotationInstructions);
waitForUser();
startDataCollection(gyroscope);
appendText(R.string.snsr_test_play_sound);
Thread.sleep(TimeUnit.SECONDS.toMillis(10));
stopDataCollection();
playSound();
// run the verification
double integratedGyroscope = 0;
long lastTimestamp = 0;
for (TestSensorEvent event : mSensorEvents) {
float[] eventValues = event.values.clone();
long eventTimestamp = event.timestamp;
if (lastTimestamp != 0) {
long timeDeltaNs = eventTimestamp - lastTimestamp;
long nanosecondsInOneSecond = TimeUnit.SECONDS.toNanos(1);
integratedGyroscope += eventValues[2] * timeDeltaNs / nanosecondsInOneSecond;
}
lastTimestamp = eventTimestamp;
}
integratedGyroscope = Math.toDegrees(integratedGyroscope);
String integrationMessage = String.format(
"Gyroscope integration expected to be=%fdeg. Found=%fdeg, Tolerance=%fdeg",
ONE_HUNDRED_EIGHTY_DEGREES,
integratedGyroscope,
GYROSCOPE_INTEGRATION_THRESHOLD_DEGREES);
Assert.assertEquals(
integrationMessage,
ONE_HUNDRED_EIGHTY_DEGREES,
integratedGyroscope,
GYROSCOPE_INTEGRATION_THRESHOLD_DEGREES);
return integrationMessage;
}
/**
* Validates the norm of a sensor.
*/
// TODO: fix up EventOrdering, EventGap and timestamp>0 Verifications so they can be added to
// this test
private String verifySensorNorm(
int sensorType,
int instructionsResId,
float expectedNorm,
float threshold) {
appendText(instructionsResId);
waitForUser();
TestSensorOperation verifyNormOperation = new TestSensorOperation(
getApplicationContext(),
sensorType,
SENSOR_RATE,
0 /* reportLatencyInUs */,
EVENTS_TO_COLLECT);
// TODO: add event ordering and timestamp > 0 verifications
ISensorVerification magnitudeVerification =
new MagnitudeVerification(expectedNorm, threshold);
verifyNormOperation.addVerification(magnitudeVerification);
verifyNormOperation.execute();
return null;
}
/**
* Verifies that the relationship between readings from calibrated and their corresponding
* uncalibrated sensors comply to the following equation:
* calibrated = uncalibrated - bias
*
* NOTES:
* Currently given that timestamps might not be synchronized, the verification attempts to
* 'match' events from both sensors by aligning them to (time delta mean) / 2.
*
* @param calibratedSensorType The type of the calibrated sensor to verify.
* @param uncalibratedSensorType The type of the uncalibrated sensor to verify.
* @param threshold The threshold to consider the difference between the equation as
* acceptable.
*/
// TODO: find a better synchronization mechanism
// TODO: revisit the need of a tolerance once a better synchronization mechanism is available
// TODO: refactor this function into a Sensor Test Operation / Verification
private String verifyCalibratedUncalibrated(
int calibratedSensorType,
int uncalibratedSensorType,
float threshold) throws Throwable {
appendText(R.string.snsr_no_interaction);
waitForUser();
Sensor calibratedSensor = mSensorManager.getDefaultSensor(calibratedSensorType);
if (calibratedSensor == null) {
throw new SensorNotSupportedException(calibratedSensorType);
}
Sensor uncalibratedSensor = mSensorManager.getDefaultSensor(uncalibratedSensorType);
if (uncalibratedSensor == null) {
throw new SensorNotSupportedException(uncalibratedSensorType);
}
// collect the required events
final long timeout = TimeUnit.SECONDS.toMillis(10);
startDataCollection(calibratedSensor);
startDataCollection(uncalibratedSensor);
appendText(R.string.snsr_test_play_sound);
Thread.sleep(timeout);
stopDataCollection();
// create a set of readings for verification
float[] calibratedValues = new float[3];
long calibratedTimestamp = 0;
long timestampDeltaSum = 0;
int calibratedEventCount = 0;
int uncalibratedEventCount = 0;
ArrayList<CalibratedUncalibratedReading> readings =
new ArrayList<CalibratedUncalibratedReading>();
for (TestSensorEvent event : mSensorEvents) {
if (event.sensor.getType() == calibratedSensorType) {
calibratedValues = event.values.clone();
calibratedTimestamp = event.receivedTimestamp;
++calibratedEventCount;
} else if (event.sensor.getType() == uncalibratedSensorType) {
float[] uncalibratedValues = event.values.clone();
long timestampDelta = event.receivedTimestamp - calibratedTimestamp;
timestampDeltaSum += timestampDelta;
++uncalibratedEventCount;
CalibratedUncalibratedReading reading = new CalibratedUncalibratedReading(
calibratedValues,
uncalibratedValues,
timestampDelta);
readings.add(reading);
}
// TODO: use delayed asserts to log on else clause
}
// verify readings that are under a timestamp synchronization threshold
String calibratedEventsMessage = String.format(
"Calibrated (%s) events expected. Found=%d.",
calibratedSensor.getName(),
calibratedEventCount);
Assert.assertTrue(calibratedEventsMessage, calibratedEventCount > 0);
String uncalibratedEventsMessage = String.format(
"Uncalibrated (%s) events expected. Found=%d.",
uncalibratedSensor.getName(),
uncalibratedEventCount);
Assert.assertTrue(uncalibratedEventsMessage, uncalibratedEventCount > 0);
long timestampDeltaMean = timestampDeltaSum / readings.size();
long timestampTolerance = timestampDeltaMean / 2;
int verifiedEventsCount = 0;
for (CalibratedUncalibratedReading reading : readings) {
if (reading.timestampDelta < timestampTolerance) {
for (int i = 0; i < 3; ++i) {
float calibrated = reading.calibratedValues[i];
float uncalibrated = reading.uncalibratedValues[i];
float bias = reading.uncalibratedValues[i + 3];
String message = String.format(
"Calibrated (%s) and Uncalibrated (%s) sensor readings are expected to"
+ " satisfy: calibrated = uncalibrated - bias. Axis=%d,"
+ " Calibrated=%s, Uncalibrated=%s, Bias=%s, Threshold=%s",
calibratedSensor.getName(),
uncalibratedSensor.getName(),
i,
calibrated,
uncalibrated,
bias,
threshold);
Assert.assertEquals(message, calibrated, uncalibrated - bias, threshold);
}
++verifiedEventsCount;
}
}
playSound();
String eventsFoundMessage = String.format(
"At least one uncalibrated event expected to be verified. Found=%d.",
verifiedEventsCount);
Assert.assertTrue(eventsFoundMessage, verifiedEventsCount > 0);
return eventsFoundMessage;
}
private void startDataCollection(Sensor sensorUnderTest) throws Throwable {
mSensorEvents.clear();
mSensorManager.registerListener(this, sensorUnderTest, SENSOR_RATE);
}
private void stopDataCollection() {
mSensorManager.unregisterListener(this);
}
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
mSensorEvents.add(new TestSensorEvent(sensorEvent, SystemClock.elapsedRealtimeNanos()));
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private class CalibratedUncalibratedReading {
public final float[] calibratedValues;
public final float[] uncalibratedValues;
public final long timestampDelta;
public CalibratedUncalibratedReading(
float[] calibratedValues,
float[] uncalibratedValues,
long timestampDelta) {
this.calibratedValues = calibratedValues;
this.uncalibratedValues = uncalibratedValues;
this.timestampDelta = timestampDelta;
}
}
}