Heartrate: ignore "0" if sent from sensor as this is an invalid value.

On import "0" is ignored.
This commit is contained in:
Dennis Guse
2025-12-11 18:32:38 +01:00
parent 708cbf0b2a
commit ae4d2da262
7 changed files with 58 additions and 7 deletions
@@ -29,7 +29,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
private static final int DATABASE_VERSION = 42;
private static final int DATABASE_VERSION = 43;
private final Context context;
@@ -86,6 +86,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 40 -> upgradeFrom39to40(db);
case 41 -> upgradeFrom40to41(db);
case 42 -> upgradeFrom41to42(db);
case 43 -> upgradeFrom42to43(db);
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
}
}
@@ -115,6 +116,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 39 -> downgradeFrom40to39(db);
case 40 -> downgradeFrom41to40(db);
case 41 -> downgradeFrom42to41(db);
case 42 -> downgradeFrom43to42(db);
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
}
}
@@ -794,4 +796,18 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.setTransactionSuccessful();
db.endTransaction();
}
private void upgradeFrom42to43(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("UPDATE trackpoints SET heartrate = null WHERE heartrate = 0.0");
db.setTransactionSuccessful();
db.endTransaction();
}
private void downgradeFrom43to42(SQLiteDatabase db) {
// Downgrade not possible: invalid data replaced with null.
}
}
@@ -12,7 +12,10 @@ public record HeartRate(float value) {
@Nullable
public static HeartRate ofOrNull(Float value) {
if (value == null) return null;
return new HeartRate(value);
HeartRate rate = of(value);
if (rate.isValid()) return rate;
return null;
}
public float getBPM() {
@@ -119,6 +119,19 @@ public record TrackPoint(
);
}
public TrackPoint removeHeartRate() {
return new TrackPoint(
id,
type,
position,
sensorDistance,
null,
cadence,
power,
altitudeGainLoss
);
}
public TrackPoint with(Position newPosition) {
return new TrackPoint(
id,
@@ -356,7 +356,7 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
HeartRate parsedHeartRate = null;
if (heartrate != null) {
try {
parsedHeartRate = HeartRate.of(Float.parseFloat(heartrate));
parsedHeartRate = HeartRate.ofOrNull(Float.parseFloat(heartrate));
} catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), e);
}
@@ -190,6 +190,15 @@ public class TrackImporter {
}
trackPoints.set(i, newCurrent);
}
}
for (int i = 0; i < trackPoints.size(); i++) {
TrackPoint current = trackPoints.get(i);
if (!current.heartRate().isValid()) {
current = current.removeHeartRate();
trackPoints.set(i, current);
}
}
}
@@ -1,6 +1,7 @@
package de.dennisguse.opentracks.sensors.driver;
import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -13,6 +14,8 @@ import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID;
public class HeartRateBluetooth implements BluetoothDriver.BluetoothParser<HeartRate> {
private static final String TAG = BluetoothHandlerManagerHeartRate.class.getSimpleName();
public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID(
new UUID(0x180D00001000L, 0x800000805f9b34fbL),
new UUID(0x2A3700001000L, 0x800000805f9b34fbL)
@@ -35,7 +38,16 @@ public class HeartRateBluetooth implements BluetoothDriver.BluetoothParser<Heart
@Override
public HeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) {
return parseHeartRate(characteristic);
HeartRate heartRate = parseHeartRate(characteristic);
if (heartRate == null) return null;
if (heartRate.value() == 0.0) {
Log.d(TAG, "Invalid heartrate received. Ignoring.");
return null;
}
return heartRate;
}
@VisibleForTesting
@@ -10,8 +10,6 @@ public class AggregatorHeartRate extends Aggregator<HeartRate, HeartRate> {
@Override
protected void computeValue(Raw<HeartRate> current) {
if (current.value().isValid()) {
this.output = current.value();
}
this.output = current.value();
}
}