blob: 2d0fc5a23435093a12f99b04f18bb31c95bacd0f [file] [log] [blame]
# Copyright 2015 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Perform calibration on spatial sensors
Spatial sensors are sensors with X, Y, Z values such as accelerometer or
gyroscope.
The step for calibration is as follows:
1) Put the device on a flat table, facing up.
2) Issue a command to calibrate them:
- echo 1 > /sys/bus/iio/devices/iio:deviceX/calibrate
- X being the ids of the accel and gyro.
3) Retrieve the calibration offsets
- cat /sys/bus/iio/devices/iio:deviceX/in_(accel|anglvel)_(x|y|z)_calibbias
4) Save them in VPD.
"""
import threading
import time
import unittest
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test import factory
from cros.factory.test import i18n
from cros.factory.test.i18n import _
from cros.factory.test.i18n import arg_utils as i18n_arg_utils
from cros.factory.test.i18n import test_ui as i18n_test_ui
from cros.factory.test import test_ui
from cros.factory.test import ui_templates
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import sync_utils
DEFAULT_NAME = _('Accelerometer')
DEFAULT_RAW_ENTRY_TEMPLATE = 'in_accel_%s_raw'
DEFAULT_CALIBBIAS_ENTRY_TEMPLATE = 'in_accel_%s_calibbias'
DEFAULT_VPD_ENTRY_TEMPLATE = 'in_accel_%s_base_calibbias'
CSS = """
body { font-size: 2em; }
.error { color: red; }
"""
class InvalidPositionError(Exception):
pass
class SpatialSensorCalibration(unittest.TestCase):
ARGS = [
Arg('timeout_secs', int, 'Timeout in seconds when waiting for device.',
default=60),
i18n_arg_utils.I18nArg('sensor_name', 'name of the sensor to calibrate.',
default=DEFAULT_NAME, accept_tuple=True),
Arg('device_name', str, 'The "name" atribute of the sensor'),
Arg('device_location', str, 'The "location" atribute of the sensor'),
Arg('raw_entry_template', str,
'Template for the sysfs raw value entry.',
default=DEFAULT_RAW_ENTRY_TEMPLATE),
Arg('calibbias_entry_template', str,
'Template for the sysfs calibbias value entry.',
default=DEFAULT_CALIBBIAS_ENTRY_TEMPLATE),
Arg('vpd_entry_template', str,
'Template for the sysfs calibbias value entry.',
default=DEFAULT_VPD_ENTRY_TEMPLATE),
Arg('stabilize_time', int, 'Time to wait until calibbias stabilize.',
default=1),
Arg('prompt', bool, 'Prompt user to put the device in correct facing',
default=True),
Arg('placement_range', list, 'A list of tuple asserting the range of '
"X, Y, Z. Each element is a tuple (min, max) or None if it's a "
"don't care.", default=[None, None, None])
]
def setUp(self):
i18n_arg_utils.ParseArg(self, 'sensor_name')
self._dut = device_utils.CreateDUTInterface()
self._device_path = None
for path in self._dut.Glob('/sys/bus/iio/devices/iio:device*'):
try:
name = self._dut.ReadFile(self._dut.path.join(path, 'name')).strip()
location = self._dut.ReadFile(
self._dut.path.join(path, 'location')).strip()
except Exception:
continue
if (name == self.args.device_name and
location == self.args.device_location):
self._device_path = path
if self._device_path is None:
raise factory.FactoryTestFailure('%s at %s not found' %
(self.args.sensor_name['en-US'],
self.args.device_location))
self._ui = test_ui.UI(css=CSS)
self._template = ui_templates.OneSection(self._ui)
self._start_event = threading.Event()
self._ui.BindKey(test_ui.ENTER_KEY, lambda _: self._start_event.set())
def runTest(self):
previous_fail = False
while True:
try:
if self.args.prompt:
self.Prompt(previous_fail)
self._ui.Run(blocking=False)
self._start_event.wait()
self.RunCalibration()
except InvalidPositionError:
previous_fail = True
self._start_event.clear()
else:
break
def RunCalibration(self):
self.WaitForDevice()
self.VerifyDevicePosition()
self._template.SetState(
i18n_test_ui.MakeI18nLabel(
'Calibrating {sensor_name}...', sensor_name=self.args.sensor_name))
self.EnableAutoCalibration(self._device_path)
self.RetrieveCalibbiasAndWriteVPD()
self._ui.Pass()
def Prompt(self, prev_fail=False):
prompt = i18n.StringJoin(
'<div class="error">',
_('Device not in position') if prev_fail else '',
'</div><br>',
_('Please put the device in face-up position'
' (press Enter to continue)'))
self._template.SetState(i18n_test_ui.MakeI18nLabel(prompt))
def WaitForDevice(self):
self._template.SetState(i18n_test_ui.MakeI18nLabel('Waiting for device...'))
sync_utils.WaitFor(self._dut.IsReady, self.args.timeout_secs)
if not self._dut.IsReady():
self.fail('failed to find deivce')
def VerifyDevicePosition(self):
for i, axis in enumerate(['x', 'y', 'z']):
_range = self.args.placement_range[i]
if _range is None:
continue
key = self.args.raw_entry_template % axis
value = int(self._dut.ReadFile(self._dut.path.join(self._device_path,
key)))
if value <= _range[0] or value >= _range[1]:
factory.console.error(
'Device not in correct position: %s-axis value: %d. '
'Valid range (%d, %d)', axis, value, _range[0], _range[1])
raise InvalidPositionError
def EnableAutoCalibration(self, path):
RETRIES = 5
for unused_i in range(RETRIES):
try:
self._dut.WriteFile(self._dut.path.join(path, 'calibrate'), '1')
except Exception:
factory.console.info('calibrate activation failed, retrying')
time.sleep(1)
else:
break
else:
raise RuntimeError('calibrate activation failed')
time.sleep(self.args.stabilize_time)
def RetrieveCalibbiasAndWriteVPD(self):
cmd = ['vpd']
for axis in ['x', 'y', 'z']:
self._template.SetState(
i18n_test_ui.MakeI18nLabel('Writing calibration data...'))
calibbias_key = self.args.calibbias_entry_template % axis
vpd_key = self.args.vpd_entry_template % axis
value = self._dut.ReadFile(self._dut.path.join(self._device_path, calibbias_key))
cmd.extend(['-s', '%s=%s' % (vpd_key, value.strip())])
self._dut.CheckCall(cmd)