blob: 643de8662d50a0a31503217333c735e4752b7c31 [file] [log] [blame]
//*****************************************************************************
//
//! @file am_util_tap_detect.c
//!
//! @brief Tap Gesture Detector
//!
//! These functions implement the tap detector utility
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2017, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision v1.2.10-2-gea660ad-hotfix2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include <am_util_tap_detect.h>
#define USE_L2_NORM
//#define DEBUG_TAP_DETECTOR
//*****************************************************************************
//
//! @brief Initialize tap detector structure
//!
//! @param tap is a pointer to the tap detector structure
//! @param dp_min_seconds minimum time to detect double (or triple) tap
//! @param dp_max_seconds maximum time to detect double (or triple) tap
//! @param srate is the sample rate at which the accel runs typically 400 or 200
//! @param slope_thresh is the sensitivity setting for tap detection, typ 800
//!
//! This function initializes the tap detector structure and sets various
//! settings, e.g. min/max times for classifying single, double or triple taps.
//! In addition the structures tells the tap detector how long one sample is
//! in time. Finally, it specifies the sensitiviy of tap detection by setting
//! a minimimum slope threshold to signal tap detections.
//!
//! returns nothing
//
//*****************************************************************************
void
am_util_tap_detect_init(am_util_tap_detect_t * tap,
float dp_min_seconds,
float dp_max_seconds,
float srate,
float slope_thresh)
{
tap->prev_accX = 0;
tap->prev_accY = 0;
tap->prev_accZ = 0;
tap->sample_count = 0;
tap->start_flag = true;
#ifndef USE_L2_NORM
tap->SlopeThreshold = slope_thresh;
#else
tap->SlopeThreshold = slope_thresh*slope_thresh;
#endif
tap->previous_peak_location = -10000000;
tap->sample_rate = srate;
tap->previous_tap_was_dbl_tap = 0;
tap->peak_min_width_seconds = dp_min_seconds;
tap->group_peak_max_threshold_seconds = dp_max_seconds;
//convert to samples
tap->peak_min_width_samples = srate*tap->peak_min_width_seconds;
tap->group_peak_max_threshold = srate*tap->group_peak_max_threshold_seconds;
tap->max_mag = 0;
}
//*****************************************************************************
//
//! @brief Print the contents of the Tap Detector Structure
//!
//! @param tap is a pointer to the tap detector structure
//!
//! This function will print the contents of the tap detector structure if
//! needed for debug.
//!
//! returns nothing
//
//*****************************************************************************
#ifdef DEBUG_TAP_DETECTOR
void
am_util_tap_detect_print(am_util_tap_detect_t * tap)
{
am_util_stdio_printf("Sampling Rate = %d\n", (int)tap->sample_rate);
am_util_stdio_printf("SlopeThreshold = %d\n", (int)tap->SlopeThreshold);
//am_util_stdio_printf("DoublePeak min seconds = %f\n",
// tap->double_peak_min_threshold_seconds);
//am_util_stdio_printf("DoublePeak max seconds = %f\n",
// tap->double_peak_max_threshold_seconds);
am_util_stdio_printf("DoublePeak min samples = %i\n",
tap->double_peak_min_threshold);
am_util_stdio_printf("DoublePeak max samples = %i\n",
tap->double_peak_max_threshold);
am_util_stdio_printf("Start Flag = %i\n", tap->start_flag);
// new stuff below
printf("Sampling Rate = %f\n", tap->sample_rate);
printf("SlopeThreshold = %f\n", tap->SlopeThreshold);
printf("DoublePeak min seconds = %f\n", tap->peak_min_width_seconds);
printf("DoublePeak max seconds = %f\n", tap->group_peak_max_threshold_seconds);
printf("DoublePeak min samples = %i\n", tap->peak_min_width_samples);
printf("DoublePeak max samples = %i\n", tap->group_peak_max_threshold);
}
#endif
//*****************************************************************************
//
//! @brief Process One Sample (Triplet) Through the Tap Dector
//!
//! @param tap is a pointer to the tap detector structure
//! @param accX Accelerometer X axis value of a triplet
//! @param accY Accelerometer Y axis value of a triplet
//! @param accZ Accelerometer Z axis value of a triplet
//!
//! This function utilizes the tap detector structure in conjunction with sample
//! counting to establish all necessary timing.
//!
//! @return NO_TAP, TAP_OCCURED, TAP, DOUBLE, TRIPLE
//
//*****************************************************************************
am_util_tap_detect_enum_t
am_util_tap_detect_process_sample(am_util_tap_detect_t * tap,
short accX, short accY, short accZ)
{
am_util_tap_detect_enum_t out = NO_TAP_DETECTED;
static int tap_occured = 0;
//initialize the first previous sample
if ( tap->start_flag )
{
tap->start_flag = false;
tap->prev_accX = accX;
tap->prev_accY = accY;
tap->prev_accZ = accZ;
}
//Feature Extract ----------------------------------------------------
//get the first derivative
float axx = accX - tap->prev_accX;
float ayy = accY - tap->prev_accY;
float azz = accZ - tap->prev_accZ;
//get the magnitude of the partial derivatives
//NOTE: do not need the sqrt!!! This is a lot of cycles!
#ifndef USE_L2_NORM
float mag_sample = sqrt((axx*axx) + (ayy*ayy) + (azz*azz));
#else
float mag_sample = ((axx*axx) + (ayy*ayy) + (azz*azz));
#endif
//PEAK DETECTION **********************************************************
if ( mag_sample > tap->SlopeThreshold )
{
int distance = (tap->sample_count - tap->previous_peak_location);
//OK detect a standard tape
if ( distance > tap->peak_min_width_samples ) //should be min peak width
{
// returned only for first tap event, TAP, DOUBLE or TRIPLE over writes
out = TAP_OCCURED;
tap_occured++;
}
//record where this peak occured
tap->previous_peak_location = tap->sample_count;
//these are handy for debugging and tuning
if ( mag_sample > tap->max_mag )
{
tap->max_mag = mag_sample;
}
tap->dist = distance;
}
//GROUPING CLASSIFICATION OF SINGLE, DOUBLE, AND TRIPLE TAPS
//*******************************************************************************
//These are grouping cases where we report a single, double, or triple tap
// If a tap is within group_peak_max_threshold, than is forms a group of taps such as DOULBLE or TRIPLE
// otherwise the tap is just a single tap
int dist_classification = (tap->sample_count - tap->previous_peak_location);
if ( (tap_occured == 1) && (dist_classification > tap->group_peak_max_threshold) )
{
out = TAP_DETECTED;
tap_occured = 0;
}
if ( (tap_occured == 2) && (dist_classification > tap->group_peak_max_threshold) )
{
out = DOUBLE_TAP_DETECTED;
tap_occured = 0;
}
if ( tap_occured == 3 )
{
out = TRIPLE_TAP_DETECTED;
tap_occured = 0;
}
//*******************************************************************************
tap->mag = mag_sample;
//store for next sample --------------------------------------------------
tap->prev_accX = accX;
tap->prev_accY = accY;
tap->prev_accZ = accZ;
//sample_count keeps track of time!!!-------------------------------------
tap->sample_count++;
return out;
}