forked from upstream-mirrors/OpenTracks
Merge with a128c27963002c1d46170696e1c1045db4e1de87
This commit is contained in:
@@ -24,14 +24,16 @@ import com.google.android.maps.mytracks.R;
|
|||||||
* A collection of methods for message parsers.
|
* A collection of methods for message parsers.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
|
* @author Nico Laum
|
||||||
*/
|
*/
|
||||||
public class SensorUtils {
|
public class SensorUtils {
|
||||||
|
|
||||||
private SensorUtils() {
|
private SensorUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract one unsigned short from a big endian byte array.
|
* Extract one unsigned short from a big endian byte array.
|
||||||
|
*
|
||||||
* @param buffer the buffer to extract the short from
|
* @param buffer the buffer to extract the short from
|
||||||
* @param index the first byte to be interpreted as part of the short
|
* @param index the first byte to be interpreted as part of the short
|
||||||
* @return The unsigned short at the given index in the buffer
|
* @return The unsigned short at the given index in the buffer
|
||||||
@@ -42,6 +44,58 @@ public class SensorUtils {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract one unsigned short from a little endian byte array.
|
||||||
|
*
|
||||||
|
* @param buffer the buffer to extract the short from
|
||||||
|
* @param index the first byte to be interpreted as part of the short
|
||||||
|
* @return The unsigned short at the given index in the buffer
|
||||||
|
*/
|
||||||
|
public static int unsignedShortToIntLittleEndian(byte[] buffer, int index) {
|
||||||
|
int r = buffer[index] & 0xFF;
|
||||||
|
r |= (buffer[index + 1] & 0xFF) << 8;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns CRC8 (polynomial 0x8C) from byte array buffer[start] until
|
||||||
|
* (including) buffer[end]
|
||||||
|
*
|
||||||
|
* @param buffer the byte array of data (payload)
|
||||||
|
* @param start the position in the byte array where the payload begins
|
||||||
|
* @param end the position in the byte array where the payload ends
|
||||||
|
* @return CRC8 value
|
||||||
|
*/
|
||||||
|
public static byte getCrc8(byte[] buffer, int start, int length) {
|
||||||
|
byte crc = 0x0;
|
||||||
|
|
||||||
|
for (int i = start; i < (start + length); i++) {
|
||||||
|
crc = crc8PushByte(crc, buffer[i]);
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a CRC8 value by using the next byte passed to this method
|
||||||
|
*
|
||||||
|
* @param crc int of crc value
|
||||||
|
* @param add the next byte to add to the CRC8 calculation
|
||||||
|
*/
|
||||||
|
private static byte crc8PushByte(byte crc, byte add) {
|
||||||
|
crc = (byte) (crc ^ add);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
if ((crc & 0x1) != 0x0) {
|
||||||
|
// Using a 0xFF bit assures that 0-bits are introduced during the shift operation.
|
||||||
|
// Otherwise, implicit casts to signed int could shift in 1-bits if the signed bit is 1.
|
||||||
|
crc = (byte) (((crc & 0xFF) >> 1) ^ 0x8C);
|
||||||
|
} else {
|
||||||
|
crc = (byte) ((crc & 0xFF) >> 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
public static String getStateAsString(Sensor.SensorState state, Context c) {
|
public static String getStateAsString(Sensor.SensorState state, Context c) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case NONE:
|
case NONE:
|
||||||
|
|||||||
+24
-13
@@ -21,12 +21,16 @@ import com.google.android.apps.mytracks.Constants;
|
|||||||
import com.google.android.apps.mytracks.content.Sensor;
|
import com.google.android.apps.mytracks.content.Sensor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of a SensorData parser for Zephyr HRM.
|
* An implementation of a Sensor MessageParser for Zephyr.
|
||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class ZephyrMessageParser implements MessageParser {
|
public class ZephyrMessageParser implements MessageParser {
|
||||||
|
|
||||||
|
public static final int ZEPHYR_HXM_BYTE_STX = 0;
|
||||||
|
public static final int ZEPHYR_HXM_BYTE_CRC = 58;
|
||||||
|
public static final int ZEPHYR_HXM_BYTE_ETX = 59;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
|
public Sensor.SensorDataSet parseBuffer(byte[] buffer) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -34,20 +38,24 @@ public class ZephyrMessageParser implements MessageParser {
|
|||||||
sb.append(String.format("%02X", buffer[i]));
|
sb.append(String.format("%02X", buffer[i]));
|
||||||
}
|
}
|
||||||
Log.w(Constants.TAG, "Got zephyr data: " + sb);
|
Log.w(Constants.TAG, "Got zephyr data: " + sb);
|
||||||
// The provided units are 1/16 strides per minute.
|
|
||||||
// TODO: Fix the cadence calculation.
|
|
||||||
// int cadence = SensorUtils.unsignedShortToInt(buffer, 56);
|
|
||||||
// Heart Rate
|
// Heart Rate
|
||||||
Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder()
|
Sensor.SensorData.Builder heartrate = Sensor.SensorData.newBuilder()
|
||||||
.setValue(buffer[12] & 0xFF)
|
.setValue(buffer[12])
|
||||||
.setState(Sensor.SensorState.SENDING);
|
.setState(Sensor.SensorState.SENDING);
|
||||||
// Cadence
|
// Changes Nico Laum (Power and Cadence)
|
||||||
//.setCadence(cadence / 16)
|
Sensor.SensorData.Builder power = Sensor.SensorData.newBuilder()
|
||||||
//.build();
|
.setValue(buffer[11])
|
||||||
|
.setState(Sensor.SensorState.SENDING);
|
||||||
|
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder()
|
||||||
|
.setValue(SensorUtils.unsignedShortToIntLittleEndian(buffer, 56) / 16)
|
||||||
|
.setState(Sensor.SensorState.SENDING);
|
||||||
|
|
||||||
Sensor.SensorDataSet sds =
|
Sensor.SensorDataSet sds =
|
||||||
Sensor.SensorDataSet.newBuilder()
|
Sensor.SensorDataSet.newBuilder()
|
||||||
.setCreationTime(System.currentTimeMillis())
|
.setCreationTime(System.currentTimeMillis())
|
||||||
.setHeartRate(b)
|
.setPower(power)
|
||||||
|
.setHeartRate(heartrate)
|
||||||
|
.setCadence(cadence)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return sds;
|
return sds;
|
||||||
@@ -55,8 +63,11 @@ public class ZephyrMessageParser implements MessageParser {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(byte[] buffer) {
|
public boolean isValid(byte[] buffer) {
|
||||||
// TODO crc etc.
|
// Check STX (Start of Text), ETX (End of Text) and CRC Checksum
|
||||||
return buffer[0] == 0x02 && buffer[59] == 0x03;
|
return buffer.length > ZEPHYR_HXM_BYTE_ETX
|
||||||
|
&& buffer[ZEPHYR_HXM_BYTE_STX] == 0x02
|
||||||
|
&& buffer[ZEPHYR_HXM_BYTE_ETX] == 0x03
|
||||||
|
&& SensorUtils.getCrc8(buffer, 3, 55) == buffer[ZEPHYR_HXM_BYTE_CRC];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+17
-1
@@ -9,10 +9,17 @@ public class ZephyrMessageParserTest extends TestCase {
|
|||||||
ZephyrMessageParser parser = new ZephyrMessageParser();
|
ZephyrMessageParser parser = new ZephyrMessageParser();
|
||||||
|
|
||||||
public void testIsValid() {
|
public void testIsValid() {
|
||||||
byte[] buf = new byte[60];
|
byte[] smallBuf = new byte[59];
|
||||||
|
assertFalse(parser.isValid(smallBuf));
|
||||||
|
// A complete and valid Zephyr HxM packet
|
||||||
|
byte[] buf = { 2,38,55,26,0,49,101,80,0,49,98,100,42,113,120,-53,-24,-60,-123,-61,117,-69,42,-75,74,-78,51,-79,27,-83,28,-88,28,-93,29,-98,25,-103,26,-108,26,-113,59,-118,0,0,0,0,0,0,-22,3,125,1,48,0,96,4,30,0 };
|
||||||
|
// Make buffer invalid
|
||||||
|
buf[0] = buf[58] = buf[59] = 0;
|
||||||
assertFalse(parser.isValid(buf));
|
assertFalse(parser.isValid(buf));
|
||||||
buf[0] = 0x02;
|
buf[0] = 0x02;
|
||||||
assertFalse(parser.isValid(buf));
|
assertFalse(parser.isValid(buf));
|
||||||
|
buf[58] = 0x1E;
|
||||||
|
assertFalse(parser.isValid(buf));
|
||||||
buf[59] = 0x03;
|
buf[59] = 0x03;
|
||||||
assertTrue(parser.isValid(buf));
|
assertTrue(parser.isValid(buf));
|
||||||
}
|
}
|
||||||
@@ -20,10 +27,19 @@ public class ZephyrMessageParserTest extends TestCase {
|
|||||||
public void testParseBuffer() {
|
public void testParseBuffer() {
|
||||||
byte[] buf = new byte[60];
|
byte[] buf = new byte[60];
|
||||||
buf[12] = 50;
|
buf[12] = 50;
|
||||||
|
buf[11] = 51;
|
||||||
|
buf[56] = 32;
|
||||||
|
buf[57] = 0;
|
||||||
Sensor.SensorDataSet sds = parser.parseBuffer(buf);
|
Sensor.SensorDataSet sds = parser.parseBuffer(buf);
|
||||||
assertTrue(sds.hasHeartRate());
|
assertTrue(sds.hasHeartRate());
|
||||||
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
|
assertTrue(sds.getHeartRate().getState() == Sensor.SensorState.SENDING);
|
||||||
assertEquals(50, sds.getHeartRate().getValue());
|
assertEquals(50, sds.getHeartRate().getValue());
|
||||||
|
assertTrue(sds.hasPower());
|
||||||
|
assertTrue(sds.getPower().getState() == Sensor.SensorState.SENDING);
|
||||||
|
assertEquals(51, sds.getPower().getValue());
|
||||||
|
assertTrue(sds.hasCadence());
|
||||||
|
assertTrue(sds.getCadence().getState() == Sensor.SensorState.SENDING);
|
||||||
|
assertEquals(2, sds.getCadence().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFindNextAlignment() {
|
public void testFindNextAlignment() {
|
||||||
|
|||||||
Reference in New Issue
Block a user