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 String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
private static final int DATABASE_VERSION = 42; private static final int DATABASE_VERSION = 43;
private final Context context; private final Context context;
@@ -86,6 +86,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 40 -> upgradeFrom39to40(db); case 40 -> upgradeFrom39to40(db);
case 41 -> upgradeFrom40to41(db); case 41 -> upgradeFrom40to41(db);
case 42 -> upgradeFrom41to42(db); case 42 -> upgradeFrom41to42(db);
case 43 -> upgradeFrom42to43(db);
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion); default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
} }
} }
@@ -115,6 +116,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 39 -> downgradeFrom40to39(db); case 39 -> downgradeFrom40to39(db);
case 40 -> downgradeFrom41to40(db); case 40 -> downgradeFrom41to40(db);
case 41 -> downgradeFrom42to41(db); case 41 -> downgradeFrom42to41(db);
case 42 -> downgradeFrom43to42(db);
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion); default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
} }
} }
@@ -794,4 +796,18 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.setTransactionSuccessful(); db.setTransactionSuccessful();
db.endTransaction(); 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 @Nullable
public static HeartRate ofOrNull(Float value) { public static HeartRate ofOrNull(Float value) {
if (value == null) return null; if (value == null) return null;
return new HeartRate(value); HeartRate rate = of(value);
if (rate.isValid()) return rate;
return null;
} }
public float getBPM() { 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) { public TrackPoint with(Position newPosition) {
return new TrackPoint( return new TrackPoint(
id, id,
@@ -356,7 +356,7 @@ public class GPXTrackImporter extends DefaultHandler implements XMLImporter.Trac
HeartRate parsedHeartRate = null; HeartRate parsedHeartRate = null;
if (heartrate != null) { if (heartrate != null) {
try { try {
parsedHeartRate = HeartRate.of(Float.parseFloat(heartrate)); parsedHeartRate = HeartRate.ofOrNull(Float.parseFloat(heartrate));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse heart rate: %s", heartrate)), 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); 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; package de.dennisguse.opentracks.sensors.driver;
import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
@@ -13,6 +14,8 @@ import de.dennisguse.opentracks.sensors.ServiceMeasurementUUID;
public class HeartRateBluetooth implements BluetoothDriver.BluetoothParser<HeartRate> { public class HeartRateBluetooth implements BluetoothDriver.BluetoothParser<HeartRate> {
private static final String TAG = BluetoothHandlerManagerHeartRate.class.getSimpleName();
public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID( public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID(
new UUID(0x180D00001000L, 0x800000805f9b34fbL), new UUID(0x180D00001000L, 0x800000805f9b34fbL),
new UUID(0x2A3700001000L, 0x800000805f9b34fbL) new UUID(0x2A3700001000L, 0x800000805f9b34fbL)
@@ -35,7 +38,16 @@ public class HeartRateBluetooth implements BluetoothDriver.BluetoothParser<Heart
@Override @Override
public HeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, BluetoothGattCharacteristic characteristic) { 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 @VisibleForTesting
@@ -10,8 +10,6 @@ public class AggregatorHeartRate extends Aggregator<HeartRate, HeartRate> {
@Override @Override
protected void computeValue(Raw<HeartRate> current) { protected void computeValue(Raw<HeartRate> current) {
if (current.value().isValid()) {
this.output = current.value(); this.output = current.value();
} }
}
} }