| # 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) |