mirror of
https://github.com/google/pebble.git
synced 2025-03-19 18:41:21 +00:00
1005 lines
38 KiB
C
1005 lines
38 KiB
C
|
/* Project Kraepelin, Main file
|
|||
|
The MIT License (MIT)
|
|||
|
|
|||
|
Copyright (c) 2015, Nathaniel T. Stockham
|
|||
|
|
|||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|||
|
of this software and associated documentation files (the "Software"), to deal
|
|||
|
in the Software without restriction, including without limitation the rights
|
|||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
|
copies of the Software, and to permit persons to whom the Software is
|
|||
|
furnished to do so, subject to the following conditions:
|
|||
|
|
|||
|
The above copyright notice and this permission notice shall be included in
|
|||
|
all copies or substantial portions of the Software.
|
|||
|
|
|||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
|
THE SOFTWARE.
|
|||
|
|
|||
|
This license is taken to apply to any other files in the Project Kraepelin
|
|||
|
Pebble App roject.
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
#include "raw_stats.h"
|
|||
|
#include "system/logging.h"
|
|||
|
|
|||
|
|
|||
|
/* RAW STATISTICS */
|
|||
|
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
int32_t mean_l1_stat(int16_t *d, int16_t dlen){
|
|||
|
int32_t mean = 0;
|
|||
|
|
|||
|
for(int16_t i = 0; i < dlen; i++ ){
|
|||
|
mean += d[i];
|
|||
|
}
|
|||
|
/* We don't overflow protection the mean output because the values can
|
|||
|
* exceed 125 over the course of a second */
|
|||
|
return mean/dlen; // divide out # samples to get mean
|
|||
|
}
|
|||
|
|
|||
|
// TEST : NONE
|
|||
|
int32_t x100_mean_l1_stat(int16_t *d, int16_t dlen){
|
|||
|
int32_t mean = 0;
|
|||
|
|
|||
|
for(int16_t i = 0; i < dlen; i++ ){
|
|||
|
mean += d[i];
|
|||
|
}
|
|||
|
/* We don't overflow protection the mean output because the values can
|
|||
|
* exceed 125 over the course of a second */
|
|||
|
return (mean*100)/dlen; // divide out # samples to get mean
|
|||
|
}
|
|||
|
|
|||
|
// https://en.wikipedia.org/wiki/Fixed-point_arithmetic
|
|||
|
static __inline__ sfxp fxp_mul(sfxp a, sfxp b){
|
|||
|
// FORMAT Q31.32, so the integers are signed and the fractional parts
|
|||
|
// are unsigned
|
|||
|
|
|||
|
int32_t ai, bi; // need this so that the negative bit is in the right place.
|
|||
|
ai = a >> 32;
|
|||
|
bi = b >> 32;
|
|||
|
|
|||
|
uint32_t af, bf;
|
|||
|
af = (uint32_t) a; // just get the bottom elements, assume positive
|
|||
|
bf = (uint32_t) b; // ibid
|
|||
|
|
|||
|
// have to add all the elements as unsigned types, because otherwise
|
|||
|
// C will try to interpert the high bit of the low 32 bits of the fractional
|
|||
|
// segment as a sign, and cause substraction, when all we really want is to
|
|||
|
// assume all elements are positive, and then encode the sign as the highest
|
|||
|
// bit of var "si",
|
|||
|
ufxp si, uf, uif;
|
|||
|
|
|||
|
// multiply the integers, and use them to store the sign in the highest bit.
|
|||
|
// but, we also need to encode overflow.
|
|||
|
si = ((ufxp)(ai*bi)) << 32;
|
|||
|
// multiply the fractional only part, and store them
|
|||
|
uf = (((ufxp)af)*((ufxp)bf)) >> 32;
|
|||
|
// we can convert the fractional part safely to unsigned long long because we
|
|||
|
// know that the range is <= 63 bit, as these are 32 and 31 bit integers multi
|
|||
|
|
|||
|
uif = ((ai)*((ufxp)bf)) + (((ufxp)af)*(bi));
|
|||
|
|
|||
|
return (sfxp) (si + uf + uif);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// we statically encode the coefficents because we only need this
|
|||
|
// filter to run fast. ALSO, MUST initialize to 0
|
|||
|
static sfxp yt[4][3] = {{0,0,0},{0,0,0},{0,0,0},{0,0,0}};
|
|||
|
static sfxp xt[5][3] = {{0,0,0},{0,0,0},{0,0,0},{0,0,0},{0,0,0}};
|
|||
|
|
|||
|
void pim_filt_init(void) {
|
|||
|
memset(yt, 0, sizeof(yt));
|
|||
|
memset(xt, 0, sizeof(xt));
|
|||
|
}
|
|||
|
|
|||
|
// -----------------------------------------------------------------------------------------
|
|||
|
// Prime the butterworth filter used in the pim filter. This helps reduce the high VMC
|
|||
|
// produced from the first set of samples fed in right after the algorithm has been initialized.
|
|||
|
// It works by priming the butterworth filter with an odd-symmetric extension of the first few
|
|||
|
// samples. These priming samples have roughly the same frequency characteristics as
|
|||
|
// the first set of samples. Since the butterworth filter's memory is 5 samples, the priming
|
|||
|
// sequence must be at least 10 long.
|
|||
|
// If the first few real samples are:
|
|||
|
// 10, 13, 9, 15, 6, ...
|
|||
|
// Then the priming samples would be:
|
|||
|
// 14, 5, 11, 7
|
|||
|
// The value for priming sample i, based on N is:
|
|||
|
// p[i] = x[0] - (x[N-1-i] - x[0]) - x[0]
|
|||
|
// p[i] = 2 * x[0] - x[N-1-i]
|
|||
|
// So, in the above example, when N is 5:
|
|||
|
// p[0] = 2 * 10 - 6 = 14
|
|||
|
// p[1] = 2 * 10 - 15 = 5
|
|||
|
// p[2] = 2 * 10 - 9 = 11
|
|||
|
// p[3] = 2 * 10 - 13 = 7
|
|||
|
void pim_filt_prime(int16_t *d, int16_t dlen, int16_t axis) {
|
|||
|
const int n = 11;
|
|||
|
int16_t prime_data[n];
|
|||
|
|
|||
|
for (int i = 0; i < n; i++) {
|
|||
|
prime_data[i] = 2 * d[0] - d[n - 1 - i];
|
|||
|
}
|
|||
|
pim_filt(prime_data, n - 1, axis);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// // TEST : PASSED
|
|||
|
uint32_t pim_filt(int16_t *d, int16_t dlen, int16_t axis){
|
|||
|
// uint32_t pim_filt(int16_t *d, int16_t dlen, int16_t axis){
|
|||
|
// we use a butterworth second order digital fitler with a bandpass
|
|||
|
// design of 0.25 to 2 hz, with coefficents double *ca_d,double *cb_dof
|
|||
|
|
|||
|
// and, we calculate all of these by multiplying by 100000x
|
|||
|
int32_t x1000_thres = 3750; // this is calibrated to pebble, 125 = 1G
|
|||
|
// the pim
|
|||
|
int32_t pim=0;
|
|||
|
sfxp ytmp=0;
|
|||
|
|
|||
|
//25hz @ 0.25 to 2hz
|
|||
|
sfxp ca[4] = {0xfffffffc92b0910cLL, 0x0000000473f9a693LL, 0xfffffffd633c7d23LL, 0x0000000096405b5cLL};
|
|||
|
sfxp cb[5] = {0x000000000721d150LL, 0x0000000000000000LL, 0xfffffffff1bc5d60LL, 0x0000000000000000LL, 0x000000000721d150LL};
|
|||
|
//25hz @ 0.25 to 2.5hz
|
|||
|
// sll ca[4] = {0xfffffffcd72d69bdLL, 0x00000003caed5dc8LL, 0xfffffffdeadc79a0LL, 0x0000000073506446LL};
|
|||
|
// sll cb[5] = {0x000000000e73680bLL, 0x0000000000000000LL, 0xffffffffe3192feaLL, 0x0000000000000000LL, 0x000000000e73680bLL};
|
|||
|
|
|||
|
for(int16_t i = 0; i < dlen; i++){
|
|||
|
// shift the input over by one
|
|||
|
for(int16_t k = 4; k >0;k-- ){ xt[k][axis] = xt[k-1][axis];}
|
|||
|
xt[0][axis] = int2sll(d[i]);
|
|||
|
|
|||
|
// get the next output element, ympt
|
|||
|
ytmp = ( ( fxp_mul(cb[0],xt[0][axis])
|
|||
|
+ fxp_mul(cb[1],xt[1][axis])
|
|||
|
+ fxp_mul(cb[2],xt[2][axis])
|
|||
|
+ fxp_mul(cb[3],xt[3][axis])
|
|||
|
+ fxp_mul(cb[4],xt[4][axis]))
|
|||
|
- (fxp_mul(ca[0],yt[0][axis])
|
|||
|
+ fxp_mul(ca[1],yt[1][axis])
|
|||
|
+ fxp_mul(ca[2],yt[2][axis])
|
|||
|
+ fxp_mul(ca[3],yt[3][axis])) );
|
|||
|
|
|||
|
// shift the y output elements
|
|||
|
for(int16_t k = 3; k >0;k-- ){ yt[k][axis] = yt[k-1][axis];}
|
|||
|
|
|||
|
// update the y output element and prevent overflow and unrealistic outputs (yt[0]<= xt[0] always)
|
|||
|
yt[0][axis] = ytmp;
|
|||
|
pim += abs( (int32_t)sll2int(ytmp) );
|
|||
|
}
|
|||
|
// REMEMBER, the scoring is done on the 1 SECOND level, so we
|
|||
|
// ONLY do thresholding at the 1 second level.
|
|||
|
return (uint32_t) ( (pim - (x1000_thres*dlen)/1000>0) ? (pim - (x1000_thres*dlen)/1000 ) : 0) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
uint32_t calc_scaled_vmc(uint32_t *pim_ary){
|
|||
|
// copy over the elements
|
|||
|
|
|||
|
// This function calculates the VMCPM from the PIM array for each
|
|||
|
// epoch.
|
|||
|
// INPUTS
|
|||
|
// *pim_ary -> the actual array of cpm for each axis
|
|||
|
// oflw_scl -> dividing factor to reduce pim_ary so doesnt overflow
|
|||
|
// PARAMTERS
|
|||
|
// oflw_cap -> cap to prevent overflow when pim_ary[:].^2 is summed
|
|||
|
// OUTPUT
|
|||
|
// @ return -> the sqrt of the scaled vmcpm
|
|||
|
|
|||
|
uint32_t d[3];
|
|||
|
uint32_t oflw_cap = 37500;
|
|||
|
uint32_t VMCPM_SCL = 10;
|
|||
|
for(int16_t axis = 0; axis < 3; axis++){
|
|||
|
|
|||
|
d[axis] = pim_ary[axis]/VMCPM_SCL;
|
|||
|
d[axis] = (d[axis] < oflw_cap) ? d[axis] : oflw_cap;
|
|||
|
}
|
|||
|
|
|||
|
/* VMCPM = sqrt(pim_x^2 + pim_y^2 + pim_z^2)*/
|
|||
|
// calculate VMCPM, then take sqrt to compress for storage
|
|||
|
return (isqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2])*VMCPM_SCL) ;
|
|||
|
}
|
|||
|
|
|||
|
uint8_t compressed_vmc(uint32_t *pim_ary){
|
|||
|
return (uint8_t) isqrt(calc_real_vmc(pim_ary) );
|
|||
|
}
|
|||
|
|
|||
|
uint32_t calc_real_vmc(uint32_t *pim_ary){
|
|||
|
|
|||
|
uint32_t scl_vmcpm = calc_scaled_vmc(pim_ary);
|
|||
|
// we first assume the PIM is calculated on 1 = 1G, then we scale
|
|||
|
// to x10 the real vmcpm, THEN we divide by 1250 cause 125*10, and
|
|||
|
// we divide the PIM calculated on 125 = 1G so we FINALLY get the
|
|||
|
// *real* VMCPM given that we calculated on the Pebble accel where
|
|||
|
// 1000/8 = 125 = 1G.
|
|||
|
return (scl_vmcpm*x100_RAW_1G_PIM_CPM_TO_REAL_CPM)/12500;
|
|||
|
}
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
uint32_t calc_real_c(uint32_t pim){
|
|||
|
// This function calculates the CPM from the PIM array for each
|
|||
|
// epoch.
|
|||
|
// INPUTS
|
|||
|
// *pim_ary -> the actual array of cpm for each axis
|
|||
|
|
|||
|
// we first assume the PIM is calculated on 1 = 1G, then we scale
|
|||
|
// to x10 the real cpm, THEN we divide by 1250 cause 125*10, and
|
|||
|
// we divide the PIM calculated on 125 = 1G so we FINALLY get the
|
|||
|
// *real* VMCPM given that we calculated on the Pebble accel where
|
|||
|
// 1000/8 = 125 = 1G.
|
|||
|
return (pim*x100_RAW_1G_PIM_CPM_TO_REAL_CPM)/12500;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
uint32_t calc_x1000_kcal(uint32_t *r_c_ary){
|
|||
|
int32_t x1000_kcal = 0;
|
|||
|
int32_t we_c_ary[3]; // wrist equivalent counts
|
|||
|
int32_t r_c;
|
|||
|
|
|||
|
// update the general config
|
|||
|
struct config_general cur_config = {
|
|||
|
.pweight_kg = 70,
|
|||
|
};
|
|||
|
#if PEBBLE_APP
|
|||
|
persist_read_data(CONFIG_GENERAL_PERSIST_KEY,&cur_config,sizeof(cur_config));
|
|||
|
#endif
|
|||
|
int32_t pweight_kg = (int32_t) cur_config.pweight_kg;
|
|||
|
// int32_t pweight_kg = 0;
|
|||
|
// CONVERT WRIST COUNTS INTO EQUIVALENT COUNTS AS SPECIFIED BY ACTIGRAPH @
|
|||
|
// https://help.theactigraph.com/entries/21187041-What-does-the-Worn-on-Wrist-option-do-in-the-Data-Scoring-tab-
|
|||
|
|
|||
|
for(int16_t axis = 0; axis < 3; axis++){
|
|||
|
r_c = (int32_t) r_c_ary[axis];
|
|||
|
if( r_c < 644){
|
|||
|
we_c_ary[axis] = 534*r_c;
|
|||
|
}else if((r_c >= 645) && (r_c < 1272)){
|
|||
|
we_c_ary[axis] = 1713*r_c - 759414;
|
|||
|
}else if((r_c >= 1273) && (r_c < 3806)){
|
|||
|
we_c_ary[axis] = 400*r_c + 911501;
|
|||
|
}else if(r_c >= 3806){
|
|||
|
we_c_ary[axis] = 13*r_c + 2383904;
|
|||
|
}
|
|||
|
we_c_ary[axis] = we_c_ary[axis]/1000;
|
|||
|
}
|
|||
|
|
|||
|
// get the equivalent scaled vmc
|
|||
|
// NOTE: We have already scaled to actual count values, so we don't
|
|||
|
// need to worry about overflow on the uint32_t.
|
|||
|
uint32_t we_vmc = isqrt(we_c_ary[0]*we_c_ary[0] + we_c_ary[1]*we_c_ary[1]
|
|||
|
+ we_c_ary[2]*we_c_ary[2] );
|
|||
|
|
|||
|
|
|||
|
// NOTE!!, this assumes the equation from Freedson VM2 11'
|
|||
|
// if VMCPM > 2453
|
|||
|
// Kcals/min= 0.001064×VM + 0.087512(BM) - 5.500229
|
|||
|
// else
|
|||
|
// Kcals/min=CPM×0.0000191×BM
|
|||
|
//
|
|||
|
// where
|
|||
|
// VM = Vector Magnitude Combination (per minute) of all 3 axes (sqrt((Axis 1)^2+(Axis 2)^2+(Axis 3)^2])
|
|||
|
// VMCPM = Vector Magnitude Counts per Minute
|
|||
|
// CPM = Counts per Minute
|
|||
|
// BM = Body Mass in kg
|
|||
|
|
|||
|
// NOTE, we have to scale ALL of these things by 10000, and since we
|
|||
|
// know that each element of the pim_ary maxes out at around 375000,
|
|||
|
// we know that the upper bound of cpm is 3*400,000 = 1,200,000, so
|
|||
|
// to stay under the 4,294,967,296 overflow cap, we can only go to
|
|||
|
// a factor of 10,000. BUT, problems still, cause we need to selectively
|
|||
|
// decide which ones we are going to add/subtract. We WILL assume
|
|||
|
// that the output of this function will be (kcal/min)*1000, which
|
|||
|
// is also captured in the worker's function, cause we don't
|
|||
|
// want to have big rounding down errors when we are at basal metabolic
|
|||
|
// rate estimates.
|
|||
|
|
|||
|
if(we_vmc > 2453){
|
|||
|
// if VMCPM > 2453
|
|||
|
// Kcals/min= 0.001064×VM + 0.087512(BM) - 5.500229
|
|||
|
x1000_kcal = (1064*((int32_t) we_vmc))/1000 + (875*pweight_kg)/10 - 5500;
|
|||
|
}else{
|
|||
|
// Kcals/min=CPM×0.0000191×BM
|
|||
|
x1000_kcal = (we_c_ary[0]*19*pweight_kg)/1000;
|
|||
|
}
|
|||
|
return (uint32_t) abs(x1000_kcal);
|
|||
|
}
|
|||
|
|
|||
|
uint8_t compressed_x1000_kcal(uint32_t x1000_kcal, int16_t num_min){
|
|||
|
// we assume that we can't get more than 17 kcal per min
|
|||
|
// assume max is 1000 calories per hour, so ~17 cal min max, so multiply
|
|||
|
// by number of minutes, then scale & sqrt it so it fits within the uint8
|
|||
|
uint32_t max_kcal = 17*num_min;
|
|||
|
return (uint8_t) isqrt( ((x1000_kcal/1000)*65535)/max_kcal );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
uint8_t compressed_stepc(uint32_t stepc, int16_t num_min){
|
|||
|
// This function simply takes in the number of steps in a given number of
|
|||
|
// minutes. Then, we calculate the maximum number of steps possible in num_min
|
|||
|
// given a max of 4hz running speed. Then, that max is turned in a scaling
|
|||
|
// factor that the step count is premultiplied by, and then the squrt is taken
|
|||
|
uint32_t max_stepc = 4*60*num_min; // @ 4hz * 60secs * num_min
|
|||
|
return (uint8_t) isqrt( (stepc*65535)/max_stepc) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|||
|
// ++++++++++++++++++++++++ SIGNALS ANALYSIS +++++++++++++++++++++++++++++
|
|||
|
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
void vm_accel(int16_t **d, int16_t *w, int16_t max_vm, int16_t dlen){
|
|||
|
/* EVALUATE THE VECTOR ACCELERATION MAGNITUDE, WRITE TO first array */
|
|||
|
// NOTE: we can look at the entire array, because we only write to
|
|||
|
// the first N_SMP_EPOCH, the remainder being 0's, which will
|
|||
|
// evalulate to a vector mag of 0
|
|||
|
for(int16_t i = 0; i < dlen; i++){
|
|||
|
// evaluate the vector mag of acceleration, write to work array w
|
|||
|
w[i] = (int16_t) isqrt( d[0][i]*d[0][i] + d[1][i]*d[1][i] + d[2][i]*d[2][i] );
|
|||
|
// w[i] = (int16_t) ( (d[0][i]*d[0][i]) + (d[1][i]*d[1][i]) + (d[2][i]*d[2][i]) );
|
|||
|
// cap the vector mags as max_vm to prevent overflow
|
|||
|
w[i] = ((abs(w[i]) <= max_vm) ? w[i] : ((w[i] > 0 ) ? 1 : (-1))*max_vm);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
void vm_accel_xy(int16_t **d, int16_t *w, int16_t max_vm, int16_t dlen){
|
|||
|
/* EVALUATE THE VECTOR ACCELERATION MAGNITUDE, WRITE TO first array */
|
|||
|
// NOTE: we can look at the entire array, because we only write to
|
|||
|
// the first N_SMP_EPOCH, the remainder being 0's, which will
|
|||
|
// evalulate to a vector mag of 0
|
|||
|
for(int16_t i = 0; i < dlen; i++){
|
|||
|
// evaluate the vector mag of acceleration, write to work array w
|
|||
|
w[i] = (int16_t) isqrt( d[0][i]*d[0][i] + d[1][i]*d[1][i]);
|
|||
|
// w[i] = (int16_t) ( (d[0][i]*d[0][i]) + (d[1][i]*d[1][i]) + (d[2][i]*d[2][i]) );
|
|||
|
// cap the vector mags as max_vm to prevent overflow
|
|||
|
w[i] = ((abs(w[i]) <= max_vm) ? w[i] : ((w[i] > 0 ) ? 1 : (-1))*max_vm);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
int16_t score_fft_alg_0pad(int16_t *d, int16_t dlen_smp, int16_t dlenpwr_ary, int16_t oflw_scl){
|
|||
|
// NOTE !!! WE ASSUME THE d INPUT IS ALREADY SCALED TO PREVENT OVERFLOW
|
|||
|
// AND THAT IS MEAN-PADDED
|
|||
|
/* PARAMETERS */
|
|||
|
int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
int16_t lhz_i = 3;
|
|||
|
int16_t hhz_i = 10;
|
|||
|
|
|||
|
// reduce the scale
|
|||
|
for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
d[i] = d[i]/oflw_scl;
|
|||
|
}
|
|||
|
|
|||
|
// set the last few elements to the mean of the first dlen_smp elements
|
|||
|
int16_t mean = mean_l1_stat(d, dlen_smp);
|
|||
|
for(int16_t i = dlen_smp ; i < dlen_ary; i++){
|
|||
|
d[i] = mean;
|
|||
|
}
|
|||
|
|
|||
|
/* EVALUATE THE MAGNITUDE OF THE FFT COEFFICIENTS AND WRITE TO d ARRAY */
|
|||
|
fft_2radix_real(d, dlenpwr_ary);
|
|||
|
fft_mag(d, dlenpwr_ary);
|
|||
|
|
|||
|
/* EVALUATE THE STEP SCORE FOR EPOCH */
|
|||
|
// -> This section can be fairly complex if needed
|
|||
|
// When remove the mean, the DC is zero, so we add the DC value back
|
|||
|
// -> (mean * length of array)
|
|||
|
// to evaluate the 4-20hz integral against the total integral
|
|||
|
return (100*(integral_abs(d, lhz_i, hhz_i)))/(integral_abs(d, 0, dlen_ary/2));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void get_fftmag_0pad(int16_t *d, int16_t dlen_smp, int16_t dlenpwr_ary, int16_t oflw_scl){
|
|||
|
int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
|
|||
|
// reduce the scale
|
|||
|
for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
d[i] = d[i]/oflw_scl;
|
|||
|
}
|
|||
|
|
|||
|
// set the last few elements to the mean of the first dlen_smp elements
|
|||
|
int16_t mean = mean_l1_stat(d, dlen_smp);
|
|||
|
for(int16_t i = dlen_smp ; i < dlen_ary; i++){
|
|||
|
d[i] = mean;
|
|||
|
}
|
|||
|
|
|||
|
/* EVALUATE THE MAGNITUDE OF THE FFT COEFFICIENTS AND WRITE TO d ARRAY */
|
|||
|
fft_2radix_real(d, dlenpwr_ary);
|
|||
|
fft_mag(d, dlenpwr_ary);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void get_fftmag_0pad_mean0(int16_t *d, int16_t dlen_smp, int16_t dlenpwr_ary, int16_t oflw_scl){
|
|||
|
int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
|
|||
|
// reduce the scale
|
|||
|
for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
d[i] = d[i]/oflw_scl;
|
|||
|
}
|
|||
|
|
|||
|
// set the last few elements to the mean of the first dlen_smp elements
|
|||
|
int16_t mean = mean_l1_stat(d, dlen_smp);
|
|||
|
for(int16_t i = 0 ; i < dlen_smp; i++){
|
|||
|
d[i] = d[i] - mean;
|
|||
|
}
|
|||
|
for(int16_t i = dlen_smp ; i < dlen_ary; i++){
|
|||
|
d[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
/* EVALUATE THE MAGNITUDE OF THE FFT COEFFICIENTS AND WRITE TO d ARRAY */
|
|||
|
fft_2radix_real(d, dlenpwr_ary);
|
|||
|
fft_mag(d, dlenpwr_ary);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
uint16_t score_fftmag_hz_rng_abs(int16_t *d, int16_t dlenpwr_ary,int16_t lhz_i,int16_t hhz_i){
|
|||
|
int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
|
|||
|
return (100*(integral_abs(d, lhz_i, hhz_i)))/(integral_abs(d, 0, dlen_ary/2));
|
|||
|
}
|
|||
|
|
|||
|
uint16_t score_fftmag_hz_rng_l2(int16_t *d, int16_t dlenpwr_ary,int16_t lhz_i,int16_t hhz_i){
|
|||
|
int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
|
|||
|
return (100*(integral_l2(d, lhz_i, hhz_i)))/(integral_l2(d, 0, dlen_ary/2));
|
|||
|
}
|
|||
|
|
|||
|
int16_t score_fftmag_max_hz_harm(int16_t *d, int16_t dlenpwr_ary,int16_t max_mag_hz){
|
|||
|
int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
int16_t next_max_mag_hz = (d[max_mag_hz+1] > d[max_mag_hz-1]) ? (max_mag_hz+1) : (max_mag_hz-1);
|
|||
|
|
|||
|
int32_t num = 0;
|
|||
|
for(int16_t i = 1; i < 4 ; i++){
|
|||
|
if( max_mag_hz+1 <= (dlen_ary/2)){
|
|||
|
num += ((d[max_mag_hz*i]) + (d[max_mag_hz*i])); // highest harmonics
|
|||
|
}
|
|||
|
}
|
|||
|
return ((100*num) / integral_abs(d, 1, dlen_ary/2) );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void filt_hann_win_mean0(int16_t *d, int16_t dlen_smp, int32_t g_fctr){
|
|||
|
int32_t d_mean = mean_l1_stat(d,dlen_smp);
|
|||
|
// g_fctr = gain factor
|
|||
|
// d[i] = d[i]*w[i]
|
|||
|
// -> w[i] = 0.5*(1 - cos(2*pi*i/dlen_smp)) ::: i = 0:dlen_smp
|
|||
|
for( uint16_t i = 0; i < dlen_smp; i++ ){
|
|||
|
d[i] = (int16_t) ( ((d[i] - d_mean)*g_fctr*(TRIG_MAX_RATIO
|
|||
|
- cos_lookup((TRIG_MAX_ANGLE*i)/dlen_smp)) ) / (2*TRIG_MAX_RATIO));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void filt_cosine_win_mean0(int16_t *d, int16_t dlen_smp, int32_t g_fctr){
|
|||
|
int32_t d_mean = mean_l1_stat(d,dlen_smp);
|
|||
|
// g_fctr = gain factor
|
|||
|
// d[i] = d[i]*w[i]
|
|||
|
// -> w[i] = 0.5*(1 - cos(2*pi*i/dlen_smp)) ::: i = 0:dlen_smp
|
|||
|
for( uint16_t i = 0; i < dlen_smp; i++ ){
|
|||
|
d[i] = (int16_t) ( ((d[i] - d_mean)*g_fctr*
|
|||
|
sin_lookup((TRIG_MAX_ANGLE*i)/(2*dlen_smp)) ) / (TRIG_MAX_RATIO));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// TEST : PASSED
|
|||
|
int16_t max_mag_hz_0pad(int16_t *d){
|
|||
|
// NOTE !!! WE ASSUME THE d INPUT IS ALREADY THE FFT MAG
|
|||
|
int16_t lhz_i = 3;
|
|||
|
int16_t hhz_i = 25;
|
|||
|
// int16_t is_step = 0; // assume it is not a step period
|
|||
|
|
|||
|
// evaluate if the period is a step epoch, based on score
|
|||
|
uint32_t max_hz_val = 0;
|
|||
|
int16_t max_hz_i = 0; // if NOT a step period, then return 0 for no steps
|
|||
|
// if it is a step epoch, find the hz index with largest mag
|
|||
|
uint32_t test_val = 0;
|
|||
|
for(int16_t i = lhz_i; i <= hhz_i; i++){
|
|||
|
test_val = (abs(d[i])*(hhz_i - i)*100 )/ 100;
|
|||
|
// test_val = d[i];
|
|||
|
if(test_val > max_hz_val ){
|
|||
|
max_hz_val = test_val;
|
|||
|
max_hz_i = i;
|
|||
|
}
|
|||
|
}
|
|||
|
/* RETURN NUMBERS OF STEPS IN EPOCH */
|
|||
|
return max_hz_i; // DC index is 0, so max_hz_i is HZ directly
|
|||
|
}
|
|||
|
|
|||
|
int16_t calc_stepc_5sec(int16_t *work_ary, int16_t dlen_smp, int16_t dlenpwr_ary,
|
|||
|
uint32_t *pim_5sec_ary, int16_t fft_oflw_scl){
|
|||
|
// get the mean acceleration to serve as minimum thresholds for running and
|
|||
|
// walking
|
|||
|
uint32_t real_vmc_5s = calc_real_vmc(pim_5sec_ary);
|
|||
|
|
|||
|
// struct config_general cg = get_config_general();
|
|||
|
// Values copied from reset_config_general_persistent_storage() in helper.c
|
|||
|
struct config_general cg = (struct config_general) {
|
|||
|
.stepc_fft_thres0 = {22, 100},
|
|||
|
.stepc_vma_thres0 = 0,
|
|||
|
.stepc_vmc_thres0 = 50,
|
|||
|
.wear_class_thres = 290 * 12 * 10,
|
|||
|
.pts_goal = 10000,
|
|||
|
};
|
|||
|
|
|||
|
// filter the result
|
|||
|
filt_cosine_win_mean0(work_ary, dlen_smp, 5);
|
|||
|
get_fftmag_0pad_mean0(work_ary, dlen_smp, dlenpwr_ary, fft_oflw_scl);
|
|||
|
|
|||
|
int16_t max_mag_hz = max_mag_hz_0pad(work_ary);
|
|||
|
|
|||
|
int16_t score0 = score_fftmag_max_hz_harm(work_ary, dlenpwr_ary, max_mag_hz);
|
|||
|
// uint8_t score0 = score_fftmag_hz_rng_abs(work_ary, dlenpwr_ary, max_mag_hz-1, max_mag_hz+1);
|
|||
|
int16_t stepc_tmp = 0;
|
|||
|
// NOTE!, use a simple linear regression to scale the fft_threshold with the
|
|||
|
// vmc. Actually, this is quite computationally sound, we just need to shift
|
|||
|
// it over by a few for safety, and we can auto adjust the parameters so that
|
|||
|
// they can be *very* tight. This way, we can reject steps very easily.
|
|||
|
if( (score0 >= cg.stepc_fft_thres0[0])
|
|||
|
&& (real_vmc_5s >= cg.stepc_vmc_thres0)
|
|||
|
&& (max_mag_hz < 40) ){
|
|||
|
// we subtract by an expected 0.5 cause the window truncation and
|
|||
|
// integer fft approximation causes to push up a half frequency ~0.1Hz.
|
|||
|
// stepc_tmp = max_mag_hz - ((uint16_t) (rand()%2));
|
|||
|
stepc_tmp = max_mag_hz;
|
|||
|
}
|
|||
|
PBL_LOG(LOG_LEVEL_DEBUG, "steps: %d, freq: %d, vmc: %d, score: %d", stepc_tmp, max_mag_hz,
|
|||
|
real_vmc_5s, score0);
|
|||
|
return stepc_tmp;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// int16_t calc_stepc_5sec(int16_t *work_ary, int16_t dlen_smp, int16_t dlenpwr_ary,
|
|||
|
// uint32_t *pim_5sec_ary, int16_t fft_oflw_scl){
|
|||
|
// // get the mean acceleration to serve as minimum thresholds for running and
|
|||
|
// // walking
|
|||
|
// // int32_t x100_mean_vm_accel = x100_mean_l1_stat(work_ary, dlen_smp);
|
|||
|
// uint32_t real_vmc_5s = calc_real_vmc(pim_5sec_ary);
|
|||
|
//
|
|||
|
// // persist_write_int(DAILY_x1000_KCAL_PERSIST_KEY,real_vmc_5s*1000);
|
|||
|
//
|
|||
|
//
|
|||
|
// struct config_general cg = get_config_general();
|
|||
|
//
|
|||
|
// // filter the result
|
|||
|
// // filt_cosine_win_mean0(work_ary, dlen_smp, 1);
|
|||
|
//
|
|||
|
// get_fftmag_0pad_mean0(work_ary, dlen_smp, dlenpwr_ary, fft_oflw_scl);
|
|||
|
//
|
|||
|
// uint16_t max_mag_hz = max_mag_hz_0pad(work_ary);
|
|||
|
//
|
|||
|
// //uint8_t score0 = score_fftmag_hz_rng_l2(work_ary, dlenpwr_ary, max_mag_hz-1, max_mag_hz+1);
|
|||
|
// // uint8_t score0 = score_fftmag_hz_rng_abs(work_ary, dlenpwr_ary, max_mag_hz, max_mag_hz);
|
|||
|
// uint8_t score0 = score_fftmag_hz_rng_abs(work_ary, dlenpwr_ary, max_mag_hz-1, max_mag_hz+1);
|
|||
|
//
|
|||
|
// uint16_t stepc_tmp = 0;
|
|||
|
// // NOTE!, use a simple linear regression to scale the fft_threshold with the
|
|||
|
// // vmc. Actually, this is quite computationally sound, we just need to shift
|
|||
|
// // it over by a few for safety, and we can auto adjust the parameters so that
|
|||
|
// // they can be *very* tight. This way, we can reject steps very easily.
|
|||
|
// if(
|
|||
|
// ((score0 >= cg.stepc_fft_thres0[0]) && (score0 <= cg.stepc_fft_thres0[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres0)&&(real_vmc_5s < cg.stepc_vmc_thres1))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres1[0]) && (score0 <= cg.stepc_fft_thres1[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres1)&&(real_vmc_5s < cg.stepc_vmc_thres2 ))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres2[0]) && (score0 <= cg.stepc_fft_thres2[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres2)&&(real_vmc_5s < cg.stepc_vmc_thres3))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres3[0]) && (score0 <= cg.stepc_fft_thres3[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres3 ) )
|
|||
|
// ){
|
|||
|
// // we subtract by an expected 0.5 cause the window truncation and
|
|||
|
// // integer fft approximation causes to push up a half frequency ~0.1Hz.
|
|||
|
// // stepc_tmp = max_mag_hz - ((uint16_t) (rand()%2));
|
|||
|
// stepc_tmp = max_mag_hz;
|
|||
|
// }
|
|||
|
// return stepc_tmp;
|
|||
|
// }
|
|||
|
|
|||
|
|
|||
|
// if(
|
|||
|
// ((score0 >= cg.stepc_fft_thres0)
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres0)&&(real_vmc_5s < cg.stepc_vmc_thres1))
|
|||
|
// || ((x100_mean_vm_accel >= cg.stepc_vma_thres0)
|
|||
|
// && (((score0 >= cg.stepc_fft_thres1)
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres1)&&(real_vmc_5s < cg.stepc_vmc_thres2 ))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres2)
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres2)&&(real_vmc_5s < cg.stepc_vmc_thres3))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres3) && (real_vmc_5s >= cg.stepc_vmc_thres3 ) )))
|
|||
|
// ){
|
|||
|
//
|
|||
|
// if(
|
|||
|
// ((score0 >= cg.stepc_fft_thres0)
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres0)&&(real_vmc_5s < cg.stepc_vmc_thres1))
|
|||
|
// || ( ((score0 >= cg.stepc_fft_thres1)
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres1)&&(real_vmc_5s < cg.stepc_vmc_thres2 ))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres2)
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres2)&&(real_vmc_5s < cg.stepc_vmc_thres3))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres3) && (real_vmc_5s >= cg.stepc_vmc_thres3 ) ))
|
|||
|
// ){
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// /* RAW STATISTICS */
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// int32_t mean_l1_stat(int16_t *d, int16_t dlen){
|
|||
|
// int32_t mean = 0;
|
|||
|
//
|
|||
|
// for(int16_t i = 0; i < dlen; i++ ){
|
|||
|
// mean += d[i];
|
|||
|
// }
|
|||
|
// /* We don't overflow protection the mean output because the values can
|
|||
|
// * exceed 125 over the course of a second */
|
|||
|
// return mean/dlen; // divide out # samples to get mean
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// int32_t pim_filt(int16_t *d, int16_t dlen){
|
|||
|
// /* This function calculates the pim of the given array, single axis
|
|||
|
// * constuction and application of filters are contained with the function */
|
|||
|
// int32_t pim_local = 0;
|
|||
|
//
|
|||
|
// /* calculate the filter */
|
|||
|
// // the mean
|
|||
|
// int32_t mean = mean_l1_stat(d, dlen);
|
|||
|
//
|
|||
|
// /* apply the filter */
|
|||
|
// // remove the mean
|
|||
|
// for(int16_t i = 0; i < dlen; i++){
|
|||
|
// pim_local += abs(d[i] - mean);
|
|||
|
// }
|
|||
|
//
|
|||
|
// /* Return the local pim for given axis */
|
|||
|
// return pim_local;
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// uint32_t calc_scaled_vmc(uint32_t *pim_ary, uint32_t oflw_scl){
|
|||
|
// // This function calculates the VMCPM from the PIM array for each
|
|||
|
// // epoch.
|
|||
|
// // INPUTS
|
|||
|
// // *pim_ary -> the actual array of cpm for each axis
|
|||
|
// // oflw_scl -> dividing factor to reduce pim_ary so doesnt overflow
|
|||
|
// // PARAMTERS
|
|||
|
// // oflw_cap -> cap to prevent overflow when pim_ary[:].^2 is summed
|
|||
|
// // OUTPUT
|
|||
|
// // @ return -> the sqrt of the scaled vmcpm
|
|||
|
// uint32_t oflw_cap = 37500;
|
|||
|
// for(int16_t axis = 0; axis < 3; axis++){
|
|||
|
// pim_ary[axis] = pim_ary[axis]/oflw_scl;
|
|||
|
// pim_ary[axis] = (pim_ary[axis] < oflw_cap) ? pim_ary[axis] : oflw_cap;
|
|||
|
// }
|
|||
|
//
|
|||
|
// /* VMCPM = sqrt(pim_x^2 + pim_y^2 + pim_z^2)*/
|
|||
|
// // calculate VMCPM, then take sqrt to compress for storage
|
|||
|
// return isqrt( pim_ary[0]*pim_ary[0] + pim_ary[1]*pim_ary[1] + pim_ary[2]*pim_ary[2]) ;
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
// uint8_t compressed_vmcpm(uint32_t *pim_ary, uint32_t oflw_scl){
|
|||
|
// return (uint8_t) isqrt(calc_scaled_vmc(pim_ary, oflw_scl));
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
// uint32_t calc_real_vmc(uint32_t *pim_ary, uint32_t oflw_scl){
|
|||
|
//
|
|||
|
// uint32_t scl_vmcpm = calc_scaled_vmc(pim_ary,oflw_scl);
|
|||
|
// // we first assume the PIM is calculated on 1 = 1G, then we scale
|
|||
|
// // to x10 the real vmcpm, THEN we divide by 1250 cause 125*10, and
|
|||
|
// // we divide the PIM calculated on 125 = 1G so we FINALLY get the
|
|||
|
// // *real* VMCPM given that we calculated on the Pebble accel where
|
|||
|
// // 1000/8 = 125 = 1G.
|
|||
|
// return (oflw_scl*scl_vmcpm*x10_RAW_1G_PIM_CPM_TO_REAL_CPM)/1250;
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// uint32_t calc_real_cpm(uint32_t *pim_ary){
|
|||
|
// // This function calculates the CPM from the PIM array for each
|
|||
|
// // epoch.
|
|||
|
// // INPUTS
|
|||
|
// // *pim_ary -> the actual array of cpm for each axis
|
|||
|
//
|
|||
|
// // we first assume the PIM is calculated on 1 = 1G, then we scale
|
|||
|
// // to x10 the real cpm, THEN we divide by 1250 cause 125*10, and
|
|||
|
// // we divide the PIM calculated on 125 = 1G so we FINALLY get the
|
|||
|
// // *real* VMCPM given that we calculated on the Pebble accel where
|
|||
|
// // 1000/8 = 125 = 1G.
|
|||
|
// return ((pim_ary[0] + pim_ary[1] + pim_ary[2])*x10_RAW_1G_PIM_CPM_TO_REAL_CPM)/1250;
|
|||
|
// }
|
|||
|
//
|
|||
|
// uint32_t calc_x1000_kcalpm(uint32_t *pim_ary, uint32_t oflw_scl){
|
|||
|
// uint32_t x1000_kcalpm;
|
|||
|
//
|
|||
|
// // update the general config
|
|||
|
//
|
|||
|
// struct config_general cur_config;
|
|||
|
// persist_read_data(CONFIG_GENERAL_PERSIST_KEY,&cur_config,sizeof(cur_config));
|
|||
|
// uint8_t pweight_kg = cur_config.pweight_kg;
|
|||
|
//
|
|||
|
//
|
|||
|
// // NOTE, we have to re-expand the vmcpm by its scaling factor
|
|||
|
// // used to prevent overflow
|
|||
|
// uint32_t real_vmcpm = calc_real_vmc(pim_ary, oflw_scl);
|
|||
|
// uint32_t real_cpm = calc_real_cpm(pim_ary);
|
|||
|
//
|
|||
|
//
|
|||
|
// // NOTE!!, this assumes the equation from Freedson VM2 11'
|
|||
|
// // if VMCPM > 2453
|
|||
|
// // Kcals/min= 0.001064×VM + 0.087512(BM) - 5.500229
|
|||
|
// // else
|
|||
|
// // Kcals/min=CPM×0.0000191×BM
|
|||
|
// //
|
|||
|
// // where
|
|||
|
// // VM = Vector Magnitude Combination (per minute) of all 3 axes (sqrt((Axis 1)^2+(Axis 2)^2+(Axis 3)^2])
|
|||
|
// // VMCPM = Vector Magnitude Counts per Minute
|
|||
|
// // CPM = Counts per Minute
|
|||
|
// // BM = Body Mass in kg
|
|||
|
//
|
|||
|
// // NOTE, we have to scale ALL of these things by 10000, and since we
|
|||
|
// // know that each element of the pim_ary maxes out at around 375000,
|
|||
|
// // we know that the upper bound of cpm is 3*400,000 = 1,200,000, so
|
|||
|
// // to stay under the 4,294,967,296 overflow cap, we can only go to
|
|||
|
// // a factor of 10,000. BUT, problems still, cause we need to selectively
|
|||
|
// // decide which ones we are going to add/subtract. We WILL assume
|
|||
|
// // that the output of this function will be (kcal/min)*1000, which
|
|||
|
// // is also captured in the worker's function, cause we don't
|
|||
|
// // want to have big rounding down errors when we are at basal metabolic
|
|||
|
// // rate estimates.
|
|||
|
//
|
|||
|
// if(real_vmcpm > 2453){
|
|||
|
// // if VMCPM > 2453
|
|||
|
// // Kcals/min= 0.001064×VM + 0.087512(BM) - 5.500229
|
|||
|
// x1000_kcalpm = (1064*real_vmcpm)/1000 + (875*pweight_kg)/10 + 5500;
|
|||
|
// }else{
|
|||
|
// // else
|
|||
|
// // Kcals/min=CPM×0.0000191×BM
|
|||
|
// x1000_kcalpm = (real_cpm*19*pweight_kg)/1000;
|
|||
|
// }
|
|||
|
//
|
|||
|
// return x1000_kcalpm;
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// void vm_accel(int16_t **d, int16_t *w, int16_t max_vm, int16_t dlen){
|
|||
|
// /* EVALUATE THE VECTOR ACCELERATION MAGNITUDE, WRITE TO first array */
|
|||
|
// // NOTE: we can look at the entire array, because we only write to
|
|||
|
// // the first N_SMP_EPOCH, the remainder being 0's, which will
|
|||
|
// // evalulate to a vector mag of 0
|
|||
|
// for(int16_t i = 0; i < dlen; i++){
|
|||
|
// // evaluate the vector mag of acceleration, write to work array w
|
|||
|
// w[i] = (int16_t) isqrt( d[0][i]*d[0][i] + d[1][i]*d[1][i] + d[2][i]*d[2][i] );
|
|||
|
// // w[i] = (int16_t) ( (d[0][i]*d[0][i]) + (d[1][i]*d[1][i]) + (d[2][i]*d[2][i]) );
|
|||
|
// // cap the vector mags as max_vm to prevent overflow
|
|||
|
// w[i] = ((abs(w[i]) <= max_vm) ? w[i] : ((w[i] > 0 ) ? 1 : (-1))*max_vm);
|
|||
|
// }
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// int16_t stepc_fftalg_0pad(int16_t *d, int16_t dlen_smp, int16_t dlenpwr_ary, int16_t oflw_scl){
|
|||
|
// // NOTE !!! WE ASSUME THE d INPUT IS ALREADY SCALED TO PREVENT OVERFLOW
|
|||
|
// // AND THAT IS MEAN-PADDED
|
|||
|
// /* PARAMETERS */
|
|||
|
// int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
// int16_t lhz_i = 3;
|
|||
|
// int16_t hhz_i = 20;
|
|||
|
// struct config_general init_ps = get_config_general();
|
|||
|
// // int16_t thres = 21; // 0.21 *100 // NOTE, thres range 0 to 100: 2 digits of precision
|
|||
|
// int16_t thres = init_ps.step_class_thres; // 0.21 *100 // NOTE, thres range 0 to 100: 2 digits of precision
|
|||
|
//
|
|||
|
// // int16_t is_step = 0; // assume it is not a step period
|
|||
|
//
|
|||
|
// // reduce the scale
|
|||
|
// for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
// d[i] = d[i]/oflw_scl;
|
|||
|
// }
|
|||
|
//
|
|||
|
// // evaluate the mean and remove it from the vm_accel
|
|||
|
// // we do this here because we want the end of the work array to maintain
|
|||
|
// // a known value of 0 from the calloc operation
|
|||
|
//
|
|||
|
// // We PRETEND that the last few values of the dlen_ary elements are
|
|||
|
// // equal to the mean of the dlen_smp. Hence, the mean of the first
|
|||
|
// // dlen_smp elements is the same as the mean of the dlen_ary elements
|
|||
|
// // THEN, we pretend that we removed the mean of the dlen_ary (also
|
|||
|
// // mean of dlen_smp) from each in dlen_ary. Then, the last few element
|
|||
|
// // in dlen_ary are 0, like we desire.
|
|||
|
// int16_t mean = mean_l1_stat(d, dlen_smp);
|
|||
|
// for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
// d[i] = d[i] - mean;
|
|||
|
// }
|
|||
|
//
|
|||
|
// /* EVALUATE THE MAGNITUDE OF THE FFT COEFFICIENTS AND WRITE TO d ARRAY */
|
|||
|
// fft_2radix_real(d, dlenpwr_ary);
|
|||
|
// fft_mag(d, dlenpwr_ary);
|
|||
|
//
|
|||
|
// /* EVALUATE THE STEP SCORE FOR EPOCH */
|
|||
|
// // -> This section can be fairly complex if needed
|
|||
|
// // When remove the mean, the DC is zero, so we add the DC value back
|
|||
|
// // -> (mean * length of array)
|
|||
|
// // to evaluate the 4-20hz integral against the total integral
|
|||
|
// int score = (100*(integral_abs(d, lhz_i, hhz_i)))/(integral_abs(d, 0, dlen_ary/2) + mean*dlen_ary);
|
|||
|
//
|
|||
|
// // evaluate if the period is a step epoch, based on score
|
|||
|
// int16_t max_hz_val = 0;
|
|||
|
// int16_t max_hz_i = 0; // if NOT a step period, then return 0 for no steps
|
|||
|
// if( score >= thres){
|
|||
|
// // if it is a step epoch, find the hz index with largest mag
|
|||
|
// for(int16_t i = lhz_i; i <= hhz_i; i++){
|
|||
|
// if(d[i] > max_hz_val ){
|
|||
|
// max_hz_val = d[i];
|
|||
|
// max_hz_i = i;
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|
|||
|
// /* RETURN NUMBERS OF STEPS IN EPOCH */
|
|||
|
// return max_hz_i; // DC index is 0, so max_hz_i is HZ directly
|
|||
|
// }
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
// // TEST : PASSED
|
|||
|
// int16_t stepc_fftalg_0pad(int16_t *d, int16_t dlen_smp, int16_t dlenpwr_ary, int16_t oflw_scl){
|
|||
|
// // NOTE !!! WE ASSUME THE d INPUT IS ALREADY SCALED TO PREVENT OVERFLOW
|
|||
|
// // AND THAT IS MEAN-PADDED
|
|||
|
// /* PARAMETERS */
|
|||
|
// int16_t dlen_ary = pow_int(2,dlenpwr_ary);
|
|||
|
// int16_t lhz_i = 3;
|
|||
|
// int16_t hhz_i = 20;
|
|||
|
// struct config_general init_ps = get_config_general();
|
|||
|
// // int16_t thres = 21; // 0.21 *100 // NOTE, thres range 0 to 100: 2 digits of precision
|
|||
|
// int16_t thres = init_ps.stepc_fft_thres3; // 0.21 *100 // NOTE, thres range 0 to 100: 2 digits of precision
|
|||
|
//
|
|||
|
// // int16_t is_step = 0; // assume it is not a step period
|
|||
|
//
|
|||
|
// // reduce the scale
|
|||
|
// for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
// d[i] = d[i]/oflw_scl;
|
|||
|
// }
|
|||
|
//
|
|||
|
// // evaluate the mean and remove it from the vm_accel
|
|||
|
// // we do this here because we want the end of the work array to maintain
|
|||
|
// // a known value of 0 from the calloc operation
|
|||
|
//
|
|||
|
// // We PRETEND that the last few values of the dlen_ary elements are
|
|||
|
// // equal to the mean of the dlen_smp. Hence, the mean of the first
|
|||
|
// // dlen_smp elements is the same as the mean of the dlen_ary elements
|
|||
|
// // THEN, we pretend that we removed the mean of the dlen_ary (also
|
|||
|
// // mean of dlen_smp) from each in dlen_ary. Then, the last few element
|
|||
|
// // in dlen_ary are 0, like we desire.
|
|||
|
//
|
|||
|
// int16_t mean = mean_l1_stat(d, dlen_smp);
|
|||
|
// for(int16_t i = 0; i < dlen_smp; i++){
|
|||
|
// d[i] = d[i] - mean;
|
|||
|
// }
|
|||
|
//
|
|||
|
// /* EVALUATE THE MAGNITUDE OF THE FFT COEFFICIENTS AND WRITE TO d ARRAY */
|
|||
|
// fft_2radix_real(d, dlenpwr_ary);
|
|||
|
// fft_mag(d, dlenpwr_ary);
|
|||
|
//
|
|||
|
// /* EVALUATE THE STEP SCORE FOR EPOCH */
|
|||
|
// // -> This section can be fairly complex if needed
|
|||
|
// // When remove the mean, the DC is zero, so we add the DC value back
|
|||
|
// // -> (mean * length of array)
|
|||
|
// // to evaluate the 4-20hz integral against the total integral
|
|||
|
// int score = (100*(integral_abs(d, lhz_i, hhz_i)))/(integral_abs(d, 0, dlen_ary/2) + mean*dlen_ary);
|
|||
|
//
|
|||
|
// // evaluate if the period is a step epoch, based on score
|
|||
|
// int16_t max_hz_val = 0;
|
|||
|
// int16_t max_hz_i = 0; // if NOT a step period, then return 0 for no steps
|
|||
|
// if( score >= thres){
|
|||
|
// // if it is a step epoch, find the hz index with largest mag
|
|||
|
// for(int16_t i = lhz_i; i <= hhz_i; i++){
|
|||
|
// if(d[i] > max_hz_val ){
|
|||
|
// max_hz_val = d[i];
|
|||
|
// max_hz_i = i;
|
|||
|
// }
|
|||
|
// }
|
|||
|
// }
|
|||
|
// /* RETURN NUMBERS OF STEPS IN EPOCH */
|
|||
|
// return max_hz_i; // DC index is 0, so max_hz_i is HZ directly
|
|||
|
// }
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// int16_t calc_stepc_5sec(int16_t *work_ary, int16_t dlen_smp, int16_t dlenpwr_ary,
|
|||
|
// uint32_t *pim_5sec_ary, int16_t fft_oflw_scl){
|
|||
|
// // get the mean acceleration to serve as minimum thresholds for running and
|
|||
|
// // walking
|
|||
|
// int32_t x100_mean_vm_accel = x100_mean_l1_stat(work_ary, dlen_smp);
|
|||
|
// uint32_t real_vmc_5s = calc_real_vmc(pim_5sec_ary);
|
|||
|
//
|
|||
|
// struct config_general cg = get_config_general();
|
|||
|
//
|
|||
|
// // filter the result
|
|||
|
// filt_cosine_win_mean0(work_ary, dlen_smp, 1);
|
|||
|
//
|
|||
|
// get_fftmag_0pad_mean0(work_ary, dlen_smp, dlenpwr_ary, fft_oflw_scl);
|
|||
|
//
|
|||
|
// uint16_t max_mag_hz = max_mag_hz_0pad(work_ary);
|
|||
|
//
|
|||
|
// //uint8_t score0 = score_fftmag_hz_rng_l2(work_ary, dlenpwr_ary, max_mag_hz-1, max_mag_hz+1);
|
|||
|
// // uint8_t score0 = score_fftmag_hz_rng_abs(work_ary, dlenpwr_ary, max_mag_hz, max_mag_hz);
|
|||
|
// uint8_t score0 = score_fftmag_hz_rng_abs(work_ary, dlenpwr_ary, max_mag_hz-1, max_mag_hz+1);
|
|||
|
//
|
|||
|
// uint16_t stepc_tmp = 0;
|
|||
|
//
|
|||
|
// struct FeatureSample new_fsmp;
|
|||
|
// new_fsmp.f[0] = real_vmc_5s;
|
|||
|
// new_fsmp.f[1] = score0;
|
|||
|
// update_fsmp_buf(new_fsmp);
|
|||
|
// update_trained_dists();
|
|||
|
// if( classify_feature_sample(new_fsmp) > 0){
|
|||
|
// stepc_tmp = max_mag_hz;
|
|||
|
// }
|
|||
|
//
|
|||
|
// return stepc_tmp;
|
|||
|
// }
|
|||
|
|
|||
|
|
|||
|
// // TEST : PASSED
|
|||
|
// int32_t pim_filt(int16_t *d, int16_t dlen){
|
|||
|
// /* This function calculates the pim of the given array, single axis
|
|||
|
// * constuction and application of filters are contained with the function */
|
|||
|
// int32_t pim_local = 0;
|
|||
|
|
|||
|
// /* calculate the filter */
|
|||
|
// // the mean
|
|||
|
// int32_t mean = mean_l1_stat(d, dlen);
|
|||
|
|
|||
|
// /* apply the filter */
|
|||
|
// // remove the mean
|
|||
|
// for(int16_t i = 0; i < dlen; i++){
|
|||
|
// pim_local += abs(d[i] - mean);
|
|||
|
// }
|
|||
|
|
|||
|
// /* Return the local pim for given axis */
|
|||
|
// return pim_local;
|
|||
|
// }
|
|||
|
|
|||
|
// if(
|
|||
|
// ((score0 >= cg.stepc_fft_thres0[0]) && (score0 <= cg.stepc_fft_thres0[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres0)&&(real_vmc_5s < cg.stepc_vmc_thres1))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres1[0]) && (score0 <= cg.stepc_fft_thres1[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres1)&&(real_vmc_5s < cg.stepc_vmc_thres2 ))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres2[0]) && (score0 <= cg.stepc_fft_thres2[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres2)&&(real_vmc_5s < cg.stepc_vmc_thres3))
|
|||
|
// || ((score0 >= cg.stepc_fft_thres3[0]) && (score0 <= cg.stepc_fft_thres3[1])
|
|||
|
// && (real_vmc_5s >= cg.stepc_vmc_thres3 ) )
|
|||
|
// ){
|
|||
|
// // we subtract by an expected 0.5 cause the window truncation and
|
|||
|
// // integer fft approximation causes to push up a half frequency ~0.1Hz.
|
|||
|
// // stepc_tmp = max_mag_hz - ((uint16_t) (rand()%2));
|
|||
|
// stepc_tmp = max_mag_hz;
|
|||
|
// }
|