blob: b9860dba27f50aafbef5157260c0f3d06983f7e3 [file] [log] [blame]
# Copyright 2017 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Compass test which requires operator place the DUT heading north and south.
"""
import enum
import math
from cros.factory.device import device_utils
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import sync_utils
from cros.factory.utils import type_utils
_TEST_ITEMS = [(_('north'), (0, 1)), (_('south'), (0, -1))]
_FLASH_STATUS_TIME = 1
class CompassTest(test_case.TestCase):
related_components = tuple()
ARGS = [
Arg('tolerance', int, 'The tolerance in degree.', default=5),
Arg('location', enum.Enum('Location', ['base', 'lid']),
'Where the compass is located.', default='base')
]
def setUp(self):
self.dut = device_utils.CreateDUTInterface()
self.controller = self.dut.magnetometer.GetController(
# yapf: disable
location=self.args.location) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
def runTest(self):
for direction_label, direction in _TEST_ITEMS:
# yapf: disable
self.ui.SetView('main') # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetInstruction(_( # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
'Put the DUT towards {direction}', direction=direction_label))
sync_utils.PollForCondition(
poll_method=type_utils.BindFunction(self._CheckDirection, direction),
timeout_secs=1000,
poll_interval_secs=0.1)
# yapf: disable
self.ui.SetView('success') # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
self.Sleep(_FLASH_STATUS_TIME)
def _CalculateDirection(self, x, y):
"""Calculate the absolute direction of the compass in degree.
Args:
x: X-axis component of the direction. X-axis points towards east.
y: Y-axis component of the direction. Y-axis points towards north.
Returns:
Directed angle relative to north (0, 1), in degree, clockwise.
For example:
North = 0
East = 90
South = 180 = -180
West = -90
"""
rad = math.atan2(x, y)
return rad / math.pi * 180
def _CalculateAngle(self, x1, y1, x2, y2):
"""Calculate the angle between two vectors (x1, y1) and (x2, y2)."""
rad = math.acos(
(x1 * x2 + y1 * y2) / math.hypot(x1, y1) / math.hypot(x2, y2))
return rad / math.pi * 180
def _CheckDirection(self, expected_direction):
values = self.controller.GetData(capture_count=1)
x, y = values['in_magn_x'], values['in_magn_y']
if x == 0 and y == 0:
# atan2(0, 0) returns 0, we need to avoid this case.
self.FailTask('Sensor outputs (0, 0), possibly not working.')
degree = self._CalculateDirection(x, y)
self._UpdateUI(degree=degree, **values)
return (
# yapf: disable
self._CalculateAngle(x, y, *expected_direction) < self.args.tolerance) # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
def _UpdateUI(self, degree, in_magn_x, in_magn_y, in_magn_z):
# yapf: disable
self.ui.SetHTML(f'{degree:.2f}', id='degree') # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetHTML(in_magn_x, id='in-magn-x') # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetHTML(in_magn_y, id='in-magn-y') # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.SetHTML(in_magn_z, id='in-magn-z') # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
# yapf: disable
self.ui.RunJS(f'document.getElementById("compass").style.transform = ' # type: ignore #TODO(b/338318729) Fixit! # pylint: disable=line-too-long
# yapf: enable
f'"rotate({int(degree)}deg)";')