pebble/tests/fw/services/activity/test_activity_calculators.c
2025-01-27 11:38:16 -08:00

235 lines
9.2 KiB
C

/*
* Copyright 2024 Google LLC
*
* 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.
*/
#include "clar.h"
#include "services/normal/activity/activity.h"
#include "services/normal/activity/activity_private.h"
#include "services/normal/activity/activity_calculators.h"
#include "util/units.h"
#include <stdint.h>
// Fakes
static uint8_t s_age_years;
uint8_t activity_prefs_get_age_years(void) {
return s_age_years;
}
static ActivityGender s_gender;
ActivityGender activity_prefs_get_gender(void) {
return s_gender;
}
static uint16_t s_weight_dag;
uint16_t activity_prefs_get_weight_dag(void) {
return s_weight_dag;
}
static uint16_t s_height_mm;
uint16_t activity_prefs_get_height_mm(void) {
return s_height_mm;
}
typedef enum {
Human_TallMale,
Human_ShortMale,
Human_TallFemale,
Human_ShortFemale,
Human_Count,
} Human;
typedef struct HumanPrefs {
uint8_t age_years;
ActivityGender gender;
uint16_t weight_dag;
uint16_t height_mm;
} HumanPrefs;
#define ACTIVITY_DEFAULT_HEIGHT_MM 1620 // 5'3.8"
// dag - decagram (10 g)
#define ACTIVITY_DEFAULT_WEIGHT_DAG 7539 // 166.2 lbs
#define ACTIVITY_DEFAULT_GENDER ActivityGenderFemale
#define ACTIVITY_DEFAULT_AGE_YEARS 30
static void prv_set_user(Human type) {
const HumanPrefs types[Human_Count] = {
[Human_TallMale] = {30, ActivityGenderMale, 7539, 1900},
[Human_ShortMale] = {30, ActivityGenderMale, 4536, 1620},
[Human_TallFemale] = {30, ActivityGenderFemale, 7539, 1900},
[Human_ShortFemale] = {30, ActivityGenderFemale, 4536, 1620},
};
s_age_years = types[type].age_years;
s_gender = types[type].gender;
s_weight_dag = types[type].weight_dag;
s_height_mm = types[type].height_mm;
}
#define MM_PER_METER 1000
#define M_PER_KM 1000
// =============================================================================================
// Start of unit tests
void test_activity_calculators__initialize(void) {
}
// ---------------------------------------------------------------------------------------
void test_activity_calculators__cleanup(void) {
}
// ---------------------------------------------------------------------------------------
void test_activity_calculators__distance(void) {
uint32_t steps;
uint32_t time_ms;
prv_set_user(Human_TallMale);
const int walking_cadence_spm = 100;
// Do an normal walk for 12 mins. An average person should cover ~1km
const int walk_time = 12;
steps = walking_cadence_spm * walk_time;
time_ms = walk_time * SECONDS_PER_MINUTE * MS_PER_SECOND;
int easy_walk_distance_m = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_within(easy_walk_distance_m, 900, 1100);
// Walk for 12 mins again, but this time faster. More distance should be covered
steps = (walking_cadence_spm * 1.2) * walk_time;
time_ms = walk_time * SECONDS_PER_MINUTE * MS_PER_SECOND;
int fast_walk_distance_m = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_gt(fast_walk_distance_m, easy_walk_distance_m);
// Walk for a long time. People can walk at roughly 5km/h, so we should be close to 50km
const int long_walk_time = 10 * MINUTES_PER_HOUR;
steps = walking_cadence_spm * long_walk_time;
time_ms = long_walk_time * SECONDS_PER_MINUTE * MS_PER_SECOND;
int long_walk_distance_m = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_within(long_walk_distance_m, 48000, 52000);
// A typical cadence is roughly 165 steps per minute
const int running_cadence_spm = 165;
// Running for 25 minutes should come out to roughly 5km
const int run_time = 25;
steps = running_cadence_spm * run_time;
time_ms = run_time * SECONDS_PER_MINUTE * MS_PER_SECOND;
int normal_run_distance_m = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_within(normal_run_distance_m, 4500, 5500);
// Running for 25 minutes again, but this time faster
steps = (running_cadence_spm * 1.15) * run_time;
time_ms = run_time * SECONDS_PER_MINUTE * MS_PER_SECOND;
int fast_run_distance_m = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_within(fast_run_distance_m, 6500, 7000);
cl_assert_gt(fast_run_distance_m, normal_run_distance_m);
// Run for 3.5 hours. This is a reasonable marathon time
const int long_run_time = 3 * MINUTES_PER_HOUR + 30;
steps = running_cadence_spm * long_run_time;
time_ms = long_run_time * SECONDS_PER_MINUTE * MS_PER_SECOND;
int long_run_distance_m = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_within(long_run_distance_m, 40000, 44000);
// Now make a shorter guy run for the same time.
// He should be in the same ballpark but cover less distance
prv_set_user(Human_ShortMale);
int short_guy_distance = activity_private_compute_distance_mm(steps, time_ms) / MM_PER_METER;
cl_assert_within(short_guy_distance, 36000, 44000);
cl_assert_gt(long_run_distance_m, short_guy_distance);
// And finally throw in a specific value so that anyone who touches the function will have to
// check up on the unit tests
cl_assert_equal_i(short_guy_distance, 36845);
}
// ---------------------------------------------------------------------------------------
void test_activity_calculators__active_calories(void) {
uint32_t distance_mm;
uint32_t time_ms;
prv_set_user(Human_ShortMale);
// Walk 1km in 12 minutes
distance_mm = 1 * M_PER_KM * MM_PER_METER;
time_ms = 12 * SECONDS_PER_MINUTE * MS_PER_SECOND;
int walk_calories = activity_private_compute_active_calories(distance_mm, time_ms) / ACTIVITY_CALORIES_PER_KCAL;
cl_assert_within(walk_calories, 20, 25); // This seems a little low, but not un-reasonable
// Run 1km in 5 minutes. This should burn more calories than walking
distance_mm = 1 * M_PER_KM * MM_PER_METER;
time_ms = 5 * SECONDS_PER_MINUTE * MS_PER_SECOND;
int run_calories = activity_private_compute_active_calories(distance_mm, time_ms) / ACTIVITY_CALORIES_PER_KCAL;
cl_assert_within(run_calories, 40, 60); // This also seems a little low, but not un-reasonable
cl_assert_gt(run_calories, walk_calories);
// Run 5km in 25 minutes
distance_mm = 5 * M_PER_KM * MM_PER_METER;
time_ms = 25 * SECONDS_PER_MINUTE * MS_PER_SECOND;
int five_k_calories = activity_private_compute_active_calories(distance_mm, time_ms) / ACTIVITY_CALORIES_PER_KCAL;
cl_assert_within(five_k_calories, 220, 250);
cl_assert_gt(five_k_calories, run_calories);
// PG: I went for the following run last night and my garmin watch said I burned 550 calories
prv_set_user(Human_TallMale);
distance_mm = 7 * M_PER_KM * MM_PER_METER;
time_ms = 30 * SECONDS_PER_MINUTE * MS_PER_SECOND;
int quick_run_calories = activity_private_compute_active_calories(distance_mm, time_ms) / ACTIVITY_CALORIES_PER_KCAL;
cl_assert_within(quick_run_calories, 520, 580);
// Run a marathon
distance_mm = 42 * M_PER_KM * MM_PER_METER;
time_ms = 3 * MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND;
int long_run_calories = activity_private_compute_active_calories(distance_mm, time_ms) / ACTIVITY_CALORIES_PER_KCAL;
cl_assert_within(long_run_calories, 3000, 3200);
// And finally throw in a specific value so that anyone who touches the function will have to
// check up on the unit tests
cl_assert_equal_i(long_run_calories, 3172);
}
// ---------------------------------------------------------------------------------------
void test_activity_calculators__inactive_calories(void) {
// People burn roughly 2000 (women) - 2400 (men) a day. This number includes active calories
// so I would expect the values we get to be less than that. I don't know enough to make
// better real world analogies though...
uint32_t long_time_m = 24 * MINUTES_PER_HOUR;
uint32_t short_time_m = 5;
prv_set_user(Human_ShortMale);
cl_assert_equal_i(1321, activity_private_compute_resting_calories(long_time_m) / ACTIVITY_CALORIES_PER_KCAL);
cl_assert_equal_i(4, activity_private_compute_resting_calories(short_time_m) / ACTIVITY_CALORIES_PER_KCAL);
prv_set_user(Human_TallMale);
cl_assert_equal_i(1796, activity_private_compute_resting_calories(long_time_m)/ ACTIVITY_CALORIES_PER_KCAL);
cl_assert_equal_i(6, activity_private_compute_resting_calories(short_time_m) / ACTIVITY_CALORIES_PER_KCAL);
prv_set_user(Human_ShortFemale);
cl_assert_equal_i(1155, activity_private_compute_resting_calories(long_time_m) / ACTIVITY_CALORIES_PER_KCAL);
cl_assert_equal_i(4, activity_private_compute_resting_calories(short_time_m) / ACTIVITY_CALORIES_PER_KCAL);
prv_set_user(Human_TallFemale);
cl_assert_equal_i(1630, activity_private_compute_resting_calories(long_time_m) / ACTIVITY_CALORIES_PER_KCAL);
cl_assert_equal_i(5, activity_private_compute_resting_calories(short_time_m) / ACTIVITY_CALORIES_PER_KCAL);
}