diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothTrisaBodyAnalyze.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothTrisaBodyAnalyze.java index 78bd03be..978eca49 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothTrisaBodyAnalyze.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/BluetoothTrisaBodyAnalyze.java @@ -33,7 +33,6 @@ import java.util.UUID; import timber.log.Timber; import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.convertJavaTimestampToDevice; -import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.getInt32; import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.parseScaleMeasurementData; /** @@ -219,7 +218,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication { Timber.e("Password data too short"); return; } - password = getInt32(data, 1); + password = Converters.fromSignedInt32Le(data, 1); if (deviceId == null) { Timber.e("Can't save password: device id not set!"); } else { @@ -247,7 +246,7 @@ public class BluetoothTrisaBodyAnalyze extends BluetoothCommunication { disconnect(true); return; } - int challenge = getInt32(data, 1); + int challenge = Converters.fromSignedInt32Le(data, 1); int response = challenge ^ password; writeCommand(DOWNLOAD_INFORMATION_RESULT_COMMAND, response); int deviceTimestamp = convertJavaTimestampToDevice(System.currentTimeMillis()); diff --git a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/lib/TrisaBodyAnalyzeLib.java b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/lib/TrisaBodyAnalyzeLib.java index 3e918464..862ef23a 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/bluetooth/lib/TrisaBodyAnalyzeLib.java +++ b/android_app/app/src/main/java/com/health/openscale/core/bluetooth/lib/TrisaBodyAnalyzeLib.java @@ -33,16 +33,6 @@ public class TrisaBodyAnalyzeLib { // Timestamp of 2010-01-01 00:00:00 UTC (or local time?) private static final long TIMESTAMP_OFFSET_SECONDS = 1262304000L; - /** - * Converts 4 little-endian bytes to a 32-bit integer, starting from {@code offset}. - * - * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code offset + 4> data.length} - */ - public static int getInt32(byte[] data, int offset) { - return (data[offset] & 0xff) | ((data[offset + 1] & 0xff) << 8) | - ((data[offset + 2] & 0xff) << 16) | ((data[offset + 3] & 0xff) << 24); - } - /** Converts 4 bytes to a floating point number, starting from {@code offset}. * *

The first three little-endian bytes form the 24-bit mantissa. The last byte contains the @@ -92,7 +82,7 @@ public class TrisaBodyAnalyzeLib { return null; } double weightKg = getBase10Float(data, 1); - int deviceTimestamp = getInt32(data, 5); + int deviceTimestamp = Converters.fromSignedInt32Le(data, 5); ScaleMeasurement measurement = new ScaleMeasurement(); measurement.setDateTime(new Date(convertDeviceTimestampToJava(deviceTimestamp))); diff --git a/android_app/app/src/main/java/com/health/openscale/core/utils/Converters.java b/android_app/app/src/main/java/com/health/openscale/core/utils/Converters.java index 8461d920..e0d69a3e 100644 --- a/android_app/app/src/main/java/com/health/openscale/core/utils/Converters.java +++ b/android_app/app/src/main/java/com/health/openscale/core/utils/Converters.java @@ -235,18 +235,26 @@ public class Converters { return kg; } - public static int fromUnsignedInt16Le(byte[] data, int offset) { - int value = (data[offset + 1] & 0xFF) << 8; + public static int fromSignedInt16Le(byte[] data, int offset) { + int value = data[offset + 1] << 8; value += data[offset] & 0xFF; return value; } - public static int fromUnsignedInt16Be(byte[] data, int offset) { - int value = (data[offset] & 0xFF) << 8; + public static int fromSignedInt16Be(byte[] data, int offset) { + int value = data[offset] << 8; value += data[offset + 1] & 0xFF; return value; } + public static int fromUnsignedInt16Le(byte[] data, int offset) { + return fromSignedInt16Le(data, offset) & 0xFFFF; + } + + public static int fromUnsignedInt16Be(byte[] data, int offset) { + return fromSignedInt16Be(data, offset) & 0xFFFF; + } + public static void toInt16Le(byte[] data, int offset, int value) { data[offset + 0] = (byte) (value & 0xFF); data[offset + 1] = (byte) ((value >> 8) & 0xFF); @@ -269,29 +277,41 @@ public class Converters { return data; } - public static int fromUnsignedInt24Le(byte[] data, int offset) { - int value = (data[offset + 2] & 0xFF) << 16; + public static int fromSignedInt24Le(byte[] data, int offset) { + int value = data[offset + 2] << 16; value += (data[offset + 1] & 0xFF) << 8; value += data[offset] & 0xFF; return value; } - public static long fromUnsignedInt32Le(byte[] data, int offset) { - long value = (long) (data[offset + 3] & 0xFF) << 24; + public static int fromUnsignedInt24Le(byte[] data, int offset) { + return fromSignedInt24Le(data, offset) & 0xFFFFFF; + } + + public static int fromSignedInt32Le(byte[] data, int offset) { + int value = data[offset + 3] << 24; value += (data[offset + 2] & 0xFF) << 16; value += (data[offset + 1] & 0xFF) << 8; value += data[offset] & 0xFF; return value; } - public static long fromUnsignedInt32Be(byte[] data, int offset) { - long value = (long) (data[offset] & 0xFF) << 24; + public static int fromSignedInt32Be(byte[] data, int offset) { + int value = data[offset] << 24; value += (data[offset + 1] & 0xFF) << 16; value += (data[offset + 2] & 0xFF) << 8; value += data[offset + 3] & 0xFF; return value; } + public static long fromUnsignedInt32Le(byte[] data, int offset) { + return (long) fromSignedInt32Le(data, offset) & 0xFFFFFFFFL; + } + + public static long fromUnsignedInt32Be(byte[] data, int offset) { + return (long) fromSignedInt32Be(data, offset) & 0xFFFFFFFFL; + } + public static void toInt32Le(byte[] data, int offset, long value) { data[offset + 3] = (byte) ((value >> 24) & 0xFF); data[offset + 2] = (byte) ((value >> 16) & 0xFF); diff --git a/android_app/app/src/test/java/com/health/openscale/ConvertersTest.java b/android_app/app/src/test/java/com/health/openscale/ConvertersTest.java index dd102e8b..379be790 100644 --- a/android_app/app/src/test/java/com/health/openscale/ConvertersTest.java +++ b/android_app/app/src/test/java/com/health/openscale/ConvertersTest.java @@ -84,18 +84,33 @@ public class ConvertersTest { } @Test - public void fromUnsignedInt16Converters() throws Exception { + public void fromInt16Converters() throws Exception { byte[] data = new byte[]{(byte) 0xfd, (byte) 0xfe, (byte) 0xfc, (byte) 0x10, (byte) 0x7f}; + assertEquals(0xfffffefd, Converters.fromSignedInt16Le(data, 0)); + assertEquals(0xfffffcfe, Converters.fromSignedInt16Le(data, 1)); + assertEquals(0x000010fc, Converters.fromSignedInt16Le(data, 2)); + assertEquals(0x00007f10, Converters.fromSignedInt16Le(data, 3)); + assertEquals(0xfefd, Converters.fromUnsignedInt16Le(data, 0)); assertEquals(0xfcfe, Converters.fromUnsignedInt16Le(data, 1)); assertEquals(0x10fc, Converters.fromUnsignedInt16Le(data, 2)); assertEquals(0x7f10, Converters.fromUnsignedInt16Le(data, 3)); + assertEquals(0xfffffdfe, Converters.fromSignedInt16Be(data, 0)); + assertEquals(0xfffffefc, Converters.fromSignedInt16Be(data, 1)); + assertEquals(0xfffffc10, Converters.fromSignedInt16Be(data, 2)); + assertEquals(0x0000107f, Converters.fromSignedInt16Be(data, 3)); + assertEquals(0xfdfe, Converters.fromUnsignedInt16Be(data, 0)); assertEquals(0xfefc, Converters.fromUnsignedInt16Be(data, 1)); assertEquals(0xfc10, Converters.fromUnsignedInt16Be(data, 2)); assertEquals(0x107f, Converters.fromUnsignedInt16Be(data, 3)); + + assertEquals(-12345, + Converters.fromSignedInt16Le(Converters.toInt16Le(-12345), 0)); + assertEquals(-12345, + Converters.fromSignedInt16Be(Converters.toInt16Be(-12345), 0)); } @Test @@ -113,31 +128,55 @@ public class ConvertersTest { } @Test - public void unsignedInt24Converters() throws Exception { + public void fromInt24Converters() throws Exception { byte[] data = new byte[]{(byte) 0xfd, (byte) 0xfe, (byte) 0xfc, (byte) 0x10, (byte) 0x7f}; + assertEquals(0xfffcfefd, Converters.fromSignedInt24Le(data, 0)); + assertEquals(0x0010fcfe, Converters.fromSignedInt24Le(data, 1)); + assertEquals(0x007f10fc, Converters.fromSignedInt24Le(data, 2)); + assertEquals(0xfcfefd, Converters.fromUnsignedInt24Le(data, 0)); assertEquals(0x10fcfe, Converters.fromUnsignedInt24Le(data, 1)); assertEquals(0x7f10fc, Converters.fromUnsignedInt24Le(data, 2)); + + assertEquals(-1234567, + Converters.fromSignedInt24Le(Converters.toInt32Le(-1234567), 0)); } @Test - public void fromUnsignedInt32Converters() throws Exception { + public void fromInt32Converters() throws Exception { byte[] data = new byte[]{(byte) 0xf1, (byte) 0xf2, (byte) 0xf3, (byte) 0x7f, (byte) 0x7e}; + assertEquals(0x7ff3f2f1, Converters.fromSignedInt32Le(data, 0)); + assertEquals(0x7e7ff3f2, Converters.fromSignedInt32Le(data, 1)); + assertEquals(0x7ff3f2f1, Converters.fromUnsignedInt32Le(data, 0)); assertEquals(0x7e7ff3f2, Converters.fromUnsignedInt32Le(data, 1)); + assertEquals(0xf1f2f37f, Converters.fromSignedInt32Be(data, 0)); + assertEquals(0xf2f37f7e, Converters.fromSignedInt32Be(data, 1)); + assertEquals(0xf1f2f37fL, Converters.fromUnsignedInt32Be(data, 0)); assertEquals(0xf2f37f7eL, Converters.fromUnsignedInt32Be(data, 1)); data = new byte[]{(byte) 0x80, (byte) 0x00, (byte) 0x01, (byte) 0xff, (byte) 0x00}; + assertEquals(0xff010080, Converters.fromSignedInt32Le(data, 0)); + assertEquals(0xff0100, Converters.fromSignedInt32Le(data, 1)); + assertEquals(0xff010080L, Converters.fromUnsignedInt32Le(data, 0)); assertEquals(0xff0100, Converters.fromUnsignedInt32Le(data, 1)); + assertEquals(0x800001ff, Converters.fromSignedInt32Be(data, 0)); + assertEquals(0x1ff00, Converters.fromSignedInt32Be(data, 1)); + assertEquals(0x800001ffL, Converters.fromUnsignedInt32Be(data, 0)); assertEquals(0x1ff00L, Converters.fromUnsignedInt32Be(data, 1)); + + assertEquals(-1234567890, + Converters.fromSignedInt32Le(Converters.toInt32Le(-1234567890), 0)); + assertEquals(-1234567890, + Converters.fromSignedInt32Be(Converters.toInt32Be(-1234567890), 0)); } @Test diff --git a/android_app/app/src/test/java/com/health/openscale/TrisaBodyAnalyzeLibTest.java b/android_app/app/src/test/java/com/health/openscale/TrisaBodyAnalyzeLibTest.java index c54d69e2..ad9cfe96 100644 --- a/android_app/app/src/test/java/com/health/openscale/TrisaBodyAnalyzeLibTest.java +++ b/android_app/app/src/test/java/com/health/openscale/TrisaBodyAnalyzeLibTest.java @@ -15,27 +15,12 @@ import java.util.GregorianCalendar; import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.convertDeviceTimestampToJava; import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.convertJavaTimestampToDevice; import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.getBase10Float; -import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.getInt32; import static com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib.parseScaleMeasurementData; import static junit.framework.Assert.assertEquals; /** Unit tests for {@link com.health.openscale.core.bluetooth.lib.TrisaBodyAnalyzeLib}.*/ public class TrisaBodyAnalyzeLibTest { - @Test - public void getInt32Tests() { - byte[] data = new byte[]{1, 2, 3, 4, 5, 6}; - assertEquals(0x04030201, getInt32(data, 0)); - assertEquals(0x05040302, getInt32(data, 1)); - assertEquals(0x06050403, getInt32(data, 2)); - - assertEquals(0xa7bdd385, getInt32(new byte[]{-123, -45, -67, -89}, 0)); - - assertThrows(IndexOutOfBoundsException.class, getInt32Runnable(data, -1)); - assertThrows(IndexOutOfBoundsException.class, getInt32Runnable(data, 5)); - assertThrows(IndexOutOfBoundsException.class, getInt32Runnable(new byte[]{1,2,3}, 0)); - } - @Test public void getBase10FloatTests() { double eps = 1e-9; // margin of error for inexact floating point comparisons @@ -117,19 +102,6 @@ public class TrisaBodyAnalyzeLibTest { assertEquals(0f, measurement.getFat()); } - /** - * Creates a {@link Runnable} that will call getInt32(). In Java 8, this can be done more - * easily with a lambda expression at the call site, but we are using Java 7. - */ - private static Runnable getInt32Runnable(final byte[] data, final int offset) { - return new Runnable() { - @Override - public void run() { - getInt32(data, offset); - } - }; - } - /** * Creates a {@link Runnable} that will call getBase10Float(). In Java 8, this can be done more * easily with a lambda expression at the call site, but we are using Java 7.