| # Copyright 2017 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. |
| |
| """Compass test which requires operator place the DUT heading north and south. |
| """ |
| |
| |
| import math |
| |
| import factory_common # pylint: disable=unused-import |
| from cros.factory.device import device_utils |
| from cros.factory.test.i18n import _ |
| from cros.factory.test import test_ui |
| 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_ui.TestCaseWithUI): |
| ARGS = [ |
| Arg('tolerance', int, 'The tolerance in degree.', |
| default=5), |
| Arg('location', type_utils.Enum(['base', 'lid']), |
| 'Where the compass is located.', |
| default='base') |
| ] |
| |
| def setUp(self): |
| self.dut = device_utils.CreateDUTInterface() |
| self.controller = self.dut.magnetometer.GetController( |
| location=self.args.location) |
| |
| def runTest(self): |
| for direction_label, direction in _TEST_ITEMS: |
| self.ui.SetView('main') |
| self.ui.SetInstruction(_( |
| '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) |
| |
| self.ui.SetView('success') |
| 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 ( |
| self._CalculateAngle(x, y, *expected_direction) < self.args.tolerance) |
| |
| def _UpdateUI(self, degree, in_magn_x, in_magn_y, in_magn_z): |
| self.ui.SetHTML('%.2f' % degree, id='degree') |
| self.ui.SetHTML(in_magn_x, id='in-magn-x') |
| self.ui.SetHTML(in_magn_y, id='in-magn-y') |
| self.ui.SetHTML(in_magn_z, id='in-magn-z') |
| self.ui.RunJS('document.getElementById("compass").style.transform = ' |
| '"rotate(%ddeg)";' % degree) |