1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-26 20:06:52 +01:00

Huawei: calculate HR Zones for walking and running workouts.

This commit is contained in:
Me7c7 2024-10-27 20:53:44 +02:00 committed by José Rebelo
parent d038c589c1
commit a7c19c8190
2 changed files with 85 additions and 1 deletions

View File

@ -16,7 +16,7 @@ public class HeartRateZonesConfig {
public static final int MAXIMUM_HEART_RATE = 220;
private final int configType;
private int calculateMethod = 0;
private int calculateMethod = 0; // 0 - MHR, 1 - HRR, 3 - LTHR
private int maxHRThreshold;
private int restHeartRate = DEFAULT_REST_HEART_RATE;
@ -231,4 +231,35 @@ public class HeartRateZonesConfig {
return LTHRThresholdHeartRate > 0 && LTHRAnaerobic > 0 && LTHRLactate > 0 && LTHRAdvancedAerobic > 0 && LTHRBasicAerobic > 0 && LTHRWarmUp > 0;
}
private int getZoneForHR(int heartRate, int zone5Threshold, int zone4Threshold, int zone3Threshold, int zone2Threshold, int zone1Threshold) {
if (heartRate >= MAXIMUM_HEART_RATE) {
return -1;
}
if (heartRate >= zone5Threshold) {
return 4;
}
if (heartRate >= zone4Threshold) {
return 3;
}
if (heartRate >= zone3Threshold) {
return 2;
}
if (heartRate >= zone2Threshold) {
return 1;
}
return heartRate >= zone1Threshold ? 0 : -1;
}
public int getMHRZone(int heartRate) {
return getZoneForHR(heartRate, MHRExtreme, MHRAnaerobic, MHRAerobic, MHRFatBurning, MHRWarmUp);
}
public int getHHRZone(int heartRate) {
return getZoneForHR(heartRate, HRRAdvancedAnaerobic, HRRBasicAnaerobic, HRRLactate, HRRAdvancedAerobic, HRRBasicAerobic);
}
public int getLTHRZone(int heartRate) {
return getZoneForHR(heartRate, LTHRAnaerobic, LTHRLactate, LTHRAdvancedAerobic, LTHRBasicAerobic, LTHRWarmUp);
}
}

View File

@ -36,10 +36,13 @@ import de.greenrobot.dao.query.QueryBuilder;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryProgressEntry;
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryTableRowEntry;
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.entries.ActivitySummaryValue;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HeartRateZonesConfig;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiSportHRZones;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.Workout;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
@ -58,6 +61,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryEntries;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
@ -486,7 +490,36 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
int sumAltitudeUp = 0;
int sumAltitudeDown = 0;
//NOTE: The method of retrieving HR zones from the Huawei watch is not discovered. It may not return zones.
// So they are calculated based on config. Enabled only for running and walking activities for testing.
// Currently only calculated zones based on MHR.
// TODO: Use other methods after the configuration will be implemented. Use calculateMethod on HeartRateZonesConfig class.
// TODO: Enable for other workout types
HeartRateZonesConfig HRZonesCfg = null;
if( type == ActivityKind.WALKING || type == ActivityKind.RUNNING) {
ActivityUser activityUser = new ActivityUser();
HuaweiSportHRZones hrSportZones = new HuaweiSportHRZones(activityUser.getAge());
HRZonesCfg = hrSportZones.getHRZonesConfigByType(HeartRateZonesConfig.TYPE_UPRIGHT);
}
int dataDelta = 5;
if (dataSamples.size() >= 2 && dataSamples.get(1).getTimestamp() - dataSamples.get(0).getTimestamp() >= 40) {
dataDelta = 60;
}
int[] HRZones = new int[5];
int dataIdx = 0;
for (HuaweiWorkoutDataSample dataSample : dataSamples) {
if(HRZonesCfg != null) {
int zoneIdx = HRZonesCfg.getMHRZone(dataSample.getHeartRate() & 0xFF);
if (zoneIdx != -1 && dataIdx < (dataSamples.size() - 1)) {
HRZones[zoneIdx] += dataDelta;
}
dataIdx++;
}
if (dataSample.getSpeed() != -1) {
speed += dataSample.getSpeed();
speedCount += 1;
@ -589,6 +622,25 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
unknownData = true;
}
if(HRZonesCfg != null) {
final double totalTime = Arrays.stream(HRZones).sum();
final List<String> zoneOrder = Arrays.asList(ActivitySummaryEntries.HR_ZONE_WARM_UP, ActivitySummaryEntries.HR_ZONE_FAT_BURN, ActivitySummaryEntries.HR_ZONE_AEROBIC, ActivitySummaryEntries.HR_ZONE_ANAEROBIC, ActivitySummaryEntries.HR_ZONE_EXTREME);
for (int i = 0; i < zoneOrder.size(); i++) {
double timeInZone = HRZones[i];
LOG.info("Zone: {} {}", zoneOrder.get(i), timeInZone);
summaryData.add(
zoneOrder.get(i),
new ActivitySummaryProgressEntry(
timeInZone,
ActivitySummaryEntries.UNIT_SECONDS,
(int) (100 * timeInZone / totalTime)
)
);
}
}
// Average the things that should be averaged
if (speedCount > 0)
speed = speed / speedCount;
@ -749,6 +801,7 @@ public class HuaweiWorkoutGbParser implements ActivitySummaryParser {
summaryData.add(ActivitySummaryEntries.ELEVATION_LOSS, elevationLoss / 10.0f, ActivitySummaryEntries.UNIT_METERS);
}
final LinkedHashMap<String, ActivitySummaryTableRowEntry> pacesTable = new LinkedHashMap<>();
pacesTable.put("paces_table",