Monday, November 13, 2017

Hop to it - Counting Jumps using a cell phone accelerometer

Last week I was harshly schooled in the difference between theoretical physics and the reality of cell phone hardware. While it may be true that you can calculate distance from acceleration, the hardware on the LG G6 isn't precise enough to do so. But I remain undeterred!

I return instead, to my original and simpler problem: can I use my phone's sensors to determine many jumps have I done in a jump rope session? The accelerometer is clearly one way to approximate this. Just check out the graph below. It shows three bursts of me jumping, with each set of jumps followed by a pause:

The simplest strategy here is to count the peaks of the graph. Swapping out the code to calculate distance with code to do this counting was simple enough. All the code can be found here, but the relevant lines as follows:

;; Counting the peaks in a given accelerometer stream.  This should
;; give us a rough approximation of jumps
(define (count-peaks accum data)
  (let ((lower 0)
 (upper 10)
 (verbose? #t)
 (a-now (+ (data 1) (data 2) (data 3)))
 (rising? (accum 0))
 (num-peaks (accum 1)))
    (cond ((and rising? (>= a-now upper))
    (if verbose?
        (show "peak discovered: " a-now))
    (list #f (+ 1 num-peaks)))
   ((and (not rising?) (<= a-now lower))
    (list #t num-peaks))
   (else
    (list rising? num-peaks)))))

For now, a peak is defined as having total acceleration over 10m/s2. I arrived at this number the way all important mathematical constants are: I stood in my living room and jumped up and down. Set the peak height too low, and simple movements are marked as jump. Set it too high, and not all the jumps are counted.

A smarter approach would be to make this number adaptive, making it somehow dependent on the data set itself. But for now, a constant of 10 seems to work OK.

With this code change, I was able to record a 10 jump data file and run it through tinyscheme:

While this was functional, I was curious if I could streamline running this script. Switching to Termux, then entering the right filename in emacs, and then evaluating the code in the *scheme* buffer was all a bit much.

Fortunately, Termux has the answer: Termux:Task. This is an add on for Tasker that allows you to kick off shell scripts under Termux. The first order of business was to wrap up the scheme code as a shell script. That wasn't hard:

#!/data/data/com.termux/files/usr/bin/sh

SRC_DIR=$HOME/dt/i2x/code/android-accelerometer-playtime/
MAIN_SCM=$SRC_DIR/main.scm

usage () {
    echo "Usage: `basename $0` /path/to/data.csv"
    exit
}

if [ -f "$1" ] ; then
    exec tinyscheme -c "(define *src-dir* \"$SRC_DIR\") (load \"$MAIN_SCM\") (show (count-jumps \"$1\"))"
else
    usage
fi

tinyscheme can be invoked with -c which allows for running arbitrary scheme expressions. I'm using this to initialize and run parameterized code.

From there, I soft linked this script under $HOME/.termux/tasker. This allowed the script to be visible inside of Tasker:

Note the use of %asfile1. This variable is set by AutoShare, a Tasker plugin that lets you kick off actions based on the system sharing menu.

With the Tasker code in place, I can record data in the Physics Toolbox Sensor Suite app, then share that data with the Count Jumps AutoShare command. This kicks off the count_jumps shell script, which invokes tinyscheme and runs my code above. Simple, right? It may be a virtual Rube Goldberg, but it does work, and successfully demonstrates how you can get from an Android App to scheme code with very little effort.

As a final test, here's me knocking out 30 test jumps:

And there you have it, 31 jumps. As approximations go, I'll take it.

No comments:

Post a Comment