1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-30 14:02:56 +01:00

WatchX(Plus) - Activity Analysis

* add activity intensity interpolation based on heart rate
* add steps per activity sample (based on total steps divided by total activity)
* search data for periods without records to add ActivityKind.TYPE_NOT_MEASURED (15 to 60 min) and ActivityKind.TYPE_NOT_WORN (more than 60 min)
* randomize ActivityKind.DEEP_SLEEP intensity for cool graph results (do not affect sleep counting)
This commit is contained in:
mamutcho 2020-04-25 16:01:13 +03:00 committed by Gitea
parent a4a2bed80c
commit f88bc0cf6d

View File

@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random;
import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.Property; import de.greenrobot.dao.Property;
@ -24,7 +25,7 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
private GBDevice mDevice; private GBDevice mDevice;
private DaoSession mSession; private DaoSession mSession;
private final float movementDivisor = 1500.0f; private final float movementDivisor = 950.0f;
private static final Logger LOG = LoggerFactory.getLogger(WatchXPlusSampleProvider.class); private static final Logger LOG = LoggerFactory.getLogger(WatchXPlusSampleProvider.class);
@ -87,6 +88,34 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
return WatchXPlusActivitySampleDao.Properties.DeviceId; return WatchXPlusActivitySampleDao.Properties.DeviceId;
} }
// generate ActivityKind.TYPE_NOT_MEASURED if there are no data for more than 15 min. and less than 60 min.
// generate ActivityKind.TYPE_NOT_WORN if there are no data for more than 60 min.
@NonNull
private List<WatchXPlusActivitySample> checkActivityData(List<WatchXPlusActivitySample> samples, int notMeasuredTS, int notWornTS) {
int oldTS = 0;
int newTS = 0;
oldTS = samples.get(0).getTimestamp();
for (int i = 0; i < samples.size(); i++) {
//oldTS = resultList.get(i).getTimestamp();
newTS = samples.get(i).getTimestamp();
if ((newTS - oldTS) < notMeasuredTS) { //check data timestamp diff is more than 15 min
oldTS = samples.get(i).getTimestamp();
} else if (((newTS - oldTS) > notMeasuredTS) && ((newTS - oldTS) < notWornTS)) { //set data to ActivityKind.TYPE_NOT_MEASURED) if timestamp diff is more than 15 min
samples.get(i-1).setRawKind(ActivityKind.TYPE_NOT_MEASURED);
samples.get(i).setRawKind(ActivityKind.TYPE_NOT_MEASURED);
oldTS = samples.get(i).getTimestamp();
} else if ((newTS - oldTS) > notWornTS) { //set data to ActivityKind.TYPE_NOT_WORN if timestamp diff is more than 60 min
samples.get(i-1).setRawKind(ActivityKind.TYPE_NOT_WORN);
samples.get(i).setRawKind(ActivityKind.TYPE_NOT_WORN);
oldTS = samples.get(i).getTimestamp();
}
}
return samples;
}
@Override @Override
public List<WatchXPlusActivitySample> getAllActivitySamples(int timestamp_from, int timestamp_to) { public List<WatchXPlusActivitySample> getAllActivitySamples(int timestamp_from, int timestamp_to) {
boolean showRawData = GBApplication.getDeviceSpecificSharedPrefs(mDevice.getAddress()).getBoolean(WatchXPlusConstants.PREF_SHOW_RAW_GRAPH, false); boolean showRawData = GBApplication.getDeviceSpecificSharedPrefs(mDevice.getAddress()).getBoolean(WatchXPlusConstants.PREF_SHOW_RAW_GRAPH, false);
@ -95,18 +124,10 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
} }
List<WatchXPlusActivitySample> samples = getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); List<WatchXPlusActivitySample> samples = getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL);
int numEntries = samples.size(); int numEntries = samples.size();
if (numEntries < 2) { if (numEntries < 3) {
return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); return samples;
} }
// LOG.info(" testing: ts_from:" + timestamp_from + " ts_to: " + timestamp_to);
/*
LOG.info(" testing: samples: ");
for (int i = 0; i < numEntries; i++) {
LOG.info(" s: " + i + " : " + samples.get(i).toString());
}
*/
List<WatchXPlusActivitySample> resultList = new ArrayList<>(numEntries); List<WatchXPlusActivitySample> resultList = new ArrayList<>(numEntries);
// how many elements to scan for sleep sate before and after sleep block // how many elements to scan for sleep sate before and after sleep block
@ -132,7 +153,7 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
if (countNextSleepStart_1 == 0) { if (countNextSleepStart_1 == 0) {
countNextSleepStart_1 = i; countNextSleepStart_1 = i;
// reset start index if next index is far ahead // reset start index if next index is far ahead
if ((countNextSleepStart_1 - sleepStartIndex_1) > seekAhead * 2) { if ((countNextSleepStart_1 - sleepStartIndex_1) > seekAhead * 3) {
sleepStartIndex_1 = countNextSleepStart_1; sleepStartIndex_1 = countNextSleepStart_1;
sleepStopIndex_1 = sleepStartIndex_1; sleepStopIndex_1 = sleepStartIndex_1;
countNextSleepStop_1 = sleepStopIndex_1; countNextSleepStop_1 = sleepStopIndex_1;
@ -141,7 +162,7 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
} }
if ((i - sleepStopIndex_1) < (seekAhead * 3)) { if ((i - sleepStopIndex_1) < (seekAhead * 4)) {
sleepStopIndex_1 = i; sleepStopIndex_1 = i;
} }
countNextSleepStop_1 = i; countNextSleepStop_1 = i;
@ -166,14 +187,14 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
if (countNextSleepStart_2 == 0) { if (countNextSleepStart_2 == 0) {
countNextSleepStart_2 = i; countNextSleepStart_2 = i;
// reset start index if next index is far ahead // reset start index if next index is far ahead
if ((countNextSleepStart_2 - sleepStartIndex_2) > seekAhead * 2) { if ((countNextSleepStart_2 - sleepStartIndex_2) > seekAhead * 3) {
sleepStartIndex_2 = countNextSleepStart_2; sleepStartIndex_2 = countNextSleepStart_2;
sleepStopIndex_2 = sleepStartIndex_2; sleepStopIndex_2 = sleepStartIndex_2;
countNextSleepStop_2 = sleepStopIndex_2; countNextSleepStop_2 = sleepStopIndex_2;
} }
} }
} }
if ((i - sleepStopIndex_2) < (seekAhead * 3)) { if ((i - sleepStopIndex_2) < (seekAhead * 4)) {
sleepStopIndex_2 = i; sleepStopIndex_2 = i;
} }
countNextSleepStop_2 = i; countNextSleepStop_2 = i;
@ -181,7 +202,7 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
} }
if (sleepStartIndex_2 != 0) { if (sleepStartIndex_2 != 0) {
secondBlock = true; secondBlock = true;
LOG.info(" second block "); LOG.info(" Found second block ");
} }
LOG.info(" sleep_1 begin index:" + sleepStartIndex_1 + " next index: " + countNextSleepStart_1 + " sleep end index: " + sleepStopIndex_1 + " sleep end: " + countNextSleepStop_1); LOG.info(" sleep_1 begin index:" + sleepStartIndex_1 + " next index: " + countNextSleepStart_1 + " sleep end index: " + sleepStopIndex_1 + " sleep end: " + countNextSleepStop_1);
@ -232,7 +253,7 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
// add sleep activity // add sleep activity
int newSleepStopIndex_1; int newSleepStopIndex_1;
if ((sleepStopIndex_1 + seekAhead) < next_block) { if ((sleepStopIndex_1 + seekAhead * 2) < next_block) {
newSleepStopIndex_1 = sleepStopIndex_1 + seekAhead * 2; newSleepStopIndex_1 = sleepStopIndex_1 + seekAhead * 2;
} else { } else {
newSleepStopIndex_1 = next_block; newSleepStopIndex_1 = next_block;
@ -314,107 +335,190 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider<WatchXPlusA
} }
} }
} }
if (!secondBlock) {
samples.get(next_block-1).setRawIntensity(100);
samples.get(next_block-1).setRawKind(-1);
resultList.add(samples.get(next_block-1));
return resultList;
}
// SLEEP BLOCK 2 // SLEEP BLOCK 2
if (secondBlock) {
// add sleep activity // add sleep activity
int newSleepStopIndex_2; int newSleepStopIndex_2;
int newSleepStartIndex_2 = 0; int newSleepStartIndex_2 = 0;
boolean replaceActivity_2 = false; boolean replaceActivity_2 = false;
if (sleepStartIndex_2 >= next_block + seekAhead) { if (sleepStartIndex_2 >= next_block + seekAhead) {
newSleepStartIndex_2 = sleepStartIndex_2 - seekAhead; newSleepStartIndex_2 = sleepStartIndex_2 - seekAhead;
} else { } else {
newSleepStartIndex_2 = next_block; newSleepStartIndex_2 = next_block;
} }
if ((sleepStopIndex_2 + seekAhead) < numEntries) { if ((sleepStopIndex_2 + seekAhead * 2) < numEntries) {
newSleepStopIndex_2 = sleepStopIndex_2 + seekAhead; newSleepStopIndex_2 = sleepStopIndex_2 + seekAhead * 2;
} else { } else {
newSleepStopIndex_2 = numEntries; newSleepStopIndex_2 = numEntries;
} }
for (int i = newSleepStartIndex_2; i < newSleepStopIndex_2; i++) { for (int i = newSleepStartIndex_2; i < newSleepStopIndex_2; i++) {
ActivitySample sample = samples.get(i); ActivitySample sample = samples.get(i);
if (i < sleepStartIndex_2) { if (i < sleepStartIndex_2) {
if (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) { if (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) {
replaceActivity_2 = true; replaceActivity_2 = true;
samples.get(i).setRawIntensity(600); samples.get(i).setRawIntensity(600);
resultList.add(samples.get(i)); resultList.add(samples.get(i));
} else { } else {
if (replaceActivity_2) { if (replaceActivity_2) {
samples.get(i).setRawKind(2); samples.get(i).setRawKind(2);
samples.get(i).setRawIntensity(600); samples.get(i).setRawIntensity(600);
resultList.add(samples.get(i)); resultList.add(samples.get(i));
} else { } else {
samples.get(i).setRawIntensity(600); samples.get(i).setRawIntensity(600);
resultList.add(samples.get(i)); resultList.add(samples.get(i));
} }
} }
} }
if ((samples.get(i).getRawKind() == ActivityKind.TYPE_DEEP_SLEEP) || (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP)) { if ((samples.get(i).getRawKind() == ActivityKind.TYPE_DEEP_SLEEP) || (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP)) {
if (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) { if (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) {
if (i > 0) { if (i > 0) {
if (samples.get(i - 1).getHeartRate() > 0) { if (samples.get(i - 1).getHeartRate() > 0) {
samples.get(i).setHeartRate(samples.get(i - 1).getHeartRate()); samples.get(i).setHeartRate(samples.get(i - 1).getHeartRate());
} }
} else { } else {
if (samples.get(i + 1).getHeartRate() > 0) { if (samples.get(i + 1).getHeartRate() > 0) {
samples.get(i).setHeartRate(samples.get(i + 1).getHeartRate()); samples.get(i).setHeartRate(samples.get(i + 1).getHeartRate());
} }
} }
samples.get(i).setRawIntensity(600); samples.get(i).setRawIntensity(600);
resultList.add(samples.get(i)); resultList.add(samples.get(i));
} else { } else {
samples.get(i).setRawIntensity(1000); samples.get(i).setRawIntensity(1000);
resultList.add(samples.get(i)); resultList.add(samples.get(i));
} }
} }
if ((samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) && (i > sleepStopIndex_2)) { if ((samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) && (i > sleepStopIndex_2)) {
samples.get(i).setRawIntensity(600); samples.get(i).setRawIntensity(600);
resultList.add(samples.get(i)); resultList.add(samples.get(i));
} }
} }
// add remaining activity // add remaining activity
if (newSleepStopIndex_2 < numEntries) { if (newSleepStopIndex_2 < numEntries) {
for (int i = newSleepStopIndex_2; i < (numEntries-1); i++) { for (int i = newSleepStopIndex_2; i < (numEntries - 1); i++) {
if (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) { if (samples.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) {
if (samples.get(i).getRawIntensity() <= 300) { if (samples.get(i).getRawIntensity() <= 300) {
samples.get(i).setRawIntensity(200); samples.get(i).setRawIntensity(200);
} else if ((samples.get(i).getRawIntensity() <= 1000) && (samples.get(i).getRawIntensity() > 100)) { } else if ((samples.get(i).getRawIntensity() <= 1000) && (samples.get(i).getRawIntensity() > 100)) {
samples.get(i).setRawIntensity(400); samples.get(i).setRawIntensity(400);
} if (samples.get(i).getRawIntensity() > 1000) { }
samples.get(i).setRawIntensity(600); if (samples.get(i).getRawIntensity() > 1000) {
} samples.get(i).setRawIntensity(600);
samples.get(i).setRawKind(1); }
resultList.add(samples.get(i)); samples.get(i).setRawKind(1);
} else { resultList.add(samples.get(i));
if (samples.get(i).getRawKind() == ActivityKind.TYPE_ACTIVITY) { } else {
if (i < (numEntries - 3)) { if (samples.get(i).getRawKind() == ActivityKind.TYPE_ACTIVITY) {
if ((samples.get(i + 1).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) || (samples.get(i + 2).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) || (samples.get(i + 3).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP)) { if (i < (numEntries - 3)) {
samples.get(i).setRawKind(1); if ((samples.get(i + 1).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) || (samples.get(i + 2).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP) || (samples.get(i + 3).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP)) {
//samples.get(i).setRawIntensity(700); samples.get(i).setRawKind(1);
} else { //samples.get(i).setRawIntensity(700);
samples.get(i).setRawIntensity(1000); } else {
} samples.get(i).setRawIntensity(1000);
} }
//samples.get(i).setRawIntensity(1000); }
} else { //samples.get(i).setRawIntensity(1000);
samples.get(i).setRawIntensity(1000); } else {
} samples.get(i).setRawIntensity(1000);
resultList.add(samples.get(i)); }
} resultList.add(samples.get(i));
} }
} }
samples.get(numEntries-1).setRawIntensity(-1); }
samples.get(numEntries-1).setRawKind(-1); }
// add one ActivityKind.TYPE_NOT_MEASURED at end of data
samples.get(numEntries-1).setRawIntensity(0);
samples.get(numEntries-1).setRawKind(ActivityKind.TYPE_NOT_MEASURED);
samples.get(numEntries-1).setHeartRate(0);
resultList.add(samples.get(numEntries-1)); resultList.add(samples.get(numEntries-1));
return resultList;
// find all steps, total activity intensity and maxHR
int totalSteps = 0;
int maxHeartRate = 10;
numEntries = resultList.size();
for (int i = 0; i < numEntries-1; i++) {
if (resultList.get(i).getRawKind() == ActivityKind.TYPE_ACTIVITY) {
if (resultList.get(i).getSteps() > 0) {
totalSteps = totalSteps + resultList.get(i).getSteps();
}
}
if (resultList.get(i).getHeartRate() > maxHeartRate) {
maxHeartRate = resultList.get(i).getHeartRate();
}
}
// reformat activity data based on heart rate
int newIntensity, correctedSteps;
int totalIntensity = 0;
for (int i = 0; i < numEntries-1; i++) {
if ((resultList.get(i).getRawKind() == ActivityKind.TYPE_ACTIVITY) || (resultList.get(i).getRawKind() == ActivityKind.TYPE_LIGHT_SLEEP)) {
if (resultList.get(i).getRawIntensity() <= 600) { // set interpolated intensity based on heart rate for every TYPE_ACTIVITY which are converted from TYPE_LIGHT_SLEEP
if (resultList.get(i).getHeartRate() < 10) {
newIntensity = resultList.get(i).getRawIntensity() + ((maxHeartRate - resultList.get(i+1).getHeartRate()) * 2);
} else {
newIntensity = resultList.get(i).getRawIntensity() + ((maxHeartRate - resultList.get(i).getHeartRate()) * 2);
}
} else { // because there are not RAW intensity values for every TYPE_ACTIVITY set interpolated intensity based on heart rate
newIntensity = resultList.get(i).getRawIntensity() - ((maxHeartRate - resultList.get(i).getHeartRate()) * 2);
}
/*
if (stepsPerActivity > 0.0f) { // because there are not steps values for every TYPE_ACTIVITY set interpolated steps
correctedSteps = (int) (resultList.get(i).getRawIntensity() / stepsPerActivity);
resultList.get(i).setSteps(correctedSteps);
}
*/
resultList.get(i).setRawIntensity(newIntensity);
if (resultList.get(i).getRawIntensity() > 0) {
totalIntensity = totalIntensity + newIntensity;
}
} else { // because there are not TYPE_DEEP_SLEEP intensity set random DEEP_SLEEP intensity
Random r = new Random();
newIntensity = resultList.get(i).getRawIntensity() - ((maxHeartRate - (int)(r.nextFloat() * maxHeartRate)) * 2);
resultList.get(i).setRawIntensity(newIntensity);
if (resultList.get(i).getRawIntensity() > 0) {
totalIntensity = totalIntensity + newIntensity;
}
}
}
// because there are not steps values for every TYPE_ACTIVITY set interpolated steps
float stepsPerActivity = 0.000f;
int newTotalSteps = 0;
int activityCount = 0;
if (totalSteps > 0) {
stepsPerActivity = totalIntensity / totalSteps;
for (int i = 0; i < numEntries - 1; i++) {
if (resultList.get(i).getRawKind() == ActivityKind.TYPE_ACTIVITY) {
if (stepsPerActivity > 0.0f) {
correctedSteps = (int) (resultList.get(i).getRawIntensity() / stepsPerActivity);
resultList.get(i).setSteps(correctedSteps);
newTotalSteps = newTotalSteps + correctedSteps;
activityCount = activityCount + 1;
}
}
}
}
if (newTotalSteps < totalSteps) {
int stepsDiff = newTotalSteps - totalSteps;
int increaseStepsWith = stepsDiff / activityCount;
if (increaseStepsWith <= 1) {
increaseStepsWith = 2;
}
newTotalSteps = 0;
for (int i = 0; i < numEntries - 1; i++) {
if (resultList.get(i).getRawKind() == ActivityKind.TYPE_ACTIVITY) {
correctedSteps = resultList.get(i).getSteps() + increaseStepsWith;
newTotalSteps = newTotalSteps + correctedSteps;
if (newTotalSteps <= totalSteps) {
resultList.get(i).setSteps(correctedSteps);
} else {
break;
}
}
}
}
return checkActivityData(resultList, 900, 3600);
} }
} }