/* This file is part of Wattzap Community Edition.
*
* Wattzap Community Edtion is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Wattzap Community Edition is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Wattzap. If not, see <http://www.gnu.org/licenses/>.
*/
package com.wattzap.model.power;
/*
*
* @author David George
* @date 11 November 2013
*/
public abstract class Power implements Comparable<Power> {
private int neutral = getNeutral();
private double posDivider = 1;
private double negDivider = 1;
String description;
/**
* Returns power in watts for a given trainer resistance
*
* @param speed
* in km/h
* @param resistance
*/
public abstract int getPower(double speed, int resistance);
public boolean equals(Object o) {
if (!(o instanceof Power))
return false;
Power n = (Power) o;
return n.toString().equals(toString());
}
public int hashCode() {
return toString().hashCode();
}
public int compareTo(Power n) {
return toString().compareTo(n.toString());
}
/**
* Returns a description of the Power class
*
* @return
*/
public abstract String toString();
public abstract int getResitanceLevels();
public int getNeutral() {
return 3;
}
public abstract double getSpeed(int power, int resistance);
public void setGrades(double max, double min) {
int levels = getResitanceLevels();
int posLevels = levels - neutral;
int negLevels = levels - posLevels - 1;
posDivider = max / posLevels;
negDivider = min / negLevels;
}
public int getResistance(double gradient) {
int r = neutral;
if (gradient >= 0 && posDivider > 0) {
r = (int) (neutral + (gradient / posDivider));
} else if (negDivider < 0) {
r = (int) (neutral - (gradient / negDivider));
}
return r;
}
/*
* P = krMs + kaAsv2d+ giMs
*
* P = 0.45s + 0.37s³ + 35.28s
*
* where
*
* P = power required (in watts)
*
* kr= rolling resistance coefficient
*
* M = mass of bike + rider
*
* s = speed of the bike on the road
*
* ka= wind resistance coefficient
*
* A = the frontal area of the bike and rider
*
* v = speed of the bike through the air (i.e. bike speed + headwind or
* tailwind)
*
* d = air density
*
* g = gravitational constant
*/
public double getRealSpeed(double mass, double slope, double power) {
double kr = 0.005;
double ka = 0.5;
double A = 0.6;
double d = 1.266;
double g = 9.81;
double c = (kr * mass) + (g * slope * mass);
double a = ka * A * d;
Cubic cubic = new Cubic();
cubic.solve(a, 0, c, 0.0 - power);
return cubic.x1;
}
/**
* P = krMs + kaAsv2d+ giMs
*
* P = 0.45s + 0.37s³ + 35.28s
*
* where
*
* P = power required (in watts)
*
* kr= rolling resistance coefficient
*
* M = mass of bike + rider in kg
*
* s = speed of the bike on the road in kmh
*
* ka= wind resistance coefficient
*
* A = the frontal area of the bike and rider
*
* v = speed of the bike through the air (i.e. bike speed + headwind or
* tailwind)
*
* d = air density
*
* g = gravitational constant
*
* i = gradient (an approximation)
*/
public static int getPower(double mass, double slope, double speed) {
// temperature = 25 # degrees C
// pressure = 101325 # pascals
// bike_rider_mass = 81 # kg
// cda = 0.324 # rider on hoods
speed = speed / 3.6; // convert to m/s
int power = getRiderPower(0.324, 25, 101325, speed, mass,
Surface.ASPALT, slope, 0);
return power;
}
/*
* Compute force due to aerodynamic drag.
*
* Formula: 0.5(CdA)(density)V^3
*
* @param cda - drag coefficient
*
* @param temp - air temperature
*
* @param pressure - air pressure
*
* @param speed - speed in meters/second
*
* Typical figures are: Sea Level 1.226 1500 Meters 1.056 3000 Meters 0.905
*/
private static double getDrag(double cda, double temp, double pressure,
double speed) {
double airDensity = pressure / (287.05 * (temp + 273.15));
return 0.5 * cda * airDensity * (speed * speed * speed);
}
/**
* Compute rolling resistance force. Formula: gm(Croll)
*/
private static double getRollingResistance(double mass, Surface surface,
double speed) {
double cr = 0.004; // ASPHALT - default
// Typical rolling resistance values for different surfaces
switch (surface) {
case TRACK:
cr = 0.001;
break;
case CONCRETE:
cr = 0.002;
break;
case ROUGH:
cr = 0.008;
}
return 9.81 * mass * cr * speed;
}
/*
* Compute force required to change elevation.
*
* Formula: gm(slope)
*/
private static double getGravityForce(double mass, double gradient,
double speed) {
return 9.81 * mass * gradient * speed;
}
/*
* Compute force required to achieve acceleration.
*
* Formula: F = ma
*
* a = (speed change)^2} / 2 x time to change speed
*/
private static double getAccelerationForce(double mass,
double acceleration, double speed) {
return mass * acceleration * speed;
}
/*
* Compute the rider's power output.
*
* Formula is: (drag + rolling resistance + climbing force + acceleration
* force) * velocity
*/
public static int getRiderPower(double cda, double temp, double pressure,
double speed, double mass, Surface surface, double gradient,
double acceleration) {
double drag = getDrag(cda, temp, pressure, speed);
double roll = getRollingResistance(mass, Surface.ASPALT, speed);
double grav = getGravityForce(mass, gradient, speed);
double accel = getAccelerationForce(mass, acceleration, speed);
int power = (int) (drag + roll + grav + accel + 0.5); // 0.5 will cause
// rounding up
// or down
if (power < 0) {
power = 0;
}
return power;
}
// Note that the inputs are now declared as doubles.
public double quadraticEquationRoot1(double a, double b, double c) {
double root1, root2; // This is now a double, too.
root1 = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
root2 = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
return Math.max(root1, root2);
}
public enum Surface {
TRACK, CONCRETE, ASPALT, ROUGH
}
}