1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-28 19:45:50 +01:00

Garmin: Fix overcounting of steps

This commit is contained in:
José Rebelo 2024-05-04 20:54:51 +01:00 committed by Daniele Gobbetti
parent 904c995a3e
commit 4d78fdd883
2 changed files with 78 additions and 34 deletions

View File

@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.User;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityPoint;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage;
@ -67,8 +68,7 @@ public class FitImporter {
private final Context context;
private final GBDevice gbDevice;
private final List<GarminActivitySample> activitySamples = new ArrayList<>();
private final SortedMap<Integer, List<GarminActivitySample>> activitySamplesPerTimestamp = new TreeMap<>();
private final SortedMap<Integer, List<FitMonitoring>> activitySamplesPerTimestamp = new TreeMap<>();
private final List<GarminStressSample> stressSamples = new ArrayList<>();
private final List<GarminSpo2Sample> spo2samples = new ArrayList<>();
private final List<GarminEventSample> events = new ArrayList<>();
@ -122,32 +122,11 @@ public class FitImporter {
sample.setStage(stage.getId());
sleepStageSamples.add(sample);
} else if (record instanceof FitMonitoring) {
final Integer hr = ((FitMonitoring) record).getHeartRate();
final Long steps = ((FitMonitoring) record).getCycles();
final Integer activityType = ((FitMonitoring) record).getComputedActivityType();
final Integer intensity = ((FitMonitoring) record).getComputedIntensity();
LOG.trace("Monitoring at {}: hr={} steps={} activityType={} intensity={}", ts, hr, steps, activityType, intensity);
final GarminActivitySample sample = new GarminActivitySample();
sample.setTimestamp(ts.intValue());
if (hr != null) {
sample.setHeartRate(hr);
LOG.trace("Monitoring at {}: {}", ts, record);
if (!activitySamplesPerTimestamp.containsKey(ts.intValue())) {
activitySamplesPerTimestamp.put(ts.intValue(), new ArrayList<>());
}
if (steps != null) {
sample.setSteps(steps.intValue());
}
if (activityType != null) {
sample.setRawKind(activityType);
}
if (intensity != null) {
sample.setRawIntensity(intensity);
}
activitySamples.add(sample);
List<GarminActivitySample> samplesForTimestamp = activitySamplesPerTimestamp.get(ts.intValue());
if (samplesForTimestamp == null) {
samplesForTimestamp = new ArrayList<>();
activitySamplesPerTimestamp.put(ts.intValue(), samplesForTimestamp);
}
samplesForTimestamp.add(sample);
Objects.requireNonNull(activitySamplesPerTimestamp.get(ts.intValue())).add((FitMonitoring) record);
} else if (record instanceof FitSpo2) {
final Integer spo2 = ((FitSpo2) record).getReadingSpo2();
if (spo2 == null || spo2 <= 0) {
@ -344,7 +323,7 @@ public class FitImporter {
}
private void reset() {
activitySamples.clear();
activitySamplesPerTimestamp.clear();
stressSamples.clear();
spo2samples.clear();
events.clear();
@ -358,11 +337,75 @@ public class FitImporter {
}
private void persistActivitySamples() {
if (activitySamples.isEmpty()) {
if (activitySamplesPerTimestamp.isEmpty()) {
return;
}
// FIXME prevent overlapping samples in the same timestamp..
final List<GarminActivitySample> activitySamples = new ArrayList<>(activitySamplesPerTimestamp.size());
// Garmin reports the cumulative steps per activity, but not always, so we need to keep
// track of the number of steps for each activity, and set the sum of all on the sample
final Map<Integer, Long> stepsPerActivity = new HashMap<>();
final int THRESHOLD_NOT_WORN = 10 * 60; // 10 min gap between samples = not-worn
int prevActivityKind = ActivityKind.TYPE_UNKNOWN;
int prevTs = -1;
for (final int ts : activitySamplesPerTimestamp.keySet()) {
if (prevTs > 0 && ts - prevTs > 60) {
// Fill gaps between samples
for (int i = prevTs; i < ts; i += 60) {
final GarminActivitySample sample = new GarminActivitySample();
sample.setTimestamp(i);
sample.setRawKind(ts - prevTs > THRESHOLD_NOT_WORN ? ActivityKind.TYPE_NOT_WORN : prevActivityKind);
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
sample.setSteps(ActivitySample.NOT_MEASURED);
activitySamples.add(sample);
}
}
final List<FitMonitoring> records = activitySamplesPerTimestamp.get(ts);
final GarminActivitySample sample = new GarminActivitySample();
sample.setTimestamp(ts);
sample.setRawKind(ActivityKind.TYPE_ACTIVITY);
sample.setRawIntensity(ActivitySample.NOT_MEASURED);
sample.setSteps(ActivitySample.NOT_MEASURED);
sample.setHeartRate(ActivitySample.NOT_MEASURED);
boolean hasSteps = false;
for (final FitMonitoring record : Objects.requireNonNull(records)) {
final Integer activityType = record.getComputedActivityType().orElse(ActivitySample.NOT_MEASURED);
final Integer hr = record.getHeartRate();
if (hr != null) {
sample.setHeartRate(hr);
}
final Long steps = record.getCycles();
if (steps != null) {
stepsPerActivity.put(activityType, steps);
hasSteps = true;
}
final Integer intensity = record.getComputedIntensity();
if (intensity != null) {
sample.setRawIntensity(intensity);
}
}
if (hasSteps) {
int sumSteps = 0;
for (final Long steps : stepsPerActivity.values()) {
sumSteps += steps;
}
sample.setSteps(sumSteps);
}
activitySamples.add(sample);
prevActivityKind = sample.getRawKind();
prevTs = ts;
}
LOG.debug("Will persist {} activity samples", activitySamples.size());

View File

@ -6,6 +6,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminTimeUti
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;
import nodomain.freeyourgadget.gadgetbridge.util.Optional;
//
// WARNING: This class was auto-generated, please avoid modifying it directly.
@ -85,18 +86,18 @@ public class FitMonitoring extends RecordData {
return computedTimestamp;
}
public Integer getComputedActivityType() {
public Optional<Integer> getComputedActivityType() {
final Integer activityType = getActivityType();
if (activityType != null) {
return activityType;
return Optional.of(activityType);
}
final Integer currentActivityTypeIntensity = getCurrentActivityTypeIntensity();
if (currentActivityTypeIntensity != null) {
return currentActivityTypeIntensity & 0x1F;
return Optional.of(currentActivityTypeIntensity & 0x1F);
}
return null;
return Optional.empty();
}
public Integer getComputedIntensity() {