Garmin: Add respiratory rate chart

This commit is contained in:
José Rebelo
2025-09-24 20:23:50 +01:00
parent 18b10e1ac2
commit b5ed458789
7 changed files with 60 additions and 12 deletions
@@ -1,6 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.activities.workouts.charts;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BPM;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_BREATHS_PER_MIN;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_KMPH;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS;
import static nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries.UNIT_METERS_PER_SECOND;
@@ -51,6 +52,7 @@ public class DefaultWorkoutCharts {
final List<Entry> cadenceDataPoints = new ArrayList<>();
final List<Entry> elevationDataPoints = new ArrayList<>();
final List<Entry> powerDataPoints = new ArrayList<>();
final List<Entry> respiratoryRatePoints = new ArrayList<>();
boolean hasSpeedValues = false;
boolean hasCadenceValues = false;
boolean hasElevationValues = false;
@@ -89,6 +91,9 @@ public class DefaultWorkoutCharts {
if (point.getPower() >= 0) {
powerDataPoints.add(new Entry(tsShorten, (float) point.getPower()));
}
if (point.getRespiratoryRate() >= 0) {
respiratoryRatePoints.add(new Entry(tsShorten, point.getRespiratoryRate()));
}
}
if (!heartRateDataPoints.isEmpty()) {
@@ -111,6 +116,10 @@ public class DefaultWorkoutCharts {
charts.add(createPowerChart(context, powerDataPoints));
}
if (!respiratoryRatePoints.isEmpty()) {
charts.add(createRespiratoryRateChart(context, respiratoryRatePoints));
}
return charts;
}
@@ -224,7 +233,7 @@ public class DefaultWorkoutCharts {
}
private static WorkoutChart createPowerChart(final Context context,
final List<Entry> powerDataPoints) {
final List<Entry> powerDataPoints) {
final String label = String.format("%s (%s)", context.getString(R.string.workout_power), getUnitString(context, UNIT_WATT));
LineDataSet dataset = createLineDataSet(context, powerDataPoints, label, context.getResources().getColor(R.color.chart_line_power));
final ValueFormatter integerFormatter = new ValueFormatter() {
@@ -236,6 +245,26 @@ public class DefaultWorkoutCharts {
return new WorkoutChart("power", context.getString(R.string.workout_power), ActivitySummaryEntries.GROUP_POWER, new LineData(dataset), integerFormatter, getUnitString(context, UNIT_WATT));
}
private static WorkoutChart createRespiratoryRateChart(final Context context,
final List<Entry> powerDataPoints) {
final String label = String.format("%s (%s)", context.getString(R.string.respiratoryrate), getUnitString(context, UNIT_BREATHS_PER_MIN));
LineDataSet dataset = createLineDataSet(context, powerDataPoints, label, context.getResources().getColor(R.color.respiratory_rate_color));
final ValueFormatter integerFormatter = new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
return String.valueOf((int) value);
}
};
return new WorkoutChart(
"respiratory_rate",
context.getString(R.string.respiratoryrate),
ActivitySummaryEntries.GROUP_RESPIRATORY_RATE,
new LineData(dataset),
integerFormatter,
getUnitString(context, UNIT_BREATHS_PER_MIN)
);
}
public static String getUnitString(final Context context, final String unit) {
final int resId = context.getResources().getIdentifier(unit, "string", context.getPackageName());
if (resId != 0) {
@@ -278,12 +278,18 @@ object ActivitySummaryGroup {
// Recovery Heart Rate
put(ActivitySummaryEntries.GROUP_RECOVERY_HEART_RATE, listOf<String>())
// Respiratory Rate
put(
ActivitySummaryEntries.GROUP_RESPIRATORY_RATE, listOf<String>(
ActivitySummaryEntries.RESPIRATION_MIN,
ActivitySummaryEntries.RESPIRATION_MAX,
ActivitySummaryEntries.RESPIRATION_AVG,
)
)
// Other
put(
ActivitySummaryEntries.GROUP_OTHER, listOf<String>(
ActivitySummaryEntries.RESPIRATION_AVG,
ActivitySummaryEntries.RESPIRATION_MIN,
ActivitySummaryEntries.RESPIRATION_MAX,
ActivitySummaryEntries.ESTIMATED_SWEAT_LOSS,
ActivitySummaryEntries.CALORIES_BURNT,
ActivitySummaryEntries.CALORIES_ACTIVE,
@@ -277,14 +277,14 @@ public class GarminWorkoutParser implements ActivitySummaryParser {
if (session.getAvgSpo2() != null) {
summaryData.add(SPO2_AVG, session.getAvgSpo2(), UNIT_PERCENTAGE);
}
if (session.getEnhancedAvgRespirationRate() != null) {
summaryData.add(RESPIRATION_AVG, session.getEnhancedAvgRespirationRate(), UNIT_BREATHS_PER_MIN);
if (session.getEnhancedMinRespirationRate() != null) {
summaryData.add(RESPIRATION_MIN, session.getEnhancedMinRespirationRate(), UNIT_BREATHS_PER_MIN);
}
if (session.getEnhancedMaxRespirationRate() != null) {
summaryData.add(RESPIRATION_MAX, session.getEnhancedMaxRespirationRate(), UNIT_BREATHS_PER_MIN);
}
if (session.getEnhancedMinRespirationRate() != null) {
summaryData.add(RESPIRATION_MIN, session.getEnhancedMinRespirationRate(), UNIT_BREATHS_PER_MIN);
if (session.getEnhancedAvgRespirationRate() != null) {
summaryData.add(RESPIRATION_AVG, session.getEnhancedAvgRespirationRate(), UNIT_BREATHS_PER_MIN);
}
if (session.getAvgStress() != null) {
summaryData.add(STRESS_AVG, session.getAvgStress(), UNIT_NONE);
@@ -42,6 +42,7 @@ public class ActivityPoint {
private float speed = -1;
private int cadence = -1;
private int power = -1;
private float respiratoryRate = -1;
// e.g. to describe a pause during the activity
private @Nullable String description;
@@ -109,4 +110,12 @@ public class ActivityPoint {
public void setPower(final int power) {
this.power = power;
}
public float getRespiratoryRate() {
return respiratoryRate;
}
public void setRespiratoryRate(final float respiratoryRate) {
this.respiratoryRate = respiratoryRate;
}
}
@@ -238,6 +238,7 @@ public class ActivitySummaryEntries {
public static final String GROUP_ELEVATION = "Elevation";
public static final String GROUP_POWER = "workout_power";
public static final String GROUP_HEART_RATE = "heart_rate";
public static final String GROUP_RESPIRATORY_RATE = "respiratoryrate";
public static final String GROUP_OTHER = "Other";
public static final String GROUP_HEART_RATE_ZONES = "HeartRateZones";
public static final String GROUP_STROKES = "Strokes";
@@ -576,7 +576,7 @@ public class GlobalFITMessage {
new FieldDefinitionPrimitive(97, BaseType.UINT8, "cns_load", 1, 0), // %
new FieldDefinitionPrimitive(98, BaseType.UINT16, "n2_load", 1, 0), // %
new FieldDefinitionPrimitive(99, BaseType.UINT8, "respiration_rate", 1, 0), // s
new FieldDefinitionPrimitive(108, BaseType.UINT16, "enhanced_respiration_rate"),
new FieldDefinitionPrimitive(108, BaseType.UINT16, "enhanced_respiration_rate", 100, 0), // breaths / min
new FieldDefinitionPrimitive(114, BaseType.FLOAT32, "grit"),
new FieldDefinitionPrimitive(115, BaseType.FLOAT32, "flow"),
new FieldDefinitionPrimitive(116, BaseType.UINT16, "current_stress", 100, 0),
@@ -450,8 +450,8 @@ public class FitRecord extends RecordData {
}
@Nullable
public Integer getEnhancedRespirationRate() {
return (Integer) getFieldByNumber(108);
public Float getEnhancedRespirationRate() {
return (Float) getFieldByNumber(108);
}
@Nullable
@@ -887,7 +887,7 @@ public class FitRecord extends RecordData {
return this;
}
public Builder setEnhancedRespirationRate(final Integer value) {
public Builder setEnhancedRespirationRate(final Float value) {
setFieldByNumber(108, value);
return this;
}
@@ -1007,6 +1007,9 @@ public class FitRecord extends RecordData {
if (getPower() != null) {
activityPoint.setPower(getPower());
}
if (getEnhancedRespirationRate() != null) {
activityPoint.setRespiratoryRate(getEnhancedRespirationRate());
}
return activityPoint;
}
}