2022-02-11 08:29:02 +00:00
// Sleep/Wake detection with Estimation of Stationary Sleep-segments (ESS):
// Marko Borazio, Eugen Berlin, Nagihan Kücükyildiz, Philipp M. Scholl and Kristof Van Laerhoven, "Towards a Benchmark for Wearable Sleep Analysis with Inertial Wrist-worn Sensing Units", ICHI 2014, Verona, Italy, IEEE Press, 2014.
// https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en
// sleeplog.status values: 0 = unknown, 1 = not worn, 2 = awake, 3 = sleeping
// load settings into global object
global . sleeplog = Object . assign ( {
enabled : true , // en-/disable completely
logfile : "sleeplog.log" , // logfile
winwidth : 13 , // 13 values, read with 12.5Hz = every 1.04s
nomothresh : 0.012 , // values lower than 0.008 getting triggert by noise
sleepthresh : 577 , // 577 times no movement * 1.04s window width > 10min
tempthresh : 27 , // every temperature above ist registered as worn
} , require ( "Storage" ) . readJSON ( "sleeplog.json" , true ) || { } ) ;
// delete app settings
[ "breaktod" , "maxawake" , "minconsec" ] . forEach ( property => delete global . sleeplog [ property ] ) ;
// check if service enabled
if ( global . sleeplog . enabled ) {
// add cached values and functions to global object
global . sleeplog = Object . assign ( global . sleeplog , {
// set cached values
ess _values : [ ] ,
nomocount : 0 ,
firstnomodate : undefined ,
resting : undefined ,
status : 0 ,
// define acceleration listener function
accel : function ( xyz ) {
// save acceleration magnitude and start calculation on enough saved data
if ( global . sleeplog . ess _values . push ( xyz . mag ) >= global . sleeplog . winwidth ) global . sleeplog . calc ( ) ;
} ,
// define calculator function
calc : function ( ) {
// calculate standard deviation over
var mean = this . ess _values . reduce ( ( prev , cur ) => cur + prev ) / this . winwidth ;
var stddev = Math . sqrt ( this . ess _values . map ( val => Math . pow ( val - mean , 2 ) ) . reduce ( ( prev , cur ) => prev + cur ) / this . winwidth ) ;
// reset saved acceleration data
this . ess _values = [ ] ;
// check for non-movement according to the threshold
if ( stddev < this . nomothresh ) {
// increment non-movement sections count, set date of first non-movement
if ( ++ this . nomocount == 1 ) this . firstnomodate = Math . floor ( Date . now ( ) ) ;
// check resting state and non-movement count against threshold
if ( this . resting !== true && this . nomocount >= this . sleepthresh ) {
// change resting state, status and write to log
this . resting = true ;
// check if the watch is worn
if ( E . getTemperature ( ) > this . tempthresh ) {
// set status and write to log as sleping
this . status = 3 ;
this . log ( this . firstnomodate , 3 , E . getTemperature ( ) ) ;
} else {
// set status and write to log as not worn
this . status = 1 ;
this . log ( this . firstnomodate , 1 , E . getTemperature ( ) ) ;
}
}
} else {
// reset non-movement sections count
this . nomocount = 0 ;
// check resting state
if ( this . resting !== false ) {
// change resting state
this . resting = false ;
// set status and write to log as awake
this . status = 2 ;
this . log ( Math . floor ( Date . now ( ) ) , 2 ) ;
}
}
} ,
// define logging function
log : function ( date , status , temperature , info ) {
// skip logging if logfile is undefined or does not end with ".log"
if ( ! this . logfile || ! this . logfile . endsWith ( ".log" ) ) return ;
// prevent logging on implausible date
if ( date < 9E11 || Date ( ) < 9E11 ) return ;
// set default value for status
status = status || 0 ;
// define storage
var storage = require ( "Storage" ) ;
// read previous logfile
2022-02-11 12:16:59 +00:00
var logContent = storage . read ( this . logfile ) || "" ;
// parse previous logfile
var log = JSON . parse ( logContent . length > 0 ? atob ( logContent ) : "[]" ) ;
2022-02-11 08:29:02 +00:00
// remove last state if it was unknown and is less then 10min ago
if ( log . length > 0 && log [ 0 ] [ 1 ] === 0 &&
Math . floor ( Date . now ( ) ) - log [ 0 ] [ 0 ] < 600000 ) log . shift ( ) ;
// add actual status at the first position if it has changed
if ( log . length === 0 || log [ 0 ] [ 1 ] !== status )
log . unshift ( info ? [ date , status , temperature , info ] : temperature ? [ date , status , temperature ] : [ date , status ] ) ;
// write log to storage
storage . write ( this . logfile , btoa ( JSON . stringify ( log ) ) ) ;
// clear variables
log = undefined ;
storage = undefined ;
} ,
// define stop function (logging will restart if enabled and boot file is executed)
stop : function ( ) {
// remove acceleration and kill listener
Bangle . removeListener ( 'accel' , global . sleeplog . accel ) ;
E . removeListener ( 'kill' , global . sleeplog . stop ) ;
// write log with undefined sleeping status
global . sleeplog . log ( Math . floor ( Date . now ( ) ) ) ;
// reset cached values
global . sleeplog . ess _values = [ ] ;
global . sleeplog . nomocount = 0 ;
global . sleeplog . firstnomodate = undefined ;
global . sleeplog . resting = undefined ;
global . sleeplog . status = 0 ;
} ,
// define restart function (also use for initial starting)
start : function ( ) {
// add acceleration listener
Bangle . on ( 'accel' , global . sleeplog . accel ) ;
// add kill listener
E . on ( 'kill' , global . sleeplog . stop ) ;
} ,
} ) ;
// initial starting
global . sleeplog . start ( ) ;
}