1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-30 22:12:55 +01:00

Garmin: fix basetype handling and truncation

Numeric BaseTypes now return fractional part if a scale is set, the return type for numeric values is unboxed from Number class so that existing comparisons and checks keep working, but values are not truncated at nearest integer value.

The codegen class has been updated and some of the messages are re-generated with the new code, with the exception of Hrv* messages, since updating those needs updating the related sample types.

Test cases expected values have been adjusted to keep into account the fractional part of some fields.

Garmin: Fix HRV value and summary types
This commit is contained in:
Daniele Gobbetti 2024-08-18 15:08:26 +02:00
parent 51a0745b06
commit fd1e81ff66
15 changed files with 112 additions and 54 deletions

View File

@ -206,12 +206,24 @@ public class FitImporter {
LOG.trace("HRV summary at {}: {}", ts, record); LOG.trace("HRV summary at {}: {}", ts, record);
final GarminHrvSummarySample sample = new GarminHrvSummarySample(); final GarminHrvSummarySample sample = new GarminHrvSummarySample();
sample.setTimestamp(ts * 1000L); sample.setTimestamp(ts * 1000L);
sample.setWeeklyAverage(hrvSummary.getWeeklyAverage()); if (hrvSummary.getWeeklyAverage() != null) {
sample.setLastNightAverage(hrvSummary.getLastNightAverage()); sample.setWeeklyAverage(Math.round(hrvSummary.getWeeklyAverage()));
sample.setLastNight5MinHigh(hrvSummary.getLastNight5MinHigh()); }
sample.setBaselineLowUpper(hrvSummary.getBaselineLowUpper()); if (hrvSummary.getLastNightAverage() != null) {
sample.setBaselineBalancedLower(hrvSummary.getBaselineBalancedLower()); sample.setLastNightAverage(Math.round(hrvSummary.getLastNightAverage()));
sample.setBaselineBalancedUpper(hrvSummary.getBaselineBalancedUpper()); }
if (hrvSummary.getLastNight5MinHigh() != null) {
sample.setLastNight5MinHigh(Math.round(hrvSummary.getLastNight5MinHigh()));
}
if (hrvSummary.getBaselineLowUpper() != null) {
sample.setBaselineLowUpper(Math.round(hrvSummary.getBaselineLowUpper()));
}
if (hrvSummary.getBaselineBalancedLower() != null) {
sample.setBaselineBalancedLower(Math.round(hrvSummary.getBaselineBalancedLower()));
}
if (hrvSummary.getBaselineBalancedUpper() != null) {
sample.setBaselineBalancedUpper(Math.round(hrvSummary.getBaselineBalancedUpper()));
}
final FieldDefinitionHrvStatus.HrvStatus status = hrvSummary.getStatus(); final FieldDefinitionHrvStatus.HrvStatus status = hrvSummary.getStatus();
if (status != null) { if (status != null) {
sample.setStatusNum(status.getId()); sample.setStatusNum(status.getId());
@ -226,7 +238,7 @@ public class FitImporter {
LOG.trace("HRV value at {}: {}", ts, hrvValue.getValue()); LOG.trace("HRV value at {}: {}", ts, hrvValue.getValue());
final GarminHrvValueSample sample = new GarminHrvValueSample(); final GarminHrvValueSample sample = new GarminHrvValueSample();
sample.setTimestamp(ts * 1000L); sample.setTimestamp(ts * 1000L);
sample.setValue(hrvValue.getValue()); sample.setValue(Math.round(hrvValue.getValue()));
hrvValueSamples.add(sample); hrvValueSamples.add(sample);
} else { } else {
LOG.trace("Unknown record: {}", record); LOG.trace("Unknown record: {}", record);

View File

@ -49,7 +49,45 @@ public enum BaseType {
} }
public Object decode(ByteBuffer byteBuffer, double scale, int offset) { public Object decode(ByteBuffer byteBuffer, double scale, int offset) {
return baseTypeInterface.decode(byteBuffer, scale, offset); Object raw = baseTypeInterface.decode(byteBuffer, scale, offset);
if (raw == null)
return null;
//the following returns STRING basetype but also all specialized FieldDefinition classes
if (!Number.class.isAssignableFrom(raw.getClass()))
return raw;
switch (this) {
case ENUM:
case SINT8:
case UINT8:
case SINT16:
case UINT16:
case UINT8Z:
case UINT16Z:
case BASE_TYPE_BYTE:
if (scale != 1) {
return ((Number) raw).floatValue();
} else {
return ((Number) raw).intValue();
}
case SINT32:
case UINT32:
case UINT32Z:
case SINT64:
case UINT64:
case UINT64Z:
if (scale != 1) {
return ((Number) raw).doubleValue();
} else {
return ((Number) raw).longValue();
}
case FLOAT32:
return ((Number) raw).floatValue();
case FLOAT64:
return ((Number) raw).doubleValue();
}
return raw;
} }
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) { public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {

View File

@ -34,7 +34,7 @@ public class BaseTypeByte implements BaseTypeInterface {
return null; return null;
if (b == invalid) if (b == invalid)
return null; return null;
return (int) (Math.round(b / scale) - offset); return (b / scale) - offset;
} }
@Override @Override

View File

@ -26,7 +26,7 @@ public class BaseTypeFloat implements BaseTypeInterface {
} }
if (Float.isNaN(f) || f == invalid) if (Float.isNaN(f) || f == invalid)
return null; return null;
return (float) (f / scale) - offset; return (f / scale) - offset;
} }
@Override @Override

View File

@ -32,7 +32,7 @@ public class BaseTypeInt implements BaseTypeInterface {
return null; return null;
if (i == invalid) if (i == invalid)
return null; return null;
return (Math.round(i / scale) - offset); return (i / scale) - offset;
} }
@Override @Override

View File

@ -32,7 +32,7 @@ public class BaseTypeShort implements BaseTypeInterface {
return null; return null;
if (s == invalid) if (s == invalid)
return null; return null;
return (int) Math.round(s / scale) - offset; return (s / scale) - offset;
} }
@Override @Override

View File

@ -268,14 +268,22 @@ public class FitCodeGen {
case UINT8Z: case UINT8Z:
case UINT16Z: case UINT16Z:
case BASE_TYPE_BYTE: case BASE_TYPE_BYTE:
return Integer.class; if (primitive.getScale() != 1) {
return Float.class;
} else {
return Integer.class;
}
case SINT32: case SINT32:
case UINT32: case UINT32:
case UINT32Z: case UINT32Z:
case SINT64: case SINT64:
case UINT64: case UINT64:
case UINT64Z: case UINT64Z:
return Long.class; if (primitive.getScale() != 1) {
return Double.class;
} else {
return Long.class;
}
case STRING: case STRING:
return String.class; return String.class;
case FLOAT32: case FLOAT32:

View File

@ -22,33 +22,33 @@ public class FitHrvSummary extends RecordData {
} }
@Nullable @Nullable
public Integer getWeeklyAverage() { public Float getWeeklyAverage() {
return (Integer) getFieldByNumber(0); return (Float) getFieldByNumber(0);
} }
@Nullable @Nullable
public Integer getLastNightAverage() { public Float getLastNightAverage() {
return (Integer) getFieldByNumber(1); return (Float) getFieldByNumber(1);
} }
@Nullable @Nullable
public Integer getLastNight5MinHigh() { public Float getLastNight5MinHigh() {
return (Integer) getFieldByNumber(2); return (Float) getFieldByNumber(2);
} }
@Nullable @Nullable
public Integer getBaselineLowUpper() { public Float getBaselineLowUpper() {
return (Integer) getFieldByNumber(3); return (Float) getFieldByNumber(3);
} }
@Nullable @Nullable
public Integer getBaselineBalancedLower() { public Float getBaselineBalancedLower() {
return (Integer) getFieldByNumber(4); return (Float) getFieldByNumber(4);
} }
@Nullable @Nullable
public Integer getBaselineBalancedUpper() { public Float getBaselineBalancedUpper() {
return (Integer) getFieldByNumber(5); return (Float) getFieldByNumber(5);
} }
@Nullable @Nullable

View File

@ -21,8 +21,8 @@ public class FitHrvValue extends RecordData {
} }
@Nullable @Nullable
public Integer getValue() { public Float getValue() {
return (Integer) getFieldByNumber(0); return (Float) getFieldByNumber(0);
} }
@Nullable @Nullable

View File

@ -41,18 +41,18 @@ public class FitLap extends RecordData {
} }
@Nullable @Nullable
public Long getTotalElapsedTime() { public Double getTotalElapsedTime() {
return (Long) getFieldByNumber(7); return (Double) getFieldByNumber(7);
} }
@Nullable @Nullable
public Long getTotalTimerTime() { public Double getTotalTimerTime() {
return (Long) getFieldByNumber(8); return (Double) getFieldByNumber(8);
} }
@Nullable @Nullable
public Long getTotalDistance() { public Double getTotalDistance() {
return (Long) getFieldByNumber(9); return (Double) getFieldByNumber(9);
} }
@Nullable @Nullable

View File

@ -36,8 +36,8 @@ public class FitRecord extends RecordData {
} }
@Nullable @Nullable
public Integer getAltitude() { public Float getAltitude() {
return (Integer) getFieldByNumber(2); return (Float) getFieldByNumber(2);
} }
@Nullable @Nullable
@ -46,13 +46,13 @@ public class FitRecord extends RecordData {
} }
@Nullable @Nullable
public Long getDistance() { public Double getDistance() {
return (Long) getFieldByNumber(5); return (Double) getFieldByNumber(5);
} }
@Nullable @Nullable
public Integer getSpeed() { public Float getSpeed() {
return (Integer) getFieldByNumber(6); return (Float) getFieldByNumber(6);
} }
@Nullable @Nullable
@ -61,8 +61,8 @@ public class FitRecord extends RecordData {
} }
@Nullable @Nullable
public Long getEnhancedAltitude() { public Double getEnhancedAltitude() {
return (Long) getFieldByNumber(78); return (Double) getFieldByNumber(78);
} }
@Nullable @Nullable

View File

@ -36,13 +36,13 @@ public class FitSession extends RecordData {
} }
@Nullable @Nullable
public Long getStartLatitude() { public Double getStartLatitude() {
return (Long) getFieldByNumber(3); return (Double) getFieldByNumber(3);
} }
@Nullable @Nullable
public Long getStartLongitude() { public Double getStartLongitude() {
return (Long) getFieldByNumber(4); return (Double) getFieldByNumber(4);
} }
@Nullable @Nullable

View File

@ -43,8 +43,8 @@ public class FitUserProfile extends RecordData {
} }
@Nullable @Nullable
public Integer getWeight() { public Float getWeight() {
return (Integer) getFieldByNumber(4); return (Float) getFieldByNumber(4);
} }
@Nullable @Nullable

View File

@ -45,8 +45,8 @@ public class FitWeather extends RecordData {
} }
@Nullable @Nullable
public Integer getWindSpeed() { public Float getWindSpeed() {
return (Integer) getFieldByNumber(4); return (Float) getFieldByNumber(4);
} }
@Nullable @Nullable

View File

@ -336,7 +336,7 @@ public class GarminSupportTest extends TestBase {
"FitFileId{serial_number=3889965805, manufacturer=1, product=1561, type=SETTINGS}, " + "FitFileId{serial_number=3889965805, manufacturer=1, product=1561, type=SETTINGS}, " +
"FitFileCreator{software_version=340}, " + "FitFileCreator{software_version=340}, " +
"FitDeviceSettings{utc_offset=0, time_offset=0, active_time_zone=0, unknown_3(ENUM/1)=0, time_mode=0, time_zone_offset=0, unknown_10(ENUM/1)=3, unknown_11(ENUM/1)=0, backlight_mode=2, unknown_13(UINT8/1)=0, unknown_14(UINT8/1)=0, unknown_15(UINT8/1)=50, unknown_21(ENUM/1)=1, unknown_22(ENUM/1)=0, unknown_26(ENUM/1)=254, unknown_27(ENUM/1)=0, unknown_29(ENUM/1)=0, unknown_52(ENUM/1)=0, unknown_53(ENUM/1)=1}, " + "FitDeviceSettings{utc_offset=0, time_offset=0, active_time_zone=0, unknown_3(ENUM/1)=0, time_mode=0, time_zone_offset=0, unknown_10(ENUM/1)=3, unknown_11(ENUM/1)=0, backlight_mode=2, unknown_13(UINT8/1)=0, unknown_14(UINT8/1)=0, unknown_15(UINT8/1)=50, unknown_21(ENUM/1)=1, unknown_22(ENUM/1)=0, unknown_26(ENUM/1)=254, unknown_27(ENUM/1)=0, unknown_29(ENUM/1)=0, unknown_52(ENUM/1)=0, unknown_53(ENUM/1)=1}, " +
"FitUserProfile{friendly_name=edge510, weight=78, gender=1, age=41, height=183, language=english, elev_setting=metric, weight_setting=metric, resting_heart_rate=60, default_max_biking_heart_rate=185, default_max_heart_rate=185, hr_setting=1, speed_setting=metric, dist_setting=metric, power_setting=1, activity_class=168, position_setting=2, temperature_setting=metric}, " + "FitUserProfile{friendly_name=edge510, weight=78.0, gender=1, age=41, height=183, language=english, elev_setting=metric, weight_setting=metric, resting_heart_rate=60, default_max_biking_heart_rate=185, default_max_heart_rate=185, hr_setting=1, speed_setting=metric, dist_setting=metric, power_setting=1, activity_class=168, position_setting=2, temperature_setting=metric}, " +
"RecordData{UNK_4, unknown_254(UINT16/2)=0, unknown_1(UINT16Z/2)=50008, unknown_0(UINT8/1)=1, unknown_3(UINT8Z/1)=1}, " + "RecordData{UNK_4, unknown_254(UINT16/2)=0, unknown_1(UINT16Z/2)=50008, unknown_0(UINT8/1)=1, unknown_3(UINT8Z/1)=1}, " +
"RecordData{UNK_6, unknown_0(STRING/4)=EVO, unknown_3(UINT32/4)=45719172, unknown_39(UINT8Z/4)=[39,53,,], unknown_41(UINT8Z/12)=[23,21,19,18,17,16,15,14,13,12,11,], unknown_254(UINT16/2)=0, unknown_7(UINT16Z/2)=47617, unknown_8(UINT16/2)=2096, unknown_9(UINT16/2)=0, unknown_10(UINT16/2)=80, unknown_11(UINT16/2)=500, unknown_12(UINT8/1)=1, unknown_13(UINT8/1)=1, unknown_14(UINT8/1)=0, unknown_15(UINT8/1)=0, unknown_16(UINT8/1)=0, unknown_17(UINT8/1)=0, unknown_18(UINT8/1)=1, unknown_19(UINT8/1)=254, unknown_20(UINT8/1)=1, unknown_24(UINT8Z/1)=5, unknown_35(UINT8/3)=[0,50,], unknown_36(ENUM/1)=4, unknown_38(UINT8Z/1)=2, unknown_40(UINT8Z/1)=11, unknown_44(ENUM/1)=0}, " + "RecordData{UNK_6, unknown_0(STRING/4)=EVO, unknown_3(UINT32/4)=45719172, unknown_39(UINT8Z/4)=[39,53,,], unknown_41(UINT8Z/12)=[23,21,19,18,17,16,15,14,13,12,11,], unknown_254(UINT16/2)=0, unknown_7(UINT16Z/2)=47617, unknown_8(UINT16/2)=2096, unknown_9(UINT16/2)=0, unknown_10(UINT16/2)=80, unknown_11(UINT16/2)=500, unknown_12(UINT8/1)=1, unknown_13(UINT8/1)=1, unknown_14(UINT8/1)=0, unknown_15(UINT8/1)=0, unknown_16(UINT8/1)=0, unknown_17(UINT8/1)=0, unknown_18(UINT8/1)=1, unknown_19(UINT8/1)=254, unknown_20(UINT8/1)=1, unknown_24(UINT8Z/1)=5, unknown_35(UINT8/3)=[0,50,], unknown_36(ENUM/1)=4, unknown_38(UINT8Z/1)=2, unknown_40(UINT8Z/1)=11, unknown_44(ENUM/1)=0}, " +
"RecordData{UNK_6, unknown_0(STRING/5)=P2SL, unknown_3(UINT32/4)=0, unknown_39(UINT8Z/4)=[39,53,,], unknown_41(UINT8Z/12)=[23,21,19,18,17,16,15,14,13,12,11,], unknown_254(UINT16/2)=1, unknown_7(UINT16Z/2)=28209, unknown_8(UINT16/2)=2096, unknown_9(UINT16/2)=0, unknown_10(UINT16/2)=90, unknown_11(UINT16/2)=500, unknown_12(UINT8/1)=1, unknown_13(UINT8/1)=1, unknown_14(UINT8/1)=0, unknown_15(UINT8/1)=0, unknown_16(UINT8/1)=0, unknown_17(UINT8/1)=0, unknown_18(UINT8/1)=1, unknown_19(UINT8/1)=254, unknown_20(UINT8/1)=1, unknown_24(UINT8Z/1)=5, unknown_35(UINT8/3)=[0,118,190], unknown_36(ENUM/1)=4, unknown_38(UINT8Z/1)=2, unknown_40(UINT8Z/1)=11, unknown_44(ENUM/1)=0}, " + "RecordData{UNK_6, unknown_0(STRING/5)=P2SL, unknown_3(UINT32/4)=0, unknown_39(UINT8Z/4)=[39,53,,], unknown_41(UINT8Z/12)=[23,21,19,18,17,16,15,14,13,12,11,], unknown_254(UINT16/2)=1, unknown_7(UINT16Z/2)=28209, unknown_8(UINT16/2)=2096, unknown_9(UINT16/2)=0, unknown_10(UINT16/2)=90, unknown_11(UINT16/2)=500, unknown_12(UINT8/1)=1, unknown_13(UINT8/1)=1, unknown_14(UINT8/1)=0, unknown_15(UINT8/1)=0, unknown_16(UINT8/1)=0, unknown_17(UINT8/1)=0, unknown_18(UINT8/1)=1, unknown_19(UINT8/1)=254, unknown_20(UINT8/1)=1, unknown_24(UINT8Z/1)=5, unknown_35(UINT8/3)=[0,118,190], unknown_36(ENUM/1)=4, unknown_38(UINT8Z/1)=2, unknown_40(UINT8Z/1)=11, unknown_44(ENUM/1)=0}, " +
@ -364,9 +364,9 @@ public class GarminSupportTest extends TestBase {
"FitFileId{manufacturer=15, type=ACTIVITY, product=9001, serial_number=1701}, " + "FitFileId{manufacturer=15, type=ACTIVITY, product=9001, serial_number=1701}, " +
"FitDeveloperData{application_id=[1,1,2,3,5,8,13,21,34,55,89,144,233,121,98,219], developer_data_index=0}, " + "FitDeveloperData{application_id=[1,1,2,3,5,8,13,21,34,55,89,144,233,121,98,219], developer_data_index=0}, " +
"FitFieldDescription{developer_data_index=0, field_definition_number=0, fit_base_type_id=1, field_name=doughnuts_earned, units=doughnuts}, " + "FitFieldDescription{developer_data_index=0, field_definition_number=0, fit_base_type_id=1, field_name=doughnuts_earned, units=doughnuts}, " +
"FitRecord{heart_rate=140, unknown_4(UINT8/1)=88, distance=510, speed=47, doughnuts_earned=1}, " + "FitRecord{heart_rate=140, unknown_4(UINT8/1)=88, distance=510.0, speed=47.488, doughnuts_earned=1}, " +
"FitRecord{heart_rate=143, unknown_4(UINT8/1)=90, distance=2080, speed=36, doughnuts_earned=2}, " + "FitRecord{heart_rate=143, unknown_4(UINT8/1)=90, distance=2080.0, speed=36.416, doughnuts_earned=2}, " +
"FitRecord{heart_rate=144, unknown_4(UINT8/1)=92, distance=3710, speed=35, doughnuts_earned=3}" + "FitRecord{heart_rate=144, unknown_4(UINT8/1)=92, distance=3710.0, speed=35.344, doughnuts_earned=3}" +
"]"; "]";
FitFile fitFile = FitFile.parseIncoming(fileContents); FitFile fitFile = FitFile.parseIncoming(fileContents);