widget/gonk/ProcessOrientation.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /*
michael@0 2 * Copyright (c) 2013, Linux Foundation. All rights reserved
michael@0 3 *
michael@0 4 * Copyright (C) 2008 The Android Open Source Project
michael@0 5 *
michael@0 6 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 7 * you may not use this file except in compliance with the License.
michael@0 8 * You may obtain a copy of the License at
michael@0 9 *
michael@0 10 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 11 *
michael@0 12 * Unless required by applicable law or agreed to in writing, software
michael@0 13 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 15 * See the License for the specific language governing permissions and
michael@0 16 * limitations under the License.
michael@0 17 */
michael@0 18
michael@0 19 #include "base/basictypes.h"
michael@0 20 #include "mozilla/Hal.h"
michael@0 21 #include "nsIScreen.h"
michael@0 22 #include "nsIScreenManager.h"
michael@0 23 #include "OrientationObserver.h"
michael@0 24 #include "ProcessOrientation.h"
michael@0 25 #include "mozilla/HalSensor.h"
michael@0 26 #include "math.h"
michael@0 27 #include "limits.h"
michael@0 28 #include "android/log.h"
michael@0 29
michael@0 30 #if 0
michael@0 31 #define LOGD(args...) __android_log_print(ANDROID_LOG_DEBUG, "ProcessOrientation" , ## args)
michael@0 32 #else
michael@0 33 #define LOGD(args...)
michael@0 34 #endif
michael@0 35
michael@0 36 namespace mozilla {
michael@0 37
michael@0 38 // We work with all angles in degrees in this class.
michael@0 39 #define RADIANS_TO_DEGREES (180/M_PI)
michael@0 40
michael@0 41 // Number of nanoseconds per millisecond.
michael@0 42 #define NANOS_PER_MS 1000000
michael@0 43
michael@0 44 // Indices into SensorEvent.values for the accelerometer sensor.
michael@0 45 #define ACCELEROMETER_DATA_X 0
michael@0 46 #define ACCELEROMETER_DATA_Y 1
michael@0 47 #define ACCELEROMETER_DATA_Z 2
michael@0 48
michael@0 49 // The minimum amount of time that a predicted rotation must be stable before
michael@0 50 // it is accepted as a valid rotation proposal. This value can be quite small
michael@0 51 // because the low-pass filter already suppresses most of the noise so we're
michael@0 52 // really just looking for quick confirmation that the last few samples are in
michael@0 53 // agreement as to the desired orientation.
michael@0 54 #define PROPOSAL_SETTLE_TIME_NANOS (40*NANOS_PER_MS)
michael@0 55
michael@0 56 // The minimum amount of time that must have elapsed since the device last
michael@0 57 // exited the flat state (time since it was picked up) before the proposed
michael@0 58 // rotation can change.
michael@0 59 #define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS (500*NANOS_PER_MS)
michael@0 60
michael@0 61 // The minimum amount of time that must have elapsed since the device stopped
michael@0 62 // swinging (time since device appeared to be in the process of being put down
michael@0 63 // or put away into a pocket) before the proposed rotation can change.
michael@0 64 #define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS (300*NANOS_PER_MS)
michael@0 65
michael@0 66 // The minimum amount of time that must have elapsed since the device stopped
michael@0 67 // undergoing external acceleration before the proposed rotation can change.
michael@0 68 #define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS (500*NANOS_PER_MS)
michael@0 69
michael@0 70 // If the tilt angle remains greater than the specified angle for a minimum of
michael@0 71 // the specified time, then the device is deemed to be lying flat
michael@0 72 // (just chillin' on a table).
michael@0 73 #define FLAT_ANGLE 75
michael@0 74 #define FLAT_TIME_NANOS (1000*NANOS_PER_MS)
michael@0 75
michael@0 76 // If the tilt angle has increased by at least delta degrees within the
michael@0 77 // specified amount of time, then the device is deemed to be swinging away
michael@0 78 // from the user down towards flat (tilt = 90).
michael@0 79 #define SWING_AWAY_ANGLE_DELTA 20
michael@0 80 #define SWING_TIME_NANOS (300*NANOS_PER_MS)
michael@0 81
michael@0 82 // The maximum sample inter-arrival time in milliseconds. If the acceleration
michael@0 83 // samples are further apart than this amount in time, we reset the state of
michael@0 84 // the low-pass filter and orientation properties. This helps to handle
michael@0 85 // boundary conditions when the device is turned on, wakes from suspend or
michael@0 86 // there is a significant gap in samples.
michael@0 87 #define MAX_FILTER_DELTA_TIME_NANOS (1000*NANOS_PER_MS)
michael@0 88
michael@0 89 // The acceleration filter time constant.
michael@0 90 //
michael@0 91 // This time constant is used to tune the acceleration filter such that
michael@0 92 // impulses and vibrational noise (think car dock) is suppressed before we try
michael@0 93 // to calculate the tilt and orientation angles.
michael@0 94 //
michael@0 95 // The filter time constant is related to the filter cutoff frequency, which
michael@0 96 // is the frequency at which signals are attenuated by 3dB (half the passband
michael@0 97 // power). Each successive octave beyond this frequency is attenuated by an
michael@0 98 // additional 6dB.
michael@0 99 //
michael@0 100 // Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
michael@0 101 // is given by Fc = 1 / (2pi * t).
michael@0 102 //
michael@0 103 // The higher the time constant, the lower the cutoff frequency, so more noise
michael@0 104 // will be suppressed.
michael@0 105 //
michael@0 106 // Filtering adds latency proportional the time constant (inversely
michael@0 107 // proportional to the cutoff frequency) so we don't want to make the time
michael@0 108 // constant too large or we can lose responsiveness. Likewise we don't want
michael@0 109 // to make it too small or we do a poor job suppressing acceleration spikes.
michael@0 110 // Empirically, 100ms seems to be too small and 500ms is too large. Android
michael@0 111 // default is 200.
michael@0 112 #define FILTER_TIME_CONSTANT_MS 200.0f
michael@0 113
michael@0 114 // State for orientation detection. Thresholds for minimum and maximum
michael@0 115 // allowable deviation from gravity.
michael@0 116 //
michael@0 117 // If the device is undergoing external acceleration (being bumped, in a car
michael@0 118 // that is turning around a corner or a plane taking off) then the magnitude
michael@0 119 // may be substantially more or less than gravity. This can skew our
michael@0 120 // orientation detection by making us think that up is pointed in a different
michael@0 121 // direction.
michael@0 122 //
michael@0 123 // Conversely, if the device is in freefall, then there will be no gravity to
michael@0 124 // measure at all. This is problematic because we cannot detect the orientation
michael@0 125 // without gravity to tell us which way is up. A magnitude near 0 produces
michael@0 126 // singularities in the tilt and orientation calculations.
michael@0 127 //
michael@0 128 // In both cases, we postpone choosing an orientation.
michael@0 129 //
michael@0 130 // However, we need to tolerate some acceleration because the angular momentum
michael@0 131 // of turning the device can skew the observed acceleration for a short period
michael@0 132 // of time.
michael@0 133 #define NEAR_ZERO_MAGNITUDE 1 // m/s^2
michael@0 134 #define ACCELERATION_TOLERANCE 4 // m/s^2
michael@0 135 #define STANDARD_GRAVITY 9.80665f
michael@0 136 #define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY-ACCELERATION_TOLERANCE)
michael@0 137 #define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY+ACCELERATION_TOLERANCE)
michael@0 138
michael@0 139 // Maximum absolute tilt angle at which to consider orientation data. Beyond
michael@0 140 // this (i.e. when screen is facing the sky or ground), we completely ignore
michael@0 141 // orientation data.
michael@0 142 #define MAX_TILT 75
michael@0 143
michael@0 144 // The gap angle in degrees between adjacent orientation angles for
michael@0 145 // hysteresis.This creates a "dead zone" between the current orientation and a
michael@0 146 // proposed adjacent orientation. No orientation proposal is made when the
michael@0 147 // orientation angle is within the gap between the current orientation and the
michael@0 148 // adjacent orientation.
michael@0 149 #define ADJACENT_ORIENTATION_ANGLE_GAP 45
michael@0 150
michael@0 151 const int
michael@0 152 ProcessOrientation::tiltTolerance[][4] = {
michael@0 153 {-25, 70}, // ROTATION_0
michael@0 154 {-25, 65}, // ROTATION_90
michael@0 155 {-25, 60}, // ROTATION_180
michael@0 156 {-25, 65} // ROTATION_270
michael@0 157 };
michael@0 158
michael@0 159 int
michael@0 160 ProcessOrientation::GetProposedRotation()
michael@0 161 {
michael@0 162 return mProposedRotation;
michael@0 163 }
michael@0 164
michael@0 165 int
michael@0 166 ProcessOrientation::OnSensorChanged(const SensorData& event,
michael@0 167 int deviceCurrentRotation)
michael@0 168 {
michael@0 169 // The vector given in the SensorEvent points straight up (towards the sky)
michael@0 170 // under ideal conditions (the phone is not accelerating). I'll call this up
michael@0 171 // vector elsewhere.
michael@0 172 const InfallibleTArray<float>& values = event.values();
michael@0 173 float x = values[ACCELEROMETER_DATA_X];
michael@0 174 float y = values[ACCELEROMETER_DATA_Y];
michael@0 175 float z = values[ACCELEROMETER_DATA_Z];
michael@0 176
michael@0 177 LOGD
michael@0 178 ("ProcessOrientation: Raw acceleration vector: x = %f, y = %f, z = %f,"
michael@0 179 "magnitude = %f\n", x, y, z, sqrt(x * x + y * y + z * z));
michael@0 180 // Apply a low-pass filter to the acceleration up vector in cartesian space.
michael@0 181 // Reset the orientation listener state if the samples are too far apart in
michael@0 182 // time or when we see values of (0, 0, 0) which indicates that we polled the
michael@0 183 // accelerometer too soon after turning it on and we don't have any data yet.
michael@0 184 const long now = event.timestamp();
michael@0 185 const long then = mLastFilteredTimestampNanos;
michael@0 186 const float timeDeltaMS = (now - then) * 0.000001f;
michael@0 187 bool skipSample = false;
michael@0 188 if (now < then
michael@0 189 || now > then + MAX_FILTER_DELTA_TIME_NANOS
michael@0 190 || (x == 0 && y == 0 && z == 0)) {
michael@0 191 LOGD
michael@0 192 ("ProcessOrientation: Resetting orientation listener.");
michael@0 193 Reset();
michael@0 194 skipSample = true;
michael@0 195 } else {
michael@0 196 const float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
michael@0 197 x = alpha * (x - mLastFilteredX) + mLastFilteredX;
michael@0 198 y = alpha * (y - mLastFilteredY) + mLastFilteredY;
michael@0 199 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
michael@0 200 LOGD
michael@0 201 ("ProcessOrientation: Filtered acceleration vector: x=%f, y=%f, z=%f,"
michael@0 202 "magnitude=%f", z, y, z, sqrt(x * x + y * y + z * z));
michael@0 203 skipSample = false;
michael@0 204 }
michael@0 205 mLastFilteredTimestampNanos = now;
michael@0 206 mLastFilteredX = x;
michael@0 207 mLastFilteredY = y;
michael@0 208 mLastFilteredZ = z;
michael@0 209
michael@0 210 bool isAccelerating = false;
michael@0 211 bool isFlat = false;
michael@0 212 bool isSwinging = false;
michael@0 213 if (skipSample) {
michael@0 214 return -1;
michael@0 215 }
michael@0 216
michael@0 217 // Calculate the magnitude of the acceleration vector.
michael@0 218 const float magnitude = sqrt(x * x + y * y + z * z);
michael@0 219 if (magnitude < NEAR_ZERO_MAGNITUDE) {
michael@0 220 LOGD
michael@0 221 ("ProcessOrientation: Ignoring sensor data, magnitude too close to"
michael@0 222 " zero.");
michael@0 223 ClearPredictedRotation();
michael@0 224 } else {
michael@0 225 // Determine whether the device appears to be undergoing external
michael@0 226 // acceleration.
michael@0 227 if (this->IsAccelerating(magnitude)) {
michael@0 228 isAccelerating = true;
michael@0 229 mAccelerationTimestampNanos = now;
michael@0 230 }
michael@0 231 // Calculate the tilt angle. This is the angle between the up vector and
michael@0 232 // the x-y plane (the plane of the screen) in a range of [-90, 90]
michael@0 233 // degrees.
michael@0 234 // -90 degrees: screen horizontal and facing the ground (overhead)
michael@0 235 // 0 degrees: screen vertical
michael@0 236 // 90 degrees: screen horizontal and facing the sky (on table)
michael@0 237 const int tiltAngle =
michael@0 238 static_cast<int>(roundf(asin(z / magnitude) * RADIANS_TO_DEGREES));
michael@0 239 AddTiltHistoryEntry(now, tiltAngle);
michael@0 240
michael@0 241 // Determine whether the device appears to be flat or swinging.
michael@0 242 if (this->IsFlat(now)) {
michael@0 243 isFlat = true;
michael@0 244 mFlatTimestampNanos = now;
michael@0 245 }
michael@0 246 if (this->IsSwinging(now, tiltAngle)) {
michael@0 247 isSwinging = true;
michael@0 248 mSwingTimestampNanos = now;
michael@0 249 }
michael@0 250 // If the tilt angle is too close to horizontal then we cannot determine
michael@0 251 // the orientation angle of the screen.
michael@0 252 if (abs(tiltAngle) > MAX_TILT) {
michael@0 253 LOGD
michael@0 254 ("ProcessOrientation: Ignoring sensor data, tilt angle too high:"
michael@0 255 " tiltAngle=%d", tiltAngle);
michael@0 256 ClearPredictedRotation();
michael@0 257 } else {
michael@0 258 // Calculate the orientation angle.
michael@0 259 // This is the angle between the x-y projection of the up vector onto
michael@0 260 // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
michael@0 261 int orientationAngle =
michael@0 262 static_cast<int>(roundf(-atan2f(-x, y) * RADIANS_TO_DEGREES));
michael@0 263 if (orientationAngle < 0) {
michael@0 264 // atan2 returns [-180, 180]; normalize to [0, 360]
michael@0 265 orientationAngle += 360;
michael@0 266 }
michael@0 267 // Find the nearest rotation.
michael@0 268 int nearestRotation = (orientationAngle + 45) / 90;
michael@0 269 if (nearestRotation == 4) {
michael@0 270 nearestRotation = 0;
michael@0 271 }
michael@0 272 // Determine the predicted orientation.
michael@0 273 if (IsTiltAngleAcceptable(nearestRotation, tiltAngle)
michael@0 274 &&
michael@0 275 IsOrientationAngleAcceptable
michael@0 276 (nearestRotation, orientationAngle, deviceCurrentRotation)) {
michael@0 277 UpdatePredictedRotation(now, nearestRotation);
michael@0 278 LOGD
michael@0 279 ("ProcessOrientation: Predicted: tiltAngle=%d, orientationAngle=%d,"
michael@0 280 " predictedRotation=%d, predictedRotationAgeMS=%f",
michael@0 281 tiltAngle,
michael@0 282 orientationAngle,
michael@0 283 mPredictedRotation,
michael@0 284 ((now - mPredictedRotationTimestampNanos) * 0.000001f));
michael@0 285 } else {
michael@0 286 LOGD
michael@0 287 ("ProcessOrientation: Ignoring sensor data, no predicted rotation:"
michael@0 288 " tiltAngle=%d, orientationAngle=%d",
michael@0 289 tiltAngle,
michael@0 290 orientationAngle);
michael@0 291 ClearPredictedRotation();
michael@0 292 }
michael@0 293 }
michael@0 294 }
michael@0 295
michael@0 296 // Determine new proposed rotation.
michael@0 297 const int oldProposedRotation = mProposedRotation;
michael@0 298 if (mPredictedRotation < 0 || IsPredictedRotationAcceptable(now)) {
michael@0 299 mProposedRotation = mPredictedRotation;
michael@0 300 }
michael@0 301 // Write final statistics about where we are in the orientation detection
michael@0 302 // process.
michael@0 303 LOGD
michael@0 304 ("ProcessOrientation: Result: oldProposedRotation=%d,currentRotation=%d, "
michael@0 305 "proposedRotation=%d, predictedRotation=%d, timeDeltaMS=%f, "
michael@0 306 "isAccelerating=%d, isFlat=%d, isSwinging=%d, timeUntilSettledMS=%f, "
michael@0 307 "timeUntilAccelerationDelayExpiredMS=%f, timeUntilFlatDelayExpiredMS=%f, "
michael@0 308 "timeUntilSwingDelayExpiredMS=%f",
michael@0 309 oldProposedRotation,
michael@0 310 deviceCurrentRotation, mProposedRotation,
michael@0 311 mPredictedRotation, timeDeltaMS, isAccelerating, isFlat,
michael@0 312 isSwinging, RemainingMS(now,
michael@0 313 mPredictedRotationTimestampNanos +
michael@0 314 PROPOSAL_SETTLE_TIME_NANOS),
michael@0 315 RemainingMS(now,
michael@0 316 mAccelerationTimestampNanos +
michael@0 317 PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS),
michael@0 318 RemainingMS(now,
michael@0 319 mFlatTimestampNanos +
michael@0 320 PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS),
michael@0 321 RemainingMS(now,
michael@0 322 mSwingTimestampNanos +
michael@0 323 PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
michael@0 324 // Tell the listener.
michael@0 325 if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) {
michael@0 326 LOGD
michael@0 327 ("ProcessOrientation: Proposed rotation changed! proposedRotation=%d, "
michael@0 328 "oldProposedRotation=%d",
michael@0 329 mProposedRotation,
michael@0 330 oldProposedRotation);
michael@0 331 return mProposedRotation;
michael@0 332 }
michael@0 333 // Don't rotate screen
michael@0 334 return -1;
michael@0 335 }
michael@0 336
michael@0 337 bool
michael@0 338 ProcessOrientation::IsTiltAngleAcceptable(int rotation, int tiltAngle)
michael@0 339 {
michael@0 340 return (tiltAngle >= tiltTolerance[rotation][0]
michael@0 341 && tiltAngle <= tiltTolerance[rotation][1]);
michael@0 342 }
michael@0 343
michael@0 344 bool
michael@0 345 ProcessOrientation::IsOrientationAngleAcceptable(int rotation,
michael@0 346 int orientationAngle,
michael@0 347 int currentRotation)
michael@0 348 {
michael@0 349 // If there is no current rotation, then there is no gap.
michael@0 350 // The gap is used only to introduce hysteresis among advertised orientation
michael@0 351 // changes to avoid flapping.
michael@0 352 if (currentRotation < 0) {
michael@0 353 return true;
michael@0 354 }
michael@0 355 // If the specified rotation is the same or is counter-clockwise adjacent
michael@0 356 // to the current rotation, then we set a lower bound on the orientation
michael@0 357 // angle. For example, if currentRotation is ROTATION_0 and proposed is
michael@0 358 // ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2.
michael@0 359 if (rotation == currentRotation || rotation == (currentRotation + 1) % 4) {
michael@0 360 int lowerBound = rotation * 90 - 45 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
michael@0 361 if (rotation == 0) {
michael@0 362 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
michael@0 363 return false;
michael@0 364 }
michael@0 365 } else {
michael@0 366 if (orientationAngle < lowerBound) {
michael@0 367 return false;
michael@0 368 }
michael@0 369 }
michael@0 370 }
michael@0 371 // If the specified rotation is the same or is clockwise adjacent, then we
michael@0 372 // set an upper bound on the orientation angle. For example, if
michael@0 373 // currentRotation is ROTATION_0 and rotation is ROTATION_270, then we want
michael@0 374 // to check orientationAngle < 315 - GAP / 2.
michael@0 375 if (rotation == currentRotation || rotation == (currentRotation + 3) % 4) {
michael@0 376 int upperBound = rotation * 90 + 45 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
michael@0 377 if (rotation == 0) {
michael@0 378 if (orientationAngle <= 45 && orientationAngle > upperBound) {
michael@0 379 return false;
michael@0 380 }
michael@0 381 } else {
michael@0 382 if (orientationAngle > upperBound) {
michael@0 383 return false;
michael@0 384 }
michael@0 385 }
michael@0 386 }
michael@0 387 return true;
michael@0 388 }
michael@0 389
michael@0 390 bool
michael@0 391 ProcessOrientation::IsPredictedRotationAcceptable(long now)
michael@0 392 {
michael@0 393 // The predicted rotation must have settled long enough.
michael@0 394 if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
michael@0 395 return false;
michael@0 396 }
michael@0 397 // The last flat state (time since picked up) must have been sufficiently long
michael@0 398 // ago.
michael@0 399 if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
michael@0 400 return false;
michael@0 401 }
michael@0 402 // The last swing state (time since last movement to put down) must have been
michael@0 403 // sufficiently long ago.
michael@0 404 if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
michael@0 405 return false;
michael@0 406 }
michael@0 407 // The last acceleration state must have been sufficiently long ago.
michael@0 408 if (now < mAccelerationTimestampNanos
michael@0 409 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
michael@0 410 return false;
michael@0 411 }
michael@0 412 // Looks good!
michael@0 413 return true;
michael@0 414 }
michael@0 415
michael@0 416 int
michael@0 417 ProcessOrientation::Reset()
michael@0 418 {
michael@0 419 mLastFilteredTimestampNanos = LONG_MIN;
michael@0 420 mProposedRotation = -1;
michael@0 421 mFlatTimestampNanos = LONG_MIN;
michael@0 422 mSwingTimestampNanos = LONG_MIN;
michael@0 423 mAccelerationTimestampNanos = LONG_MIN;
michael@0 424 ClearPredictedRotation();
michael@0 425 ClearTiltHistory();
michael@0 426 return -1;
michael@0 427 }
michael@0 428
michael@0 429 void
michael@0 430 ProcessOrientation::ClearPredictedRotation()
michael@0 431 {
michael@0 432 mPredictedRotation = -1;
michael@0 433 mPredictedRotationTimestampNanos = LONG_MIN;
michael@0 434 }
michael@0 435
michael@0 436 void
michael@0 437 ProcessOrientation::UpdatePredictedRotation(long now, int rotation)
michael@0 438 {
michael@0 439 if (mPredictedRotation != rotation) {
michael@0 440 mPredictedRotation = rotation;
michael@0 441 mPredictedRotationTimestampNanos = now;
michael@0 442 }
michael@0 443 }
michael@0 444
michael@0 445 bool
michael@0 446 ProcessOrientation::IsAccelerating(float magnitude)
michael@0 447 {
michael@0 448 return magnitude < MIN_ACCELERATION_MAGNITUDE
michael@0 449 || magnitude > MAX_ACCELERATION_MAGNITUDE;
michael@0 450 }
michael@0 451
michael@0 452 void
michael@0 453 ProcessOrientation::ClearTiltHistory()
michael@0 454 {
michael@0 455 mTiltHistory.history[0].timestampNanos = LONG_MIN;
michael@0 456 mTiltHistory.index = 1;
michael@0 457 }
michael@0 458
michael@0 459 void
michael@0 460 ProcessOrientation::AddTiltHistoryEntry(long now, float tilt)
michael@0 461 {
michael@0 462 mTiltHistory.history[mTiltHistory.index].tiltAngle = tilt;
michael@0 463 mTiltHistory.history[mTiltHistory.index].timestampNanos = now;
michael@0 464 mTiltHistory.index = (mTiltHistory.index + 1) % TILT_HISTORY_SIZE;
michael@0 465 mTiltHistory.history[mTiltHistory.index].timestampNanos = LONG_MIN;
michael@0 466 }
michael@0 467
michael@0 468 bool
michael@0 469 ProcessOrientation::IsFlat(long now)
michael@0 470 {
michael@0 471 for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
michael@0 472 if (mTiltHistory.history[i].tiltAngle < FLAT_ANGLE) {
michael@0 473 break;
michael@0 474 }
michael@0 475 if (mTiltHistory.history[i].timestampNanos + FLAT_TIME_NANOS <= now) {
michael@0 476 // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
michael@0 477 return true;
michael@0 478 }
michael@0 479 }
michael@0 480 return false;
michael@0 481 }
michael@0 482
michael@0 483 bool
michael@0 484 ProcessOrientation::IsSwinging(long now, float tilt)
michael@0 485 {
michael@0 486 for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
michael@0 487 if (mTiltHistory.history[i].timestampNanos + SWING_TIME_NANOS < now) {
michael@0 488 break;
michael@0 489 }
michael@0 490 if (mTiltHistory.history[i].tiltAngle + SWING_AWAY_ANGLE_DELTA <= tilt) {
michael@0 491 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
michael@0 492 return true;
michael@0 493 }
michael@0 494 }
michael@0 495 return false;
michael@0 496 }
michael@0 497
michael@0 498 int
michael@0 499 ProcessOrientation::NextTiltHistoryIndex(int index)
michael@0 500 {
michael@0 501 index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
michael@0 502 return mTiltHistory.history[index].timestampNanos != LONG_MIN ? index : -1;
michael@0 503 }
michael@0 504
michael@0 505 float
michael@0 506 ProcessOrientation::RemainingMS(long now, long until)
michael@0 507 {
michael@0 508 return now >= until ? 0 : (until - now) * 0.000001f;
michael@0 509 }
michael@0 510
michael@0 511 } // namespace mozilla

mercurial