From 17487961ad69b7a1b05b84c18d33b7795d84e5b9 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 5 Nov 2023 13:02:48 +0100 Subject: [PATCH 01/11] Bluetooth: upgrade minSDK=26 for simplification (race condition prevention). Part of #1424. --- build.gradle | 2 +- .../AbstractBluetoothConnectionManager.java | 18 +++++------------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/build.gradle b/build.gradle index 038b7715d..3bfafad2e 100644 --- a/build.gradle +++ b/build.gradle @@ -69,7 +69,7 @@ android { buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\"" - minSdk 24 + minSdk 26 targetSdk 34 testInstrumentationRunner "de.dennisguse.opentracks.TestRunner" diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java index 7a7185b68..6692ffabd 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java @@ -16,6 +16,7 @@ package de.dennisguse.opentracks.sensors; +import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; @@ -24,7 +25,6 @@ import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.content.Context; -import android.os.Build; import android.os.Handler; import android.util.Log; @@ -40,6 +40,7 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorData; * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. * Also parses the transferred data into {@link SensorDataObserver}. */ +@SuppressLint("MissingPermission") public abstract class AbstractBluetoothConnectionManager { private static final String TAG = AbstractBluetoothConnectionManager.class.getSimpleName(); @@ -122,12 +123,7 @@ public abstract class AbstractBluetoothConnectionManager { SensorData sensorData = parsePayload(serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic); if (sensorData != null) { Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - observer.onChanged(sensorData); - } else { - //TODO This might lead to NPEs in case of race conditions due to shutdown. - observer.getHandler().post(() -> observer.onChanged(sensorData)); - } + observer.onChanged(sensorData); } } }; @@ -150,11 +146,8 @@ public abstract class AbstractBluetoothConnectionManager { Log.d(TAG, "Connecting to: " + device); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, this.observer.getHandler()); - } else { - bluetoothGatt = device.connectGatt(context, false, connectCallback); - } + bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, this.observer.getHandler()); + SensorData sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress()); observer.onChanged(sensorData); } @@ -163,7 +156,6 @@ public abstract class AbstractBluetoothConnectionManager { observer.onDisconnecting(createEmptySensorData(bluetoothGatt.getDevice().getAddress())); } - synchronized void disconnect() { if (bluetoothGatt == null) { Log.w(TAG, "Cannot disconnect if not connected."); From 3a2a62a6d784ccb97175c652822d8768c2bc7c48 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 5 Nov 2023 13:05:43 +0100 Subject: [PATCH 02/11] Bluetooth: send sensor data directly to SensorManager. Part of #1424. --- .../TrackRecordingServiceRecordingTest.java | 20 +++++----- .../AbstractBluetoothConnectionManager.java | 31 ++++---------- ...etoothConnectionManagerCyclingCadence.java | 2 +- ...ConnectionManagerCyclingDistanceSpeed.java | 6 ++- ...luetoothConnectionManagerCyclingPower.java | 6 ++- .../BluetoothConnectionManagerHeartRate.java | 6 ++- ...toothConnectionRunningSpeedAndCadence.java | 6 ++- .../sensors/BluetoothRemoteSensorManager.java | 40 +++++++------------ .../opentracks/sensors/SensorManager.java | 4 +- 9 files changed, 50 insertions(+), 71 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java index 95e35f88e..de9c486eb 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -35,7 +35,7 @@ import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; import de.dennisguse.opentracks.sensors.AltitudeSumManager; -import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager; +import de.dennisguse.opentracks.sensors.SensorManager; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; @@ -252,17 +252,17 @@ public class TrackRecordingServiceRecordingTest { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); - BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager(); + SensorManager sensorManager = trackPointCreator.getSensorManager(); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); - remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored + sensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored String sensor3 = "2020-02-02T02:02:13Z"; trackPointCreator.setClock(sensor3); - remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(7))); + sensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(7))); String stopTime = "2020-02-02T02:02:15Z"; trackPointCreator.setClock(stopTime); @@ -589,17 +589,17 @@ public class TrackRecordingServiceRecordingTest { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); - BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager(); + SensorManager sensorManager = trackPointCreator.getSensorManager(); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); //Should be ignored + sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); //Should be ignored // when String sensor2 = "2020-02-02T02:02:04Z"; trackPointCreator.setClock(sensor2); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2))); + sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2))); // when String gps1 = "2020-02-02T02:02:05Z"; @@ -608,12 +608,12 @@ public class TrackRecordingServiceRecordingTest { // when String sensor3 = "2020-02-02T02:02:06Z"; trackPointCreator.setClock(sensor3); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12))); + sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12))); // when String sensor4 = "2020-02-02T02:02:07Z"; trackPointCreator.setClock(sensor4); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); //Should be ignored + sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); //Should be ignored // when String gps2 = "2020-02-02T02:02:08Z"; @@ -622,7 +622,7 @@ public class TrackRecordingServiceRecordingTest { // when String sensor5 = "2020-02-02T02:02:10Z"; trackPointCreator.setClock(sensor5); - remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16))); //Should be ignored + sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16))); //Should be ignored // when String gps3 = "2020-02-02T02:02:12Z"; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java index 6692ffabd..97cf4147c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java @@ -38,14 +38,13 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorData; /** * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. - * Also parses the transferred data into {@link SensorDataObserver}. */ @SuppressLint("MissingPermission") public abstract class AbstractBluetoothConnectionManager { private static final String TAG = AbstractBluetoothConnectionManager.class.getSimpleName(); - private final SensorDataObserver observer; + private final SensorManager.SensorDataChangedObserver observer; private final List serviceMeasurementUUIDs; private BluetoothGatt bluetoothGatt; @@ -123,22 +122,17 @@ public abstract class AbstractBluetoothConnectionManager { SensorData sensorData = parsePayload(serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic); if (sensorData != null) { Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData); - observer.onChanged(sensorData); + observer.onChange(sensorData); } } }; - AbstractBluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) { - this.serviceMeasurementUUIDs = List.of(serviceUUUID); - this.observer = observer; - } - - AbstractBluetoothConnectionManager(List serviceUUUID, SensorDataObserver observer) { + AbstractBluetoothConnectionManager(List serviceUUUID, SensorManager.SensorDataChangedObserver observer) { this.serviceMeasurementUUIDs = serviceUUUID; this.observer = observer; } - synchronized void connect(Context context, @NonNull BluetoothDevice device) { + synchronized void connect(Context context, Handler handler, @NonNull BluetoothDevice device) { if (bluetoothGatt != null) { Log.w(TAG, "Already connected; ignoring."); return; @@ -146,14 +140,14 @@ public abstract class AbstractBluetoothConnectionManager { Log.d(TAG, "Connecting to: " + device); - bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, this.observer.getHandler()); + bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler); SensorData sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress()); - observer.onChanged(sensorData); + observer.onChange(sensorData); } private synchronized void clearData() { - observer.onDisconnecting(createEmptySensorData(bluetoothGatt.getDevice().getAddress())); + observer.onDisconnect(createEmptySensorData(bluetoothGatt.getDevice().getAddress())); } synchronized void disconnect() { @@ -180,15 +174,4 @@ public abstract class AbstractBluetoothConnectionManager { * @return null if data could not be parsed. */ protected abstract SensorData parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic); - - @Deprecated //TODO REMOVE - interface SensorDataObserver { - - void onChanged(SensorData sensorData); - - void onDisconnecting(SensorData sensorData); - - @NonNull - Handler getHandler(); - } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java index 58980afa7..0b2353264 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java @@ -12,7 +12,7 @@ public class BluetoothConnectionManagerCyclingCadence extends AbstractBluetoothC private static final String TAG = BluetoothConnectionManagerCyclingCadence.class.getSimpleName(); - BluetoothConnectionManagerCyclingCadence(SensorDataObserver observer) { + BluetoothConnectionManagerCyclingCadence(SensorManager.SensorDataChangedObserver observer) { super(BluetoothUtils.CYCLING_CADENCE, observer); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java index 538183ea6..8334d69a3 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java @@ -2,13 +2,15 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; +import java.util.List; + import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; public class BluetoothConnectionManagerCyclingDistanceSpeed extends AbstractBluetoothConnectionManager { - BluetoothConnectionManagerCyclingDistanceSpeed(SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer); + BluetoothConnectionManagerCyclingDistanceSpeed(SensorManager.SensorDataChangedObserver observer) { + super(List.of(BluetoothUtils.CYCLING_SPEED_CADENCE), observer); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java index b5b5f8649..5ca3f089a 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java @@ -4,13 +4,15 @@ import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; +import java.util.List; + import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; public class BluetoothConnectionManagerCyclingPower extends AbstractBluetoothConnectionManager { - BluetoothConnectionManagerCyclingPower(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.CYCLING_POWER, observer); + BluetoothConnectionManagerCyclingPower(@NonNull SensorManager.SensorDataChangedObserver observer) { + super(List.of(BluetoothUtils.CYCLING_POWER), observer); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java index b8271920b..de9882742 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java @@ -4,13 +4,15 @@ import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; +import java.util.List; + import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; public class BluetoothConnectionManagerHeartRate extends AbstractBluetoothConnectionManager { - BluetoothConnectionManagerHeartRate(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.HEARTRATE, observer); + BluetoothConnectionManagerHeartRate(@NonNull SensorManager.SensorDataChangedObserver observer) { + super(List.of(BluetoothUtils.HEARTRATE), observer); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java index 42c952628..cb97166d7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java @@ -4,12 +4,14 @@ import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; +import java.util.List; + import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; public class BluetoothConnectionRunningSpeedAndCadence extends AbstractBluetoothConnectionManager { - BluetoothConnectionRunningSpeedAndCadence(@NonNull SensorDataObserver observer) { - super(BluetoothUtils.RUNNING_SPEED_CADENCE, observer); + BluetoothConnectionRunningSpeedAndCadence(@NonNull SensorManager.SensorDataChangedObserver observer) { + super(List.of(BluetoothUtils.RUNNING_SPEED_CADENCE), observer); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 3e6ddc54f..5dad69260 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -29,7 +29,6 @@ import androidx.annotation.Nullable; import java.time.Duration; import de.dennisguse.opentracks.R; -import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.util.PermissionRequester; @@ -47,7 +46,7 @@ import de.dennisguse.opentracks.util.PermissionRequester; * * @author Sandor Dornbush */ -public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBluetoothConnectionManager.SensorDataObserver, SharedPreferences.OnSharedPreferenceChangeListener { +public class BluetoothRemoteSensorManager implements SensorConnector, SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName(); @@ -56,20 +55,25 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl private final BluetoothAdapter bluetoothAdapter; private final Context context; private final Handler handler; - private final SensorManager.SensorDataChangedObserver observer; private boolean started = false; - private final BluetoothConnectionManagerHeartRate heartRate = new BluetoothConnectionManagerHeartRate(this); - private final BluetoothConnectionManagerCyclingCadence cyclingCadence = new BluetoothConnectionManagerCyclingCadence(this); - private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(this); - private final BluetoothConnectionManagerCyclingPower cyclingPower = new BluetoothConnectionManagerCyclingPower(this); - private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(this); + private final BluetoothConnectionManagerHeartRate heartRate; + private final BluetoothConnectionManagerCyclingCadence cyclingCadence; + private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed; + private final BluetoothConnectionManagerCyclingPower cyclingPower; + private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence; public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) { this.context = context; this.handler = handler; - this.observer = observer; bluetoothAdapter = BluetoothUtils.getAdapter(context); + + this.heartRate = new BluetoothConnectionManagerHeartRate(observer); + this.cyclingCadence = new BluetoothConnectionManagerCyclingCadence(observer); + this.cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(observer); + this.cyclingPower = new BluetoothConnectionManagerCyclingPower(observer); + this.runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(observer); + } @Override @@ -120,28 +124,12 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl Log.i(TAG, "Connecting to bluetooth address: " + address); try { BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address); - connectionManager.connect(context, device); + connectionManager.connect(context, handler, device); } catch (IllegalArgumentException e) { Log.e(TAG, "Unable to get remote device for: " + address, e); } } - @Override - public synchronized void onChanged(SensorData sensorData) { - observer.onChange(sensorData); - } - - @Override - public void onDisconnecting(SensorData sensorData) { - observer.onDisconnect(sensorData); - } - - @NonNull - @Override - public Handler getHandler() { - return handler; - } - @Override public void onSharedPreferenceChanged(SharedPreferences unused, @Nullable String key) { if (!started) return; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index 789bee4be..cc79da9bb 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -91,8 +91,8 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange @Deprecated @VisibleForTesting - public BluetoothRemoteSensorManager getBluetoothSensorManager() { - return bluetoothSensorManager; + public void onChanged(SensorData data) { + listener.onChange(data); } public GPSManager getGpsManager() { From 4d54472c930132902b06f4ce5cfd31cb749e2d26 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 5 Nov 2023 14:15:02 +0100 Subject: [PATCH 03/11] Bluetooth: use composition instead of inheritance. Part of #1424. --- .../sensors/BluetoothUtilsTest.java | 16 +++--- ...r.java => BluetoothConnectionManager.java} | 37 +++++-------- ...etoothConnectionManagerCyclingCadence.java | 47 ---------------- ...ConnectionManagerCyclingDistanceSpeed.java | 24 +++++---- ...luetoothConnectionManagerCyclingPower.java | 23 +++++--- .../BluetoothConnectionManagerHeartRate.java | 32 ++++++++--- ...toothConnectionRunningSpeedAndCadence.java | 21 +++++--- .../BluetoothHandlerCyclingCadence.java | 53 +++++++++++++++++++ .../sensors/BluetoothRemoteSensorManager.java | 22 ++++---- .../opentracks/sensors/BluetoothUtils.java | 36 ------------- .../sensorData/SensorHandlerInterface.java | 17 ++++++ .../BluetoothLeCyclingCadencePreference.java | 4 +- .../BluetoothLeCyclingPowerPreference.java | 4 +- .../BluetoothLeCyclingSpeedPreference.java | 4 +- .../BluetoothLeHeartRatePreference.java | 4 +- ...othLeRunningSpeedAndCadencePreference.java | 4 +- 16 files changed, 185 insertions(+), 163 deletions(-) rename src/main/java/de/dennisguse/opentracks/sensors/{AbstractBluetoothConnectionManager.java => BluetoothConnectionManager.java} (80%) delete mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java index 92dc59f21..73d523249 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java @@ -21,7 +21,7 @@ public class BluetoothUtilsTest { @Test public void parseHeartRate_uint8() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, 0x3C}); // when @@ -34,7 +34,7 @@ public class BluetoothUtilsTest { @Test public void parseHeartRate_uint16() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, 0x01, 0x01}); // when @@ -59,7 +59,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingSpeedCadence_crankOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); // when @@ -72,7 +72,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingSpeedCadence_wheelOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); // when @@ -85,7 +85,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingSpeedCadence_crankWheel() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); // when @@ -98,7 +98,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingPower_power() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0, 0, 40, 0}); // when @@ -110,7 +110,7 @@ public class BluetoothUtilsTest { @Test public void parseCyclingPower_power_with_cadence() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); // when @@ -125,7 +125,7 @@ public class BluetoothUtilsTest { @Test public void parseRunningSpeedAndCadence_with_distance() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1}); // when diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java similarity index 80% rename from src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java rename to src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index 97cf4147c..dd2640b72 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/AbstractBluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -30,23 +30,23 @@ import android.util.Log; import androidx.annotation.NonNull; -import java.util.List; import java.util.Optional; import java.util.UUID; import de.dennisguse.opentracks.sensors.sensorData.SensorData; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; /** * Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications. */ @SuppressLint("MissingPermission") -public abstract class AbstractBluetoothConnectionManager { +public class BluetoothConnectionManager { - private static final String TAG = AbstractBluetoothConnectionManager.class.getSimpleName(); + private static final String TAG = BluetoothConnectionManager.class.getSimpleName(); private final SensorManager.SensorDataChangedObserver observer; - private final List serviceMeasurementUUIDs; + private final SensorHandlerInterface sensorHandler; private BluetoothGatt bluetoothGatt; private final BluetoothGattCallback connectCallback = new BluetoothGattCallback() { @@ -76,7 +76,7 @@ public abstract class AbstractBluetoothConnectionManager { public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) { BluetoothGattService gattService = null; ServiceMeasurementUUID serviceMeasurement = null; - for (ServiceMeasurementUUID s : serviceMeasurementUUIDs) { + for (ServiceMeasurementUUID s : sensorHandler.getServices()) { gattService = gatt.getService(s.serviceUUID()); if (gattService != null) { serviceMeasurement = s; @@ -112,24 +112,22 @@ public abstract class AbstractBluetoothConnectionManager { public void onCharacteristicChanged(BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) { UUID serviceUUID = characteristic.getService().getUuid(); Log.d(TAG, "Received data from " + gatt.getDevice().getAddress() + " with service " + serviceUUID + " and characteristics " + characteristic.getUuid()); - Optional serviceMeasurementUUID = serviceMeasurementUUIDs.stream() - .filter(s -> s.serviceUUID().equals(characteristic.getService().getUuid())).findFirst(); + Optional serviceMeasurementUUID = sensorHandler.getServices() + .stream() + .filter(s -> s.serviceUUID().equals(characteristic.getService().getUuid())) + .findFirst(); if (serviceMeasurementUUID.isEmpty()) { Log.e(TAG, "Unknown service UUID; not supported?"); return; } - SensorData sensorData = parsePayload(serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic); - if (sensorData != null) { - Log.d(TAG, "Decoded data from " + gatt.getDevice().getAddress() + ": " + sensorData); - observer.onChange(sensorData); - } + sensorHandler.handlePayload(observer, serviceMeasurementUUID.get(), gatt.getDevice().getName(), gatt.getDevice().getAddress(), characteristic); } }; - AbstractBluetoothConnectionManager(List serviceUUUID, SensorManager.SensorDataChangedObserver observer) { - this.serviceMeasurementUUIDs = serviceUUUID; + BluetoothConnectionManager(SensorManager.SensorDataChangedObserver observer, SensorHandlerInterface sensorHandler) { this.observer = observer; + this.sensorHandler = sensorHandler; } synchronized void connect(Context context, Handler handler, @NonNull BluetoothDevice device) { @@ -142,12 +140,12 @@ public abstract class AbstractBluetoothConnectionManager { bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler); - SensorData sensorData = createEmptySensorData(bluetoothGatt.getDevice().getAddress()); + SensorData sensorData = sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress()); observer.onChange(sensorData); } private synchronized void clearData() { - observer.onDisconnect(createEmptySensorData(bluetoothGatt.getDevice().getAddress())); + observer.onDisconnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress())); } synchronized void disconnect() { @@ -167,11 +165,4 @@ public abstract class AbstractBluetoothConnectionManager { return address.equals(bluetoothGatt.getDevice().getAddress()); } - - protected abstract SensorData createEmptySensorData(String address); - - /** - * @return null if data could not be parsed. - */ - protected abstract SensorData parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java deleted file mode 100644 index 0b2353264..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingCadence.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import android.bluetooth.BluetoothGattCharacteristic; -import android.util.Log; - -import de.dennisguse.opentracks.data.models.Cadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; - -public class BluetoothConnectionManagerCyclingCadence extends AbstractBluetoothConnectionManager { - - private static final String TAG = BluetoothConnectionManagerCyclingCadence.class.getSimpleName(); - - BluetoothConnectionManagerCyclingCadence(SensorManager.SensorDataChangedObserver observer) { - super(BluetoothUtils.CYCLING_CADENCE, observer); - } - - @Override - protected SensorDataCyclingCadence createEmptySensorData(String address) { - return new SensorDataCyclingCadence(address); - } - - @Override - protected SensorDataCyclingCadence parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - - //TODO Implement to ServiceMeasurement.parse()? - if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_POWER)) { - SensorDataCyclingPower.Data data = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic); - if (data!= null) { - return data.cadence(); - } - } else if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_SPEED_CADENCE)) { - SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); - if (cadenceAndSpeed == null) { - return null; - } - - if (cadenceAndSpeed.getCadence() != null) { - return cadenceAndSpeed.getCadence(); - } - } - - Log.e(TAG, "Don't know how to decode this payload."); - return null; - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java index 8334d69a3..eb8dad423 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java @@ -3,32 +3,38 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; import java.util.List; +import java.util.UUID; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionManagerCyclingDistanceSpeed extends AbstractBluetoothConnectionManager { +public class BluetoothConnectionManagerCyclingDistanceSpeed implements SensorHandlerInterface { - BluetoothConnectionManagerCyclingDistanceSpeed(SensorManager.SensorDataChangedObserver observer) { - super(List.of(BluetoothUtils.CYCLING_SPEED_CADENCE), observer); + public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID( + new UUID(0x181600001000L, 0x800000805f9b34fbL), + new UUID(0x2A5B00001000L, 0x800000805f9b34fbL) + ); + + @Override + public List getServices() { + return List.of(CYCLING_SPEED_CADENCE); } @Override - protected SensorDataCyclingDistanceSpeed createEmptySensorData(String address) { + public SensorDataCyclingDistanceSpeed createEmptySensorData(String address) { return new SensorDataCyclingDistanceSpeed(address); } @Override - protected SensorDataCyclingDistanceSpeed parsePayload(ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); if (cadenceAndSpeed == null) { - return null; + return; } if (cadenceAndSpeed.getDistanceSpeed() != null) { - return cadenceAndSpeed.getDistanceSpeed(); + observer.onChange(cadenceAndSpeed.getDistanceSpeed()); } - - return null; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java index 5ca3f089a..e5b94ccfb 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java @@ -5,25 +5,34 @@ import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; import java.util.List; +import java.util.UUID; -import de.dennisguse.opentracks.data.models.Power; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionManagerCyclingPower extends AbstractBluetoothConnectionManager { +public class BluetoothConnectionManagerCyclingPower implements SensorHandlerInterface { - BluetoothConnectionManagerCyclingPower(@NonNull SensorManager.SensorDataChangedObserver observer) { - super(List.of(BluetoothUtils.CYCLING_POWER), observer); + public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID( + new UUID(0x181800001000L, 0x800000805f9b34fbL), + new UUID(0x2A6300001000L, 0x800000805f9b34fbL) + ); + + @Override + public List getServices() { + return List.of(CYCLING_POWER); } @Override - protected SensorDataCyclingPower createEmptySensorData(String address) { + public SensorDataCyclingPower createEmptySensorData(String address) { return new SensorDataCyclingPower(address); } @Override - protected SensorDataCyclingPower parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic); - return cyclingPower != null ? cyclingPower.power() : null; + if (cyclingPower != null) { + observer.onChange(cyclingPower.power()); + } } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java index de9882742..ad13326c2 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java @@ -5,25 +5,45 @@ import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; import java.util.List; +import java.util.UUID; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionManagerHeartRate extends AbstractBluetoothConnectionManager { +public class BluetoothConnectionManagerHeartRate implements SensorHandlerInterface { - BluetoothConnectionManagerHeartRate(@NonNull SensorManager.SensorDataChangedObserver observer) { - super(List.of(BluetoothUtils.HEARTRATE), observer); + public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID( + new UUID(0x180D00001000L, 0x800000805f9b34fbL), + new UUID(0x2A3700001000L, 0x800000805f9b34fbL) + ); + + // Used for device discovery in preferences + public static final List HEART_RATE_SUPPORTING_DEVICES = List.of( + HEARTRATE, + //Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery). + new ServiceMeasurementUUID( + UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3 + HEARTRATE.measurementUUID() + ) + ); + + @Override + public List getServices() { + return HEART_RATE_SUPPORTING_DEVICES; } @Override - protected SensorDataHeartRate createEmptySensorData(String address) { + public SensorDataHeartRate createEmptySensorData(String address) { return new SensorDataHeartRate(address); } @Override - protected SensorDataHeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic); - return heartRate != null ? new SensorDataHeartRate(address, sensorName, heartRate) : null; + if (heartRate != null) { + observer.onChange(new SensorDataHeartRate(address, sensorName, heartRate)); + } } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java index cb97166d7..68fb1ff41 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java @@ -5,22 +5,31 @@ import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; import java.util.List; +import java.util.UUID; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionRunningSpeedAndCadence extends AbstractBluetoothConnectionManager { +public class BluetoothConnectionRunningSpeedAndCadence implements SensorHandlerInterface { - BluetoothConnectionRunningSpeedAndCadence(@NonNull SensorManager.SensorDataChangedObserver observer) { - super(List.of(BluetoothUtils.RUNNING_SPEED_CADENCE), observer); + + public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID( + new UUID(0x181400001000L, 0x800000805f9b34fbL), + new UUID(0x2A5300001000L, 0x800000805f9b34fbL) + ); + + @Override + public List getServices() { + return List.of(RUNNING_SPEED_CADENCE); } @Override - protected SensorDataRunning createEmptySensorData(String address) { + public SensorDataRunning createEmptySensorData(String address) { return new SensorDataRunning(address); } @Override - protected SensorDataRunning parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - return BluetoothUtils.parseRunningSpeedAndCadence(address, sensorName, characteristic); + public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + observer.onChange(BluetoothUtils.parseRunningSpeedAndCadence(address, sensorName, characteristic)); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java new file mode 100644 index 000000000..625a686cb --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java @@ -0,0 +1,53 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; +import android.util.Log; + +import java.util.List; + +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; + +public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { + private static final String TAG = BluetoothHandlerCyclingCadence.class.getSimpleName(); + + public static final List CYCLING_CADENCE = List.of( + BluetoothConnectionManagerCyclingPower.CYCLING_POWER, + BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE + ); + + @Override + public List getServices() { + return CYCLING_CADENCE; + } + + @Override + public SensorDataCyclingCadence createEmptySensorData(String address) { + return new SensorDataCyclingCadence(address); + } + + @Override + public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + + //TODO Implement to ServiceMeasurement.parse()? + if (serviceMeasurementUUID.equals(BluetoothConnectionManagerCyclingPower.CYCLING_POWER)) { + SensorDataCyclingPower.Data data = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic); + if (data!= null) { + observer.onChange(data.cadence()); + } + } else if (serviceMeasurementUUID.equals(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) { + SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + if (cadenceAndSpeed == null) { + return; + } + + if (cadenceAndSpeed.getCadence() != null) { + observer.onChange(cadenceAndSpeed.getCadence()); + } + } + + Log.e(TAG, "Don't know how to decode this payload."); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index 5dad69260..c62b3d57c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -57,22 +57,22 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref private final Handler handler; private boolean started = false; - private final BluetoothConnectionManagerHeartRate heartRate; - private final BluetoothConnectionManagerCyclingCadence cyclingCadence; - private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed; - private final BluetoothConnectionManagerCyclingPower cyclingPower; - private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence; + private final BluetoothConnectionManager heartRate; + private final BluetoothConnectionManager cyclingCadence; + private final BluetoothConnectionManager cyclingSpeed; + private final BluetoothConnectionManager cyclingPower; + private final BluetoothConnectionManager runningSpeedAndCadence; public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) { this.context = context; this.handler = handler; bluetoothAdapter = BluetoothUtils.getAdapter(context); - this.heartRate = new BluetoothConnectionManagerHeartRate(observer); - this.cyclingCadence = new BluetoothConnectionManagerCyclingCadence(observer); - this.cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(observer); - this.cyclingPower = new BluetoothConnectionManagerCyclingPower(observer); - this.runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(observer); + this.heartRate = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerHeartRate()); + this.cyclingCadence = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerCyclingDistanceSpeed()); + this.cyclingSpeed = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerCyclingDistanceSpeed()); + this.cyclingPower = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerCyclingPower()); + this.runningSpeedAndCadence = new BluetoothConnectionManager(observer, new BluetoothConnectionRunningSpeedAndCadence()); } @@ -99,7 +99,7 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref return bluetoothAdapter != null && bluetoothAdapter.isEnabled(); } - private synchronized void connect(AbstractBluetoothConnectionManager connectionManager, String address) { + private synchronized void connect(BluetoothConnectionManager connectionManager, String address) { if (!isEnabled()) { Log.w(TAG, "Bluetooth not enabled."); return; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index 48e0bb296..33d0a712a 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -23,7 +23,6 @@ import android.util.Log; import androidx.annotation.NonNull; -import java.util.List; import java.util.UUID; import de.dennisguse.opentracks.data.models.AtmosphericPressure; @@ -53,47 +52,12 @@ public class BluetoothUtils { new UUID(0x2A1900001000L, 0x800000805f9b34fbL) ); - public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID( - new UUID(0x180D00001000L, 0x800000805f9b34fbL), - new UUID(0x2A3700001000L, 0x800000805f9b34fbL) - ); - - // Used for device discovery in preferences - public static final List HEART_RATE_SUPPORTING_DEVICES = List.of( - HEARTRATE, - //Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery). - new ServiceMeasurementUUID( - UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3 - HEARTRATE.measurementUUID() - ) - ); - private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL); public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID( ENVIRONMENTAL_SENSING_SERVICE, new UUID(0x2A6D00001000L, 0x800000805f9b34fbL) ); - public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID( - new UUID(0x181800001000L, 0x800000805f9b34fbL), - new UUID(0x2A6300001000L, 0x800000805f9b34fbL) - ); - - public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID( - new UUID(0x181600001000L, 0x800000805f9b34fbL), - new UUID(0x2A5B00001000L, 0x800000805f9b34fbL) - ); - - public static final List CYCLING_CADENCE = List.of( - CYCLING_POWER, - CYCLING_SPEED_CADENCE - ); - - public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID( - new UUID(0x181400001000L, 0x800000805f9b34fbL), - new UUID(0x2A5300001000L, 0x800000805f9b34fbL) - ); - private static final String TAG = BluetoothUtils.class.getSimpleName(); private BluetoothUtils() { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java new file mode 100644 index 000000000..215b86dab --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java @@ -0,0 +1,17 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import android.bluetooth.BluetoothGattCharacteristic; + +import java.util.List; + +import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID; + +public interface SensorHandlerInterface { + + List getServices(); + + SensorData createEmptySensorData(String address); + + void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic); +} diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java index bf9577621..ce0d9a2f7 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingCadencePreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence; public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeCyclingCadencePreference extends BluetoothLeSensorPrefer @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_CADENCE); + return BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerCyclingCadence.CYCLING_CADENCE); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java index af020ce07..adfbcd143 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.BluetoothConnectionManagerCyclingPower; public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_POWER); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionManagerCyclingPower.CYCLING_POWER); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java index db918d97d..1938a17e9 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.BluetoothConnectionManagerCyclingDistanceSpeed; public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java index db6df1979..dbb88fe44 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.BluetoothConnectionManagerHeartRate; public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionManagerHeartRate.HEART_RATE_SUPPORTING_DEVICES); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java index 856c79b92..14563972e 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothUtils; +import de.dennisguse.opentracks.sensors.BluetoothConnectionRunningSpeedAndCadence; public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSens @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothUtils.RUNNING_SPEED_CADENCE); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionRunningSpeedAndCadence.RUNNING_SPEED_CADENCE); } } From f2594371d388b700b85f189892a7aceb422ae53c Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 5 Nov 2023 15:13:40 +0100 Subject: [PATCH 04/11] Bluetooth: inline BluetoothUtils into Handler classes. --- ...ectionManagerCyclingDistanceSpeedTest.java | 52 ++++++ ...oothConnectionManagerCyclingPowerTest.java | 40 +++++ ...uetoothConnectionManagerHeartRateTest.java | 38 ++++ ...hConnectionRunningSpeedAndCadenceTest.java | 29 +++ ...luetoothHandlerBarometricPressureTest.java | 25 +++ .../sensors/BluetoothUtilsTest.java | 139 -------------- ...ConnectionManagerCyclingDistanceSpeed.java | 41 ++++- ...luetoothConnectionManagerCyclingPower.java | 50 +++++- .../BluetoothConnectionManagerHeartRate.java | 22 ++- ...toothConnectionRunningSpeedAndCadence.java | 51 +++++- .../BluetoothHandlerBarometricPressure.java | 45 +++++ .../BluetoothHandlerCyclingCadence.java | 4 +- .../opentracks/sensors/BluetoothUtils.java | 169 ------------------ 13 files changed, 391 insertions(+), 314 deletions(-) create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java new file mode 100644 index 000000000..189963e9d --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java @@ -0,0 +1,52 @@ +package de.dennisguse.opentracks.sensors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.bluetooth.BluetoothGattCharacteristic; + +import org.junit.Test; + +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; + +public class BluetoothConnectionManagerCyclingDistanceSpeedTest { + @Test + public void parseCyclingSpeedCadence_crankOnly() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); + + // when + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + + // then + assertNull(sensor.getDistanceSpeed()); + assertEquals(200, sensor.getCadence().getCrankRevolutionsCount()); + } + + @Test + public void parseCyclingSpeedCadence_wheelOnly() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); + + // when + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + + // then + assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); + assertNull(sensor.getCadence()); + } + + @Test + public void parseCyclingSpeedCadence_crankWheel() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); + + // when + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + + // then + assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); + assertEquals(225, sensor.getCadence().getCrankRevolutionsCount()); + } + +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java new file mode 100644 index 000000000..5ca6cfb52 --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java @@ -0,0 +1,40 @@ +package de.dennisguse.opentracks.sensors; + +import static org.junit.Assert.assertEquals; + +import android.bluetooth.BluetoothGattCharacteristic; + +import org.junit.Test; + +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; + +public class BluetoothConnectionManagerCyclingPowerTest { + + @Test + public void parseCyclingPower_power() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0, 0, 40, 0}); + + // when + SensorDataCyclingPower.Data powerCadence = BluetoothConnectionManagerCyclingPower.parseCyclingPower("", "", characteristic); + + // then + assertEquals(40, powerCadence.power().getValue().getW(), 0.01); + } + + @Test + public void parseCyclingPower_power_with_cadence() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); + + // when + SensorDataCyclingPower.Data powerCadence = BluetoothConnectionManagerCyclingPower.parseCyclingPower("", "", characteristic); + + // then + assertEquals(0, powerCadence.power().getValue().getW(), 0.01); + + assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount()); + assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime()); + } + +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java new file mode 100644 index 000000000..42259ccc0 --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java @@ -0,0 +1,38 @@ +package de.dennisguse.opentracks.sensors; + +import static org.junit.Assert.assertEquals; + +import android.bluetooth.BluetoothGattCharacteristic; + +import org.junit.Test; + +import de.dennisguse.opentracks.data.models.HeartRate; + +public class BluetoothConnectionManagerHeartRateTest { + + @Test + public void parseHeartRate_uint8() { + // given + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x02, 0x3C}); + + // when + HeartRate heartRate = BluetoothConnectionManagerHeartRate.parseHeartRate(characteristic); + + // then + assertEquals(HeartRate.of(60), heartRate); + } + + @Test + public void parseHeartRate_uint16() { + // given + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{0x01, 0x01, 0x01}); + + // when + HeartRate heartRate = BluetoothConnectionManagerHeartRate.parseHeartRate(characteristic); + + // then + assertEquals(HeartRate.of(257), heartRate); + } +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java new file mode 100644 index 000000000..43f687ecf --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java @@ -0,0 +1,29 @@ +package de.dennisguse.opentracks.sensors; + +import static org.junit.Assert.assertEquals; + +import android.bluetooth.BluetoothGattCharacteristic; + +import org.junit.Test; + +import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.data.models.Speed; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; + +public class BluetoothConnectionRunningSpeedAndCadenceTest { + + @Test + public void parseRunningSpeedAndCadence_with_distance() { + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1}); + + // when + SensorDataRunning sensor = BluetoothConnectionRunningSpeedAndCadence.parseRunningSpeedAndCadence("address", "sensorName", characteristic); + + // then + assertEquals(Speed.of(5), sensor.getSpeed()); + assertEquals(Cadence.of(80), sensor.getCadence()); + assertEquals(Distance.of(6553.5 + 1677721.6), sensor.getTotalDistance()); + } +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java new file mode 100644 index 000000000..03f513ff6 --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressureTest.java @@ -0,0 +1,25 @@ +package de.dennisguse.opentracks.sensors; + +import static org.junit.Assert.assertEquals; + +import android.bluetooth.BluetoothGattCharacteristic; + +import org.junit.Test; + +import de.dennisguse.opentracks.data.models.AtmosphericPressure; + +public class BluetoothHandlerBarometricPressureTest { + + @Test + public void parseEnvironmentalSensing_Pa() { + // given + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerBarometricPressure.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0); + characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00}); + + // when + AtmosphericPressure pressure = BluetoothHandlerBarometricPressure.parseEnvironmentalSensing(characteristic); + + // then + assertEquals(AtmosphericPressure.ofPA(100165), pressure); + } +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java deleted file mode 100644 index 73d523249..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothUtilsTest.java +++ /dev/null @@ -1,139 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import android.bluetooth.BluetoothGattCharacteristic; - -import org.junit.Test; - -import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.data.models.Cadence; -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.HeartRate; -import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; - -public class BluetoothUtilsTest { - - @Test - public void parseHeartRate_uint8() { - // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0x02, 0x3C}); - - // when - HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic); - - // then - assertEquals(HeartRate.of(60), heartRate); - } - - @Test - public void parseHeartRate_uint16() { - // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0x01, 0x01, 0x01}); - - // when - HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic); - - // then - assertEquals(HeartRate.of(257), heartRate); - } - - @Test - public void parseEnvironmentalSensing_Pa() { - // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00}); - - // when - AtmosphericPressure pressure = BluetoothUtils.parseEnvironmentalSensing(characteristic); - - // then - assertEquals(AtmosphericPressure.ofPA(100165), pressure); - } - - @Test - public void parseCyclingSpeedCadence_crankOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); - - // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); - - // then - assertNull(sensor.getDistanceSpeed()); - assertEquals(200, sensor.getCadence().getCrankRevolutionsCount()); - } - - @Test - public void parseCyclingSpeedCadence_wheelOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); - - // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); - - // then - assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); - assertNull(sensor.getCadence()); - } - - @Test - public void parseCyclingSpeedCadence_crankWheel() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); - - // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothUtils.parseCyclingCrankAndWheel("address", "sensorName", characteristic); - - // then - assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); - assertEquals(225, sensor.getCadence().getCrankRevolutionsCount()); - } - - @Test - public void parseCyclingPower_power() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0, 0, 40, 0}); - - // when - SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic); - - // then - assertEquals(40, powerCadence.power().getValue().getW(), 0.01); - } - - @Test - public void parseCyclingPower_power_with_cadence() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); - - // when - SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic); - - // then - assertEquals(0, powerCadence.power().getValue().getW(), 0.01); - - assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount()); - assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime()); - } - - @Test - public void parseRunningSpeedAndCadence_with_distance() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); - characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1}); - - // when - SensorDataRunning sensor = BluetoothUtils.parseRunningSpeedAndCadence("address", "sensorName", characteristic); - - // then - assertEquals(Speed.of(5), sensor.getSpeed()); - assertEquals(Cadence.of(80), sensor.getCadence()); - assertEquals(Distance.of(6553.5 + 1677721.6), sensor.getTotalDistance()); - } -} \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java index eb8dad423..4dd666ad5 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java @@ -2,9 +2,13 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + import java.util.List; import java.util.UUID; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -28,7 +32,7 @@ public class BluetoothConnectionManagerCyclingDistanceSpeed implements SensorHa @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = parseCyclingCrankAndWheel(address, sensorName, characteristic); if (cadenceAndSpeed == null) { return; } @@ -37,4 +41,39 @@ public class BluetoothConnectionManagerCyclingDistanceSpeed implements SensorHa observer.onChange(cadenceAndSpeed.getDistanceSpeed()); } } + + + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public static SensorDataCyclingCadenceAndDistanceSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { + // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.csc_measurement.xml + int valueLength = characteristic.getValue().length; + if (valueLength == 0) { + return null; + } + + int flags = characteristic.getValue()[0]; + boolean hasWheel = (flags & 0x01) > 0; + boolean hasCrank = (flags & 0x02) > 0; + + int index = 1; + SensorDataCyclingDistanceSpeed speed = null; + if (hasWheel && valueLength - index >= 6) { + int wheelTotalRevolutionCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index); + index += 4; + int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s + speed = new SensorDataCyclingDistanceSpeed(address, sensorName, wheelTotalRevolutionCount, wheelTime); + index += 2; + } + + SensorDataCyclingCadence cadence = null; + if (hasCrank && valueLength - index >= 4) { + long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); + index += 2; + + int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s + cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); + } + + return new SensorDataCyclingCadenceAndDistanceSpeed(address, sensorName, cadence, speed); + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java index e5b94ccfb..5e1876e8f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java @@ -3,10 +3,13 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.UUID; +import de.dennisguse.opentracks.data.models.Power; +import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -29,10 +32,55 @@ public class BluetoothConnectionManagerCyclingPower implements SensorHandlerInt @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic); + SensorDataCyclingPower.Data cyclingPower = parseCyclingPower(address, sensorName, characteristic); if (cyclingPower != null) { observer.onChange(cyclingPower.power()); } } + + + @VisibleForTesting + public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) { + // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml + int valueLength = characteristic.getValue().length; + if (valueLength == 0) { + return null; + } + + int index = 0; + int flags1 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++); + int flags2 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++); + boolean hasPedalPowerBalance = (flags1 & 0x01) > 0; + boolean hasAccumulatedTorque = (flags1 & 0x04) > 0; + boolean hasWheel = (flags1 & 16) > 0; + boolean hasCrank = (flags1 & 32) > 0; + + Integer instantaneousPower = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, index); + index += 2; + + if (hasPedalPowerBalance) { + index += 1; + } + if (hasAccumulatedTorque) { + index += 2; + } + if (hasWheel) { + index += 2 + 2; + } + + SensorDataCyclingCadence cadence = null; + if (hasCrank && valueLength - index >= 4) { + long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); + index += 2; + + int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s + + cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); + } + + + return new SensorDataCyclingPower.Data(new SensorDataCyclingPower(sensorName, address, Power.of(instantaneousPower)), cadence); + } + } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java index ad13326c2..afe095b75 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java @@ -3,6 +3,7 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.UUID; @@ -40,10 +41,29 @@ public class BluetoothConnectionManagerHeartRate implements SensorHandlerInterfa @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic); + HeartRate heartRate = parseHeartRate(characteristic); if (heartRate != null) { observer.onChange(new SensorDataHeartRate(address, sensorName, heartRate)); } } + + @VisibleForTesting + public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) { + //DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml + byte[] raw = characteristic.getValue(); + if (raw.length == 0) { + return null; + } + + boolean formatUINT16 = ((raw[0] & 0x1) == 1); + if (formatUINT16 && raw.length >= 3) { + return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1)); + } + if (!formatUINT16 && raw.length >= 2) { + return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1)); + } + + return null; + } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java index 68fb1ff41..83476c96d 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java @@ -3,10 +3,14 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.UUID; +import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -30,6 +34,51 @@ public class BluetoothConnectionRunningSpeedAndCadence implements SensorHandlerI @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - observer.onChange(BluetoothUtils.parseRunningSpeedAndCadence(address, sensorName, characteristic)); + observer.onChange(parseRunningSpeedAndCadence(address, sensorName, characteristic)); + } + + @VisibleForTesting + public static SensorDataRunning parseRunningSpeedAndCadence(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { + // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.rsc_measurement.xml + int valueLength = characteristic.getValue().length; + if (valueLength == 0) { + return null; + } + + int flags = characteristic.getValue()[0]; + boolean hasStrideLength = (flags & 0x01) > 0; + boolean hasTotalDistance = (flags & 0x02) > 0; + boolean hasStatus = (flags & 0x03) > 0; // walking vs running + + Speed speed = null; + Cadence cadence = null; + Distance totalDistance = null; + + int index = 1; + if (valueLength - index >= 2) { + speed = Speed.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index) / 256f); + } + + index = 3; + if (valueLength - index >= 1) { + cadence = Cadence.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index)); + + // Hacky workaround as the Wahoo Tickr X provides cadence in SPM (steps per minute) in violation to the standard. + if (sensorName != null && sensorName.startsWith("TICKR X")) { + cadence = Cadence.of(cadence.getRPM() / 2); + } + } + + index = 4; + if (hasStrideLength && valueLength - index >= 2) { + Distance strideDistance = Distance.ofCM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index)); + index += 2; + } + + if (hasTotalDistance && valueLength - index >= 4) { + totalDistance = Distance.ofDM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index)); + } + + return new SensorDataRunning(address, sensorName, speed, cadence, totalDistance); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java new file mode 100644 index 000000000..e0f3cd320 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java @@ -0,0 +1,45 @@ +package de.dennisguse.opentracks.sensors; + +import android.bluetooth.BluetoothGattCharacteristic; + +import java.util.List; +import java.util.UUID; + +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.sensorData.SensorData; +import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; + +public class BluetoothHandlerBarometricPressure implements SensorHandlerInterface { + private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL); + public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID( + ENVIRONMENTAL_SENSING_SERVICE, + new UUID(0x2A6D00001000L, 0x800000805f9b34fbL) + ); + + @Override + public List getServices() { + return List.of(BAROMETRIC_PRESSURE); + } + + @Override + public SensorData createEmptySensorData(String address) { + return null; //TODO + } + + @Override + public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { + //TODO + } + + public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { + byte[] raw = characteristic.getValue(); + + if (raw.length < 4) { + return null; + } + + Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0); + return AtmosphericPressure.ofPA(pressure / 10f); + } + +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java index 625a686cb..abc27a4e0 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java @@ -33,12 +33,12 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { //TODO Implement to ServiceMeasurement.parse()? if (serviceMeasurementUUID.equals(BluetoothConnectionManagerCyclingPower.CYCLING_POWER)) { - SensorDataCyclingPower.Data data = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic); + SensorDataCyclingPower.Data data = BluetoothConnectionManagerCyclingPower.parseCyclingPower(address, sensorName, characteristic); if (data!= null) { observer.onChange(data.cadence()); } } else if (serviceMeasurementUUID.equals(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) { - SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic); + SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic); if (cadenceAndSpeed == null) { return; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java index 33d0a712a..c0f2f2d1f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothUtils.java @@ -21,22 +21,9 @@ import android.bluetooth.BluetoothManager; import android.content.Context; import android.util.Log; -import androidx.annotation.NonNull; - import java.util.UUID; -import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.BatteryLevel; -import de.dennisguse.opentracks.data.models.Cadence; -import de.dennisguse.opentracks.data.models.Distance; -import de.dennisguse.opentracks.data.models.HeartRate; -import de.dennisguse.opentracks.data.models.Power; -import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; /** * Utilities for dealing with bluetooth devices. @@ -52,17 +39,9 @@ public class BluetoothUtils { new UUID(0x2A1900001000L, 0x800000805f9b34fbL) ); - private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL); - public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID( - ENVIRONMENTAL_SENSING_SERVICE, - new UUID(0x2A6D00001000L, 0x800000805f9b34fbL) - ); private static final String TAG = BluetoothUtils.class.getSimpleName(); - private BluetoothUtils() { - } - public static BluetoothAdapter getAdapter(Context context) { BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); if (bluetoothManager == null) { @@ -87,152 +66,4 @@ public class BluetoothUtils { final int batteryLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0); return BatteryLevel.of(batteryLevel); } - - public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) { - //DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml - byte[] raw = characteristic.getValue(); - if (raw.length == 0) { - return null; - } - - boolean formatUINT16 = ((raw[0] & 0x1) == 1); - if (formatUINT16 && raw.length >= 3) { - return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1)); - } - if (!formatUINT16 && raw.length >= 2) { - return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1)); - } - - return null; - } - - public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) { - byte[] raw = characteristic.getValue(); - - if (raw.length < 4) { - return null; - } - - Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0); - return AtmosphericPressure.ofPA(pressure / 10f); - } - - public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) { - // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml - int valueLength = characteristic.getValue().length; - if (valueLength == 0) { - return null; - } - - int index = 0; - int flags1 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++); - int flags2 = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index++); - boolean hasPedalPowerBalance = (flags1 & 0x01) > 0; - boolean hasAccumulatedTorque = (flags1 & 0x04) > 0; - boolean hasWheel = (flags1 & 16) > 0; - boolean hasCrank = (flags1 & 32) > 0; - - Integer instantaneousPower = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT16, index); - index += 2; - - if (hasPedalPowerBalance) { - index += 1; - } - if (hasAccumulatedTorque) { - index += 2; - } - if (hasWheel) { - index += 2 + 2; - } - - SensorDataCyclingCadence cadence = null; - if (hasCrank && valueLength - index >= 4) { - long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); - index += 2; - - int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - - cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); - } - - - return new SensorDataCyclingPower.Data(new SensorDataCyclingPower(sensorName, address, Power.of(instantaneousPower)), cadence); - } - - public static SensorDataCyclingCadenceAndDistanceSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { - // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.csc_measurement.xml - int valueLength = characteristic.getValue().length; - if (valueLength == 0) { - return null; - } - - int flags = characteristic.getValue()[0]; - boolean hasWheel = (flags & 0x01) > 0; - boolean hasCrank = (flags & 0x02) > 0; - - int index = 1; - SensorDataCyclingDistanceSpeed speed = null; - if (hasWheel && valueLength - index >= 6) { - int wheelTotalRevolutionCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index); - index += 4; - int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - speed = new SensorDataCyclingDistanceSpeed(address, sensorName, wheelTotalRevolutionCount, wheelTime); - index += 2; - } - - SensorDataCyclingCadence cadence = null; - if (hasCrank && valueLength - index >= 4) { - long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); - index += 2; - - int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); - } - - return new SensorDataCyclingCadenceAndDistanceSpeed(address, sensorName, cadence, speed); - } - - public static SensorDataRunning parseRunningSpeedAndCadence(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { - // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.rsc_measurement.xml - int valueLength = characteristic.getValue().length; - if (valueLength == 0) { - return null; - } - - int flags = characteristic.getValue()[0]; - boolean hasStrideLength = (flags & 0x01) > 0; - boolean hasTotalDistance = (flags & 0x02) > 0; - boolean hasStatus = (flags & 0x03) > 0; // walking vs running - - Speed speed = null; - Cadence cadence = null; - Distance totalDistance = null; - - int index = 1; - if (valueLength - index >= 2) { - speed = Speed.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index) / 256f); - } - - index = 3; - if (valueLength - index >= 1) { - cadence = Cadence.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, index)); - - // Hacky workaround as the Wahoo Tickr X provides cadence in SPM (steps per minute) in violation to the standard. - if (sensorName != null && sensorName.startsWith("TICKR X")) { - cadence = Cadence.of(cadence.getRPM() / 2); - } - } - - index = 4; - if (hasStrideLength && valueLength - index >= 2) { - Distance strideDistance = Distance.ofCM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index)); - index += 2; - } - - if (hasTotalDistance && valueLength - index >= 4) { - totalDistance = Distance.ofDM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index)); - } - - return new SensorDataRunning(address, sensorName, speed, cadence, totalDistance); - } } From 15f3ccbcbd0e2792a8e4540bca17210ae782ad3e Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 5 Nov 2023 15:34:02 +0100 Subject: [PATCH 05/11] Bluetooth: rename to BluetoothHandlers. --- ... BluetoothHandlerCyclingDistanceSpeedTest.java} | 14 +++++++------- ....java => BluetoothHandlerCyclingPowerTest.java} | 10 +++++----- ...a => BluetoothHandlerManagerHeartRateTest.java} | 10 +++++----- ...luetoothHandlerRunningSpeedAndCadenceTest.java} | 6 +++--- .../sensors/BluetoothHandlerCyclingCadence.java | 12 ++++++------ ...a => BluetoothHandlerCyclingDistanceSpeed.java} | 2 +- ...va => BluetoothHandlerManagerCyclingPower.java} | 2 +- ....java => BluetoothHandlerManagerHeartRate.java} | 2 +- ...=> BluetoothHandlerRunningSpeedAndCadence.java} | 2 +- .../sensors/BluetoothRemoteSensorManager.java | 10 +++++----- .../BluetoothLeCyclingPowerPreference.java | 4 ++-- .../BluetoothLeCyclingSpeedPreference.java | 4 ++-- .../bluetooth/BluetoothLeHeartRatePreference.java | 4 ++-- ...luetoothLeRunningSpeedAndCadencePreference.java | 4 ++-- 14 files changed, 43 insertions(+), 43 deletions(-) rename src/androidTest/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionManagerCyclingDistanceSpeedTest.java => BluetoothHandlerCyclingDistanceSpeedTest.java} (70%) rename src/androidTest/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionManagerCyclingPowerTest.java => BluetoothHandlerCyclingPowerTest.java} (67%) rename src/androidTest/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionManagerHeartRateTest.java => BluetoothHandlerManagerHeartRateTest.java} (64%) rename src/androidTest/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionRunningSpeedAndCadenceTest.java => BluetoothHandlerRunningSpeedAndCadenceTest.java} (73%) rename src/main/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionManagerCyclingDistanceSpeed.java => BluetoothHandlerCyclingDistanceSpeed.java} (97%) rename src/main/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionManagerCyclingPower.java => BluetoothHandlerManagerCyclingPower.java} (97%) rename src/main/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionManagerHeartRate.java => BluetoothHandlerManagerHeartRate.java} (96%) rename src/main/java/de/dennisguse/opentracks/sensors/{BluetoothConnectionRunningSpeedAndCadence.java => BluetoothHandlerRunningSpeedAndCadence.java} (97%) diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java similarity index 70% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java index 189963e9d..40e515920 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeedTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java @@ -9,14 +9,14 @@ import org.junit.Test; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; -public class BluetoothConnectionManagerCyclingDistanceSpeedTest { +public class BluetoothHandlerCyclingDistanceSpeedTest { @Test public void parseCyclingSpeedCadence_crankOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertNull(sensor.getDistanceSpeed()); @@ -25,11 +25,11 @@ public class BluetoothConnectionManagerCyclingDistanceSpeedTest { @Test public void parseCyclingSpeedCadence_wheelOnly() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); @@ -38,11 +38,11 @@ public class BluetoothConnectionManagerCyclingDistanceSpeedTest { @Test public void parseCyclingSpeedCadence_crankWheel() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java similarity index 67% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java index 5ca6cfb52..36272ee4a 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPowerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java @@ -8,15 +8,15 @@ import org.junit.Test; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; -public class BluetoothConnectionManagerCyclingPowerTest { +public class BluetoothHandlerCyclingPowerTest { @Test public void parseCyclingPower_power() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0, 0, 40, 0}); // when - SensorDataCyclingPower.Data powerCadence = BluetoothConnectionManagerCyclingPower.parseCyclingPower("", "", characteristic); + SensorDataCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower("", "", characteristic); // then assertEquals(40, powerCadence.power().getValue().getW(), 0.01); @@ -24,11 +24,11 @@ public class BluetoothConnectionManagerCyclingPowerTest { @Test public void parseCyclingPower_power_with_cadence() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerCyclingPower.CYCLING_POWER.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); // when - SensorDataCyclingPower.Data powerCadence = BluetoothConnectionManagerCyclingPower.parseCyclingPower("", "", characteristic); + SensorDataCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower("", "", characteristic); // then assertEquals(0, powerCadence.power().getValue().getW(), 0.01); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRateTest.java similarity index 64% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRateTest.java index 42259ccc0..55fecb1d5 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRateTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRateTest.java @@ -8,16 +8,16 @@ import org.junit.Test; import de.dennisguse.opentracks.data.models.HeartRate; -public class BluetoothConnectionManagerHeartRateTest { +public class BluetoothHandlerManagerHeartRateTest { @Test public void parseHeartRate_uint8() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x02, 0x3C}); // when - HeartRate heartRate = BluetoothConnectionManagerHeartRate.parseHeartRate(characteristic); + HeartRate heartRate = BluetoothHandlerManagerHeartRate.parseHeartRate(characteristic); // then assertEquals(HeartRate.of(60), heartRate); @@ -26,11 +26,11 @@ public class BluetoothConnectionManagerHeartRateTest { @Test public void parseHeartRate_uint16() { // given - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerManagerHeartRate.HEARTRATE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{0x01, 0x01, 0x01}); // when - HeartRate heartRate = BluetoothConnectionManagerHeartRate.parseHeartRate(characteristic); + HeartRate heartRate = BluetoothHandlerManagerHeartRate.parseHeartRate(characteristic); // then assertEquals(HeartRate.of(257), heartRate); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java similarity index 73% rename from src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java rename to src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java index 43f687ecf..053937a51 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadenceTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java @@ -11,15 +11,15 @@ import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; -public class BluetoothConnectionRunningSpeedAndCadenceTest { +public class BluetoothHandlerRunningSpeedAndCadenceTest { @Test public void parseRunningSpeedAndCadence_with_distance() { - BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothConnectionRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); + BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothHandlerRunningSpeedAndCadence.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0); characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1}); // when - SensorDataRunning sensor = BluetoothConnectionRunningSpeedAndCadence.parseRunningSpeedAndCadence("address", "sensorName", characteristic); + SensorDataRunning sensor = BluetoothHandlerRunningSpeedAndCadence.parseRunningSpeedAndCadence("address", "sensorName", characteristic); // then assertEquals(Speed.of(5), sensor.getSpeed()); diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java index abc27a4e0..6559a6dde 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java @@ -14,8 +14,8 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { private static final String TAG = BluetoothHandlerCyclingCadence.class.getSimpleName(); public static final List CYCLING_CADENCE = List.of( - BluetoothConnectionManagerCyclingPower.CYCLING_POWER, - BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE + BluetoothHandlerManagerCyclingPower.CYCLING_POWER, + BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE ); @Override @@ -32,13 +32,13 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { //TODO Implement to ServiceMeasurement.parse()? - if (serviceMeasurementUUID.equals(BluetoothConnectionManagerCyclingPower.CYCLING_POWER)) { - SensorDataCyclingPower.Data data = BluetoothConnectionManagerCyclingPower.parseCyclingPower(address, sensorName, characteristic); + if (serviceMeasurementUUID.equals(BluetoothHandlerManagerCyclingPower.CYCLING_POWER)) { + SensorDataCyclingPower.Data data = BluetoothHandlerManagerCyclingPower.parseCyclingPower(address, sensorName, characteristic); if (data!= null) { observer.onChange(data.cadence()); } - } else if (serviceMeasurementUUID.equals(BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) { - SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothConnectionManagerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic); + } else if (serviceMeasurementUUID.equals(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) { + SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic); if (cadenceAndSpeed == null) { return; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java similarity index 97% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java rename to src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java index 4dd666ad5..d13c7f8de 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java @@ -13,7 +13,7 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDi import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionManagerCyclingDistanceSpeed implements SensorHandlerInterface { +public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterface { public static final ServiceMeasurementUUID CYCLING_SPEED_CADENCE = new ServiceMeasurementUUID( new UUID(0x181600001000L, 0x800000805f9b34fbL), diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java similarity index 97% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java rename to src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java index 5e1876e8f..ec65ebc7c 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java @@ -13,7 +13,7 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionManagerCyclingPower implements SensorHandlerInterface { +public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterface { public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID( new UUID(0x181800001000L, 0x800000805f9b34fbL), diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java similarity index 96% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java rename to src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java index afe095b75..7b32af948 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java @@ -12,7 +12,7 @@ import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionManagerHeartRate implements SensorHandlerInterface { +public class BluetoothHandlerManagerHeartRate implements SensorHandlerInterface { public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID( new UUID(0x180D00001000L, 0x800000805f9b34fbL), diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java similarity index 97% rename from src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java rename to src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java index 83476c96d..6add68d39 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java @@ -14,7 +14,7 @@ import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; -public class BluetoothConnectionRunningSpeedAndCadence implements SensorHandlerInterface { +public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInterface { public static final ServiceMeasurementUUID RUNNING_SPEED_CADENCE = new ServiceMeasurementUUID( diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index c62b3d57c..d9258738d 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -68,11 +68,11 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref this.handler = handler; bluetoothAdapter = BluetoothUtils.getAdapter(context); - this.heartRate = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerHeartRate()); - this.cyclingCadence = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerCyclingDistanceSpeed()); - this.cyclingSpeed = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerCyclingDistanceSpeed()); - this.cyclingPower = new BluetoothConnectionManager(observer, new BluetoothConnectionManagerCyclingPower()); - this.runningSpeedAndCadence = new BluetoothConnectionManager(observer, new BluetoothConnectionRunningSpeedAndCadence()); + this.heartRate = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerHeartRate()); + this.cyclingCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingDistanceSpeed()); + this.cyclingSpeed = new BluetoothConnectionManager(observer, new BluetoothHandlerCyclingDistanceSpeed()); + this.cyclingPower = new BluetoothConnectionManager(observer, new BluetoothHandlerManagerCyclingPower()); + this.runningSpeedAndCadence = new BluetoothConnectionManager(observer, new BluetoothHandlerRunningSpeedAndCadence()); } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java index adfbcd143..025251bb0 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingPowerPreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothConnectionManagerCyclingPower; +import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower; public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeCyclingPowerPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionManagerCyclingPower.CYCLING_POWER); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerManagerCyclingPower.CYCLING_POWER); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java index 1938a17e9..5b37a9ca3 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeCyclingSpeedPreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothConnectionManagerCyclingDistanceSpeed; +import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeCyclingSpeedPreference extends BluetoothLeSensorPreferen @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionManagerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE); } } diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java index dbb88fe44..17183bf53 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeHeartRatePreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothConnectionManagerHeartRate; +import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerHeartRate; public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeHeartRatePreference extends BluetoothLeSensorPreference @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionManagerHeartRate.HEART_RATE_SUPPORTING_DEVICES); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerManagerHeartRate.HEART_RATE_SUPPORTING_DEVICES); } } \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java index 14563972e..d857ace9e 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java +++ b/src/main/java/de/dennisguse/opentracks/settings/bluetooth/BluetoothLeRunningSpeedAndCadencePreference.java @@ -5,7 +5,7 @@ import android.util.AttributeSet; import androidx.preference.PreferenceDialogFragmentCompat; -import de.dennisguse.opentracks.sensors.BluetoothConnectionRunningSpeedAndCadence; +import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSensorPreference { @@ -27,6 +27,6 @@ public class BluetoothLeRunningSpeedAndCadencePreference extends BluetoothLeSens @Override public PreferenceDialogFragmentCompat createInstance() { - return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothConnectionRunningSpeedAndCadence.RUNNING_SPEED_CADENCE); + return BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(getKey(), BluetoothHandlerRunningSpeedAndCadence.RUNNING_SPEED_CADENCE); } } From 58d79380cf18074827d5783daeaca0b019eb7ba5 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Sun, 12 Nov 2023 12:03:03 +0100 Subject: [PATCH 06/11] Sensors: separate data generation and aggregation. Part of #1424. --- .../io/file/importer/ExportImportTest.java | 14 +- ...etoothHandlerCyclingDistanceSpeedTest.java | 21 ++- .../BluetoothHandlerCyclingPowerTest.java | 14 +- ...oothHandlerRunningSpeedAndCadenceTest.java | 9 +- .../sensorData/SensorDataCyclingTest.java | 127 +++++----------- .../TrackRecordingServiceRecordingTest.java | 19 ++- .../sensors/BluetoothConnectionManager.java | 4 +- .../BluetoothHandlerBarometricPressure.java | 2 +- .../BluetoothHandlerCyclingCadence.java | 32 ++-- .../BluetoothHandlerCyclingDistanceSpeed.java | 35 +++-- .../BluetoothHandlerManagerCyclingPower.java | 19 +-- .../BluetoothHandlerManagerHeartRate.java | 3 +- ...luetoothHandlerRunningSpeedAndCadence.java | 10 +- .../sensors/BluetoothRemoteSensorManager.java | 2 +- .../opentracks/sensors/SensorManager.java | 21 ++- .../opentracks/sensors/sensorData/Raw.java | 16 ++ .../sensors/sensorData/SensorData.java | 37 ++--- .../sensorData/SensorDataCyclingCadence.java | 55 ++----- ...sorDataCyclingCadenceAndDistanceSpeed.java | 27 ---- .../SensorDataCyclingDistanceSpeed.java | 70 +++------ .../sensorData/SensorDataCyclingPower.java | 33 +--- .../sensorData/SensorDataHeartRate.java | 10 +- .../sensors/sensorData/SensorDataRunning.java | 74 +++------ .../sensors/sensorData/SensorDataSet.java | 143 ++++++++---------- .../sensorData/SensorHandlerInterface.java | 2 +- 25 files changed, 314 insertions(+), 485 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java delete mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 7065be17d..e2966bcf7 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -548,22 +548,28 @@ public class ExportImportTest { private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) { SensorDataSet sensorDataSet = new SensorDataSet(); - sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power))); - sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate))); + + sensorDataSet.cyclingPower = Mockito.mock(SensorDataCyclingPower.class); + Mockito.when( sensorDataSet.cyclingPower.hasValue()).thenReturn(true); + Mockito.when(sensorDataSet.cyclingPower.getValue()).thenReturn(Power.of(power)); + + sensorDataSet.heartRate = Mockito.mock(SensorDataHeartRate.class); + Mockito.when(sensorDataSet.heartRate.getValue()).thenReturn(HeartRate.of(heartRate)); SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class); Mockito.when(cyclingCadence.hasValue()).thenReturn(true); Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence)); - sensorDataSet.set(cyclingCadence); + sensorDataSet.cyclingCadence = cyclingCadence; if (distance != null && speed != null) { SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class); Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); + SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class); Mockito.when(distanceSpeed.hasValue()).thenReturn(true); Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); - sensorDataSet.set(distanceSpeed); + sensorDataSet.cyclingDistanceSpeed = distanceSpeed; } trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet; diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java index 40e515920..58bbbfacd 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeedTest.java @@ -4,11 +4,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import android.bluetooth.BluetoothGattCharacteristic; +import android.util.Pair; import org.junit.Test; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; - public class BluetoothHandlerCyclingDistanceSpeedTest { @Test public void parseCyclingSpeedCadence_crankOnly() { @@ -16,11 +15,11 @@ public class BluetoothHandlerCyclingDistanceSpeedTest { characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99}); // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + Pair sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then - assertNull(sensor.getDistanceSpeed()); - assertEquals(200, sensor.getCadence().getCrankRevolutionsCount()); + assertNull(sensor.first); + assertEquals(200, sensor.second.crankRevolutionsCount()); } @Test @@ -29,11 +28,11 @@ public class BluetoothHandlerCyclingDistanceSpeedTest { characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99}); // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + Pair sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then - assertEquals(65535 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); - assertNull(sensor.getCadence()); + assertEquals(65535 + 16777216, sensor.first.wheelRevolutionsCount()); + assertNull(sensor.second); } @Test @@ -42,11 +41,11 @@ public class BluetoothHandlerCyclingDistanceSpeedTest { characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99}); // when - SensorDataCyclingCadenceAndDistanceSpeed sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); + Pair sensor = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel("address", "sensorName", characteristic); // then - assertEquals(200 + 16777216, sensor.getDistanceSpeed().getWheelRevolutionsCount()); - assertEquals(225, sensor.getCadence().getCrankRevolutionsCount()); + assertEquals(200 + 16777216, sensor.first.wheelRevolutionsCount()); + assertEquals(225, sensor.second.crankRevolutionsCount()); } } \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java index 36272ee4a..2f4d56e14 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingPowerTest.java @@ -6,8 +6,6 @@ import android.bluetooth.BluetoothGattCharacteristic; import org.junit.Test; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; - public class BluetoothHandlerCyclingPowerTest { @Test @@ -16,10 +14,10 @@ public class BluetoothHandlerCyclingPowerTest { characteristic.setValue(new byte[]{0, 0, 40, 0}); // when - SensorDataCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower("", "", characteristic); + BluetoothHandlerManagerCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic); // then - assertEquals(40, powerCadence.power().getValue().getW(), 0.01); + assertEquals(40, powerCadence.power().getW(), 0.01); } @Test @@ -28,13 +26,13 @@ public class BluetoothHandlerCyclingPowerTest { characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42}); // when - SensorDataCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower("", "", characteristic); + BluetoothHandlerManagerCyclingPower.Data powerCadence = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic); // then - assertEquals(0, powerCadence.power().getValue().getW(), 0.01); + assertEquals(0, powerCadence.power().getW(), 0.01); - assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount()); - assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime()); + assertEquals(12, powerCadence.crank().crankRevolutionsCount()); + assertEquals(17125, powerCadence.crank().crankRevolutionsTime()); } } \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java index 053937a51..7ca2507e3 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadenceTest.java @@ -9,7 +9,6 @@ import org.junit.Test; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; public class BluetoothHandlerRunningSpeedAndCadenceTest { @@ -19,11 +18,11 @@ public class BluetoothHandlerRunningSpeedAndCadenceTest { characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1}); // when - SensorDataRunning sensor = BluetoothHandlerRunningSpeedAndCadence.parseRunningSpeedAndCadence("address", "sensorName", characteristic); + BluetoothHandlerRunningSpeedAndCadence.Data sensor = BluetoothHandlerRunningSpeedAndCadence.parseRunningSpeedAndCadence("sensorName", characteristic); // then - assertEquals(Speed.of(5), sensor.getSpeed()); - assertEquals(Cadence.of(80), sensor.getCadence()); - assertEquals(Distance.of(6553.5 + 1677721.6), sensor.getTotalDistance()); + assertEquals(Speed.of(5), sensor.speed()); + assertEquals(Cadence.of(80), sensor.cadence()); + assertEquals(Distance.of(6553.5 + 1677721.6), sensor.totalDistance()); } } \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java index 4dd5ca5ba..babf235f8 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java @@ -2,17 +2,17 @@ package de.dennisguse.opentracks.sensors.sensorData; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import androidx.test.ext.junit.runners.AndroidJUnit4; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; +import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence; +import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.UintUtils; @RunWith(AndroidJUnit4.class) @@ -20,12 +20,11 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_1() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); // 1s - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 2048); // 2s + SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); // when - current.compute(previous); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 2048))); // then assertEquals(60, current.getValue().getRPM(), 0.01); @@ -33,12 +32,11 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_2() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 6184); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 8016); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); // when - current.compute(previous); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 6184))); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 8016))); // then assertEquals(33.53, current.getValue().getRPM(), 0.01); @@ -46,12 +44,11 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_sameCount() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 2048); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); // when - current.compute(previous); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 2048))); // then assertEquals(Cadence.of(0), current.getValue()); @@ -60,126 +57,72 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_sameTime() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, 1024); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 1024); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); // when - current.compute(previous); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 1024))); // then - assertFalse(current.hasValue()); + assertFalse(current.hasValue()); //TODO Cadence should be 0? } @Test public void compute_cadence_rollOverTime() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", 1, UintUtils.UINT16_MAX - 1024); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 2, 0); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); // when - current.compute(previous); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024))); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 0))); // then assertEquals(60, current.getValue().getRPM(), 0.01); } - @Ignore("Disabled from #953") @Test @Deprecated public void compute_cadence_rollOverCount() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); // when - current.compute(previous); - - // then - assertEquals(60, current.getValue().getRPM(), 0.01); - } - - @Test - public void compute_cadence_overflow() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048); - - // when - current.compute(previous); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024))); + current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(0, 2048))); // then + // TODO See #953 +// assertEquals(60, current.getValue().getRPM(), 0.01); assertNull(current.getValue()); } @Test public void compute_speed() { - // given - SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 1, 6184); - SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 2, 8016); + SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("", ""); + current.setWheelCircumference(Distance.ofMM(2150)); // when - current.compute(previous, Distance.ofMM(2150)); + current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184))); + current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016))); // then assertEquals(2.15, current.getValue().getDistance().toM(), 0.01); assertEquals(1.20, current.getValue().getSpeed().toMPS(), 0.01); } - @Ignore("Disabled from #953") @Test @Deprecated public void compute_speed_rollOverCount() { - // given - SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048); + SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("", ""); + current.setWheelCircumference(Distance.ofMM(2000)); // when - current.compute(previous, Distance.ofMM(2000)); - - // then - assertEquals(2, current.getValue().getDistance().toM(), 0.01); - assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01); - } - - @Test - public void compute_speed_overflow() { - // given - SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024); - SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048); - - // when - current.compute(previous, Distance.ofMM(2000)); + current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024))); + current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048))); + // then + // TODO See #953 +// assertEquals(2, current.getValue().getDistance().toM(), 0.01); +// assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01); assertNull(current.getValue()); } - - @Test - public void equals_speed_with_no_data() { - // given - SensorDataCyclingDistanceSpeed previous = new SensorDataCyclingDistanceSpeed("sensorAddress"); - SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("sensorAddress", "sensorName", 0, 2048); - - // when - previous.toString(); - - // then - assertNotEquals(previous, current); - assertNotEquals(previous, previous); - } - - @Test - public void equals_cadence_with_no_data() { - // given - SensorDataCyclingCadence previous = new SensorDataCyclingCadence("sensorAddress"); - SensorDataCyclingCadence current = new SensorDataCyclingCadence("sensorAddress", "sensorName", 0, 2048); - - // when - previous.toString(); - - // then - assertNotEquals(previous, current); - assertNotEquals(previous, previous); - } } \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java index de9c486eb..9d7a50106 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -35,7 +35,9 @@ import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; import de.dennisguse.opentracks.sensors.AltitudeSumManager; +import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; @@ -253,16 +255,16 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); SensorManager sensorManager = trackPointCreator.getSensorManager(); - + sensorManager.sensorDataSet.add(new SensorDataHeartRate("", "")); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); - sensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored + sensorManager.onChanged(new Raw<>(HeartRate.of(5))); //Should be ignored String sensor3 = "2020-02-02T02:02:13Z"; trackPointCreator.setClock(sensor3); - sensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(7))); + sensorManager.onChanged(new Raw<>(HeartRate.of(7))); String stopTime = "2020-02-02T02:02:15Z"; trackPointCreator.setClock(stopTime); @@ -590,16 +592,17 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); SensorManager sensorManager = trackPointCreator.getSensorManager(); + sensorManager.sensorDataSet.add(new SensorDataRunning("", "")); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); - sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0))); //Should be ignored + sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(0)))); //Should be ignored // when String sensor2 = "2020-02-02T02:02:04Z"; trackPointCreator.setClock(sensor2); - sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2))); + sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2)))); // when String gps1 = "2020-02-02T02:02:05Z"; @@ -608,12 +611,12 @@ public class TrackRecordingServiceRecordingTest { // when String sensor3 = "2020-02-02T02:02:06Z"; trackPointCreator.setClock(sensor3); - sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12))); + sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12)))); // when String sensor4 = "2020-02-02T02:02:07Z"; trackPointCreator.setClock(sensor4); - sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14))); //Should be ignored + sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored // when String gps2 = "2020-02-02T02:02:08Z"; @@ -622,7 +625,7 @@ public class TrackRecordingServiceRecordingTest { // when String sensor5 = "2020-02-02T02:02:10Z"; trackPointCreator.setClock(sensor5); - sensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16))); //Should be ignored + sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored // when String gps3 = "2020-02-02T02:02:12Z"; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java index dd2640b72..bbb483d15 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothConnectionManager.java @@ -33,7 +33,6 @@ import androidx.annotation.NonNull; import java.util.Optional; import java.util.UUID; -import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; /** @@ -140,8 +139,7 @@ public class BluetoothConnectionManager { bluetoothGatt = device.connectGatt(context, false, connectCallback, BluetoothDevice.TRANSPORT_AUTO, 0, handler); - SensorData sensorData = sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress()); - observer.onChange(sensorData); + observer.onConnect(sensorHandler.createEmptySensorData(bluetoothGatt.getDevice().getAddress())); } private synchronized void clearData() { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java index e0f3cd320..0ba7811bc 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java @@ -22,7 +22,7 @@ public class BluetoothHandlerBarometricPressure implements SensorHandlerInterfac } @Override - public SensorData createEmptySensorData(String address) { + public SensorData createEmptySensorData(String address) { return null; //TODO } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java index 6559a6dde..c0757e1ac 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java @@ -2,12 +2,12 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; import android.util.Log; +import android.util.Pair; import java.util.List; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { @@ -30,24 +30,28 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - - //TODO Implement to ServiceMeasurement.parse()? if (serviceMeasurementUUID.equals(BluetoothHandlerManagerCyclingPower.CYCLING_POWER)) { - SensorDataCyclingPower.Data data = BluetoothHandlerManagerCyclingPower.parseCyclingPower(address, sensorName, characteristic); - if (data!= null) { - observer.onChange(data.cadence()); - } - } else if (serviceMeasurementUUID.equals(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) { - SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic); - if (cadenceAndSpeed == null) { - return; + BluetoothHandlerManagerCyclingPower.Data data = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic); + if (data != null && data.crank() != null) { + observer.onChange(new Raw<>(data.crank())); } + return; + } - if (cadenceAndSpeed.getCadence() != null) { - observer.onChange(cadenceAndSpeed.getCadence()); + if (serviceMeasurementUUID.equals(BluetoothHandlerCyclingDistanceSpeed.CYCLING_SPEED_CADENCE)) { + Pair data = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic); + + if (data != null && data.second != null) { + observer.onChange(new Raw<>(data.second)); } + return; } Log.e(TAG, "Don't know how to decode this payload."); } + + public record CrankData( + long crankRevolutionsCount, // UINT32 + int crankRevolutionsTime // UINT16; 1/1024s + ) {} } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java index d13c7f8de..f9c64a1e1 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java @@ -1,6 +1,7 @@ package de.dennisguse.opentracks.sensors; import android.bluetooth.BluetoothGattCharacteristic; +import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -8,8 +9,7 @@ import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.UUID; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -32,19 +32,15 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = parseCyclingCrankAndWheel(address, sensorName, characteristic); - if (cadenceAndSpeed == null) { - return; - } - - if (cadenceAndSpeed.getDistanceSpeed() != null) { - observer.onChange(cadenceAndSpeed.getDistanceSpeed()); + Pair data = parseCyclingCrankAndWheel(address, sensorName, characteristic); + if (data.first != null) { + observer.onChange(new Raw<>(data.first)); } } @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) - public static SensorDataCyclingCadenceAndDistanceSpeed parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { + public static Pair parseCyclingCrankAndWheel(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.csc_measurement.xml int valueLength = characteristic.getValue().length; if (valueLength == 0) { @@ -56,24 +52,31 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf boolean hasCrank = (flags & 0x02) > 0; int index = 1; - SensorDataCyclingDistanceSpeed speed = null; + WheelData wheelData = null; if (hasWheel && valueLength - index >= 6) { - int wheelTotalRevolutionCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index); + long wheelTotalRevolutionCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index); index += 4; int wheelTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - speed = new SensorDataCyclingDistanceSpeed(address, sensorName, wheelTotalRevolutionCount, wheelTime); + wheelData = new WheelData(wheelTotalRevolutionCount, wheelTime); index += 2; } - SensorDataCyclingCadence cadence = null; + BluetoothHandlerCyclingCadence.CrankData crankData = null; if (hasCrank && valueLength - index >= 4) { long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); index += 2; int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); + crankData = new BluetoothHandlerCyclingCadence.CrankData(crankCount, crankTime); } - return new SensorDataCyclingCadenceAndDistanceSpeed(address, sensorName, cadence, speed); + return new Pair<>(wheelData, crankData); } + + public record WheelData( + + long wheelRevolutionsCount, // UINT32 + + int wheelRevolutionsTime // UINT16; 1/1024s + ) {} } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java index ec65ebc7c..1605dec08 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.UUID; import de.dennisguse.opentracks.data.models.Power; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -32,16 +32,16 @@ public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterfa @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - SensorDataCyclingPower.Data cyclingPower = parseCyclingPower(address, sensorName, characteristic); + Data cyclingPower = parseCyclingPower(characteristic); if (cyclingPower != null) { - observer.onChange(cyclingPower.power()); + observer.onChange(new Raw<>(cyclingPower)); } } - @VisibleForTesting - public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) { + @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public static Data parseCyclingPower(BluetoothGattCharacteristic characteristic) { // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml int valueLength = characteristic.getValue().length; if (valueLength == 0) { @@ -69,18 +69,19 @@ public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterfa index += 2 + 2; } - SensorDataCyclingCadence cadence = null; + BluetoothHandlerCyclingCadence.CrankData crankData = null; if (hasCrank && valueLength - index >= 4) { long crankCount = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); index += 2; int crankTime = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, index); // 1/1024s - cadence = new SensorDataCyclingCadence(address, sensorName, crankCount, crankTime); + crankData = new BluetoothHandlerCyclingCadence.CrankData(crankCount, crankTime); } - - return new SensorDataCyclingPower.Data(new SensorDataCyclingPower(sensorName, address, Power.of(instantaneousPower)), cadence); + return new Data(Power.of(instantaneousPower), crankData); } + public record Data(Power power, BluetoothHandlerCyclingCadence.CrankData crank) {} + } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java index 7b32af948..1af0d581f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.UUID; import de.dennisguse.opentracks.data.models.HeartRate; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -44,7 +45,7 @@ public class BluetoothHandlerManagerHeartRate implements SensorHandlerInterface HeartRate heartRate = parseHeartRate(characteristic); if (heartRate != null) { - observer.onChange(new SensorDataHeartRate(address, sensorName, heartRate)); + observer.onChange(new Raw<>(heartRate)); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java index 6add68d39..ffc293ca5 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java @@ -11,6 +11,7 @@ import java.util.UUID; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; @@ -34,11 +35,12 @@ public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInte @Override public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) { - observer.onChange(parseRunningSpeedAndCadence(address, sensorName, characteristic)); + Data data = parseRunningSpeedAndCadence(sensorName, characteristic); + observer.onChange(new Raw<>(data)); } @VisibleForTesting - public static SensorDataRunning parseRunningSpeedAndCadence(String address, String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { + public static Data parseRunningSpeedAndCadence(String sensorName, @NonNull BluetoothGattCharacteristic characteristic) { // DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.rsc_measurement.xml int valueLength = characteristic.getValue().length; if (valueLength == 0) { @@ -79,6 +81,8 @@ public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInte totalDistance = Distance.ofDM(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, index)); } - return new SensorDataRunning(address, sensorName, speed, cadence, totalDistance); + return new Data(speed, cadence, totalDistance); } + + public record Data(Speed speed, Cadence cadence, Distance totalDistance) {} } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java index d9258738d..96f62779b 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothRemoteSensorManager.java @@ -50,7 +50,7 @@ public class BluetoothRemoteSensorManager implements SensorConnector, SharedPref private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName(); - public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(5); + public static final Duration MAX_SENSOR_DATE_SET_AGE = Duration.ofSeconds(50); //TODO private final BluetoothAdapter bluetoothAdapter; private final Context context; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index cc79da9bb..6fd6a768f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -9,6 +9,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import de.dennisguse.opentracks.data.models.TrackPoint; +import de.dennisguse.opentracks.sensors.sensorData.Raw; import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.services.handlers.GPSManager; @@ -27,13 +28,19 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange private final SensorDataChangedObserver listener = new SensorDataChangedObserver() { @Override - public void onChange(SensorData sensorData) { - sensorDataSet.set(sensorData); + public void onConnect(SensorData sensorData) { + sensorDataSet.add(sensorData); observer.onChange(new SensorDataSet(sensorDataSet)); } @Override - public void onDisconnect(SensorData sensorData) { + public void onChange(Raw sensorData) { + sensorDataSet.update(sensorData); + observer.onChange(new SensorDataSet(sensorDataSet)); + } + + @Override + public void onDisconnect(SensorData sensorData) { sensorDataSet.remove(sensorData); observer.onChange(new SensorDataSet(sensorDataSet)); } @@ -91,7 +98,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange @Deprecated @VisibleForTesting - public void onChanged(SensorData data) { + public void onChanged(Raw data) { listener.onChange(data); } @@ -120,8 +127,10 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange } public interface SensorDataChangedObserver { - void onChange(SensorData sensorData); - void onDisconnect(SensorData sensorData); + void onConnect(SensorData sensorData); + void onChange(Raw sensorData); + + void onDisconnect(SensorData sensorData); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java new file mode 100644 index 000000000..5f88bb229 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Raw.java @@ -0,0 +1,16 @@ +package de.dennisguse.opentracks.sensors.sensorData; + + +import androidx.annotation.NonNull; + +import java.time.Instant; + +public record Raw( + @NonNull T value, + + @NonNull Instant time +) { + public Raw(@NonNull T value) { + this(value, Instant.now()); //TODO We should be using the MonotonicClock + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java index 365d6bc48..aa59c24d7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java @@ -1,48 +1,48 @@ package de.dennisguse.opentracks.sensors.sensorData; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import java.time.Instant; import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager; -public abstract class SensorData { +public abstract class SensorData { - protected T value; + protected Raw previous; + + protected Output value; private final String sensorAddress; private final String sensorName; - private final Instant time; - SensorData(String sensorAddress) { this(sensorAddress, null); } SensorData(String sensorAddress, String sensorName) { - this(sensorAddress, sensorName, Instant.now()); - } - - @VisibleForTesting - SensorData(String sensorAddress, String sensorName, Instant time) { this.sensorAddress = sensorAddress; this.sensorName = sensorName; - this.time = time; } public String getSensorNameOrAddress() { return sensorName != null ? sensorName : sensorAddress; } + public final void add(Raw current) { + computeValue(current); + previous = current; + } + + protected abstract void computeValue(Raw current); + public boolean hasValue() { return value != null; } @NonNull - protected abstract T getNoneValue(); + protected abstract Output getNoneValue(); - public T getValue() { + public Output getValue() { if (!hasValue()) { return null; } @@ -55,20 +55,23 @@ public abstract class SensorData { /** * Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance. */ - public void reset() { - } + public void reset() {}; /** * Is the data recent considering the current time. */ private boolean isRecent() { + if (previous == null) { + return false; + } + return Instant.now() - .isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE)); + .isBefore(previous.time().plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE)); } @NonNull @Override public String toString() { - return "sensorAddress='" + sensorAddress; + return "sensorAddress=" + sensorAddress + " data=" + value; } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java index 0fbdb193e..a1ab8c4ba 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java @@ -3,53 +3,29 @@ package de.dennisguse.opentracks.sensors.sensorData; import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import java.time.Duration; import de.dennisguse.opentracks.data.models.Cadence; +import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence; import de.dennisguse.opentracks.sensors.UintUtils; -public class SensorDataCyclingCadence extends SensorData { +public class SensorDataCyclingCadence extends SensorData { private final String TAG = SensorDataCyclingCadence.class.getSimpleName(); - private final Long crankRevolutionsCount; // UINT32 - private final Integer crankRevolutionsTime; // UINT16; 1/1024s - public SensorDataCyclingCadence(String sensorAddress) { super(sensorAddress); - this.crankRevolutionsCount = null; - this.crankRevolutionsTime = null; } - public SensorDataCyclingCadence(String sensorAddress, String sensorName, long crankRevolutionsCount, int crankRevolutionsTime) { + public SensorDataCyclingCadence(String sensorAddress, String sensorName) { super(sensorAddress, sensorName); - this.crankRevolutionsCount = crankRevolutionsCount; - this.crankRevolutionsTime = crankRevolutionsTime; } - public boolean hasData() { - return crankRevolutionsCount != null && crankRevolutionsTime != null; - } - - public long getCrankRevolutionsCount() { - return crankRevolutionsCount; - } - - public int getCrankRevolutionsTime() { - return crankRevolutionsTime; - } - - @NonNull @Override - protected Cadence getNoneValue() { - return Cadence.of(0); - } - - public void compute(SensorDataCyclingCadence previous) { - if (hasData() && previous != null && previous.hasData()) { - float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * 1000; + protected void computeValue(Raw current) { + if (previous != null) { + float timeDiff_ms = UintUtils.diff(current.value().crankRevolutionsTime(), previous.value().crankRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000; Duration timeDiff = Duration.ofMillis((long) timeDiff_ms); if (timeDiff.isZero() || timeDiff.isNegative()) { Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence."); @@ -58,30 +34,19 @@ public class SensorDataCyclingCadence extends SensorData { } // TODO We have to treat with overflow according to the documentation: read https://github.com/OpenTracksApp/OpenTracks/pull/953#discussion_r711625268 - if (crankRevolutionsCount < previous.crankRevolutionsCount) { + if (current.value().crankRevolutionsCount() < previous.value().crankRevolutionsCount()) { Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence."); return; } - long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX); + long crankDiff = UintUtils.diff(current.value().crankRevolutionsCount(), previous.value().crankRevolutionsCount(), UintUtils.UINT32_MAX); value = Cadence.of(crankDiff, timeDiff); } } @NonNull @Override - public String toString() { - return super.toString() + " cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof SensorDataCyclingCadence comp)) return false; - - if (hasData() && comp.hasData() == hasData()) { - return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime(); - } else { - return false; - } + protected Cadence getNoneValue() { + return Cadence.of(0); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java deleted file mode 100644 index 26d4cd9c2..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadenceAndDistanceSpeed.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.dennisguse.opentracks.sensors.sensorData; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; - -public class SensorDataCyclingCadenceAndDistanceSpeed extends SensorData> { - - public SensorDataCyclingCadenceAndDistanceSpeed(String sensorAddress, String sensorName, @Nullable SensorDataCyclingCadence cadence, @Nullable SensorDataCyclingDistanceSpeed distanceSpeed) { - super(sensorAddress, sensorName); - this.value = new Pair<>(cadence, distanceSpeed); - } - - public SensorDataCyclingCadence getCadence() { - return this.value != null ? this.value.first : null; - } - - public SensorDataCyclingDistanceSpeed getDistanceSpeed() { - return this.value != null ? this.value.second : null; - } - - @NonNull - @Override - protected Pair getNoneValue() { - return new Pair<>(null, null); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java index 0e76713ac..7f9641a1e 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java @@ -3,58 +3,32 @@ package de.dennisguse.opentracks.sensors.sensorData; import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import java.time.Duration; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; +import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.UintUtils; -public class SensorDataCyclingDistanceSpeed extends SensorData { +public class SensorDataCyclingDistanceSpeed extends SensorData { private final String TAG = SensorDataCyclingDistanceSpeed.class.getSimpleName(); - private final Long wheelRevolutionsCount; // UINT32 - private final Integer wheelRevolutionsTime; // UINT16; 1/1024s + private Distance wheelCircumference; public SensorDataCyclingDistanceSpeed(String sensorAddress) { super(sensorAddress); - this.wheelRevolutionsCount = null; - this.wheelRevolutionsTime = null; } - public SensorDataCyclingDistanceSpeed(String sensorAddress, String sensorName, long wheelRevolutionsCount, int wheelRevolutionsTime) { + public SensorDataCyclingDistanceSpeed(String sensorAddress, String sensorName) { super(sensorAddress, sensorName); - this.wheelRevolutionsCount = wheelRevolutionsCount; - this.wheelRevolutionsTime = wheelRevolutionsTime; } - public boolean hasData() { - return wheelRevolutionsCount != null && wheelRevolutionsTime != null; - } - - public long getWheelRevolutionsCount() { - return wheelRevolutionsCount; - } - - public int getWheelRevolutionsTime() { - return wheelRevolutionsTime; - } - - @NonNull @Override - protected Data getNoneValue() { - if (value != null) { - return new Data(value.distance, value.distanceOverall, Speed.zero()); - } else { - return new Data(Distance.of(0), Distance.of(0), Speed.zero()); - } - } - - public void compute(SensorDataCyclingDistanceSpeed previous, Distance wheelCircumference) { - if (hasData() && previous != null && previous.hasData()) { - float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * 1000; + protected void computeValue(Raw current) { + if (previous != null) { + float timeDiff_ms = UintUtils.diff(current.value().wheelRevolutionsTime(), previous.value().wheelRevolutionsTime(), UintUtils.UINT16_MAX) / 1024f * 1000; Duration timeDiff = Duration.ofMillis((long) timeDiff_ms); if (timeDiff.isZero() || timeDiff.isNegative()) { Log.e(TAG, "Timestamps difference is invalid: cannot compute speed."); @@ -62,16 +36,16 @@ public class SensorDataCyclingDistanceSpeed extends SensorData type, @Nullable SensorData sensorData) { - if (type instanceof SensorDataHeartRate) { - this.heartRate = (SensorDataHeartRate) sensorData; + private void set(@NonNull SensorData type, @Nullable SensorData sensorData) { + if (type instanceof SensorDataHeartRate hr) { + heartRate = hr; return; } - - if (type instanceof SensorDataCyclingCadence) { - SensorDataCyclingCadence previous = getCyclingCadence(); - Log.d(TAG, "Previous: " + previous + "; current: " + sensorData); - - if (sensorData != null && sensorData.equals(previous)) { - Log.d(TAG, "onChanged: cadence data repeated."); - return; - } - - this.cyclingCadence = (SensorDataCyclingCadence) sensorData; - if (this.cyclingCadence != null) { - this.cyclingCadence.compute(previous); - } + if (type instanceof SensorDataCyclingCadence cc) { + cyclingCadence = cc; return; } - - if (type instanceof SensorDataCyclingDistanceSpeed) { - SensorDataCyclingDistanceSpeed previous = getCyclingDistanceSpeed(); - Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); - if (sensorData != null && sensorData.equals(previous)) { - Log.d(TAG, "onChanged: cycling speed data repeated."); - return; - } - Distance preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(); //TODO Fetch once and then listen for changes. - - this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) sensorData; - if (this.cyclingDistanceSpeed != null) { - this.cyclingDistanceSpeed.compute(previous, preferenceWheelCircumference); - } + if (type instanceof SensorDataCyclingDistanceSpeed ds) { + cyclingDistanceSpeed = ds; return; } - - if (type instanceof SensorDataCyclingPower) { - this.cyclingPower = (SensorDataCyclingPower) sensorData; + if (type instanceof SensorDataCyclingPower cp) { + cyclingPower = cp; return; } - - if (type instanceof SensorDataRunning) { - SensorDataRunning previous = getRunningDistanceSpeedCadence(); - Log.d(TAG, "Previous: " + previous + "; Current" + sensorData); - if (sensorData != null && sensorData.equals(previous)) { - Log.d(TAG, "onChanged: running speed data repeated."); - return; - } - - this.runningDistanceSpeedCadence = (SensorDataRunning) sensorData; - if (this.runningDistanceSpeedCadence != null) { - this.runningDistanceSpeedCadence.compute(previous); - } + if (type instanceof SensorDataRunning rr) { + runningDistanceSpeedCadence = rr; return; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java index 215b86dab..9893ae556 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java @@ -11,7 +11,7 @@ public interface SensorHandlerInterface { List getServices(); - SensorData createEmptySensorData(String address); + SensorData createEmptySensorData(String address); void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic); } From d47fa27e026afb0b85f1ab7021f33cfd6c1791e6 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 15 Nov 2023 07:20:03 +0100 Subject: [PATCH 07/11] Cleanup: SensorDataCyclingDistanceSpeed.Data is a record. --- .../io/file/importer/ExportImportTest.java | 6 +--- .../sensorData/SensorDataCyclingTest.java | 4 +-- .../SensorDataCyclingDistanceSpeed.java | 35 +------------------ .../sensors/sensorData/SensorDataSet.java | 6 ++-- 4 files changed, 7 insertions(+), 44 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index e2966bcf7..48af105fb 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -562,13 +562,9 @@ public class ExportImportTest { sensorDataSet.cyclingCadence = cyclingCadence; if (distance != null && speed != null) { - SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class); - Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance); - Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed)); - SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class); Mockito.when(distanceSpeed.hasValue()).thenReturn(true); - Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData); + Mockito.when(distanceSpeed.getValue()).thenReturn(new SensorDataCyclingDistanceSpeed.Data(null, distance, Speed.of(speed))); sensorDataSet.cyclingDistanceSpeed = distanceSpeed; } diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java index babf235f8..ac98399e5 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingTest.java @@ -104,8 +104,8 @@ public class SensorDataCyclingTest { current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016))); // then - assertEquals(2.15, current.getValue().getDistance().toM(), 0.01); - assertEquals(1.20, current.getValue().getSpeed().toMPS(), 0.01); + assertEquals(2.15, current.getValue().distance().toM(), 0.01); + assertEquals(1.20, current.getValue().speed().toMPS(), 0.01); } @Test diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java index 7f9641a1e..271323e00 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java @@ -73,38 +73,5 @@ public class SensorDataCyclingDistanceSpeed extends SensorData(new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); @@ -32,7 +32,7 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_2() { - SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); + AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 6184))); @@ -44,7 +44,7 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_sameCount() { - SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); + AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); @@ -57,7 +57,7 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_sameTime() { - SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); + AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024))); @@ -69,7 +69,7 @@ public class SensorDataCyclingTest { @Test public void compute_cadence_rollOverTime() { - SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); + AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024))); @@ -82,7 +82,7 @@ public class SensorDataCyclingTest { @Test @Deprecated public void compute_cadence_rollOverCount() { - SensorDataCyclingCadence current = new SensorDataCyclingCadence("", ""); + AggregatorCyclingCadence current = new AggregatorCyclingCadence("", ""); // when current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024))); @@ -96,7 +96,7 @@ public class SensorDataCyclingTest { @Test public void compute_speed() { - SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("", ""); + AggregatorCyclingDistanceSpeed current = new AggregatorCyclingDistanceSpeed("", ""); current.setWheelCircumference(Distance.ofMM(2150)); // when @@ -111,7 +111,7 @@ public class SensorDataCyclingTest { @Test @Deprecated public void compute_speed_rollOverCount() { - SensorDataCyclingDistanceSpeed current = new SensorDataCyclingDistanceSpeed("", ""); + AggregatorCyclingDistanceSpeed current = new AggregatorCyclingDistanceSpeed("", ""); current.setWheelCircumference(Distance.ofMM(2000)); // when diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java index 9d7a50106..6a0b045a7 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -37,9 +37,9 @@ import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; import de.dennisguse.opentracks.sensors.AltitudeSumManager; import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.stats.TrackStatistics; @@ -255,7 +255,7 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); SensorManager sensorManager = trackPointCreator.getSensorManager(); - sensorManager.sensorDataSet.add(new SensorDataHeartRate("", "")); + sensorManager.sensorDataSet.add(new AggregatorHeartRate("", "")); // when String sensor1 = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(sensor1); @@ -592,7 +592,7 @@ public class TrackRecordingServiceRecordingTest { Track.Id trackId = service.startNewTrack(); trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); SensorManager sensorManager = trackPointCreator.getSensorManager(); - sensorManager.sensorDataSet.add(new SensorDataRunning("", "")); + sensorManager.sensorDataSet.add(new AggregatorRunning("", "")); // when String sensor1 = "2020-02-02T02:02:03Z"; diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java index 0ba7811bc..44adebbc9 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerBarometricPressure.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.UUID; import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.sensorData.SensorData; +import de.dennisguse.opentracks.sensors.sensorData.Aggregator; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerBarometricPressure implements SensorHandlerInterface { @@ -22,7 +22,7 @@ public class BluetoothHandlerBarometricPressure implements SensorHandlerInterfac } @Override - public SensorData createEmptySensorData(String address) { + public Aggregator createEmptySensorData(String address) { return null; //TODO } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java index c0757e1ac..74ad56d33 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingCadence.java @@ -6,8 +6,8 @@ import android.util.Pair; import java.util.List; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { @@ -24,8 +24,8 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface { } @Override - public SensorDataCyclingCadence createEmptySensorData(String address) { - return new SensorDataCyclingCadence(address); + public AggregatorCyclingCadence createEmptySensorData(String address) { + return new AggregatorCyclingCadence(address); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java index f9c64a1e1..a0cb87a48 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerCyclingDistanceSpeed.java @@ -9,8 +9,8 @@ import androidx.annotation.VisibleForTesting; import java.util.List; import java.util.UUID; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterface { @@ -26,8 +26,8 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf } @Override - public SensorDataCyclingDistanceSpeed createEmptySensorData(String address) { - return new SensorDataCyclingDistanceSpeed(address); + public AggregatorCyclingDistanceSpeed createEmptySensorData(String address) { + return new AggregatorCyclingDistanceSpeed(address); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java index 1605dec08..149ec9207 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerCyclingPower.java @@ -9,8 +9,8 @@ import java.util.List; import java.util.UUID; import de.dennisguse.opentracks.data.models.Power; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterface { @@ -26,8 +26,8 @@ public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterfa } @Override - public SensorDataCyclingPower createEmptySensorData(String address) { - return new SensorDataCyclingPower(address); + public AggregatorCyclingPower createEmptySensorData(String address) { + return new AggregatorCyclingPower(address); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java index 1af0d581f..10ace7bc0 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerManagerHeartRate.java @@ -9,8 +9,8 @@ import java.util.List; import java.util.UUID; import de.dennisguse.opentracks.data.models.HeartRate; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerManagerHeartRate implements SensorHandlerInterface { @@ -36,8 +36,8 @@ public class BluetoothHandlerManagerHeartRate implements SensorHandlerInterface } @Override - public SensorDataHeartRate createEmptySensorData(String address) { - return new SensorDataHeartRate(address); + public AggregatorHeartRate createEmptySensorData(String address) { + return new AggregatorHeartRate(address); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java index ffc293ca5..1a7f8a6f4 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/BluetoothHandlerRunningSpeedAndCadence.java @@ -11,8 +11,8 @@ import java.util.UUID; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Speed; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning; import de.dennisguse.opentracks.sensors.sensorData.SensorHandlerInterface; public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInterface { @@ -29,8 +29,8 @@ public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInte } @Override - public SensorDataRunning createEmptySensorData(String address) { - return new SensorDataRunning(address); + public AggregatorRunning createEmptySensorData(String address) { + return new AggregatorRunning(address); } @Override diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index 6fd6a768f..1f1535a6f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -9,8 +9,8 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import de.dennisguse.opentracks.data.models.TrackPoint; +import de.dennisguse.opentracks.sensors.sensorData.Aggregator; import de.dennisguse.opentracks.sensors.sensorData.Raw; -import de.dennisguse.opentracks.sensors.sensorData.SensorData; import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet; import de.dennisguse.opentracks.services.handlers.GPSManager; import de.dennisguse.opentracks.services.handlers.TrackPointCreator; @@ -28,7 +28,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange private final SensorDataChangedObserver listener = new SensorDataChangedObserver() { @Override - public void onConnect(SensorData sensorData) { + public void onConnect(Aggregator sensorData) { sensorDataSet.add(sensorData); observer.onChange(new SensorDataSet(sensorDataSet)); } @@ -40,7 +40,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange } @Override - public void onDisconnect(SensorData sensorData) { + public void onDisconnect(Aggregator sensorData) { sensorDataSet.remove(sensorData); observer.onChange(new SensorDataSet(sensorDataSet)); } @@ -128,9 +128,9 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange public interface SensorDataChangedObserver { - void onConnect(SensorData sensorData); + void onConnect(Aggregator sensorData); void onChange(Raw sensorData); - void onDisconnect(SensorData sensorData); + void onDisconnect(Aggregator sensorData); } } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java similarity index 91% rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java index aa59c24d7..82ae79c2f 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorData.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/Aggregator.java @@ -6,7 +6,7 @@ import java.time.Instant; import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager; -public abstract class SensorData { +public abstract class Aggregator { protected Raw previous; @@ -15,11 +15,11 @@ public abstract class SensorData { private final String sensorAddress; private final String sensorName; - SensorData(String sensorAddress) { + Aggregator(String sensorAddress) { this(sensorAddress, null); } - SensorData(String sensorAddress, String sensorName) { + Aggregator(String sensorAddress, String sensorName) { this.sensorAddress = sensorAddress; this.sensorName = sensorName; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java similarity index 87% rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java index a1ab8c4ba..61a348354 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingCadence.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java @@ -10,15 +10,15 @@ import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence; import de.dennisguse.opentracks.sensors.UintUtils; -public class SensorDataCyclingCadence extends SensorData { +public class AggregatorCyclingCadence extends Aggregator { - private final String TAG = SensorDataCyclingCadence.class.getSimpleName(); + private final String TAG = AggregatorCyclingCadence.class.getSimpleName(); - public SensorDataCyclingCadence(String sensorAddress) { + public AggregatorCyclingCadence(String sensorAddress) { super(sensorAddress); } - public SensorDataCyclingCadence(String sensorAddress, String sensorName) { + public AggregatorCyclingCadence(String sensorAddress, String sensorName) { super(sensorAddress, sensorName); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java similarity index 89% rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java index 271323e00..71374c019 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingDistanceSpeed.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java @@ -11,17 +11,17 @@ import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.UintUtils; -public class SensorDataCyclingDistanceSpeed extends SensorData { +public class AggregatorCyclingDistanceSpeed extends Aggregator { - private final String TAG = SensorDataCyclingDistanceSpeed.class.getSimpleName(); + private final String TAG = AggregatorCyclingDistanceSpeed.class.getSimpleName(); private Distance wheelCircumference; - public SensorDataCyclingDistanceSpeed(String sensorAddress) { + public AggregatorCyclingDistanceSpeed(String sensorAddress) { super(sensorAddress); } - public SensorDataCyclingDistanceSpeed(String sensorAddress, String sensorName) { + public AggregatorCyclingDistanceSpeed(String sensorAddress, String sensorName) { super(sensorAddress, sensorName); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingPower.java similarity index 70% rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingPower.java index 5a6fa125d..e60e08ee6 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataCyclingPower.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingPower.java @@ -4,13 +4,13 @@ import androidx.annotation.NonNull; import de.dennisguse.opentracks.data.models.Power; -public class SensorDataCyclingPower extends SensorData { +public class AggregatorCyclingPower extends Aggregator { - public SensorDataCyclingPower(String address) { + public AggregatorCyclingPower(String address) { super(address); } - public SensorDataCyclingPower(String name, String address) { + public AggregatorCyclingPower(String name, String address) { super(name, address); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataHeartRate.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java similarity index 72% rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataHeartRate.java rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java index 903c72232..68879cb59 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataHeartRate.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorHeartRate.java @@ -4,13 +4,13 @@ import androidx.annotation.NonNull; import de.dennisguse.opentracks.data.models.HeartRate; -public class SensorDataHeartRate extends SensorData { +public class AggregatorHeartRate extends Aggregator { - public SensorDataHeartRate(String address) { + public AggregatorHeartRate(String address) { super(address); } - public SensorDataHeartRate(String name, String address) { + public AggregatorHeartRate(String name, String address) { super(name, address); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataRunning.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java similarity index 84% rename from src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataRunning.java rename to src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java index 0025ea867..425f20705 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataRunning.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorRunning.java @@ -10,15 +10,15 @@ import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; /** * Provides cadence in rpm and speed in milliseconds from Bluetooth LE Running Speed and Cadence sensors. */ -public final class SensorDataRunning extends SensorData { +public final class AggregatorRunning extends Aggregator { - private static final String TAG = SensorDataRunning.class.getSimpleName(); + private static final String TAG = AggregatorRunning.class.getSimpleName(); - public SensorDataRunning(String sensorAddress) { + public AggregatorRunning(String sensorAddress) { super(sensorAddress); } - public SensorDataRunning(String sensorAddress, String sensorName) { + public AggregatorRunning(String sensorAddress, String sensorName) { super(sensorAddress, sensorName); } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java index e426a1963..ae7cc254b 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -22,19 +22,19 @@ public final class SensorDataSet { private static final String TAG = SensorDataSet.class.getSimpleName(); @VisibleForTesting - public SensorDataHeartRate heartRate; + public AggregatorHeartRate heartRate; @VisibleForTesting - public SensorDataCyclingCadence cyclingCadence; + public AggregatorCyclingCadence cyclingCadence; @VisibleForTesting - public SensorDataCyclingDistanceSpeed cyclingDistanceSpeed; + public AggregatorCyclingDistanceSpeed cyclingDistanceSpeed; @VisibleForTesting - public SensorDataCyclingPower cyclingPower; + public AggregatorCyclingPower cyclingPower; @VisibleForTesting - public SensorDataRunning runningDistanceSpeedCadence; + public AggregatorRunning runningDistanceSpeedCadence; public SensorDataSet() { } @@ -79,11 +79,11 @@ public final class SensorDataSet { return null; } - public SensorDataCyclingPower getCyclingPower() { + public AggregatorCyclingPower getCyclingPower() { return cyclingPower; } - public void add(@NonNull SensorData data) { + public void add(@NonNull Aggregator data) { set(data, data); } @@ -117,7 +117,7 @@ public final class SensorDataSet { throw new UnsupportedOperationException(data.getClass().getCanonicalName()); } - public void remove(@NonNull SensorData type) { + public void remove(@NonNull Aggregator type) { set(type, null); } @@ -163,24 +163,24 @@ public final class SensorDataSet { if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.reset(); } - private void set(@NonNull SensorData type, @Nullable SensorData sensorData) { - if (type instanceof SensorDataHeartRate hr) { + private void set(@NonNull Aggregator type, @Nullable Aggregator sensorData) { + if (type instanceof AggregatorHeartRate hr) { heartRate = hr; return; } - if (type instanceof SensorDataCyclingCadence cc) { + if (type instanceof AggregatorCyclingCadence cc) { cyclingCadence = cc; return; } - if (type instanceof SensorDataCyclingDistanceSpeed ds) { + if (type instanceof AggregatorCyclingDistanceSpeed ds) { cyclingDistanceSpeed = ds; return; } - if (type instanceof SensorDataCyclingPower cp) { + if (type instanceof AggregatorCyclingPower cp) { cyclingPower = cp; return; } - if (type instanceof SensorDataRunning rr) { + if (type instanceof AggregatorRunning rr) { runningDistanceSpeedCadence = rr; return; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java index 9893ae556..89e409f5a 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorHandlerInterface.java @@ -11,7 +11,7 @@ public interface SensorHandlerInterface { List getServices(); - SensorData createEmptySensorData(String address); + Aggregator createEmptySensorData(String address); void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic); } From 5c7731bb1fd0237e2ca4651a03da46b146d7192a Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Thu, 16 Nov 2023 06:53:19 +0100 Subject: [PATCH 09/11] Bugfix: removing sensor didn't remove the aggregator. Introduced in 58d79380cf18074827d5783daeaca0b019eb7ba5. --- .../sensors/sensorData/SensorDataSet.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java index ae7cc254b..50ed66799 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -164,24 +164,24 @@ public final class SensorDataSet { } private void set(@NonNull Aggregator type, @Nullable Aggregator sensorData) { - if (type instanceof AggregatorHeartRate hr) { - heartRate = hr; + if (type instanceof AggregatorHeartRate) { + heartRate = (AggregatorHeartRate) sensorData; return; } - if (type instanceof AggregatorCyclingCadence cc) { - cyclingCadence = cc; + if (type instanceof AggregatorCyclingCadence) { + cyclingCadence = (AggregatorCyclingCadence) sensorData; return; } - if (type instanceof AggregatorCyclingDistanceSpeed ds) { - cyclingDistanceSpeed = ds; + if (type instanceof AggregatorCyclingDistanceSpeed) { + cyclingDistanceSpeed = (AggregatorCyclingDistanceSpeed) sensorData; return; } - if (type instanceof AggregatorCyclingPower cp) { - cyclingPower = cp; + if (type instanceof AggregatorCyclingPower) { + cyclingPower = (AggregatorCyclingPower) sensorData; return; } - if (type instanceof AggregatorRunning rr) { - runningDistanceSpeedCadence = rr; + if (type instanceof AggregatorRunning) { + runningDistanceSpeedCadence = (AggregatorRunning) sensorData; return; } From 8d5a7e7e8b4c967d951d70332063477a4151b1e6 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Wed, 15 Nov 2023 21:28:20 +0100 Subject: [PATCH 10/11] Barometer: use sensor aggregator. Part of #1424. --- .../io/file/importer/ExportImportTest.java | 75 ++++++------ .../sensors/AltitudeSumManagerTest.java | 58 ---------- .../sensorData/AggregatorBarometerTest.java | 28 +++++ .../TrackRecordingServiceRecordingTest.java | 60 +++++----- src/androidTest/res/raw/csv_export.csv | 2 +- .../data/models/AltitudeGainLoss.java | 3 + .../sensors/AltitudeSumManager.java | 108 ------------------ .../opentracks/sensors/GainManager.java | 46 ++++++++ .../opentracks/sensors/SensorManager.java | 10 +- .../sensors/driver/BarometerInternal.java | 7 +- .../sensorData/AggregatorBarometer.java | 46 ++++++++ .../sensors/sensorData/SensorDataSet.java | 28 ++++- .../services/handlers/TrackPointCreator.java | 9 +- 13 files changed, 235 insertions(+), 245 deletions(-) delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java create mode 100644 src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java create mode 100644 src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java delete mode 100644 src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/GainManager.java create mode 100644 src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java index 3d8da5ae0..97b61dda0 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/io/file/importer/ExportImportTest.java @@ -48,6 +48,7 @@ import de.dennisguse.opentracks.TimezoneRule; import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.models.ActivityType; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.HeartRate; @@ -58,8 +59,8 @@ import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.TrackFileFormat; import de.dennisguse.opentracks.io.file.exporter.TrackExporter; -import de.dennisguse.opentracks.sensors.AltitudeSumManager; import de.dennisguse.opentracks.sensors.sensorData.Aggregator; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingCadence; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingDistanceSpeed; import de.dennisguse.opentracks.sensors.sensorData.AggregatorCyclingPower; @@ -78,8 +79,6 @@ import de.dennisguse.opentracks.stats.TrackStatistics; @RunWith(AndroidJUnit4.class) public class ExportImportTest { - private static final String TAG = ExportImportTest.class.getSimpleName(); - @Rule public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS); @@ -140,23 +139,22 @@ public class ExportImportTest { Distance sensorDistance = Distance.of(10); // recording distance interval - sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1); + sendLocation(trackPointCreator, "2020-02-02T02:02:03Z", 3, 14, 10, 13, 15, 10, 1f); service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null); // A sensor-only TrackPoint trackPointCreator.setClock("2020-02-02T02:02:04Z"); - mockAltitudeChange(trackPointCreator, 1); - mockBLESensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f); + mockSensorData(trackPointCreator, 15f, sensorDistance, 66f, 3f, 50f, 1f); trackPointCreator.setClock("2020-02-02T02:02:14Z"); - mockBLESensorData(trackPointCreator, 15f, null, 67f, 3f, 50f); + mockSensorData(trackPointCreator, 15f, null, 67f, 3f, 50f, null); trackPointCreator.setClock("2020-02-02T02:02:15Z"); - mockBLESensorData(trackPointCreator, null, null, 68f, 3f, 50f); + mockSensorData(trackPointCreator, null, null, 68f, 3f, 50f, null); trackPointCreator.setClock("2020-02-02T02:02:16Z"); - mockBLESensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f); // Distance will be added to next TrackPoint + mockSensorData(trackPointCreator, 5f, Distance.of(2), 69f, 3f, 50f, null); // Distance will be added to next TrackPoint - sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:02:17Z", 3, 14.001, 10, 13, 15, 10, 0f); service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null); trackPointCreator.setClock("2020-02-02T02:02:18Z"); @@ -166,15 +164,16 @@ public class ExportImportTest { trackPointCreator.setClock("2020-02-02T02:03:20Z"); service.resumeTrack(trackId); - sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:03:21Z", 3, 14.002, 10, 13, 15, 10, 0f); - sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0f); trackPointCreator.setClock("2020-02-02T02:03:30Z"); service.getTrackRecordingManager().onIdle(); - sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0); + sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f); + trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(); trackPointCreator.setClock("2020-02-02T02:04:00Z"); service.endCurrentTrack(); @@ -272,7 +271,9 @@ public class ExportImportTest { .setAltitudeLoss(0f) .setAltitudeGain(0f) .setHorizontalAccuracy(Distance.of(10)), - new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")), + new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")) + .setAltitudeLoss(0f) + .setAltitudeGain(0f), new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z")) .setLatitude(3) .setLongitude(16.001) @@ -282,7 +283,6 @@ public class ExportImportTest { .setAltitudeGain(0f) .setHorizontalAccuracy(Distance.of(10)), new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z")) - ), actual); } @@ -546,42 +546,51 @@ public class ExportImportTest { } } - private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) { + private void mockSensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power, Float altitudeGain) { + SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet; - SensorDataSet sensorDataSet = new SensorDataSet(); + AggregatorCyclingPower cyclingPower = Mockito.mock(AggregatorCyclingPower.class); + Mockito.when(cyclingPower.hasValue()).thenReturn(true); + Mockito.when(cyclingPower.getValue()).thenReturn(Power.of(power)); + sensorDataSet.add(cyclingPower); - sensorDataSet.cyclingPower = Mockito.mock(AggregatorCyclingPower.class); - Mockito.when( sensorDataSet.cyclingPower.hasValue()).thenReturn(true); - Mockito.when(sensorDataSet.cyclingPower.getValue()).thenReturn(Power.of(power)); - - sensorDataSet.heartRate = Mockito.mock(AggregatorHeartRate.class); - Mockito.when(sensorDataSet.heartRate.getValue()).thenReturn(HeartRate.of(heartRate)); + AggregatorHeartRate agheartRate = Mockito.mock(AggregatorHeartRate.class); + Mockito.when(agheartRate.getValue()).thenReturn(HeartRate.of(heartRate)); + sensorDataSet.add(agheartRate); AggregatorCyclingCadence cyclingCadence = Mockito.mock(AggregatorCyclingCadence.class); Mockito.when(cyclingCadence.hasValue()).thenReturn(true); Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence)); - sensorDataSet.cyclingCadence = cyclingCadence; + sensorDataSet.add(cyclingCadence); if (distance != null && speed != null) { AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class); Mockito.when(distanceSpeed.hasValue()).thenReturn(true); Mockito.when(distanceSpeed.getValue()).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed))); - sensorDataSet.cyclingDistanceSpeed = distanceSpeed; + sensorDataSet.add(distanceSpeed); + } else { + sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", "")); } - trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet; + mockAltitudeChange(trackPointCreator, altitudeGain); + trackPointCreator.onChange(sensorDataSet); - - //TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet() } - private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) { - AltitudeSumManager altitudeSumManager = trackPointCreator.getSensorManager().getAltitudeSumManager(); - altitudeSumManager.setAltitudeGain_m(altitudeGain); - altitudeSumManager.setAltitudeLoss_m(altitudeGain); + private void mockAltitudeChange(TrackPointCreator trackPointCreator, Float altitudeGain) { + SensorDataSet sensorDataSet = trackPointCreator.getSensorManager().sensorDataSet; + + if (altitudeGain != null) { + AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class); + Mockito.when(barometer.hasValue()).thenReturn(true); + Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain)); + sensorDataSet.add(barometer); + } else { + sensorDataSet.add(new AggregatorBarometer("")); + } } - private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, float altitudeGain) { + private void sendLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, float verticalAccuracy, float speed, float altitude, Float altitudeGain) { Location location = new Location("mock"); location.setLatitude(latitude); location.setLongitude(longitude); diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java deleted file mode 100644 index 557d3ff71..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/AltitudeSumManagerTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import static org.mockito.Mockito.when; - -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.driver.BarometerInternal; - -/** - * NOTE: Test data is completely artificial. - */ -@RunWith(MockitoJUnitRunner.class) -public class AltitudeSumManagerTest { - - private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) { - for (float f : values) { - altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f)); - } - } - - @Mock - BarometerInternal driver; - - @Test - public void getAltitudeGainLoss_downhill() { - // given - AltitudeSumManager subject = new AltitudeSumManager(driver); - - when(driver.isConnected()).thenReturn(true); - - // then - addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}); - - // then - Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01); - Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01); - } - - @Test - public void sensorUnavailable() { - // given - AltitudeSumManager subject = new AltitudeSumManager(driver); - - when(driver.isConnected()).thenReturn(true); - - // then - subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f)); - - // then - Assert.assertNull(subject.getAltitudeGain_m()); - Assert.assertNull(subject.getAltitudeLoss_m()); - } -} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java new file mode 100644 index 000000000..5ef2b51c6 --- /dev/null +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometerTest.java @@ -0,0 +1,28 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import org.junit.Assert; +import org.junit.Test; + +import de.dennisguse.opentracks.data.models.AtmosphericPressure; + +public class AggregatorBarometerTest { + + private static void addSensorValue(AggregatorBarometer aggregatorBarometer, float[] values) { + for (float f : values) { + aggregatorBarometer.add(new Raw<>(AtmosphericPressure.ofHPA(f))); + } + } + + @Test + public void getAltitudeGainLoss_downhill() { + // given + AggregatorBarometer subject = new AggregatorBarometer(""); + + // then + addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f}); + + // then + Assert.assertEquals(0f, subject.value.gain_m(), 0.01); + Assert.assertEquals(15f, subject.value.loss_m(), 0.01); + } +} \ No newline at end of file diff --git a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java index 6a0b045a7..82c75ce8e 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/services/TrackRecordingServiceRecordingTest.java @@ -7,7 +7,6 @@ import android.content.Context; import android.content.Intent; import android.os.Looper; -import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; @@ -19,6 +18,7 @@ import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.time.Instant; import java.util.List; @@ -28,15 +28,16 @@ import java.util.concurrent.TimeoutException; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.content.data.TestDataUtil; import de.dennisguse.opentracks.data.ContentProviderUtils; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.Speed; import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.data.models.TrackPoint; import de.dennisguse.opentracks.io.file.importer.TrackPointAssert; -import de.dennisguse.opentracks.sensors.AltitudeSumManager; import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence; import de.dennisguse.opentracks.sensors.SensorManager; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; import de.dennisguse.opentracks.sensors.sensorData.AggregatorHeartRate; import de.dennisguse.opentracks.sensors.sensorData.AggregatorRunning; import de.dennisguse.opentracks.sensors.sensorData.Raw; @@ -46,8 +47,6 @@ import de.dennisguse.opentracks.stats.TrackStatistics; /** * Tests insert location. - * Note: on API23, the TrackRecordingService may not be stopped properly before the next test. - * So, if something fails, subsequent tests may be affected. */ @RunWith(AndroidJUnit4.class) public class TrackRecordingServiceRecordingTest { @@ -60,14 +59,6 @@ public class TrackRecordingServiceRecordingTest { private TrackRecordingService service; - private final AltitudeSumManager altitudeSumManager = new AltitudeSumManager() { - @Override - public void fill(@NonNull TrackPoint trackPoint) { - trackPoint.setAltitudeGain(0f); - trackPoint.setAltitudeLoss(0f); - } - }; - @BeforeClass public static void preSetUp() { // Prepare looper for Android's message queue @@ -107,7 +98,8 @@ public class TrackRecordingServiceRecordingTest { String startTime = "2020-02-02T02:02:02Z"; trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // then assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null) @@ -144,7 +136,8 @@ public class TrackRecordingServiceRecordingTest { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String pauseTime = "2020-02-02T02:02:03Z"; @@ -190,7 +183,8 @@ public class TrackRecordingServiceRecordingTest { String starTime = "2020-02-02T02:02:02Z"; trackPointCreator.setClock(starTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + String pauseTime = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(pauseTime); @@ -222,7 +216,8 @@ public class TrackRecordingServiceRecordingTest { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + String stopTime = "2020-02-02T02:02:03Z"; trackPointCreator.setClock(stopTime); @@ -232,7 +227,8 @@ public class TrackRecordingServiceRecordingTest { String resumeTime = "2020-02-02T02:02:04Z"; trackPointCreator.setClock(resumeTime); service.resumeTrack(trackId); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // then new TrackPointAssert().assertEquals(List.of( @@ -253,7 +249,8 @@ public class TrackRecordingServiceRecordingTest { trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + SensorManager sensorManager = trackPointCreator.getSensorManager(); sensorManager.sensorDataSet.add(new AggregatorHeartRate("", "")); // when @@ -284,7 +281,6 @@ public class TrackRecordingServiceRecordingTest { ), TestDataUtil.getTrackPoints(contentProviderUtils, trackId)); } - @MediumTest @Test public void testRecording_gpsOnly_recordingDistance_above() { @@ -293,7 +289,7 @@ public class TrackRecordingServiceRecordingTest { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); // when @@ -367,7 +363,8 @@ public class TrackRecordingServiceRecordingTest { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String gps1 = "2020-02-02T02:02:03Z"; @@ -431,7 +428,8 @@ public class TrackRecordingServiceRecordingTest { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String gps1 = "2020-02-02T02:02:03Z"; @@ -481,7 +479,8 @@ public class TrackRecordingServiceRecordingTest { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); + // when String gps1 = "2020-02-02T02:02:03Z"; @@ -527,7 +526,7 @@ public class TrackRecordingServiceRecordingTest { TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + mockAltitudeChange(trackPointCreator, 0); // when String gps1 = "2020-02-02T02:02:03Z"; @@ -583,16 +582,15 @@ public class TrackRecordingServiceRecordingTest { @Test public void testRecording_gpsAndSensor_gpsIdleMoving_sensorMoving() { // TODO Check TrackStatistics - AltitudeSumManager altitudeSumManager = new AltitudeSumManager(); - // given String startTime = "2020-02-02T02:02:02Z"; TrackPointCreator trackPointCreator = service.getTrackPointCreator(); trackPointCreator.setClock(startTime); Track.Id trackId = service.startNewTrack(); - trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager); + SensorManager sensorManager = trackPointCreator.getSensorManager(); sensorManager.sensorDataSet.add(new AggregatorRunning("", "")); + sensorManager.sensorDataSet.barometer = null; // when String sensor1 = "2020-02-02T02:02:03Z"; @@ -674,4 +672,12 @@ public class TrackRecordingServiceRecordingTest { .setSensorDistance(Distance.of(0)) ), TestDataUtil.getTrackPoints(contentProviderUtils, trackId)); } + + private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) { + AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class); + Mockito.when(barometer.hasValue()).thenReturn(true); + Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain)); + + trackPointCreator.getSensorManager().sensorDataSet.barometer = barometer; + } } diff --git a/src/androidTest/res/raw/csv_export.csv b/src/androidTest/res/raw/csv_export.csv index cdefb81a1..f4344cd20 100644 --- a/src/androidTest/res/raw/csv_export.csv +++ b/src/androidTest/res/raw/csv_export.csv @@ -8,6 +8,6 @@ "2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,, "2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,, "2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,, -"2020-02-02T03:03:30+01:00","IDLE",,,,,,,,,,,, +"2020-02-02T03:03:30+01:00","IDLE",,,,,,,0,0,,,, "2020-02-02T03:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,, "2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,, \ No newline at end of file diff --git a/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java b/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java new file mode 100644 index 000000000..a33047393 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/data/models/AltitudeGainLoss.java @@ -0,0 +1,3 @@ +package de.dennisguse.opentracks.data.models; + +public record AltitudeGainLoss(float gain_m, float loss_m) {} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java b/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java deleted file mode 100644 index 63587e033..000000000 --- a/src/main/java/de/dennisguse/opentracks/sensors/AltitudeSumManager.java +++ /dev/null @@ -1,108 +0,0 @@ -package de.dennisguse.opentracks.sensors; - -import android.content.Context; -import android.os.Handler; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.data.models.TrackPoint; -import de.dennisguse.opentracks.sensors.driver.BarometerInternal; - -/** - * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). - */ -public class AltitudeSumManager implements SensorConnector { - - private static final String TAG = AltitudeSumManager.class.getSimpleName(); - - private final BarometerInternal driver; - - private AtmosphericPressure lastAcceptedSensorValue; - - private AtmosphericPressure lastSeenSensorValue; - - private Float altitudeGain_m; - private Float altitudeLoss_m; - - public AltitudeSumManager() { - driver = new BarometerInternal(); - } - - @VisibleForTesting - public AltitudeSumManager(BarometerInternal mock) { - this.driver = mock; - } - - public void start(Context context, Handler handler) { - driver.connect(context, handler, this); - - lastAcceptedSensorValue = null; - reset(); - } - - public void stop(Context context) { - Log.d(TAG, "Stop"); - - driver.disconnect(context); - reset(); - } - - public void fill(@NonNull TrackPoint trackPoint) { - trackPoint.setAltitudeGain(altitudeGain_m); - trackPoint.setAltitudeLoss(altitudeLoss_m); - } - - @Nullable - public Float getAltitudeGain_m() { - return driver.isConnected() ? altitudeGain_m : null; - } - - @VisibleForTesting - public void setAltitudeGain_m(float altitudeGain_m) { - this.altitudeGain_m = altitudeGain_m; - } - - @Nullable - public Float getAltitudeLoss_m() { - return driver.isConnected() ? altitudeLoss_m : null; - } - - @VisibleForTesting - public void setAltitudeLoss_m(float altitudeLoss_m) { - this.altitudeLoss_m = altitudeLoss_m; - } - - public void reset() { - Log.d(TAG, "Reset"); - altitudeGain_m = null; - altitudeLoss_m = null; - } - - public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { - if (lastAcceptedSensorValue == null) { - lastAcceptedSensorValue = currentSensorValue; - lastSeenSensorValue = currentSensorValue; - return; - } - - altitudeGain_m = altitudeGain_m != null ? altitudeGain_m : 0; - altitudeLoss_m = altitudeLoss_m != null ? altitudeLoss_m : 0; - - PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, lastSeenSensorValue, currentSensorValue); - if (altitudeChange != null) { - altitudeGain_m += altitudeChange.getAltitudeGain_m(); - - altitudeLoss_m += altitudeChange.getAltitudeLoss_m(); - - lastAcceptedSensorValue = altitudeChange.currentSensorValue(); - } - - lastSeenSensorValue = currentSensorValue; - - Log.v(TAG, "altitude gain: " + altitudeGain_m + ", altitude loss: " + altitudeLoss_m); - } -} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java new file mode 100644 index 000000000..40e1749f9 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/GainManager.java @@ -0,0 +1,46 @@ +package de.dennisguse.opentracks.sensors; + +import android.content.Context; +import android.os.Handler; +import android.util.Log; + +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.driver.BarometerInternal; +import de.dennisguse.opentracks.sensors.sensorData.AggregatorBarometer; +import de.dennisguse.opentracks.sensors.sensorData.Raw; + +/** + * Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer). + */ +public class GainManager implements SensorConnector { + + private static final String TAG = GainManager.class.getSimpleName(); + + private final BarometerInternal driver; + + private final SensorManager.SensorDataChangedObserver listener; + + public GainManager(SensorManager.SensorDataChangedObserver listener) { + this.listener = listener; + driver = new BarometerInternal(); + } + + public void start(Context context, Handler handler) { + driver.connect(context, handler, this); + + if (driver.isConnected()) { + listener.onConnect(new AggregatorBarometer("internal")); + } + } + + public void stop(Context context) { + Log.d(TAG, "Stop"); + + driver.disconnect(context); + listener.onDisconnect(new AggregatorBarometer("internal")); + } + + public void onSensorValueChanged(AtmosphericPressure currentSensorValue) { + listener.onChange(new Raw<>(currentSensorValue)); + } +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java index 1f1535a6f..385d69ca7 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java @@ -48,7 +48,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange private BluetoothRemoteSensorManager bluetoothSensorManager; - private AltitudeSumManager altitudeSumManager; + private GainManager altitudeSumManager; private GPSManager gpsManager; @@ -58,7 +58,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange public void start(Context context, Handler handler) { gpsManager = new GPSManager(observer); //TODO Pass listener - altitudeSumManager = new AltitudeSumManager(); + altitudeSumManager = new GainManager(listener); bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener); onSharedPreferenceChanged(null, null); @@ -82,7 +82,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange } public SensorDataSet fill(TrackPoint trackPoint) { - altitudeSumManager.fill(trackPoint); sensorDataSet.fillTrackPoint(trackPoint); return new SensorDataSet(sensorDataSet); } @@ -93,7 +92,6 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange return; } sensorDataSet.reset(); - altitudeSumManager.reset(); } @Deprecated @@ -108,13 +106,13 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange @Deprecated @VisibleForTesting - public AltitudeSumManager getAltitudeSumManager() { + public GainManager getAltitudeSumManager() { return altitudeSumManager; } @Deprecated @VisibleForTesting - public void setAltitudeSumManager(AltitudeSumManager altitudeSumManager) { + public void setAltitudeSumManager(GainManager altitudeSumManager) { this.altitudeSumManager = altitudeSumManager; } diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java index 8a405f720..913be0223 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java @@ -11,7 +11,7 @@ import android.util.Log; import java.util.concurrent.TimeUnit; import de.dennisguse.opentracks.data.models.AtmosphericPressure; -import de.dennisguse.opentracks.sensors.AltitudeSumManager; +import de.dennisguse.opentracks.sensors.GainManager; public class BarometerInternal { @@ -19,7 +19,7 @@ public class BarometerInternal { private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5); - private AltitudeSumManager observer; + private GainManager observer; private final SensorEventListener listener = new SensorEventListener() { @Override @@ -38,14 +38,13 @@ public class BarometerInternal { } }; - public void connect(Context context, Handler handler, AltitudeSumManager observer) { + public void connect(Context context, Handler handler, GainManager observer) { this.observer = observer; SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); if (pressureSensor == null) { Log.w(TAG, "No pressure sensor available."); this.observer = null; - return; } if (sensorManager.registerListener(listener, pressureSensor, SAMPLING_PERIOD, handler)) { diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java new file mode 100644 index 000000000..7e5ed7c57 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorBarometer.java @@ -0,0 +1,46 @@ +package de.dennisguse.opentracks.sensors.sensorData; + +import androidx.annotation.NonNull; + +import de.dennisguse.opentracks.data.models.Altitude; +import de.dennisguse.opentracks.data.models.AltitudeGainLoss; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; +import de.dennisguse.opentracks.sensors.PressureSensorUtils; + +public class AggregatorBarometer extends Aggregator { + + private AtmosphericPressure lastAcceptedSensorValue; + + public AggregatorBarometer(String sensorAddress) { + super(sensorAddress); + } + + @Override + protected void computeValue(Raw current) { + if (previous == null) { + lastAcceptedSensorValue = current.value(); + value = getNoneValue(); + return; + } + + PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, previous.value(), current.value()); + if (altitudeChange != null) { + value = new AltitudeGainLoss(value.gain_m() + altitudeChange.getAltitudeGain_m(), value.loss_m() + altitudeChange.getAltitudeLoss_m()); + + lastAcceptedSensorValue = altitudeChange.currentSensorValue(); + } + } + + @NonNull + @Override + protected AltitudeGainLoss getNoneValue() { + return new AltitudeGainLoss(0f, 0f); + } + + @Override + public void reset() { + value = getNoneValue(); + } + + public record Data(Altitude gain, Altitude loss) {} +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java index 50ed66799..485760817 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/SensorDataSet.java @@ -1,11 +1,13 @@ package de.dennisguse.opentracks.sensors.sensorData; +import android.util.Log; import android.util.Pair; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import de.dennisguse.opentracks.data.models.AtmosphericPressure; import de.dennisguse.opentracks.data.models.Cadence; import de.dennisguse.opentracks.data.models.HeartRate; import de.dennisguse.opentracks.data.models.Power; @@ -36,6 +38,9 @@ public final class SensorDataSet { @VisibleForTesting public AggregatorRunning runningDistanceSpeedCadence; + @VisibleForTesting + public AggregatorBarometer barometer; + public SensorDataSet() { } @@ -45,6 +50,7 @@ public final class SensorDataSet { this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed; this.cyclingPower = toCopy.cyclingPower; this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence; + this.barometer = toCopy.barometer; } public Pair getHeartRate() { @@ -113,8 +119,12 @@ public final class SensorDataSet { this.cyclingPower.add((Raw) data); return; } + if (value instanceof AtmosphericPressure) { + this.barometer.add((Raw) data); + return; + } - throw new UnsupportedOperationException(data.getClass().getCanonicalName()); + throw new UnsupportedOperationException(data.getClass().getCanonicalName() + " " + data.value().getClass().getCanonicalName()); } public void remove(@NonNull Aggregator type) { @@ -122,11 +132,13 @@ public final class SensorDataSet { } public void clear() { + Log.i(TAG, "Removing all aggregators"); this.heartRate = null; this.cyclingCadence = null; this.cyclingDistanceSpeed = null; this.cyclingPower = null; this.runningDistanceSpeedCadence = null; + this.barometer = null; } public void fillTrackPoint(TrackPoint trackPoint) { @@ -153,17 +165,27 @@ public final class SensorDataSet { if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) { trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().distance()); } + + if (barometer != null && barometer.hasValue()) { + trackPoint.setAltitudeGain(barometer.getValue().gain_m()); + trackPoint.setAltitudeLoss(barometer.getValue().loss_m()); + } } public void reset() { + Log.i(TAG, "Resetting data"); + if (heartRate != null) heartRate.reset(); if (cyclingCadence != null) cyclingCadence.reset(); if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset(); if (cyclingPower != null) cyclingPower.reset(); if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.reset(); + if (barometer != null) barometer.reset(); } private void set(@NonNull Aggregator type, @Nullable Aggregator sensorData) { + Log.i(TAG, "Setting aggregator " + type.getClass().getCanonicalName()); + if (type instanceof AggregatorHeartRate) { heartRate = (AggregatorHeartRate) sensorData; return; @@ -184,6 +206,10 @@ public final class SensorDataSet { runningDistanceSpeedCadence = (AggregatorRunning) sensorData; return; } + if (type instanceof AggregatorBarometer) { + barometer = (AggregatorBarometer) sensorData; + return; + } throw new UnsupportedOperationException(type.getClass().getCanonicalName()); } diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java index 49384b291..f779e6ddc 100644 --- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java +++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java @@ -34,7 +34,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh @NonNull private Clock clock = new MonotonicClock(); - private SensorManager sensorManager; + private final SensorManager sensorManager; public TrackPointCreator(Callback service) { this.service = service; @@ -64,7 +64,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh return sensorManager.fill(trackPoint); } - public synchronized void stop() { + public void stop() { sensorManager.stop(context); this.context = null; } @@ -139,11 +139,6 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh return sensorManager; } - @VisibleForTesting - public void setSensorManager(SensorManager sensorManager) { - this.sensorManager = sensorManager; - } - @VisibleForTesting public void setClock(@NonNull String time) { this.clock = Clock.fixed(Instant.parse(time), ZoneId.of("CET")); From caf8b5526d91253e8a3e8281b75b7a0bb3a30e60 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Tue, 21 Nov 2023 07:18:10 +0100 Subject: [PATCH 11/11] Bugfix: BarometerInternal shouldn't send data after disconnect(). Part of #1424. --- .../dennisguse/opentracks/sensors/driver/BarometerInternal.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java index 913be0223..4fc47b116 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/driver/BarometerInternal.java @@ -39,7 +39,6 @@ public class BarometerInternal { }; public void connect(Context context, Handler handler, GainManager observer) { - this.observer = observer; SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE); if (pressureSensor == null) { @@ -58,6 +57,7 @@ public class BarometerInternal { public void disconnect(Context context) { SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); sensorManager.unregisterListener(listener); + observer = null; } public boolean isConnected() {