mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
Added headwind to Aerolab calculation for iAero
Added a headwind data field, which is available when using an iAero head unit, to dramatically improve the calculation of Chung analysis for users of more recent iAero devices. All other data files than the iAero have the headwind term set to zero when they append a point.
This commit is contained in:
committed by
Sean Rhea
parent
afdc862cc2
commit
a02bfaf810
@@ -158,6 +158,10 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) {
|
||||
// Unpack:
|
||||
double power = max(0, p1->watts);
|
||||
double v = p1->kph/vfactor;
|
||||
double headwind = v;
|
||||
if( dataPresent->headwind ) {
|
||||
headwind = p1->headwind/vfactor;
|
||||
}
|
||||
double f = 0.0;
|
||||
double a = 0.0;
|
||||
d += v * dt;
|
||||
@@ -172,7 +176,7 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) {
|
||||
}
|
||||
|
||||
f *= eta; // adjust for drivetrain efficiency if using a crank-based meter
|
||||
double s = slope( f, a, m, crr, cda, rho, v );
|
||||
double s = slope( f, a, m, crr, cda, rho, headwind );
|
||||
double de = s * v * dt;
|
||||
|
||||
e += de;
|
||||
|
||||
@@ -237,7 +237,7 @@ RideFile *Computrainer3dpFileReader::openRideFile(QFile & file,
|
||||
// special case first data point
|
||||
rideFile->appendPoint((double) ms/1000, (double) cad,
|
||||
(double) hr, km, speed, 0.0, watts,
|
||||
altitude, 0, 0, 0);
|
||||
altitude, 0, 0, 0.0, 0);
|
||||
}
|
||||
// while loop since an interval in the .3dp file might
|
||||
// span more than one CT_EMIT_MS interval
|
||||
@@ -285,6 +285,7 @@ RideFile *Computrainer3dpFileReader::openRideFile(QFile & file,
|
||||
interpol_alt,
|
||||
0, // lon
|
||||
0, // lat
|
||||
0.0, // headwind
|
||||
0);
|
||||
|
||||
// reset averaging sums
|
||||
|
||||
@@ -142,6 +142,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
else if (lineno > unitsHeader) {
|
||||
double minutes,nm,kph,watts,km,cad,alt,hr,dfpm;
|
||||
double lat = 0.0, lon = 0.0;
|
||||
double headwind = 0.0;
|
||||
int interval;
|
||||
int pause;
|
||||
if (!ergomo && !iBike) {
|
||||
@@ -175,6 +176,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
if( iBikeVersion >= 11 && ( dfpm > 0.0 || dfpmExists ) ) {
|
||||
dfpmExists = true;
|
||||
watts = dfpm;
|
||||
headwind = line.section(',', 1, 1).toDouble();
|
||||
}
|
||||
else {
|
||||
watts = line.section(',', 2, 2).toDouble();
|
||||
@@ -195,6 +197,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
km *= KM_PER_MILE;
|
||||
kph *= KM_PER_MILE;
|
||||
alt *= METERS_PER_FOOT;
|
||||
headwind *= KM_PER_MILE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -234,7 +237,7 @@ RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
watts = 0;
|
||||
|
||||
rideFile->appendPoint(minutes * 60.0, cad, hr, km,
|
||||
kph, nm, watts, alt, lat, lon, interval);
|
||||
kph, nm, watts, alt, lat, lon, headwind, interval);
|
||||
}
|
||||
++lineno;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ GcFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
for (QDomElement sample = samples.firstChildElement("sample");
|
||||
!sample.isNull(); sample = sample.nextSiblingElement("sample")) {
|
||||
double secs, cad, hr, km, kph, nm, watts, alt, lon, lat;
|
||||
double headwind = 0.0;
|
||||
secs = sample.attribute("secs", "0.0").toDouble();
|
||||
cad = sample.attribute("cad", "0.0").toDouble();
|
||||
hr = sample.attribute("hr", "0.0").toDouble();
|
||||
@@ -101,7 +102,7 @@ GcFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
lat = sample.attribute("lat", "0.0").toDouble();
|
||||
while ((interval < intervalStops.size()) && (secs >= intervalStops[interval]))
|
||||
++interval;
|
||||
rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, interval);
|
||||
rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, interval);
|
||||
if (!recIntSet) {
|
||||
rideFile->setRecIntSecs(sample.attribute("len").toDouble());
|
||||
recIntSet = true;
|
||||
|
||||
@@ -125,7 +125,7 @@ RideFile *ManualFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
interval = 0;
|
||||
|
||||
rideFile->appendPoint(minutes * 60.0, cad, hr, km,
|
||||
kph, nm, watts, alt, 0.0, 0.0, interval);
|
||||
kph, nm, watts, alt, 0.0, 0.0, 0.0, interval);
|
||||
QMap<QString,QString> bsm;
|
||||
bsm.insert("value", QString("%1").arg(bs));
|
||||
rideFile->metricOverrides.insert("skiba_bike_score", bsm);
|
||||
|
||||
@@ -200,7 +200,7 @@ this differently
|
||||
next_interval = intervals.at(interval);
|
||||
}
|
||||
}
|
||||
rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, interval);
|
||||
rideFile->appendPoint(seconds, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, 0.0, interval);
|
||||
//fprintf(stderr, " %f, %f, %f, %f, %f, %f, %f, %d\n", seconds, cad, hr, km, kph, nm, watts, alt, interval);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ QuarqParser::incrementTime( const double new_time )
|
||||
while (time_diff > seconds_from_start) {
|
||||
|
||||
rideFile->appendPoint(seconds_from_start, cad, hr, km,
|
||||
kph, nm, watts, 0, 0.0, 0.0, 0);
|
||||
kph, nm, watts, 0, 0.0, 0.0, 0.0, 0);
|
||||
|
||||
seconds_from_start += SAMPLE_INTERVAL;
|
||||
}
|
||||
@@ -114,7 +114,7 @@ QuarqParser::endElement( const QString&, const QString&, const QString& qName)
|
||||
// flush one last data point
|
||||
if (qName == "Qollector") {
|
||||
rideFile->appendPoint(seconds_from_start, cad, hr, km,
|
||||
kph, nm, watts, 0, 0.0, 0.0, 0);
|
||||
kph, nm, watts, 0, 0.0, 0.0, 0.0, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
@@ -70,7 +70,7 @@ time_cb(struct tm *, time_t since_epoch, void *context)
|
||||
double secs = since_epoch - state->start_since_epoch;
|
||||
state->rideFile->appendPoint(secs, 0.0, 0.0,
|
||||
state->last_miles * KM_PER_MILE, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, state->last_interval);
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, state->last_interval);
|
||||
state->last_secs = secs;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ data_cb(double secs, double nm, double mph, double watts, double miles, double a
|
||||
|
||||
ReadState *state = (ReadState*) context;
|
||||
state->rideFile->appendPoint(secs, cad, hr, miles * KM_PER_MILE,
|
||||
mph * KM_PER_MILE, nm, watts, alt, 0.0, 0.0, interval);
|
||||
mph * KM_PER_MILE, nm, watts, alt, 0.0, 0.0, 0.0, interval);
|
||||
state->last_secs = secs;
|
||||
state->last_miles = miles;
|
||||
state->last_interval = interval;
|
||||
|
||||
@@ -231,10 +231,10 @@ QStringList RideFileFactory::listRideFiles(const QDir &dir) const
|
||||
|
||||
void RideFile::appendPoint(double secs, double cad, double hr, double km,
|
||||
double kph, double nm, double watts, double alt,
|
||||
double lon, double lat, int interval)
|
||||
double lon, double lat, double headwind, int interval)
|
||||
{
|
||||
dataPoints_.append(new RideFilePoint(secs, cad, hr, km, kph,
|
||||
nm, watts, alt, lon, lat, interval));
|
||||
nm, watts, alt, lon, lat, headwind, interval));
|
||||
dataPresent.secs |= (secs != 0);
|
||||
dataPresent.cad |= (cad != 0);
|
||||
dataPresent.hr |= (hr != 0);
|
||||
@@ -245,5 +245,6 @@ void RideFile::appendPoint(double secs, double cad, double hr, double km,
|
||||
dataPresent.alt |= (alt != 0);
|
||||
dataPresent.lon |= (lon != 0);
|
||||
dataPresent.lat |= (lat != 0);
|
||||
dataPresent.headwind |= (headwind != 0);
|
||||
dataPresent.interval |= (interval != 0);
|
||||
}
|
||||
|
||||
@@ -43,23 +43,23 @@
|
||||
|
||||
struct RideFilePoint
|
||||
{
|
||||
double secs, cad, hr, km, kph, nm, watts, alt, lon, lat;;
|
||||
double secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind;;
|
||||
int interval;
|
||||
RideFilePoint() : secs(0.0), cad(0.0), hr(0.0), km(0.0), kph(0.0),
|
||||
nm(0.0), watts(0.0), alt(0.0), lon(0.0), lat(0.0), interval(0) {}
|
||||
nm(0.0), watts(0.0), alt(0.0), lon(0.0), lat(0.0), headwind(0.0), interval(0) {}
|
||||
RideFilePoint(double secs, double cad, double hr, double km, double kph,
|
||||
double nm, double watts, double alt, double lon, double lat, int interval) :
|
||||
double nm, double watts, double alt, double lon, double lat, double headwind, int interval) :
|
||||
secs(secs), cad(cad), hr(hr), km(km), kph(kph), nm(nm),
|
||||
watts(watts), alt(alt), lon(lon), lat(lat), interval(interval) {}
|
||||
watts(watts), alt(alt), lon(lon), lat(lat), headwind(headwind), interval(interval) {}
|
||||
};
|
||||
|
||||
struct RideFileDataPresent
|
||||
{
|
||||
bool secs, cad, hr, km, kph, nm, watts, alt, lon, lat, interval;
|
||||
bool secs, cad, hr, km, kph, nm, watts, alt, lon, lat, headwind, interval;
|
||||
// whether non-zero data of each field is present
|
||||
RideFileDataPresent():
|
||||
secs(false), cad(false), hr(false), km(false),
|
||||
kph(false), nm(false), watts(false), alt(false), lon(false), lat(false), interval(false) {}
|
||||
kph(false), nm(false), watts(false), alt(false), lon(false), lat(false), headwind(false), interval(false) {}
|
||||
};
|
||||
|
||||
struct RideFileInterval
|
||||
@@ -106,7 +106,7 @@ class RideFile
|
||||
|
||||
void appendPoint(double secs, double cad, double hr, double km,
|
||||
double kph, double nm, double watts, double alt,
|
||||
double lon, double lat, int interval);
|
||||
double lon, double lat, double headwind, int interval);
|
||||
|
||||
const QList<RideFileInterval> &intervals() const { return intervals_; }
|
||||
void addInterval(double start, double stop, const QString &name) {
|
||||
|
||||
@@ -184,7 +184,7 @@ RideSummaryWindow::htmlSummary() const
|
||||
if (p->secs >= interval.stop)
|
||||
break;
|
||||
f.appendPoint(p->secs, p->cad, p->hr, p->km, p->kph, p->nm,
|
||||
p->watts, p->alt, p->lon, p->lat, 0);
|
||||
p->watts, p->alt, p->lon, p->lat, p->headwind, 0);
|
||||
}
|
||||
if (f.dataPoints().size() == 0) {
|
||||
// Interval empty, do not compute any metrics
|
||||
|
||||
@@ -140,7 +140,7 @@ SplitRideDialog::CreateNewRideFile(const RideFile *ride, int nRecStart, int nRec
|
||||
{
|
||||
RideFilePoint *pPoint = ride->dataPoints().at(nItem);
|
||||
newRideFile->appendPoint(pPoint->secs-pointStart->secs, pPoint->cad, pPoint->hr, pPoint->km - pointStart->km,
|
||||
pPoint->kph, pPoint->nm, pPoint->watts, pPoint->alt, pPoint->lon, pPoint->lat, pPoint->interval-pointStart->interval);
|
||||
pPoint->kph, pPoint->nm, pPoint->watts, pPoint->alt, pPoint->lon, pPoint->lat, pPoint->headwind, pPoint->interval-pointStart->interval);
|
||||
}
|
||||
newRideFile->setDeviceType(ride->deviceType());
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ RideFile *SrmFileReader::openRideFile(QFile &file, QStringList &errorStrings) co
|
||||
km += result->recIntSecs() * kph / 3600.0;
|
||||
|
||||
double nm = watts / 2.0 / PI / cad * 60.0;
|
||||
result->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, interval);
|
||||
result->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, 0.0, 0.0, 0.0, interval);
|
||||
|
||||
++blkidx;
|
||||
if ((blkidx == blockhdrs[blknum].chunkcnt) && (blknum + 1 < blockcnt)) {
|
||||
|
||||
@@ -132,10 +132,11 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName)
|
||||
// for smart recording, the delta_t will not be constant
|
||||
// average all the calculations based on the previous
|
||||
// point.
|
||||
headwind = 0.0;
|
||||
if(rideFile->dataPoints().empty()) {
|
||||
// first point
|
||||
rideFile->appendPoint(secs, cadence, hr, distance,
|
||||
speed, torque, power, alt, lon, lat, lap);
|
||||
speed, torque, power, alt, lon, lat, headwind, lap);
|
||||
}
|
||||
else {
|
||||
// assumption that the change in ride is linear... :)
|
||||
@@ -153,7 +154,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName)
|
||||
if(deltaSecs == 1) {
|
||||
// no smart recording, just insert the data
|
||||
rideFile->appendPoint(secs, cadence, hr, distance,
|
||||
speed, torque, power, alt, lon, lat, lap);
|
||||
speed, torque, power, alt, lon, lat, headwind, lap);
|
||||
}
|
||||
else {
|
||||
for(int i = 1; i <= deltaSecs; i++) {
|
||||
@@ -176,6 +177,7 @@ TcxParser::endElement( const QString&, const QString&, const QString& qName)
|
||||
prevPoint->alt + (deltaAlt * weight),
|
||||
lon, // lon
|
||||
lat, // lat
|
||||
headwind, // headwind
|
||||
lap);
|
||||
}
|
||||
prevPoint = rideFile->dataPoints().back();
|
||||
|
||||
@@ -56,6 +56,7 @@ private:
|
||||
double alt;
|
||||
double lat;
|
||||
double lon;
|
||||
double headwind;
|
||||
};
|
||||
|
||||
#endif // _TcxParser_h
|
||||
|
||||
@@ -414,7 +414,7 @@ WKO_UCHAR *WkoParseRawData(WKO_UCHAR *fb, RideFile *rideFile, QStringList &error
|
||||
|
||||
// !! needs to be modified to support the new alt patch
|
||||
rideFile->appendPoint((double)rtime/1000, cad, hr, km,
|
||||
kph, nm, watts, alt, lon, lat, 0);
|
||||
kph, nm, watts, alt, lon, lat, 0.0, 0);
|
||||
}
|
||||
|
||||
// increment time - even for null records (perhaps especially for null
|
||||
|
||||
Reference in New Issue
Block a user