blob: e555c538318f2f31814aa45b6055ccf47a8fdef5 [file] [log] [blame]
# Copyright 2014 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A factory test for accelerometers calibration.
Description
-----------
This is a calibration test for tri-axis (x, y, and z) accelerometers.
From one accelerometer, we can obtain digital output proportional to the linear
acceleration in each axis. If we put it on a flat table we can get
(x, y, z) = (0, 0, 9.8) at an ideal case. For upside down we'll have
(x, y, z) = (0, 0, -9.8).
Since accelerometer is very sensitive, the digital output will be different
for each query. For example, (0.325, -0.278, 9.55).
In addition, temperature or the assembly quality may impact the accuracy
of the accelerometer during manufacturing (ex, position is tilt). To mitigate
this kind of errors, we'll sample several records of raw data and compute
its average value under an ideal environment. Then store the offset as
a calibrated value for future calculation.
In a horizontal calibration, we'll put accelerometers on a flat position then
sample 100 records of raw data. In this position, two axes are under 0g and
one axis is under 1g. Then we'll update the calibration bias using the
difference between the ideal value (0 and +/-9.8 in the example above) and
the average value of 100 samples.
Test Procedure
--------------
1. Put the device (base/lid) on a horizontal plane then press space.
2. Wait for completion.
Dependency
----------
- Device API (``cros.factory.device.accelerometer``).
Examples
--------
To run horizontal calibration on base accelerometer:
.. test_list::
generic_ec_component_accel_examples:BaseAccelerometersCalibration
To run horizontal calibration on lid accelerometer:
.. test_list::
generic_ec_component_accel_examples:LidAccelerometersCalibration
"""
import enum
from cros.factory.device import accelerometer
from cros.factory.device import device_utils
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.test import test_ui
from cros.factory.utils.arg_utils import Arg
class AccelerometersCalibration(test_case.TestCase):
related_components = (test_case.TestCategory.ACCELEROMETER, )
ARGS = [
# TODO(bowgotsai): add six-sided calibration.
Arg(
'calibration_method', enum.Enum('CalibrationMethod', ['horizontal']),
'Currently there is only one calibration method available: '
'horizontal calibration.', default='horizontal'),
Arg(
'orientation', dict,
'Keys: the name of the accelerometer signal. For example, '
'"in_accel_x_base" or "in_accel_x_lid". The possible keys are '
'"in_accel_(x|y|z)_(base|lid)".'
'Values: an int or a list of [orientation-1, orientation-2, ...].'
'Each orientation is 0, 1 or -1 representing the ideal '
'value for gravity under such orientation. For example, 1 or '
'[0, 0, 1, 0, 0, -1].'
'An example of orientation for horizontal calibration: {'
' "in_accel_x_base": 0,'
' "in_accel_y_base": 0,'
' "in_accel_z_base": 1,'
' "in_accel_x_lid": 0,'
' "in_accel_y_lid": 0,'
' "in_accel_z_lid": -1}.'
'Another example of orientation_gravity for six-sided calibration: {'
' "in_accel_x_base": [0, 0, 1, -1, 0, 0],'
' "in_accel_y_base": [0, 0, 0, 0, 1, -1],'
' "in_accel_z_base": [1, -1, 0, 0, 0, 0],'
' "in_accel_x_lid": [0, 0, 1, -1, 0, 0],'
' "in_accel_y_lid": [0, 0, 0, 0, 1, -1],'
' "in_accel_z_lid": [1, -1, 0, 0, 0, 0]}.'),
Arg('sample_rate_hz', int, 'The sample rate in Hz to get raw data from '
'accelerometers.', default=20),
Arg(
'capture_count', int, 'How many times to capture the raw data to '
'calculate the average value.', default=100),
Arg('setup_time_secs', int, 'How many seconds to wait before starting '
'to calibration.', default=2),
Arg(
'spec_offset', list,
'Two numbers, ex: [0.5, 0.5] indicating the tolerance in m/s^2 for '
'the digital output of sensors under 0 and 1G.'),
Arg('autostart', bool, 'Starts the test automatically without prompting.',
default=False),
Arg('location', enum.Enum('location', ['base', 'lid']),
'The location for the accelerometer', default='base'),
Arg(
'variance_threshold', float, 'The variance of capture data can not be'
'larger than the threshold.', default=5.0),
]
def setUp(self):
# yapf: disable
self.ui.ToggleTemplateClass('font-large', True) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.dut = device_utils.CreateDUTInterface()
# Checks arguments.
# yapf: disable
self.assertEqual(2, len(self.args.spec_offset)) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.accelerometer_controller = (
# yapf: disable
self.dut.accelerometer.GetController(self.args.location)) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
def runTest(self):
# yapf: disable
if self.args.calibration_method == 'horizontal': # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.HorizontalCalibration()
else:
raise NotImplementedError
def HorizontalCalibration(self):
"""Prompt for space, waits a period of time and then starts calibration."""
# yapf: disable
if not self.args.autostart: # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetState( # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
_('Please put device on a horizontal plane then press space to '
'start calibration.'))
# yapf: disable
self.ui.WaitKeysOnce(test_ui.SPACE_KEY) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
else:
# yapf: disable
self.ui.SetState(_('Please put device on a horizontal plane.')) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.Sleep(1)
# Waits for a few seconds to let machine become stable.
# yapf: disable
for i in range(self.args.setup_time_secs): # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetState( # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
_('Calibration will be started within {time} seconds.'
'Please do not move device.',
# yapf: disable
time=self.args.setup_time_secs - i)) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.Sleep(1)
# Cleanup offsets before calibration
self.accelerometer_controller.CleanUpCalibrationValues()
# Starts calibration.
# yapf: disable
self.ui.SetState( # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
_('Calibration is in progress, please do not move device.'))
try:
# yapf: disable
raw_data = self.accelerometer_controller.GetData(self.args.capture_count, # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
average=False)
except accelerometer.AccelerometerException:
self.FailTask('Read raw data failed.')
# Check the variance of raw_data
if self.accelerometer_controller.IsVarianceOutOfRange(
# yapf: disable
raw_data, self.args.variance_threshold): # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.FailTask('Variance out of range, the accelerometers may be damaged.')
# Calculate average value of raw_data
raw_data = {k: (sum(v) / len(v))
for k, v in raw_data.items()}
# Checks accelerometer is normal or not before calibration.
if not self.accelerometer_controller.IsWithinOffsetRange(
# yapf: disable
raw_data, self.args.orientation, self.args.spec_offset): # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.FailTask('Raw data out of range, the accelerometers may be damaged.')
calib_bias = self.accelerometer_controller.CalculateCalibrationBias(
# yapf: disable
raw_data, self.args.orientation) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.accelerometer_controller.UpdateCalibrationBias(calib_bias)