1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-10-06 05:07:18 +02:00

Garmin: fix major issue with field encoding/decoding

Our implementation of scale and offset was backwards: we were adding offset and then dividing by scale when decoding fields, but the publicly available protocol description dictates otherwise ( http://web.archive.org/web/20240519102659/https://developer.garmin.com/fit/protocol/#scaleoffset ):  "the binary quantity is divided by the scale factor and then the offset is subtracted".
For this reason the sign of GARMIN_TIME_EPOCH in Timestamp field definition must be flipped as well.
This commit is contained in:
Daniele Gobbetti 2024-08-16 13:57:00 +02:00
parent 404e432adf
commit f2f6536ea8
9 changed files with 30 additions and 29 deletions

View File

@ -48,11 +48,11 @@ public enum BaseType {
return identifier;
}
public Object decode(ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(ByteBuffer byteBuffer, double scale, int offset) {
return baseTypeInterface.decode(byteBuffer, scale, offset);
}
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
baseTypeInterface.encode(byteBuffer, o, scale, offset);
}

View File

@ -28,22 +28,22 @@ public class BaseTypeByte implements BaseTypeInterface {
}
@Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(final ByteBuffer byteBuffer, double scale, int offset) {
int b = unsigned ? Byte.toUnsignedInt(byteBuffer.get()) : byteBuffer.get();
if (b < min || b > max)
return null;
if (b == invalid)
return null;
return (b + offset) / scale;
return (int) (Math.round(b / scale) - offset);
}
@Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
int i = ((Number) o).intValue() * scale - offset;
int i = (int) ((((Number) o).intValue() + offset) * scale);
if (i < min || i > max) {
invalidate(byteBuffer);
return;

View File

@ -19,23 +19,23 @@ public class BaseTypeDouble implements BaseTypeInterface {
}
@Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(final ByteBuffer byteBuffer, double scale, int offset) {
double d = byteBuffer.getDouble();
if (d < min || d > max) {
return null;
}
if (Double.isNaN(d) || d == invalid)
return null;
return (d + offset) / scale;
return (d / scale) - offset;
}
@Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
double d = ((Number) o).doubleValue() * scale - offset;
double d = (((Number) o).doubleValue() + offset) * scale;
if (d < min || d > max) {
invalidate(byteBuffer);
return;

View File

@ -19,23 +19,23 @@ public class BaseTypeFloat implements BaseTypeInterface {
}
@Override
public Object decode(ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(ByteBuffer byteBuffer, double scale, int offset) {
float f = byteBuffer.getFloat();
if (f < min || f > max) {
return null;
}
if (Float.isNaN(f) || f == invalid)
return null;
return (f + offset) / scale;
return (float) (f / scale) - offset;
}
@Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
float f = ((Number) o).floatValue() * scale - offset;
float f = (float) ((((Number) o).floatValue() + offset) * scale);
if (f < min || f > max) {
invalidate(byteBuffer);
return;

View File

@ -26,22 +26,22 @@ public class BaseTypeInt implements BaseTypeInterface {
}
@Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(final ByteBuffer byteBuffer, double scale, int offset) {
long i = unsigned ? Integer.toUnsignedLong(byteBuffer.getInt()) : byteBuffer.getInt();
if (i < min || i > max)
return null;
if (i == invalid)
return null;
return ((i + offset) / scale);
return (Math.round(i / scale) - offset);
}
@Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
long l = ((Number) o).longValue() * scale - offset;
long l = (long) ((((Number) o).longValue() + offset) * scale);
if (l < min || l > max) {
invalidate(byteBuffer);
return;

View File

@ -5,9 +5,9 @@ import java.nio.ByteBuffer;
public interface BaseTypeInterface {
int getByteSize();
Object decode(ByteBuffer byteBuffer, int scale, int offset);
Object decode(ByteBuffer byteBuffer, double scale, int offset);
void encode(ByteBuffer byteBuffer, Object o, int scale, int offset);
void encode(ByteBuffer byteBuffer, Object o, double scale, int offset);
void invalidate(ByteBuffer byteBuffer);
}

View File

@ -1,5 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.baseTypes;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
@ -27,22 +28,22 @@ public class BaseTypeLong implements BaseTypeInterface {
}
@Override
public Object decode(ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(ByteBuffer byteBuffer, double scale, int offset) {
BigInteger i = unsigned ? BigInteger.valueOf(byteBuffer.getLong() & 0xFFFFFFFFFFFFFFFFL) : BigInteger.valueOf(byteBuffer.getLong());
if (!unsigned && (i.compareTo(min) < 0 || i.compareTo(max) > 0))
return null;
if (i.compareTo(BigInteger.valueOf(invalid)) == 0)
return null;
return (i.longValue() + offset) / scale;
return new BigDecimal(i).divide(BigDecimal.valueOf(scale)).subtract(BigDecimal.valueOf(offset)).toBigInteger().longValue();
}
@Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
BigInteger i = BigInteger.valueOf(((Number) o).longValue() * scale - offset);
BigInteger i = new BigDecimal(((Number) o).longValue()).multiply(BigDecimal.valueOf(scale)).add(BigDecimal.valueOf(offset)).toBigInteger();
if (!unsigned && (i.compareTo(min) < 0 || i.compareTo(max) > 0)) {
invalidate(byteBuffer);
return;

View File

@ -26,22 +26,22 @@ public class BaseTypeShort implements BaseTypeInterface {
}
@Override
public Object decode(final ByteBuffer byteBuffer, int scale, int offset) {
public Object decode(final ByteBuffer byteBuffer, double scale, int offset) {
int s = unsigned ? Short.toUnsignedInt(byteBuffer.getShort()) : byteBuffer.getShort();
if (s < min || s > max)
return null;
if (s == invalid)
return null;
return (s + offset) / scale;
return (int) Math.round(s / scale) - offset;
}
@Override
public void encode(ByteBuffer byteBuffer, Object o, int scale, int offset) {
public void encode(ByteBuffer byteBuffer, Object o, double scale, int offset) {
if (null == o) {
invalidate(byteBuffer);
return;
}
int i = ((Number) o).intValue() * scale - offset;
int i = (int) ((((Number) o).intValue() + offset) * scale);
if (i < min || i > max) {
invalidate(byteBuffer);
return;

View File

@ -7,7 +7,7 @@ import static nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.Garmin
public class FieldDefinitionTimestamp extends FieldDefinition {
public FieldDefinitionTimestamp(int localNumber, int size, BaseType baseType, String name) {
super(localNumber, size, baseType, name, 1, GARMIN_TIME_EPOCH);
super(localNumber, size, baseType, name, 1, -GARMIN_TIME_EPOCH);
}
// @Override