motion_lid: Rewrite lid angle calculation based on chromium code

Use code from ash/wm/tablet_mode/tablet_mode_controller.cc, in
particular TabletModeController::HandleHingeRotation()
to calculate lid angle.
Add unit tests based on
ash/wm/tablet_mode/tablet_mode_controller_unittest.cc and the data file
accelerometer_test_data_literals.cc.

BUG=b:120346412
BRANCH=none
TEST=Check unit tests pass, check it compile on FPU based EC, EC without
FPU and no 64 bit support (ampton).
Check lid calculation is correct on eve:
- with "while true ; do ectool motionsense lid_angle ; sleep 1 ; done"
Check when hinge is almost vertical lid angle is close to constant or
marked are unrieliable.
Check when shaking device, lid angle is also unreliable
Check with evtest SW_TABLET_MODE event is trigger when lid angle is
available and cross 180 region.

Change-Id: I545f7333ed9b53accedb75f238f747f66bae1f5d
Signed-off-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1388844
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Jett Rink <jettrink@chromium.org>
This commit is contained in:
Gwendal Grignou 2018-12-17 15:14:00 -08:00 committed by chrome-bot
parent 4a48404aee
commit 40f9e2fc0f
14 changed files with 2716 additions and 251 deletions

View File

@ -38,7 +38,7 @@ static fp_t last_lid_angle_fp = FLOAT_TO_FP(-1);
* measurements when the lid is physically closed. This will be used in
* reliability calculations.
*/
#define SMALL_LID_ANGLE_RANGE 15
#define SMALL_LID_ANGLE_RANGE (FLOAT_TO_FP(15))
#endif
/* Current acceleration vectors and current lid angle. */
@ -46,15 +46,15 @@ static int lid_angle_deg;
static int lid_angle_is_reliable;
/*
* Angle threshold for how close the hinge aligns with gravity before
* considering the lid angle calculation unreliable. For computational
* efficiency, value is given unit-less, so if you want the threshold to be
* at 15 degrees, the value would be cos(15 deg) = 0.96593.
*
* Here we're using cos(27.5 deg) = 0.88701.
*/
#define HINGE_ALIGNED_WITH_GRAVITY_THRESHOLD FLOAT_TO_FP(0.88701)
/* Smoothed vectors to increase accurency. */
static intv3_t smoothed_base, smoothed_lid;
/* 8.7 m/s^2 is the the maximum acceleration parallel to the hinge */
#define SCALED_HINGE_VERTICAL_MAXIMUM \
((int)((8.7f * MOTION_SCALING_FACTOR) / MOTION_ONE_G))
#define SCALED_HINGE_VERTICAL_SMOOTHING_START \
((int)((7.0f * MOTION_SCALING_FACTOR) / MOTION_ONE_G))
/*
* Constant to debounce lid angle changes around 360 - 0:
@ -67,9 +67,20 @@ static int lid_angle_is_reliable;
* under the same acceleration. This constant, which mirrors
* kNoisyMagnitudeDeviation used in Chromium, is an integer which defines the
* maximum deviation in magnitude between the base and lid vectors. The units
* are in m/s^2.
* are in g. Currently set at 1m/s^2.
*/
#define NOISY_MAGNITUDE_DEVIATION 1
#define NOISY_MAGNITUDE_DEVIATION ((int)(MOTION_SCALING_FACTOR / MOTION_ONE_G))
/*
* Even with noise, any measurement greater than 1g on any axis is not suitable
* for lid calculation. It means the device is moving.
* To avoid using 64bits arithmetic, we need to be sure that square of magnitude
* is less than 1<<31, so magnitude is less sqrt(2)*(1<<15), less than ~40% over
* 1g. This is way above any usable noise. Assume noise is less than 10%.
*/
#define MOTION_SCALING_AXIS_MAX (MOTION_SCALING_FACTOR * 110)
#define MOTION_SCALING_FACTOR2 (MOTION_SCALING_FACTOR * MOTION_SCALING_FACTOR)
/*
* Define the accelerometer orientation matrices based on the standard
@ -77,43 +88,16 @@ static int lid_angle_is_reliable;
* frame before calculating lid angle).
*/
#ifdef CONFIG_ACCEL_STD_REF_FRAME_OLD
const struct accel_orientation acc_orient = {
/* Hinge aligns with y axis. */
.rot_hinge_90 = {
{ 0, 0, FLOAT_TO_FP(1)},
{ 0, FLOAT_TO_FP(1), 0},
{ FLOAT_TO_FP(-1), 0, 0}
},
.rot_hinge_180 = {
{ FLOAT_TO_FP(-1), 0, 0},
{ 0, FLOAT_TO_FP(1), 0},
{ 0, 0, FLOAT_TO_FP(-1)}
},
.hinge_axis = {0, 1, 0},
};
static const intv3_t hinge_axis = { 0, 1, 0};
#define HINGE_AXIS Y
#else
const struct accel_orientation acc_orient = {
/* Hinge aligns with x axis. */
.rot_hinge_90 = {
{ FLOAT_TO_FP(1), 0, 0},
{ 0, 0, FLOAT_TO_FP(1)},
{ 0, FLOAT_TO_FP(-1), 0}
},
.rot_hinge_180 = {
{ FLOAT_TO_FP(1), 0, 0},
{ 0, FLOAT_TO_FP(-1), 0},
{ 0, 0, FLOAT_TO_FP(-1)}
},
.hinge_axis = {1, 0, 0},
};
static const intv3_t hinge_axis = { 1, 0, 0};
#define HINGE_AXIS X
#endif
/* Pointer to constant acceleration orientation data. */
const struct accel_orientation * const p_acc_orient = &acc_orient;
const struct motion_sensor_t * const accel_base =
static const struct motion_sensor_t * const accel_base =
&motion_sensors[CONFIG_LID_ANGLE_SENSOR_BASE];
const struct motion_sensor_t * const accel_lid =
static const struct motion_sensor_t * const accel_lid =
&motion_sensors[CONFIG_LID_ANGLE_SENSOR_LID];
#ifdef CONFIG_TABLET_MODE
@ -165,16 +149,6 @@ static fp_t laptop_zone_lid_angle =
static int tablet_mode_lid_angle = DEFAULT_TABLET_MODE_ANGLE;
static int tablet_mode_hys_degree = DEFAULT_TABLET_MODE_HYS;
/*
* We will change our tablet mode status when we are "convinced" that it has
* changed. This means we will have to consecutively calculate our new tablet
* mode while the angle is stable and come to the same conclusion. The number
* of consecutive calculations is the debounce count with an interval between
* readings set by the motion_sense task. This should avoid spurious forces
* that may trigger false transitions of the tablet mode switch.
*/
#define TABLET_MODE_DEBOUNCE_COUNT 3
static void motion_lid_set_tablet_mode(int reliable)
{
static int tablet_mode_debounce_cnt = TABLET_MODE_DEBOUNCE_COUNT;
@ -305,109 +279,41 @@ static void motion_lid_set_dptf_profile(int reliable)
static int calculate_lid_angle(const intv3_t base, const intv3_t lid,
int *lid_angle)
{
intv3_t v;
fp_t lid_to_base_fp, cos_lid_90, cos_lid_270;
fp_t lid_to_base, base_to_hinge;
fp_t denominator;
int reliable = 1;
int base_magnitude2, lid_magnitude2;
int base_range, lid_range, i;
intv3_t scaled_base, scaled_lid;
intv3_t cross, proj_lid, proj_base, scaled_base, scaled_lid;
fp_t lid_to_base_fp, smoothed_ratio;
int base_magnitude2, lid_magnitude2, largest_hinge_accel;
int reliable = 1, i;
/*
* The angle between lid and base is:
* acos((cad(base, lid) - cad(base, hinge)^2) /(1 - cad(base, hinge)^2))
* where cad() is the cosine_of_angle_diff() function.
*
* Make sure to check for divide by 0.
* Scale the vectors by their range, to be able to compare them.
* If a single measurement is greated than 1g, we may overflow fixed
* point calculation. However, we can exclude such a measurement, it
* means the device is in movement and lid angle calculation is not
* possible.
*/
lid_to_base = cosine_of_angle_diff(base, lid);
base_to_hinge = cosine_of_angle_diff(base, p_acc_orient->hinge_axis);
/*
* If hinge aligns too closely with gravity, then result may be
* unreliable.
*/
if (fp_abs(base_to_hinge) > HINGE_ALIGNED_WITH_GRAVITY_THRESHOLD)
reliable = 0;
base_to_hinge = fp_sq(base_to_hinge);
/* Check divide by 0. */
denominator = FLOAT_TO_FP(1.0) - base_to_hinge;
if (fp_abs(denominator) < FLOAT_TO_FP(0.01)) {
*lid_angle = 0;
return 0;
}
lid_to_base_fp = arc_cos(fp_div(lid_to_base - base_to_hinge,
denominator));
/*
* The previous calculation actually has two solutions, a positive and
* a negative solution. To figure out the sign of the answer, calculate
* the cosine of the angle between the actual lid angle and the
* estimated vector if the lid were open to 90 deg, cos_lid_90. Also
* calculate the cosine of the angle between the actual lid angle and
* the estimated vector if the lid were open to 270 deg,
* cos_lid_270. The smaller of the two angles represents which one is
* closer. If the lid is closer to the estimated 270 degree vector then
* the result is negative, otherwise it is positive.
*/
rotate(base, p_acc_orient->rot_hinge_90, v);
cos_lid_90 = cosine_of_angle_diff(v, lid);
rotate(v, p_acc_orient->rot_hinge_180, v);
cos_lid_270 = cosine_of_angle_diff(v, lid);
/*
* Note that cos_lid_90 and cos_lid_270 are not in degrees, because
* the arc_cos() was never performed. But, since arc_cos() is
* monotonically decreasing, we can do this comparison without ever
* taking arc_cos(). But, since the function is monotonically
* decreasing, the logic of this comparison is reversed.
*/
if (cos_lid_270 > cos_lid_90)
lid_to_base_fp = -lid_to_base_fp;
/* Place lid angle between 0 and 360 degrees. */
if (lid_to_base_fp < 0)
lid_to_base_fp += FLOAT_TO_FP(360);
/*
* Perform some additional reliability checks.
*
* If the magnitude of the two vectors differ too greatly, then the
* readings are unreliable and we can't use them to calculate the lid
* angle.
*/
/* Scale the vectors by their range. */
base_range = accel_base->drv->get_range(accel_base);
lid_range = accel_lid->drv->get_range(accel_lid);
for (i = X; i <= Z; i++) {
/*
* To increase precision, we'll use 8x the sensor data in the
* intermediate calculation. We would normally divide by 2^15.
*
* This is safe because even at a range of 8g, calculating the
* magnitude squared should still be less than the max of a
* 32-bit signed integer.
*
* The max that base[i] could be is 32768, resulting in a max
* value for scaled_base[i] of 640 @ 8g range and force.
* Typically our range is set to 2g.
*/
scaled_base[i] = base[i] * base_range * 10 >> 12;
scaled_lid[i] = lid[i] * lid_range * 10 >> 12;
scaled_base[i] = base[i] *
accel_base->drv->get_range(accel_base);
scaled_lid[i] = lid[i] *
accel_lid->drv->get_range(accel_lid);
if (ABS(scaled_base[i]) > MOTION_SCALING_AXIS_MAX ||
ABS(scaled_lid[i]) > MOTION_SCALING_AXIS_MAX) {
reliable = 0;
goto end_calculate_lid_angle;
}
}
base_magnitude2 = (scaled_base[X] * scaled_base[X] +
scaled_base[Y] * scaled_base[Y] +
scaled_base[Z] * scaled_base[Z]) >> 6;
lid_magnitude2 = (scaled_lid[X] * scaled_lid[X] +
scaled_lid[Y] * scaled_lid[Y] +
scaled_lid[Z] * scaled_lid[Z]) >> 6;
/*
* Calculate square of vector magnitude in g.
* Each entry is guaranteed to be up to +/- 1<<15, so the square will be
* less than 1<<30.
*/
base_magnitude2 = scaled_base[X] * scaled_base[X] +
scaled_base[Y] * scaled_base[Y] +
scaled_base[Z] * scaled_base[Z];
lid_magnitude2 = scaled_lid[X] * scaled_lid[X] +
scaled_lid[Y] * scaled_lid[Y] +
scaled_lid[Z] * scaled_lid[Z];
/*
* Check to see if they differ than more than NOISY_MAGNITUDE_DEVIATION.
@ -417,27 +323,93 @@ static int calculate_lid_angle(const intv3_t base, const intv3_t lid,
* magnitude, but we can work with the magnitudes squared directly as
* shown below:
*
* If A and B are the base and lid magnitudes, and x is the noisy
* magnitude deviation:
* If A is a magnitudes, and x is the noisy magnitude deviation:
*
* A - B < x
* A^2 - B^2 < x * (A + B)
* A^2 - B^2 < 2 * x * avg(A, B)
* 0 < 1g - A < x
* 0 < 1g^2 - A^2 < x * (A + B)
* 0 < 1g^2 - A^2 < 2 * x * avg(A, B)
*
* If we assume that the average acceleration should be about 1g, then
* we have:
*
* (A^2 - B^2) < 2 * 1g * NOISY_MAGNITUDE_DEVIATION
* 0 < 1g^2 - A^2 < 2 * 1g * NOISY_MAGNITUDE_DEVIATION
*/
if (ABS(base_magnitude2 - lid_magnitude2) >
(2 * 10 * NOISY_MAGNITUDE_DEVIATION))
if (MOTION_SCALING_FACTOR2 - base_magnitude2 >
2 * MOTION_SCALING_FACTOR * NOISY_MAGNITUDE_DEVIATION) {
reliable = 0;
goto end_calculate_lid_angle;
}
if (MOTION_SCALING_FACTOR2 - lid_magnitude2 >
2 * MOTION_SCALING_FACTOR * NOISY_MAGNITUDE_DEVIATION) {
reliable = 0;
goto end_calculate_lid_angle;
}
largest_hinge_accel = MAX(ABS(scaled_base[HINGE_AXIS]),
ABS(scaled_lid[HINGE_AXIS]));
smoothed_ratio = MAX(INT_TO_FP(0), MIN(INT_TO_FP(1),
fp_div(INT_TO_FP(largest_hinge_accel -
SCALED_HINGE_VERTICAL_SMOOTHING_START),
INT_TO_FP(SCALED_HINGE_VERTICAL_MAXIMUM -
SCALED_HINGE_VERTICAL_SMOOTHING_START))));
/* Check hinge is not too vertical */
if (largest_hinge_accel > SCALED_HINGE_VERTICAL_MAXIMUM) {
reliable = 0;
goto end_calculate_lid_angle;
}
/* Smooth input to reduce calculation error due to noise. */
vector_scale(smoothed_base, smoothed_ratio);
vector_scale(smoothed_lid, smoothed_ratio);
vector_scale(scaled_base, INT_TO_FP(1) - smoothed_ratio);
vector_scale(scaled_lid, INT_TO_FP(1) - smoothed_ratio);
for (i = X; i <= Z; i++) {
smoothed_base[i] += scaled_base[i];
smoothed_lid[i] += scaled_lid[i];
}
/* Project vectors on the hinge hyperplan, putting smooth ones aside. */
memcpy(proj_base, smoothed_base, sizeof(intv3_t));
memcpy(proj_lid, smoothed_lid, sizeof(intv3_t));
proj_base[HINGE_AXIS] = 0;
proj_lid[HINGE_AXIS] = 0;
/* Calculate the clockwise angle */
lid_to_base_fp = arc_cos(cosine_of_angle_diff(proj_base, proj_lid));
cross_product(proj_base, proj_lid, cross);
/*
* If the dot product of this cross product is normal, it means that
* the shortest angle between |base| and |lid| was counterclockwise
* with respect to the surface represented by |hinge_axis| and this
* angle must be reversed.
*/
if (dot_product(cross, hinge_axis) > 0)
lid_to_base_fp = FLOAT_TO_FP(360) - lid_to_base_fp;
#ifndef CONFIG_ACCEL_STD_REF_FRAME_OLD
/*
* Angle is between the keyboard and the front of screen: we need to
* anlge between keyboard and back of screen:
* 180 instead of 0 when lid and base are flat on surface.
* 0 instead of 180 when lid is closed on keyboard.
*/
lid_to_base_fp = FLOAT_TO_FP(180) - lid_to_base_fp;
#endif
/* Place lid angle between 0 and 360 degrees. */
if (lid_to_base_fp < 0)
lid_to_base_fp += FLOAT_TO_FP(360);
#ifdef CONFIG_TABLET_MODE
/* Ignore large angles when the lid is closed. */
if (!lid_is_open() &&
(lid_to_base_fp > FLOAT_TO_FP(SMALL_LID_ANGLE_RANGE)))
(lid_to_base_fp > SMALL_LID_ANGLE_RANGE)) {
reliable = 0;
goto end_calculate_lid_angle;
}
/*
* Ignore small angles when the lid is open.
@ -451,33 +423,31 @@ static int calculate_lid_angle(const intv3_t base, const intv3_t lid,
* reliable readings over a threshold to disable key scanning.
*/
if (lid_is_open() &&
(lid_to_base_fp <= FLOAT_TO_FP(SMALL_LID_ANGLE_RANGE)))
(lid_to_base_fp <= SMALL_LID_ANGLE_RANGE)) {
reliable = 0;
if (reliable) {
/*
* Seed the lid angle now that we have a reliable
* measurement.
*/
if (last_lid_angle_fp == FLOAT_TO_FP(-1))
last_lid_angle_fp = lid_to_base_fp;
/*
* If the angle was last seen as really large and now it's quite
* small, we may be rotating around from 360->0 so correct it to
* be large. But in case that the lid switch is closed, we can
* prove the small angle we see is correct so we take the angle
* as is.
*/
if ((last_lid_angle_fp >=
FLOAT_TO_FP(360) - DEBOUNCE_ANGLE_DELTA) &&
(lid_to_base_fp <= DEBOUNCE_ANGLE_DELTA) &&
(lid_is_open()))
last_lid_angle_fp = FLOAT_TO_FP(360) - lid_to_base_fp;
else
last_lid_angle_fp = lid_to_base_fp;
goto end_calculate_lid_angle;
}
/* Seed the lid angle now that we have a reliable measurement. */
if (last_lid_angle_fp == FLOAT_TO_FP(-1))
last_lid_angle_fp = lid_to_base_fp;
/*
* If the angle was last seen as really large and now it's quite
* small, we may be rotating around from 360->0 so correct it to
* be large. But in case that the lid switch is closed, we can
* prove the small angle we see is correct so we take the angle
* as is.
*/
if ((last_lid_angle_fp >=
FLOAT_TO_FP(360) - DEBOUNCE_ANGLE_DELTA) &&
(lid_to_base_fp <= DEBOUNCE_ANGLE_DELTA) &&
(lid_is_open()))
last_lid_angle_fp = FLOAT_TO_FP(360) - lid_to_base_fp;
else
last_lid_angle_fp = lid_to_base_fp;
end_calculate_lid_angle:
/*
* Round to nearest int by adding 0.5. Note, only works because lid
* angle is known to be positive.
@ -493,7 +463,9 @@ static int calculate_lid_angle(const intv3_t base, const intv3_t lid,
#endif /* CONFIG_DPTF_MULTI_PROFILE && CONFIG_DPTF_MOTION_LID_NO_HALL_SENSOR */
#else /* CONFIG_TABLET_MODE */
*lid_angle = FP_TO_INT(lid_to_base_fp + FLOAT_TO_FP(0.5));
end_calculate_lid_angle:
if (reliable)
*lid_angle = FP_TO_INT(lid_to_base_fp + FLOAT_TO_FP(0.5));
#endif
return reliable;
}
@ -511,25 +483,10 @@ int motion_lid_get_angle(void)
*/
void motion_lid_calc(void)
{
#ifndef CONFIG_ACCEL_STD_REF_FRAME_OLD
/*
* rotate lid vector by 180 deg to be in the right coordinate frame
* because calculate_lid_angle assumes when the lid is closed, that
* the lid and base accelerometer data matches
*/
intv3_t lid = { accel_lid->xyz[X],
accel_lid->xyz[Y] * -1,
accel_lid->xyz[Z] * -1};
/* Calculate angle of lid accel. */
lid_angle_is_reliable = calculate_lid_angle(
accel_base->xyz, lid,
&lid_angle_deg);
#else
/* Calculate angle of lid accel. */
lid_angle_is_reliable = calculate_lid_angle(
accel_base->xyz, accel_lid->xyz,
&lid_angle_deg);
#endif
#ifdef CONFIG_LID_ANGLE_UPDATE
lid_angle_update(motion_lid_get_angle());

View File

@ -10,6 +10,14 @@
/* Header file for accelerometer / gyro drivers. */
/*
* EC reports sensor data on 16 bits. For accel/gyro/mag.. the MSB is the sign.
* For instance, for gravity,
* real_value[in g] = measured_value * range >> 15
*/
#define MOTION_SCALING_FACTOR (1 << 15)
#define MOTION_ONE_G (9.80665f)
struct accelgyro_drv {
/**
* Initialize accelerometers.

View File

@ -11,27 +11,15 @@
#include "host_command.h"
#include "math_util.h"
/**
* This structure defines all of the data needed to specify the orientation
* of the base and lid accelerometers in order to calculate the lid angle.
/*
* We will change our tablet mode status when we are "convinced" that it has
* changed. This means we will have to consecutively calculate our new tablet
* mode while the angle is stable and come to the same conclusion. The number
* of consecutive calculations is the debounce count with an interval between
* readings set by the motion_sense task. This should avoid spurious forces
* that may trigger false transitions of the tablet mode switch.
*/
struct accel_orientation {
/* Rotation matrix to rotate positive 90 degrees around the hinge. */
mat33_fp_t rot_hinge_90;
/*
* Rotation matrix to rotate 180 degrees around the hinge. The value
* here should be rot_hinge_90 ^ 2.
*/
mat33_fp_t rot_hinge_180;
/* Vector pointing along hinge axis. */
intv3_t hinge_axis;
};
/* Link global structure for orientation. This must be defined in board.c. */
extern const struct accel_orientation acc_orient;
#define TABLET_MODE_DEBOUNCE_COUNT 3
/**
* Get last calculated lid angle. Note, the lid angle calculated by the EC

View File

@ -41,6 +41,8 @@ test-list-host += kb_scan
test-list-host += lid_sw
test-list-host += lightbar
test-list-host += math_util
test-list-host += motion_angle
test-list-host += motion_angle_tablet
test-list-host += motion_lid
test-list-host += mutex
test-list-host += nvmem
@ -97,6 +99,8 @@ kb_scan-y=kb_scan.o
lid_sw-y=lid_sw.o
lightbar-y=lightbar.o
math_util-y=math_util.o
motion_angle-y=motion_angle.o motion_angle_data_literals.o motion_common.o
motion_angle_tablet-y=motion_angle_tablet.o motion_angle_data_literals_tablet.o motion_common.o
motion_lid-y=motion_lid.o
mutex-y=mutex.o
nvmem-y=nvmem.o

103
test/motion_angle.c Normal file
View File

@ -0,0 +1,103 @@
/* Copyright 2019 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.
*
* Test motion sense code: Check lid angle calculation and tablet mode
* transition.
*/
#include <math.h>
#include <stdio.h>
#include "accelgyro.h"
#include "common.h"
#include "gpio.h"
#include "hooks.h"
#include "motion_common.h"
#include "motion_lid.h"
#include "motion_sense.h"
#include "tablet_mode.h"
#include "test_util.h"
#include "util.h"
/*****************************************************************************/
/* Test utilities */
/* Array units is in m/s^2 - old matrix format. */
int filler(const struct motion_sensor_t *s, const float v)
{
return (v * MOTION_SCALING_FACTOR) / s->drv->get_range(s);
}
static int test_lid_angle_less180(void)
{
int index = 0, lid_angle;
struct motion_sensor_t *lid = &motion_sensors[
CONFIG_LID_ANGLE_SENSOR_LID];
struct motion_sensor_t *base = &motion_sensors[
CONFIG_LID_ANGLE_SENSOR_BASE];
/* We don't have TASK_CHIP so simulate init ourselves */
hook_notify(HOOK_CHIPSET_SHUTDOWN);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S5);
TEST_ASSERT(lid->drv->get_data_rate(lid) == 0);
TEST_ASSERT(motion_interval == 0);
/* Go to S0 state */
hook_notify(HOOK_CHIPSET_SUSPEND);
hook_notify(HOOK_CHIPSET_RESUME);
msleep(1000);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S0);
TEST_ASSERT(lid->drv->get_data_rate(lid) == TEST_LID_FREQUENCY);
TEST_ASSERT(motion_interval == TEST_LID_EC_RATE);
/* Open lid, testing close to 180 degree. */
gpio_set_level(GPIO_LID_OPEN, 1);
msleep(1000);
cprints(CC_ACCEL, "start loop");
/* Check we will never enter tablet mode. */
while (index < kAccelerometerLaptopModeTestDataLength) {
feed_accel_data(kAccelerometerLaptopModeTestData,
&index, filler);
wait_for_valid_sample();
lid_angle = motion_lid_get_angle();
cprints(CC_ACCEL, "%d : LID(%d, %d, %d)/BASE(%d, %d, %d): %d",
index / TEST_LID_SAMPLE_SIZE,
lid->xyz[X], lid->xyz[Y], lid->xyz[Z],
base->xyz[X], base->xyz[Y], base->xyz[Z],
lid_angle);
/* We need few sample to debounce and enter laptop mode. */
TEST_ASSERT(index < TEST_LID_SAMPLE_SIZE *
(TABLET_MODE_DEBOUNCE_COUNT + 2) ||
!tablet_get_mode());
}
/* Check we will never exit tablet mode. */
index = 0;
while (index < kAccelerometerFullyOpenTestDataLength) {
feed_accel_data(kAccelerometerFullyOpenTestData,
&index, filler);
wait_for_valid_sample();
lid_angle = motion_lid_get_angle();
cprints(CC_ACCEL, "%d : LID(%d, %d, %d)/BASE(%d, %d, %d): %d",
index / TEST_LID_SAMPLE_SIZE,
lid->xyz[X], lid->xyz[Y], lid->xyz[Z],
base->xyz[X], base->xyz[Y], base->xyz[Z],
lid_angle);
TEST_ASSERT(index < TEST_LID_SAMPLE_SIZE *
(TABLET_MODE_DEBOUNCE_COUNT + 2) ||
tablet_get_mode());
}
return EC_SUCCESS;
}
void run_test(void)
{
test_reset();
RUN_TEST(test_lid_angle_less180);
test_print_result();
}

View File

@ -0,0 +1,18 @@
/* Copyright (c) 2014 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.
*/
/**
* List of enabled tasks in the priority order
*
* The first one has the lowest priority.
*
* For each task, use the macro TASK_TEST(n, r, d, s) where :
* 'n' in the name of the task
* 'r' in the main routine of the task
* 'd' in an opaque parameter passed to the routine at startup
* 's' is the stack size in bytes; must be a multiple of 8
*/
#define CONFIG_TEST_TASK_LIST \
TASK_TEST(MOTIONSENSE, motion_sense_task, NULL, TASK_STACK_SIZE)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

111
test/motion_angle_tablet.c Normal file
View File

@ -0,0 +1,111 @@
/* Copyright 2018 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.
*
* Test motion sense code, when in tablet mode.
*/
#include <math.h>
#include <stdio.h>
#include "accelgyro.h"
#include "common.h"
#include "gpio.h"
#include "hooks.h"
#include "motion_common.h"
#include "motion_lid.h"
#include "motion_sense.h"
#include "tablet_mode.h"
#include "test_util.h"
#include "util.h"
/*****************************************************************************/
/* Test utilities */
/* convert array value from g to m.s^2. */
int filler(const struct motion_sensor_t *s, const float v)
{
return FP_TO_INT( fp_div(
FLOAT_TO_FP(v) * MOTION_SCALING_FACTOR,
fp_mul(INT_TO_FP(s->drv->get_range(s)), MOTION_ONE_G)));
}
static int test_lid_angle_less180(void)
{
int index = 0, lid_angle;
struct motion_sensor_t *lid = &motion_sensors[
CONFIG_LID_ANGLE_SENSOR_LID];
struct motion_sensor_t *base = &motion_sensors[
CONFIG_LID_ANGLE_SENSOR_BASE];
/* We don't have TASK_CHIP so simulate init ourselves */
hook_notify(HOOK_CHIPSET_SHUTDOWN);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S5);
TEST_ASSERT(lid->drv->get_data_rate(lid) == 0);
TEST_ASSERT(motion_interval == 0);
/* Go to S0 state */
hook_notify(HOOK_CHIPSET_SUSPEND);
hook_notify(HOOK_CHIPSET_RESUME);
msleep(1000);
TEST_ASSERT(sensor_active == SENSOR_ACTIVE_S0);
TEST_ASSERT(lid->drv->get_data_rate(lid) == TEST_LID_FREQUENCY);
TEST_ASSERT(motion_interval == TEST_LID_EC_RATE);
/* Open lid, testing close to 180 degree. */
gpio_set_level(GPIO_LID_OPEN, 1);
msleep(1000);
cprints(CC_ACCEL, "start loop");
/* Force clamshell mode, to be sure we go in tablet mode ASAP. */
tablet_set_mode(0);
/* Check we stay in tablet mode, even when hinge is vertical. */
while (index < kAccelerometerVerticalHingeTestDataLength) {
feed_accel_data(kAccelerometerVerticalHingeTestData,
&index, filler);
wait_for_valid_sample();
lid_angle = motion_lid_get_angle();
cprints(CC_ACCEL, "%d : LID(%d, %d, %d)/BASE(%d, %d, %d): %d",
index / TEST_LID_SAMPLE_SIZE,
lid->xyz[X], lid->xyz[Y], lid->xyz[Z],
base->xyz[X], base->xyz[Y], base->xyz[Z],
lid_angle);
/* We need few sample to debounce and enter laptop mode. */
TEST_ASSERT(index < 2 * TEST_LID_SAMPLE_SIZE * \
(TABLET_MODE_DEBOUNCE_COUNT + 2) ||
tablet_get_mode());
}
/*
* Check we stay in tablet mode, even when hinge is vertical and
* shaked.
*/
tablet_set_mode(0);
while (index < kAccelerometerVerticalHingeUnstableTestDataLength) {
feed_accel_data(kAccelerometerVerticalHingeUnstableTestData,
&index, filler);
wait_for_valid_sample();
lid_angle = motion_lid_get_angle();
cprints(CC_ACCEL, "%d : LID(%d, %d, %d)/BASE(%d, %d, %d): %d",
index / TEST_LID_SAMPLE_SIZE,
lid->xyz[X], lid->xyz[Y], lid->xyz[Z],
base->xyz[X], base->xyz[Y], base->xyz[Z],
lid_angle);
/* We need few sample to debounce and enter laptop mode. */
TEST_ASSERT(index < TEST_LID_SAMPLE_SIZE *
(TABLET_MODE_DEBOUNCE_COUNT + 2) ||
tablet_get_mode());
}
return EC_SUCCESS;
}
void run_test(void)
{
test_reset();
RUN_TEST(test_lid_angle_less180);
test_print_result();
}

View File

@ -0,0 +1,18 @@
/* Copyright (c) 2014 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.
*/
/**
* List of enabled tasks in the priority order
*
* The first one has the lowest priority.
*
* For each task, use the macro TASK_TEST(n, r, d, s) where :
* 'n' in the name of the task
* 'r' in the main routine of the task
* 'd' in an opaque parameter passed to the routine at startup
* 's' is the stack size in bytes; must be a multiple of 8
*/
#define CONFIG_TEST_TASK_LIST \
TASK_TEST(MOTIONSENSE, motion_sense_task, NULL, TASK_STACK_SIZE)

125
test/motion_common.c Normal file
View File

@ -0,0 +1,125 @@
/* Copyright 2018 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.
*
* Common test code to test lid angle calculation.
*/
#include "accelgyro.h"
#include "host_command.h"
#include "motion_common.h"
#include "motion_sense.h"
#include "task.h"
#include "timer.h"
/*****************************************************************************/
/* Mock functions */
static int accel_init(const struct motion_sensor_t *s)
{
return EC_SUCCESS;
}
static int accel_read(const struct motion_sensor_t *s, intv3_t v)
{
rotate(s->xyz, *s->rot_standard_ref, v);
return EC_SUCCESS;
}
static int accel_get_range(const struct motion_sensor_t *s)
{
return s->default_range;
}
static int accel_get_resolution(const struct motion_sensor_t *s)
{
return 0;
}
int test_data_rate[2] = { 0 };
static int accel_set_data_rate(const struct motion_sensor_t *s,
const int rate,
const int rnd)
{
test_data_rate[s - motion_sensors] = rate | (rnd ? ROUND_UP_FLAG : 0);
return EC_SUCCESS;
}
static int accel_get_data_rate(const struct motion_sensor_t *s)
{
return test_data_rate[s - motion_sensors];
}
const struct accelgyro_drv test_motion_sense = {
.init = accel_init,
.read = accel_read,
.get_range = accel_get_range,
.get_resolution = accel_get_resolution,
.set_data_rate = accel_set_data_rate,
.get_data_rate = accel_get_data_rate,
};
struct motion_sensor_t motion_sensors[] = {
{
.name = "base",
.active_mask = SENSOR_ACTIVE_S0_S3_S5,
.chip = MOTIONSENSE_CHIP_LSM6DS0,
.type = MOTIONSENSE_TYPE_ACCEL,
.location = MOTIONSENSE_LOC_BASE,
.drv = &test_motion_sense,
.rot_standard_ref = NULL,
.default_range = 2, /* g, enough for laptop. */
.config = {
/* EC use accel for angle detection */
[SENSOR_CONFIG_EC_S0] = {
.odr = TEST_LID_FREQUENCY,
},
},
},
{
.name = "lid",
.active_mask = SENSOR_ACTIVE_S0,
.chip = MOTIONSENSE_CHIP_KXCJ9,
.type = MOTIONSENSE_TYPE_ACCEL,
.location = MOTIONSENSE_LOC_LID,
.drv = &test_motion_sense,
.rot_standard_ref = NULL,
.default_range = 2, /* g, enough for laptop. */
.config = {
/* EC use accel for angle detection */
[SENSOR_CONFIG_EC_S0] = {
.odr = TEST_LID_FREQUENCY,
},
},
},
};
const unsigned int motion_sensor_count = ARRAY_SIZE(motion_sensors);
/* Read 6 samples from array to sensor vectors, convert units if necessary. */
void feed_accel_data(const float *array, int *idx,
int (filler)(const struct motion_sensor_t*, const float))
{
int i, j;
for (i = 0; i < motion_sensor_count; i++) {
struct motion_sensor_t *s = &motion_sensors[i];
for (j = X; j <= Z; j++)
s->xyz[j] = filler(s, array[*idx + i * 3 + j]);
}
*idx += 6;
}
void wait_for_valid_sample(void)
{
uint8_t sample;
uint8_t *lpc_status = host_get_memmap(EC_MEMMAP_ACC_STATUS);
sample = *lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
usleep(TEST_LID_EC_RATE);
task_wake(TASK_ID_MOTIONSENSE);
while ((*lpc_status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK) == sample)
usleep(TEST_LID_SLEEP_RATE);
}

69
test/motion_common.h Normal file
View File

@ -0,0 +1,69 @@
/* Copyright 2018 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.
*
* Common test code to test lid angle calculation.
*/
#ifndef __CROS_EC_MOTION_COMMON_H
#define __CROS_EC_MOTION_COMMON_H
#include "motion_sense.h"
/*
* Period in us for the motion task period.
* The task will read the vectors at that interval
*/
#define TEST_LID_EC_RATE (1 * MSEC)
#define TEST_LID_FREQUENCY (1e9 / TEST_LID_EC_RATE) /* mHz */
/*
* Time in ms to wait for the task to read the vectors.
*/
#define TEST_LID_SLEEP_RATE (TEST_LID_EC_RATE / 5)
/* We gather 6 elements [2 vectors of 3 axis] per sample. */
#define TEST_LID_SAMPLE_SIZE (2 * 3)
extern enum chipset_state_mask sensor_active;
extern unsigned int motion_interval;
extern struct motion_sensor_t motion_sensors[];
extern const unsigned int motion_sensor_count;
void wait_for_valid_sample(void);
void feed_accel_data(const float *array, int *idx,
int (filler)(const struct motion_sensor_t *s, const float f));
/*
* External data - from
* chromium/src/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
*
* Test accelerometer data taken with the lid at less than 180 degrees while
* shaking the device around. The data is to be interpreted in groups of 6 where
* each 6 values corresponds to the base accelerometer (-y / g, -x / g, -z / g)
* followed by the lid accelerometer (-y / g , x / g, z / g).
* [ CONFIG_ACCEL_STD_REF_FRAME_OLD must be defined to used this array. ]
*/
extern const float kAccelerometerLaptopModeTestData[];
extern const size_t kAccelerometerLaptopModeTestDataLength;
/*
* Test accelerometer data taken with the lid open 360 degrees while
* shaking the device around. The data is to be interpreted in groups of 6 where
* each 6 values corresponds to the base accelerometer (-y / g, -x / g, -z / g)
* followed by the lid accelerometer (-y / g , x / g, z / g).
* [ CONFIG_ACCEL_STD_REF_FRAME_OLD must be defined to used this array. ]
*/
extern const float kAccelerometerFullyOpenTestData[];
extern const size_t kAccelerometerFullyOpenTestDataLength;
/*
* Test accelerometer data taken with the lid open 360 degrees while the device
* hinge was nearly vertical, while shaking the device around. The data is to be
* interpreted in groups of 6 where each 6 values corresponds to the X, Y, and Z
* readings from the base and lid accelerometers in this order.
*/
extern const float kAccelerometerVerticalHingeTestData[];
extern const size_t kAccelerometerVerticalHingeTestDataLength;
extern const float kAccelerometerVerticalHingeUnstableTestData[];
extern const size_t kAccelerometerVerticalHingeUnstableTestDataLength;
#endif /* __CROS_EC_MOTION_COMMON_H */

View File

@ -33,6 +33,7 @@ extern unsigned motion_interval;
* Time in ms to wait for the task to read the vectors.
*/
#define TEST_LID_SLEEP_RATE (TEST_LID_EC_RATE / 5)
#define ONE_G_MEASURED (1 << 14)
/*****************************************************************************/
/* Mock functions */
@ -56,7 +57,7 @@ static int accel_set_range(const struct motion_sensor_t *s,
static int accel_get_range(const struct motion_sensor_t *s)
{
return 0;
return s->default_range;
}
static int accel_get_resolution(const struct motion_sensor_t *s)
@ -97,7 +98,7 @@ struct motion_sensor_t motion_sensors[] = {
.location = MOTIONSENSE_LOC_BASE,
.drv = &test_motion_sense,
.rot_standard_ref = NULL,
.default_range = 2, /* g, enough for laptop. */
.default_range = MOTION_SCALING_FACTOR / ONE_G_MEASURED,
.config = {
/* AP: by default shutdown all sensors */
[SENSOR_CONFIG_AP] = {
@ -127,7 +128,7 @@ struct motion_sensor_t motion_sensors[] = {
.location = MOTIONSENSE_LOC_LID,
.drv = &test_motion_sense,
.rot_standard_ref = NULL,
.default_range = 2, /* g, enough for laptop. */
.default_range = MOTION_SCALING_FACTOR / ONE_G_MEASURED,
.config = {
/* AP: by default shutdown all sensors */
[SENSOR_CONFIG_AP] = {
@ -174,6 +175,7 @@ static int test_lid_angle(void)
CONFIG_LID_ANGLE_SENSOR_BASE];
struct motion_sensor_t *lid = &motion_sensors[
CONFIG_LID_ANGLE_SENSOR_LID];
int lid_angle;
/* We don't have TASK_CHIP so simulate init ourselves */
hook_notify(HOOK_CHIPSET_SHUTDOWN);
@ -195,10 +197,10 @@ static int test_lid_angle(void)
*/
base->xyz[X] = 0;
base->xyz[Y] = 0;
base->xyz[Z] = 1000;
base->xyz[Z] = ONE_G_MEASURED;
lid->xyz[X] = 0;
lid->xyz[Y] = 0;
lid->xyz[Z] = -1000;
lid->xyz[Z] = -ONE_G_MEASURED;
gpio_set_level(GPIO_LID_OPEN, 0);
/* Initial wake up, like init does */
task_wake(TASK_ID_MOTIONSENSE);
@ -208,11 +210,16 @@ static int test_lid_angle(void)
task_wake(TASK_ID_MOTIONSENSE);
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 0);
lid_angle = motion_lid_get_angle();
cprints(CC_ACCEL, "LID(%d, %d, %d)/BASE(%d, %d, %d): %d",
lid->xyz[X], lid->xyz[Y], lid->xyz[Z],
base->xyz[X], base->xyz[Y], base->xyz[Z],
lid_angle);
TEST_ASSERT(lid_angle == 0);
/* Set lid open to 90 degrees. */
lid->xyz[X] = 0;
lid->xyz[Y] = 1000;
lid->xyz[Y] = ONE_G_MEASURED;
lid->xyz[Z] = 0;
gpio_set_level(GPIO_LID_OPEN, 1);
msleep(100);
@ -222,15 +229,15 @@ static int test_lid_angle(void)
/* Set lid open to 225. */
lid->xyz[X] = 0;
lid->xyz[Y] = -500;
lid->xyz[Z] = 500;
lid->xyz[Y] = -1 * ONE_G_MEASURED * 0.707106;
lid->xyz[Z] = ONE_G_MEASURED * 0.707106;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 225);
/* Set lid open to 350 */
lid->xyz[X] = 0;
lid->xyz[Y] = -173;
lid->xyz[Z] = -984;
lid->xyz[Y] = -1 * ONE_G_MEASURED * 0.1736;
lid->xyz[Z] = -1 * ONE_G_MEASURED * 0.9848;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 350);
@ -239,15 +246,15 @@ static int test_lid_angle(void)
* open, we should be getting an unreliable reading.
*/
lid->xyz[X] = 0;
lid->xyz[Y] = 173;
lid->xyz[Z] = -984;
lid->xyz[Y] = ONE_G_MEASURED * 0.1736;
lid->xyz[Z] = -1 * ONE_G_MEASURED * 0.9848;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);
/* Rotate back to 180 and then 10 */
lid->xyz[X] = 0;
lid->xyz[Y] = 0;
lid->xyz[Z] = 1000;
lid->xyz[Z] = ONE_G_MEASURED;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 180);
@ -256,8 +263,8 @@ static int test_lid_angle(void)
* See SMALL_LID_ANGLE_RANGE.
*/
lid->xyz[X] = 0;
lid->xyz[Y] = 173;
lid->xyz[Z] = -984;
lid->xyz[Y] = ONE_G_MEASURED * 0.1736;
lid->xyz[Z] = -1 * ONE_G_MEASURED * 0.9848;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);
@ -265,7 +272,7 @@ static int test_lid_angle(void)
* Align base with hinge and make sure it returns unreliable for angle.
* In this test it doesn't matter what the lid acceleration vector is.
*/
base->xyz[X] = 1000;
base->xyz[X] = ONE_G_MEASURED;
base->xyz[Y] = 0;
base->xyz[Z] = 0;
wait_for_valid_sample();
@ -275,12 +282,12 @@ static int test_lid_angle(void)
* Use all three axes and set lid to negative base and make sure
* angle is 180.
*/
base->xyz[X] = 500;
base->xyz[Y] = 400;
base->xyz[Z] = 300;
lid->xyz[X] = 500;
lid->xyz[Y] = 400;
lid->xyz[Z] = 300;
base->xyz[X] = 5296;
base->xyz[Y] = 7856;
base->xyz[Z] = 13712;
lid->xyz[X] = 5296;
lid->xyz[Y] = 7856;
lid->xyz[Z] = 13712;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 180);
@ -289,10 +296,10 @@ static int test_lid_angle(void)
*/
base->xyz[X] = 0;
base->xyz[Y] = 0;
base->xyz[Z] = 1000;
base->xyz[Z] = ONE_G_MEASURED;
lid->xyz[X] = 0;
lid->xyz[Y] = 0;
lid->xyz[Z] = -1000;
lid->xyz[Z] = -1 * ONE_G_MEASURED;
gpio_set_level(GPIO_LID_OPEN, 0);
msleep(100);
wait_for_valid_sample();
@ -303,8 +310,8 @@ static int test_lid_angle(void)
* be regarded as unreliable.
*/
lid->xyz[X] = 0;
lid->xyz[Y] = -173;
lid->xyz[Z] = -984;
lid->xyz[Y] = -1 * ONE_G_MEASURED * 0.1736;
lid->xyz[Z] = -1 * ONE_G_MEASURED * 0.9848;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == LID_ANGLE_UNRELIABLE);
@ -317,8 +324,8 @@ static int test_lid_angle(void)
gpio_set_level(GPIO_LID_OPEN, 0);
msleep(100);
lid->xyz[X] = 0;
lid->xyz[Y] = 173;
lid->xyz[Z] = -984;
lid->xyz[Y] = ONE_G_MEASURED * 0.1736;
lid->xyz[Z] = -1 * ONE_G_MEASURED * 0.9848;
wait_for_valid_sample();
TEST_ASSERT(motion_lid_get_angle() == 10);

View File

@ -66,7 +66,8 @@
#define CONFIG_MAG_CALIBRATE
#endif
#ifdef TEST_MOTION_LID
#if defined(TEST_MOTION_LID) || defined(TEST_MOTION_ANGLE) || \
defined(TEST_MOTION_ANGLE_TABLET)
#define CONFIG_LID_ANGLE
#define CONFIG_LID_ANGLE_SENSOR_BASE 0
#define CONFIG_LID_ANGLE_SENSOR_LID 1
@ -74,6 +75,19 @@
#define CONFIG_MOTION_FILL_LPC_SENSE_DATA
#endif
#if defined(TEST_MOTION_ANGLE)
#define CONFIG_ACCEL_FORCE_MODE_MASK \
((1 << CONFIG_LID_ANGLE_SENSOR_BASE) | \
(1 << CONFIG_LID_ANGLE_SENSOR_LID))
#define CONFIG_ACCEL_STD_REF_FRAME_OLD
#endif
#if defined(TEST_MOTION_ANGLE_TABLET)
#define CONFIG_ACCEL_FORCE_MODE_MASK \
((1 << CONFIG_LID_ANGLE_SENSOR_BASE) | \
(1 << CONFIG_LID_ANGLE_SENSOR_LID))
#endif
#ifdef TEST_RMA_AUTH
/* Test server public and private keys */