diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5dc68c0 --- /dev/null +++ b/.clang-format @@ -0,0 +1,52 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: true +AlwaysBreakTemplateDeclarations: false +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BinPackParameters: true +ColumnLimit: 120 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerBindsToType: false +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: true +Standard: Cpp11 +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +BreakBeforeBraces: Attach +IndentFunctionDeclarationAfterType: false +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 4 +CommentPragmas: '^ IWYU pragma:' +SpaceBeforeParens: ControlStatements +... + diff --git a/.gitignore b/.gitignore index 9fa3b1b..30950b2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ *.exe *.out *.app + +# Built folder(s) +bin diff --git a/.travis.yml b/.travis.yml index 6f726d7..0706d95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,7 @@ compiler: - gcc - clang script: - - cd test/ - make run -branches: - only: - - master - - 1.8.7 notifications: recipients: - shalecraig@gmail.com diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..97d0aa7 --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +# TODO: update this. +.PHONY = default echo/objects echo/sources list tests run bin clean + +SOURCES_C = $(wildcard src/*.c) +TEST_CC = $(wildcard test/*.cc) + +_SOURCES_OBJECTS_TMP = ${SOURCES_C:.c=.o} +_TEST_OBJECTS_TMP = ${TEST_CC:.cc=.o} +SOURCES_OBJECTS = $(subst src,bin,$(_SOURCES_OBJECTS_TMP)) +TEST_OBJECTS = $(subst test,bin,$(_TEST_OBJECTS_TMP)) + +EXEC = tests + +# removed: -Wl,-z,relro -Wl,-z,now +C_ARGS = -ansi -DIS_NOT_PEBBLE -DRELEASE -ffunction-sections -fno-exceptions -fPIC -fPIE -fstack-protector-all -fstrict-overflow -fvisibility=hidden -g -m64 -Os -pedantic-errors -pipe -std=c99 -W -Wall -Wbad-function-cast -Wc++-compat -Wcast-qual -Wcomment -Wconversion -Wdeprecated -Werror -Wextra -Wfloat-equal -Wformat -Wformat-nonliteral -Wformat-security -Wformat-y2k -Winit-self -Wmissing-include-dirs -Wmultichar -Wnested-externs -Wold-style-definition -Wpacked -Wpointer-arith -Wredundant-decls -Wreturn-type -Wsign-compare -Wstack-protector -Wstrict-prototypes -Wswitch-default -Wswitch-enum -Wtrigraphs -Wundef -Wuninitialized -Wuninitialized -Wunused-function -Wunused-label -Wunused-macros -Wunused-parameter -Wvariadic-macros -Wwrite-strings -Wlong-long -Wincompatible-pointer-types -Wtype-limits -Werror=type-limits +# removed: -lpthread, -Wl,-z,relro -Wl,-z,now -Wsign-conversion +CXX_ARGS = -DIS_NOT_PEBBLE -fno-exceptions -fPIC -fstack-protector -fvisibility=hidden -g -lpthread -m64 -O3 -pipe -W -Wall -Wextra -Wextra-tokens -Wconversion -Wformat -Wformat-nonliteral -Wformat-security -Winit-self -Wmultichar -Wno-deprecated -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wpointer-arith -Wreturn-type -Wsign-compare -Wuninitialized -Wcast-qual -Wsign-compare -Wno-sign-conversion -Wno-conversion + +default: + @make all + +echo/objects: + @echo ${SOURCES_OBJECTS} + @echo ${TEST_OBJECTS} + +echo/sources: + @echo ${SOURCES_C} + @echo ${TEST_CC} + +list: + @echo $(.PHONY) + +tests: bin/tests + +bin/tests: ${SOURCES_OBJECTS} ${TEST_OBJECTS} bin/libgtest.a bin + clang++ -Ilib/gtest-1.7.0/include ${SOURCES_OBJECTS} ${TEST_OBJECTS} bin/libgtest.a -lpthread -o bin/tests + +run: tests + ./bin/tests + +bin/%.o : src/%.c bin + clang ${C_ARGS} -c $< -o $@ + +bin/%.o : test/%.cc bin + clang++ -Ilib/gtest-1.7.0/include ${CXX_ARGS} -c $< -o $@ + +bin/libgtest.a: bin + clang++ -Ilib/gtest-1.7.0/include -Ilib/gtest-1.7.0 -c lib/gtest-1.7.0/src/gtest-all.cc -o bin/libgtest.a + +bin: + mkdir -p bin + +clean: + rm -rf bin diff --git a/s/hooks/auto/pre-commit/build.sh b/s/hooks/auto/pre-commit/build.sh new file mode 100755 index 0000000..bc80873 --- /dev/null +++ b/s/hooks/auto/pre-commit/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +pushd $(git rev-parse --show-toplevel || echo ".") + +make clean run + +popd diff --git a/s/hooks/auto/pre-commit/clang-format.sh b/s/hooks/auto/pre-commit/clang-format.sh new file mode 100755 index 0000000..6dee0bc --- /dev/null +++ b/s/hooks/auto/pre-commit/clang-format.sh @@ -0,0 +1,11 @@ +#!/bin/sh +set -e + +pushd $(git rev-parse --show-toplevel || echo ".") + +# Use clang-format on changed files. +git diff --name-only --cached | \ + grep -E '\.(h|c|cc|cpp)' | \ + xargs clang-format -i + +popd diff --git a/s/hooks/auto/pre-commit/update_demo.sh b/s/hooks/auto/pre-commit/update_demo.sh new file mode 100755 index 0000000..b47453b --- /dev/null +++ b/s/hooks/auto/pre-commit/update_demo.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +pushd $(git rev-parse --show-toplevel || echo ".") + +cp src/* sample/simple-accelerometer/src/ + +popd diff --git a/s/hooks/config/pre-commit b/s/hooks/config/pre-commit new file mode 100755 index 0000000..4766afa --- /dev/null +++ b/s/hooks/config/pre-commit @@ -0,0 +1,21 @@ +#!/bin/sh + +set -e + +echo ------------------------------------------- +echo -- Pre-commit -- +echo ------------------------------------------- + +# Goto root. +pushd $(git rev-parse --show-toplevel || echo ".") + +# Make the individual scripts runnable +find . -path ./s/hooks/auto/pre-commit/\*.sh -exec chmod +x "{}" \; + +find . -path ./s/hooks/auto/pre-commit/\*.sh -exec sh "{}" \; + +popd + +echo ------------------------------------------- +echo -- Done! -- +echo ------------------------------------------- diff --git a/s/setup.sh b/s/setup.sh new file mode 100755 index 0000000..5afcc89 --- /dev/null +++ b/s/setup.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +echo ------------------------------------------- +echo -- Git Hooks -- +echo ------------------------------------------- + +# Goto root. +pushd $(git rev-parse --show-toplevel || echo ".") + +find . -path ./s/hooks/config/\* -exec chmod +x "{}" \; +find . -path ./s/hooks/config/\* -exec cp "{}" .git/hooks \; + +popd + +echo ------------------------------------------- +echo -- Done! -- +echo ------------------------------------------- diff --git a/sample/simple-accelerometer/.gitignore b/sample/simple-accelerometer/.gitignore index 9ed9d4f..1ce1856 100644 --- a/sample/simple-accelerometer/.gitignore +++ b/sample/simple-accelerometer/.gitignore @@ -1,3 +1,5 @@ # Ignore build generated files build + +.lock* diff --git a/sample/simple-accelerometer/.lock-waf_linux2_build b/sample/simple-accelerometer/.lock-waf_linux2_build deleted file mode 100644 index aed0a81..0000000 --- a/sample/simple-accelerometer/.lock-waf_linux2_build +++ /dev/null @@ -1,8 +0,0 @@ -argv = ['/home/vagrant/pebble-dev/PebbleSDK-2.0-BETA5/Pebble/waf', 'configure', 'build'] -environ = {'XDG_RUNTIME_DIR': '/run/user/1000', 'LESS': '-R', 'LC_CTYPE': 'en_US.UTF-8', 'SSH_CLIENT': '10.0.2.2 49994 22', 'LSCOLORS': 'Gxfxcxdxbxegedabagacad', 'LOGNAME': 'vagrant', 'USER': 'vagrant', 'PATH': '/home/vagrant/pebble-dev/PebbleSDK-2.0-BETA5/arm-cs-tools/bin:/home/vagrant/bin:/usr/local/bin:/home/vagrant/pebble-dev/PebbleSDK-2.0-BETA5/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games', 'HOME': '/home/vagrant', 'XDG_SESSION_ID': '11', '_': '/home/vagrant/pebble-dev/PebbleSDK-2.0-BETA5/bin/pebble', 'LANG': 'en_US.UTF-8', 'TERM': 'xterm-256color', 'SHELL': '/bin/zsh', 'XDG_SESSION_COOKIE': '16ccae8f6a53e04feed02d6f52cb75dd-1390072954.343424-1847087206', 'SHLVL': '1', 'SSH_TTY': '/dev/pts/1', 'OLDPWD': '/home/vagrant', 'PWD': '/vagrant/mount/code/accel/sample/simple-accelerometer', 'GREP_OPTIONS': '--color=auto', 'MAIL': '/var/mail/vagrant', 'GREP_COLOR': '1;32', 'SSH_CONNECTION': '10.0.2.2 49994 10.0.2.15 22', 'PAGER': 'less'} -files = ['/vagrant/mount/code/accel/sample/simple-accelerometer/wscript'] -hash = -326433459 -options = {'files': '', 'jobs': 1, 'verbose': 0, 'nocache': False, 'progress_bar': 0, 'timestamp': None, 'distcheck_args': None, 'top': '', 'destdir': '', 'keep': 0, 'zones': '', 'debug': False, 'prefix': '/usr/local/', 'download': False, 'force': False, 'targets': '', 'out': ''} -out_dir = '/vagrant/mount/code/accel/sample/simple-accelerometer/build' -run_dir = '/vagrant/mount/code/accel/sample/simple-accelerometer' -top_dir = '/vagrant/mount/code/accel/sample/simple-accelerometer' diff --git a/sample/simple-accelerometer/src/accel.c b/sample/simple-accelerometer/src/accel.c index 78bb0cc..2880ba5 100644 --- a/sample/simple-accelerometer/src/accel.c +++ b/sample/simple-accelerometer/src/accel.c @@ -8,15 +8,11 @@ #include #include #else -#define my_realloc(a, b, c) realloc(a, b) -#define my_calloc(a, b) calloc(a, b) +#define my_realloc(a, b, c) (realloc(a, b)) +#define my_calloc(a, b) (calloc(a, b)) #endif -#ifndef INT16_MAX -#define INT16_MAX 0x7fff -#endif - -// cbrt is defined and importable for everybody! +// cbrt/abs is defined and importable for everybody! #include #include #include @@ -29,50 +25,73 @@ typedef struct { bool is_recording; bool is_recorded; - int recording_size; - int **normalized_recording; + // TODO: uint32_t needed instead? + uint16_t recording_size; + int32_t **normalized_recording; moving_avg_values **moving_avg_values; - int *affinities; + int32_t *offsets; } accel_gesture; typedef struct internalAccelState { - int window_size; - int num_gestures_saved; + // TODO: uint32_t needed instead? + uint16_t num_gestures_saved; + uint16_t window_size; + uint32_t threshold; accel_gesture **gestures; } internal_accel_state; -#define PRECONDITION_NOT_NULL(foo_$) \ - if (foo_$ == NULL) { return ACCEL_PARAM_ERROR; } +#define PRECONDITION_NOT_NULL(INPUT) \ + if (INPUT == NULL) { \ + return ACCEL_PARAM_ERROR; \ + } + +#define PRECONDITION_NULL(INPUT) \ + if (INPUT != NULL) { \ + return ACCEL_PARAM_ERROR; \ + } + +#define PRECONDITION_VALID_STATE(INPUT_STATE) \ + if (INPUT_STATE == NULL) { \ + return ACCEL_PARAM_ERROR; \ + } \ + if (INPUT_STATE->state == NULL) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->dimensions <= 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->state->window_size <= 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->state->gestures == NULL && INPUT_STATE->state->num_gestures_saved != 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->state->gestures != NULL && INPUT_STATE->state->num_gestures_saved == 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } +// if (INPUT_STATE->state->num_gestures_saved < 0) { +// return ACCEL_INTERNAL_ERROR; +// } + +#define PRECONDITION_TRUE_PARAM(TRUE_COND) \ + { \ + if (!TRUE_COND) { \ + return ACCEL_PARAM_ERROR; \ + } \ + } -#define PRECONDITION_NULL(foo_$) \ - if (foo_$ != NULL) { return ACCEL_PARAM_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 offsets as floats instead? +#define ALPHA ((float)1.0) -#define PRECONDITION_VALID_STATE(state_$) \ - if (state_$ == NULL) { return ACCEL_PARAM_ERROR; } \ - if (state_$->state == NULL) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->dimensions <= 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->window_size <= 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->num_gestures_saved < 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->gestures == NULL && state_$->state->num_gestures_saved != 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->gestures != NULL && state_$->state->num_gestures_saved == 0) { return ACCEL_INTERNAL_ERROR; } +// TODO: include this from a header file? +// TODO: include as a static inline function? +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +// #define MAX(a,b) (((a)>(b))?(a):(b)) -// 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? -#define ALPHA 1.0 - -// TODO: include these from a header file? -#define MAX(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a > _b ? _a : _b; }) -#define MIN(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; }) - -void accel_destroy_gesture(accel_gesture **gesture, int dimensions) { +void accel_destroy_gesture(accel_gesture **gesture, uint32_t dimensions) { if (gesture == NULL || *gesture == NULL) { return; } @@ -80,12 +99,12 @@ void accel_destroy_gesture(accel_gesture **gesture, int dimensions) { accel_gesture *gest = *gesture; if (gest->moving_avg_values != NULL) { - for (int i=0; imoving_avg_values[i])); } } if (gest->normalized_recording != NULL) { - for (int i=0; irecording_size; ++i) { + for (uint16_t i = 0; i < gest->recording_size; ++i) { if (gest->normalized_recording[i] != NULL) { free(gest->normalized_recording[i]); gest->normalized_recording[i] = NULL; @@ -94,16 +113,15 @@ 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); *gesture = NULL; } - int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { PRECONDITION_VALID_STATE(state); PRECONDITION_NOT_NULL(gesture); @@ -112,7 +130,7 @@ int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { PRECONDITION_NULL((*gesture)); size_t gesture_size = sizeof(accel_gesture); - *gesture = (accel_gesture *) malloc(gesture_size); + *gesture = (accel_gesture *)malloc(gesture_size); if (*gesture == NULL) { return ACCEL_MALLOC_ERROR; } @@ -121,18 +139,19 @@ int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { (*gesture)->is_recorded = false; (*gesture)->normalized_recording = NULL; - (*gesture)->moving_avg_values = (moving_avg_values **) my_calloc(state->dimensions, sizeof(moving_avg_values *)); + (*gesture)->moving_avg_values = (moving_avg_values **)my_calloc(state->dimensions, sizeof(moving_avg_values *)); if ((*gesture)->moving_avg_values == NULL) { free((*gesture)); *gesture = NULL; return ACCEL_MALLOC_ERROR; } - for (int i=0; idimensions; ++i) { + for (uint32_t i = 0; i < state->dimensions; ++i) { // TODO: these two shouldn't both be the same.... - int result = allocate_moving_avg(state->state->window_size, state->state->window_size, &((*gesture)->moving_avg_values[i])); + int result = allocate_moving_avg(state->state->window_size, state->state->window_size, + &((*gesture)->moving_avg_values[i])); if (result != ACCEL_SUCCESS) { - for (int j=0; jmoving_avg_values[i])); } accel_destroy_gesture(gesture, state->dimensions); @@ -142,25 +161,31 @@ int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { return ACCEL_SUCCESS; } -// TODO: needs direct testing with invalid objects. -int accel_generate_state(accel_state **state, int dimensions, int window_size) { +int accel_generate_state(accel_state **state, uint32_t dimensions, uint16_t window_size, accel_callback callback, + const uint32_t threshold) { PRECONDITION_NOT_NULL(state); // TODO: write a test for this value. PRECONDITION_NULL(*state); - if (dimensions <= 0) { + if (dimensions == 0) { return ACCEL_PARAM_ERROR; } if (window_size <= 0) { return ACCEL_PARAM_ERROR; } + if (threshold == 0 && callback != NULL) { + return ACCEL_PARAM_ERROR; + } + if (threshold != 0 && callback == NULL) { + return ACCEL_PARAM_ERROR; + } size_t state_size = sizeof(accel_state); size_t internal_state_size = sizeof(internal_accel_state); - internal_accel_state *internal_state = (internal_accel_state *) malloc(internal_state_size); - *state = (accel_state *) malloc(state_size); + internal_accel_state *internal_state = (internal_accel_state *)malloc(internal_state_size); + *state = (accel_state *)malloc(state_size); if (*state == NULL || internal_state == NULL) { if (state != NULL) { free(*state); @@ -178,9 +203,10 @@ int accel_generate_state(accel_state **state, int dimensions, int window_size) { (*state)->state = internal_state; - + (*state)->callback = callback; (*state)->dimensions = dimensions; (*state)->state->window_size = window_size > 0 ? window_size : 2; + (*state)->state->threshold = threshold; return ACCEL_SUCCESS; } @@ -189,12 +215,12 @@ int accel_destroy_state(accel_state **state) { PRECONDITION_NOT_NULL(state); PRECONDITION_NOT_NULL(*state); - int dimensions = (*state)->dimensions; + uint32_t dimensions = (*state)->dimensions; if ((*state)->state != NULL) { internal_accel_state *istate = (*state)->state; if (istate->gestures != NULL) { /* TODO: remove all additional fields inside the accel_state variable */ - for (int i=0; inum_gestures_saved; ++i) { + for (uint16_t i = 0; i < istate->num_gestures_saved; ++i) { accel_gesture *gest = (istate->gestures[i]); accel_destroy_gesture(&(gest), dimensions); } @@ -211,12 +237,14 @@ int accel_destroy_state(accel_state **state) { return ACCEL_SUCCESS; } -int accel_start_record_gesture(accel_state *state, int *gesture) { +int accel_start_record_gesture(accel_state *state, uint16_t *gesture) { PRECONDITION_VALID_STATE(state); PRECONDITION_NOT_NULL(gesture); if (state->state->num_gestures_saved != 0) { - accel_gesture **tmp = (accel_gesture **)my_realloc(state->state->gestures, (state->state->num_gestures_saved + 1)*sizeof(accel_gesture *), (state->state->num_gestures_saved)*sizeof(accel_gesture *)); + accel_gesture **tmp = (accel_gesture **)my_realloc( + state->state->gestures, (state->state->num_gestures_saved + 1) * sizeof(accel_gesture *), + (state->state->num_gestures_saved) * sizeof(accel_gesture *)); if (tmp == NULL) { return ACCEL_MALLOC_ERROR; } @@ -233,12 +261,12 @@ int accel_start_record_gesture(accel_state *state, int *gesture) { int result = accel_generate_gesture(state, &(state->state->gestures[*gesture])); if (result != ACCEL_SUCCESS) { - *gesture = -1; if (state->state->num_gestures_saved == 1) { free(state->state->gestures); state->state->gestures = NULL; } else { - accel_gesture ** tmp = (accel_gesture **)my_realloc(state->state->gestures, state->state->num_gestures_saved - 1, state->state->num_gestures_saved); + accel_gesture **tmp = (accel_gesture **)my_realloc( + state->state->gestures, state->state->num_gestures_saved - 1, state->state->num_gestures_saved); if (tmp != NULL) { // If tmp is null, we don't really care that realloc failed, since a future use of realloc will help us. state->state->gestures = tmp; @@ -251,22 +279,52 @@ int accel_start_record_gesture(accel_state *state, int *gesture) { return ACCEL_SUCCESS; } +// Taken from hackers delight +// http://www.hackersdelight.org/hdcodetxt/icbrt.c.txt +uint32_t icbrt1(uint32_t x) { + int32_t s; + uint32_t y, b; + + y = 0; + for (s = 30; s >= 0; s = s - 3) { + y = 2 * y; + b = (3 * y * (y + 1) + 1) << s; + if (x >= b) { + x = x - b; + y = y + 1; + } + } + return y; +} + // The uWave paper suggests a mapping from [-20, 20]->[-15, 15], but cube root // should to work better for variable ranges. // TODO: revisit this decision. -int normalize(int sum) { - return (int) cbrt(sum); +int32_t normalize(int32_t sum) { + if (sum < 0) { + return (int)-cbrtf(-((float)sum)); + } + return -(int)-cbrtf(((float)sum)); +} + +int reset_gesture(accel_gesture *gest, const uint32_t dimensions) { + PRECONDITION_NOT_NULL(gest); + for (uint16_t i = 0; i < gest->recording_size; ++i) { + gest->offsets[i] = INT16_MAX; + } + for (uint32_t 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) { +int accel_end_record_gesture(accel_state *state, uint16_t gesture_id) { PRECONDITION_VALID_STATE(state); - // TODO: use an unsigned int instead so we don't need to check for this type of error. - if (gesture_id < 0) { - return ACCEL_PARAM_ERROR; - } internal_accel_state *istate = state->state; + // TODO: gesture_id == istate->num_gestures_saved is incorrect. + // Write a test for it and fix it. if (gesture_id > istate->num_gestures_saved) { return ACCEL_PARAM_ERROR; } @@ -284,64 +342,76 @@ 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 = (int32_t *)malloc(gesture->recording_size * sizeof(int32_t)); + if (gesture->offsets == NULL) { return ACCEL_MALLOC_ERROR; } - gesture->is_recording = false; - gesture->is_recorded = true; + 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; + } - for (int i=0; irecording_size; ++i) { - gesture->affinities[i] = INT16_MAX; + for (uint16_t i = 0; i < gesture->recording_size; ++i) { + gesture->offsets[i] = INT16_MAX; } - for (int d=0; ddimensions; ++d) { + for (uint32_t d = 0; d < state->dimensions; ++d) { reset_moving_avg(gesture->moving_avg_values[d]); } return ACCEL_SUCCESS; } -// TODO: check for malloc failure in this function. +// TODO: gracefully handle malloc failure in this function. // TODO: this should return error types instead of being void. - // Follow-up: find usages of this method. -void handle_recording_tick(accel_gesture *gesture, int dimensions) { - if (gesture == NULL) { return; } +// Follow-up: find usages of this method. +void handle_recording_tick(accel_gesture *gesture, uint32_t dimensions) { + if (gesture == NULL) { + return; + } // TODO: grow exponentially, not linearly. Linear growth allocates too frequently. if (gesture->recording_size != 0) { - gesture->normalized_recording = (int **) my_realloc(gesture->normalized_recording, (gesture->recording_size + 1) * sizeof(int *), gesture->recording_size * sizeof(int *)); + gesture->normalized_recording = + (int32_t **)my_realloc(gesture->normalized_recording, (gesture->recording_size + 1) * sizeof(int32_t *), + gesture->recording_size * sizeof(int32_t *)); if (gesture->normalized_recording == NULL) { return; } } else { - gesture->normalized_recording = (int **) malloc(sizeof(int *)); + gesture->normalized_recording = (int32_t **)malloc(sizeof(int32_t *)); } - gesture->normalized_recording[gesture->recording_size] = (int *) malloc(sizeof(int) * dimensions); - for (int i=0; inormalized_recording[gesture->recording_size] = (int32_t *)malloc(sizeof(int32_t) * dimensions); + for (uint32_t i = 0; i < dimensions; ++i) { // TODO: fix this int/float business. - // TODO: check resultant output. - get_latest_frame_moving_avg(gesture->moving_avg_values[i], &(gesture->normalized_recording[gesture->recording_size][i])); - gesture->normalized_recording[gesture->recording_size][i] = normalize(gesture->normalized_recording[gesture->recording_size][i]); + // TODO: complain about invalid return values. + get_latest_frame_moving_avg(gesture->moving_avg_values[i], + &(gesture->normalized_recording[gesture->recording_size][i])); + gesture->normalized_recording[gesture->recording_size][i] = + normalize(gesture->normalized_recording[gesture->recording_size][i]); } - ++gesture->recording_size; + ++(gesture->recording_size); } -int handle_evaluation_tick(accel_gesture *gesture, int dimensions) { +int handle_evaluation_tick(accel_state *state, accel_gesture *gesture, uint16_t gesture_id) { // TODO: load the input at the beginning instead of gesture->recording_size times. PRECONDITION_NOT_NULL(gesture); + uint32_t dimensions = state->dimensions; - if (gesture->moving_avg_values == NULL || - gesture->affinities == NULL) { + if (gesture->moving_avg_values == NULL || gesture->offsets == NULL) { return ACCEL_INTERNAL_ERROR; } - int i = gesture->recording_size; + uint16_t i = gesture->recording_size; while (i != 0) { --i; int cost = 0; - for (int d=0; dnormalized_recording[i][d]; - int input_i_d = 0; + int32_t input_i_d = 0; // TODO: complain about invalid return values. get_latest_frame_moving_avg(gesture->moving_avg_values[d], &input_i_d); input_i_d = normalize(input_i_d); @@ -353,16 +423,16 @@ 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((int)(ALPHA * gesture->offsets[i]), cost + gesture->offsets[i - 1]); } } - for (i=1; irecording_size; ++i) { + for (i = 1; i < gesture->recording_size; ++i) { int cost = 0; - for (int d=0; dnormalized_recording[i][d]; - int input_i_d = 0; + int32_t input_i_d = 0; // TODO: complain about invalid return values. get_latest_frame_moving_avg(gesture->moving_avg_values[d], &input_i_d); if (recording_i_d > input_i_d) { @@ -372,7 +442,24 @@ 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); + } + if (state->callback != NULL) { + if (state->state->threshold == 0) { + return ACCEL_PARAM_ERROR; + } + float avg_affinity = ((float)gesture->offsets[gesture->recording_size - 1]) / gesture->recording_size; + if (avg_affinity < 0) { + return ACCEL_INTERNAL_ERROR; + } + if (avg_affinity < state->state->threshold) { + bool reset; + int retval = state->callback(state, gesture_id, (uint32_t)(avg_affinity), &reset); + if (reset == true) { + reset_gesture(gesture, dimensions); + } + return retval; + } } return ACCEL_SUCCESS; } @@ -382,8 +469,8 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) { PRECONDITION_NOT_NULL(accel_data); int retcode = ACCEL_SUCCESS; - for (int gesture_iter = 0; gesture_iter < state->state->num_gestures_saved; ++gesture_iter) { - accel_gesture *gesture = state->state->gestures[gesture_iter]; + for (uint16_t gesture_id = 0; gesture_id < state->state->num_gestures_saved; ++gesture_id) { + accel_gesture *gesture = state->state->gestures[gesture_id]; if (gesture == NULL) { retcode = ACCEL_INTERNAL_ERROR; continue; @@ -399,7 +486,7 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) { // If the moving average is at a final line. bool avg_line = false; int returned = ACCEL_SUCCESS; - for (int d=0; ddimensions && returned == 0; ++d) { + for (uint32_t d = 0; d < state->dimensions && returned == 0; ++d) { returned = append_to_moving_avg(gesture->moving_avg_values[d], accel_data[d], &avg_line); } if (returned != ACCEL_SUCCESS) { @@ -407,13 +494,15 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) { continue; } - if (!avg_line) { continue; } + if (!avg_line) { + continue; + } returned = ACCEL_SUCCESS; if (gesture->is_recording) { handle_recording_tick(gesture, state->dimensions); } else if (gesture->is_recorded) { - returned = handle_evaluation_tick(gesture, state->dimensions); + returned = handle_evaluation_tick(state, gesture, gesture_id); if (returned != ACCEL_SUCCESS) { retcode = returned; } @@ -425,17 +514,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, uint16_t *gesture_id, int *offset) { PRECONDITION_VALID_STATE(state); PRECONDITION_NOT_NULL(gesture_id); - PRECONDITION_NOT_NULL(affinity); - - *gesture_id = ACCEL_NO_VALID_GESTURE; - *affinity = ACCEL_NO_VALID_GESTURE; + PRECONDITION_NOT_NULL(offset); - if (state->state->num_gestures_saved < 0) { - return ACCEL_INTERNAL_ERROR; - } + *gesture_id = UINT16_MAX; + *offset = ACCEL_NO_VALID_GESTURE; if (state->state->num_gestures_saved == 0) { return ACCEL_NO_VALID_GESTURE; @@ -445,16 +530,16 @@ int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *aff return ACCEL_INTERNAL_ERROR; } - for (int i=0; istate->num_gestures_saved; ++i) { + for (uint16_t i = 0; i < state->state->num_gestures_saved; ++i) { accel_gesture *gesture = state->state->gestures[i]; - // TODO: this should be tested. + // TODO: Should this be tested? if (gesture == NULL) { return ACCEL_INTERNAL_ERROR; } - if ((*gesture_id == ACCEL_NO_VALID_GESTURE || *affinity == ACCEL_NO_VALID_GESTURE) && - *gesture_id != *affinity) { + // Both should be the default or changed at the same time. We have a programming error otherwise. + if ((*gesture_id == UINT16_MAX) != (*offset == ACCEL_NO_VALID_GESTURE)) { return ACCEL_INTERNAL_ERROR; } @@ -468,15 +553,26 @@ 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) { + if (*gesture_id == UINT16_MAX || *offset == ACCEL_NO_VALID_GESTURE) { return ACCEL_NO_VALID_GESTURE; } return ACCEL_SUCCESS; } + +int accel_reset_affinities_for_gesture(accel_state *state, uint16_t gesture_id) { + PRECONDITION_VALID_STATE(state); + PRECONDITION_NOT_NULL(state->state); + PRECONDITION_TRUE_PARAM((state->state->num_gestures_saved > gesture_id)); + + accel_gesture *gesture = state->state->gestures[gesture_id]; + + PRECONDITION_TRUE_PARAM(!gesture->is_recording); + PRECONDITION_TRUE_PARAM(gesture->is_recorded); + + return reset_gesture(gesture, state->dimensions); +} diff --git a/sample/simple-accelerometer/src/accel.h b/sample/simple-accelerometer/src/accel.h index c601b21..47ffdaa 100644 --- a/sample/simple-accelerometer/src/accel.h +++ b/sample/simple-accelerometer/src/accel.h @@ -1,9 +1,12 @@ #ifndef ACCEL_H #define ACCEL_H -#include +#ifdef __cplusplus +extern "C" { +#endif -#include "accel_consts.c" +#include +#include #define ACCEL_SUCCESS 0 #define ACCEL_PARAM_ERROR -1 @@ -11,32 +14,81 @@ #define ACCEL_MALLOC_ERROR -3 #define ACCEL_NO_VALID_GESTURE -4 -#define ACCEL_VERSION_GEN(major, minor, point, isBeta, isAlpha) \ - (4*(100*((100*major)+minor)+point) + 3 - (isAlpha?2:0) - (isBeta?1:0)) +#define ACCEL_VERSION_GEN(major, minor, point, isBeta, isAlpha) \ + (4 * (100 * ((100 * major) + minor) + point) + 3 - (isAlpha ? 2 : 0) - (isBeta ? 1 : 0)) -#define ACCEL_VERSION_CODE ACCEL_VERSION_GEN(1, 0, 0, true, false) +#define ACCEL_VERSION_CODE ACCEL_VERSION_GEN(1, 1, 0, false, true) struct internalAccelState; +struct accelState; -typedef struct { - int dimensions; +/** + * 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 int (*accel_callback)(struct accelState *state, uint16_t gesture_id, uint32_t offset_found, + bool *reset_gesture); + +// TODO: define this as a partial-type instead of exposing some fields. +typedef struct accelState { + uint32_t dimensions; + accel_callback callback; struct internalAccelState *state; } accel_state; /** * 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 * represents. * @param window_size The size of the moving windows used to calculate smoothed * sensor readings. + * @param callback A callback that is triggered whenever a gesture passes a + * threshold. See the ``accel_callback`` typedef for more + * information. + * @param threshold The minimum threshold offset (divided by length) that all + * gestures must be before the callback is called. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_generate_state(accel_state **state, int dimensions, int window_size); +int accel_generate_state(accel_state **state, uint32_t dimensions, uint16_t window_size, accel_callback callback, + const uint32_t threshold); /** * Destroys the state object at the pointer pointed to by the state pointer. @@ -47,30 +99,30 @@ int accel_generate_state(accel_state **state, int dimensions, int window_size); int accel_destroy_state(accel_state **state); /** - * Starts recording a accel gesture + * Starts recording an accel gesture * @param state A pointer to a non-NULL state variable that holds recording * metadata. * @param gesture Non-NULL pointer that will be populated with the gesture id. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_start_record_gesture(accel_state *state, int *gesture); +int accel_start_record_gesture(accel_state *state, uint16_t *gesture); /** - * Ends recording a accel gesture + * Ends recording an accel gesture * @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 * recorded. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_end_record_gesture(accel_state *state, int gesture_id); +int accel_end_record_gesture(accel_state *state, uint16_t gesture_id); /** - * Updates the state variable's current state based on the accel data array - * passed in. + * Updates the state variable's current state based on the raw accelerometer + * data array passed in. * @param state A pointer to a non-NULL state variable that holds * recording metadata. - * @param accel_data An with accelerometer data. + * @param accel_data A state->dimensions array with accelerometer data. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ int accel_process_timer_tick(accel_state *state, int *accel_data); @@ -86,6 +138,21 @@ int accel_process_timer_tick(accel_state *state, int *accel_data); * distance corresponding to the returned gesture. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *distance); +// TODO: rename distance to offset +int accel_find_most_likely_gesture(accel_state *state, uint16_t *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, uint16_t gesture_id); + +#ifdef __cplusplus +} +#endif #endif diff --git a/sample/simple-accelerometer/src/moving_avg_ticker.c b/sample/simple-accelerometer/src/moving_avg_ticker.c index 6856502..63a1ed0 100644 --- a/sample/simple-accelerometer/src/moving_avg_ticker.c +++ b/sample/simple-accelerometer/src/moving_avg_ticker.c @@ -3,34 +3,31 @@ #include "moving_avg_ticker.h" -#define PRECONDITION_NOT_NULL(foo) \ - if (foo == NULL) { return MOVING_AVG_PARAM_ERROR; } +#define PRECONDITION_NOT_NULL(foo) \ + if (foo == NULL) { \ + return MOVING_AVG_PARAM_ERROR; \ + } +// TODO: make this into a macro. int precondition_valid_moving_avg_values(moving_avg_values *input) { PRECONDITION_NOT_NULL(input); if (input->wbuf == NULL) { return MOVING_AVG_INTERNAL_ERROR; } - if (input->wbuf_end < 0) { - return MOVING_AVG_INTERNAL_ERROR; - } if (input->wbuf_len <= 0) { return MOVING_AVG_INTERNAL_ERROR; } - if (input->subtotal_size < 0) { - return MOVING_AVG_INTERNAL_ERROR; - } if (input->subtotal_size >= input->max_subtotal_size) { return MOVING_AVG_INTERNAL_ERROR; } if (input->max_subtotal_size <= 0) { return MOVING_AVG_INTERNAL_ERROR; } - return 0; + return MOVING_AVG_SUCCESS; } -int allocate_moving_avg(int num_wbuf, int subtotal_sizes, moving_avg_values **allocated) { +int allocate_moving_avg(uint16_t num_wbuf, uint16_t subtotal_sizes, moving_avg_values **allocated) { PRECONDITION_NOT_NULL(allocated); if (*allocated != NULL) { return MOVING_AVG_PARAM_ERROR; @@ -44,28 +41,28 @@ int allocate_moving_avg(int num_wbuf, int subtotal_sizes, moving_avg_values **al } size_t size = sizeof(moving_avg_values); - *allocated = (moving_avg_values *) malloc(size); + *allocated = (moving_avg_values *)malloc(size); if (allocated == NULL) { return MOVING_AVG_MALLOC_ERROR; } memset(*allocated, 0, size); (*allocated)->max_subtotal_size = subtotal_sizes; - int *wbuf = (int *) calloc(num_wbuf, sizeof(int)); + moving_avg_data_type *wbuf = (moving_avg_data_type *)calloc(num_wbuf, sizeof(moving_avg_data_type)); if (wbuf == NULL) { // Run away, fast! - free (allocated); + free(allocated); *allocated = NULL; return MOVING_AVG_MALLOC_ERROR; } (*allocated)->wbuf = wbuf; (*allocated)->wbuf_len = num_wbuf; - return 0; + return MOVING_AVG_SUCCESS; } int reset_moving_avg(moving_avg_values *reset) { int value = precondition_valid_moving_avg_values(reset); - if (value != 0) { + if (value != MOVING_AVG_SUCCESS) { return value; } @@ -73,12 +70,14 @@ int reset_moving_avg(moving_avg_values *reset) { reset->wbuf_end = reset->wbuf_len - 1; reset->subtotal = 0; reset->subtotal_size = 0; - return 0; + return MOVING_AVG_SUCCESS; } -int append_to_moving_avg(moving_avg_values *value, int appended, bool* is_at_end) { +int append_to_moving_avg(moving_avg_values *value, moving_avg_data_type appended, bool *is_at_end) { int is_valid_return_value = precondition_valid_moving_avg_values(value); - if (is_valid_return_value != 0) {return is_valid_return_value;} + if (is_valid_return_value != MOVING_AVG_SUCCESS) { + return is_valid_return_value; + } PRECONDITION_NOT_NULL(is_at_end); @@ -86,7 +85,7 @@ int append_to_moving_avg(moving_avg_values *value, int appended, bool* is_at_end value->subtotal += appended; if (value->subtotal_size != value->max_subtotal_size) { *is_at_end = false; - return 0; + return MOVING_AVG_SUCCESS; } value->wbuf_end = (value->wbuf_end + 1) % value->wbuf_len; @@ -95,21 +94,23 @@ int append_to_moving_avg(moving_avg_values *value, int appended, bool* is_at_end value->subtotal = 0; value->subtotal_size = 0; *is_at_end = true; - return 0; + return MOVING_AVG_SUCCESS; } -int get_latest_frame_moving_avg(moving_avg_values *value, int *frame) { +int get_latest_frame_moving_avg(moving_avg_values *value, moving_avg_data_type *frame) { int is_valid_return_value = precondition_valid_moving_avg_values(value); - if (is_valid_return_value != 0) {return is_valid_return_value;} + if (is_valid_return_value != MOVING_AVG_SUCCESS) { + return is_valid_return_value; + } PRECONDITION_NOT_NULL(frame); float sum = 0; - for (int i=0; iwbuf_len; ++i) { + for (uint16_t i = 0; i < value->wbuf_len; ++i) { sum += value->wbuf[i] * 1.0 / value->wbuf_len; } - *frame = (int) sum; - return 0; + *frame = (int32_t)sum; + return MOVING_AVG_SUCCESS; } int free_moving_avg(moving_avg_values **value) { @@ -122,5 +123,5 @@ int free_moving_avg(moving_avg_values **value) { } free(*value); *value = NULL; - return 0; + return MOVING_AVG_SUCCESS; } diff --git a/sample/simple-accelerometer/src/moving_avg_ticker.h b/sample/simple-accelerometer/src/moving_avg_ticker.h index 66afc5a..4d081b0 100644 --- a/sample/simple-accelerometer/src/moving_avg_ticker.h +++ b/sample/simple-accelerometer/src/moving_avg_ticker.h @@ -1,32 +1,48 @@ #ifndef ACCEL_AVG_TICKER #define ACCEL_AVG_TICKER +#ifdef __cplusplus +extern "C" { +#endif + +#include + #include "accel.h" +#define MOVING_AVG_SUCCESS ACCEL_SUCCESS #define MOVING_AVG_PARAM_ERROR ACCEL_PARAM_ERROR #define MOVING_AVG_INTERNAL_ERROR ACCEL_INTERNAL_ERROR #define MOVING_AVG_MALLOC_ERROR ACCEL_MALLOC_ERROR +#ifndef MOVING_AVG_DATA_TYPE +#define MOVING_AVG_DATA_TYPE int32_t +#endif + +typedef MOVING_AVG_DATA_TYPE moving_avg_data_type; + typedef struct moving_avg_values { // Circular buffer - int *wbuf; - int wbuf_end; - int wbuf_len; + moving_avg_data_type *wbuf; + uint16_t wbuf_end; + uint16_t wbuf_len; - int subtotal; - int subtotal_size; - int max_subtotal_size; + moving_avg_data_type subtotal; + uint16_t subtotal_size; + uint16_t max_subtotal_size; } moving_avg_values; - -int allocate_moving_avg(int num_wbuf, int subtotal_sizes, moving_avg_values **allocated); +int allocate_moving_avg(uint16_t num_wbuf, uint16_t subtotal_sizes, moving_avg_values **allocated); int reset_moving_avg(moving_avg_values *reset); -int append_to_moving_avg(moving_avg_values *value, int appended, bool *isAtEnd); +int append_to_moving_avg(moving_avg_values *value, moving_avg_data_type appended, bool *is_at_end); -int get_latest_frame_moving_avg(moving_avg_values *value, int *frame); +int get_latest_frame_moving_avg(moving_avg_values *value, moving_avg_data_type *frame); int free_moving_avg(moving_avg_values **value); +#ifdef __cplusplus +} +#endif + #endif diff --git a/sample/simple-accelerometer/src/pebble_makeup.h b/sample/simple-accelerometer/src/pebble_makeup.h index 4982a9d..f09e29b 100644 --- a/sample/simple-accelerometer/src/pebble_makeup.h +++ b/sample/simple-accelerometer/src/pebble_makeup.h @@ -1,6 +1,10 @@ #ifndef ACCEL_PEBBLE_SUPPLEMENTARY #define ACCEL_PEBBLE_SUPPLEMENTARY +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -18,7 +22,9 @@ void *my_realloc(void *old_ptr, size_t new_size, size_t old_size) { } void *p = malloc(new_size); - if (!p) return NULL; + if (!p) { + return NULL; + } size_t min_size = new_size < old_size ? new_size : old_size; memcpy(p, old_ptr, min_size); @@ -27,10 +33,16 @@ void *my_realloc(void *old_ptr, size_t new_size, size_t old_size) { } void *my_calloc(size_t num, size_t size) { - void * allocd = malloc(num * size); - if (allocd == NULL) return allocd; + void *allocd = malloc(num * size); + if (allocd == NULL) { + return allocd; + } memset(allocd, 0, num * size); return allocd; } +#ifdef __cplusplus +} +#endif + #endif diff --git a/sample/simple-accelerometer/src/simple-accelerometer.c b/sample/simple-accelerometer/src/simple-accelerometer.c index 3599367..1a162e6 100644 --- a/sample/simple-accelerometer/src/simple-accelerometer.c +++ b/sample/simple-accelerometer/src/simple-accelerometer.c @@ -1,4 +1,5 @@ #include +#include #include "accel.h" @@ -6,110 +7,105 @@ static Window *window; static TextLayer *text_layer; static accel_state *state = NULL; -static int recording_gesture = 0; +static uint16_t recording_gesture = 0; static bool recording = false; static void select_click_handler(ClickRecognizerRef recognizer, void *context) { - int result = 0; - if (recording) { - text_layer_set_text(text_layer, "Done recording"); - result = accel_end_record_gesture(state, recording_gesture); - if (result == ACCEL_SUCCESS) { - recording = false; - text_layer_set_text(text_layer, "End Successfully"); - } else { - text_layer_set_text(text_layer, "End Unsuccessfully"); - } - } else { - result = accel_start_record_gesture(state, &recording_gesture); - if (result == ACCEL_SUCCESS) { - recording = true; - text_layer_set_text(text_layer, "Start Successfully"); + int result = 0; + if (recording) { + text_layer_set_text(text_layer, "Done recording"); + result = accel_end_record_gesture(state, recording_gesture); + if (result == ACCEL_SUCCESS) { + recording = false; + text_layer_set_text(text_layer, "End Successfully"); + } else { + text_layer_set_text(text_layer, "End Unsuccessfully"); + } } else { - text_layer_set_text(text_layer, "Start Unsuccessfully"); + result = accel_start_record_gesture(state, &recording_gesture); + if (result == ACCEL_SUCCESS) { + recording = true; + text_layer_set_text(text_layer, "Start Successfully"); + } else { + text_layer_set_text(text_layer, "Start Unsuccessfully"); + } } - } } static void up_click_handler(ClickRecognizerRef recognizer, void *context) { - if (state == NULL) { - int result = accel_generate_state(&state, 3, 1); - if (result == ACCEL_SUCCESS) { - text_layer_set_text(text_layer, "Allocated Successfuly"); - } else { - text_layer_set_text(text_layer, "sAllocated Unsuccessfullys"); - } - } else { - int result = accel_destroy_state(&state); - if (result == ACCEL_SUCCESS) { - text_layer_set_text(text_layer, "Successful deletion"); + if (state == NULL) { + int result = accel_generate_state(&state, 3, 1, NULL, 0); + if (result == ACCEL_SUCCESS) { + text_layer_set_text(text_layer, "Allocated Successfuly"); + } else { + text_layer_set_text(text_layer, "sAllocated Unsuccessfullys"); + } } else { - text_layer_set_text(text_layer, "Non-successful deletion"); + int result = accel_destroy_state(&state); + if (result == ACCEL_SUCCESS) { + text_layer_set_text(text_layer, "Successful deletion"); + } else { + text_layer_set_text(text_layer, "Non-successful deletion"); + } } - } } static void down_click_handler(ClickRecognizerRef recognizer, void *context) { - AccelData data; - accel_service_peek(&data); - - int accel_data[3] = {data.x, data.y, data.z}; - - int result = accel_process_timer_tick(state, accel_data); - if (result == ACCEL_SUCCESS) { - text_layer_set_text(text_layer, "Successful tick"); - - int gesture_id = 0; - int distance = 0; - accel_find_most_likely_gesture(state, &gesture_id, &distance); - APP_LOG(APP_LOG_LEVEL_INFO, "Distance: %i, gesture: %i", distance, gesture_id); - } else { - text_layer_set_text(text_layer, "Non-successful tick"); - } + AccelData data; + accel_service_peek(&data); + + int accel_data[3] = {data.x, data.y, data.z}; + + int result = accel_process_timer_tick(state, accel_data); + if (result == ACCEL_SUCCESS) { + text_layer_set_text(text_layer, "Successful tick"); + + uint16_t gesture_id = 0; + int distance = 0; + accel_find_most_likely_gesture(state, &gesture_id, &distance); + APP_LOG(APP_LOG_LEVEL_INFO, "Distance: %i, gesture: %i", distance, gesture_id); + } else { + text_layer_set_text(text_layer, "Non-successful tick"); + } } static void click_config_provider(void *context) { - window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler); - window_single_click_subscribe(BUTTON_ID_UP, up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler); + window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler); + window_single_click_subscribe(BUTTON_ID_UP, up_click_handler); + window_single_click_subscribe(BUTTON_ID_DOWN, down_click_handler); } static void window_load(Window *window) { - Layer *window_layer = window_get_root_layer(window); - GRect bounds = layer_get_bounds(window_layer); + Layer *window_layer = window_get_root_layer(window); + GRect bounds = layer_get_bounds(window_layer); - text_layer = text_layer_create((GRect) { .origin = { 0, 72 }, .size = { bounds.size.w, 20 } }); - text_layer_set_text(text_layer, "Press a button"); - text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); - layer_add_child(window_layer, text_layer_get_layer(text_layer)); + text_layer = text_layer_create((GRect) {.origin = {0, 72}, .size = {bounds.size.w, 20}}); + text_layer_set_text(text_layer, "Press a button"); + text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); + layer_add_child(window_layer, text_layer_get_layer(text_layer)); } -static void window_unload(Window *window) { - text_layer_destroy(text_layer); -} +static void window_unload(Window *window) { text_layer_destroy(text_layer); } static void init(void) { - window = window_create(); - window_set_click_config_provider(window, click_config_provider); - window_set_window_handlers(window, (WindowHandlers) { - .load = window_load, - .unload = window_unload, - }); - const bool animated = true; - accel_data_service_subscribe(0, NULL); - window_stack_push(window, animated); + window = window_create(); + window_set_click_config_provider(window, click_config_provider); + window_set_window_handlers(window, (WindowHandlers) {.load = window_load, .unload = window_unload, }); + const bool animated = true; + accel_data_service_subscribe(0, NULL); + window_stack_push(window, animated); } static void deinit(void) { - accel_data_service_unsubscribe(); - window_destroy(window); + accel_data_service_unsubscribe(); + window_destroy(window); } int main(void) { - init(); + init(); - APP_LOG(APP_LOG_LEVEL_DEBUG, "Done initializing, pushed window: %p", window); + APP_LOG(APP_LOG_LEVEL_DEBUG, "Done initializing, pushed window: %p", window); - app_event_loop(); - deinit(); + app_event_loop(); + deinit(); } diff --git a/src/accel.c b/src/accel.c index 78bb0cc..2880ba5 100644 --- a/src/accel.c +++ b/src/accel.c @@ -8,15 +8,11 @@ #include #include #else -#define my_realloc(a, b, c) realloc(a, b) -#define my_calloc(a, b) calloc(a, b) +#define my_realloc(a, b, c) (realloc(a, b)) +#define my_calloc(a, b) (calloc(a, b)) #endif -#ifndef INT16_MAX -#define INT16_MAX 0x7fff -#endif - -// cbrt is defined and importable for everybody! +// cbrt/abs is defined and importable for everybody! #include #include #include @@ -29,50 +25,73 @@ typedef struct { bool is_recording; bool is_recorded; - int recording_size; - int **normalized_recording; + // TODO: uint32_t needed instead? + uint16_t recording_size; + int32_t **normalized_recording; moving_avg_values **moving_avg_values; - int *affinities; + int32_t *offsets; } accel_gesture; typedef struct internalAccelState { - int window_size; - int num_gestures_saved; + // TODO: uint32_t needed instead? + uint16_t num_gestures_saved; + uint16_t window_size; + uint32_t threshold; accel_gesture **gestures; } internal_accel_state; -#define PRECONDITION_NOT_NULL(foo_$) \ - if (foo_$ == NULL) { return ACCEL_PARAM_ERROR; } +#define PRECONDITION_NOT_NULL(INPUT) \ + if (INPUT == NULL) { \ + return ACCEL_PARAM_ERROR; \ + } + +#define PRECONDITION_NULL(INPUT) \ + if (INPUT != NULL) { \ + return ACCEL_PARAM_ERROR; \ + } + +#define PRECONDITION_VALID_STATE(INPUT_STATE) \ + if (INPUT_STATE == NULL) { \ + return ACCEL_PARAM_ERROR; \ + } \ + if (INPUT_STATE->state == NULL) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->dimensions <= 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->state->window_size <= 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->state->gestures == NULL && INPUT_STATE->state->num_gestures_saved != 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } \ + if (INPUT_STATE->state->gestures != NULL && INPUT_STATE->state->num_gestures_saved == 0) { \ + return ACCEL_INTERNAL_ERROR; \ + } +// if (INPUT_STATE->state->num_gestures_saved < 0) { +// return ACCEL_INTERNAL_ERROR; +// } + +#define PRECONDITION_TRUE_PARAM(TRUE_COND) \ + { \ + if (!TRUE_COND) { \ + return ACCEL_PARAM_ERROR; \ + } \ + } -#define PRECONDITION_NULL(foo_$) \ - if (foo_$ != NULL) { return ACCEL_PARAM_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 offsets as floats instead? +#define ALPHA ((float)1.0) -#define PRECONDITION_VALID_STATE(state_$) \ - if (state_$ == NULL) { return ACCEL_PARAM_ERROR; } \ - if (state_$->state == NULL) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->dimensions <= 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->window_size <= 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->num_gestures_saved < 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->gestures == NULL && state_$->state->num_gestures_saved != 0) { return ACCEL_INTERNAL_ERROR; } \ - if (state_$->state->gestures != NULL && state_$->state->num_gestures_saved == 0) { return ACCEL_INTERNAL_ERROR; } +// TODO: include this from a header file? +// TODO: include as a static inline function? +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +// #define MAX(a,b) (((a)>(b))?(a):(b)) -// 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? -#define ALPHA 1.0 - -// TODO: include these from a header file? -#define MAX(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a > _b ? _a : _b; }) -#define MIN(a,b) \ - ({ __typeof__ (a) _a = (a); \ - __typeof__ (b) _b = (b); \ - _a < _b ? _a : _b; }) - -void accel_destroy_gesture(accel_gesture **gesture, int dimensions) { +void accel_destroy_gesture(accel_gesture **gesture, uint32_t dimensions) { if (gesture == NULL || *gesture == NULL) { return; } @@ -80,12 +99,12 @@ void accel_destroy_gesture(accel_gesture **gesture, int dimensions) { accel_gesture *gest = *gesture; if (gest->moving_avg_values != NULL) { - for (int i=0; imoving_avg_values[i])); } } if (gest->normalized_recording != NULL) { - for (int i=0; irecording_size; ++i) { + for (uint16_t i = 0; i < gest->recording_size; ++i) { if (gest->normalized_recording[i] != NULL) { free(gest->normalized_recording[i]); gest->normalized_recording[i] = NULL; @@ -94,16 +113,15 @@ 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); *gesture = NULL; } - int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { PRECONDITION_VALID_STATE(state); PRECONDITION_NOT_NULL(gesture); @@ -112,7 +130,7 @@ int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { PRECONDITION_NULL((*gesture)); size_t gesture_size = sizeof(accel_gesture); - *gesture = (accel_gesture *) malloc(gesture_size); + *gesture = (accel_gesture *)malloc(gesture_size); if (*gesture == NULL) { return ACCEL_MALLOC_ERROR; } @@ -121,18 +139,19 @@ int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { (*gesture)->is_recorded = false; (*gesture)->normalized_recording = NULL; - (*gesture)->moving_avg_values = (moving_avg_values **) my_calloc(state->dimensions, sizeof(moving_avg_values *)); + (*gesture)->moving_avg_values = (moving_avg_values **)my_calloc(state->dimensions, sizeof(moving_avg_values *)); if ((*gesture)->moving_avg_values == NULL) { free((*gesture)); *gesture = NULL; return ACCEL_MALLOC_ERROR; } - for (int i=0; idimensions; ++i) { + for (uint32_t i = 0; i < state->dimensions; ++i) { // TODO: these two shouldn't both be the same.... - int result = allocate_moving_avg(state->state->window_size, state->state->window_size, &((*gesture)->moving_avg_values[i])); + int result = allocate_moving_avg(state->state->window_size, state->state->window_size, + &((*gesture)->moving_avg_values[i])); if (result != ACCEL_SUCCESS) { - for (int j=0; jmoving_avg_values[i])); } accel_destroy_gesture(gesture, state->dimensions); @@ -142,25 +161,31 @@ int accel_generate_gesture(accel_state *state, accel_gesture **gesture) { return ACCEL_SUCCESS; } -// TODO: needs direct testing with invalid objects. -int accel_generate_state(accel_state **state, int dimensions, int window_size) { +int accel_generate_state(accel_state **state, uint32_t dimensions, uint16_t window_size, accel_callback callback, + const uint32_t threshold) { PRECONDITION_NOT_NULL(state); // TODO: write a test for this value. PRECONDITION_NULL(*state); - if (dimensions <= 0) { + if (dimensions == 0) { return ACCEL_PARAM_ERROR; } if (window_size <= 0) { return ACCEL_PARAM_ERROR; } + if (threshold == 0 && callback != NULL) { + return ACCEL_PARAM_ERROR; + } + if (threshold != 0 && callback == NULL) { + return ACCEL_PARAM_ERROR; + } size_t state_size = sizeof(accel_state); size_t internal_state_size = sizeof(internal_accel_state); - internal_accel_state *internal_state = (internal_accel_state *) malloc(internal_state_size); - *state = (accel_state *) malloc(state_size); + internal_accel_state *internal_state = (internal_accel_state *)malloc(internal_state_size); + *state = (accel_state *)malloc(state_size); if (*state == NULL || internal_state == NULL) { if (state != NULL) { free(*state); @@ -178,9 +203,10 @@ int accel_generate_state(accel_state **state, int dimensions, int window_size) { (*state)->state = internal_state; - + (*state)->callback = callback; (*state)->dimensions = dimensions; (*state)->state->window_size = window_size > 0 ? window_size : 2; + (*state)->state->threshold = threshold; return ACCEL_SUCCESS; } @@ -189,12 +215,12 @@ int accel_destroy_state(accel_state **state) { PRECONDITION_NOT_NULL(state); PRECONDITION_NOT_NULL(*state); - int dimensions = (*state)->dimensions; + uint32_t dimensions = (*state)->dimensions; if ((*state)->state != NULL) { internal_accel_state *istate = (*state)->state; if (istate->gestures != NULL) { /* TODO: remove all additional fields inside the accel_state variable */ - for (int i=0; inum_gestures_saved; ++i) { + for (uint16_t i = 0; i < istate->num_gestures_saved; ++i) { accel_gesture *gest = (istate->gestures[i]); accel_destroy_gesture(&(gest), dimensions); } @@ -211,12 +237,14 @@ int accel_destroy_state(accel_state **state) { return ACCEL_SUCCESS; } -int accel_start_record_gesture(accel_state *state, int *gesture) { +int accel_start_record_gesture(accel_state *state, uint16_t *gesture) { PRECONDITION_VALID_STATE(state); PRECONDITION_NOT_NULL(gesture); if (state->state->num_gestures_saved != 0) { - accel_gesture **tmp = (accel_gesture **)my_realloc(state->state->gestures, (state->state->num_gestures_saved + 1)*sizeof(accel_gesture *), (state->state->num_gestures_saved)*sizeof(accel_gesture *)); + accel_gesture **tmp = (accel_gesture **)my_realloc( + state->state->gestures, (state->state->num_gestures_saved + 1) * sizeof(accel_gesture *), + (state->state->num_gestures_saved) * sizeof(accel_gesture *)); if (tmp == NULL) { return ACCEL_MALLOC_ERROR; } @@ -233,12 +261,12 @@ int accel_start_record_gesture(accel_state *state, int *gesture) { int result = accel_generate_gesture(state, &(state->state->gestures[*gesture])); if (result != ACCEL_SUCCESS) { - *gesture = -1; if (state->state->num_gestures_saved == 1) { free(state->state->gestures); state->state->gestures = NULL; } else { - accel_gesture ** tmp = (accel_gesture **)my_realloc(state->state->gestures, state->state->num_gestures_saved - 1, state->state->num_gestures_saved); + accel_gesture **tmp = (accel_gesture **)my_realloc( + state->state->gestures, state->state->num_gestures_saved - 1, state->state->num_gestures_saved); if (tmp != NULL) { // If tmp is null, we don't really care that realloc failed, since a future use of realloc will help us. state->state->gestures = tmp; @@ -251,22 +279,52 @@ int accel_start_record_gesture(accel_state *state, int *gesture) { return ACCEL_SUCCESS; } +// Taken from hackers delight +// http://www.hackersdelight.org/hdcodetxt/icbrt.c.txt +uint32_t icbrt1(uint32_t x) { + int32_t s; + uint32_t y, b; + + y = 0; + for (s = 30; s >= 0; s = s - 3) { + y = 2 * y; + b = (3 * y * (y + 1) + 1) << s; + if (x >= b) { + x = x - b; + y = y + 1; + } + } + return y; +} + // The uWave paper suggests a mapping from [-20, 20]->[-15, 15], but cube root // should to work better for variable ranges. // TODO: revisit this decision. -int normalize(int sum) { - return (int) cbrt(sum); +int32_t normalize(int32_t sum) { + if (sum < 0) { + return (int)-cbrtf(-((float)sum)); + } + return -(int)-cbrtf(((float)sum)); +} + +int reset_gesture(accel_gesture *gest, const uint32_t dimensions) { + PRECONDITION_NOT_NULL(gest); + for (uint16_t i = 0; i < gest->recording_size; ++i) { + gest->offsets[i] = INT16_MAX; + } + for (uint32_t 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) { +int accel_end_record_gesture(accel_state *state, uint16_t gesture_id) { PRECONDITION_VALID_STATE(state); - // TODO: use an unsigned int instead so we don't need to check for this type of error. - if (gesture_id < 0) { - return ACCEL_PARAM_ERROR; - } internal_accel_state *istate = state->state; + // TODO: gesture_id == istate->num_gestures_saved is incorrect. + // Write a test for it and fix it. if (gesture_id > istate->num_gestures_saved) { return ACCEL_PARAM_ERROR; } @@ -284,64 +342,76 @@ 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 = (int32_t *)malloc(gesture->recording_size * sizeof(int32_t)); + if (gesture->offsets == NULL) { return ACCEL_MALLOC_ERROR; } - gesture->is_recording = false; - gesture->is_recorded = true; + 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; + } - for (int i=0; irecording_size; ++i) { - gesture->affinities[i] = INT16_MAX; + for (uint16_t i = 0; i < gesture->recording_size; ++i) { + gesture->offsets[i] = INT16_MAX; } - for (int d=0; ddimensions; ++d) { + for (uint32_t d = 0; d < state->dimensions; ++d) { reset_moving_avg(gesture->moving_avg_values[d]); } return ACCEL_SUCCESS; } -// TODO: check for malloc failure in this function. +// TODO: gracefully handle malloc failure in this function. // TODO: this should return error types instead of being void. - // Follow-up: find usages of this method. -void handle_recording_tick(accel_gesture *gesture, int dimensions) { - if (gesture == NULL) { return; } +// Follow-up: find usages of this method. +void handle_recording_tick(accel_gesture *gesture, uint32_t dimensions) { + if (gesture == NULL) { + return; + } // TODO: grow exponentially, not linearly. Linear growth allocates too frequently. if (gesture->recording_size != 0) { - gesture->normalized_recording = (int **) my_realloc(gesture->normalized_recording, (gesture->recording_size + 1) * sizeof(int *), gesture->recording_size * sizeof(int *)); + gesture->normalized_recording = + (int32_t **)my_realloc(gesture->normalized_recording, (gesture->recording_size + 1) * sizeof(int32_t *), + gesture->recording_size * sizeof(int32_t *)); if (gesture->normalized_recording == NULL) { return; } } else { - gesture->normalized_recording = (int **) malloc(sizeof(int *)); + gesture->normalized_recording = (int32_t **)malloc(sizeof(int32_t *)); } - gesture->normalized_recording[gesture->recording_size] = (int *) malloc(sizeof(int) * dimensions); - for (int i=0; inormalized_recording[gesture->recording_size] = (int32_t *)malloc(sizeof(int32_t) * dimensions); + for (uint32_t i = 0; i < dimensions; ++i) { // TODO: fix this int/float business. - // TODO: check resultant output. - get_latest_frame_moving_avg(gesture->moving_avg_values[i], &(gesture->normalized_recording[gesture->recording_size][i])); - gesture->normalized_recording[gesture->recording_size][i] = normalize(gesture->normalized_recording[gesture->recording_size][i]); + // TODO: complain about invalid return values. + get_latest_frame_moving_avg(gesture->moving_avg_values[i], + &(gesture->normalized_recording[gesture->recording_size][i])); + gesture->normalized_recording[gesture->recording_size][i] = + normalize(gesture->normalized_recording[gesture->recording_size][i]); } - ++gesture->recording_size; + ++(gesture->recording_size); } -int handle_evaluation_tick(accel_gesture *gesture, int dimensions) { +int handle_evaluation_tick(accel_state *state, accel_gesture *gesture, uint16_t gesture_id) { // TODO: load the input at the beginning instead of gesture->recording_size times. PRECONDITION_NOT_NULL(gesture); + uint32_t dimensions = state->dimensions; - if (gesture->moving_avg_values == NULL || - gesture->affinities == NULL) { + if (gesture->moving_avg_values == NULL || gesture->offsets == NULL) { return ACCEL_INTERNAL_ERROR; } - int i = gesture->recording_size; + uint16_t i = gesture->recording_size; while (i != 0) { --i; int cost = 0; - for (int d=0; dnormalized_recording[i][d]; - int input_i_d = 0; + int32_t input_i_d = 0; // TODO: complain about invalid return values. get_latest_frame_moving_avg(gesture->moving_avg_values[d], &input_i_d); input_i_d = normalize(input_i_d); @@ -353,16 +423,16 @@ 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((int)(ALPHA * gesture->offsets[i]), cost + gesture->offsets[i - 1]); } } - for (i=1; irecording_size; ++i) { + for (i = 1; i < gesture->recording_size; ++i) { int cost = 0; - for (int d=0; dnormalized_recording[i][d]; - int input_i_d = 0; + int32_t input_i_d = 0; // TODO: complain about invalid return values. get_latest_frame_moving_avg(gesture->moving_avg_values[d], &input_i_d); if (recording_i_d > input_i_d) { @@ -372,7 +442,24 @@ 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); + } + if (state->callback != NULL) { + if (state->state->threshold == 0) { + return ACCEL_PARAM_ERROR; + } + float avg_affinity = ((float)gesture->offsets[gesture->recording_size - 1]) / gesture->recording_size; + if (avg_affinity < 0) { + return ACCEL_INTERNAL_ERROR; + } + if (avg_affinity < state->state->threshold) { + bool reset; + int retval = state->callback(state, gesture_id, (uint32_t)(avg_affinity), &reset); + if (reset == true) { + reset_gesture(gesture, dimensions); + } + return retval; + } } return ACCEL_SUCCESS; } @@ -382,8 +469,8 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) { PRECONDITION_NOT_NULL(accel_data); int retcode = ACCEL_SUCCESS; - for (int gesture_iter = 0; gesture_iter < state->state->num_gestures_saved; ++gesture_iter) { - accel_gesture *gesture = state->state->gestures[gesture_iter]; + for (uint16_t gesture_id = 0; gesture_id < state->state->num_gestures_saved; ++gesture_id) { + accel_gesture *gesture = state->state->gestures[gesture_id]; if (gesture == NULL) { retcode = ACCEL_INTERNAL_ERROR; continue; @@ -399,7 +486,7 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) { // If the moving average is at a final line. bool avg_line = false; int returned = ACCEL_SUCCESS; - for (int d=0; ddimensions && returned == 0; ++d) { + for (uint32_t d = 0; d < state->dimensions && returned == 0; ++d) { returned = append_to_moving_avg(gesture->moving_avg_values[d], accel_data[d], &avg_line); } if (returned != ACCEL_SUCCESS) { @@ -407,13 +494,15 @@ int accel_process_timer_tick(accel_state *state, int *accel_data) { continue; } - if (!avg_line) { continue; } + if (!avg_line) { + continue; + } returned = ACCEL_SUCCESS; if (gesture->is_recording) { handle_recording_tick(gesture, state->dimensions); } else if (gesture->is_recorded) { - returned = handle_evaluation_tick(gesture, state->dimensions); + returned = handle_evaluation_tick(state, gesture, gesture_id); if (returned != ACCEL_SUCCESS) { retcode = returned; } @@ -425,17 +514,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, uint16_t *gesture_id, int *offset) { PRECONDITION_VALID_STATE(state); PRECONDITION_NOT_NULL(gesture_id); - PRECONDITION_NOT_NULL(affinity); - - *gesture_id = ACCEL_NO_VALID_GESTURE; - *affinity = ACCEL_NO_VALID_GESTURE; + PRECONDITION_NOT_NULL(offset); - if (state->state->num_gestures_saved < 0) { - return ACCEL_INTERNAL_ERROR; - } + *gesture_id = UINT16_MAX; + *offset = ACCEL_NO_VALID_GESTURE; if (state->state->num_gestures_saved == 0) { return ACCEL_NO_VALID_GESTURE; @@ -445,16 +530,16 @@ int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *aff return ACCEL_INTERNAL_ERROR; } - for (int i=0; istate->num_gestures_saved; ++i) { + for (uint16_t i = 0; i < state->state->num_gestures_saved; ++i) { accel_gesture *gesture = state->state->gestures[i]; - // TODO: this should be tested. + // TODO: Should this be tested? if (gesture == NULL) { return ACCEL_INTERNAL_ERROR; } - if ((*gesture_id == ACCEL_NO_VALID_GESTURE || *affinity == ACCEL_NO_VALID_GESTURE) && - *gesture_id != *affinity) { + // Both should be the default or changed at the same time. We have a programming error otherwise. + if ((*gesture_id == UINT16_MAX) != (*offset == ACCEL_NO_VALID_GESTURE)) { return ACCEL_INTERNAL_ERROR; } @@ -468,15 +553,26 @@ 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) { + if (*gesture_id == UINT16_MAX || *offset == ACCEL_NO_VALID_GESTURE) { return ACCEL_NO_VALID_GESTURE; } return ACCEL_SUCCESS; } + +int accel_reset_affinities_for_gesture(accel_state *state, uint16_t gesture_id) { + PRECONDITION_VALID_STATE(state); + PRECONDITION_NOT_NULL(state->state); + PRECONDITION_TRUE_PARAM((state->state->num_gestures_saved > gesture_id)); + + accel_gesture *gesture = state->state->gestures[gesture_id]; + + PRECONDITION_TRUE_PARAM(!gesture->is_recording); + PRECONDITION_TRUE_PARAM(gesture->is_recorded); + + return reset_gesture(gesture, state->dimensions); +} diff --git a/src/accel.h b/src/accel.h index e968fb1..47ffdaa 100644 --- a/src/accel.h +++ b/src/accel.h @@ -1,7 +1,12 @@ #ifndef ACCEL_H #define ACCEL_H +#ifdef __cplusplus +extern "C" { +#endif + #include +#include #define ACCEL_SUCCESS 0 #define ACCEL_PARAM_ERROR -1 @@ -9,32 +14,81 @@ #define ACCEL_MALLOC_ERROR -3 #define ACCEL_NO_VALID_GESTURE -4 -#define ACCEL_VERSION_GEN(major, minor, point, isBeta, isAlpha) \ - (4*(100*((100*major)+minor)+point) + 3 - (isAlpha?2:0) - (isBeta?1:0)) +#define ACCEL_VERSION_GEN(major, minor, point, isBeta, isAlpha) \ + (4 * (100 * ((100 * major) + minor) + point) + 3 - (isAlpha ? 2 : 0) - (isBeta ? 1 : 0)) -#define ACCEL_VERSION_CODE ACCEL_VERSION_GEN(1, 0, 0, true, false) +#define ACCEL_VERSION_CODE ACCEL_VERSION_GEN(1, 1, 0, false, true) struct internalAccelState; +struct accelState; -typedef struct { - int dimensions; +/** + * 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 int (*accel_callback)(struct accelState *state, uint16_t gesture_id, uint32_t offset_found, + bool *reset_gesture); +// TODO: define this as a partial-type instead of exposing some fields. +typedef struct accelState { + uint32_t dimensions; + + accel_callback callback; struct internalAccelState *state; } accel_state; /** * 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 * represents. * @param window_size The size of the moving windows used to calculate smoothed * sensor readings. + * @param callback A callback that is triggered whenever a gesture passes a + * threshold. See the ``accel_callback`` typedef for more + * information. + * @param threshold The minimum threshold offset (divided by length) that all + * gestures must be before the callback is called. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_generate_state(accel_state **state, int dimensions, int window_size); +int accel_generate_state(accel_state **state, uint32_t dimensions, uint16_t window_size, accel_callback callback, + const uint32_t threshold); /** * Destroys the state object at the pointer pointed to by the state pointer. @@ -45,30 +99,30 @@ int accel_generate_state(accel_state **state, int dimensions, int window_size); int accel_destroy_state(accel_state **state); /** - * Starts recording a accel gesture + * Starts recording an accel gesture * @param state A pointer to a non-NULL state variable that holds recording * metadata. * @param gesture Non-NULL pointer that will be populated with the gesture id. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_start_record_gesture(accel_state *state, int *gesture); +int accel_start_record_gesture(accel_state *state, uint16_t *gesture); /** - * Ends recording a accel gesture + * Ends recording an accel gesture * @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 * recorded. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_end_record_gesture(accel_state *state, int gesture_id); +int accel_end_record_gesture(accel_state *state, uint16_t gesture_id); /** - * Updates the state variable's current state based on the accel data array - * passed in. + * Updates the state variable's current state based on the raw accelerometer + * data array passed in. * @param state A pointer to a non-NULL state variable that holds * recording metadata. - * @param accel_data An with accelerometer data. + * @param accel_data A state->dimensions array with accelerometer data. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ int accel_process_timer_tick(accel_state *state, int *accel_data); @@ -84,6 +138,21 @@ int accel_process_timer_tick(accel_state *state, int *accel_data); * distance corresponding to the returned gesture. * @return ACCEL_SUCCESS if successful, an error code otherwise. */ -int accel_find_most_likely_gesture(accel_state *state, int *gesture_id, int *distance); +// TODO: rename distance to offset +int accel_find_most_likely_gesture(accel_state *state, uint16_t *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, uint16_t gesture_id); + +#ifdef __cplusplus +} +#endif #endif diff --git a/src/moving_avg_ticker.c b/src/moving_avg_ticker.c index 6856502..63a1ed0 100644 --- a/src/moving_avg_ticker.c +++ b/src/moving_avg_ticker.c @@ -3,34 +3,31 @@ #include "moving_avg_ticker.h" -#define PRECONDITION_NOT_NULL(foo) \ - if (foo == NULL) { return MOVING_AVG_PARAM_ERROR; } +#define PRECONDITION_NOT_NULL(foo) \ + if (foo == NULL) { \ + return MOVING_AVG_PARAM_ERROR; \ + } +// TODO: make this into a macro. int precondition_valid_moving_avg_values(moving_avg_values *input) { PRECONDITION_NOT_NULL(input); if (input->wbuf == NULL) { return MOVING_AVG_INTERNAL_ERROR; } - if (input->wbuf_end < 0) { - return MOVING_AVG_INTERNAL_ERROR; - } if (input->wbuf_len <= 0) { return MOVING_AVG_INTERNAL_ERROR; } - if (input->subtotal_size < 0) { - return MOVING_AVG_INTERNAL_ERROR; - } if (input->subtotal_size >= input->max_subtotal_size) { return MOVING_AVG_INTERNAL_ERROR; } if (input->max_subtotal_size <= 0) { return MOVING_AVG_INTERNAL_ERROR; } - return 0; + return MOVING_AVG_SUCCESS; } -int allocate_moving_avg(int num_wbuf, int subtotal_sizes, moving_avg_values **allocated) { +int allocate_moving_avg(uint16_t num_wbuf, uint16_t subtotal_sizes, moving_avg_values **allocated) { PRECONDITION_NOT_NULL(allocated); if (*allocated != NULL) { return MOVING_AVG_PARAM_ERROR; @@ -44,28 +41,28 @@ int allocate_moving_avg(int num_wbuf, int subtotal_sizes, moving_avg_values **al } size_t size = sizeof(moving_avg_values); - *allocated = (moving_avg_values *) malloc(size); + *allocated = (moving_avg_values *)malloc(size); if (allocated == NULL) { return MOVING_AVG_MALLOC_ERROR; } memset(*allocated, 0, size); (*allocated)->max_subtotal_size = subtotal_sizes; - int *wbuf = (int *) calloc(num_wbuf, sizeof(int)); + moving_avg_data_type *wbuf = (moving_avg_data_type *)calloc(num_wbuf, sizeof(moving_avg_data_type)); if (wbuf == NULL) { // Run away, fast! - free (allocated); + free(allocated); *allocated = NULL; return MOVING_AVG_MALLOC_ERROR; } (*allocated)->wbuf = wbuf; (*allocated)->wbuf_len = num_wbuf; - return 0; + return MOVING_AVG_SUCCESS; } int reset_moving_avg(moving_avg_values *reset) { int value = precondition_valid_moving_avg_values(reset); - if (value != 0) { + if (value != MOVING_AVG_SUCCESS) { return value; } @@ -73,12 +70,14 @@ int reset_moving_avg(moving_avg_values *reset) { reset->wbuf_end = reset->wbuf_len - 1; reset->subtotal = 0; reset->subtotal_size = 0; - return 0; + return MOVING_AVG_SUCCESS; } -int append_to_moving_avg(moving_avg_values *value, int appended, bool* is_at_end) { +int append_to_moving_avg(moving_avg_values *value, moving_avg_data_type appended, bool *is_at_end) { int is_valid_return_value = precondition_valid_moving_avg_values(value); - if (is_valid_return_value != 0) {return is_valid_return_value;} + if (is_valid_return_value != MOVING_AVG_SUCCESS) { + return is_valid_return_value; + } PRECONDITION_NOT_NULL(is_at_end); @@ -86,7 +85,7 @@ int append_to_moving_avg(moving_avg_values *value, int appended, bool* is_at_end value->subtotal += appended; if (value->subtotal_size != value->max_subtotal_size) { *is_at_end = false; - return 0; + return MOVING_AVG_SUCCESS; } value->wbuf_end = (value->wbuf_end + 1) % value->wbuf_len; @@ -95,21 +94,23 @@ int append_to_moving_avg(moving_avg_values *value, int appended, bool* is_at_end value->subtotal = 0; value->subtotal_size = 0; *is_at_end = true; - return 0; + return MOVING_AVG_SUCCESS; } -int get_latest_frame_moving_avg(moving_avg_values *value, int *frame) { +int get_latest_frame_moving_avg(moving_avg_values *value, moving_avg_data_type *frame) { int is_valid_return_value = precondition_valid_moving_avg_values(value); - if (is_valid_return_value != 0) {return is_valid_return_value;} + if (is_valid_return_value != MOVING_AVG_SUCCESS) { + return is_valid_return_value; + } PRECONDITION_NOT_NULL(frame); float sum = 0; - for (int i=0; iwbuf_len; ++i) { + for (uint16_t i = 0; i < value->wbuf_len; ++i) { sum += value->wbuf[i] * 1.0 / value->wbuf_len; } - *frame = (int) sum; - return 0; + *frame = (int32_t)sum; + return MOVING_AVG_SUCCESS; } int free_moving_avg(moving_avg_values **value) { @@ -122,5 +123,5 @@ int free_moving_avg(moving_avg_values **value) { } free(*value); *value = NULL; - return 0; + return MOVING_AVG_SUCCESS; } diff --git a/src/moving_avg_ticker.h b/src/moving_avg_ticker.h index 66afc5a..4d081b0 100644 --- a/src/moving_avg_ticker.h +++ b/src/moving_avg_ticker.h @@ -1,32 +1,48 @@ #ifndef ACCEL_AVG_TICKER #define ACCEL_AVG_TICKER +#ifdef __cplusplus +extern "C" { +#endif + +#include + #include "accel.h" +#define MOVING_AVG_SUCCESS ACCEL_SUCCESS #define MOVING_AVG_PARAM_ERROR ACCEL_PARAM_ERROR #define MOVING_AVG_INTERNAL_ERROR ACCEL_INTERNAL_ERROR #define MOVING_AVG_MALLOC_ERROR ACCEL_MALLOC_ERROR +#ifndef MOVING_AVG_DATA_TYPE +#define MOVING_AVG_DATA_TYPE int32_t +#endif + +typedef MOVING_AVG_DATA_TYPE moving_avg_data_type; + typedef struct moving_avg_values { // Circular buffer - int *wbuf; - int wbuf_end; - int wbuf_len; + moving_avg_data_type *wbuf; + uint16_t wbuf_end; + uint16_t wbuf_len; - int subtotal; - int subtotal_size; - int max_subtotal_size; + moving_avg_data_type subtotal; + uint16_t subtotal_size; + uint16_t max_subtotal_size; } moving_avg_values; - -int allocate_moving_avg(int num_wbuf, int subtotal_sizes, moving_avg_values **allocated); +int allocate_moving_avg(uint16_t num_wbuf, uint16_t subtotal_sizes, moving_avg_values **allocated); int reset_moving_avg(moving_avg_values *reset); -int append_to_moving_avg(moving_avg_values *value, int appended, bool *isAtEnd); +int append_to_moving_avg(moving_avg_values *value, moving_avg_data_type appended, bool *is_at_end); -int get_latest_frame_moving_avg(moving_avg_values *value, int *frame); +int get_latest_frame_moving_avg(moving_avg_values *value, moving_avg_data_type *frame); int free_moving_avg(moving_avg_values **value); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/pebble_makeup.h b/src/pebble_makeup.h index 4982a9d..f09e29b 100644 --- a/src/pebble_makeup.h +++ b/src/pebble_makeup.h @@ -1,6 +1,10 @@ #ifndef ACCEL_PEBBLE_SUPPLEMENTARY #define ACCEL_PEBBLE_SUPPLEMENTARY +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -18,7 +22,9 @@ void *my_realloc(void *old_ptr, size_t new_size, size_t old_size) { } void *p = malloc(new_size); - if (!p) return NULL; + if (!p) { + return NULL; + } size_t min_size = new_size < old_size ? new_size : old_size; memcpy(p, old_ptr, min_size); @@ -27,10 +33,16 @@ void *my_realloc(void *old_ptr, size_t new_size, size_t old_size) { } void *my_calloc(size_t num, size_t size) { - void * allocd = malloc(num * size); - if (allocd == NULL) return allocd; + void *allocd = malloc(num * size); + if (allocd == NULL) { + return allocd; + } memset(allocd, 0, num * size); return allocd; } +#ifdef __cplusplus +} +#endif + #endif diff --git a/test/.gitignore b/test/.gitignore index ddc2d6e..e5c940c 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,3 +1,5 @@ test_executable accel_test moving_avg_ticker_test + +*.dSYM diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index d28644e..0000000 --- a/test/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -PHONY = default tests libgtest.a tests clean list - -default: - @make all - -list: - echo $(PHONY) - -# TODO: use variables and stuff, this is super messy. - -tests: moving_avg_ticker_test accel_test - -moving_avg_ticker_test: libgtest.a moving_avg_ticker_test.cc ../src/moving_avg_ticker.h ../src/moving_avg_ticker.c - @clang++ -DIS_NOT_PEBBLE -I../lib/gtest-1.7.0/include -pthread moving_avg_ticker_test.cc ../src/moving_avg_ticker.c libgtest.a -o moving_avg_ticker_test -Wall - -accel_test: libgtest.a accel_test.cc ../src - @clang++ -DIS_NOT_PEBBLE -I../lib/gtest-1.7.0/include -pthread accel_test.cc ../src/accel.c ../src/moving_avg_ticker.c libgtest.a -o accel_test -Wall - -libgtest.a: - clang++ -I../lib/gtest-1.7.0/include -I../lib/gtest-1.7.0 -c ../lib/gtest-1.7.0/src/gtest-all.cc -lpthread - ar -rv libgtest.a gtest-all.o - -run: moving_avg_ticker_test accel_test - ./moving_avg_ticker_test - ./accel_test - -clean: - rm -f libgtest.a gtest-all.o moving_avg_ticker_test accel_test diff --git a/test/accel_test.cc b/test/accel_test.cc deleted file mode 100644 index 52fde91..0000000 --- a/test/accel_test.cc +++ /dev/null @@ -1,320 +0,0 @@ -#include "gtest/gtest.h" - -#include "../src/accel.h" - -const void * void_null = NULL; - -accel_state *test_fabricate_state(int dimensions) { - accel_state *state = NULL; - int result = accel_generate_state(&state, dimensions, 1); - EXPECT_EQ(0, result); - EXPECT_NE(void_null, state); - return state; -} - -accel_state *test_fabricate_1d_state() { - return test_fabricate_state(1); -} - -accel_state *test_fabricate_3d_state() { - return test_fabricate_state(3); -} - -void test_burn_state(accel_state ** state) { - int result = accel_destroy_state(state); - EXPECT_EQ(0, result); - EXPECT_EQ(void_null, *state); -} - - -TEST(AccelFuzzTest, generate_state_null_state) { - int result = accel_generate_state(NULL, 3, 1); - EXPECT_EQ(result, ACCEL_PARAM_ERROR); -} - -TEST(AccelFuzzTest, generate_state_negative_or_zero_dimensions) { - accel_state *state = NULL; - // 0 dimensions must fail - int result = accel_generate_state(&state, 0, 1); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // -1 dimension must fail - result = accel_generate_state(&state, -1, 1); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // 1 dimension must succeed. - state = NULL; - result = accel_generate_state(&state, 1, 1); - EXPECT_EQ(0, result); - // TODO: result's memory is leaked :s -} - -TEST(AccelFuzzTest, generate_state_invalid_window_size) { - accel_state *state = NULL; - int result = 0; - - // Size 0 must fail - result = accel_generate_state(&state, 1, 0); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Size -1 must fail - result = accel_generate_state(&state, 1, -1); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Size 1 must succeed - result = accel_generate_state(&state, 1, 1); - EXPECT_EQ(0, result); - EXPECT_NE(void_null, state); -} - -TEST(AccelFuzzTest, accel_destroy_state_invalid_input) { - int result = 0; - - // Destroying x (x = NULL) will fail. - result = accel_destroy_state(NULL); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Destroying x (*x = NULL) will fail. - accel_state *state = NULL; - result = accel_destroy_state(&state); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Destroying x (*x != NULL) should succeed. - state = test_fabricate_1d_state(); - - // Destroy the state - result = accel_destroy_state(&state); - EXPECT_EQ(0, result); - EXPECT_EQ(void_null, state); -} - -TEST(AccelFuzzTest, accel_start_record_gesture_invalid_input) { - int result = 0; - int gesture_id = 0; - result = accel_start_record_gesture(NULL, &gesture_id); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - accel_state *state = test_fabricate_1d_state(); - - result = accel_start_record_gesture(state, NULL); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - test_burn_state(&state); -} - -TEST(AccelFuzzTest, accel_end_record_gesture_invalid_input) { - int result = 0; - accel_state *state = NULL; - - // Null state: - result = accel_end_record_gesture(NULL, 1); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Negative index: - state = test_fabricate_1d_state(); - result = accel_end_record_gesture(state, -1); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - test_burn_state(&state); - - // Unused index: - state = test_fabricate_1d_state(); - result = accel_end_record_gesture(state, 1); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - test_burn_state(&state); - - // Verify it works for valid indexes. - state = test_fabricate_1d_state(); - int gesture = 1234; - result = accel_start_record_gesture(state, &gesture); - EXPECT_NE(1234, gesture); - EXPECT_EQ(0, result); - int data[1] = {1}; - EXPECT_EQ(0, accel_process_timer_tick(state, data)); - - result = accel_end_record_gesture(state, gesture); - EXPECT_EQ(0, result) << "gesture " << gesture << " couldn't be recorded correctly" << std::endl; - test_burn_state(&state); -} - -TEST(AccelFuzzTest, accel_process_timer_tick_invalid_input) { - int result = 0; - int accel_data = 0; - accel_state *state = NULL; - - // Null state value. - result = accel_process_timer_tick(NULL, &accel_data); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Null array input. - state = test_fabricate_1d_state(); - result = accel_process_timer_tick(state, NULL); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - test_burn_state(&state); -} - -TEST(AccelFuzzTest, accel_find_most_likely_gesture_invalid_input) { - int result = 0; - int gesture_id = 0; - int affinity = 0; - accel_state *state = NULL; - - // Null state: - result = accel_find_most_likely_gesture(NULL, &gesture_id, &affinity); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - - // Null gesture id is passed in. - state = test_fabricate_1d_state(); - result = accel_find_most_likely_gesture(state, NULL, &affinity); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - test_burn_state(&state); - - // Null affinity is passed in. - state = test_fabricate_1d_state(); - result = accel_find_most_likely_gesture(state, &gesture_id, NULL); - EXPECT_EQ(ACCEL_PARAM_ERROR, result); - test_burn_state(&state); - - // No tests exist, but otherwise valid parameters. - state = test_fabricate_1d_state(); - result = accel_find_most_likely_gesture(state, &gesture_id, &affinity); - EXPECT_EQ(ACCEL_NO_VALID_GESTURE, gesture_id); - EXPECT_EQ(ACCEL_NO_VALID_GESTURE, affinity); - EXPECT_EQ(ACCEL_NO_VALID_GESTURE, result); -} - -TEST(AccelTest, accel_generate_and_destroy) { - accel_state *state = NULL; - for (int i=1; i<10; ++i) { - EXPECT_EQ(void_null, state) << "i = " << i; - EXPECT_EQ(0, accel_generate_state(&state, 2*i, i)) << "i = " << i; - EXPECT_EQ(0, accel_destroy_state(&state)) << "i = " << i; - EXPECT_EQ(void_null, state) << "i = " << i; - } -} - -TEST(AccelTest, start_recording_and_close_many_gestures) { - accel_state *state = NULL; - state = test_fabricate_1d_state(); - - int data[1] = {0}; - for (int i=0; i<10; ++i) { - int gesture = 0; - ASSERT_EQ(0, accel_start_record_gesture(state, &gesture)); - ASSERT_EQ(i, gesture); - ASSERT_EQ(0, accel_process_timer_tick(state, data)); - } - for (int i=0; i<10; ++i) { - ASSERT_EQ(0, accel_end_record_gesture(state, i)); - } - test_burn_state(&state); -} - -TEST(AccelTest, record_incredibly_long_sequence) { - accel_state *state = NULL; - state = test_fabricate_1d_state(); - - int gesture = 0; - EXPECT_EQ(0, accel_start_record_gesture(state, &gesture)); - EXPECT_EQ(0, gesture); - - int data[] = {1}; - for (int i=0; i<10000; ++i) { - EXPECT_EQ(0, accel_process_timer_tick(state, data)); - } - - EXPECT_EQ(0, accel_end_record_gesture(state, gesture)); - test_burn_state(&state); -} - -TEST(AccelTest, end_to_end_test_single_recording) { - accel_state *state = NULL; - state = test_fabricate_1d_state(); - - int gesture = 0; - EXPECT_EQ(0, accel_start_record_gesture(state, &gesture)); - EXPECT_EQ(0, gesture); - - int data[] = {1}; - for (int i=0; i<10; ++i) { - data[0] = i*100; - EXPECT_EQ(0, accel_process_timer_tick(state, data)); - } - - EXPECT_EQ(0, accel_end_record_gesture(state, gesture)); - - int prev_affinity = 0; - for (int i=0; i<10; ++i) { - data[0] = i*100; - int gesture_found = 1; - int affinity_of_gesture = 1; - ASSERT_EQ(0, accel_process_timer_tick(state, data)); - ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture)); - ASSERT_EQ(gesture, gesture_found); - if (i != 0) { - ASSERT_LT(affinity_of_gesture, prev_affinity) << "i=" << i; - } - prev_affinity = affinity_of_gesture; - } - - test_burn_state(&state); -} - -TEST(AccelTest, end_to_end_test_multiple_recordings) { - // g_1(x) = x, g_2(x) = x*x. Sample data is f(x) = 2x, we want to verify that g_1 is chosen over g_2. - accel_state *state = NULL; - state = test_fabricate_1d_state(); - - int first_gesture = 0; - EXPECT_EQ(0, accel_start_record_gesture(state, &first_gesture)); - EXPECT_EQ(0, first_gesture); - - int data[] = {1}; - for (int i=0; i<10; ++i) { - data[0] = i; - EXPECT_EQ(0, accel_process_timer_tick(state, data)); - } - - EXPECT_EQ(0, accel_end_record_gesture(state, first_gesture)); - - int second_gesture = 0; - EXPECT_EQ(0, accel_start_record_gesture(state, &second_gesture)); - EXPECT_NE(first_gesture, second_gesture); - - for (int i=0; i<10; ++i) { - data[0] = i*i; - EXPECT_EQ(0, accel_process_timer_tick(state, data)); - } - - EXPECT_EQ(0, accel_end_record_gesture(state, second_gesture)); - - int prev_affinity = 0; - for (int i=0; i<10; ++i) { - data[0] = i*2; - int gesture_found = 1; - int affinity_of_gesture = 1; - ASSERT_EQ(0, accel_process_timer_tick(state, data)); - ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture)); - ASSERT_EQ(first_gesture, gesture_found); - prev_affinity = affinity_of_gesture; - } - - test_burn_state(&state); -} - -int main (int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - - int returnValue; - - //Do whatever setup here you will need for your tests here - // - // - - returnValue = RUN_ALL_TESTS(); - - //Do Your teardown here if required - // - // - - return returnValue; -} diff --git a/test/callback_util.h b/test/callback_util.h new file mode 100644 index 0000000..8d7f830 --- /dev/null +++ b/test/callback_util.h @@ -0,0 +1,32 @@ +#ifndef TEST_CALLBACK_UTIL_H +#define TEST_CALLBACK_UTIL_H + +#ifndef IS_NOT_PEBBLE +#ifndef PEBBLE +#define PEBBLE +#endif +#endif + +#ifdef IS_NOT_PEBBLE +#include +#else +#error // no support yet. +#endif + +// TODO: this is so hack. + +#define TEST_CALLBACK_NAME(testClass, testName, uniqueIdentifier) \ + generated_##testClass##_##testName##_##uniqueIdentifier##_name + +#define TEST_CALLBACK_COUNTER(testClass, testName, uniqueIdentifier) \ + generated_##testClass##_##testName##_##uniqueIdentifier##_counter + +#define TEST_CALLBACK(returnSignature, testClass, testName, uniqueIdentifier, args...) \ + /* count the number of times the callback was invoked. */ \ + uint32_t generated_##testClass##_##testName##_##uniqueIdentifier##_counter = 0; \ + /* define a method that proxies calls to the real method. */ \ + returnSignature generated_##testClass##_##testName##_##uniqueIdentifier##_name(args) { \ + /* increment the counter */ \ + generated_##testClass##_##testName##_##uniqueIdentifier##_counter += 1; + +#endif diff --git a/test/main.cc b/test/main.cc new file mode 100644 index 0000000..bfadb96 --- /dev/null +++ b/test/main.cc @@ -0,0 +1,764 @@ +#include "gtest/gtest.h" + +#include "../src/moving_avg_ticker.h" +#include "../src/accel.h" + +// TODO: use accel_test_util.h +#include "callback_util.h" +#include "util.h" + +accel_state *test_fabricate_state_with_callback(int dimensions, accel_callback callback, const int threshold) { + accel_state *state = NULL; + int result = accel_generate_state(&state, dimensions, 1, callback, threshold); + EXPECT_EQ(ACCEL_SUCCESS, result); + if (ACCEL_SUCCESS != result) { + int *myNull = NULL; + myNull = 0; + } + EXPECT_NE(VOID_NULL, state); + if (VOID_NULL == state) { + int *myNull = NULL; + myNull = 0; + } + return state; +} + +accel_state *test_fabricate_1d_state_with_callback(accel_callback callback, const int threshold) { + return test_fabricate_state_with_callback(1, callback, threshold); +} + +accel_state *test_fabricate_3d_state_with_callback(accel_callback callback, const int threshold) { + return test_fabricate_state_with_callback(3, callback, threshold); +} + +accel_state *test_fabricate_state(int dimensions) { return test_fabricate_state_with_callback(dimensions, NULL, 0); } + +accel_state *test_fabricate_1d_state() { return test_fabricate_state(1); } + +accel_state *test_fabricate_3d_state() { return test_fabricate_state(3); } + +void test_burn_state(accel_state **state) { + int result = accel_destroy_state(state); + EXPECT_EQ(ACCEL_SUCCESS, result); + EXPECT_EQ(VOID_NULL, *state); +} + +TEST(AccelFuzzTest, generate_state_null_state) { + int result = accel_generate_state(NULL, 3, 1, NULL, 0); + EXPECT_EQ(result, ACCEL_PARAM_ERROR); +} + +TEST(AccelFuzzTest, generate_state_negative_or_zero_dimensions) { + accel_state *state = NULL; + // 0 dimensions must fail + int result = accel_generate_state(&state, 0, 1, NULL, 0); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // 1 dimension must succeed. + state = NULL; + result = accel_generate_state(&state, 1, 1, NULL, 0); + EXPECT_EQ(ACCEL_SUCCESS, result); + accel_destroy_state(&state); +} + +TEST(AccelFuzzTest, generate_state_invalid_threshold_with_callback_params) { + accel_state *state = NULL; + accel_callback nonNullCallback = (accel_callback)1; + + // Fails for zero. + int result = accel_generate_state(&state, 1, 1, nonNullCallback, 0); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Succeeds for 1. + result = accel_generate_state(&state, 1, 1, nonNullCallback, 1); + EXPECT_EQ(ACCEL_SUCCESS, result); +} + +TEST(AccelFuzzTest, generate_state_invalid_window_size) { + accel_state *state = NULL; + int result = 0; + + // Size 0 must fail + result = accel_generate_state(&state, 1, 0, NULL, 0); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Size 1 must succeed + result = accel_generate_state(&state, 1, 1, NULL, 0); + EXPECT_EQ(ACCEL_SUCCESS, result); + EXPECT_NE(VOID_NULL, state); +} + +TEST(AccelFuzzTest, generate_state_threshold_fuzzing) { + accel_state *state = NULL; + + // Threshold of 1 with a non-null callback succeeds + EXPECT_EQ(NULL, state); + EXPECT_EQ(ACCEL_SUCCESS, accel_generate_state(&state, 1, 1, (accel_callback)0x1, 1)); + accel_destroy_state(&state); + + // Threshold of 1 with a null callback fails + EXPECT_EQ(NULL, state); + EXPECT_EQ(ACCEL_PARAM_ERROR, accel_generate_state(&state, 1, 1, (accel_callback)NULL, 1)); + + // Threshold of 0 with a null callback succeeds + EXPECT_EQ(NULL, state); + EXPECT_EQ(ACCEL_SUCCESS, accel_generate_state(&state, 1, 1, (accel_callback)NULL, 0)); + accel_destroy_state(&state); + + // Threshold of 0 with a non-null callback fails + EXPECT_EQ(NULL, state); + EXPECT_EQ(ACCEL_PARAM_ERROR, accel_generate_state(&state, 1, 1, (accel_callback)0x1, 0)); +} + +TEST(AccelFuzzTest, accel_generate_state_null_callback) { + int result = 0; + accel_state *state = NULL; + + // Null callback must be successful + result = accel_generate_state(&state, 1, 1, NULL, 0); +} + +TEST_CALLBACK(int, AccelFuzzTest, accel_generate_state_valid_callback, myTest, accel_state *state, uint16_t gesture_id, + uint32_t offset_found, bool *reset_gesture) +*reset_gesture = true; +return ACCEL_SUCCESS; +} + +TEST(AccelFuzzTest, accel_generate_state_valid_callback) { + uint16_t gesture_id = 0; + accel_state *state = NULL; + + // Non-null callback, watch it iterate over this stuff. + state = test_fabricate_1d_state_with_callback( + &TEST_CALLBACK_NAME(AccelFuzzTest, accel_generate_state_valid_callback, myTest), 10); + + EXPECT_EQ(ACCEL_SUCCESS, accel_start_record_gesture(state, &gesture_id)); + + for (int i = 0; i < 100; ++i) { + int data[1] = {i}; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + } + EXPECT_EQ(ACCEL_SUCCESS, accel_end_record_gesture(state, gesture_id)); + + for (int i = 0; i < 100; ++i) { + int data[1] = {i}; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + } + EXPECT_GT(TEST_CALLBACK_COUNTER(AccelFuzzTest, accel_generate_state_valid_callback, myTest), (uint32_t)0); +} + +TEST(AccelFuzzTest, accel_destroy_state_invalid_input) { + int result = 0; + + // Destroying x (x = NULL) will fail. + result = accel_destroy_state(NULL); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Destroying x (*x = NULL) will fail. + accel_state *state = NULL; + result = accel_destroy_state(&state); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Destroying x (*x != NULL) should succeed. + state = test_fabricate_1d_state(); + + // Destroy the state + result = accel_destroy_state(&state); + EXPECT_EQ(ACCEL_SUCCESS, result); + EXPECT_EQ(VOID_NULL, state); +} + +TEST(AccelFuzzTest, accel_start_record_gesture_invalid_input) { + int result = 0; + uint16_t gesture_id = 0; + result = accel_start_record_gesture(NULL, &gesture_id); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + accel_state *state = test_fabricate_1d_state(); + + result = accel_start_record_gesture(state, NULL); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + test_burn_state(&state); +} + +TEST(AccelFuzzTest, accel_end_record_gesture_invalid_input) { + int result = 0; + accel_state *state = NULL; + + // Null state: + result = accel_end_record_gesture(NULL, 1); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Negative index: + state = test_fabricate_1d_state(); + result = accel_end_record_gesture(state, -1); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + test_burn_state(&state); + + // Unused index: + state = test_fabricate_1d_state(); + result = accel_end_record_gesture(state, 1); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + test_burn_state(&state); + + // Verify it works for valid indexes. + state = test_fabricate_1d_state(); + uint16_t gesture = 1234; + result = accel_start_record_gesture(state, &gesture); + EXPECT_NE(1234, gesture); + EXPECT_EQ(ACCEL_SUCCESS, result); + int data[1] = {1}; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + + result = accel_end_record_gesture(state, gesture); + EXPECT_EQ(ACCEL_SUCCESS, result) << "gesture " << gesture << " couldn't be recorded correctly" << std::endl; + test_burn_state(&state); +} + +TEST(AccelFuzzTest, accel_process_timer_tick_invalid_input) { + int result = 0; + int accel_data = 0; + accel_state *state = NULL; + + // Null state value. + result = accel_process_timer_tick(NULL, &accel_data); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Null array input. + state = test_fabricate_1d_state(); + result = accel_process_timer_tick(state, NULL); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + test_burn_state(&state); +} + +TEST(AccelFuzzTest, accel_find_most_likely_gesture_invalid_input) { + int result = 0; + uint16_t gesture_id = 0; + int affinity = 0; + accel_state *state = NULL; + + // Null state: + result = accel_find_most_likely_gesture(NULL, &gesture_id, &affinity); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + + // Null gesture id is passed in. + state = test_fabricate_1d_state(); + result = accel_find_most_likely_gesture(state, NULL, &affinity); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + test_burn_state(&state); + + // Null affinity is passed in. + state = test_fabricate_1d_state(); + result = accel_find_most_likely_gesture(state, &gesture_id, NULL); + EXPECT_EQ(ACCEL_PARAM_ERROR, result); + test_burn_state(&state); + + // No tests exist, but otherwise valid parameters. + state = test_fabricate_1d_state(); + result = accel_find_most_likely_gesture(state, &gesture_id, &affinity); + EXPECT_EQ(UINT16_MAX, gesture_id); + EXPECT_EQ(ACCEL_NO_VALID_GESTURE, affinity); + EXPECT_EQ(ACCEL_NO_VALID_GESTURE, result); +} + +TEST(AccelTest, accel_generate_and_destroy) { + accel_state *state = NULL; + for (int i = 1; i < 10; ++i) { + EXPECT_EQ(VOID_NULL, state) << "i = " << i; + EXPECT_EQ(ACCEL_SUCCESS, accel_generate_state(&state, 2 * i, i, NULL, 0)) << "i = " << i; + EXPECT_EQ(ACCEL_SUCCESS, accel_destroy_state(&state)) << "i = " << i; + EXPECT_EQ(VOID_NULL, state) << "i = " << i; + } +} + +TEST(AccelTest, start_recording_and_close_many_gestures) { + accel_state *state = NULL; + state = test_fabricate_1d_state(); + + int data[1] = {0}; + for (int i = 0; i < 10; ++i) { + uint16_t gesture = 0; + ASSERT_EQ(0, accel_start_record_gesture(state, &gesture)); + ASSERT_EQ(i, gesture); + ASSERT_EQ(0, accel_process_timer_tick(state, data)); + } + for (int i = 0; i < 10; ++i) { + ASSERT_EQ(0, accel_end_record_gesture(state, i)); + } + test_burn_state(&state); +} + +TEST(AccelTest, record_incredibly_long_sequence) { + accel_state *state = NULL; + state = test_fabricate_1d_state(); + + uint16_t gesture = 0; + EXPECT_EQ(ACCEL_SUCCESS, accel_start_record_gesture(state, &gesture)); + EXPECT_EQ(ACCEL_SUCCESS, gesture); + + int data[] = {1}; + for (int i = 0; i < 10000; ++i) { + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + } + + EXPECT_EQ(ACCEL_SUCCESS, accel_end_record_gesture(state, gesture)); + test_burn_state(&state); +} + +TEST(AccelTest, end_to_end_test_single_recording) { + accel_state *state = NULL; + state = test_fabricate_1d_state(); + + uint16_t gesture = 0; + EXPECT_EQ(ACCEL_SUCCESS, accel_start_record_gesture(state, &gesture)); + EXPECT_EQ(ACCEL_SUCCESS, gesture); + + int data[] = {1}; + for (int i = 0; i < 10; ++i) { + data[0] = i * 100; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + } + + EXPECT_EQ(ACCEL_SUCCESS, accel_end_record_gesture(state, gesture)); + + int prev_affinity = 0; + for (int i = 0; i < 10; ++i) { + data[0] = i * 100; + uint16_t gesture_found = 1; + int affinity_of_gesture = 1; + ASSERT_EQ(0, accel_process_timer_tick(state, data)); + ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture)); + ASSERT_EQ(gesture, gesture_found); + if (i != 0) { + ASSERT_LT(affinity_of_gesture, prev_affinity) << "i=" << i; + } + prev_affinity = affinity_of_gesture; + } + + test_burn_state(&state); +} + +TEST(AccelTest, end_to_end_test_multiple_recordings) { + // g_1(x) = x, g_2(x) = x*x. Sample data is f(x) = 2x, we want to verify that g_1 is chosen over g_2. + accel_state *state = NULL; + state = test_fabricate_1d_state(); + + uint16_t first_gesture = 0; + EXPECT_EQ(ACCEL_SUCCESS, accel_start_record_gesture(state, &first_gesture)); + EXPECT_EQ(ACCEL_SUCCESS, first_gesture); + + int data[] = {1}; + for (int i = 0; i < 10; ++i) { + data[0] = i; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + } + + EXPECT_EQ(ACCEL_SUCCESS, accel_end_record_gesture(state, first_gesture)); + + uint16_t second_gesture = 0; + EXPECT_EQ(ACCEL_SUCCESS, accel_start_record_gesture(state, &second_gesture)); + EXPECT_NE(first_gesture, second_gesture); + + for (int i = 0; i < 10; ++i) { + data[0] = i * i; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + } + + EXPECT_EQ(ACCEL_SUCCESS, accel_end_record_gesture(state, second_gesture)); + + int prev_affinity = 0; + for (int i = 0; i < 10; ++i) { + data[0] = i * 2; + uint16_t gesture_found = 1; + int affinity_of_gesture = 1; + ASSERT_EQ(0, accel_process_timer_tick(state, data)); + ASSERT_EQ(0, accel_find_most_likely_gesture(state, &gesture_found, &affinity_of_gesture)); + ASSERT_EQ(first_gesture, gesture_found); + prev_affinity = affinity_of_gesture; + } + + test_burn_state(&state); +} + +TEST(AccelTest, test_fuzz_reset_affinities) { + accel_state *state = NULL; + + // Null accel states. + EXPECT_EQ(ACCEL_PARAM_ERROR, accel_reset_affinities_for_gesture(NULL, 0)); + + // No recorded accelerations + state = test_fabricate_1d_state(); + EXPECT_EQ(ACCEL_PARAM_ERROR, accel_reset_affinities_for_gesture(state, 0)); + + uint16_t gesture_id = 0; + EXPECT_EQ(ACCEL_SUCCESS, accel_start_record_gesture(state, &gesture_id)); + + // A recording gesture with no data. + EXPECT_EQ(ACCEL_PARAM_ERROR, accel_reset_affinities_for_gesture(state, gesture_id)); + + int data[1] = {0}; + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + + // A recording gesture with some data. + EXPECT_EQ(ACCEL_PARAM_ERROR, accel_reset_affinities_for_gesture(state, gesture_id)); + + EXPECT_EQ(ACCEL_SUCCESS, accel_end_record_gesture(state, gesture_id)); + + // No ticks have been recorded. + EXPECT_EQ(ACCEL_SUCCESS, accel_reset_affinities_for_gesture(state, gesture_id)); + + uint16_t gesture = 1; + int initial_distance = 1; + int after_run_distance = 1; + int after_reset_distance = 1; + + EXPECT_EQ(ACCEL_SUCCESS, accel_find_most_likely_gesture(state, &gesture, &initial_distance)); + + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_process_timer_tick(state, data)); + EXPECT_EQ(ACCEL_SUCCESS, accel_find_most_likely_gesture(state, &gesture, &after_run_distance)); + + EXPECT_EQ(ACCEL_SUCCESS, accel_reset_affinities_for_gesture(state, gesture_id)); + EXPECT_EQ(ACCEL_SUCCESS, accel_find_most_likely_gesture(state, &gesture, &after_reset_distance)); + + EXPECT_EQ(initial_distance, after_reset_distance); + EXPECT_GE(after_reset_distance, after_run_distance); + + test_burn_state(&state); +} + +TEST(MovingAvgTicker, InvalidInputValues) { + + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(1, 0, &allocated); + EXPECT_EQ(NULL, allocated); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + retval = allocate_moving_avg(0, 1, &allocated); + EXPECT_EQ(NULL, allocated); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + + // Pass in a non-null pointer-to-pointer. + moving_avg_values *initial_non_null_value = (moving_avg_values *)1; + allocated = initial_non_null_value; + retval = allocate_moving_avg(1, 1, &allocated); + EXPECT_EQ(initial_non_null_value, allocated); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + // TODO: find a way to test failing malloc/calloc. +} + +TEST(MovingAvgTicker, AllocatesAndFreesCorrectly) { + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(1, 1, &allocated); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_NE(VOID_NULL, allocated->wbuf); + EXPECT_EQ(retval, 0); + + retval = free_moving_avg(&allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(VOID_NULL, allocated); +} + +TEST(MovingAvgTicker, ResetsCorrectly) { + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(1, 1, &allocated); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + + retval = reset_moving_avg(NULL); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + + retval = reset_moving_avg(allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); +} + +TEST(MovingAvgTicker, AppendsCorrectly1_1) { + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(1, 1, &allocated); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + + bool is_at_end = false; + retval = append_to_moving_avg(allocated, 1, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + int frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(1, frame); + + is_at_end = false; + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(2, frame); +} + +TEST(MovingAvgTicker, AppendsCorrectly2_1) { + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(2, 1, &allocated); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + + bool is_at_end = false; + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + int frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(1, frame); + + is_at_end = false; + retval = append_to_moving_avg(allocated, 4, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(3, frame); + + is_at_end = false; + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(3, frame); +} + +TEST(MovingAvgTicker, AppendsCorrectly1_2) { + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(1, 2, &allocated); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + + bool is_at_end = false; + retval = append_to_moving_avg(allocated, 1, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_FALSE(is_at_end); + + retval = append_to_moving_avg(allocated, 1, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + int frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(2, frame); + + is_at_end = false; + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_FALSE(is_at_end); + + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(4, frame); +} + +TEST(MovingAvgTicker, AppendsCorrectly2_2) { + moving_avg_values *allocated = NULL; + int retval = allocate_moving_avg(2, 2, &allocated); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + + bool is_at_end = false; + retval = append_to_moving_avg(allocated, 1, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_FALSE(is_at_end); + + retval = append_to_moving_avg(allocated, 1, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + int frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(1, frame); + + is_at_end = false; + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_FALSE(is_at_end); + + retval = append_to_moving_avg(allocated, 2, &is_at_end); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_TRUE(is_at_end); + + frame = 0; + retval = get_latest_frame_moving_avg(allocated, &frame); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(3, frame); + + retval = free_moving_avg(&allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_EQ(VOID_NULL, allocated); +} + +TEST(MovingAvgTicker, AppendToInvalid) { + int retval = 0; + bool tru = true; + retval = append_to_moving_avg((moving_avg_values *)NULL, 1, &tru); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); +} + +TEST(MovingAvgTicker, AppendWithInvalidAtEnd) { + int retval = 0; + moving_avg_values *allocated = NULL; + retval = allocate_moving_avg(2, 2, &allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_NE(VOID_NULL, allocated); + + retval = append_to_moving_avg(allocated, 1, (bool *)NULL); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); +} + +TEST(MovingAvgTicker, InvalidLatestFrameParams) { + int retval = 0; + int frame = 0; + retval = get_latest_frame_moving_avg(NULL, &frame); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + + moving_avg_values *allocated = NULL; + retval = allocate_moving_avg(2, 2, &allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, retval); + EXPECT_NE(VOID_NULL, allocated); + + retval = get_latest_frame_moving_avg(allocated, (int *)NULL); + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); + + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); +} + +TEST(MovingAvgTickerFuzzTest, allocate_moving_avg) { + moving_avg_values *allocated = NULL; + + // Test with zero num_wbuf + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(0, 1, &allocated)); + + // Test with zero subtotal_size + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, 0, &allocated)); + + // Test with NULL pointer + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, 0, NULL)); + + // Test with non-NULL pointer-pointer + allocated = (moving_avg_values *)0x1; + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, 1, &allocated)); + + // Test with success, to validate that there was only one difference between this and the above tests. + allocated = NULL; + EXPECT_EQ(MOVING_AVG_SUCCESS, allocate_moving_avg(1, 1, &allocated)); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); + EXPECT_EQ(VOID_NULL, allocated); +} + +TEST(MovingAvgTickerFuzzTest, reset_moving_avg) { + // Test with null pointer + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, reset_moving_avg(NULL)); +} + +TEST(MovingAvgTickerFuzzTest, append_to_moving_avg) { + // Setup: + bool is_at_end = false; + moving_avg_values *allocated = NULL; + EXPECT_EQ(MOVING_AVG_SUCCESS, allocate_moving_avg(1, 1, &allocated)); + + // Test with null pointer + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, append_to_moving_avg(NULL, 1, &is_at_end)); + + // Test with null isAtEnd + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, append_to_moving_avg(allocated, 1, NULL)); + + // Test with all valid input + EXPECT_EQ(MOVING_AVG_SUCCESS, append_to_moving_avg(allocated, 1, &is_at_end)); + + // Cleanup: + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); +} + +TEST(MovingAvgTickerFuzzTest, get_latest_frame_moving_avg) { + int frame = 0; + moving_avg_values *allocated = NULL; + EXPECT_EQ(MOVING_AVG_SUCCESS, allocate_moving_avg(1, 1, &allocated)); + + // Both are NULL + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, get_latest_frame_moving_avg(NULL, NULL)); + + // First is NULL + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, get_latest_frame_moving_avg(NULL, &frame)); + + // Second is NULL + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, get_latest_frame_moving_avg(allocated, NULL)); + + // Validate it works to justify the above unit tests + EXPECT_EQ(MOVING_AVG_SUCCESS, get_latest_frame_moving_avg(allocated, &frame)); + + // Cleanup + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); +} + +TEST(MovingAvgTickerFuzzTest, free_moving_avg) { + // Test with null pointer-pointer + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, free_moving_avg(NULL)); + + // Test with null pointer + moving_avg_values *allocated = NULL; + EXPECT_EQ(MOVING_AVG_PARAM_ERROR, free_moving_avg(&allocated)); + + // Test with null wbuf + EXPECT_EQ(MOVING_AVG_SUCCESS, allocate_moving_avg(1, 1, &allocated)); + EXPECT_NE(VOID_NULL, allocated); + free(allocated->wbuf); + allocated->wbuf = NULL; + // TODO: successfully completes, even with invalid input. + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); + EXPECT_EQ(VOID_NULL, allocated); + + // Test normal path. + EXPECT_EQ(MOVING_AVG_SUCCESS, allocate_moving_avg(1, 1, &allocated)); + EXPECT_NE(VOID_NULL, allocated); + EXPECT_EQ(MOVING_AVG_SUCCESS, free_moving_avg(&allocated)); + EXPECT_EQ(VOID_NULL, allocated); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + + int returnValue; + + // Do whatever setup here you will need for your tests here + // + // + + returnValue = RUN_ALL_TESTS(); + + // Do Your teardown here if required + // + // + + return returnValue; +} diff --git a/test/moving_avg_ticker_test.cc b/test/moving_avg_ticker_test.cc deleted file mode 100644 index 59bcd5c..0000000 --- a/test/moving_avg_ticker_test.cc +++ /dev/null @@ -1,341 +0,0 @@ -#include "gtest/gtest.h" - -#include "../src/moving_avg_ticker.h" - -const void * void_null = NULL; - -TEST(MovingAvgTicker, InvalidInputValues) { - - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(1, 0, &allocated); - EXPECT_EQ(NULL, allocated); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - retval = allocate_moving_avg(0, 1, &allocated); - EXPECT_EQ(NULL, allocated); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - - // Pass in a non-null pointer-to-pointer. - moving_avg_values *initial_non_null_value = (moving_avg_values *) 1; - allocated = initial_non_null_value; - retval = allocate_moving_avg(1, 1, &allocated); - EXPECT_EQ(initial_non_null_value, allocated); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - // TODO: find a way to test failing malloc/calloc. -} - -TEST(MovingAvgTicker, AllocatesAndFreesCorrectly) { - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(1, 1, &allocated); - EXPECT_NE(void_null, allocated); - EXPECT_NE(void_null, allocated->wbuf); - EXPECT_EQ(retval, 0); - - retval = free_moving_avg(&allocated); - EXPECT_EQ(0, retval); - EXPECT_EQ(void_null, allocated); -} - -TEST(MovingAvgTicker, ResetsCorrectly) { - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(1, 1, &allocated); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, retval); - - retval = reset_moving_avg(NULL); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - - retval = reset_moving_avg(allocated); - EXPECT_EQ(0, retval); -} - -TEST(MovingAvgTicker, AppendsCorrectly1_1) { - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(1, 1, &allocated); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, retval); - - bool is_at_end = false; - retval = append_to_moving_avg(allocated, 1, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - int frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(1, frame); - - is_at_end = false; - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(2, frame); -} - -TEST(MovingAvgTicker, AppendsCorrectly2_1) { - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(2, 1, &allocated); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, retval); - - bool is_at_end = false; - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - int frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(1, frame); - - is_at_end = false; - retval = append_to_moving_avg(allocated, 4, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(3, frame); - - is_at_end = false; - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(3, frame); -} - -TEST(MovingAvgTicker, AppendsCorrectly1_2) { - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(1, 2, &allocated); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, retval); - - bool is_at_end = false; - retval = append_to_moving_avg(allocated, 1, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_FALSE(is_at_end); - - retval = append_to_moving_avg(allocated, 1, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - int frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(2, frame); - - is_at_end = false; - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_FALSE(is_at_end); - - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(4, frame); -} - -TEST(MovingAvgTicker, AppendsCorrectly2_2) { - moving_avg_values *allocated = NULL; - int retval = allocate_moving_avg(2, 2, &allocated); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, retval); - - bool is_at_end = false; - retval = append_to_moving_avg(allocated, 1, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_FALSE(is_at_end); - - retval = append_to_moving_avg(allocated, 1, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - int frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(1, frame); - - is_at_end = false; - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_FALSE(is_at_end); - - retval = append_to_moving_avg(allocated, 2, &is_at_end); - EXPECT_EQ(0, retval); - EXPECT_TRUE(is_at_end); - - frame = 0; - retval = get_latest_frame_moving_avg(allocated, &frame); - EXPECT_EQ(0, retval); - EXPECT_EQ(3, frame); - - retval = free_moving_avg(&allocated); - EXPECT_EQ(0, retval); - EXPECT_EQ(void_null, allocated); -} - -TEST(MovingAvgTicker, AppendToInvalid) { - int retval = 0; - bool tru = true; - retval = append_to_moving_avg((moving_avg_values *) NULL, 1, &tru); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); -} - -TEST(MovingAvgTicker, AppendWithInvalidAtEnd) { - int retval = 0; - moving_avg_values *allocated = NULL; - retval = allocate_moving_avg(2, 2, &allocated); - EXPECT_EQ(0, retval); - EXPECT_NE(void_null, allocated); - - retval = append_to_moving_avg(allocated, 1, (bool *)NULL); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - - EXPECT_EQ(0, free_moving_avg(&allocated)); -} - -TEST(MovingAvgTicker, InvalidLatestFrameParams) { - int retval = 0; - int frame = 0; - retval = get_latest_frame_moving_avg(NULL, &frame); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - - moving_avg_values *allocated = NULL; - retval = allocate_moving_avg(2, 2, &allocated); - EXPECT_EQ(0, retval); - EXPECT_NE(void_null, allocated); - - retval = get_latest_frame_moving_avg(allocated, (int *) NULL); - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, retval); - - EXPECT_EQ(0, free_moving_avg(&allocated)); -} - -TEST(MovingAvgTickerFuzzTest, allocate_moving_avg) { - moving_avg_values *allocated = NULL; - - // Test with negative num_wbuf - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(-1, 1, &allocated)); - - // Test with zero num_wbuf - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(0, 1, &allocated)); - - // Test with negative subtotal_size - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, -1, &allocated)); - - // Test with zero subtotal_size - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, 0, &allocated)); - - // Test with NULL pointer - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, 0, NULL)); - - // Test with non-NULL pointer-pointer - allocated = (moving_avg_values *) 0x1; - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, allocate_moving_avg(1, 1, &allocated)); - - // Test with success, to validate that there was only one difference between this and the above tests. - allocated = NULL; - EXPECT_EQ(0, allocate_moving_avg(1, 1, &allocated)); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, free_moving_avg(&allocated)); - EXPECT_EQ(void_null, allocated); -} - -TEST(MovingAvgTickerFuzzTest, reset_moving_avg) { - // Test with null pointer - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, reset_moving_avg(NULL)); -} - -TEST(MovingAvgTickerFuzzTest, append_to_moving_avg) { - // Setup: - bool is_at_end = false; - moving_avg_values *allocated = NULL; - EXPECT_EQ(0, allocate_moving_avg(1, 1, &allocated)); - - // Test with null pointer - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, append_to_moving_avg(NULL, 1, &is_at_end)); - - // Test with null isAtEnd - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, append_to_moving_avg(allocated, 1, NULL)); - - // Test with all valid input - EXPECT_EQ(0, append_to_moving_avg(allocated, 1, &is_at_end)); - - // Cleanup: - EXPECT_EQ(0, free_moving_avg(&allocated)); -} - -TEST(MovingAvgTickerFuzzTest, get_latest_frame_moving_avg) { - int frame = 0; - moving_avg_values *allocated = NULL; - EXPECT_EQ(0, allocate_moving_avg(1, 1, &allocated)); - - // Both are NULL - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, get_latest_frame_moving_avg(NULL, NULL)); - - // First is NULL - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, get_latest_frame_moving_avg(NULL, &frame)); - - // Second is NULL - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, get_latest_frame_moving_avg(allocated, NULL)); - - // Validate it works to justify the above unit tests - EXPECT_EQ(0, get_latest_frame_moving_avg(allocated, &frame)); - - // Cleanup - EXPECT_EQ(0, free_moving_avg(&allocated)); -} - -TEST(MovingAvgTickerFuzzTest, free_moving_avg) { - // Test with null pointer-pointer - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, free_moving_avg(NULL)); - - // Test with null pointer - moving_avg_values *allocated = NULL; - EXPECT_EQ(MOVING_AVG_PARAM_ERROR, free_moving_avg(&allocated)); - - // Test with null wbuf - EXPECT_EQ(0, allocate_moving_avg(1, 1, &allocated)); - EXPECT_NE(void_null, allocated); - free(allocated->wbuf); - allocated->wbuf = NULL; - // TODO: successfully completes, even with invalid input. - EXPECT_EQ(0, free_moving_avg(&allocated)); - EXPECT_EQ(void_null, allocated); - - // Test normal path. - EXPECT_EQ(0, allocate_moving_avg(1, 1, &allocated)); - EXPECT_NE(void_null, allocated); - EXPECT_EQ(0, free_moving_avg(&allocated)); - EXPECT_EQ(void_null, allocated); -} - -int main (int argc, char** argv) { - ::testing::InitGoogleTest(&argc, argv); - - int returnValue; - - //Do whatever setup here you will need for your tests here - // - // - - returnValue = RUN_ALL_TESTS(); - - //Do Your teardown here if required - // - // - - return returnValue; -} diff --git a/test/util.h b/test/util.h new file mode 100644 index 0000000..789a2ab --- /dev/null +++ b/test/util.h @@ -0,0 +1,6 @@ +#ifndef TEST_UTILS +#define TEST_UTILS + +const void *VOID_NULL = NULL; + +#endif