/** * Updates the basic gesture recognizer. This performs instantaneous pose recognition, and also some low pass filtering to promote * stability. */ void MicroBitAccelerometer::updateGesture() { // Check for High/Low G force events - typically impulses, impacts etc. // Again, during such spikes, these event take priority of the posture of the device. // For these events, we don't perform any low pass filtering. int force = instantaneousAccelerationSquared(); if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD) { if (force > MICROBIT_ACCELEROMETER_3G_THRESHOLD && !shake.impulse_3) { MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_3G); shake.impulse_3 = 1; } if (force > MICROBIT_ACCELEROMETER_6G_THRESHOLD && !shake.impulse_6) { MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_6G); shake.impulse_6 = 1; } if (force > MICROBIT_ACCELEROMETER_8G_THRESHOLD && !shake.impulse_8) { MicroBitEvent e(MICROBIT_ID_GESTURE, MICROBIT_ACCELEROMETER_EVT_8G); shake.impulse_8 = 1; } impulseSigma = 0; } // Reset the impulse event onve the acceleration has subsided. if (impulseSigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING) impulseSigma++; else shake.impulse_3 = shake.impulse_6 = shake.impulse_8 = 0; // Determine what it looks like we're doing based on the latest sample... uint16_t g = instantaneousPosture(); // Perform some low pass filtering to reduce jitter from any detected effects if (g == currentGesture) { if (sigma < MICROBIT_ACCELEROMETER_GESTURE_DAMPING) sigma++; } else { currentGesture = g; sigma = 0; } // If we've reached threshold, update our record and raise the relevant event... if (currentGesture != lastGesture && sigma >= MICROBIT_ACCELEROMETER_GESTURE_DAMPING) { lastGesture = currentGesture; MicroBitEvent e(MICROBIT_ID_GESTURE, lastGesture); } }
/** * Service function. * Determines a 'best guess' posture of the device based on instantaneous data. * * This makes no use of historic data, and forms the input to the filter implemented in updateGesture(). * * @return A 'best guess' of the current posture of the device, based on instanataneous data. */ uint16_t MicroBitAccelerometer::instantaneousPosture() { bool shakeDetected = false; // Test for shake events. // We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by // a strong acceleration to the right, then we can infer a shake. Similarly, we can do this for each axis (left/right, up/down, in/out). // // If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device // has been shaken. if ((getX() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (getX() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x)) { shakeDetected = true; shake.x = !shake.x; } if ((getY() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (getY() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y)) { shakeDetected = true; shake.y = !shake.y; } if ((getZ() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (getZ() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z)) { shakeDetected = true; shake.z = !shake.z; } // If we detected a zero crossing in this sample period, count this. if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD) { shake.count++; if (shake.count == 1) shake.timer = 0; if (shake.count == MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD) { shake.shaken = 1; shake.timer = 0; return MICROBIT_ACCELEROMETER_EVT_SHAKE; } } // measure how long we have been detecting a SHAKE event. if (shake.count > 0) { shake.timer++; // If we've issued a SHAKE event already, and sufficient time has assed, allow another SHAKE event to be issued. if (shake.shaken && shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_RTX) { shake.shaken = 0; shake.timer = 0; shake.count = 0; } // Decay our count of zero crossings over time. We don't want them to accumulate if the user performs slow moving motions. else if (!shake.shaken && shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_DAMPING) { shake.timer = 0; if (shake.count > 0) shake.count--; } } if (instantaneousAccelerationSquared() < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD) return MICROBIT_ACCELEROMETER_EVT_FREEFALL; // Determine our posture. if (getX() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_LEFT; if (getX() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT; if (getY() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_DOWN; if (getY() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_UP; if (getZ() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_FACE_UP; if (getZ() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_FACE_DOWN; return MICROBIT_ACCELEROMETER_EVT_NONE; }
/** * Service function. * Determines a 'best guess' posture of the device based on instantaneous data. * * This makes no use of historic data, and forms the input to the filter implemented in updateGesture(). * * @return A 'best guess' of the current posture of the device, based on instanataneous data. */ uint16_t MicroBitAccelerometer::instantaneousPosture() { bool shakeDetected = false; // Test for shake events. // We detect a shake by measuring zero crossings in each axis. In other words, if we see a strong acceleration to the left followed by // a strong acceleration to the right, then we can infer a shake. Similarly, we can do this for each axis (left/right, up/down, in/out). // // If we see enough zero crossings in succession (MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD), then we decide that the device // has been shaken. if ((getX() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.x) || (getX() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.x)) { shakeDetected = true; shake.x = !shake.x; } if ((getY() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.y) || (getY() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.y)) { shakeDetected = true; shake.y = !shake.y; } if ((getZ() < -MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && shake.z) || (getZ() > MICROBIT_ACCELEROMETER_SHAKE_TOLERANCE && !shake.z)) { shakeDetected = true; shake.z = !shake.z; } if (shakeDetected && shake.count < MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD && ++shake.count == MICROBIT_ACCELEROMETER_SHAKE_COUNT_THRESHOLD) shake.shaken = 1; if (++shake.timer >= MICROBIT_ACCELEROMETER_SHAKE_DAMPING) { shake.timer = 0; if (shake.count > 0) { if(--shake.count == 0) shake.shaken = 0; } } // Shake events take the highest priority, as under high levels of change, other events // are likely to be transient. if (shake.shaken) return MICROBIT_ACCELEROMETER_EVT_SHAKE; if (instantaneousAccelerationSquared() < MICROBIT_ACCELEROMETER_FREEFALL_THRESHOLD) return MICROBIT_ACCELEROMETER_EVT_FREEFALL; // Determine our posture. if (getX() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_LEFT; if (getX() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_RIGHT; if (getY() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_DOWN; if (getY() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_TILT_UP; if (getZ() < (-1000 + MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_FACE_UP; if (getZ() > (1000 - MICROBIT_ACCELEROMETER_TILT_TOLERANCE)) return MICROBIT_ACCELEROMETER_EVT_FACE_DOWN; return MICROBIT_ACCELEROMETER_EVT_NONE; }