summaryrefslogtreecommitdiff
path: root/common/motion_sense.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/motion_sense.c')
-rw-r--r--common/motion_sense.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/common/motion_sense.c b/common/motion_sense.c
new file mode 100644
index 0000000000..beb4ddbf2b
--- /dev/null
+++ b/common/motion_sense.c
@@ -0,0 +1,214 @@
+/* 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.
+ */
+
+/* Motion sense module to read from various motion sensors. */
+
+#include "accelerometer.h"
+#include "common.h"
+#include "console.h"
+#include "hooks.h"
+#include "math_util.h"
+#include "motion_sense.h"
+#include "timer.h"
+#include "task.h"
+#include "util.h"
+
+/* Console output macros */
+#define CPUTS(outstr) cputs(CC_MOTION_SENSE, outstr)
+#define CPRINTF(format, args...) cprintf(CC_MOTION_SENSE, format, ## args)
+
+/* Minimum time in between running motion sense task loop. */
+#define MIN_MOTION_SENSE_WAIT_TIME (1 * MSEC)
+
+/* Current acceleration vectors and current lid angle. */
+static vector_3_t acc_lid_raw, acc_lid, acc_base;
+static float lid_angle_deg;
+
+/* Sampling interval for measuring acceleration and calculating lid angle. */
+static int accel_interval_ms = 250;
+
+#ifdef CONFIG_CMD_LID_ANGLE
+static int accel_disp;
+#endif
+
+/* Pointer to constant acceleration orientation data. */
+const struct accel_orientation * const p_acc_orient = &acc_orient;
+
+/**
+ * Calculate the lid angle using two acceleration vectors, one recorded in
+ * the base and one in the lid.
+ */
+static float calculate_lid_angle(vector_3_t base, vector_3_t lid)
+{
+ vector_3_t v;
+ float ang_lid_to_base, ang_lid_90, ang_lid_270;
+ float lid_to_base, base_to_hinge;
+
+ /*
+ * 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.
+ */
+ lid_to_base = cosine_of_angle_diff(base, lid);
+ base_to_hinge = cosine_of_angle_diff(base, p_acc_orient->hinge_axis);
+ base_to_hinge = SQ(base_to_hinge);
+
+ /* Check divide by 0. */
+ if (ABS(1.0F - base_to_hinge) < 0.01F)
+ return 0.0;
+
+ ang_lid_to_base = arc_cos(
+ (lid_to_base - base_to_hinge) / (1 - base_to_hinge));
+
+ /*
+ * The previous calculation actually has two solutions, a positive and
+ * a negative solution. To figure out the sign of the answer, calculate
+ * the angle between the actual lid angle and the estimated vector if
+ * the lid were open to 90 deg, ang_lid_90. Also calculate the angle
+ * between the actual lid angle and the estimated vector if the lid
+ * were open to 270 deg, ang_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);
+ ang_lid_90 = cosine_of_angle_diff(v, lid);
+ rotate(v, &p_acc_orient->rot_hinge_180, &v);
+ ang_lid_270 = cosine_of_angle_diff(v, lid);
+
+ /*
+ * Note that ang_lid_90 and ang_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 (ang_lid_270 > ang_lid_90)
+ ang_lid_to_base = -ang_lid_to_base;
+
+ return ang_lid_to_base;
+}
+
+int motion_get_lid_angle(void)
+{
+ return (int)lid_angle_deg;
+}
+
+#ifdef CONFIG_ACCEL_CALIBRATE
+void motion_get_accel_lid(vector_3_t *v, int adjusted)
+{
+ memcpy(v, adjusted ? &acc_lid : &acc_lid_raw, sizeof(vector_3_t));
+}
+
+void motion_get_accel_base(vector_3_t *v)
+{
+ memcpy(v, &acc_base, sizeof(vector_3_t));
+}
+#endif
+
+
+void motion_sense_task(void)
+{
+ timestamp_t ts0, ts1;
+ int wait_us;
+ int ret;
+
+ /* Initialize accelerometers. */
+ ret = accel_init(ACCEL_LID);
+ ret |= accel_init(ACCEL_BASE);
+
+ /* If accelerometers do not initialize, then end task. */
+ if (ret != EC_SUCCESS) {
+ CPRINTF("[%T, Accelerometers failed to initialize. Stopping "
+ "motion sense task.\n");
+ return;
+ }
+
+ while (1) {
+ ts0 = get_time();
+
+ /* Read all accelerations. */
+ accel_read(ACCEL_LID, &acc_lid_raw[0], &acc_lid_raw[1],
+ &acc_lid_raw[2]);
+ accel_read(ACCEL_BASE, &acc_base[0], &acc_base[1],
+ &acc_base[2]);
+
+ /*
+ * Rotate the lid vector so the reference frame aligns with
+ * the base sensor.
+ */
+ rotate(acc_lid_raw, &p_acc_orient->rot_align, &acc_lid);
+
+ /* Calculate angle of lid. */
+ lid_angle_deg = calculate_lid_angle(acc_base, acc_lid);
+
+ /* TODO(crosbug.com/p/25597): Add filter to smooth lid angle. */
+
+ /*
+ * TODO(crosbug.com/p/25599): Add acceleration data to LPC
+ * shared memory.
+ */
+
+#ifdef CONFIG_CMD_LID_ANGLE
+ if (accel_disp) {
+ CPRINTF("[%T ACC base=%-5d, %-5d, %-5d lid=%-5d, "
+ "%-5d, %-5d a=%-6.1d]\n",
+ acc_base[0], acc_base[1], acc_base[2],
+ acc_lid[0], acc_lid[1], acc_lid[2],
+ (int)(10*lid_angle_deg));
+ }
+#endif
+
+ /* Delay appropriately to keep sampling time consistent. */
+ ts1 = get_time();
+ wait_us = accel_interval_ms * MSEC - (ts1.val-ts0.val);
+
+ /*
+ * Guarantee some minimum delay to allow other lower priority
+ * tasks to run.
+ */
+ if (wait_us < MIN_MOTION_SENSE_WAIT_TIME)
+ wait_us = MIN_MOTION_SENSE_WAIT_TIME;
+
+ task_wait_event(wait_us);
+ }
+}
+
+/*****************************************************************************/
+/* Console commands */
+#ifdef CONFIG_CMD_LID_ANGLE
+static int command_ctrl_print_lid_angle_calcs(int argc, char **argv)
+{
+ char *e;
+ int val;
+
+ if (argc > 3)
+ return EC_ERROR_PARAM_COUNT;
+
+ /* First argument is on/off whether to display accel data. */
+ if (argc > 1) {
+ if (!parse_bool(argv[1], &val))
+ return EC_ERROR_PARAM1;
+
+ accel_disp = val;
+ }
+
+ /* Second arg changes the accel task time interval. */
+ if (argc > 2) {
+ val = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ accel_interval_ms = val;
+ }
+
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(lidangle, command_ctrl_print_lid_angle_calcs,
+ "on/off [interval]",
+ "Print lid angle calculations and set calculation frequency.", NULL);
+#endif /* CONFIG_CMD_LID_ANGLE */