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]