#ifdef __cplusplus
extern "C" {
#endif
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#ifdef __cplusplus
}
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* XXX andere Compiler ? */
#ifndef __inline__
#ifndef __GNUC__
#define __inline__
#endif /* __GNUC__ */
#endif /* __inline__ */
#undef MYDEBUG
/* 5.004 to 5.006 compatibility */
#ifndef SvPV_nolen
# ifdef PL_na
# define SvPV_nolen(s) SvPV(s,PL_na)
# else
# define SvPV_nolen(s) SvPV(s,na)
# endif
#endif
/* XXX Bei Bedarf die folgenden Makros zusammenfassen */
#define BIKEPOWER_ACCSTRINGNUMBER_VAR(member,var) \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(self); \
PUTBACK; \
count = perl_call_method(#member, G_SCALAR); \
SPAGAIN; \
if (count != 1) \
croak("method call " #member " returned nothing"); \
{ SV *s; s = POPs; var = atof(SvPV_nolen(s)); }\
PUTBACK; \
FREETMPS; \
LEAVE;
#define BIKEPOWER_ACCSTRING_VAR(member,var) \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(self); \
PUTBACK; \
count = perl_call_method(#member, G_SCALAR); \
SPAGAIN; \
if (count != 1) \
croak("method call " #member " returned nothing"); \
{ SV *s; s = POPs; strncpy(var, SvPV_nolen(s), 10); }\
PUTBACK; \
FREETMPS; \
LEAVE;
#define BIKEPOWER_ACCBOOL_VAR(member,var) \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(self); \
PUTBACK; \
count = perl_call_method(#member, G_SCALAR); \
SPAGAIN; \
if (count != 1) \
croak("method call " #member " returned nothing"); \
{ SV *s; s = POPs; var = SvTRUE(s); }\
PUTBACK; \
FREETMPS; \
LEAVE;
#define BIKEPOWER_ACC_VAR(member,type,var) \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(self); \
PUTBACK; \
count = perl_call_method(#member, G_SCALAR); \
SPAGAIN; \
if (count != 1) \
croak("method call " #member " returned nothing"); \
var = POP ## type; \
PUTBACK; \
FREETMPS; \
LEAVE;
#define BIKEPOWER_ACC(member,type) \
BIKEPOWER_ACC_VAR(member,type,member)
#define BIKEPOWER_MUT(member,arg) \
ENTER; \
SAVETMPS; \
PUSHMARK(SP); \
XPUSHs(self); \
XPUSHs(sv_2mortal(arg)); \
PUTBACK; \
count = perl_call_method(#member, G_DISCARD); \
FREETMPS; \
LEAVE; \
#define SQR(a) ((a)*(a))
typedef SV* BikePower;
MODULE = BikePower PACKAGE = BikePower
PROTOTYPES: DISABLE
void
calcXS(self)
BikePower self;
PREINIT:
double eff_H, A_c, R, A2;
double F_a, F_r, F_g, F, BM, consumption, human_efficiency;
double headwind;
int cross_wind, imperial;
char *A_c_str, *R_str, given[10];
double V_lo = 0, V = 64, V_hi = 128;
double A1, transmission_efficiency, grade, total_weight_N, power;
double P_try, P_t;
HV *out;
SV **tmp;
int count;
CODE:
dSP;
BIKEPOWER_ACC(headwind,n);
BIKEPOWER_ACC(cross_wind,i);
eff_H = headwind * (cross_wind ? .7 : 1);
BIKEPOWER_ACCSTRINGNUMBER_VAR(A_c,A_c);
BIKEPOWER_ACCSTRINGNUMBER_VAR(R,R);
BIKEPOWER_ACC_VAR(calc_A2,n,A2);
BIKEPOWER_ACCSTRING_VAR(given,given);
BIKEPOWER_ACC(A1,n);
BIKEPOWER_ACC(total_weight_N,n);
BIKEPOWER_ACC(grade,n);
BIKEPOWER_ACC(transmission_efficiency,n);
if (*given == 'P' || *given == 'C') {
/* Given P, solve for V by bisection search
True Velocity lies in the interval [V_lo, V_hi].
*/
BIKEPOWER_ACC(power,n);
while (V - V_lo > 0.001) {
F_a = A2 * SQR(V+eff_H) + A1 * (V + eff_H);
if (V + eff_H < 0)
F_a *= -1;
P_try = (V/transmission_efficiency) *
(F_a + (R + grade) * total_weight_N);
if (P_try < power)
V_lo = V;
else
V_hi = V;
V = 0.5 * (V_lo + V_hi);
}
BIKEPOWER_MUT(velocity, newSVnv(V));
} else {
BIKEPOWER_ACC_VAR(velocity,n,V);
}
/* Calculate the force (+/-) of the air */
F_a = A2 * SQR(V + eff_H) + A1 * (V + eff_H);
if (V + eff_H < 0)
F_a *= -1;
/* Calculate the force or rolling restance */
F_r = R * total_weight_N;
/* Calculate the force (+/-) of the grade */
F_g = grade * total_weight_N;
/* Calculate the total force */
F = F_a + F_r + F_g;
/* Calculate Power in Watts */
power = V * F / transmission_efficiency;
BIKEPOWER_MUT(power, newSVnv(power));
/* Calculate Calories and drivetrain loss */
BIKEPOWER_ACC(BM,n);
if (power > 0) {
double human_efficiency, BM;
BIKEPOWER_ACC(human_efficiency,n);
consumption = power/human_efficiency + BM;
P_t = (1.0 - transmission_efficiency) * power;
} else {
consumption = power/human_efficiency + BM;
P_t = 0.0;
}
BIKEPOWER_MUT(consumption, newSVnv(consumption));
tmp = hv_fetch((HV*)SvRV(self), "_out", 4, 1);
if (!SvROK(*tmp) || SvTYPE(SvRV(*tmp)) != SVt_PVHV) {
out = newHV();
hv_store((HV*)SvRV(self), "_out", 4, newRV_inc((SV*)out), 0); /* inc oder noinc XXXX? */
} else {
out = (HV*)SvRV(*tmp);
}
hv_store(out, "Pa", 2, newSVnv(V * F_a),0);
hv_store(out, "Pr", 2, newSVnv(V * F_r),0);
hv_store(out, "Pg", 2, newSVnv(V * F_g),0);
hv_store(out, "Pt", 2, newSVnv(P_t),0);
hv_store(out, "P", 1, newSVnv(power),0);
hv_store(out, "hp", 2, newSVnv(power/SvNV(perl_get_sv("BikePower::Watts__per__horsepower",1))),0);
hv_store(out, "heat", 4, newSVnv(consumption-(BM+power)),0);
hv_store(out, "C", 1, newSVnv(consumption),0);
hv_store(out, "B", 1, newSVnv(BM),0);
BIKEPOWER_ACCBOOL_VAR(imperial,imperial);
if (!imperial) {
double velocity_kmh;
BIKEPOWER_ACC(velocity_kmh,n);
hv_store(out, "V", 1, newSVnv(velocity_kmh),0);
hv_store(out, "F", 1, newSVnv(SvNV(perl_get_sv("BikePower::kg__per__Nt",1))*F),0);
hv_store(out, "kJh", 3, newSVnv(consumption*SvNV(perl_get_sv("BikePower::Watts__per__Cal_hr",1))),0); /* really Cal/hr */
} else {
hv_store(out, "V", 1, newSVnv(V),0);
hv_store(out, "F", 1, newSVnv(F/SvNV(perl_get_sv("BikePower::Nt__per__lb",1))*F),0);
hv_store(out, "kJh", 3, newSVnv(consumption*SvNV(perl_get_sv("BikePower::Watts__per__Cal_hr",1))),0); /* really Cal/hr */
}