mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-02 09:33:06 +02:00
Update formulas in CalorieUtils
This commit is contained in:
@@ -20,7 +20,6 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils.LocationIt
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.stats.TripStatisticsUpdater;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
@@ -36,7 +35,7 @@ public class CalorieUtils {
|
||||
* Activity types.
|
||||
*/
|
||||
public enum ActivityType {
|
||||
CYCLING, FOOT, INVALID
|
||||
CYCLING, RUNNING, WALKING, INVALID
|
||||
}
|
||||
|
||||
private CalorieUtils() {}
|
||||
@@ -47,15 +46,10 @@ public class CalorieUtils {
|
||||
*/
|
||||
private static final double RESTING_VO2 = 3.5;
|
||||
|
||||
/**
|
||||
* Ratio to convert liter to kcal.
|
||||
*/
|
||||
private static final double L_TO_KCAL = 5.0;
|
||||
|
||||
/**
|
||||
* Standard gravity in meters per second squared (m/s^2).
|
||||
*/
|
||||
private static final double EARTH_GRAVITY = 9.80665;
|
||||
private static final double EARTH_GRAVITY = 9.81;
|
||||
|
||||
/**
|
||||
* Lumped constant for all frictional losses (tires, bearings, chain).
|
||||
@@ -63,22 +57,10 @@ public class CalorieUtils {
|
||||
private static final double K1 = 0.0053;
|
||||
|
||||
/**
|
||||
* Lumped constant for aerodynamic drag.
|
||||
* Lumped constant for aerodynamic drag (kg/m)
|
||||
*/
|
||||
private static final double K2 = 0.185;
|
||||
|
||||
/**
|
||||
* Critical speed running in meters per minute. Converts 4.5 miles per hour to
|
||||
* meters per minute. 4 miles per hour is regarded as the max speed of walking
|
||||
* and 5 miles per hour is regarded as the minimal speed of running, so we use
|
||||
* 4.5 miles per hour as the critical speed. <a href=
|
||||
* "http://www.ideafit.com/fitness-library/calculating-caloric-expenditure-0"
|
||||
* >Reference</a>
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final double CRTICAL_SPEED_RUNNING = 4.5 * UnitConversions.MI_TO_KM
|
||||
* UnitConversions.KM_TO_M / UnitConversions.HR_TO_MIN;
|
||||
|
||||
/**
|
||||
* Gets the activity type.
|
||||
*
|
||||
@@ -89,9 +71,10 @@ public class CalorieUtils {
|
||||
if (activityType == null || activityType.equals("")) {
|
||||
return ActivityType.INVALID;
|
||||
}
|
||||
if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.WALK)
|
||||
|| TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.RUN)) {
|
||||
return ActivityType.FOOT;
|
||||
if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.WALK)) {
|
||||
return ActivityType.WALKING;
|
||||
} else if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.RUN)) {
|
||||
return ActivityType.RUNNING;
|
||||
} else if (TrackIconUtils.getIconValue(context, activityType).equals(TrackIconUtils.BIKE)) {
|
||||
return ActivityType.CYCLING;
|
||||
}
|
||||
@@ -122,6 +105,7 @@ public class CalorieUtils {
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT);
|
||||
double weight = PreferencesUtils.getFloat(
|
||||
context, R.string.stats_weight_key, PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
tripStatisticsUpdater.addLocation(
|
||||
iterator.next(), recordingDistanceInterval, true, activityType, weight);
|
||||
@@ -144,137 +128,83 @@ public class CalorieUtils {
|
||||
if (activityType == ActivityType.INVALID) {
|
||||
return 0.0;
|
||||
}
|
||||
return ActivityType.CYCLING == activityType ? getCyclingCalorie(start, stop, grade, weight)
|
||||
: getFootCalorie(start, stop, grade, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cycling calorie in kcal between two locations.
|
||||
*
|
||||
* @param start the start location
|
||||
* @param stop the stop location
|
||||
* @param grade the grade
|
||||
* @param weight the weight in kilogram of the rider plus bike
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getCyclingCalorie(Location start, Location stop, double grade, double weight) {
|
||||
// Gets duration in seconds
|
||||
double duration = (double) (stop.getTime() - start.getTime()) * UnitConversions.MS_TO_S;
|
||||
// Get speed in meters per second
|
||||
double speed = (start.getSpeed() + stop.getSpeed()) / 2.0;
|
||||
|
||||
if (grade < 0) {
|
||||
grade = 0.0;
|
||||
}
|
||||
return getCyclingCalorie(speed, grade, weight, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cycling calorie in kcal using the following equation: <br>
|
||||
* P = g * m * Vg * (K1 + s) + K2 * (Va)^2 * Vg
|
||||
* <ul>
|
||||
* <li>P - Power in watt (Joule/second)</li>
|
||||
* <li>g - gravity, using 9.80665 meter/second^2</li>
|
||||
* <li>m - mass of the rider plus bike in kilogram</li>
|
||||
* <li>s - surface grade</li>
|
||||
* <li>Vg - ground relative speed in meter/second</li>
|
||||
* <li>Va - air relative speed. We assume the air speed is zero, so Va is
|
||||
* equal to Vg.</li>
|
||||
* <li>K1 - a constant which represents frictional losses, using 0.0053.</li>
|
||||
* <li>K2 - a constant representing aerodynamic drag, using 0.185.</li>
|
||||
* <li></li>
|
||||
* </ul>
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param grade the grade
|
||||
* @param weight weight in kilogram of the rider plus bike
|
||||
* @param duration the duration in seconds
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getCyclingCalorie(double speed, double grade, double weight, double duration) {
|
||||
// Get the power in watt (Joule/second)
|
||||
double power = EARTH_GRAVITY * weight * speed * (K1 + grade) + K2 * (speed * speed * speed);
|
||||
|
||||
// Get the calories in kcal
|
||||
return power * duration * UnitConversions.J_TO_KCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the foot calorie in kcal between two locations.
|
||||
*
|
||||
* @param start the start location
|
||||
* @param stop the stop location
|
||||
* @param grade the grade
|
||||
* @param weight the weight of the user in kilogram
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getFootCalorie(Location start, Location stop, double grade, double weight) {
|
||||
// Get speed in meters per second
|
||||
double averageSpeed = (start.getSpeed() + stop.getSpeed()) / 2.0;
|
||||
// Get VO2 in mL/kg/min
|
||||
double vo2 = getFootVo2(averageSpeed, grade);
|
||||
// Duration in minutes
|
||||
// Speed in m/s
|
||||
double speed = (start.getSpeed() + stop.getSpeed()) / 2.0;
|
||||
// Duration in min
|
||||
double duration = (double) (stop.getTime() - start.getTime()) * UnitConversions.MS_TO_S
|
||||
* UnitConversions.S_TO_MIN;
|
||||
/*
|
||||
* Get the calorie. The unit of calorie is kcal which is came from
|
||||
* (mL/kg/min * min * kg * L/mL * kcal/L)
|
||||
*/
|
||||
return vo2 * duration * weight * UnitConversions.ML_TO_L * L_TO_KCAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the foot VO2 value in ml/kg/min.
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param grade the grade
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getFootVo2(double speed, double grade) {
|
||||
if (grade < 0) {
|
||||
grade = 0.0;
|
||||
if (activityType == ActivityType.CYCLING) {
|
||||
/*
|
||||
* Power in watt (Joule/second). See
|
||||
* http://en.wikipedia.org/wiki/Bicycle_performance.
|
||||
*/
|
||||
double power = EARTH_GRAVITY * weight * speed * (K1 + grade) + K2 * (speed * speed * speed);
|
||||
|
||||
// WorkRate in kgm/min
|
||||
double workRate = power * UnitConversions.W_TO_KGM;
|
||||
|
||||
/*
|
||||
* VO2 in kgm/min/kg 1.8 = oxygen cost of producing 1 kgm/min of power
|
||||
* output. 7 = oxygen cost of unloaded cycling plus resting oxygen
|
||||
* consumption
|
||||
*/
|
||||
double vo2 = (1.8 * workRate / weight) + 7;
|
||||
|
||||
// Calorie in kcal
|
||||
return vo2 * duration * weight * UnitConversions.KGM_TO_KCAL;
|
||||
} else {
|
||||
double vo2 = activityType == ActivityType.RUNNING ? getRunningVo2(speed, grade)
|
||||
: getWalkingVo2(speed, grade);
|
||||
|
||||
/*
|
||||
* Calorie in kcal (mL/kg/min * min * kg * L/mL * kcal/L)
|
||||
*/
|
||||
return vo2 * duration * weight * UnitConversions.ML_TO_L * UnitConversions.L_TO_KCAL;
|
||||
}
|
||||
return speed > CRTICAL_SPEED_RUNNING ? getHighSpeedFootVo2(speed, grade)
|
||||
: getLowSpeedFootVo2(speed, grade);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the high speed foot VO2 in ml/kg/min. This equation is appropriate for
|
||||
* speeds greater than 4.5 mph like running.
|
||||
* Gets the running VO2 in ml/kg/min. This equation is appropriate for speeds
|
||||
* greater than 5 mi/hr (or 3 mi/hr or greater if the subject is truly
|
||||
* jogging).
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param speed the speed in m/s
|
||||
* @param grade the grade
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getHighSpeedFootVo2(double speed, double grade) {
|
||||
// Change from meters per second to meters per minute
|
||||
private static double getRunningVo2(double speed, double grade) {
|
||||
// Change from m/s to m/min
|
||||
speed = speed / UnitConversions.S_TO_MIN;
|
||||
|
||||
/*
|
||||
* 0.2 means oxygen cost per meter of moving each kilogram (kg) of body
|
||||
* weight while running (horizontally). 0.9 means oxygen cost per meter of
|
||||
* moving total body mass against gravity (vertically).
|
||||
* 0.2 = oxygen cost per meter of moving each kg of body weight while
|
||||
* running (horizontally). 0.9 = oxygen cost per meter of moving total body
|
||||
* mass against gravity (vertically).
|
||||
*/
|
||||
return 0.2 * speed + 0.9 * speed * Math.abs(grade) + RESTING_VO2;
|
||||
return 0.2 * speed + 0.9 * speed * grade + RESTING_VO2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the low speed foot VO2 in ml/kg/min. This equation is appropriate for
|
||||
* speed less than 4.5 mph like walking.
|
||||
* Gets the walking VO2 in ml/kg/min. This equation is appropriate for speed
|
||||
* from 1.9 to 4 mi/hr.
|
||||
*
|
||||
* @param speed the speed in meters per second
|
||||
* @param speed the speed in m/s
|
||||
* @param grade the grade
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static double getLowSpeedFootVo2(double speed, double grade) {
|
||||
// Change from meters per second to meters per minute
|
||||
private static double getWalkingVo2(double speed, double grade) {
|
||||
// Change from m/s to m/min
|
||||
speed = speed / UnitConversions.S_TO_MIN;
|
||||
|
||||
/*
|
||||
* 0.1 means oxygen cost per meter of moving each kilogram (kg) of body
|
||||
* weight while walking (horizontally). 1.8 means oxygen cost per meter of
|
||||
* moving total body mass against gravity (vertically).
|
||||
* 0.1 = oxygen cost per meter of moving each kilogram (kg) of body weight
|
||||
* while walking (horizontally). 1.8 = oxygen cost per meter of moving total
|
||||
* body mass against gravity (vertically).
|
||||
*/
|
||||
return 0.1 * speed + 1.8 * speed * Math.abs(grade) + RESTING_VO2;
|
||||
return 0.1 * speed + 1.8 * speed * grade + RESTING_VO2;
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,8 @@ public class UnitConversions {
|
||||
|
||||
private UnitConversions() {}
|
||||
|
||||
// multiplication factor to convert milliliters to liters
|
||||
public static final double ML_TO_L = 1 / 1000.0;
|
||||
|
||||
// Time
|
||||
|
||||
// multiplication factor to convert milliseconds to seconds
|
||||
public static final double MS_TO_S = 1 / 1000.0;
|
||||
|
||||
@@ -42,14 +41,18 @@ public class UnitConversions {
|
||||
// multiplication factor to convert minutes to hours
|
||||
public static final double MIN_TO_HR = 1 / HR_TO_MIN;
|
||||
|
||||
// multiplication factor to convert kilometers to miles
|
||||
public static final double KM_TO_MI = 0.621371192;
|
||||
|
||||
// Weight
|
||||
|
||||
// multiplication factor to convert kilograms to pounds
|
||||
public static final double KG_TO_LB = 2.2046;
|
||||
|
||||
// multiplication factor to convert pounds to kilograms
|
||||
public static final double LB_TO_KG = 1 / KG_TO_LB;
|
||||
|
||||
// Distance
|
||||
|
||||
// multiplication factor to convert kilometers to miles
|
||||
public static final double KM_TO_MI = 0.621371192;
|
||||
|
||||
// multiplication factor to convert miles to kilometers
|
||||
public static final double MI_TO_KM = 1 / KM_TO_MI;
|
||||
@@ -60,26 +63,37 @@ public class UnitConversions {
|
||||
// multiplication factor to convert feet to miles
|
||||
public static final double FT_TO_MI = 1 / MI_TO_FT;
|
||||
|
||||
// multiplication factor to covert kilometers to meters
|
||||
public static final double KM_TO_M = 1000.0;
|
||||
|
||||
// multiplication factor to convert meters to kilometers
|
||||
public static final double M_TO_KM = 1 / 1000.0;
|
||||
|
||||
public static final double KM_TO_M = 1 / M_TO_KM;
|
||||
|
||||
// multiplication factor to convert meters per second to kilometers per hour
|
||||
public static final double MS_TO_KMH = 3.6;
|
||||
|
||||
public static final double M_TO_KM = 1 / KM_TO_M;
|
||||
|
||||
// multiplication factor to convert meters to miles
|
||||
public static final double M_TO_MI = M_TO_KM * KM_TO_MI;
|
||||
|
||||
// multiplication factor to convert meters to feet
|
||||
public static final double M_TO_FT = M_TO_MI * MI_TO_FT;
|
||||
|
||||
// multiplication factor to convert meters per second to kilometers per hour
|
||||
public static final double MS_TO_KMH = 3.6;
|
||||
|
||||
// Calories
|
||||
|
||||
// multiplication factor to convert milliliters to liters
|
||||
public static final double ML_TO_L = 1 / 1000.0;
|
||||
|
||||
// multiplication factor to convert liter to kcal
|
||||
public static final double L_TO_KCAL = 5.0;
|
||||
|
||||
// multiplication factor to convert watt to kgm/min
|
||||
public static final double W_TO_KGM = 6.12;
|
||||
|
||||
// multiplication factor to convert kgm to kcal
|
||||
public static final double KGM_TO_KCAL = 1 / 427.0;
|
||||
|
||||
// Others
|
||||
|
||||
// multiplication factor to convert degrees to radians
|
||||
public static final double DEG_TO_RAD = Math.PI / 180.0;
|
||||
|
||||
// multiplication factor to convert Kcal to joules.
|
||||
public static final double KCAL_TO_J = 4184;
|
||||
|
||||
// multiplication factor to convert joules to Kcal.
|
||||
public static final double J_TO_KCAL = 1 / KCAL_TO_J;
|
||||
}
|
||||
|
||||
+3
-3
@@ -225,7 +225,7 @@ public class TripStatisticsUpdaterTest extends TestCase {
|
||||
Location location = getLocation(i + locationOffset, (i + locationOffset) * .001, MOVING_SPEED,
|
||||
startTime + (timeOffset + i) * TEN_SECONDS);
|
||||
tripStatisticsUpdater.addLocation(location,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.FOOT,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.WALKING,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
|
||||
@@ -272,7 +272,7 @@ public class TripStatisticsUpdaterTest extends TestCase {
|
||||
Location location = getLocation(
|
||||
locationOffset, locationOffset * .001, 0, startTime + (i + timeOffset) * TEN_SECONDS);
|
||||
tripStatisticsUpdater.addLocation(location,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, false, ActivityType.FOOT,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, false, ActivityType.WALKING,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
|
||||
tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
@@ -310,7 +310,7 @@ public class TripStatisticsUpdaterTest extends TestCase {
|
||||
Location location = getLocation(i + locationOffset, (i + locationOffset) * .001, 99999,
|
||||
startTime + (timeOffset + i) * TEN_SECONDS);
|
||||
tripStatisticsUpdater.addLocation(location,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.FOOT,
|
||||
PreferencesUtils.RECORDING_DISTANCE_INTERVAL_DEFAULT, true, ActivityType.WALKING,
|
||||
PreferencesUtils.STATS_WEIGHT_DEFAULT);
|
||||
tripStatistics = tripStatisticsUpdater.getTripStatistics();
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* A unit test for {@link CalorieUtils}.
|
||||
*
|
||||
* @author youtaol
|
||||
*/
|
||||
public class CalorieUtilsTest extends TestCase {
|
||||
|
||||
Location start = new Location(LocationManager.GPS_PROVIDER);
|
||||
Location stop = new Location(LocationManager.GPS_PROVIDER);
|
||||
private double grade = 0.07;
|
||||
private int weight = 80;
|
||||
private final long TIME_INTERVAL = 1000l;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
// Make the time interval is not 0
|
||||
stop.setTime(start.getTime() + TIME_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using foot calculation equation.
|
||||
*/
|
||||
public void testGetCalories_foot() {
|
||||
double actual = CalorieUtils.getCalorie(start, stop, grade, weight,
|
||||
CalorieUtils.ActivityType.FOOT);
|
||||
double expected = CalorieUtils.getFootCalorie(start, stop, grade, weight);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether using foot calculation equation while grade is negative.
|
||||
*/
|
||||
public void testGetCalories_footNegativeGrade() {
|
||||
double actualGrade = -5;
|
||||
double expectGrade = 0;
|
||||
double actual = CalorieUtils.getCalorie(start, stop, actualGrade, weight,
|
||||
CalorieUtils.ActivityType.FOOT);
|
||||
double expected = CalorieUtils.getFootCalorie(start, stop, expectGrade, weight);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using cycling calculation equation.
|
||||
*/
|
||||
public void testGetCalories_cycling() {
|
||||
double actual = CalorieUtils.getCalorie(start, stop, grade, weight,
|
||||
CalorieUtils.ActivityType.CYCLING);
|
||||
double expected = CalorieUtils.getCyclingCalorie(start, stop, grade, weight);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using running VO2 equation.
|
||||
*/
|
||||
public void testGetVO2_running() {
|
||||
double actual = CalorieUtils.getFootVo2(CalorieUtils.CRTICAL_SPEED_RUNNING * 2, grade);
|
||||
double expected = CalorieUtils.getHighSpeedFootVo2(CalorieUtils.CRTICAL_SPEED_RUNNING * 2,
|
||||
grade);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks using walking VO2 equation.
|
||||
*/
|
||||
public void testGetVO2_walking() {
|
||||
// Test at half the critical speed
|
||||
double footSpeed = CalorieUtils.CRTICAL_SPEED_RUNNING / 2.0;
|
||||
|
||||
double actual = CalorieUtils.getFootVo2(footSpeed, grade);
|
||||
double expected = CalorieUtils.getLowSpeedFootVo2(footSpeed, grade);
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks calculating cycling calorie. 175 is the watt usage for a 90 kg bike
|
||||
* + rider to go 9 m/s (20 mph or 32 km/h) on the flat. <a href=
|
||||
* "http://www.ideafit.com/fitness-library/calculating-caloric-expenditure-0"
|
||||
* >Reference</a>
|
||||
*/
|
||||
public void testCalculateCalorieCycling() {
|
||||
grade = 0;
|
||||
weight = 90;
|
||||
start.setSpeed(9);
|
||||
stop.setSpeed(9);
|
||||
double expected = 175.0 * (TIME_INTERVAL / 1000) * UnitConversions.J_TO_KCAL;
|
||||
double actual = CalorieUtils.getCyclingCalorie(start, stop, grade, weight);
|
||||
assertTrue((actual - expected) / expected < 0.02);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user