Skip to content

Commit

Permalink
Revert "Bangle.js2: Switched to proprietary heart rate algorithm (fro…
Browse files Browse the repository at this point in the history
…m our Open Source version). It just works better."

This reverts commit 21e7dd8.
  • Loading branch information
thyttan committed Oct 19, 2023
1 parent 442c0c7 commit f22e8dd
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 154 deletions.
8 changes: 2 additions & 6 deletions boards/BANGLEJS2.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,8 @@
'WRAPPERSOURCES += libs/graphics/jswrap_font_12x20.c',
'SOURCES += libs/misc/nmea.c',
'SOURCES += libs/misc/stepcount.c',
'SOURCES += libs/misc/hrm_vc31.c',
# Standard open-source heart rate algorithm:
# 'SOURCES += libs/misc/heartrate.c',
# Proprietary heart rate algorithm:
'SOURCES += libs/misc/heartrate_vc31_binary.c', 'DEFINES += -DHEARTRATE_VC31_BINARY=1', 'PRECOMPILED_OBJS += libs/misc/vc31_binary/algo.o libs/misc/vc31_binary/modle5_10.o libs/misc/vc31_binary/modle5_11.o libs/misc/vc31_binary/modle5_12.o libs/misc/vc31_binary/modle5_13.o libs/misc/vc31_binary/modle5_14.o libs/misc/vc31_binary/modle5_15.o libs/misc/vc31_binary/modle5_16.o libs/misc/vc31_binary/modle5_17.o libs/misc/vc31_binary/modle5_18.o libs/misc/vc31_binary/modle5_1.o libs/misc/vc31_binary/modle5_2.o libs/misc/vc31_binary/modle5_3.o libs/misc/vc31_binary/modle5_4.o libs/misc/vc31_binary/modle5_5.o libs/misc/vc31_binary/modle5_6.o libs/misc/vc31_binary/modle5_7.o libs/misc/vc31_binary/modle5_8.o libs/misc/vc31_binary/modle5_9.o',
# ------------------------
'SOURCES += libs/misc/heartrate.c',
'SOURCES += libs/misc/hrm_vc31.c',
'SOURCES += libs/misc/unistroke.c',
'WRAPPERSOURCES += libs/misc/jswrap_unistroke.c',
'DEFINES += -DESPR_BANGLE_UNISTROKE=1',
Expand Down
24 changes: 18 additions & 6 deletions libs/banglejs/jswrap_bangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,9 @@ Can be used for housekeeping tasks that don't want to be run during the day.

#define ACCEL_HISTORY_LEN 50 ///< Number of samples of accelerometer history

typedef struct {
short x,y,z;
} Vector3;

// =========================================================================
// DEVICE SPECIFIC CONFIG
Expand Down Expand Up @@ -853,7 +856,7 @@ bool magOnWhenCharging;
#ifdef MAG_DEVICE_GMC303
uint8_t magCalib[3]; // Magnetic Coefficient Registers - used to rescale the magnetometer values
#endif
/// accelerometer data. 8192 = 1G
/// accelerometer data
Vector3 acc;
/// squared accelerometer magnitude
int accMagSquared;
Expand Down Expand Up @@ -1696,7 +1699,7 @@ void peripheralPollHandler() {

#ifdef HEARTRATE
static void hrmHandler(int ppgValue) {
if (hrm_new(ppgValue, &acc)) {
if (hrm_new(ppgValue)) {
bangleTasks |= JSBT_HRM_DATA;
// keep track of best HRM sample during this period
if (hrmInfo.confidence >= healthCurrent.bpmConfidence) {
Expand Down Expand Up @@ -4315,11 +4318,11 @@ bool jswrap_banglejs_idle() {
JsVar *o = hrm_sensor_getJsVar();
if (o) {
jsvObjectSetChildAndUnLock(o,"raw",jsvNewFromInteger(hrmInfo.raw));
jsvObjectSetChildAndUnLock(o,"bpm",jsvNewFromFloat(hrmInfo.bpm10 / 10.0));
jsvObjectSetChildAndUnLock(o,"confidence",jsvNewFromInteger(hrmInfo.confidence));
jsvObjectSetChildAndUnLock(o,"filt",jsvNewFromInteger(hrmInfo.filtered));
jsvObjectSetChildAndUnLock(o,"avg",jsvNewFromInteger(hrmInfo.avg));
hrm_get_hrm_raw_info(o);
jsvObjectSetChildAndUnLock(o,"isBeat",jsvNewFromBool(hrmInfo.isBeat));
jsvObjectSetChildAndUnLock(o,"bpm",jsvNewFromFloat(hrmInfo.bpm10 / 10.0));
jsvObjectSetChildAndUnLock(o,"confidence",jsvNewFromInteger(hrmInfo.confidence));
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"HRM-raw", &o, 1);
jsvUnLock(o);
}
Expand All @@ -4329,7 +4332,16 @@ bool jswrap_banglejs_idle() {
if (o) {
jsvObjectSetChildAndUnLock(o,"bpm",jsvNewFromInteger(hrmInfo.bpm10 / 10.0));
jsvObjectSetChildAndUnLock(o,"confidence",jsvNewFromInteger(hrmInfo.confidence));
hrm_get_hrm_info(o);
JsVar *a = jsvNewEmptyArray();
if (a) {
int n = hrmInfo.timeIdx;
for (int i=0;i<HRM_HIST_LEN;i++) {
jsvArrayPushAndUnLock(a, jsvNewFromFloat(hrm_time_to_bpm10(hrmInfo.times[n]) / 10.0));
n++;
if (n==HRM_HIST_LEN) n=0;
}
jsvObjectSetChildAndUnLock(o,"history",a);
}
jsiQueueObjectCallbacks(bangle, JS_EVENT_PREFIX"HRM", &o, 1);
jsvUnLock(o);
}
Expand Down
21 changes: 1 addition & 20 deletions libs/misc/heartrate.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ bool hrm_had_beat() {
}

/// Add new heart rate value
bool hrm_new(int hrmValue, Vector3 *acc) {
bool hrm_new(int hrmValue) {
if (hrmValue<HRMVALUE_MIN) hrmValue=HRMVALUE_MIN;
if (hrmValue>HRMVALUE_MAX) hrmValue=HRMVALUE_MAX;
hrmInfo.raw = hrmValue;
Expand Down Expand Up @@ -379,22 +379,3 @@ bool hrm_new(int hrmValue, Vector3 *acc) {

return hadBeat;
}

// Append extra information to an existing HRM event object
void hrm_get_hrm_info(JsVar *o) {
JsVar *a = jsvNewEmptyArray();
if (a) {
int n = hrmInfo.timeIdx;
for (int i=0;i<HRM_HIST_LEN;i++) {
jsvArrayPushAndUnLock(a, jsvNewFromFloat(hrm_time_to_bpm10(hrmInfo.times[n]) / 10.0));
n++;
if (n==HRM_HIST_LEN) n=0;
}
jsvObjectSetChildAndUnLock(o,"history",a);
}
}

// Append extra information to an existing HRM-raw event object
void hrm_get_hrm_raw_info(JsVar *o) {
jsvObjectSetChildAndUnLock(o,"isBeat",jsvNewFromBool(hrmInfo.isBeat));
}
29 changes: 7 additions & 22 deletions libs/misc/heartrate.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@

#include "jsutils.h"

#ifndef HEARTRATE_VC31_BINARY
#define HRM_HIST_LEN 16 // how many BPM values do we keep a history of
#define HRM_MEDIAN_LEN 8 // how many BPM values do we average in our median filter to get a BPM reading
#endif

// Do we use 8 or 16 bits for data storage?
#ifdef HEARTRATE_DEVICE_VC31
Expand All @@ -32,43 +30,30 @@
#endif

typedef struct {
uint16_t bpm10; // 10x BPM
uint8_t confidence; // 0..100%

HrmValueType raw;
int16_t avg; // average signal value, moving average
int16_t filtered;
#ifndef HEARTRATE_VC31_BINARY
int16_t filtered1; // before filtered
int16_t filtered2; // before filtered1
int16_t avg; // average signal value, moving average

bool wasLow; // has the signal gone below the average? set =false when a beat detected
bool isBeat; // was this sample classified as a detected beat?
JsSysTime lastBeatTime; // timestamp of last heartbeat
uint8_t times[HRM_HIST_LEN]; // times of previous beats, in 1/100th secs
uint8_t timeIdx; // index in times
#else // HEARTRATE_VC31_BINARY
bool isWorn; // is the bangle being worn? Used to re-init if we go from not worn to worn
JsSysTime lastPPGTime; // timestamp of last PPG sample
/* last values we got from the algo. It doesn't tell us when there's a new
value so we have to guess based on when it changes */
int lastHRM, lastConfidence;
int msSinceLastHRM; // how long was it since the last HRM reading?
uint8_t sportMode; // The sport mode passed to the algorithm
#endif
uint16_t bpm10; // 10x BPM
uint8_t confidence; // 0..100%
} HrmInfo;

extern HrmInfo hrmInfo;

uint16_t hrm_time_to_bpm10(uint8_t time);

/// Initialise heart rate monitoring
void hrm_init();

/// Add new heart rate value, return true if there was a heart beat
bool hrm_new(int hrmValue, Vector3 *acc);
bool hrm_new(int hrmValue);

void hrm_sensor_on();
void hrm_sensor_off();

// Append extra information to an existing HRM event object
void hrm_get_hrm_info(JsVar *var);
// Append extra information to an existing HRM-raw event object
void hrm_get_hrm_raw_info(JsVar *var);
25 changes: 20 additions & 5 deletions libs/misc/hrm_vc31.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
*/

#include "hrm.h"
#include "hrm_vc31.h"
#include "jsutils.h"
#include "platform_config.h"
#include "jshardware.h"
Expand Down Expand Up @@ -167,6 +166,25 @@ typedef enum {
VC31B_DEVICE
} VC31Type;

// Hack to fix Eclipse syntax lint
#ifndef PACKED_FLAGS
#define PACKED_FLAGS
#endif
// ---

// VC31 info shared between VC31A/B
typedef struct {
bool isWearing;
int8_t isWearCnt, unWearCnt; // counters for switching worn/unworn state
uint16_t ppgValue; // current PPG value
uint16_t ppgLastValue; // last PPG value
int16_t ppgOffset; // PPG 'offset' value. When PPG adjusts we change the offset so it matches the last value, then slowly adjust 'ppgOffset' back down to 0
uint8_t wasAdjusted; // true if LED/etc adjusted since the last reading
// the meaning of these is device-dependent but it's nice to have them in one place
uint8_t irqStatus;
uint8_t raw[12];
} PACKED_FLAGS VC31Info;

// VC31A-specific info
typedef struct {
uint8_t ctrl; // current VC31A_CTRL reg value
Expand All @@ -175,6 +193,7 @@ typedef struct {
uint16_t envValue;
uint16_t psValue;
VC31AdjustInfo_t adjustInfo;

} PACKED_FLAGS VC31AInfo;
// VC31B-specific info
typedef struct {
Expand Down Expand Up @@ -574,7 +593,6 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {
vcaInfo.preValue = (buf[6] << 8) | buf[5];
vcaInfo.psValue = (buf[8] << 8) | buf[7];
vcaInfo.envValue = (buf[10] << 8) | buf[9];
vcInfo.envValue = vcaInfo.envValue;

if (vcInfo.irqStatus & VC31A_STATUS_D_PPG_OK) {
vc31_new_ppg(ppgValue); // send PPG value
Expand Down Expand Up @@ -614,9 +632,6 @@ void vc31_irqhandler(bool state, IOEventFlags flags) {

// if we had environment sensing, check for wear status and update LEDs accordingly
if (vcInfo.irqStatus & VC31B_INT_PS) {
vcInfo.envValue = vcbInfo.sampleData.envValue[2];
if (vcInfo.pushEnvData)
jsbangle_push_event(JSBE_HRM_ENV, vcInfo.envValue);
//jsiConsolePrintf("e %d %d\n", vcbInfo.sampleData.psValue, vcbInfo.sampleData.envValue[2] );
if (vcInfo.allowWearDetect)
vc31b_wearstatus();
Expand Down
24 changes: 0 additions & 24 deletions libs/misc/vc31_binary/README.md

This file was deleted.

70 changes: 0 additions & 70 deletions libs/misc/vc31_binary/algo.h

This file was deleted.

2 changes: 1 addition & 1 deletion make/family/NRF52.make
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ INCLUDE += -I$(SOFTDEVICE_PATH)/headers
INCLUDE += -I$(SOFTDEVICE_PATH)/headers/nrf52

DEFINES += -DBLE_STACK_SUPPORT_REQD
DEFINES += -DSWI_DISABLE0 -DSOFTDEVICE_PRESENT
DEFINES += -DSWI_DISABLE0 -DSOFTDEVICE_PRESENT -DFLOAT_ABI_HARD
DEFINES += -DNRF52_SERIES
# Nordic screwed over anyone who used -DNRF52 in new SDK versions
# but then old SDKs won't work without it
Expand Down

0 comments on commit f22e8dd

Please sign in to comment.