Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added storage of the callback and threshold. #38

Open
wants to merge 7 commits into
base: 1.1.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 57 additions & 29 deletions sample/simple-accelerometer/src/accel.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ typedef struct {
int **normalized_recording;

moving_avg_values **moving_avg_values;
int *affinities;
int *offsets;
} accel_gesture;

typedef struct internalAccelState {
Expand All @@ -59,7 +59,7 @@ typedef struct internalAccelState {
if (state_$->state->gestures != NULL && state_$->state->num_gestures_saved == 0) { return ACCEL_INTERNAL_ERROR; }

// Decay rate of values we choose to keep. 1.0 is no decay, 2.0 is a doubling every time we keep them.
// TODO: should we store the affinities as floats instead?
// TODO: should we store the offsets as floats instead?
#define ALPHA 1.0

// TODO: include these from a header file?
Expand Down Expand Up @@ -94,9 +94,9 @@ void accel_destroy_gesture(accel_gesture **gesture, int dimensions) {
free(gest->normalized_recording);
gest->normalized_recording = NULL;
}
if (gest->affinities != NULL) {
free(gest->affinities);
gest->affinities = NULL;
if (gest->offsets != NULL) {
free(gest->offsets);
gest->offsets = NULL;
}

free(*gesture);
Expand Down Expand Up @@ -258,6 +258,17 @@ int normalize(int sum) {
return (int) cbrt(sum);
}

int reset_gesture(accel_gesture *gest, const int dimensions) {
PRECONDITION_NOT_NULL(gest);
for (int i=0; i<gest->recording_size; ++i) {
gest->offsets[i] = INT16_MAX;
}
for (int d=0; d<dimensions; ++d) {
reset_moving_avg(gest->moving_avg_values[d]);
}
return ACCEL_SUCCESS;
}

// TODO: does this work for zero recorded timestamps?
int accel_end_record_gesture(accel_state *state, int gesture_id) {
PRECONDITION_VALID_STATE(state);
Expand All @@ -284,21 +295,21 @@ int accel_end_record_gesture(accel_state *state, int gesture_id) {
return ACCEL_PARAM_ERROR;
}

gesture->affinities = (int *) malloc(gesture->recording_size * sizeof(int));
if (gesture->affinities == NULL) {
gesture->offsets = (int *) malloc(gesture->recording_size * sizeof(int));
if (gesture->offsets == NULL) {
return ACCEL_MALLOC_ERROR;
}

gesture->is_recording = false;
gesture->is_recorded = true;

for (int i=0; i<gesture->recording_size; ++i) {
gesture->affinities[i] = INT16_MAX;
}
for (int d=0; d<state->dimensions; ++d) {
reset_moving_avg(gesture->moving_avg_values[d]);
int reset_result = reset_gesture(gesture, state->dimensions);
if (reset_result != ACCEL_SUCCESS) {
free(gesture->offsets);
gesture->offsets = NULL;
} else {
gesture->is_recording = false;
gesture->is_recorded = true;
}
return ACCEL_SUCCESS;

return reset_result;
}

// TODO: check for malloc failure in this function.
Expand Down Expand Up @@ -330,7 +341,7 @@ int handle_evaluation_tick(accel_gesture *gesture, int dimensions) {
PRECONDITION_NOT_NULL(gesture);

if (gesture->moving_avg_values == NULL ||
gesture->affinities == NULL) {
gesture->offsets == NULL) {
return ACCEL_INTERNAL_ERROR;
}

Expand All @@ -353,9 +364,9 @@ int handle_evaluation_tick(accel_gesture *gesture, int dimensions) {
}
}
if (i == 0) {
gesture->affinities[i] = cost;
gesture->offsets[i] = cost;
} else {
gesture->affinities[i] = MIN(ALPHA * gesture->affinities[i], cost+gesture->affinities[i-1]);
gesture->offsets[i] = MIN(ALPHA * gesture->offsets[i], cost+gesture->offsets[i-1]);
}
}
for (i=1; i<gesture->recording_size; ++i) {
Expand All @@ -372,7 +383,7 @@ int handle_evaluation_tick(accel_gesture *gesture, int dimensions) {
cost += input_i_d - recording_i_d;
}
}
gesture->affinities[i] = MIN(gesture->affinities[i], gesture->affinities[i-1] + cost);
gesture->offsets[i] = MIN(gesture->offsets[i], gesture->offsets[i-1] + cost);
}
return ACCEL_SUCCESS;
}
Expand Down Expand Up @@ -425,13 +436,13 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) {
return retcode;
}

int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *affinity) {
int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *offset) {
PRECONDITION_VALID_STATE(state);
PRECONDITION_NOT_NULL(gesture_id);
PRECONDITION_NOT_NULL(affinity);
PRECONDITION_NOT_NULL(offset);

*gesture_id = ACCEL_NO_VALID_GESTURE;
*affinity = ACCEL_NO_VALID_GESTURE;
*offset = ACCEL_NO_VALID_GESTURE;

if (state->state->num_gestures_saved < 0) {
return ACCEL_INTERNAL_ERROR;
Expand All @@ -453,8 +464,8 @@ int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *aff
return ACCEL_INTERNAL_ERROR;
}

if ((*gesture_id == ACCEL_NO_VALID_GESTURE || *affinity == ACCEL_NO_VALID_GESTURE) &&
*gesture_id != *affinity) {
if ((*gesture_id == ACCEL_NO_VALID_GESTURE || *offset == ACCEL_NO_VALID_GESTURE) &&
*gesture_id != *offset) {
return ACCEL_INTERNAL_ERROR;
}

Expand All @@ -468,15 +479,32 @@ int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *aff
continue;
}

if (*affinity == ACCEL_NO_VALID_GESTURE ||
gesture->affinities[gesture->recording_size-1] < *affinity) {
*affinity = gesture->affinities[gesture->recording_size-1];
if (*offset == ACCEL_NO_VALID_GESTURE ||
gesture->offsets[gesture->recording_size-1] < *offset) {
*offset = gesture->offsets[gesture->recording_size-1];
*gesture_id = i;
}
}
if (*gesture_id == ACCEL_NO_VALID_GESTURE ||
*affinity == ACCEL_NO_VALID_GESTURE) {
*offset == ACCEL_NO_VALID_GESTURE) {
return ACCEL_NO_VALID_GESTURE;
}
return ACCEL_SUCCESS;
}

int accel_reset_affinities_for_gesture(accel_state *state, int gesture_id) {
PRECONDITION_VALID_STATE(state);
internal_accel_state *ias = state->state;
if (ias->num_gestures_saved <= gesture_id || gesture_id < 0) {
return ACCEL_PARAM_ERROR;
}
accel_gesture *gest = ias->gestures[gesture_id];
if (gest == NULL) {
return ACCEL_INTERNAL_ERROR;
}
if (!gest->is_recorded || gest->is_recording) {
// Gesture is in the wrong state for resetting.
return ACCEL_PARAM_ERROR;
}
return reset_gesture(gest, state->dimensions);
}
55 changes: 51 additions & 4 deletions sample/simple-accelerometer/src/accel.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

#include <stdbool.h>

#include "accel_consts.c"

#define ACCEL_SUCCESS 0
#define ACCEL_PARAM_ERROR -1
#define ACCEL_INTERNAL_ERROR -2
Expand All @@ -24,10 +22,49 @@ typedef struct {
struct internalAccelState *state;
} accel_state;

/**
* Callback called whenever a given gesture drops below the offset/length
* threshold specified when the state is initialized.
*
* A simple accel_callback is as follows:
*
* const int my_callback(accel_state *state, int gesture_id, int offset_found, bool *reset_gesture) {
* int retval = ACCEL_SUCCESS;
* if (gesture_id == 1) {
* *reset_gesture = true;
* ...
* } else {
* logger->info("unrecognized gesture %i ", gesture_id);
* retval = ACCEL_MIN_RESERVED - 1;
* }
* return retval;
* }
*
* For the callback method, the documentation is as follows:
* @param state A non-NULL pointer to a state variable that holds
* recording metadata.
* @param gesture_id The identifier of the gesture that has been
* triggered.
* @param offset_found The offset of the triggered gesture_id to the
* recorded gesture.
* @param reset_gesture Setting reset_gesture to be true will result in the
* gesture being reset after the callback is triggered,
* and setting it to false will prevent the gesture
* from being reset. No default value is promised.
* @return int Returns ACCEL_SUCCESS if successful. Values that are
* not ACCEL_SUCCESS will cause the calling method to
* immediately abort and proxy-return the value
* returned by the callback.
* Implementers wishing to return a custom value should
* refer to the ACCEL_MIN_RESERVED definition inside
* their implementations.
*/
typedef const int (*accel_callback)(accel_state *state, int gesture_id, int offset_found, bool *reset_gesture);

/**
* Creates a state object, essentially a constructor.
* @param state Pointer-to-pointer of the state being generated, populated
* by the method.
* @param state Pointer-to-pointer of the state being generated,
* populated by the method.
* The current value of the pointer's pointed (*state) must
* be NULL.
* @param dimensions The number of dimensions of input that the state
Expand Down Expand Up @@ -88,4 +125,14 @@ int accel_process_timer_tick(accel_state *state, int *accel_data);
*/
int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *distance);

/**
* For a given state and recorded gesture, resets the gesture's offset state
* entirely.
* @param state A pointer to a non-NULL state variable that holds recording
* metadata.
* @param gesture_id Value that corresponds to a gesture currently being reset.
* @return ACCEL_SUCCESS if successful, an error code otherwise.
*/
int accel_reset_affinities_for_gesture(accel_state *state, int gesture_id);

#endif
Loading