Garmin: Map ECG fields

This commit is contained in:
José Rebelo
2025-07-27 15:05:42 +01:00
parent 2f971645f0
commit 4536a0f42a
6 changed files with 262 additions and 2 deletions
@@ -489,6 +489,28 @@ public class GlobalFITMessage {
new FieldDefinitionPrimitive(253, BaseType.UINT32, "timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP)
));
public static GlobalFITMessage ECG_SUMMARY = new GlobalFITMessage(336, "ECG_SUMMARY", Arrays.asList(
new FieldDefinitionPrimitive(0, BaseType.UINT16, "unknown_0"), // 10103
new FieldDefinitionPrimitive(1, BaseType.ENUM, "unknown_1"), // 3
new FieldDefinitionPrimitive(2, BaseType.FLOAT32, "unknown_2"), // 512
new FieldDefinitionPrimitive(3, BaseType.FLOAT32, "unknown_3"), // 128
new FieldDefinitionPrimitive(4, BaseType.UINT32, "ecg_timestamp", FieldDefinitionFactory.FIELD.TIMESTAMP),
new FieldDefinitionPrimitive(5, BaseType.UINT32, "local_timestamp"), // garmin timestamp, but in user timezone
new FieldDefinitionPrimitive(6, BaseType.ENUM, "unknown_6"), // 1
new FieldDefinitionPrimitive(7, BaseType.FLOAT32, "average_heart_rate"), // bpm
new FieldDefinitionPrimitive(10, BaseType.STRING, "unknown_10"), // ?
new FieldDefinitionPrimitive(11, BaseType.UINT16, "unknown_11"), // 30
new FieldDefinitionPrimitive(12, BaseType.UINT8, "unknown_12") // 39
));
public static GlobalFITMessage ECG_RAW_SAMPLE = new GlobalFITMessage(337, "ECG_RAW_SAMPLE", Arrays.asList(
new FieldDefinitionPrimitive(0, BaseType.FLOAT32, "value")
));
public static GlobalFITMessage ECG_SMOOTH_SAMPLE = new GlobalFITMessage(338, "ECG_SMOOTH_SAMPLE", Arrays.asList(
new FieldDefinitionPrimitive(0, BaseType.FLOAT32, "value")
));
public static GlobalFITMessage SLEEP_STATS = new GlobalFITMessage(346, "SLEEP_STATS", Arrays.asList(
new FieldDefinitionPrimitive(0, BaseType.UINT8, "combined_awake_score"),
new FieldDefinitionPrimitive(1, BaseType.UINT8, "awake_time_score"),
@@ -594,6 +616,9 @@ public class GlobalFITMessage {
put(268, DIVE_SUMMARY);
put(275, SLEEP_STAGE);
put(297, RESPIRATION_RATE);
put(336, ECG_SUMMARY);
put(337, ECG_RAW_SAMPLE);
put(338, ECG_SMOOTH_SAMPLE);
put(346, SLEEP_STATS);
put(370, HRV_SUMMARY);
put(371, HRV_VALUE);
@@ -22,6 +22,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.FileType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.GlobalFITMessage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
@@ -59,7 +60,6 @@ public class FitCodeGen {
sbFactory.append("package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;\n");
sbFactory.append("\n");
sbFactory.append("import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;\n");
sbFactory.append("import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;\n");
sbFactory.append("import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;\n");
sbFactory.append("import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordHeader;\n");
@@ -100,6 +100,7 @@ public class FitCodeGen {
final List<String> imports = new ArrayList<>();
imports.add(Nullable.class.getCanonicalName());
imports.add(FitRecordDataBuilder.class.getCanonicalName());
imports.add(RecordData.class.getCanonicalName());
imports.add(RecordDefinition.class.getCanonicalName());
imports.add(RecordHeader.class.getCanonicalName());
@@ -0,0 +1,44 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
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 FitEcgRawSample extends RecordData {
public FitEcgRawSample(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
super(recordDefinition, recordHeader);
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
if (globalNumber != 337) {
throw new IllegalArgumentException("FitEcgRawSample expects global messages of " + 337 + ", got " + globalNumber);
}
}
@Nullable
public Float getValue() {
return (Float) getFieldByNumber(0);
}
public static class Builder extends FitRecordDataBuilder {
public Builder() {
super(337);
}
public Builder setValue(final Float value) {
setFieldByNumber(0, value);
return this;
}
@Override
public FitEcgRawSample build() {
return (FitEcgRawSample) super.build();
}
}
}
@@ -0,0 +1,44 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
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 FitEcgSmoothSample extends RecordData {
public FitEcgSmoothSample(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
super(recordDefinition, recordHeader);
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
if (globalNumber != 338) {
throw new IllegalArgumentException("FitEcgSmoothSample expects global messages of " + 338 + ", got " + globalNumber);
}
}
@Nullable
public Float getValue() {
return (Float) getFieldByNumber(0);
}
public static class Builder extends FitRecordDataBuilder {
public Builder() {
super(338);
}
public Builder setValue(final Float value) {
setFieldByNumber(0, value);
return this;
}
@Override
public FitEcgSmoothSample build() {
return (FitEcgSmoothSample) super.build();
}
}
}
@@ -0,0 +1,144 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
import androidx.annotation.Nullable;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
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 FitEcgSummary extends RecordData {
public FitEcgSummary(final RecordDefinition recordDefinition, final RecordHeader recordHeader) {
super(recordDefinition, recordHeader);
final int globalNumber = recordDefinition.getGlobalFITMessage().getNumber();
if (globalNumber != 336) {
throw new IllegalArgumentException("FitEcgSummary expects global messages of " + 336 + ", got " + globalNumber);
}
}
@Nullable
public Integer getUnknown0() {
return (Integer) getFieldByNumber(0);
}
@Nullable
public Integer getUnknown1() {
return (Integer) getFieldByNumber(1);
}
@Nullable
public Float getUnknown2() {
return (Float) getFieldByNumber(2);
}
@Nullable
public Float getUnknown3() {
return (Float) getFieldByNumber(3);
}
@Nullable
public Long getEcgTimestamp() {
return (Long) getFieldByNumber(4);
}
@Nullable
public Long getLocalTimestamp() {
return (Long) getFieldByNumber(5);
}
@Nullable
public Integer getUnknown6() {
return (Integer) getFieldByNumber(6);
}
@Nullable
public Float getAverageHeartRate() {
return (Float) getFieldByNumber(7);
}
@Nullable
public String getUnknown10() {
return (String) getFieldByNumber(10);
}
@Nullable
public Integer getUnknown11() {
return (Integer) getFieldByNumber(11);
}
@Nullable
public Integer getUnknown12() {
return (Integer) getFieldByNumber(12);
}
public static class Builder extends FitRecordDataBuilder {
public Builder() {
super(336);
}
public Builder setUnknown0(final Integer value) {
setFieldByNumber(0, value);
return this;
}
public Builder setUnknown1(final Integer value) {
setFieldByNumber(1, value);
return this;
}
public Builder setUnknown2(final Float value) {
setFieldByNumber(2, value);
return this;
}
public Builder setUnknown3(final Float value) {
setFieldByNumber(3, value);
return this;
}
public Builder setEcgTimestamp(final Long value) {
setFieldByNumber(4, value);
return this;
}
public Builder setLocalTimestamp(final Long value) {
setFieldByNumber(5, value);
return this;
}
public Builder setUnknown6(final Integer value) {
setFieldByNumber(6, value);
return this;
}
public Builder setAverageHeartRate(final Float value) {
setFieldByNumber(7, value);
return this;
}
public Builder setUnknown10(final String value) {
setFieldByNumber(10, value);
return this;
}
public Builder setUnknown11(final Integer value) {
setFieldByNumber(11, value);
return this;
}
public Builder setUnknown12(final Integer value) {
setFieldByNumber(12, value);
return this;
}
@Override
public FitEcgSummary build() {
return (FitEcgSummary) super.build();
}
}
}
@@ -1,6 +1,5 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitRecordDataBuilder;
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;
@@ -54,6 +53,9 @@ public class FitRecordDataFactory {
case 274 -> new FitSleepDataRaw(recordDefinition, recordHeader);
case 275 -> new FitSleepStage(recordDefinition, recordHeader);
case 297 -> new FitRespirationRate(recordDefinition, recordHeader);
case 336 -> new FitEcgSummary(recordDefinition, recordHeader);
case 337 -> new FitEcgRawSample(recordDefinition, recordHeader);
case 338 -> new FitEcgSmoothSample(recordDefinition, recordHeader);
case 346 -> new FitSleepStats(recordDefinition, recordHeader);
case 370 -> new FitHrvSummary(recordDefinition, recordHeader);
case 371 -> new FitHrvValue(recordDefinition, recordHeader);