Added cycling speed sensor.

Fixed speed and cadence computation.
Fixed that a sensor change in settings resulted in a disconnect for all connected sensors.
This commit is contained in:
Dennis Guse
2020-04-25 19:59:18 +02:00
parent 3408ea97f4
commit 4893a2b743
10 changed files with 111 additions and 62 deletions
@@ -4,7 +4,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import de.dennisguse.opentracks.util.UintUtils;
import de.dennisguse.opentracks.util.UnitConversions;
@@ -43,13 +42,6 @@ public final class SensorDataCycling {
this(speed.getSensorAddress(), speed.getSensorName(), speed.getWheelRevolutionsCount(), speed.getWheelRevolutionsTime());
}
@VisibleForTesting
public Cadence(long crankRevolutionsCount, int crankRevolutionsTime) {
super("sensorAddress", "sensorName");
this.crankRevolutionsCount = crankRevolutionsCount;
this.crankRevolutionsTime = crankRevolutionsTime;
}
public boolean hasData() {
return crankRevolutionsCount != INVALID_VALUE_INT && crankRevolutionsTime != INVALID_VALUE_INT;
}
@@ -72,8 +64,7 @@ public final class SensorDataCycling {
public void compute(Cadence previous) {
if (hasData() && previous != null && previous.hasData()) {
Log.e(TAG, previous.getCrankRevolutionsTime() + " " + previous.getCrankRevolutionsCount() + " - " + this.getCrankRevolutionsTime() + " " + getCrankRevolutionsCount()); //TODO REMOVE
long timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) * 1024 / UnitConversions.S_TO_MS;
long timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024 * UnitConversions.S_TO_MS;
if (timeDiff_ms <= 0) {
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
cadence_rpm = INVALID_VALUE_FLOAT;
@@ -100,12 +91,11 @@ public final class SensorDataCycling {
}
}
//TODO Speed computation; needs wheel diameter / circumference
public static class Speed extends SensorData {
private int wheelRevolutionsCount; // UINT16
private int wheelRevolutionsTime; // UINT16; 1/1024s
private float speed_ms = INVALID_VALUE_FLOAT;
private float speed_mps = INVALID_VALUE_FLOAT;
public Speed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
super(sensorAddress, sensorName);
@@ -125,23 +115,24 @@ public final class SensorDataCycling {
return wheelRevolutionsTime;
}
public boolean hasSpeed() {
return !Float.isNaN(speed_ms);
public boolean hasSpeed_mps() {
return !Float.isNaN(speed_mps);
}
public float getSpeed_ms() {
return speed_ms;
public float getSpeed_mps() {
return speed_mps;
}
public void compute(Speed previous) {
public void compute(Speed previous, int wheel_circumference_mm) {
if (hasData() && previous != null && previous.hasData()) {
long timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) * 1024 / UnitConversions.S_TO_MS;
long timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024 * UnitConversions.S_TO_MS;
if (timeDiff_ms <= 0) {
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
speed_ms = INVALID_VALUE_FLOAT;
speed_mps = INVALID_VALUE_FLOAT;
} else {
long crankDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT32_MAX);
speed_ms = crankDiff / (float) timeDiff_ms;
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX);
double timeDiff_s = timeDiff_ms * UnitConversions.MS_TO_S;
speed_mps = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M / timeDiff_s);
}
}
}
@@ -149,7 +140,7 @@ public final class SensorDataCycling {
@NonNull
@Override
public String toString() {
return "speed=" + getSpeed_ms() + "_" + getWheelRevolutionsTime();
return "speed=" + getSpeed_mps() + "_" + getWheelRevolutionsTime();
}
@Override
@@ -71,8 +71,8 @@ public final class SensorDataSet {
trackPoint.setCyclingCadence_rpm(cyclingCadence.getCadence_rpm());
}
if (cyclingSpeed != null && cyclingSpeed.hasSpeed()) {
trackPoint.setSpeed(cyclingSpeed.getSpeed_ms());
if (cyclingSpeed != null && cyclingSpeed.hasSpeed_mps()) {
trackPoint.setSpeed(cyclingSpeed.getSpeed_mps());
}
}
@@ -449,10 +449,10 @@ public class StatsFragment extends Fragment implements TrackDataListener {
setHeartRateSensorData(sensorDataSet, isSelectedTrackRecording());
setCadenceSensorData(sensorDataSet, isSelectedTrackRecording());
setSpeedSensorData(sensorDataSet, isSelectedTrackRecording());
}
private void setHeartRateSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
// heart rate
int isVisible = View.VISIBLE;
if (!isRecording || PreferencesUtils.isBluetoothHeartRateSensorAddressNone(getContext())) {
isVisible = View.INVISIBLE;
@@ -475,7 +475,6 @@ public class StatsFragment extends Fragment implements TrackDataListener {
}
private void setCadenceSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
// heart rate
int isVisible = View.VISIBLE;
if (!isRecording || PreferencesUtils.isBluetoothCyclingCadenceSensorAddressNone(getContext())) {
isVisible = View.INVISIBLE;
@@ -497,6 +496,16 @@ public class StatsFragment extends Fragment implements TrackDataListener {
}
}
private void setSpeedSensorData(SensorDataSet sensorDataSet, boolean isRecording) {
if (isRecording) {
if (sensorDataSet != null && sensorDataSet.getCyclingSpeed() != null) {
if (sensorDataSet.getCyclingSpeed().hasSpeed_mps() && sensorDataSet.getCyclingSpeed().isRecent()) {
setSpeed(sensorDataSet.getCyclingSpeed().getSpeed_mps());
}
}
}
}
private void updateStats() {
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
@@ -600,12 +609,8 @@ public class StatsFragment extends Fragment implements TrackDataListener {
// Set speed/pace
speedContainer.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
if (isRecording) {
speedLabel.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
double speed = lastTrackPoint != null && lastTrackPoint.hasSpeed() ? lastTrackPoint.getSpeed() : Double.NaN;
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed);
speedValue.setText(parts.first);
speedUnit.setText(parts.second);
setSpeed(speed);
}
// Set elevation
@@ -635,4 +640,15 @@ public class StatsFragment extends Fragment implements TrackDataListener {
longitudeValue.setText(longitudeText);
}
}
private void setSpeed(double speed) {
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext());
speedLabel.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, metricUnits, reportSpeed);
speedValue.setText(parts.first);
speedUnit.setText(parts.second);
}
}
@@ -56,8 +56,9 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
private boolean started = false;
private final BluetoothConnectionManager heartRate = new BluetoothConnectionManager.HeartRate(this);
private final BluetoothConnectionManager cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this);
private final BluetoothConnectionManager.HeartRate heartRate = new BluetoothConnectionManager.HeartRate(this);
private final BluetoothConnectionManager.CyclingCadence cyclingCadence = new BluetoothConnectionManager.CyclingCadence(this);
private final BluetoothConnectionManager.CyclingSpeed cyclingSpeed = new BluetoothConnectionManager.CyclingSpeed(this);
private final SensorDataSet sensorDataSet = new SensorDataSet();
@@ -67,23 +68,20 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
if (!started) return;
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_heart_rate_key, key)) {
if (PreferencesUtils.isBluetoothHeartRateSensorAddressNone(context)) {
stop();
return;
}
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(context);
connect(heartRate, address);
}
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) {
if (PreferencesUtils.isBluetoothCyclingCadenceSensorAddressNone(context)) {
stop();
return;
}
String address = PreferencesUtils.getBluetoothCyclingCadenceSensorAddress(context);
connect(cyclingCadence, address);
}
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) {
String address = PreferencesUtils.getBluetoothCyclingSpeedSensorAddress(context);
connect(cyclingSpeed, address);
}
}
};
@@ -163,7 +161,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
Log.d(TAG, "onChanged: speed data repeated.");
return;
}
((SensorDataCycling.Speed) sensorData).compute(sensorDataSet.getCyclingSpeed());
((SensorDataCycling.Speed) sensorData).compute(sensorDataSet.getCyclingSpeed(), PreferencesUtils.getWheelCircumference(context));
}
sensorDataSet.set(sensorData);
@@ -49,7 +49,7 @@ public class PreferencesUtils {
@Deprecated
//NOTE: is at the moment still used to determine if a track is currently recorded; better ask the service directly.
//NOTE: This was also used to recover from a reboot, but this data should not be exposed to the whole application.
//NOTE: This was also used to recover from a service restart, but this data should not be exposed to the whole application.
public static final long RECORDING_TRACK_ID_DEFAULT = -1L;
public static long getRecordingTrackId(Context context) {
@@ -250,6 +250,10 @@ public class PreferencesUtils {
return getString(context, R.string.settings_sensor_bluetooth_cycling_speed_key, getBluetoothSensorAddressNone(context));
}
public static int getWheelCircumference(Context context) {
final int DEFAULT = Integer.parseInt(context.getResources().getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_default));
return getInt(context, R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, DEFAULT);
}
public static boolean shouldShowStatsOnLockscreen(Context context) {
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = context.getResources().getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
@@ -46,6 +46,8 @@ public class UnitConversions {
// multiplication factor to convert kilometers to miles
public static final double KM_TO_MI = 0.621371192;
public static final double MM_TO_M = 0.001;
// Distance
// multiplication factor to convert miles to feet
public static final double MI_TO_FT = 5280.0;