/* * FILE: * bike-calc.gob * * FUNCTION: * Implements the core of the Bicycle Ride Calorie Calculator * using "gob" (http://www.5z.com/jirka/gob.html) to simplify * the creation of GObjects. This is a re-write of the old * "calcalc" Bicycle Ride Calorie Calculator from Greg Kondrasuk * <kondrag@geocities.com>, which was written in FLTK and is at * http://www.geocities.com/SiliconValley/Vista/6434/calcalc.html * * This code does two basic things: * 1) Declares an object with half-dozen input paramters, and * three output paramters. * 2) Defines a subroutine to calculate the calories burned * (the output), based on the input paramters. * * Even if you've never seen a 'gob' created before, this file * is still simple enough to understand. * * HISTORY: * Copyright (c) 2004 Linas Vepstas <linas@linas.org> April 2004 */ requires 2.0.0 %{ #include <libintl.h> #include <math.h> #include <stdio.h> #include "bike-calc.h" #include "bike-calc-private.h" #define _(X) gettext(X) #define KM 0.62137 // kilometers per mile #define KG 2.20462 // kilograms per pound #define MT 3.28084 // meters per foot %} class Bike:Calc from G:Object { /* Inputs to the calculator */ /* ---------- Ride Data ----------------- */ private float distance=0.0; property FLOAT distance (nick = "distance", blurb = _("Ride Distance"), minimum = 0.0, export, link); private int time_interval=0.0; property INT time_interval (nick = "time interval", blurb = _("Ride Time"), minimum = 0, export, link); private float draft=0.0; property FLOAT draft (nick = "draft", blurb = _("Percentage of Time Spent Drafting"), minimum = 0.0, maximum = 100.0, export, link); private float climb=0.0; property FLOAT climb (nick = "climb", blurb = _("Percentage of Time Spent Climbing"), minimum = 0.0, maximum = 100.0, export, link); private float elevation=0.0; property FLOAT elevation (nick = "elevation", blurb = _("Elevation Gain"), minimum = 0.0, export, link); private gboolean loop=1; property BOOLEAN loop (nick = "loop", blurb = _("Loop if True, else a Point-to-Point Ride"), export, link); private gboolean aero=1; property BOOLEAN aero (nick = "aero", blurb = _("If True, then Aerodynamic Position"), export, link); private int wind_direction = 0; property INT wind_direction (nick = "wind_direction", blurb = _("Wind Direction"), export, link); private float wind_speed=0.0; property FLOAT wind_speed (nick = "wind_speed", blurb = _("Wind Speed"), minimum = 0.0, maximum = 200.0, export, link); /* ---------- Rider Data ----------------- */ private float weight=100.0; property FLOAT weight (nick = "weight", blurb = _("Rider Weight"), default_value = 100.0, minimum = 0.0, maximum = 1000.0, export, link); /* ---------- Units (English/Metric) ----------------- */ private gboolean metric_units = 0; property BOOLEAN metric_units (nick = "metric_units", blurb = _("Use Metric Units if set"), default_value = 0, export) set { gboolean use_mu = g_value_get_boolean (VAL); if (self->_priv->metric_units != use_mu) { if (use_mu) { self->_priv->distance /= KM; self->_priv->elevation /= MT; self->_priv->wind_speed /= KM; self->_priv->weight /= KG; } else { self->_priv->distance *= KM; self->_priv->elevation *= MT; self->_priv->wind_speed *= KM; self->_priv->weight *= KG; } } self->_priv->metric_units = use_mu; } get { g_value_set_boolean (VAL, self->_priv->metric_units); }; /* output from the calculator */ /* ---------- Ride Statistics Output ----------------- */ private float avg_speed=0.0; property FLOAT avg_speed (nick = "avg speed", blurb = _("Average Speed"), minimum = 0.0, maximum = 100.0, export) set {} get { self_compute (self); g_value_set_float (VAL, self->_priv->avg_speed); }; private float total_calories=0.0; property FLOAT total_calories (nick = "total calories", blurb = _("Total Calories"), minimum = 0.0, maximum = 10000.0, export, // XXX this should be read-only link); private float ride_calories=0.0; property FLOAT ride_calories (nick = "ride calories", blurb = _("Ride Calories"), minimum = 0.0, maximum = 10000.0, export, // XXX this should be read-only link); public GObject * new (void) { return (GObject *) GET_NEW; } private void compute (self) { printf ("Object Values:\n"); printf ("\tdistance=%f\n", self->_priv->distance); printf ("\ttime=%d\n", self->_priv->time_interval); printf ("\tdraft=%f\n", self->_priv->draft); printf ("\tclimb=%f\n", self->_priv->climb); printf ("\televaction=%f\n", self->_priv->elevation); printf ("\tloop=%d\n", self->_priv->loop); printf ("\taero=%d\n", self->_priv->aero); printf ("\twind_direction=%d\n", self->_priv->wind_direction); printf ("\twind_speed=%f\n", self->_priv->wind_speed); printf ("\tweight=%f\n", self->_priv->weight); printf ("\n"); self->_priv->total_calories = 2.0; /* basic setup */ float time_min = ((float) self->_priv->time_interval) / 60.0; float distance = self->_priv->distance; float weight = self->_priv->weight; float avg_speed = 60.0 * (distance / time_min); float climb = self->_priv->climb; int loop = self->_priv->loop; int aero = self->_priv->aero; float elev_gain = self->_priv->elevation; float draft = self->_priv->draft; int wind_direction = self->_priv->wind_direction; float wind_speed = self->_priv->wind_speed; gboolean use_metric = self->_priv->metric_units; if (use_metric) { distance *= KM; weight *= KG; avg_speed *= KM; // convert to miles per hour elev_gain *= MT; wind_speed *= KM; } /* Computations. * Copyright (C) 1998 Greg Kondrasuk (kondrag@geocities.com) * Greg originally wrote: * >> The formula for this is a direct translation of the * >> calorie calculation worksheet that appeared in an article * >> published in the May 1989 issue of Bicyling Magazine, * >> pp. 100-103. * I translated his formulas to those below, hopefully without * introducing errors. Looks to me like these formulas expect * english units (feet, miles, pounds) */ // calculate the baseline value float baseline = (8.79618E-6 * avg_speed * avg_speed * avg_speed - 1.46998E-4 * avg_speed * avg_speed + 0.00359 * avg_speed + 0.00556 ) * weight; float modBase = baseline; // surface area adjustment modBase -= ((weight-154.0) / 200.0) * baseline; // terrain adjustment float terrainAdj = (climb / 1000.0) * modBase; // need to do terrain adjustment for a point to point ride if (0 == loop) { terrainAdj += weight * elev_gain * 0.0014 / time_min; modBase += terrainAdj; } // Wind Adjustment #define Headwind 0 #define CrossHeadwind 1 #define CrossWind 2 #define CrossTailwind 3 #define Tailwind 4 if (0 == loop) { float airspeed=0.0; switch (wind_direction) { case Headwind: case CrossHeadwind: airspeed = avg_speed + 0.5 * wind_speed; break; case Tailwind: case CrossTailwind: airspeed = avg_speed - 0.5 * wind_speed; break; } float tempBase = (8.79618E-6 * airspeed * airspeed * airspeed - 1.46998E-4 * airspeed * airspeed + 0.00359 * airspeed + 0.00556) * weight; switch (wind_direction) { case Headwind: case Tailwind: modBase += tempBase - baseline; break; case CrossHeadwind: case CrossTailwind: modBase += 0.7*(tempBase - baseline); break; } } // Riding Position adjustment if ((15.0 < avg_speed) && (0 == aero)) { modBase += (-0.66893 + 0.0467*avg_speed) * modBase; } // Drafting Adjustment modBase -= (draft / 100.0) * (avg_speed / 100.0); // total calorie expenditure float totalCals = modBase * time_min; // Natural caloric expenditure modBase = modBase - 0.01 * weight; float rideCals = modBase * time_min; /* copy to the widget */ if (use_metric) { avg_speed /= KM; // convert back to km per hour } self->_priv->avg_speed = avg_speed; self->_priv->total_calories = totalCals; self->_priv->ride_calories = rideCals; } }