1 #include "poweranalysis.h" 
    2 #include <QApplication> 
    3 PowerAnalysis::~PowerAnalysis(){
 
    4     for(
int i = 0; i < 9; i++)
 
    9 PowerAnalysis::PowerAnalysis(){
 
   10     for(
int i = 0; i < 9; i++)
 
   14     setWindowTitle(
"Power Analysis");
 
   17     ip[0 ] = 
new Inputs(
"mass [kg]"          ,0  ,200 ,&(s->
m));
 
   19     ip[2 ] = 
new Inputs(
"temperatur [°C]"    ,-40,50  ,&(s->
T));
 
   20     ip[3 ] = 
new Inputs(
"air pressure [hPa]" ,900,1100,&(s->
p0));
 
   21     ip[4 ] = 
new Inputs(
"wind strength [bft]",0  ,12  ,&(s->ws));
 
   22     ip[5 ] = 
new Inputs(
"wind direction [°]" ,0  ,360 ,&(s->
wd));
 
   23     ip[6 ] = 
new Inputs(
"Pmax [W]"           ,0  ,500 ,&(s->Pmax));
 
   24     ip[7 ] = 
new Inputs(
"Pvd [km/h]"         ,0  ,50  ,&(s->Pvd));
 
   25     ip[8 ] = 
new Inputs(
"PT [km/h]"          ,0  ,10  ,&(s->
PT));
 
   26     ip[9 ] = 
new Inputs(
"cwAmax"             ,0.2,0.7 ,&(s->cwAmax));
 
   27     ip[10] = 
new Inputs(
"cwAmin"             ,0.0,0.7 ,&(s->cwAmin));
 
   28     ip[11] = 
new Inputs(
"cwAvd [km/h]"       ,0  ,50  ,&(s->cwAvd));
 
   33     QGridLayout* L = 
new QGridLayout();
 
   34     L->setContentsMargins(10,0,10,0);
 
   35     L->addWidget(
ip[0],0,0);
 
   36     L->addWidget(
ip[1],1,0);
 
   37     L->addWidget(
ip[2],2,0);
 
   38     L->addWidget(
ip[3],3,0);
 
   39     L->addWidget(&
Pt,0,1,4,2);
 
   41     fwcb.setText(
"fight wind");
 
   43     L->addWidget(&
fwcb,4,0);
 
   44     L->addWidget(
ip[4],5,0);
 
   45     L->addWidget(
ip[5],6,0);
 
   47     L->addWidget(
wl,4,1,3,1);
 
   48     textbox.setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);
 
   49     L->addWidget(&textbox,4,2,3,1);
 
   51     L->addWidget(
ip[6],7,0);
 
   52     L->addWidget(
ip[7],8,0);
 
   53     L->addWidget(
ip[8],9,0);
 
   54     Pvl.setText(
"Power setting");
 
   56     L->addWidget(&Pvl,10,0);
 
   57     L->addWidget(&
Pv,7,1,4,2);
 
   59     L->addWidget(
ip[9 ],11,0);
 
   60     L->addWidget(
ip[10],12,0);
 
   61     L->addWidget(
ip[11],13,0);
 
   62     L->addWidget(
ip[12],14,0);
 
   63     cwAvl.setText(
"cwA setting");
 
   64     L->addWidget(&
cwAvl,15,0);
 
   65     L->addWidget(&
cwAv,11,1,5,2);
 
   67     L->addWidget(
ip[13],16,0);
 
   68     L->addWidget(
ip[14],17,0);
 
   69     bdefault.setText(
"Default");
 
   70     brecalc.setText(
"Recalc");
 
   71     btoggletimestamp.setText(
"Ignore/Subordinate time stamp");
 
   72     L->addWidget(&bdefault,18,0);
 
   73     L->addWidget(&brecalc,18,1);
 
   74     L->addWidget(&btoggletimestamp,18,2);
 
   77     connect(&brecalc,SIGNAL(clicked()),
this,SLOT(
recalc()));
 
   79     connect(
ip[6],SIGNAL(changed()),
this,SLOT(
draw_Pv()));
 
   80     connect(
ip[7],SIGNAL(changed()),
this,SLOT(
draw_Pv()));
 
   81     connect(
ip[8],SIGNAL(changed()),
this,SLOT(
draw_Pv()));
 
   82     connect(&
Pv,SIGNAL(mousex(
double)),
this,SLOT(
update_Pvl(
double)));
 
   84     connect(
ip[9 ],SIGNAL(changed()),
this,SLOT(
draw_cwAv()));
 
   85     connect(
ip[10],SIGNAL(changed()),
this,SLOT(
draw_cwAv()));
 
   86     connect(
ip[11],SIGNAL(changed()),
this,SLOT(
draw_cwAv()));
 
   87     connect(
ip[12],SIGNAL(changed()),
this,SLOT(
draw_cwAv()));
 
   88     connect(
ip[4],SIGNAL(changed()),
wl,SLOT(refresh()));
 
   89     connect(
ip[5],SIGNAL(changed()),
wl,SLOT(refresh()));
 
   91     connect(
wl,SIGNAL(clicked()),
ip[4],SLOT(refresh()));
 
   92     connect(
wl,SIGNAL(clicked()),
ip[5],SLOT(refresh()));
 
   94     for(
int i = 0; i < 15; i++)
 
   95         connect(
ip[i],SIGNAL(changed()),
this,SLOT(show_slopespeed_table()));
 
   97     setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);
 
   99     pfs.singlecolor = 
true;
 
  100     pfs.fillcolor = QColor(255,0,0,140).rgba();
 
  108 void PowerAnalysis::init(){
 
  112     for(
int i = 0; i < 100; i++)
 
  115     for(
int i = 0; i < 9; i++)
 
  138     slider = 
new QSlider(Qt::Horizontal);
 
  139     label = 
new QLabel(labeltext);
 
  140     edit = 
new QLineEdit(QString(
"%1").arg(*v));
 
  143     slider->setMinimum(0);
 
  144     slider->setMaximum(1000);
 
  145     if(*v >= min && *v <= max){
 
  146         slider->setValue((*v-min)/(max-min));
 
  148     slider->setValue(500);
 
  149     QBoxLayout* L = 
new QBoxLayout(QBoxLayout::LeftToRight);
 
  150     L->setContentsMargins(0,0,0,0);
 
  151     L->addWidget(label,2);
 
  152     L->addWidget(slider,2);
 
  153     L->addWidget(edit,1);
 
  155     connect(slider,SIGNAL(sliderReleased()),
this,SLOT(
slidertoedit()));
 
  156     connect(edit,SIGNAL(textEdited(QString)),
this,SLOT(
checkedit(QString)));
 
  157     connect(slider,SIGNAL(valueChanged(
int)),
this,SLOT(
sliderpreview(
int)));
 
  158     connect(slider,SIGNAL(actionTriggered(
int)),
this,SLOT(
slidertoedit()));
 
  161     edit->setText(QString(
"%1").arg(smin+1.*slider->value()/1000.*(smax-smin)));
 
  162     *
v = edit->text().toDouble();
 
  166     edit->setText(QString(
"%1").arg(smin+1.*i/1000.*(smax-smin)));
 
  170     double buf = newedit.toDouble(&OK);
 
  176         edit->setText(
"type number > 0");
 
  179     edit->setText(QString(
"%1").arg(*
v));
 
  181 WindLabel::WindLabel(
double *ws, 
double *wd){
 
  182     setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum);
 
  186 void WindLabel::set_para(
double *ws, 
double *wd){
 
  191 void WindLabel::paintEvent(QPaintEvent *){
 
  193     p.drawPixmap(0,0,pm);
 
  195 void WindLabel::resizeEvent(QResizeEvent *){
 
  198 void WindLabel::mouseReleaseEvent(QMouseEvent *event){
 
  199     float r = qMin(width(),height())/2.-2;
 
  200     QPointF mouse = 
event->pos();
 
  201     QPointF center(width()/2,height()/2);
 
  202     QPointF d = mouse-center;
 
  203     *ws = sqrt(d.x()*d.x()+d.y()*d.y())/r*6;
 
  205         *wd = atan2(d.x(),-d.y())/3.1415*180;
 
  208     if(*wd < 0) *wd += 360;
 
  212 void WindLabel::refresh(){
 
  216 void WindLabel::refresh_pixmap(){
 
  217     pm = QPixmap(size());
 
  218     pm.fill(QPalette().color(QPalette::Window));
 
  220     p.setRenderHint(QPainter::Antialiasing);
 
  221     p.setPen(QPen(Qt::black,2));
 
  222     float r = qMin(width(),height())/2.-2;
 
  223     QPointF center(width()/2,height()/2);
 
  224     p.drawEllipse(center,r,r);
 
  225     p.drawEllipse(center,r/2,r/2);
 
  226     p.drawEllipse(center,r/4,r/4);
 
  229     tri[0] = center + QPointF(l/1.*sin(*wd/180*3.1415),
 
  230                              -l/1.*cos(*wd/180*3.1415));
 
  231     tri[1] = center + QPointF(l/3.*sin((*wd+150)/180*3.1415),
 
  232                              -l/3.*cos((*wd+150)/180*3.1415));
 
  233     tri[2] = center + QPointF(l/3.*sin((*wd+210)/180*3.1415),
 
  234                              -l/3.*cos((*wd+210)/180*3.1415));
 
  235     p.setBrush(QBrush(Qt::red));
 
  236     p.drawPolygon(tri,3);
 
  239     for(
int i = 0; i < 100;i++){
 
  246     for(
int i = 0; i < 100;i++){
 
  254     for(
int i = 0; i < 15; i++)
 
  261     Pvl.setText(QString(
"%1 km/h:  P=%2 W").arg(v,0,
'f',1)
 
  265     cwAvl.setText(QString(
"%1 km/h:  cwA=%2").arg(v,0,
'f',1)
 
  266                 .arg(
cwA(v),0,
'f',3));
 
  268 void PowerAnalysis::show_slopespeed_table(){
 
  270     for(
double s  = 0; s < 0.16; s += 0.01)
 
  271         t.append(QString(
"%1 % %2 km/h    %3 % %4 km/h\n")
 
  273                  .arg(
vmax( s,0)*3.6,5,
'f',1)
 
  274                  .arg(s*-100.,3,
'f',0)
 
  275                  .arg(
vmax(-s,0)*3.6,5,
'f',1));
 
  276     textbox.setPlainText(t);
 
  279     double rho0 = 
rho(0);
 
  280     double vl = 0, vm = 10, vr = 50;
 
  281     double Pl = 
P(vl,slope,direction,rho0)-
P(vl);
 
  282     double Pm = 
P(vm,slope,direction,rho0)-
P(vm);
 
  283     double Pr = 
P(vr,slope,direction,rho0)-
P(vr);
 
  286         Pr = 
P(vr,slope,direction,rho0)-
P(vr);
 
  289     while(Pm < -0.3 || Pm > 0.3){
 
  300         vm = (-Pl*vr+Pr*vl)/(Pr-Pl);
 
  301         Pm = 
P(vm,slope,direction,rho0)-
P(vm);
 
  307     return 1./(273.15+
T(h))*(s->
p0*100*exp(-1.2931*9.81/101325*h))/287.058;
 
  311     return s->
T + 0.75*(h/100.);
 
  314     return fermi(v*3.6,s->Pmax,s->
PT,s->Pvd);
 
  317     return v-
vwind*cos(direction-s->
wd/180*3.1415);
 
  322     double veff0 = 
veff(v,direction);
 
  323     return  cwA(veff0)*rho0*0.5*veff0*veff0*veff0
 
  324             +((s->
cr+slope*sqrt(1./(slope*slope+1)))*s->
m*9.81)*v;
 
  327     return s->cwAmin+fermi(v*3.6,s->cwAmax-s->cwAmin,s->
cwAT,s->cwAvd);
 
  330     vwind = 0.836*pow(s->ws,1.5)*0.72*0.5;
 
  344         for(
int i = 0; i < 9; i++){
 
  347             d[i] = 
new double[
count];
 
  350     for(
int i = 0; i < 
count; i++){
 
  356     for(
int i = 2; i < count-2; i++){
 
  357         double d1 = +d[2][i+2]-d[2][i-2];
 
  358         double d2 = +d[2][i+1]-d[2][i-1];
 
  359         if(d1 > M_PI) d1 -= 2*M_PI;
 
  360         if(d1 <-M_PI) d1 += 2*M_PI;
 
  361         if(d2 > M_PI) d2 -= 2*M_PI;
 
  362         if(d2 <-M_PI) d2 += 2*M_PI;
 
  366         d[3][1] = -d[3][0]/2.+d[3][2]/2.;
 
  367         d[3][count-2] = -d[3][count-3]/2.+d[3][count-1]/2.;
 
  370         d[3][0] = -d[3][0]+d[3][1];
 
  371         d[3][count-1] = -d[3][count-2]+d[3][count-1];
 
  374     for(
int i = 0; i < 
count; i++){
 
  376         if(qAbs(d[3][i])<1e-10)
 
  379             d[3][i] = qAbs(ds / 2. / sin(0.5*d[3][i]));
 
  385     for(
int i = 
count-2; i >= 0; i--)
 
  386         d[4][i] = qMin(sqrt(d[4][i+1]*d[4][i+1] + 2*ds*s->
abrake),
 
  393     double slope,rho0,st,E,v = 1,a,Pnom,Pis,dtt,dt,vnew;
 
  397         for(
int i = 0; i < 
count-1;i++){
 
  398             slope = (d[1][i+1]-d[1][i])/ds;
 
  400             vnew =  (d[0][qMin(i+10,count-1)]-d[0][qMax(i-10,0)])
 
  401                    /(d[6][qMin(i+10,count-1)]-d[6][qMax(i-10,0)]);
 
  402             Pis = 
P(vnew,slope,d[2][i],rho0);
 
  403             a = (vnew-v)/(d[6][i+1]-d[6][i]);
 
  404             Pnom = a*v*s->
m + Pis;
 
  407                 dE = Pnom*(d[6][i+1]-d[6][i]);
 
  410                 Ebrake += Pnom*(d[6][i+1]-d[6][i]);
 
  416             d[8][i+1] = d[8][i]+dE;
 
  419         for(
int i = 1; i < count-1;i++)
 
  420             d[7][i] = (d[8][qMin(i+20,count-1)]-d[8][qMax(i-20,0)])
 
  421                      /(d[6][qMin(i+20,count-1)]-d[6][qMax(i-20,0)]);
 
  424         for(
int i = 0; i < 
count-1;i++){
 
  425             slope = (d[1][i+1]-d[1][i])/ds;
 
  427             st = E = dE = dt = 0;
 
  429             if(s->fightwind)    Pnom = 
P(
veff(v,d[2][i]+M_PI));
 
  431             Pis = 
P(v,slope,d[2][i],rho0);
 
  432             a = (Pnom-Pis)/s->
m/v;
 
  433             while(st + v + a/2. < ds){
 
  438                 if(s->fightwind)    Pnom = 
P(
veff(v,d[2][i]));
 
  440                 Pis = 
P(v,slope,d[2][i],rho0);
 
  441                 a = (Pnom-Pis)/s->
m/v;
 
  445                     Pnom = a*v*s->
m + Pis;
 
  448             dtt = (-v+sqrt(2*(ds-st)*a+v*v))/a;
 
  456                 d[6][i+1] = d[6][i]+dt;
 
  458                 d[8][i+1] = d[8][i]+dE;
 
  462                 dt = 2*ds/(d[4][i+1]+d[5][i]);
 
  463                 a = (d[4][i+1]-d[5][i])/dt;
 
  466                 double jmax = ceil(dt);
 
  467                 for(
double j = 0; j < jmax; j++){
 
  468                     v = d[5][i] + (j+0.5)/jmax*(d[4][i+1]-d[5][i]);
 
  469                     Pis = 
P(v,slope,d[2][i],rho0);
 
  470                     Pnom = a*v*s->
m + Pis;
 
  475                         Ebrake += Pnom *dt/jmax;
 
  478                 d[5][i+1] = d[4][i+1];
 
  479                 d[6][i+1] = d[6][i]+dt;
 
  481                 d[8][i+1] = d[8][i]+dE;
 
  487     for(
int i = 0; i < 
count;i++)
 
  490     for(
int i = 0; i < 100;i++){
 
  495     for(
int i = 1; i < 
count;i++){
 
  496         index = qMax(0,qMin(
int(floor(d[7][i]/maxP*100)),99));
 
  497         plotdata[1][index] += d[6][i]-d[6][i-1];
 
  504     double Eflat = 
P(d[0][count-1]/d[6][count-1],0,s->
wd+90,
rho(0))
 
  506     textbox.setPlainText(QString(
"<v> = %1\n<P> = %2 W\n%3% Eflat\n%4% Ebrake\n%5% Eclimb+wind")
 
  507                          .arg(d[0][count-1]/d[6][count-1]*3.6)
 
  508                          .arg(Eout/d[6][count-1])
 
  509                          .arg(Eflat/Eout*100,100,
'f',0)
 
  510                          .arg(-Ebrake/Eout*100,100,
'f',0)
 
  511                          .arg((Eout+Ebrake-Eflat)/Eout*100,100,
'f',0));
 
  514                                double* time, 
int count){
 
  519     for(
int i = 0;i < this->
count; i++){
 
  520         while(d[0][i] > timed[r]*1000 && r < count)
 
  522         if(d[0][i] <= timed[r]*1000 && timed[r] > timed[r-1]){
 
  523             d[6][i] =( (d[0][i]      -timed[r-1]*1000)*time[r]
 
  524                       +(timed[r]*1000-        d[0][i])*time[r-1])
 
  525                      /(timed[r]*1000-timed[r-1]*1000)-t0;
 
  526             if(i > 0 && d[6][i] == d[6][i-1])
 
  530             d[6][i] = d[6][qMax(0,i-1)]+1;
 
  544         return Eout/d[6][
count-1];
 
  550     int index = int(ceil(
count*d*1000/this->d[0][
count-1]));
 
  551     if(index<count && index>=0)
 
  552         return this->d[6][index];
 
  559     int index = int(ceil(
count*d*1000/this->d[0][
count-1]));
 
  560     if(index<count && index>=0)
 
  561         return this->d[5][index]*3.6;
 
  567     int index = int(ceil(
count*d*1000/this->d[0][
count-1]));
 
  568     if(index<count && index>=0)
 
  569         return this->d[7][index];
 
  575     int index1 = int(ceil(
count*dmin*1000/d[0][
count-1]));
 
  576     int index2 = int(ceil(
count*dmax*1000/d[0][
count-1]));
 
  577     if(index1<count && index1>=0 && index2<count && index2>=0
 
  578        && d[6][index2]>d[6][index1])
 
  579         return  (d[0][index2]-d[0][index1])
 
  580                /(d[6][index2]-d[6][index1])*3.6;
 
  586     int index1 = int(ceil(
count*dmin*1000/d[0][
count-1]));
 
  587     int index2 = int(ceil(
count*dmax*1000/d[0][
count-1]));
 
  588     if(index1<count && index1>=0 && index2<count && index2>=0
 
  589        && d[6][index2]>d[6][index1])
 
  590         return  (d[8][index2]-d[8][index1])
 
  591                /(d[6][index2]-d[6][index1]);
 
  638         textbox.setPlainText(
"Calculation will be based on user defined, velocity dependend power output.");
 
  644         for(
int i = 0; i < 
count; i++)
 
  645             if(d[6][i] == d[6][i-1])
 
  648             textbox.setPlainText(
"Calculation will be based on current time/velocity stamps.");
 
  652             textbox.setPlainText(
"Time/velocity stamps are not valid, stamps will be ignored.");
 
double get_P()
return average power 
bool showreduceddata
average data over multiple screen pixels to sensfully show fill color 
void track_to_data(Track *t)
extracs calculation data 
double ** get_vdata()
returns data array pointer 
double ** get_Pdata()
returns data array pointer 
provide all user settings 
double vwind
wind velocity 
double T
Temperature at sea level [°C]. 
double * plotdata[2]
container to collect plotting data 
plot2D Pv
user defined power output 
double rho(double h)
physics: air pressure dependend from height (barometric formula) 
void update_cwAl(double v)
update info label 
int get_datacount()
returns data array length 
double abrake
maximum brake accelartion [m/s²] 
double cwA(double v)
velocity depend cwA value, parametrised with fermi function 
void update_Pvl(double v)
update info label 
void set_default_refresh()
reset PowerAnalysisSetting s 
setting for poweranalysis 
short extract_h(double d, double *h)
creates double for height refering distance point d 
plot2D cwAv
user defined cwA value 
double get_v()
return average velocity 
void recalced()
informs about finished calculation 
void draw_Pv()
draw P(v) distriubtion 
double vmax(double slope, double direction)
physics: calculate the resulting velocity from P(v) at specific slope 
define wind strength and direction by clicking a wind rose picture 
official representation of Track in BTP3 
double aradial
maximum radial accelaration [m/s²] 
double get_length()
returns Track length [km] 
double bft_to_mpros()
wind speed in m/s from beaufort 
double T(double h)
physics: temperature from height with 0.7 K/100m temperature decrease 
void set_default(PowerAnalysisSetting *s)
apply default setting to the PowerAnalysisSetting s 
void toggle_timestamp()
makes poweranalysis ignoring or subordinate time stamp 
double P(double v)
velocity depend output power, parametrised with fermi function 
QLabel cwAvl
plot2d info area 
WindLabel * wl
wind visualization 
QCheckBox fwcb
fight wind checkbox 
Inputs * ip[15]
user interface 
double cwAT
Fermi CwA function parameter[,,km/h,]. 
void calc_power()
execute the calculation 
double get_t(double d)
interpolate time in data 
double cr
rolling resistance 
void set_data(double **dataxy, int count)
setting one dataset and repaint plot 
void draw_cwAv()
draw cwA(v) distriubtion 
bool timeexisting
is time data existing? 
int count
data array length 
double PT
Fermi Power function parameter [km/h,W,km/h]. 
double m
system weigth [kg] 
double wd
Wind strenght ws [bft] and direction wd [°]. 
int isready()
returns whether data is loaded 
PowerAnalysisSetting * s
assigned settings to be used 
void calc_track(Track *t)
calc power from P(v) distribution 
void settings(ProfilSettings *s)
asign settings to plot 
void toggle_fightwind()
switch wind handling in fightwind of PowerAnalysisSetting 
double veff(double v, double direction)
physics: total air speed from movement speed v and wind 
short extract_azimtut(double d, double *azimut)
extract way direction at distance point d 
plot2D Pt
calculated power-time distriubtion 
double p0
air pressure at sea level [hPa]