diff --git a/MyTracks/res/values/do_not_translate.xml b/MyTracks/res/values/do_not_translate.xml index 9454de3fc..17116204f 100644 --- a/MyTracks/res/values/do_not_translate.xml +++ b/MyTracks/res/values/do_not_translate.xml @@ -33,7 +33,7 @@ limitations under the License. NONE ANT SRM_ANT_BRIDGE - Polar + POLAR ZEPHYR diff --git a/MyTracks/src/com/google/android/apps/mytracks/SettingsActivity.java b/MyTracks/src/com/google/android/apps/mytracks/SettingsActivity.java index 87773b1df..043479139 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/SettingsActivity.java +++ b/MyTracks/src/com/google/android/apps/mytracks/SettingsActivity.java @@ -19,8 +19,8 @@ import static com.google.android.apps.mytracks.Constants.TAG; import com.google.android.apps.mytracks.io.backup.BackupActivityHelper; import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener; +import com.google.android.apps.mytracks.services.StatusAnnouncerFactory; import com.google.android.apps.mytracks.services.sensors.ant.AntUtils; -import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory; import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.apps.mytracks.util.BluetoothDeviceUtils; import com.google.android.maps.mytracks.R; @@ -232,8 +232,8 @@ public class SettingsActivity extends PreferenceActivity { private void updateSensorSettings(String sensorType) { boolean usesBluetooth = - getString(R.string.zephyr_sensor_type).equals(sensorType) || - getString(R.string.polar_sensor_type).equals(sensorType); + getString(R.string.zephyr_sensor_type).equals(sensorType) + || getString(R.string.polar_sensor_type).equals(sensorType); findPreference( getString(R.string.bluetooth_sensor_key)).setEnabled(usesBluetooth); findPreference( @@ -267,6 +267,9 @@ public class SettingsActivity extends PreferenceActivity { final ListPreference minRequiredAccuracy = (ListPreference) findPreference( getString(R.string.min_required_accuracy_key)); + final ListPreference splitFrequency = + (ListPreference) findPreference( + getString(R.string.split_frequency_key)); minRecordingDistance.setEntries(isMetric ? R.array.min_recording_distance_options @@ -277,20 +280,11 @@ public class SettingsActivity extends PreferenceActivity { minRequiredAccuracy.setEntries(isMetric ? R.array.min_required_accuracy_options : R.array.min_required_accuracy_options_ft); - setTaskOptions(isMetric, R.string.announcement_frequency_key); - setTaskOptions(isMetric, R.string.split_frequency_key); + splitFrequency.setEntries(isMetric + ? R.array.split_frequency_options + : R.array.split_frequency_options_ft); } - /** - * Set the user visible options for a periodic task. - */ - private void setTaskOptions(boolean isMetric, int listId) { - final ListPreference taskFrequency = - (ListPreference) findPreference(getString(listId)); - taskFrequency.setEntries(isMetric - ? R.array.task_frequency_options - : R.array.task_frequency_options_ft); - } /** * Configures preference actions related to bluetooth. diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java index 3f1c6ba27..8fbbdc44b 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java @@ -396,8 +396,7 @@ public class BluetoothConnectionManager { // Send copy of the obtained bytes to the UI Activity. // Avoids memory inconsistency issues. - byte[] b = buffer.clone(); - handler.obtainMessage(MESSAGE_READ, bytes, -1, b) + handler.obtainMessage(MESSAGE_READ, bytes, -1, buffer.clone()) .sendToTarget(); } catch (IOException e) { Log.e(Constants.TAG, "disconnected", e); diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarMessageParser.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarMessageParser.java index ee11135a7..1e5620bfb 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarMessageParser.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarMessageParser.java @@ -15,84 +15,98 @@ */ package com.google.android.apps.mytracks.services.sensors; -import android.util.Log; - -import com.google.android.apps.mytracks.Constants; import com.google.android.apps.mytracks.content.Sensor; /** - * An implementation of a SensorData parser for Polar Wearlink Bluetooth HRM. + * An implementation of a Sensor MessageParser for Polar Wearlink Bluetooth HRM. * + * Polar Bluetooth Wearlink packet example; + * Hdr Len Chk Seq Status HeartRate RRInterval_16-bits + * FE 08 F7 06 F1 48 03 64 + * where; + * Hdr always = 254 (0xFE), + * Chk = 255 - Len + * Seq range 0 to 15 + * Status = Upper nibble may be battery voltage + * bit 0 is Beat Detection flag. + * + * Additional packet examples; + * FE 08 F7 06 F1 48 03 64 + * FE 0A F5 06 F1 48 03 64 03 70 + * * @author John R. Gerthoffer */ public class PolarMessageParser implements MessageParser { - // Last Heart Rate value storage (in case of corrupt buffer) + private int lastHeartRate = 0; + + /** + * Applies Polar packet validation rules to buffer. + * Polar packets are checked for following; + * offset 0 = header byte, 254 (0xFE). + * offset 1 = packet length byte, 8, 10, 12, 14. + * offset 2 = check byte, 255 - packet length. + * offset 3 = sequence byte, range from 0 to 15. + * + * @param an array of bytes to parse + * @param buffer offset to beginning of packet. + * @return whether buffer has a valid packet at offset i + */ + private boolean packetValid (byte[] buffer, int i) { + boolean headerValid = (buffer[i] & 0xFF) == 0xFE; + boolean checkbyteValid = (buffer[i + 2] & 0xFF) == (0xFF - (buffer[i + 1] & 0xFF)); + boolean sequenceValid = (buffer[i + 3] & 0xFF) < 16; + + return headerValid && checkbyteValid && sequenceValid; + } @Override public Sensor.SensorDataSet parseBuffer(byte[] buffer) { - StringBuilder sb = new StringBuilder(); + int heartRate = 0; + boolean heartrateValid = false; - // Due to a memory inconsistency issue, the packet does not always start at buffer[0]. - // While it works stepping thru in debug mode, it is not consistent when running real-time. - // Note: After changing the BluetoothConnectionManager to pass a copy of 'buffer', - // the problem was resolved! Passing a buffer copy in threaded comm's is typical in my experience. - // Minimum length Polar packets is 8, so stop search 8 bytes before buffer ends. - heartRate = lastHeartRate; // Default to use last value. (If our buffer is corrupted). for (int i = 0; i < buffer.length - 8; i++) { - boolean bHdrOK = ((buffer[i] & 0xFF) == 0xFE); - boolean bChkOK = ((buffer[i+2] & 0xFF) == (0xFF - (buffer[i+1] & 0xFF))); - boolean bSeqOK = ((buffer[i+3] & 0xFF) < 16); - boolean bStatusOK = ((buffer[i+4] & 0xFF) > 128); // I've seen 0xF1 or 0xE1. - - if (bHdrOK && bChkOK && bSeqOK && bStatusOK) { + heartrateValid = packetValid(buffer,i); + if (heartrateValid) { heartRate = buffer[i + 5] & 0xFF; - lastHeartRate = heartRate; // Remember good value for next time. - break; // Let's go store our data. + break; } } + // If our buffer is corrupted, use decaying last good value. + if(!heartrateValid) { + heartRate = (int) (lastHeartRate * 0.8); + if(heartRate < 50) + heartRate = 0; + } + + lastHeartRate = heartRate; // Remember good value for next time. + // Heart Rate Sensor.SensorData.Builder b = Sensor.SensorData.newBuilder() .setValue(heartRate) .setState(Sensor.SensorState.SENDING); Sensor.SensorDataSet sds = - Sensor.SensorDataSet.newBuilder() - .setCreationTime(System.currentTimeMillis()) - .setHeartRate(b) - .build(); + Sensor.SensorDataSet.newBuilder() + .setCreationTime(System.currentTimeMillis()) + .setHeartRate(b) + .build(); return sds; } /** - * Applies packet validation rule to buffer - * Parsing rule for a good Polar HRM packet; - * boolean goodHdr = ((buffer[0] & 0xFF) == 0xFE); - * boolean goodChk = ((buffer[2] & 0xFF) == (0xFF - (buffer[1] & 0xFF))); - * goodPacket = goodHdr && goodChk; + * Applies packet validation rules to buffer * * @param an array of bytes to parse * @return whether buffer has a valid packet starting at index zero */ @Override public boolean isValid(byte[] buffer) { - /** - * Polar Bluetooth Wearlink packet example; - * Hdr Len Chk Seq Status HeartRate RRInterval_16-bits - * FE 08 F7 06 F1 48 03 64 - * where Hdr always = 0xFE, Chk = 0xFF - Len - * - * Additional packet examples; - * FE 08 F7 06 F1 48 03 64 - * FE 0A F5 06 F1 48 03 64 03 70 - */ - boolean goodHdr = ((buffer[0] & 0xFF) == 0xFE); - boolean goodChk = ((buffer[2] & 0xFF) == (0xFF - (buffer[1] & 0xFF))); - return goodHdr && goodChk; + return packetValid(buffer,0); } /** @@ -110,18 +124,15 @@ public class PolarMessageParser implements MessageParser { /** * Searches buffer for the beginning of a valid packet. - * Parsing rule for a good Polar HRM packet; - * boolean goodHdr = ((buffer[0] & 0xFF) == 0xFE); - * boolean goodChk = ((buffer[2] & 0xFF) == (0xFF - (buffer[1] & 0xFF))); - * goodPacket = goodHdr && goodChk; * * @param an array of bytes to parse + * @return index to beginning of good packet, or -1 if none found. */ @Override public int findNextAlignment(byte[] buffer) { // Minimum length Polar packets is 8, so stop search 8 bytes before buffer ends. for (int i = 0; i < buffer.length - 8; i++) { - if (((buffer[i] & 0xFF) == 0xFE) && ((buffer[i+2] & 0xFF) == (0xFF - (buffer[i+1] & 0xFF)))) { + if (packetValid(buffer,i)) { return i; } } diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarSensorManager.java index 7240cd316..d1d1f7441 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarSensorManager.java +++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/PolarSensorManager.java @@ -18,7 +18,7 @@ package com.google.android.apps.mytracks.services.sensors; import android.content.Context; /** - * PolarSensorManager - Straight copy from ZephyrSensorManager, renamed, of course. + * PolarSensorManager - A sensor manager for Polar heart rate monitors. */ public class PolarSensorManager extends BluetoothSensorManager {