Moved exercise threshold values to the exercise type configuration & initial support for curls
parent
c9fa1b8f65
commit
83aa32ee84
|
|
@ -2,7 +2,13 @@
|
||||||
|
|
||||||
Can automatically track exercises while wearing the Bangle.js watch.
|
Can automatically track exercises while wearing the Bangle.js watch.
|
||||||
|
|
||||||
Currently only push ups are supported.
|
Currently only push ups and curls are supported.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
This app is very experimental.
|
||||||
|
It could be and is likely that the threshold values for detecting exercises do not work for everyone.
|
||||||
|
Therefore it would be great if we could improve this app together :-)
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
@ -17,7 +23,6 @@ Press stop to end your exercise.
|
||||||
## TODO
|
## TODO
|
||||||
* Add other exercise types:
|
* Add other exercise types:
|
||||||
* Rope jumps
|
* Rope jumps
|
||||||
* Curls
|
|
||||||
* Sit ups
|
* Sit ups
|
||||||
* ...
|
* ...
|
||||||
* Save exercises to file system
|
* Save exercises to file system
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,35 @@ let lastZeroPassTime = 0;
|
||||||
let lastExerciseCmpltTime = 0;
|
let lastExerciseCmpltTime = 0;
|
||||||
|
|
||||||
let exerciseType = {
|
let exerciseType = {
|
||||||
|
"id": "",
|
||||||
"name": ""
|
"name": ""
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// add new exercises here:
|
||||||
const exerciseTypes = [{
|
const exerciseTypes = [{
|
||||||
"id": "pushup",
|
"id": "pushup",
|
||||||
"name": "Push ups",
|
"name": "Push ups",
|
||||||
"useYaxe": true,
|
"useYaxe": true,
|
||||||
"useZaxe": false
|
"useZaxe": false,
|
||||||
} // add other exercises here
|
"thresholdY": 2500,
|
||||||
|
"thresholdTime": 1400 // mininmal time between two push ups
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "curl",
|
||||||
|
"name": "Curls",
|
||||||
|
"useYaxe": true,
|
||||||
|
"useZaxe": false,
|
||||||
|
"thresholdY": 2500,
|
||||||
|
"thresholdTime": 1000 // mininmal time between two curls
|
||||||
|
}
|
||||||
];
|
];
|
||||||
let exerciseCounter = 0;
|
let exerciseCounter = 0;
|
||||||
|
|
||||||
let layout;
|
let layout;
|
||||||
let recordActive = false;
|
let recordActive = false;
|
||||||
|
|
||||||
/**
|
// Size of average window for data analysis
|
||||||
* Thresholds
|
|
||||||
*/
|
|
||||||
const avgSize = 6;
|
const avgSize = 6;
|
||||||
const pushUpThresholdY = 2500;
|
|
||||||
const pushUpThresholdTime = 1400; // mininmal time between two push ups
|
|
||||||
|
|
||||||
let hrtValue;
|
let hrtValue;
|
||||||
|
|
||||||
|
|
@ -55,7 +64,9 @@ function showMainMenu() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exerciseCounter > 0) {
|
if (exerciseCounter > 0) {
|
||||||
menu["----"] = {};
|
menu["----"] = {
|
||||||
|
value: ""
|
||||||
|
};
|
||||||
menu["Last:"] = {
|
menu["Last:"] = {
|
||||||
value: exerciseCounter + " " + exerciseType.name
|
value: exerciseCounter + " " + exerciseType.name
|
||||||
};
|
};
|
||||||
|
|
@ -101,19 +112,18 @@ function accelHandler(accel) {
|
||||||
if (l > 1) {
|
if (l > 1) {
|
||||||
const p1 = historyAvgY[l - 2];
|
const p1 = historyAvgY[l - 2];
|
||||||
const p2 = historyAvgY[l - 1];
|
const p2 = historyAvgY[l - 1];
|
||||||
const mY = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000);
|
const slopeY = (p2[1] - p1[1]) / (p2[0] / 1000 - p1[0] / 1000);
|
||||||
if (Math.abs(mY) >= pushUpThresholdY) {
|
|
||||||
historyAvgY.shift();
|
|
||||||
historySlopeY.push([t, mY]);
|
|
||||||
//console.log(t, Math.abs(mY));
|
|
||||||
|
|
||||||
const lMY = historySlopeY.length;
|
// we use this data for exercises which can be detected by using Y axis data
|
||||||
if (lMY > 1) {
|
switch (exerciseType.id) {
|
||||||
const pMY1 = historySlopeY[lMY - 2][1];
|
case "pushup":
|
||||||
const pMY2 = historySlopeY[lMY - 1][1];
|
isValidYAxisExercise(slopeY, t);
|
||||||
isValidPushUp(pMY1, pMY2, t);
|
break;
|
||||||
}
|
case "curl":
|
||||||
|
isValidYAxisExercise(slopeY, t);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,55 +133,81 @@ function accelHandler(accel) {
|
||||||
if (l > 1) {
|
if (l > 1) {
|
||||||
const p1 = historyAvgZ[l - 2];
|
const p1 = historyAvgZ[l - 2];
|
||||||
const p2 = historyAvgZ[l - 1];
|
const p2 = historyAvgZ[l - 1];
|
||||||
const mZ = (p2[1] - p1[1]) / (p2[0] - p1[0]);
|
const slopeZ = (p2[1] - p1[1]) / (p2[0] - p1[0]);
|
||||||
historyAvgZ.shift();
|
historyAvgZ.shift();
|
||||||
historySlopeZ.push([p2[0] - p1[0], mZ]);
|
historySlopeZ.push([p2[0] - p1[0], slopeZ]);
|
||||||
|
|
||||||
|
// TODO: we can use this data for some exercises which can be detected by using Z axis data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidPushUp(p1, p2, t) {
|
/*
|
||||||
if (p1 > 0 && p2 < 0) {
|
* Check if slope value of Y-axis data looks like an exercise
|
||||||
|
*
|
||||||
|
* In detail we look for slop values which are bigger than the configured Y threshold for the current exercise
|
||||||
|
* Then we look for two consecutive slope values of which one is above 0 and the other is below zero.
|
||||||
|
* If we find one pair of these values this could be part of one exercise.
|
||||||
|
* Then we look for a pair of values which cross the zero from the otherwise direction
|
||||||
|
*/
|
||||||
|
function isValidYAxisExercise(slopeY, t) {
|
||||||
|
if (!exerciseType) return;
|
||||||
|
|
||||||
if (lastZeroPassType == "-+") {
|
const thresholdY = exerciseType.thresholdY;
|
||||||
console.log(t, "Push up half complete...");
|
const thresholdTime = exerciseType.thresholdTime;
|
||||||
|
const exerciseName = exerciseType.name;
|
||||||
|
|
||||||
layout.progress.label = "*";
|
if (Math.abs(slopeY) >= thresholdY) {
|
||||||
layout.render();
|
historyAvgY.shift();
|
||||||
}
|
historySlopeY.push([t, slopeY]);
|
||||||
|
//console.log(t, Math.abs(slopeY));
|
||||||
|
|
||||||
lastZeroPassType = "+-";
|
const lSlopeY = historySlopeY.length;
|
||||||
lastZeroPassTime = t;
|
if (lSlopeY > 1) {
|
||||||
}
|
const p1 = historySlopeY[lSlopeY - 2][1];
|
||||||
if (p2 > 0 && p1 < 0) {
|
const p2 = historySlopeY[lSlopeY - 1][1];
|
||||||
|
if (p1 > 0 && p2 < 0) {
|
||||||
|
if (lastZeroPassType == "-+") {
|
||||||
|
console.log(t, exerciseName + " half complete...");
|
||||||
|
|
||||||
if (lastZeroPassType == "+-") {
|
layout.progress.label = "*";
|
||||||
// potential complete push up. Let's check the time difference...
|
layout.render();
|
||||||
const tDiffLastPushUp = t - lastExerciseCmpltTime;
|
}
|
||||||
const tDiffStart = t - tStart;
|
|
||||||
console.log(t, "Push up maybe complete?", Math.round(tDiffLastPushUp), Math.round(tDiffStart));
|
|
||||||
|
|
||||||
if ((lastExerciseCmpltTime <= 0 && tDiffStart >= pushUpThresholdTime) || tDiffLastPushUp >= pushUpThresholdTime) {
|
lastZeroPassType = "+-";
|
||||||
console.log(t, "Push up complete!!!");
|
lastZeroPassTime = t;
|
||||||
|
}
|
||||||
|
if (p2 > 0 && p1 < 0) {
|
||||||
|
if (lastZeroPassType == "+-") {
|
||||||
|
// potential complete exercise. Let's check the time difference...
|
||||||
|
const tDiffLastPushUp = t - lastExerciseCmpltTime;
|
||||||
|
const tDiffStart = t - tStart;
|
||||||
|
console.log(t, exerciseName + " maybe complete?", Math.round(tDiffLastPushUp), Math.round(tDiffStart));
|
||||||
|
|
||||||
lastExerciseCmpltTime = t;
|
if ((lastExerciseCmpltTime <= 0 && tDiffStart >= thresholdTime) || tDiffLastPushUp >= thresholdTime) {
|
||||||
exerciseCounter++;
|
console.log(t, exerciseName + " complete!!!");
|
||||||
|
|
||||||
layout.count.label = exerciseCounter;
|
lastExerciseCmpltTime = t;
|
||||||
layout.progress.label = "";
|
exerciseCounter++;
|
||||||
layout.render();
|
|
||||||
|
|
||||||
Bangle.buzz(100, 0.3); // TODO make configurable
|
layout.count.label = exerciseCounter;
|
||||||
} else {
|
layout.progress.label = "";
|
||||||
console.log(t, "Push up to quick for threshold!");
|
layout.render();
|
||||||
|
|
||||||
|
Bangle.buzz(100, 0.4); // TODO make configurable
|
||||||
|
} else {
|
||||||
|
console.log(t, exerciseName + " to quick for time threshold!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastZeroPassType = "-+";
|
||||||
|
lastZeroPassTime = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastZeroPassType = "-+";
|
|
||||||
lastZeroPassTime = t;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
historyY = [];
|
historyY = [];
|
||||||
historyZ = [];
|
historyZ = [];
|
||||||
|
|
@ -252,14 +288,12 @@ function startRecording() {
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
btns: [
|
btns: [{
|
||||||
{
|
label: "STOP",
|
||||||
label: "STOP",
|
cb: () => {
|
||||||
cb: () => {
|
stopRecording();
|
||||||
stopRecording();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
}],
|
||||||
lazy: true
|
lazy: true
|
||||||
});
|
});
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue