From b0afea35ee90d02151bd573646ca4ea9b56c3b1e Mon Sep 17 00:00:00 2001 From: ml1050 Date: Sat, 21 Jan 2012 21:35:58 +0100 Subject: [PATCH] added more comments + minor refactoring --- MyTracks/res/values/keys.xml | 2 +- .../sensors/ant/AntDirectSensorManager.java | 18 +-- .../services/sensors/ant/AntSensorBase.java | 6 +- .../sensors/ant/AntSensorDataCollector.java | 13 +- .../services/sensors/ant/CadenceSensor.java | 10 +- .../sensors/ant/CadenceSpeedSensor.java | 10 +- .../services/sensors/ant/HeartRateSensor.java | 8 +- .../sensors/ant/SensorDataProcessor.java | 147 ++++++++++++------ 8 files changed, 138 insertions(+), 76 deletions(-) diff --git a/MyTracks/res/values/keys.xml b/MyTracks/res/values/keys.xml index 9d0a6c603..960e631e9 100644 --- a/MyTracks/res/values/keys.xml +++ b/MyTracks/res/values/keys.xml @@ -19,7 +19,7 @@ limitations under the License. allowAccess announcementFrequency antCadenceSensorId - antCadSpdSensorId + antCadenceSpeedSensorId antHeartRateSensorId antSRMBridgeSensorId autoResumeTrackTimeout diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java index c2e32f92e..a16883f98 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java @@ -200,13 +200,13 @@ public class AntDirectSensorManager extends AntSensorManager Log.i(TAG, "setup channel=" + channel + " deviceType=" + sensor.getDeviceType()); return setupAntSensorChannel(sensor.getNetworkNumber(), - channel, - sensor.getDeviceNumber(), - sensor.getDeviceType(), - (byte) 0x01, - sensor.getChannelPeriod(), - sensor.getFrequency(), - (byte) 0); + channel, + sensor.getDeviceNumber(), + sensor.getDeviceType(), + (byte) 0x01, + sensor.getChannelPeriod(), + sensor.getFrequency(), + (byte) 0); } private void sendSensorData(byte index, int value) { @@ -242,11 +242,11 @@ public class AntDirectSensorManager extends AntSensorManager sensorData = b.setCreationTime(now).build(); } - public void sendCadence(int value) { + public void setCadence(int value) { sendSensorData(CADENCE_DATA_INDEX, value); } - public void sendHeartRate(int value) { + public void setHeartRate(int value) { sendSensorData(HEART_RATE_DATA_INDEX, value); } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorBase.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorBase.java index 4212eed72..6e2dfb3f1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorBase.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorBase.java @@ -32,9 +32,9 @@ public abstract class AntSensorBase { public static final byte NETWORK_NUMBER = 1; public static final byte RF_FREQUENCY = 57; - protected short deviceNumber; - private byte deviceType; - private short channelPeriod; + private short deviceNumber; + private final byte deviceType; + private final short channelPeriod; AntSensorBase(short deviceNumber, byte deviceType, String deviceTypeString, short channelPeriod) { diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorDataCollector.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorDataCollector.java index 88990a65a..3efbaa696 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorDataCollector.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSensorDataCollector.java @@ -17,14 +17,19 @@ package com.google.android.apps.mytracks.services.sensors.ant; /** - * Interface for collecting data from the sensor classes - * + * Interface for collecting data from the sensors. * @author Laszlo Molnar */ public interface AntSensorDataCollector { - public void sendCadence(int value); + /** + * Sets the current cadence to the value specified. + */ + void setCadence(int value); - public void sendHeartRate(int value); + /** + * Sets the current heart rate to the value specified. + */ + void setHeartRate(int value); } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSensor.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSensor.java index 1cc557a78..99a2ea0e1 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSensor.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSensor.java @@ -17,7 +17,7 @@ package com.google.android.apps.mytracks.services.sensors.ant; /** - * Cadence sensor. + * Ant+ cadence sensor. * * @author Laszlo Molnar */ @@ -29,20 +29,20 @@ public class CadenceSensor extends AntSensorBase { public static final byte CADENCE_DEVICE_TYPE = 122; public static final short CADENCE_CHANNEL_PERIOD = 8102; - SensorDataProcessor cadence = new SensorDataProcessor(); + SensorDataProcessor dataProcessor = new SensorDataProcessor(); CadenceSensor(short devNum) { - super(devNum, CADENCE_DEVICE_TYPE, - "cadence sensor", CADENCE_CHANNEL_PERIOD); + super(devNum, CADENCE_DEVICE_TYPE, "cadence sensor", CADENCE_CHANNEL_PERIOD); } /** * Decode an ANT+ cadence sensor message. * @param antMessage The byte array received from the cadence sensor. */ + @Override public void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c) { int sensorTime = ((int) antMessage[5] & 0xFF) + ((int) antMessage[6] & 0xFF) * 256; int crankRevs = ((int) antMessage[7] & 0xFF) + ((int) antMessage[8] & 0xFF) * 256; - c.sendCadence(cadence.getValue(crankRevs, sensorTime)); + c.setCadence(dataProcessor.getValue(crankRevs, sensorTime)); } }; diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSpeedSensor.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSpeedSensor.java index 1331fea66..93c8aa6dd 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSpeedSensor.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/CadenceSpeedSensor.java @@ -17,7 +17,7 @@ package com.google.android.apps.mytracks.services.sensors.ant; /** - * Combined cadence and speed sensor. + * Ant+ combined cadence and speed sensor. * * @author Laszlo Molnar */ @@ -29,20 +29,20 @@ public class CadenceSpeedSensor extends AntSensorBase { public static final byte CADENCE_SPEED_DEVICE_TYPE = 121; public static final short CADENCE_SPEED_CHANNEL_PERIOD = 8086; - SensorDataProcessor cadence = new SensorDataProcessor(); + SensorDataProcessor dataProcessor = new SensorDataProcessor(); CadenceSpeedSensor(short devNum) { - super(devNum, CADENCE_SPEED_DEVICE_TYPE, - "speed&cadence sensor", CADENCE_SPEED_CHANNEL_PERIOD); + super(devNum, CADENCE_SPEED_DEVICE_TYPE, "speed&cadence sensor", CADENCE_SPEED_CHANNEL_PERIOD); } /** * Decode an ANT+ cadence&speed sensor message. * @param antMessage The byte array received from the sensor. */ + @Override public void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c) { int sensorTime = ((int) antMessage[1] & 0xFF) + ((int) antMessage[2] & 0xFF) * 256; int crankRevs = ((int) antMessage[3] & 0xFF) + ((int) antMessage[4] & 0xFF) * 256; - c.sendCadence(cadence.getValue(crankRevs, sensorTime)); + c.setCadence(dataProcessor.getValue(crankRevs, sensorTime)); } }; diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/HeartRateSensor.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/HeartRateSensor.java index a1331578f..406e56c2c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/HeartRateSensor.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/HeartRateSensor.java @@ -20,7 +20,7 @@ import static com.google.android.apps.mytracks.Constants.TAG; import android.util.Log; /** - * Heart reate monitor sensor. + * Ant+ heart reate monitor sensor. * * @author Laszlo Molnar */ @@ -33,17 +33,17 @@ public class HeartRateSensor extends AntSensorBase { public static final short HEART_RATE_CHANNEL_PERIOD = 8070; HeartRateSensor(short devNum) { - super(devNum, HEART_RATE_DEVICE_TYPE, - "heart rate monitor", HEART_RATE_CHANNEL_PERIOD); + super(devNum, HEART_RATE_DEVICE_TYPE, "heart rate monitor", HEART_RATE_CHANNEL_PERIOD); } /** * Decode an ANT+ heart rate monitor message. * @param antMessage The byte array received from the heart rate monitor. */ + @Override public void handleBroadcastData(byte[] antMessage, AntSensorDataCollector c) { int bpm = (int) antMessage[8] & 0xFF; Log.d(TAG, "now:" + System.currentTimeMillis() + " heart rate=" + bpm); - c.sendHeartRate(bpm); + c.setHeartRate(bpm); } }; diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/SensorDataProcessor.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/SensorDataProcessor.java index 9306aeb3f..5aec3733c 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/SensorDataProcessor.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/SensorDataProcessor.java @@ -20,7 +20,7 @@ import android.util.Log; import java.util.LinkedList; /** - * Processes an ANT sensor data (counter) + timestamp pair, + * Processes an ANT+ sensor data (counter) + timestamp pair, * and returns the instantaneous value of the sensor * * @author Laszlo Molnar @@ -28,84 +28,141 @@ import java.util.LinkedList; public class SensorDataProcessor { /** - * History is used for looking back at the old data - * when no new data is present, but an instantaneous value is needed + * HistoryElement stores a time stamped sensor counter */ - private class HistoryElement { - public long sysTime; - public int data; - public int sensorTime; + private static class HistoryElement { + final long systemTime; + final int data; + final int sensorTime; - HistoryElement(long sys, int d, int sens) { - sysTime = sys; - data = d; - sensorTime = sens; + HistoryElement(long systemTime, int data, int sensorTime) { + this.systemTime = systemTime; + this.data = data; + this.sensorTime = sensorTime; } - }; + } - // Removes old data from the history. - // Returns true if the remaining history is not empty. + /** + * Removes old data from the history. + * + * @param now the current system time + * @return true if the remaining history is not empty + */ protected boolean removeOldHistory(long now) { HistoryElement h; while ((h = history.peek()) != null) { - if (now - h.sysTime <= historyLengthMillis) { + // if the first element of the list is in our desired time range then return + if (now - h.systemTime <= HISTORY_LENGTH_MILLIS) { return true; } + // otherwise remove the too old element, and look at the next (newer) one history.removeFirst(); } return false; } - private int counter = -1; // the latest counter value reported by the sensor - private int actValue = 0; - public static final int historyLengthMillis = 5000; // 5 sec + /** + * The latest counter value reported by the sensor + */ + private int counter = -1; + + /** + * The calculated instantaneous sensor value to be displayed + */ + private int displayedValue = 0; + + private static final int ONE_SECOND_MILLIS = 1000; + private static final int HISTORY_LENGTH_MILLIS = ONE_SECOND_MILLIS * 5; + private static final int ONE_MINUTE_MILLIS = ONE_SECOND_MILLIS * 60; + private static final int SENSOR_TIME_RESOLUTION = 1024; // in a second + private static final int SENSOR_TIME_ONE_MINUTE = SENSOR_TIME_RESOLUTION * 60; + + /** + * History of previous sensor data - oldest first + * only the latest HISTORY_LENGTH_MILLIS milliseconds of data is stored + */ private LinkedList history = new LinkedList(); + /** + * Calculates the instantaneous sensor value to be displayed + * using the history when the sensor only resends the old data + */ + private int getValueFromHistory(int data, long now) { + if (!removeOldHistory(now)) { + // there is nothing in the history, return 0 + return displayedValue = 0; + } + HistoryElement f = history.getFirst(); + HistoryElement l = history.getLast(); + int sensorTimeChange = (l.sensorTime - f.sensorTime) & 0xFFFF; + int counterChange = (data - f.data) & 0xFFFF; + + // difference between now and systemTime of the oldest history entry + // for better precision sensor timestamps are considered between + // the first and the last history entry (could be overkill) + int systemTimeChange = (int) (now - l.systemTime + + (sensorTimeChange * ONE_SECOND_MILLIS) / SENSOR_TIME_RESOLUTION); + + // displayedValue is not overwritten by this calculated value + // because it is still needed when a new sensor event arrives + int v = (counterChange * ONE_MINUTE_MILLIS) / systemTimeChange; + Log.d(TAG, "getValue returns (2):" + v); + + // do not return larger number than displayedValue + return v < displayedValue ? v : displayedValue; + } + + /** + * Calculates the instantaneous sensor value to be displayed + * + * @param data sensor reported counter value + * @param sensorTime sensor reported timestamp + * @return the calculated value + */ public int getValue(int data, int sensorTime) { long now = System.currentTimeMillis(); - int dDelta = (data - counter) & 0xFFFF; + int counterChange = (data - counter) & 0xFFFF; Log.d(TAG, "now=" + now + " data=" + data + " sensortime=" + sensorTime); if (counter < 0) { - // store the actual counter value from the sensor + // store the initial counter value reported by the sensor + // the timestamp is probably out of date, so the history is not updated counter = data; - return actValue = 0; + return displayedValue = 0; } counter = data; - if (dDelta != 0) { + if (counterChange != 0) { + // if new data is arrived from the sensor ... if (removeOldHistory(now)) { + // ... and the history is not empty, then use the latest entry HistoryElement h = history.getLast(); - actValue = ((int) ((data - h.data) & 0xFFFF)) * 1024 * 60 - / (int) ((sensorTime - h.sensorTime) & 0xFFFF); + int sensorTimeChange = (sensorTime - h.sensorTime) & 0xFFFF; + counterChange = (data - h.data) & 0xFFFF; + displayedValue = counterChange * SENSOR_TIME_ONE_MINUTE / sensorTimeChange; } history.addLast(new HistoryElement(now, data, sensorTime)); } else if (!history.isEmpty()) { - HistoryElement h = history.getLast(); - if (60000 < (now - h.sysTime) * actValue) { - if (!removeOldHistory(now)) { - actValue = 0; - } else { - HistoryElement f = history.getFirst(); - HistoryElement l = history.getLast(); - int sDelta = (l.sensorTime - f.sensorTime) & 0xFFFF; - int cDelta = (data - f.data) & 0xFFFF; + // the sensor has resent an old (counter,timestamp) pair, + // but the history is not empty - // the saved actValue is not overwritten by this - // the returned value is computed from the history - int v = (int) (cDelta * 60 * 1000 / (now - l.sysTime + (sDelta / 1024) * 1000)); - Log.d(TAG, "getValue returns (2):" + v); - return v < actValue ? v : actValue; - } - } else { - // the current actValue is still valid, nothing to do here - } + HistoryElement h = history.getLast(); + if (ONE_MINUTE_MILLIS < (now - h.systemTime) * displayedValue) { + // Too much time has passed since the last counter change. + // This means that a smaller value than displayedValue must be + // returned. So the history is looked up, and a calculated + // average number is returned. + // Note, that displayedValue is NOT updated unless the history + // is empty or contains outdated entries. In that case it is zeroed. + return getValueFromHistory(data, now); + } // else the current displayedValue is still valid, nothing to do here } else { - actValue = 0; + // no new data from the sensor & the history is empty -> return 0 + displayedValue = 0; } - Log.d(TAG, "getValue returns:" + actValue); - return actValue; + Log.d(TAG, "getValue returns:" + displayedValue); + return displayedValue; } }