mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-09-01 03:55:47 +02:00
Garmin: Parse workout physiological metrics
This commit is contained in:
parent
21de228204
commit
42dfb6ad4a
@ -106,6 +106,7 @@ public class ActivitySummaryEntries {
|
|||||||
public static final String MAXIMUM_OXYGEN_UPTAKE = "maximumOxygenUptake";
|
public static final String MAXIMUM_OXYGEN_UPTAKE = "maximumOxygenUptake";
|
||||||
public static final String RECOVERY_TIME = "recoveryTime";
|
public static final String RECOVERY_TIME = "recoveryTime";
|
||||||
public static final String ESTIMATED_SWEAT_LOSS = "estimatedSweatLoss";
|
public static final String ESTIMATED_SWEAT_LOSS = "estimatedSweatLoss";
|
||||||
|
public static final String LACTATE_THRESHOLD_HR = "lactateThresholdHeartRate";
|
||||||
|
|
||||||
public static final String CYCLING_POWER_AVERAGE = "cyclingPowerAverage";
|
public static final String CYCLING_POWER_AVERAGE = "cyclingPowerAverage";
|
||||||
public static final String CYCLING_POWER_MIN = "cyclingPowerMin";
|
public static final String CYCLING_POWER_MIN = "cyclingPowerMin";
|
||||||
|
@ -226,7 +226,7 @@ public class ActivitySummaryJsonSummary {
|
|||||||
));
|
));
|
||||||
put("TrainingEffect", Arrays.asList(
|
put("TrainingEffect", Arrays.asList(
|
||||||
TRAINING_EFFECT_AEROBIC, TRAINING_EFFECT_ANAEROBIC, WORKOUT_LOAD,
|
TRAINING_EFFECT_AEROBIC, TRAINING_EFFECT_ANAEROBIC, WORKOUT_LOAD,
|
||||||
MAXIMUM_OXYGEN_UPTAKE
|
MAXIMUM_OXYGEN_UPTAKE, RECOVERY_TIME, LACTATE_THRESHOLD_HR
|
||||||
));
|
));
|
||||||
put("laps", Arrays.asList(
|
put("laps", Arrays.asList(
|
||||||
LAP_PACE_AVERAGE, LAPS, LANE_LENGTH
|
LAP_PACE_AVERAGE, LAPS, LANE_LENGTH
|
||||||
|
@ -14,11 +14,20 @@ import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.
|
|||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_FAT_BURN;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_FAT_BURN;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_NA;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_NA;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_WARM_UP;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.HR_ZONE_WARM_UP;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.LACTATE_THRESHOLD_HR;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.MAXIMUM_OXYGEN_UPTAKE;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.RECOVERY_TIME;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TRAINING_EFFECT_AEROBIC;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.TRAINING_EFFECT_ANAEROBIC;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BPM;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BPM;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_HOURS;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KCAL;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KCAL;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_ML;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_ML;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_ML_KG_MIN;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_NONE;
|
||||||
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS;
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_SECONDS;
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.WORKOUT_LOAD;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@ -76,6 +85,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitHrvSummary;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitHrvSummary;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitHrvValue;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitHrvValue;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitMonitoring;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitMonitoring;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitPhysiologicalMetrics;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitRecord;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSession;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSleepStage;
|
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages.FitSleepStage;
|
||||||
@ -105,6 +115,7 @@ public class FitImporter {
|
|||||||
private FitFileId fileId = null;
|
private FitFileId fileId = null;
|
||||||
private FitSession session = null;
|
private FitSession session = null;
|
||||||
private FitSport sport = null;
|
private FitSport sport = null;
|
||||||
|
private FitPhysiologicalMetrics physiologicalMetrics = null;
|
||||||
|
|
||||||
public FitImporter(final Context context, final GBDevice gbDevice) {
|
public FitImporter(final Context context, final GBDevice gbDevice) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -201,6 +212,9 @@ public class FitImporter {
|
|||||||
// We only support 1 session
|
// We only support 1 session
|
||||||
session = (FitSession) record;
|
session = (FitSession) record;
|
||||||
}
|
}
|
||||||
|
} else if (record instanceof FitPhysiologicalMetrics) {
|
||||||
|
LOG.debug("Physiological Metrics: {}", record);
|
||||||
|
physiologicalMetrics = (FitPhysiologicalMetrics) record;
|
||||||
} else if (record instanceof FitSport) {
|
} else if (record instanceof FitSport) {
|
||||||
LOG.debug("Sport: {}", record);
|
LOG.debug("Sport: {}", record);
|
||||||
if (sport != null) {
|
if (sport != null) {
|
||||||
@ -379,6 +393,25 @@ public class FitImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (physiologicalMetrics != null) {
|
||||||
|
// TODO lactate_threshold_heart_rate
|
||||||
|
if (physiologicalMetrics.getAerobicEffect() != null) {
|
||||||
|
summaryData.add(TRAINING_EFFECT_AEROBIC, physiologicalMetrics.getAerobicEffect(), UNIT_NONE);
|
||||||
|
}
|
||||||
|
if (physiologicalMetrics.getAnaerobicEffect() != null) {
|
||||||
|
summaryData.add(TRAINING_EFFECT_ANAEROBIC, physiologicalMetrics.getAnaerobicEffect(), UNIT_NONE);
|
||||||
|
}
|
||||||
|
if (physiologicalMetrics.getMetMax() != null) {
|
||||||
|
summaryData.add(MAXIMUM_OXYGEN_UPTAKE, physiologicalMetrics.getMetMax().floatValue() * 3.5f, UNIT_ML_KG_MIN);
|
||||||
|
}
|
||||||
|
if (physiologicalMetrics.getRecoveryTime() != null) {
|
||||||
|
summaryData.add(RECOVERY_TIME, physiologicalMetrics.getRecoveryTime() / 60f, UNIT_HOURS);
|
||||||
|
}
|
||||||
|
if (physiologicalMetrics.getLactateThresholdHeartRate() != null) {
|
||||||
|
summaryData.add(LACTATE_THRESHOLD_HR, physiologicalMetrics.getLactateThresholdHeartRate(), UNIT_BPM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
summary.setSummaryData(summaryData.toString());
|
summary.setSummaryData(summaryData.toString());
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
summary.setRawDetailsPath(file.getAbsolutePath());
|
summary.setRawDetailsPath(file.getAbsolutePath());
|
||||||
@ -457,6 +490,7 @@ public class FitImporter {
|
|||||||
fileId = null;
|
fileId = null;
|
||||||
session = null;
|
session = null;
|
||||||
sport = null;
|
sport = null;
|
||||||
|
physiologicalMetrics = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persistActivitySamples() {
|
private void persistActivitySamples() {
|
||||||
|
@ -208,6 +208,17 @@ public class GlobalFITMessage {
|
|||||||
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// https://github.com/GoldenCheetah/GoldenCheetah/blob/71e3928bc614f3209d9977d90cc50b942999b855/src/FileIO/FitRideFile.cpp#L1998
|
||||||
|
public static GlobalFITMessage PHYSIOLOGICAL_METRICS = new GlobalFITMessage(140, "PHYSIOLOGICAL_METRICS", Arrays.asList(
|
||||||
|
new FieldDefinitionPrimitive(4, BaseType.UINT8, "aerobic_effect", 10, 0),
|
||||||
|
new FieldDefinitionPrimitive(7, BaseType.SINT32, "met_max", 65536, 0),
|
||||||
|
new FieldDefinitionPrimitive(9, BaseType.UINT16, "recovery_time", 1, 0), // minutes
|
||||||
|
new FieldDefinitionPrimitive(14, BaseType.UINT16, "lactate_threshold_heart_rate", 1, 0), // bpm
|
||||||
|
//new FieldDefinitionPrimitive(15, BaseType.UINT16, "lactate_threshold_speed", 1, 0), // m/s // TODO confirm scale
|
||||||
|
new FieldDefinitionPrimitive(20, BaseType.UINT8, "anaerobic_effect", 10, 0),
|
||||||
|
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
|
||||||
|
));
|
||||||
|
|
||||||
public static GlobalFITMessage WATCHFACE_SETTINGS = new GlobalFITMessage(159, "WATCHFACE_SETTINGS", Arrays.asList(
|
public static GlobalFITMessage WATCHFACE_SETTINGS = new GlobalFITMessage(159, "WATCHFACE_SETTINGS", Arrays.asList(
|
||||||
new FieldDefinitionPrimitive(0, BaseType.ENUM, "mode"), //1=analog
|
new FieldDefinitionPrimitive(0, BaseType.ENUM, "mode"), //1=analog
|
||||||
new FieldDefinitionPrimitive(1, BaseType.BASE_TYPE_BYTE, "layout")
|
new FieldDefinitionPrimitive(1, BaseType.BASE_TYPE_BYTE, "layout")
|
||||||
@ -314,6 +325,7 @@ public class GlobalFITMessage {
|
|||||||
put(55, MONITORING);
|
put(55, MONITORING);
|
||||||
put(127, CONNECTIVITY);
|
put(127, CONNECTIVITY);
|
||||||
put(128, WEATHER);
|
put(128, WEATHER);
|
||||||
|
put(140, PHYSIOLOGICAL_METRICS);
|
||||||
put(159, WATCHFACE_SETTINGS);
|
put(159, WATCHFACE_SETTINGS);
|
||||||
put(160, GPS_METADATA);
|
put(160, GPS_METADATA);
|
||||||
put(206, FIELD_DESCRIPTION);
|
put(206, FIELD_DESCRIPTION);
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//
|
||||||
|
// WARNING: This class was auto-generated, please avoid modifying it directly.
|
||||||
|
// See nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.codegen.FitCodeGen
|
||||||
|
//
|
||||||
|
public class FitPhysiologicalMetrics extends RecordData {
|
||||||
|
public FitPhysiologicalMetrics(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
|
||||||
|
super(recordDefinition, recordHeader);
|
||||||
|
|
||||||
|
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
|
||||||
|
if (globalNumber != 140) {
|
||||||
|
throw new IllegalArgumentException("FitPhysiologicalMetrics expects global messages of " + 140 + ", got " + globalNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Float getAerobicEffect() {
|
||||||
|
return (Float) getFieldByNumber(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Double getMetMax() {
|
||||||
|
return (Double) getFieldByNumber(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getRecoveryTime() {
|
||||||
|
return (Integer) getFieldByNumber(9);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Integer getLactateThresholdHeartRate() {
|
||||||
|
return (Integer) getFieldByNumber(14);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Float getAnaerobicEffect() {
|
||||||
|
return (Float) getFieldByNumber(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Long getTimestamp() {
|
||||||
|
return (Long) getFieldByNumber(253);
|
||||||
|
}
|
||||||
|
}
|
@ -47,6 +47,8 @@ public class FitRecordDataFactory {
|
|||||||
return new FitConnectivity(recordDefinition, recordHeader);
|
return new FitConnectivity(recordDefinition, recordHeader);
|
||||||
case 128:
|
case 128:
|
||||||
return new FitWeather(recordDefinition, recordHeader);
|
return new FitWeather(recordDefinition, recordHeader);
|
||||||
|
case 140:
|
||||||
|
return new FitPhysiologicalMetrics(recordDefinition, recordHeader);
|
||||||
case 159:
|
case 159:
|
||||||
return new FitWatchfaceSettings(recordDefinition, recordHeader);
|
return new FitWatchfaceSettings(recordDefinition, recordHeader);
|
||||||
case 160:
|
case 160:
|
||||||
|
@ -2131,6 +2131,8 @@
|
|||||||
<string name="currentWorkoutLoad">Workout Load</string>
|
<string name="currentWorkoutLoad">Workout Load</string>
|
||||||
<string name="maximumOxygenUptake">Maximum Oxygen Uptake</string>
|
<string name="maximumOxygenUptake">Maximum Oxygen Uptake</string>
|
||||||
<string name="estimatedSweatLoss">Estimated Sweat Loss</string>
|
<string name="estimatedSweatLoss">Estimated Sweat Loss</string>
|
||||||
|
<string name="lactateThresholdHeartRate">Lactate Threshold Heart Rate</string>
|
||||||
|
<string name="recoveryTime">Recovery Time</string>
|
||||||
<string name="averageStride">Average Stride</string>
|
<string name="averageStride">Average Stride</string>
|
||||||
<string name="maxStride">Max Stride</string>
|
<string name="maxStride">Max Stride</string>
|
||||||
<string name="minStride">Min Stride</string>
|
<string name="minStride">Min Stride</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user