# bike_pow.py Wolfgang Strobl ws@mystrobl.de 2002-02-17 # based on # bike_power.c July 20, 1990 # by Ken Roberts (roberts@cs.columbia.edu) # Dept of Computer Science, Columbia University, New York __version__=" $Id: bike_pow.py,v 1.1.1.1 2002/09/22 11:56:24 cvsroot Exp $" # $Log: bike_pow.py,v $ # Revision 1.1.1.1 2002/09/22 11:56:24 cvsroot # www.mystrobl.de komplett Stand 22.Sept 2002 # # Revision 1.18 2002/03/13 17:44:48 cvsroot # Abhängigkeit von wxPython rausgemacht (Pycrust), # statictextcontrol rausgeworfen # Revision 1.1.1.1 2002/02/17 13:46:01 cvsroot # BikePower in Python, initial port # # constants import pickle #from Scientific.Physics.PhysicalQuantities import * import Numeric # wg. py2exe from PQ import * # PQ is a patched version of Scientific.Physics.PhysicalQuantities pq=PhysicalQuantity # alias class bparm(int): # basic parameter set props=["V","Vincrement","Vnumber","H","G","Wc","Wm","cw","rho","A","R","E","T","BM_r",] derived=["P","Pincrement","Pnumber", "F_a","F_r","F_g","F","P_t","C","B",] dict={} # "V":("km/h","velocity") basic unit, title def __init__(self): self.cw = 0.9 # cw self.rho = pq("1.2 kg/m**3") # density of air self.A = pq("0.33m**2") # frontal area self.Wc = pq("68.1 kp") # weight of cyclist self.Wm = pq("11.3 kp") # weight of machine and clothing [Newtons] self.R = 0.006 # coefficient of rolling friction self.G = 0.00 # grade of the hill self.E = 0.249 # efficiency of human in cycling self.T = 0.95 # transmission efficiency of bicycle # drivetrain self.H = pq("0.0 m/s") # velocity of headwind [meters/second] self.BM_r = pq("1.4 W/kg") # Basal Metabolism rate [Watts / kg of body weight] self._V=pq("0 km/h") self.Vincrement=pq("10 km/h") self.Vnumber=5 self._P = pq("200.0 W") # Power output self.Pincrement=pq("50 W") self.Pnumber=0 def getH(self): return self._H def setH(self,h): self._H=h H=property(getH,setH,None,"Velocity of headwind") dict["H"]=("km/h","headwind") def getBM_r(self): return self._BM_r def setBM_r(self,bmr): self._BM_r=bmr BM_r=property(getBM_r,setBM_r,None,"Basal Metabolism rate [Watts / kg of body weight]") dict["BM_r"]=("W/kg","metab. rate") def getT(self): return self._T def setT(self,t): self._T=t T=property(getT,setT,None,"Transmission efficiency of bicycle drivetrain") dict["T"]=("%","drivetrain") def getR(self): return self._R def setR(self,r): self._R=r R=property(getR,setR,None,"Coefficient of rolling friction") dict["R"]=("%","tires") def getG(self): return self._G def setG(self,g): self._G=g G=property(getG,setG,None,"Grade of the hill") dict["G"]=("%","grade") def getE(self): return self._E def setE(self,e): self._E=e E=property(getE,setE,None,"Efficiency of the human body in cycling") dict["E"]=("%","human eff.") def getcw(self): return self._cw def setcw(self,cw): self._cw=cw cw=property(getcw,setcw,None,"cW value (deutsch: Luftwiderstandsbeiwert)") dict["cw"]=("","cw") def getrho(self): return self._rho def setrho(self,rho): self._rho=rho rho=property(getrho,setrho,None,"Density of air") dict["rho"]=("","air density") def getA(self): return self._A def setA(self,a): self._A=a A=property(getA,setA,None,"Frontal area of cyclist and bicycle") dict["A"]=("","frontal area") def getWc(self): return self._Wc def setWc(self,wc): self._Wc=wc Wc=property(getWc,setWc,None,"Weight of cyclist") dict["Wc"]=("kp","body weight") def getWm(self): return self._Wm def setWm(self,wm): self._Wm=wm Wm=property(getWm,setWm,None,"Weight of machine and clothing") dict["Wm"]=("kp","equipment weight") def getP(self): return self._P def setP(self,p): # set power, adjust V self._P=p; self.solveV() P = property(getP,setP,None,"Power output") dict["P"]=("W","power") def getV(self): # get velocity (km/h) self._V.convertToUnit("km/h") return self._V def setV(self,v): # set velocity (km/h), compute Power #print "setV",v if not isPhysicalQuantity(v): if type(v)==type(""): v=PhysicalQuantity(v) else: v=PhysicalQuantity(v,"km/h") self._V=v self.computeP() V = property(getV,setV,None, "Velocity (start value in table)") dict["V"]=("km/h","velocity") def getVincrement(self): self._Vincrement.convertToUnit("km/h") return self._Vincrement def setVincrement(self,v): if not isPhysicalQuantity(v): if type(v)==type(""): v=PhysicalQuantity(v) else: v=PhysicalQuantity(v,"km/h") self._Vincrement=v Vincrement = property(getVincrement,setVincrement,None, "Velocity (increment) ") dict["Vincrement"]=("km/h","velocity increment") def getVnumber(self): return self._Vnumber def setVnumber(self,v): self._Vnumber=int(v) Vnumber = property(getVnumber,setVnumber,None, "Velocity (number of rows wanted)") dict["Vnumber"]=("","velocity #rows") def getPincrement(self): self._Pincrement.convertToUnit("W") return self._Pincrement def setPincrement(self,v): if not isPhysicalQuantity(v): if type(v)==type(""): v=PhysicalQuantity(v) else: v=PhysicalQuantity(v,"W") self._Pincrement=v Pincrement = property(getPincrement,setPincrement,None, "Power (increment) ") dict["Pincrement"]=("W","power increment") def getPnumber(self): return self._Pnumber def setPnumber(self,v): self._Pnumber=int(v) Pnumber = property(getPnumber,setPnumber,None, "Power (number of rows wanted)") dict["Pnumber"]=("","power #rows") def getW(self): return self.Wc+self.Wm W = property(getW,None,None,"weight = weight_cyclist + weight_machine") dict["W"]=("kp","weight") def qu(self,v): r= self.cw*self.A*self.rho*v*v/2 r.convertToUnit("kp") return r def getF_a(self): v=self.V+self.H f = self.qu(v) return f F_a = property(getF_a,None,None,"force of air resistance") dict["F_a"]=("kp","force air") def getF_r(self): f=self.R * self.W f.convertToUnit("kp") return f F_r = property(getF_r,None,None,"force of rolling friction") dict["F_r"]=("kp","force tire") def getF_g(self): f=self.G*self.W f.convertToUnit("kp") return f F_g = property(getF_g,None,None,"force to overcome gravity on hill") dict["F_g"]=("kp","force grav") def getF(self): return self.F_a+self.F_r+self.F_g F = property(getF,None,None,"Force") dict["F"]=("kp","force") def getP_t(self): return (1.0-self.T)*self.P P_t = property(getP_t,None,None,"power loss due to drivetrain inefficiency") dict["P_t"]=("W","drivetr loss") def getC(self): if self.P.value>0: return self.P/self.E+self.B return self.B C = property(getC,None,None,"power consumption") dict["C"]=("W","power cons.") def getB(self): return self.BM_r * self.Wc * pq("1kg/kp") B = property(getB,None,None,"basal metalbolism power consumption") dict["B"]=("W","basal met pwr") def computeP(self): # input V+F, output P #print "computeP",self._P self._P=self._V*self.F/self.T self._P.convertToUnit("W") def solveV(self): # Given P, solve for V by bisection search # True Velocity lies in the interval [V_lo, V_hi]. V_lo,V,V_hi = pq("0.0m/s"), pq("64.0m/s"), pq("128.0m/s") eps=pq("0.001m/s") while V - V_lo > eps: x = (V / self.T) frc= (self.qu(V+self.H)+(self.R+self.G)*self.W) P_try=x*frc P_try.convertToUnit("W") #P_try = (V / self.T) * (self.A2 * (V+self.H) * (V+self.H) + # self.A1 * (V+self.H) + (self.R + self.G) * self.W) if P_try < self.P: V_lo = V else: V_hi = V V = 0.5 * (V_lo + V_hi) self._V=V def refresh(self): # compute remaining dependant values self.computeP() def getP_g(self): return (self._V * self.F_g).inUnitsOf("W") P_g = property(getP_g,None,None,"power for climbing") def getP_r(self): return (self._V * self.F_r).inUnitsOf("W") P_r = property(getP_r,None,None,"power for rolling resistance") def getP_a(self): return (self._V * self.F_a).inUnitsOf("W") P_a = property(getP_a,None,None,"power for air drag") def repr_metric(self): #print self.V,self.F,self.F_a,self.F_r,self.F_g,self.P_t,self.P,self.B #return "" hp=(self.P*1.0).inUnitsOf("hp").value C = self.C.inUnitsOf("W").value kJh = self.C.inUnitsOf("kJ/h").value formatstring="%5.1f %4.1f %4.0f %4.0f %5.0f %4.0f %5.0f %5.2f %5.0f %3.0f %5.0f %5.0f" values= (self.V.value, self.F.value, self.P_a.value, self.P_r.value, self.P_g.value, self.P_t.value, self.P.value, hp, (self.C - (self.B + self.P)).value, self.B.value, C, kJh) return formatstring%values def metricheader(self): return " kph F_kg P_a P_r P_g P_t P hp heat BM C kJ/hr" def print_metric(self): print self.metricheader() print self.repr_metric() def print_parm(self): self.H.convertToUnit('km/h') print "grade of hill: %5.1f%%"%(100.0 * self.G) print "headwind: %s" % (self.H) self.Wc.convertToUnit("kp") self.Wm.convertToUnit("kp") self.W.convertToUnit("kp") print "weight: cyclist %s + machine %s = total %s " % \ (self.Wc, self.Wm, self.W) print "rolling friction coeff:",self.R print "BM rate: ",self.BM_r print "air resistance coeff: cw=%s, rho=%s, A=%s" % \ (self.cw, self.rho, self.A) print "efficiency: transmission = %5.1f%% human = %4.1f%%" % \ (100.0 * self.T, 100.0 * self.E) def __repr__(self): s=""" cw: %14s \t%s rho: %14s \t%s A: %14s \t%s Wc: %14s \t%s Wm: %14s \t%s R: %14s \t%s G: %14s \t%s E: %14s \t%s T: %14s \t%s H: %14s \t%s BM_r %14s \t%s P: %14s \t%s V: %14s \t%s B: %14s \t%s """ t=(self.cw, bparm.cw.__doc__, self.rho,bparm.rho.__doc__, self.A,bparm.A.__doc__, self.Wc,bparm.Wc.__doc__, self.Wm,bparm.Wm.__doc__, self.R,bparm.R.__doc__, self.G,bparm.G.__doc__, self.E,bparm.E.__doc__, self.T,bparm.T.__doc__, self.H,bparm.H.__doc__, self.BM_r,bparm.BM_r.__doc__, self.P,bparm.P.__doc__, self.V,bparm.V.__doc__, self.B,bparm.B.__doc__, ) return s%t def save(self,f): pickle.dump(self,f,1) def load(f): return pickle.load(f) def t1(): """ >>> t1() grade of hill = 0.0% headwind = 0.0 km/h weight: cyclist 68.1 kp + machine 11.3 kp = total 79.4 kp rolling friction coeff = 0.0060 BM rate = 1.40 W/kg air resistance coeff = (0.1720, 0.000000) efficiency: transmission = 95.0% human = 24.9% """ b=bparm() b.refresh() b._P.convertToUnit("W") #print b.P #print b.B b.print_parm() def t2(): """ >>> t2() kph F_kg P_a P_r P_g P_t P hp heat BM C kJ/hr 40.2 2.7 240 52 0 10 200 0.27 603 95 899 3235 kph F_kg P_a P_r P_g P_t P hp heat BM C kJ/hr 40.2 4.3 240 52 174 25 491 0.66 1481 95 2067 7441 """ b=bparm() b.print_metric() b.G=0.02 b.refresh() b.print_metric() def t3(): """ >>> t3() kph F_kg P_a P_r P_g P_t P hp heat BM C kJ/hr 10.0 0.6 4 13 0 1 18 0.02 53 95 166 597 20.0 1.0 29 26 0 3 58 0.08 176 95 330 1187 30.0 1.7 100 39 0 7 146 0.20 440 95 681 2451 40.0 2.6 236 52 0 15 303 0.41 914 95 1312 4724 50.0 3.9 461 65 0 28 553 0.74 1669 95 2318 8344 """ b=bparm() print b.metricheader() for v in range(10,60,10): b.V=pq(float(v),"km/h") print b.repr_metric() def t4(): """ >>> t4() kph F_kg P_a P_r P_g P_t P hp heat BM C kJ/hr 18.5 0.9 23 24 0 3 50 0.07 151 95 296 1066 20.3 1.0 31 26 0 3 60 0.08 181 95 336 1211 21.8 1.1 38 28 0 4 70 0.09 211 95 376 1355 """ b=bparm() print b.metricheader() for p in range(50,80,10): b.P=pq(float(p),"W") print b.repr_metric() def _test(): import doctest, bike_pow return doctest.testmod(bike_pow) try: from wxPython.wx import * def Crust(): application = CrustApp(0) application.MainLoop() from wxPython.lib.PyCrust.shell import ShellMenu,Shell class BikeShellFrame(wxFrame, ShellMenu): """Frame containing the PyCrust shell component.""" name = 'BikePyCrust Shell Frame' revision = __version__ def __init__(self, parent=None, id=-1, title='PyShell', \ pos=wxDefaultPosition, size=wxDefaultSize, \ style=wxDEFAULT_FRAME_STYLE, locals=None, \ InterpClass=None, *args, **kwds): """Create a PyCrust ShellFrame instance.""" wxFrame.__init__(self, parent, id, title, pos, size, style) intro = """PyCrust + BikePower, %s *** Predefined Names: bike_pow: imported module bike_pow bp: an instance of bike_pow.bparm() -- a set of parameters bparm: the "class" of bp pq: shorthand for Scientific.Physics.PhysicalQuantities.PhysicalQuantity *** Examples: >>> for v in range(10,40,10): bp.V=pq(v,"km/h"); print bp.V, bp.P, bp.C.inUnitsOf("kcal/h") 10.0 km/h 17.6809592398 W 143.129141099 kcal/h 20.0 km/h 59.4847254971 W 287.582248487 kcal/h 30.0 km/h 149.534105789 W 598.748298012 kcal/h >>> bp.P=pq("200 W"); print bp.P, bp.V, bp.F_a 200.0 W 33.704296875 km/h 1.59276816066 kp now set >>> bp.G=0.01 # 1%% grade and try again ... """%__version__ self.CreateStatusBar() self.SetStatusText(intro.replace('\n', ', ')) if wxPlatform == '__WXMSW__': icon = wxIcon('images/bike_pow.ico', wxBITMAP_TYPE_ICO) self.SetIcon(icon) self.shell = Shell(parent=self, id=-1, introText=intro, \ locals=locals, InterpClass=InterpClass, \ *args, **kwds) # Override the shell so that status messages go to the status bar. self.shell.setStatusText = self.SetStatusText self.createMenus() class CrustApp(wxApp): """PyShell standalone application.""" def OnInit(self): #from wxPython.lib.PyCrust.shell import ShellFrame import bike_pow locals = {'__app__': 'Bike_Power',"bp":bparm(),"bparm":bike_pow.bparm, "bike_pow":bike_pow,"pq":PhysicalQuantity} self.shellFrame = BikeShellFrame(title="BikePower", locals=locals, size=(800,600)) self.shellFrame.SetStatusText("Hallo!") self.shellFrame.Show(true) self.SetTopWindow(self.shellFrame) # Add the application object to the sys module's namespace. # This allows a shell user to do: # >>> import sys # >>> sys.application.whatever import sys sys.application = self return true #---------------------------------------------------------------------- hasCrust=1 except ImportError: hasCrust=0 def percent(s): if s[-1]=="%": v=float(s[:-1])/100.0 else: v=float(s) return v def displayPropertydoc(p): print p.ljust(5), print str(getattr(bp,p)).rjust(16), print getattr(bparm,p).__doc__ def displayDerivedPropertydoc(p): print p.ljust(5), print getattr(bparm,p).__doc__ def explainProperties(): bp=bparm() bp.refresh() print "A bicycling situation is characterized by the following properties:" print print "Property Default Description" for p in bparm.props: displayPropertydoc(p) print print "Derived Properties" print "Property Description" for p in bparm.derived: displayDerivedPropertydoc(p) print def help(shortoptions,longoptions): print "BIKE_POWER" print print "Version: ",__version__ print print "Properties" print "----------" print explainProperties() print print "Options" print "-------" print shortoptions for o in longoptions: print o if __name__=="__main__": import sys if len(sys.argv)>1 and sys.argv[1]=="test": _test() else: import getopt shortoptions="Dv:g:b:e:h:r:t:p:" longoptions=["crust","help","grade=","velocity=", "wcyclist=","wmachine=","vincrement=", "vnumber=","pincrement=","pnumber=", "cw=","rho=","A=", "save=","load=", ] displayparms=1 bp=bparm() try: optlist,args=getopt.getopt(sys.argv[1:],"Dv:g:a:A:b:e:h:r:t:p:", longoptions) except getopt.GetoptError,msg: print msg sys.exit(1) for key,value in optlist: try: if key=="-D": displayparms=0 elif key=="-v" or key=="--velocity": bp.V=pq(value) elif key=="-g" or key=="--grade": bp.G=percent(value) elif key=="-b": bp.BM_r=pq(value) elif key=="-e": bp.E=percent(value) elif key=="-h": bp.H=pq(value) elif key=="-r": bp.R=percent(value) elif key=="-t": bp.T=percent(value) elif key=="-p": bp.P=pq(value) elif key=="--wcyclist": bp.Wc=pq(value) elif key=="--wmachine": bp.Wm=pq(value) elif key=="--cw": bp.cw=float(value) elif key=="--rho": bp.rho=pq(value) elif key=="--A": bp.A=pq(value) elif key=="--save": bp.save(open(value+".bikepwr","wb")) elif key=="--load": bp=load(open(value+".bikepwr","rb")) elif key=="--vincrement": bp.Vincrement=pq(value) elif key=="--vnumber": bp.Vnumber=int(value) elif key=="--pincrement": bp.Pincrement=pq(value) elif key=="--pnumber": bp.Pnumber=int(value) elif key=="--help": help(shortoptions,longoptions) sys.exit(0) elif key=="--crust": if hasCrust: Crust() sys.exit(0) else: print "Cannot run the PyCrust Shell," print "please install wxPython 2.3.2.1+! (see http://www.wxpython.org)" sys.exit(1) else: print "Option",key,"not known" sys.exit(1) except TypeError,msg: print "Error while processing option=",repr(key),", value=",repr(value) print " ",msg sys.exit(1) except SyntaxError,msg: print "Error while processing option=",repr(key) print " please check validity of",repr(value) print " ",msg sys.exit(1) bp.refresh() bp._P.convertToUnit("W") if displayparms: bp.print_parm() print print bp.metricheader() print bp.repr_metric() if bp.Vnumber>1: for i in range(bp.Vnumber-1): bp.V = bp.V+bp.Vincrement print bp.repr_metric() if bp.Pnumber>1: for i in range(bp.pnumber-1): bp.P=bp.P+bp.Pincrement print bp.repr_metric() ## t1() ## print ## t2() ## print ## t3() ## print ## t4()