210
213
return intervalCount() * 4;
213
QwtData *IntervalAerolabData::copy() const
216
QPointF IntervalAerolabData::sample(size_t i) const {
217
return QPointF(x(i), y(i));
220
QRectF IntervalAerolabData::boundingRect() const
215
return new IntervalAerolabData( aerolab, mainWindow );
222
return QRectF(-5000, 5000, 10000, 10000);
219
225
//**********************************************
220
226
//** END IntervalAerolabData **
221
227
//**********************************************
413
419
recalc(new_zoom);
422
//setTitle("no data");
428
Aerolab::setAxisTitle(int axis, QString label)
430
// setup the default fonts
431
QFont stGiles; // hoho - Chart Font St. Giles ... ok you have to be British to get this joke
432
stGiles.fromString(appsettings->value(this, GC_FONT_CHARTLABELS, QFont().toString()).toString());
433
stGiles.setPointSize(appsettings->value(NULL, GC_FONT_CHARTLABELS_SIZE, 8).toInt());
435
QwtText title(label);
436
title.setFont(stGiles);
437
QwtPlot::setAxisFont(axis, stGiles);
438
QwtPlot::setAxisTitle(axis, title);
422
442
Aerolab::adjustEoffset() {
424
444
if (autoEoffset && !altArray.empty()) {
775
* Estimate CdA and Crr usign energy balance in segments defined by
777
* Returns an explanatory error message ff it fails to do the estimation,
778
* otherwise it updates cda and crr and returns an empty error message.
779
* Author: Alejandro Martinez
782
QString Aerolab::estimateCdACrr(RideItem *rideItem)
784
// HARD-CODED DATA: p1->kph
785
const double vfactor = 3.600;
786
const double g = 9.80665;
787
RideFile *ride = rideItem->ride();
791
const RideFileDataPresent *dataPresent = ride->areDataPresent();
792
if(dataPresent->alt && dataPresent->watts) {
793
double dt = ride->recIntSecs();
794
int npoints = ride->dataPoints().size();
795
double X1[npoints], X2[npoints], Egain[npoints];
797
double altInit = 0, vInit = 0;
798
/* For each segment, defined between points with alt != 0,
799
* this loop computes X1, X2 and Egain to verify:
800
* Aero-Loss + RR-Loss = Egain
802
* Aero-Loss = X1[nSgeg] * CdA
803
* RR-Loss = X2[nSgeg] * Crr
804
* are the aero and rr components of the energy loss with
805
* X1[nSeg] = sum(0.5 * rho * headwind*headwind * distance)
806
* X2[nSeg] = sum(totalMass * g * distance)
807
* and the energy gain sums power in the segment with
808
* potential and kinetic variations:
809
* Egain = sum(eta * power * dt) +
810
* totalMass * (g * (altInit - alt) +
811
* 0.5 * (vInit*vInit - v*v))
813
foreach(const RideFilePoint *p1, ride->dataPoints()) {
815
double power = max(0, p1->watts);
816
double v = p1->kph/vfactor;
817
double distance = v * dt;
819
if( dataPresent->headwind ) {
820
headwind = p1->headwind/vfactor;
822
double alt = p1->alt;
823
// start initial segment
824
if (nSeg < 0 && alt != 0) {
826
X1[nSeg] = X2[nSeg] = Egain[nSeg] = 0.0;
830
// accumulate segment data
832
// X1[nSgeg] * CdA == Aero-Loss
833
X1[nSeg] += 0.5 * rho * headwind*headwind * distance;
834
// X2[nSgeg] * Crr == RR-Loss
835
X2[nSeg] += totalMass * g * distance;
837
Egain[nSeg] += eta * power * dt;
839
// close current segment and start a new one
840
if (nSeg >= 0 && alt != 0) {
841
// Add change in potential and kinetic energy
842
Egain[nSeg] += totalMass * (g * (altInit - alt) + 0.5 * (vInit*vInit - v*v));
843
// Start a new segment
845
X1[nSeg] = X2[nSeg] = Egain[nSeg] = 0.0;
850
/* At least two segmentes needed to approximate:
851
* X1 * CdA + X2 * Crr = Egain
852
* which, in matrix form, is:
853
* X * [ CdA ; Crr ] = Egain
854
* and pre-multiplying by X transpose (X'):
855
* X'* X [ CdA ; Crr ] = X' * Egain
856
* which is a system with two equations and two unknowns
857
* A * [ CdA ; Crr ] = B
860
// compute the normal equation: A * [ CdA ; Crr ] = B
863
double A11 = 0, A12 = 0, A21 = 0, A22 = 0, B1 = 0, B2 = 0;
864
for (int i = 0; i < nSeg; i++) {
865
A11 += X1[i] * X1[i];
866
A12 += X1[i] * X2[i];
867
A21 += X2[i] * X1[i];
868
A22 += X2[i] * X2[i];
869
B1 += X1[i] * Egain[i];
870
B2 += X2[i] * Egain[i];
872
// Solve the normal equation
873
// A11 * CdA + A12 * Crr = B1
874
// A21 * CdA + A22 * Crr = B2
875
double det = A11 * A22 - A12 * A21;
876
if (fabs(det) > 0.00) {
877
// round and update if the values are in Aerolab's range
878
double cda = floor(10000 * (A22 * B1 - A12 * B2) / det + 0.5) / 10000;
879
double crr = floor(1000000 * (A11 * B2 - A21 * B1) / det + 0.5) / 1000000;
880
if (cda >= 0.001 and cda <= 1.0 and crr >= 0.0001 and crr <= 0.1) {
883
errMsg = ""; // No error
885
errMsg = tr("Estimates out-of-range");
888
errMsg = tr("At least two segments must be independent");
891
errMsg = tr("At least two segments must be defined");
894
errMsg = tr("Altitude and Power data must be present");
897
errMsg = tr("No ride selected");