package nodomain.freeyourgadget.gadgetbridge.devices.huawei; // TODO: make this configurable // TODO: retrieve from device and set real Rest HR // NOTE: algorithms used in this class are generic. So this data can be used with other devices. // We can move this class to global scope. public class HeartRateZonesConfig { public static final int TYPE_UPRIGHT = 1; public static final int TYPE_SITTING = 2; public static final int TYPE_SWIMMING = 3; public static final int TYPE_OTHER = 4; public static final int CALCULATE_METHOD_MHR = 0; public static final int CALCULATE_METHOD_HRR = 1; public static final int CALCULATE_METHOD_LTHR = 3; private static final int DEFAULT_REST_HEART_RATE = 60; public static final int MAXIMUM_HEART_RATE = 220; private final int configType; private int calculateMethod = CALCULATE_METHOD_MHR; // 0 - MHR, 1 - HRR, 3 - LTHR private int maxHRThreshold; private int restHeartRate = DEFAULT_REST_HEART_RATE; private boolean warningEnable; private int warningHRLimit; //MHR percentage private int MHRExtreme; private int MHRAnaerobic; private int MHRAerobic; private int MHRFatBurning; private int MHRWarmUp; //HRR percentage private int HRRAdvancedAnaerobic; private int HRRBasicAnaerobic; private int HRRLactate; private int HRRAdvancedAerobic; private int HRRBasicAerobic; //LTHR percentage private int LTHRAnaerobic; private int LTHRLactate; private int LTHRAdvancedAerobic; private int LTHRBasicAerobic; private int LTHRWarmUp; private int LTHRThresholdHeartRate; public HeartRateZonesConfig(int type, int age) { this.configType = type; this.warningEnable = true; this.warningHRLimit = MAXIMUM_HEART_RATE - age; resetHRZones(age); } public void resetHRZones(int age) { this.maxHRThreshold = (MAXIMUM_HEART_RATE - age) - getHRCorrection(); resetHRRZonesConfig(); resetMHRZonesConfig(); //NOTE: LTHR only supported for TYPE_UPRIGHT if (this.configType == TYPE_UPRIGHT) { resetLTHRZonesConfig(); } } private void resetLTHRZonesConfig() { this.LTHRThresholdHeartRate = Math.round(((float) this.restHeartRate) + (((float) ((this.maxHRThreshold - this.restHeartRate) * 85)) / 100.0f)); this.LTHRAnaerobic = Math.round(((float) (this.LTHRThresholdHeartRate * 102)) / 100.0f); this.LTHRLactate = Math.round(((float) (this.LTHRThresholdHeartRate * 97)) / 100.0f); this.LTHRAdvancedAerobic = Math.round(((float) (this.LTHRThresholdHeartRate * 89)) / 100.0f); this.LTHRBasicAerobic = Math.round(((float) (this.LTHRThresholdHeartRate * 80)) / 100.0f); this.LTHRWarmUp = Math.round(((float) (this.LTHRThresholdHeartRate * 67)) / 100.0f); } private void resetMHRZonesConfig() { this.MHRExtreme = Math.round(((float) (this.maxHRThreshold * 90)) / 100.0f); this.MHRAnaerobic = Math.round(((float) (this.maxHRThreshold * 80)) / 100.0f); this.MHRAerobic = Math.round(((float) (this.maxHRThreshold * 70)) / 100.0f); this.MHRFatBurning = Math.round(((float) (this.maxHRThreshold * 60)) / 100.0f); this.MHRWarmUp = Math.round(((float) (this.maxHRThreshold * 50)) / 100.0f); } private void resetHRRZonesConfig() { int calcHR = this.maxHRThreshold - this.restHeartRate; this.HRRAdvancedAnaerobic = Math.round(((float) (calcHR * 95)) / 100.0f) + this.restHeartRate; this.HRRBasicAnaerobic = Math.round(((float) (calcHR * 88)) / 100.0f) + this.restHeartRate; this.HRRLactate = Math.round(((float) (calcHR * 84)) / 100.0f) + this.restHeartRate; this.HRRAdvancedAerobic = Math.round(((float) (calcHR * 74)) / 100.0f) + this.restHeartRate; this.HRRBasicAerobic = Math.round(((float) (calcHR * 59)) / 100.0f) + this.restHeartRate; } //TODO: I am not sure about this. But it looks correct. private int getHRCorrection() { switch (this.configType) { case TYPE_SITTING: return 6; case TYPE_SWIMMING: return 10; case TYPE_OTHER: return 5; default: return 0; } } public int getCalculateMethod() { return this.calculateMethod; } public boolean getWarningEnable() { return this.warningEnable; } public int getWarningHRLimit() { return this.warningHRLimit; } public int getMaxHRThreshold() { return this.maxHRThreshold; } public int getRestHeartRate() { return this.restHeartRate; } public int getMHRWarmUp() { return this.MHRWarmUp; } public int getMHRFatBurning() { return this.MHRFatBurning; } public int getMHRAerobic() { return this.MHRAerobic; } public int getMHRAnaerobic() { return this.MHRAnaerobic; } public int getMHRExtreme() { return this.MHRExtreme; } public int getHRRBasicAerobic() { return this.HRRBasicAerobic; } public int getHRRAdvancedAerobic() { return this.HRRAdvancedAerobic; } public int getHRRLactate() { return this.HRRLactate; } public int getHRRBasicAnaerobic() { return this.HRRBasicAnaerobic; } public int getHRRAdvancedAnaerobic() { return this.HRRAdvancedAnaerobic; } public int getLTHRThresholdHeartRate() { return this.LTHRThresholdHeartRate; } public int getLTHRAnaerobic() { return this.LTHRAnaerobic; } public int getLTHRLactate() { return this.LTHRLactate; } public int getLTHRAdvancedAerobic() { return this.LTHRAdvancedAerobic; } public int getLTHRBasicAerobic() { return this.LTHRBasicAerobic; } public int getLTHRWarmUp() { return this.LTHRWarmUp; } private boolean checkValue(int val) { return val >= 0 && val < MAXIMUM_HEART_RATE; } public boolean isValid() { return checkValue(this.configType) && checkValue(this.calculateMethod) && checkValue(this.warningHRLimit) && checkValue(this.maxHRThreshold) && checkValue(this.restHeartRate) && checkValue(this.MHRWarmUp) && checkValue(this.MHRFatBurning) && checkValue(this.MHRAerobic) && checkValue(this.MHRAnaerobic) && checkValue(this.MHRExtreme) && checkValue(this.HRRBasicAerobic) && checkValue(this.HRRAdvancedAerobic) && checkValue(this.HRRLactate) && checkValue(this.HRRBasicAnaerobic) && checkValue(this.HRRAdvancedAnaerobic) && checkValue(this.LTHRThresholdHeartRate) && checkValue(this.LTHRAnaerobic) && checkValue(this.LTHRLactate) && checkValue(this.LTHRAdvancedAerobic) && checkValue(this.LTHRBasicAerobic) && checkValue(this.LTHRWarmUp) && this.warningHRLimit > 0; } public boolean hasValidMHRData() { return MHRWarmUp > 0 && MHRFatBurning > 0 && MHRAerobic > 0 && MHRAnaerobic > 0 && MHRExtreme > 0 && maxHRThreshold > 0; } public boolean hasValidHRRData() { return restHeartRate > 0 && HRRBasicAerobic > 0 && HRRAdvancedAerobic > 0 && HRRLactate > 0 && HRRBasicAnaerobic > 0 && HRRAdvancedAnaerobic > 0; } public boolean hasValidLTHRData() { 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); } public int getZoneByMethod(int heartRate, int method) { if(method == CALCULATE_METHOD_LTHR) { return getLTHRZone(heartRate); } else if(method == CALCULATE_METHOD_MHR) { return getMHRZone(heartRate); } return getHHRZone(heartRate); } public static boolean isCalculateMethodValidFroType(int type, int method) { if(method == CALCULATE_METHOD_LTHR && type == TYPE_UPRIGHT) { return true; } return (method == CALCULATE_METHOD_MHR) || (method == CALCULATE_METHOD_HRR); } }