diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java
index 3d5b9b6e9..09697499e 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/CalorieUtils.java
@@ -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. Reference
- */
- @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:
- * P = g * m * Vg * (K1 + s) + K2 * (Va)^2 * Vg
- *
- * - P - Power in watt (Joule/second)
- * - g - gravity, using 9.80665 meter/second^2
- * - m - mass of the rider plus bike in kilogram
- * - s - surface grade
- * - Vg - ground relative speed in meter/second
- * - Va - air relative speed. We assume the air speed is zero, so Va is
- * equal to Vg.
- * - K1 - a constant which represents frictional losses, using 0.0053.
- * - K2 - a constant representing aerodynamic drag, using 0.185.
- *
- *
- *
- * @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;
}
}
\ No newline at end of file
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/UnitConversions.java b/MyTracks/src/com/google/android/apps/mytracks/util/UnitConversions.java
index 7f5e0e2ec..003de174a 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/UnitConversions.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/UnitConversions.java
@@ -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;
}
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java
index efcba5995..ee3f43a3b 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/stats/TripStatisticsUpdaterTest.java
@@ -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();
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java
deleted file mode 100644
index 6ae3f7748..000000000
--- a/MyTracksTest/src/com/google/android/apps/mytracks/util/CalorieUtilsTest.java
+++ /dev/null
@@ -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. Reference
- */
- 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);
- }
-}
\ No newline at end of file