/*
bike_power.c July 20, 1990
by Ken Roberts (roberts@cs.columbia.edu)
Dept of Computer Science, Columbia University, New York
This program is written in ANSI C.
It has been successfully compiled by the GNU gcc compiler
and run under Unix.
I compiled it with the command:
# cc bike_power.c -o bike_power
If that produces error messages, then you have a non-ANSI compiler.
Either find an ANSI compiler (e.g. try "gcc" instead of "cc")
or edit the program to make it compatible with your compiler
(should be pretty straightforward).
The meanings of the command-line flags are given by
# bike_power +H
It does not matter if whitespace is put between the flag
and its following argument:
# bike_power -v25 -i.5 -n11 -g-.12
is exactly equivalent to
# bike_power -v 25.0 -i 0.5 -n 11 -g -0.12
Command-line arguments are applied in the order given.
A wild example is:
# bike_power +M -v10 -n13 -M -f5 -h 5 +M
which is processed as follows:
Set the units to Metric, then set the velocity V = 10 kph,
and the number of table entries N_entry = 13.
Then set units to English, and set up the English version of
table format 5, which resets the value of N_entry to 6.
Then the headwind is 5 mph. Finally the table is printed out
in Metric units.
Usually it's better to first give any flags from the set {+M +P +C -s -f}
since they can affect the interpretation or override the values
of other flags.
Also +P +C -v are all mutually contradictory, since the program
always takes one and calculates the other two from it.
(the default is -v).
The meanings of the columns in the output table are given by
# bike_power +O
All program internal values are in standard scientific
meter-kilogram-second (MKS) units.
Conversions to other units are made only at the point of input or output.
See below under "Conversion factors".
Accurate values for the various parameters:
These are difficult to come by. Good luck.
Fortunately the lack of accurate parameters does not prevent
the investigation of many interesting questions.
The comments contain derivations and discussions of the various
parameter values, and relate them to some published sources.
I got a lot of help from the book
Frank R. Whitt and David G. Wilson, Bicycling Science,
(Second Edition), MIT Press, 1982
hereafter referred to as "W&W".
You will surely want to customize this program, now that you
have the source. (Don't be deterred by lack of knowledge
of the details of the C programming language. It's really
pretty straightforward, once you find the variable you want to change.)
The most obvious change is to the default values
for the weights of the cyclist (Wc) and the machine (Wm).
You'll probably also want your own -s sets and -f formats.
Just go down to the appropriate sections under "Process command line flags"
and start editing.
*/
#include
/* Conversion factors */
#define mOs_per_miOhr 0.44704 /* meters/second per kilometers/hour */
#define mOs_per_kmOhr (1000.0 / 3600.0) /* meters/second per miles/hour */
#define Nt_per_lb 4.4482
#define kg_per_Nt 0.102
#define Watts_per_CalOhr 1.163 /* Watts per dietary Calories/hour */
/* 1 dietary Calorie == 1 kcal == 1000 calories */
#define Watts_per_horsepower 745.700
double read_double_arg (int i);
int read_int_arg (int i);
void read_string_arg (int i, char* s);
void print_help();
void explain_table();
static char** argp;
void
main(
int argc,
char* argv[])
{
int metric_flag = 0;
int display_parameters_flag = 1;
int v_given = 1;
int p_given = 0;
int calOhr_given = 0;
char arg_string[32];
int entry;
double A2 = 0.172;
double A1 = 0.0;
/*
Force of air resistance
F_a = A2 * (V + H)^2 + A1 * (V + H)
From W&W p85ff, air resistance coefficient C_a
C_a = F_a / (dynamic_pressure_of_air * frontal_area)
Where F_a is force of air resistance. At bicycling speeds:
dynamic_pressure_of_air = air_density * (V + H)^2 / 2
where V + H is the relative air velocity. So
F_a = C_a * air_density * frontal_area * (V + H)^2 / 2
p90 suggests that the Reynolds number is in the ballpark of 200000,
so there may be some advantage to wearing rough, rather than smooth clothing.
air_density is about 1.2 kg/m^3 at 20^C = 68^F.
p91 suggests that roughly, C_a = 0.9 for a bicycle and rider.
F_a = (0.9 * 1.2 / 2) * frontal_area * (V + H)^2
= 0.54 * frontal_area * (V + H)^2
p93 says that frontal_area = 0.33 m^2 (racing crouch)
frontal_area = 0.5 m^2 (erect tourist).
F_a = 0.18 * (V + H)^2 ==> A2 = 0.18 [racing crouch]
F_a = 0.27 * (V + H)^2 ==> A2 = 0.27 [upright sitting]
Derived from Bicycling magazine, August 1990, p98,
Typical values for A2: lb*hr^2/mi^2 Nt*s^2/m^2
standing 0.016 0.356
upright (hands on bar tops) 0.012 0.267
crouch (hands on drops, elbows locked) 0.0105 0.233
racing crouch (hands on crops, elbows bent) 0.0075 0.167
full downhill tuck 0.0065 0.145
Effect of drafting:
Bicycling magazine, around July 1990 says that the reduction
in total power requirements due to drafting are:
following a paceline of 1 or more riders 20 mph 18%
(longer paceline provides 23 mph 27%
no additional reduction) 25 mph 27%
in the middle of a pack of riders 25 mph 39%
Since those are reductions in total power output,
A2 must be reduced by a larger percentage than that.
I get values like
following a paceline of 1 or more riders 25 mph A2 = 0.12
in the middle of a pack of riders 25 mph A2 = 0.10
*/
double Wc = Nt_per_lb * 150.0; /* weight of cyclist [Newtons] */
/* current value is 150 pounds */
double Wm = Nt_per_lb * 25.0; /* weight of machine and clothing [Newtons] */
/* current value is 25 pounds */
double W; /* weight = weight_cyclist + weight_machine [Newtons] */
double R = 0.006; /* coefficient of rolling friction */
/*
Force of rolling friction
F_r = R * W
Bicycling magazine, August 1990 has F_r = 0.4 lb = 1.78 Nt, which gives
R = 1.78 Nt / (80 kg * 9.8 Nt/kg) = 0.0022
From W&W p109:
R = k / wheel_diameter
and suggested value of k is 0.0012.
(k values for steel-on-steel range 0.0002--0.0060)
R = 0.0012 / (27 * 0.0254) = 0.00175 [27 inch wheel]
R = 0.0012 / (26 * 0.0254) = 0.00182 [26 inch wheel]
But W&W go on to discuss R values for various tires, pressures, etc.
On very smooth surfaces:
For narrow tubular tires, values are around 0.004 to 0.0055
For 1.125 inch tires, the value is R = 0.0047
For 26 x 1.375, value is R = 0.0066
Other results for 27 x 1.25 inch tires are 0.0051,
with the lowest experimental value R = 0.004
W&W says there is little dependence of R on increasing velocity
(although there was one experiment that reported something like
R = 0.003 + 0.00017 * V for narrow tubulars on very smooth surface.)
Bicycling magazine August 1990 showed no velocity dependence for R.
I have chosen the default R for what I guess to be
realistic road and tire conditions for my usual riding.
*/
double G = 0.00; /* grade of the hill */
/*
Force of gravity
F_g = G * W
Grade is here measured as the sine of the hill angle:
grade G = vertical_rise / odometer_distance
[Actually the convention I prefer for grade uses the tangent of the angle:
vertical_rise / horizontal_distance
But for most hills of interest to bicyclists, the difference between
sine and tangent is less than 1%.
In case it matters: G = tangent/sqrt(1+tangent^2) ]
*/
double E = 0.249; /* efficiency of human in cycling */
double T = 0.95; /* transmission efficiency of bicycle drivetrain */
/*
From W&W, p29:
"The peak efficiencies of the two systems [bicyclist and automobile]
(the energy in the power going to the crankshaft divided by
the energy in the food or fuel) are remarkably close to one another,
in the region of 20--30 percent."
From W&W p 37:
Racer Tourist
mph V mph V P C ~Cal/hr
27 12.1 22.5 10.1 373 1680 1440
25 11.2 21 9.4 298 1365 1170
22 9.8 18.5 8.3 224 1050 900
19 8.5 16 7.2 149 735 630
14.5 6.5 12 5.4 82 420 360
10.5 4.7 8.3 3.7 37 263 225
7.2 3.2 6 2.7 19 186 159
3.2 1.4 1.8 0.8 6 133 114
0 0 0 0 0 105 90
mph = velocity [miles/hour]
V = velocity [m/s]
r = total mass 77kg, frontal area 0.34m^2, tire pressure 689 kPa (100psi)
Tourist = total mass 85kg, frontal area 0.511m^2, tire pressure 345 kPa (50psi)
This gives a fairly consistent measure of efficiency = P / (C - BM)
where BM = C[V=0], as efficiency = 0.2365.
Interpolating to standard 5 mph increments gives for the Racer:
mph P C Cal/hr
5 12 157 135
10 34 251 216
15 89 455 391
20 174 840 722
25 298 1365 1174
30 486 2153 1851
These values correspond fairly closely with
# bike_power -v5 -n6 -i5 -a 0.172 -wc 175 -wm 25 -t1 -e.2365
Mostly because it seems like an easy thing to do,
I've decided to pretend that this 0.2365 efficiency represents
the product of both human and transmission inefficiency: 0.249 * 0.95.
Transmission efficiency value of 0.95 is suggested by W&W p 157.
These values of efficiency also yield rough equivalence with an
informally given table of calorie consumption vs velocity,
which I found in Bicycling magazine around January 1990:
mph Cal/mi C Cal/hr
10 26 302 260
15 31 541 465
20 38 884 760
25 47 1367 1175
30 59 2059 1770
Unfortunately, this table doesn't make a reasonable fit with a V^3 power law.
One simple way to account for this departure from V^3 is that people
tend to sit up more at low speeds and crouch down at high speeds,
so the higher speeds actually had lower A2 values.
*/
double H = 0.0; /* velocity of headwind [meters/second] */
double BM_rate = 1.4; /* Basal Metabolism rate [Watts / kg of body weight] */
double F_a; /* force of air resistance [Newtons] */
double F_r; /* force of rolling friction */
double F_g; /* force to overcome gravity on hill */
double F; /* total force resisting forward motion [Newtons] */
double P = 200.0; /* power output [Watts] */
double P_t; /* power loss due to drivetrain inefficiency [Watts] */
/*
From W&W p51:
For elite bicyclists, P > 1200W is sustainable for very very short periods,
drops to about 570W for duration of 60 sec, then curves to about 430W for 300s,
on down to 390W for 3000s, finally dropping off to 240W for 100000s.
Eddy Merckx could sustain about 440W for about 3000s on an ergometer.
(So it seems unlikely that LeMond was actually putting out
3/4 hp in the 1989 TdF final time trial,
as Frank Berto claims in Bicycling Magazine, June 1990.
Remember, LeMond had aero bars and a tailwind.)
*/
double C; /* power consumption [Watts] */
double B; /* basal metabolism [Watts] */
double V = mOs_per_miOhr * 25.0; /* velocity [meters/second] */
double V_incr = mOs_per_miOhr * 1.0; /* velocity increment in table */
double P_incr = 10.0; /* power increment in table */
double C_incr = Watts_per_CalOhr * 100.0;/* consumed_power increment in table */
int N_entry = 1;
/* Process command line flags */
argp = argv + 1;
while (*argp){
register char c;
c = (*argp)[0];
if (c == '+'){
c = (*argp)[1];
if (c == 'H'){
print_help();
return;
}
else if (c == 'M'){
/* metric = velocity in kph, weight in kg */
metric_flag = 1;
}
else if (c == 'P'){
p_given = 1;
calOhr_given = 0;
v_given = 0;
P = read_double_arg (2);
}
else if (c == 'C'){
calOhr_given = 1;
p_given = 1;
v_given = 0;
C = Watts_per_CalOhr * read_double_arg (2);
}
else if (c == 'D'){
display_parameters_flag = 1;
}
else if (c == 'O'){
explain_table();
return;
}
else{
printf("Unknown flag \"%s\"\n", *argp);
print_help();
return;
}
}
else if (c == '-'){
c = (*argp)[1];
if (c == 'A'){ /* linear coefficient of air resistance */
A1 = read_double_arg (2);
}
else if (c == 'M'){
metric_flag = 0;
}
else if (c == 'D'){
display_parameters_flag = 0;
}
else if (c == 'a'){ /* quadratic coefficient of air resistance */
A2 = read_double_arg (2);
}
else if (c == 'b'){ /* basal metabolism [Watts per kg] */
BM_rate = read_double_arg (2);
}
else if (c == 'e'){
E = read_double_arg (2);
}
else if (c == 'f'){
read_string_arg (2, arg_string);
if (strcmp (arg_string, "1") == 0){
N_entry = 1;
}
else if (strcmp (arg_string, "5") == 0){
if (metric_flag){
V = mOs_per_kmOhr * 5.0;
V_incr = mOs_per_kmOhr * 5.0;
N_entry = 10;
}
else{
V = mOs_per_miOhr * 5.0;
V_incr = mOs_per_miOhr * 5.0;
N_entry = 6;
}
}
else if (strcmp (arg_string, "10") == 0){
if (metric_flag){
V = mOs_per_kmOhr * 10.0;
V_incr = mOs_per_kmOhr * 10.0;
N_entry = 5;
}
else{
V = mOs_per_miOhr * 10.0;
V_incr = mOs_per_miOhr * 0.5;
N_entry = 21;
}
}
else if (strcmp (arg_string, "20") == 0){
if (metric_flag){
V = mOs_per_kmOhr * 20.0;
V_incr = mOs_per_kmOhr * 0.5;
}
else{
V = mOs_per_miOhr * 20.0;
V_incr = mOs_per_miOhr * 0.5;
}
N_entry = 21;
}
else if (strcmp (arg_string, "33") == 0){
if (metric_flag){
V = mOs_per_kmOhr * 33.0;
V_incr = mOs_per_kmOhr * 0.5;
N_entry = 27;
}
else{
V = mOs_per_miOhr * 33.0;
V_incr = mOs_per_miOhr * 1.0;
N_entry = 28;
}
}
else{
printf("Unknown -f table format \"%s\"\n", arg_string);
printf("Valid formats are: 1 5 10 20 33\n");
return;
}
}
else if (c == 'g'){ /* grade of hill */
G = read_double_arg (2);
}
else if (c == 'h'){ /* headwind velocity */
if (metric_flag){
H = mOs_per_kmOhr * read_double_arg (2);
}
else{
H = mOs_per_miOhr * read_double_arg (2);
}
}
else if (c == 'i'){ /* increment of velocities */
if (p_given){
if (calOhr_given)
C_incr = Watts_per_CalOhr * read_double_arg (2);
else
P_incr = read_double_arg (2);
}
else{
if (metric_flag){
V_incr = mOs_per_kmOhr * read_double_arg (2);
}
else{
V_incr = mOs_per_miOhr * read_double_arg (2);
}
}
}
else if (c == 'n'){ /* number of entries in the table */
N_entry = read_int_arg (2);
}
else if (c == 'r'){ /* coefficient of rolling friction */
R = read_double_arg (2);
}
else if (c == 's'){
read_string_arg (2, arg_string);
if (strcmp (arg_string, "u") == 0){
/* uphill seated climbing */
G = 0.06;
A2 = 0.27;
}
else if (strcmp (arg_string, "s") == 0){
/* 175 lb cyclist, 23 lb machine, standing out of the saddle */
Wc = Nt_per_lb * 175.0;
Wm = Nt_per_lb * 23.0;
A2 = 0.36;
}
else if (strcmp (arg_string, "d") == 0){
/* 150 lb cyclist, 25 lb machine, full downhill tuck */
Wc = Nt_per_lb * 150.0;
Wm = Nt_per_lb * 25.0;
A2 = 0.145;
}
else if (strcmp (arg_string, "atb") == 0){
/* mountain bike: high rolling friction,
position sitting with hands up on straight bar */
R = 0.012;
A2 = 0.27;
}
else{
printf("Unknown -s table format \"%s\"\n", arg_string);
printf("Valid formats are: u s d atb\n");
return;
}
}
else if (c == 't'){ /* transmission efficiency of drivetrain */
T = read_double_arg (2);
}
else if (c == 'v'){ /* lowest velocity */
v_given = 1;
p_given = 0;
calOhr_given = 0;
if (metric_flag){
V = mOs_per_kmOhr * read_double_arg (2);
}
else{
V = mOs_per_miOhr * read_double_arg (2);
}
}
else if (c == 'w'){ /* weight */
c = (*argp)[2];
if (c == 'c'){ /* weight of cyclist */
if (metric_flag){
/* input is in kilograms, so convert to Newtons */
Wc = read_double_arg (3) / kg_per_Nt;
}
else{
Wc = Nt_per_lb * read_double_arg (3);
}
}
else if (c == 'm'){ /* weight of machine */
if (metric_flag){
/* kilograms */
/* input is in kilograms, so convert to Newtons */
Wm = read_double_arg (3) / kg_per_Nt;
}
else{
Wm = Nt_per_lb * read_double_arg (3);
}
}
else{
printf("Unknown flag \"%s\"\n", *argp);
print_help();
return;
}
}
else{
printf("Unknown flag \"%s\"\n", *argp);
print_help();
return;
}
}
else {
printf("Unknown argument \"%s\"\n", *argp);
print_help();
return;
/* Read parameters from file */
/*
if ((fd = fopen (*argp, "r")) == 0){
printf("Could not open file \"%s\"\n", *argp);
print_help();
return;
}
*/
}
argp++;
}
W = Wc + Wm;
B = BM_rate * kg_per_Nt * Wc;
if (calOhr_given){
P = E * (C - B);
P_incr = E * C_incr;
}
/* Display parameters */
if (display_parameters_flag){
if (metric_flag){
printf(
"grade of hill = %5.1f%% headwind = %4.1f kph\n",
100.0 * G, H / mOs_per_kmOhr);
printf("weight: cyclist %5.1f + machine %4.1f = total %5.1f kg\n",
kg_per_Nt * Wc, kg_per_Nt * Wm, kg_per_Nt * W);
}
else{
printf(
"grade of hill = %5.1f%% headwind = %4.1f mph\n",
100.0 * G, H / mOs_per_miOhr);
printf("weight: cyclist %5.1f + machine %4.1f = total %5.1f lb\n",
Wc / Nt_per_lb, Wm / Nt_per_lb, W / Nt_per_lb);
}
printf("rolling friction coeff = %6.4f BM rate = %5.2f W/kg\n",
R, BM_rate);
printf("air resistance coeff = (%6.4f, %g)\n",
A2, A1);
printf("efficiency: transmission = %5.1f%% human = %4.1f%%\n",
100.0 * T, 100.0 * E);
printf("\n");
}
if (metric_flag)
printf(
" kph F_kg P_a P_r P_g P_t P hp heat BM C kJ/hr \n");
else
printf(
" mph F_lb P_a P_r P_g P_t P hp heat BM C Cal/hr\n");
/* Calculate values for output table */
for (entry = 0; entry < N_entry; entry++){
if (p_given){
/* Given P, solve for V by bisection search
True Velocity lies in the interval [V_lo, V_hi].
*/
double P_try;
double V_lo = 0.0;
double V_hi = 128.0;
V = 64.0;
while (V - V_lo > 0.001){
P_try = (V / T) * (A2 * (V+H) * (V+H) + A1 * (V+H) + (R + G) * W);
if (P_try < P)
V_lo = V;
else
V_hi = V;
V = 0.5 * (V_lo + V_hi);
}
}
F_a = A2 * (V + H) * (V + H) + A1 * (V + H);
F_r = R * W;
F_g = G * W;
F = F_a + F_r + F_g;
P = V * F / T;
if (P > 0){
C = P / E + B;
P_t = (1.0 - T) * P;
}
else{
C = B;
P_t = 0.0;
}
if (metric_flag){
printf(
"%5.1f %4.1f %4.0f %4.0f %5.0f %4.0f %5.0f %5.2f %5.0f %3.0f %5.0f %5.0f\n",
V / mOs_per_kmOhr, kg_per_Nt * F,
V * F_a, V * F_r, V * F_g, P_t, P, P / Watts_per_horsepower,
C - (B + P), B, C, (3600.0 / 1000.0) * C);
}
else{
printf(
"%5.1f %4.1f %4.0f %4.0f %5.0f %4.0f %5.0f %5.2f %5.0f %3.0f %5.0f %5.0f\n",
V / mOs_per_miOhr, F / Nt_per_lb,
V * F_a, V * F_r, V * F_g, P_t, P, P / Watts_per_horsepower,
C - (B + P), B, C, C / Watts_per_CalOhr);
}
if (p_given)
P += P_incr;
else
V += V_incr;
}
}
/*
Each of these three functions reads the value of a command line argument.
First it tries reading at character i in the current argument,
which is presumable the first character after the flag itself.
If that is null, it then tries to read the entire next argument.
*/
double
read_double_arg(
int i)
{
double B;
if (*(*argp + i)) {
if (sscanf (*argp + i, "%lf", &B) != 1){
printf("Argument for \"%s\" must be a number\n", *argp);
print_help();
exit(-1);
}
}
else{
argp++;
if (sscanf (*argp, "%lf", &B) != 1){
printf("Argument for \"%s\" must be a number\n", *(argp - 1));
print_help();
exit(-1);
}
}
return B;
}
int
read_int_arg(
int i)
{
int J;
if (*(*argp + i)) {
if (sscanf (*argp + i, "%d", &J) != 1){
printf("Argument for \"%s\" must be an integer\n", *argp);
print_help();
exit(-1);
}
}
else{
argp++;
if (sscanf (*argp, "%d", &J) != 1){
printf("Argument for \"%s\" must be an integer\n", *(argp - 1));
print_help();
exit(-1);
}
}
return J;
}
void
read_string_arg(
int i,
char* S)
{
if (*(*argp + i)) {
if (sscanf (*argp + i, "%s", S) != 1){
printf("Argument for \"%s\" must be a string\n", *argp);
print_help();
exit(-1);
}
}
else{
argp++;
if (sscanf (*argp, "%s", S) != 1){
printf("Argument for \"%s\" must be a string\n", *(argp - 1));
print_help();
exit(-1);
}
}
}
void
print_help()
{
printf("bike_power [flag]*\n");
printf(" flags:\n");
printf(" +H = display this message\n");
printf(" +O = explanation of output table headings\n");
printf(" +P = power in Watts (program solves for velocity)\n");
printf(" +C = consumption in Calories/hour (program solves for velocity)\n");
printf(" +D = prints display of the parameters.\n");
printf(" +M = metric units (velocity in kph, weight and force in kg)\n");
printf(" -M = English units (velocity in mph, weight and force in lb)\n");
printf(" -D = suppresses display of the parameters.\n");
printf(" -A = linear coefficient of air resistance (A1).\n");
printf(" -a = quadratic coefficient of air resistance (A2).\n");
printf(" -b = basal metabolism rate [Watts per kg]\n");
printf(" -e = human cycling efficiency\n");
printf(" -f = table format: 1 5 10 20 33\n");
printf(" -g = grade of hill = vertical_rise / odometer_distance\n");
printf(" -h = headwind velocity\n");
printf(" -i = increment in output table\n");
printf(" -n = number of entries in output table\n");
printf(" -r = coefficient of rolling friction\n");
printf(" -s = set of pre-specified parameters: u s d atb\n");
printf(" -t = transmission efficiency of bicycle drivetrain\n");
printf(" -v = first velocity in output table\n");
printf(" -wc = weight of cyclist\n");
printf(" -wm = weight of machine and clothing\n");
}
void
explain_table()
{
printf("kph = velocity [kilometers per hour].\n");
printf("mph = velocity [miles per hour].\n");
printf("F_kg = total force resisting forward motion [kilograms].\n");
printf("F_lb = total force resisting forward motion [lb = pounds].\n");
printf("P_a = power output to overcome air resistance [Watts].\n");
printf("P_r = power output to overcome rolling friction [Watts].\n");
printf("P_g = power output to climb grade [Watts].\n");
printf("P = P_a + P_r + P_g = total power output [Watts].\n");
printf("hp = total power output [horsepower].\n");
printf(
"heat = C - (P + BM) = power wasted due to human inefficiency [Watts].\n");
printf("BM = basal metabolism [Watts].\n");
printf("C = total power consumption [Watts].\n");
printf("kJ/hr = total power consumption [kilo-Joules per hour].\n");
printf("Cal/hr = total power consumption [dietary Calories per hour].\n");
}