mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Show kJoules, TSS/BikeScore et al on Train View
The refactoring of the realtime display last year removed the display of metrics such as BikeScore and kJoules. This patch adds more metrics that can be displayed; * Averages for; power, hr, cadence, speed * KJoules of work * Coggan Metrics; NP, TSS, IF, VI * Skiba Metrics; xPower, BikeScore, RI, Skiba VI Also included is an updated default layout to include some of these metrics. Fixes #231
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
DialWindow::DialWindow(MainWindow *mainWindow) :
|
||||
GcWindow(mainWindow), mainWindow(mainWindow)
|
||||
{
|
||||
rolling.resize(150); // enough for 30 seconds at 5hz
|
||||
|
||||
setContentsMargins(0,0,0,0);
|
||||
setInstanceName("Dial");
|
||||
|
||||
@@ -125,6 +127,207 @@ DialWindow::telemetryUpdate(const RealtimeData &rtData)
|
||||
valueLabel->setText(QString("%1").arg(value, 0, 'f', 3));
|
||||
break;
|
||||
|
||||
case RealtimeData::AvgWatts:
|
||||
sum += rtData.value(RealtimeData::Watts);
|
||||
count++;
|
||||
value = sum / count;
|
||||
valueLabel->setText(QString("%1").arg(round(value)));
|
||||
break;
|
||||
|
||||
case RealtimeData::AvgSpeed:
|
||||
sum += rtData.value(RealtimeData::Speed);
|
||||
count++;
|
||||
value = sum / count;
|
||||
if (!mainWindow->useMetricUnits) value *= MILES_PER_KM;
|
||||
valueLabel->setText(QString("%1").arg(value, 0, 'f', 1));
|
||||
break;
|
||||
|
||||
case RealtimeData::AvgCadence:
|
||||
sum += rtData.value(RealtimeData::Cadence);
|
||||
count++;
|
||||
value = sum / count;
|
||||
valueLabel->setText(QString("%1").arg(round(value)));
|
||||
break;
|
||||
|
||||
case RealtimeData::AvgHeartRate:
|
||||
sum += rtData.value(RealtimeData::HeartRate);
|
||||
count++;
|
||||
value = sum / count;
|
||||
valueLabel->setText(QString("%1").arg(round(value)));
|
||||
break;
|
||||
|
||||
// ENERGY
|
||||
case RealtimeData::Joules:
|
||||
sum += rtData.value(RealtimeData::Watts) / 5; // joules
|
||||
valueLabel->setText(QString("%1").arg(round(sum/1000))); // kJoules
|
||||
break;
|
||||
|
||||
// COGGAN Metrics
|
||||
case RealtimeData::NP:
|
||||
case RealtimeData::IF:
|
||||
case RealtimeData::TSS:
|
||||
case RealtimeData::VI:
|
||||
{
|
||||
|
||||
// Update sum of watts for last 30 seconds
|
||||
sum += rtData.value(RealtimeData::Watts);
|
||||
sum -= rolling[index];
|
||||
rolling[index] = rtData.value(RealtimeData::Watts);
|
||||
|
||||
// raise average to the 4th power
|
||||
rollingSum += pow(sum/150,4); // raise rolling average to 4th power
|
||||
count ++;
|
||||
|
||||
// move index on/round
|
||||
index = (index >= 149) ? 0 : index+1;
|
||||
|
||||
// calculate NP
|
||||
double np = pow(rollingSum / (count), 0.25);
|
||||
|
||||
if (series == RealtimeData::NP) {
|
||||
// We only wanted NP so thats it
|
||||
valueLabel->setText(QString("%1").arg(round(np)));
|
||||
|
||||
} else {
|
||||
|
||||
double rif, cp;
|
||||
// carry on and calculate IF
|
||||
if (mainWindow->zones()) {
|
||||
|
||||
// get cp for today
|
||||
int zonerange = mainWindow->zones()->whichRange(QDateTime::currentDateTime().date());
|
||||
if (zonerange >= 0) cp = mainWindow->zones()->getCP(zonerange);
|
||||
else cp = 0;
|
||||
|
||||
} else {
|
||||
cp = 0;
|
||||
}
|
||||
|
||||
if (cp) rif = np / cp;
|
||||
else rif = 0;
|
||||
|
||||
if (series == RealtimeData::IF) {
|
||||
|
||||
// we wanted IF so thats it
|
||||
valueLabel->setText(QString("%1").arg(rif, 0, 'f', 3));
|
||||
|
||||
} else {
|
||||
|
||||
double normWork = np * (rtData.value(RealtimeData::Time) / 1000); // msecs
|
||||
double rawTSS = normWork * rif;
|
||||
double workInAnHourAtCP = cp * 3600;
|
||||
double tss = rawTSS / workInAnHourAtCP * 100.0;
|
||||
|
||||
if (series == RealtimeData::TSS) {
|
||||
|
||||
valueLabel->setText(QString("%1").arg(tss, 0, 'f', 1));
|
||||
|
||||
} else {
|
||||
|
||||
// track average power for VI
|
||||
apsum += rtData.value(RealtimeData::Watts);
|
||||
apcount++;
|
||||
|
||||
double ap = apsum ? apsum / apcount : 0;
|
||||
|
||||
// VI is all that is left!
|
||||
valueLabel->setText(QString("%1").arg(ap ? np / ap : 0, 0, 'f', 3));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
// SKIBA Metrics
|
||||
case RealtimeData::XPower:
|
||||
case RealtimeData::RI:
|
||||
case RealtimeData::BikeScore:
|
||||
case RealtimeData::SkibaVI:
|
||||
{
|
||||
|
||||
static const double exp = 2.0f / ((25.0f / 0.2f) + 1.0f);
|
||||
static const double rem = 1.0f - exp;
|
||||
|
||||
count++;
|
||||
|
||||
if (count < 125) {
|
||||
|
||||
// get up to speed
|
||||
rsum += rtData.value(RealtimeData::Watts);
|
||||
ewma = rsum / count;
|
||||
|
||||
} else {
|
||||
|
||||
// we're up to speed
|
||||
ewma = (rtData.value(RealtimeData::Watts) * exp) + (ewma * rem);
|
||||
}
|
||||
|
||||
sum += pow(ewma, 4.0f);
|
||||
double xpower = pow(sum / count, 0.25f);
|
||||
|
||||
if (series == RealtimeData::XPower) {
|
||||
|
||||
// We wanted XPower!
|
||||
valueLabel->setText(QString("%1").arg(round(xpower)));
|
||||
|
||||
} else {
|
||||
|
||||
double rif, cp;
|
||||
// carry on and calculate IF
|
||||
if (mainWindow->zones()) {
|
||||
|
||||
// get cp for today
|
||||
int zonerange = mainWindow->zones()->whichRange(QDateTime::currentDateTime().date());
|
||||
if (zonerange >= 0) cp = mainWindow->zones()->getCP(zonerange);
|
||||
else cp = 0;
|
||||
|
||||
} else {
|
||||
cp = 0;
|
||||
}
|
||||
|
||||
if (cp) rif = xpower / cp;
|
||||
else rif = 0;
|
||||
|
||||
if (series == RealtimeData::RI) {
|
||||
|
||||
// we wanted IF so thats it
|
||||
valueLabel->setText(QString("%1").arg(rif, 0, 'f', 3));
|
||||
|
||||
} else {
|
||||
|
||||
double normWork = xpower * (rtData.value(RealtimeData::Time) / 1000); // msecs
|
||||
double rawTSS = normWork * rif;
|
||||
double workInAnHourAtCP = cp * 3600;
|
||||
double tss = rawTSS / workInAnHourAtCP * 100.0;
|
||||
|
||||
if (series == RealtimeData::BikeScore) {
|
||||
|
||||
valueLabel->setText(QString("%1").arg(tss, 0, 'f', 1));
|
||||
|
||||
} else {
|
||||
|
||||
// track average power for Relative Intensity
|
||||
apsum += rtData.value(RealtimeData::Watts);
|
||||
apcount++;
|
||||
|
||||
double ap = apsum ? apsum / apcount : 0;
|
||||
|
||||
// RI is all that is left!
|
||||
valueLabel->setText(QString("%1").arg(ap ? xpower / ap : 0, 0, 'f', 3));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
valueLabel->setText(QString("%1").arg(round(value)));
|
||||
break;
|
||||
@@ -135,7 +338,7 @@ DialWindow::telemetryUpdate(const RealtimeData &rtData)
|
||||
void DialWindow::resizeEvent(QResizeEvent * )
|
||||
{
|
||||
// set point size
|
||||
int size = geometry().height()-15;
|
||||
int size = geometry().height()-24;
|
||||
if (size <= 0) size = 4;
|
||||
if (size >= 64) size = 64;
|
||||
|
||||
@@ -158,30 +361,40 @@ void DialWindow::seriesChanged()
|
||||
case RealtimeData::LapTime:
|
||||
case RealtimeData::Distance:
|
||||
case RealtimeData::Lap:
|
||||
case RealtimeData::RI:
|
||||
case RealtimeData::IF:
|
||||
case RealtimeData::VI:
|
||||
case RealtimeData::SkibaVI:
|
||||
case RealtimeData::None:
|
||||
foreground = GColor(CPLOTMARKER);
|
||||
break;
|
||||
|
||||
case RealtimeData::Load:
|
||||
case RealtimeData::BikeScore:
|
||||
case RealtimeData::TSS:
|
||||
foreground = Qt::blue;
|
||||
break;
|
||||
|
||||
case RealtimeData::XPower:
|
||||
case RealtimeData::NP:
|
||||
case RealtimeData::Joules:
|
||||
case RealtimeData::BikeScore:
|
||||
case RealtimeData::Watts:
|
||||
case RealtimeData::AvgWatts:
|
||||
foreground = GColor(CPOWER);
|
||||
break;
|
||||
|
||||
case RealtimeData::Speed:
|
||||
case RealtimeData::AvgSpeed:
|
||||
foreground = GColor(CSPEED);
|
||||
break;
|
||||
|
||||
case RealtimeData::Cadence:
|
||||
case RealtimeData::AvgCadence:
|
||||
foreground = GColor(CCADENCE);
|
||||
break;
|
||||
|
||||
case RealtimeData::HeartRate:
|
||||
case RealtimeData::AvgHeartRate:
|
||||
foreground = GColor(CHEARTRATE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "Zones.h" // for data series types
|
||||
#include "RideFile.h" // for data series types
|
||||
#include "RealtimeData.h" // for realtimedata structure
|
||||
|
||||
@@ -99,8 +100,33 @@ class DialWindow : public GcWindow
|
||||
double instantValue;
|
||||
double avg30, avgLap, avgTotal;
|
||||
double lapNumber;
|
||||
|
||||
// for calculating averages
|
||||
int count;
|
||||
void resetValues() { instantValue = avg30 = avgLap = avgTotal = lapNumber = 0; telemetryUpdate(RealtimeData()); }
|
||||
double sum;
|
||||
|
||||
// for keeping track of rolling averages (max 30s at 5hz)
|
||||
// used by NP and XPower
|
||||
QVector<double> rolling;
|
||||
double rollingSum;
|
||||
int index; // index into rolling (circular buffer)
|
||||
|
||||
// VI/RI makes us track AP too
|
||||
int apcount;
|
||||
int apsum;
|
||||
|
||||
// used by XPower algorithm
|
||||
double rsum, ewma;
|
||||
|
||||
void resetValues() {
|
||||
|
||||
rolling.fill(0.00);
|
||||
rsum = ewma = 0.0f;
|
||||
rollingSum = index = 0;
|
||||
apcount = count = sum = instantValue = avg30 =
|
||||
apsum = avgLap = avgTotal = lapNumber = 0;
|
||||
telemetryUpdate(RealtimeData());
|
||||
}
|
||||
|
||||
// controls
|
||||
QComboBox *seriesSelector;
|
||||
|
||||
@@ -25,7 +25,7 @@ RealtimeData::RealtimeData()
|
||||
{
|
||||
name[0] = '\0';
|
||||
lap = watts = hr = speed = wheelRpm = cadence = load = 0;
|
||||
msecs = lapMsecs = bikeScore = joules = 0;
|
||||
msecs = lapMsecs = /* bikeScore = joules =*/ 0;
|
||||
}
|
||||
|
||||
void RealtimeData::setName(char *name)
|
||||
@@ -68,19 +68,6 @@ void RealtimeData::setDistance(double x)
|
||||
{
|
||||
this->distance = x;
|
||||
}
|
||||
void RealtimeData::setJoules(long x)
|
||||
{
|
||||
this->joules = x;
|
||||
}
|
||||
void RealtimeData::setBikeScore(long x)
|
||||
{
|
||||
this->bikeScore = x;
|
||||
}
|
||||
void RealtimeData::setXPower(long x)
|
||||
{
|
||||
this->xPower = x;
|
||||
}
|
||||
|
||||
const char *
|
||||
RealtimeData::getName() const
|
||||
{
|
||||
@@ -122,18 +109,6 @@ double RealtimeData::getDistance() const
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
long RealtimeData::getJoules() const
|
||||
{
|
||||
return joules;
|
||||
}
|
||||
long RealtimeData::getBikeScore() const
|
||||
{
|
||||
return bikeScore;
|
||||
}
|
||||
long RealtimeData::getXPower() const
|
||||
{
|
||||
return xPower;
|
||||
}
|
||||
|
||||
double RealtimeData::value(DataSeries series) const
|
||||
{
|
||||
@@ -151,15 +126,6 @@ double RealtimeData::value(DataSeries series) const
|
||||
case Distance: return distance;
|
||||
break;
|
||||
|
||||
case Joules: return joules;
|
||||
break;
|
||||
|
||||
case BikeScore: return bikeScore;
|
||||
break;
|
||||
|
||||
case XPower: return xPower;
|
||||
break;
|
||||
|
||||
case Watts: return watts;
|
||||
break;
|
||||
|
||||
@@ -202,8 +168,18 @@ const QList<RealtimeData::DataSeries> &RealtimeData::listDataSeries()
|
||||
seriesList << HeartRate;
|
||||
seriesList << Load;
|
||||
seriesList << BikeScore;
|
||||
seriesList << SkibaVI;
|
||||
seriesList << TSS;
|
||||
seriesList << XPower;
|
||||
seriesList << NP;
|
||||
seriesList << RI;
|
||||
seriesList << IF;
|
||||
seriesList << VI;
|
||||
seriesList << Joules;
|
||||
seriesList << AvgWatts;
|
||||
seriesList << AvgSpeed;
|
||||
seriesList << AvgCadence;
|
||||
seriesList << AvgHeartRate;
|
||||
}
|
||||
return seriesList;
|
||||
}
|
||||
@@ -225,15 +201,33 @@ QString RealtimeData::seriesName(DataSeries series)
|
||||
case LapTime: return tr("Lap Time");
|
||||
break;
|
||||
|
||||
case TSS: return tr("TSS");
|
||||
break;
|
||||
|
||||
case BikeScore: return tr("BikeScore");
|
||||
break;
|
||||
|
||||
case Joules: return tr("Joules");
|
||||
case Joules: return tr("kJoules");
|
||||
break;
|
||||
|
||||
case XPower: return tr("XPower");
|
||||
break;
|
||||
|
||||
case NP: return tr("Normalized Power");
|
||||
break;
|
||||
|
||||
case IF: return tr("Intensity Factor");
|
||||
break;
|
||||
|
||||
case RI: return tr("Relative Intensity");
|
||||
break;
|
||||
|
||||
case SkibaVI: return tr("Skiba Variability Index");
|
||||
break;
|
||||
|
||||
case VI: return tr("Variability Index");
|
||||
break;
|
||||
|
||||
case Distance: return tr("Distance");
|
||||
break;
|
||||
|
||||
@@ -251,6 +245,18 @@ QString RealtimeData::seriesName(DataSeries series)
|
||||
|
||||
case Load: return tr("Target Power");
|
||||
break;
|
||||
|
||||
case AvgWatts: return tr("Average Power");
|
||||
break;
|
||||
|
||||
case AvgSpeed: return tr("Average Speed");
|
||||
break;
|
||||
|
||||
case AvgHeartRate: return tr("Average Heartrate");
|
||||
break;
|
||||
|
||||
case AvgCadence: return tr("Average Cadence");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ public:
|
||||
// abstract to dataseries
|
||||
enum dataseries { None=0, Time, LapTime, Distance, Lap,
|
||||
Watts, Speed, Cadence, HeartRate, Load,
|
||||
XPower, BikeScore, Joules };
|
||||
XPower, BikeScore, RI, Joules, SkibaVI,
|
||||
NP, TSS, IF, VI,
|
||||
AvgWatts, AvgSpeed, AvgCadence, AvgHeartRate };
|
||||
|
||||
typedef enum dataseries DataSeries;
|
||||
double value(DataSeries) const;
|
||||
@@ -69,9 +71,6 @@ public:
|
||||
long getMsecs() const;
|
||||
long getLapMsecs() const;
|
||||
double getDistance() const;
|
||||
long getBikeScore() const;
|
||||
long getJoules() const;
|
||||
long getXPower() const;
|
||||
long getLap() const;
|
||||
|
||||
|
||||
@@ -87,8 +86,6 @@ private:
|
||||
long lap;
|
||||
long msecs;
|
||||
long lapMsecs;
|
||||
long bikeScore, joules, xPower;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -201,7 +201,6 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
|
||||
disk_timer = new QTimer(this);
|
||||
stream_timer = new QTimer(this);
|
||||
load_timer = new QTimer(this);
|
||||
metrics_timer = new QTimer(this);
|
||||
|
||||
session_time = QTime();
|
||||
session_elapsed_msec = 0;
|
||||
@@ -222,13 +221,10 @@ TrainTool::TrainTool(MainWindow *parent, const QDir &home) : GcWindow(parent), h
|
||||
displaySpeed = displayCadence = displayGradient = displayLoad = 0;
|
||||
manualOverride = false;
|
||||
|
||||
rideFile = boost::shared_ptr<RideFile>(new RideFile(QDateTime::currentDateTime(),1));
|
||||
|
||||
connect(gui_timer, SIGNAL(timeout()), this, SLOT(guiUpdate()));
|
||||
connect(disk_timer, SIGNAL(timeout()), this, SLOT(diskUpdate()));
|
||||
connect(stream_timer, SIGNAL(timeout()), this, SLOT(streamUpdate()));
|
||||
connect(load_timer, SIGNAL(timeout()), this, SLOT(loadUpdate()));
|
||||
connect(metrics_timer, SIGNAL(timeout()), this, SLOT(metricsUpdate()));
|
||||
|
||||
configChanged(); // will reset the workout tree
|
||||
|
||||
@@ -603,9 +599,6 @@ void TrainTool::Start() // when start button is pressed
|
||||
}
|
||||
}
|
||||
|
||||
// create a new rideFile
|
||||
rideFile = boost::shared_ptr<RideFile>(new RideFile(QDateTime::currentDateTime(),1));
|
||||
|
||||
|
||||
// stream
|
||||
if (status & RT_STREAMING) {
|
||||
@@ -613,7 +606,6 @@ void TrainTool::Start() // when start button is pressed
|
||||
}
|
||||
|
||||
gui_timer->start(REFRESHRATE); // start recording
|
||||
metrics_timer->start(METRICSRATE);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -633,7 +625,6 @@ void TrainTool::Pause() // pause capture to recalibrate
|
||||
deviceController->restart();
|
||||
setPauseText(tr("Pause"));
|
||||
gui_timer->start(REFRESHRATE);
|
||||
metrics_timer->start(METRICSRATE);
|
||||
if (status & RT_STREAMING) stream_timer->start(STREAMRATE);
|
||||
if (status & RT_RECORDING) disk_timer->start(SAMPLERATE);
|
||||
load_period.restart();
|
||||
@@ -650,7 +641,6 @@ void TrainTool::Pause() // pause capture to recalibrate
|
||||
setPauseText(tr("Un-Pause"));
|
||||
status |=RT_PAUSED;
|
||||
gui_timer->stop();
|
||||
metrics_timer->stop();
|
||||
if (status & RT_STREAMING) stream_timer->stop();
|
||||
if (status & RT_RECORDING) disk_timer->stop();
|
||||
if (status & RT_WORKOUT) load_timer->stop();
|
||||
@@ -676,7 +666,6 @@ void TrainTool::Stop(int deviceStatus) // when stop button is pressed
|
||||
deviceController = NULL;
|
||||
|
||||
gui_timer->stop();
|
||||
metrics_timer->stop();
|
||||
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
@@ -777,11 +766,6 @@ void TrainTool::guiUpdate() // refreshes the telemetry
|
||||
rtData.setMsecs(total_msecs);
|
||||
rtData.setLapMsecs(lap_msecs);
|
||||
|
||||
// metrics
|
||||
rtData.setJoules(kjoules);
|
||||
rtData.setBikeScore(bikescore);
|
||||
rtData.setXPower(xpower);
|
||||
|
||||
// local stuff ...
|
||||
displayPower = rtData.getWatts();
|
||||
displayCadence = rtData.getCadence();
|
||||
@@ -900,23 +884,6 @@ void TrainTool::diskUpdate()
|
||||
<< "," << Altitude
|
||||
<< "," << "\n";
|
||||
|
||||
rideFile->appendPoint(total_msecs/1000,displayCadence,displayHeartRate,displayDistance,displaySpeed,0,
|
||||
displayPower,Altitude,0,0,0,displayLap + displayWorkoutLap);
|
||||
}
|
||||
|
||||
void TrainTool::metricsUpdate()
|
||||
{
|
||||
// calculate bike score, xpower
|
||||
const RideMetricFactory &factory = RideMetricFactory::instance();
|
||||
const RideMetric *rm = factory.rideMetric("skiba_xpower");
|
||||
|
||||
QStringList metrics;
|
||||
metrics.append("skiba_bike_score");
|
||||
metrics.append("skiba_xpower");
|
||||
QHash<QString,RideMetricPtr> results = rm->computeMetrics(
|
||||
this->main,&*rideFile,this->main->zones(),this->main->hrZones(),metrics);
|
||||
bikescore = results["skiba_bike_score"]->value(true);
|
||||
xpower = results["skiba_xpower"]->value(true);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#define STREAMRATE 200 // rate at which we stream updates to remote peer
|
||||
#define SAMPLERATE 1000 // disk update in milliseconds
|
||||
#define LOADRATE 1000 // rate at which load is adjusted
|
||||
#define METRICSRATE 1000 // rate the metrics are updated
|
||||
|
||||
// device treeview node types
|
||||
#define HEAD_TYPE 6666
|
||||
@@ -140,7 +139,6 @@ class TrainTool : public GcWindow
|
||||
void diskUpdate(); // writes to CSV file
|
||||
void streamUpdate(); // writes to remote Peer
|
||||
void loadUpdate(); // sets Load on CT like devices
|
||||
void metricsUpdate(); // calculates the metrics
|
||||
|
||||
// When no config has been setup
|
||||
void warnnoConfig();
|
||||
@@ -183,9 +181,6 @@ class TrainTool : public GcWindow
|
||||
double displayDistance, displayWorkoutDistance;
|
||||
int displayLap; // user increment for Lap
|
||||
int displayWorkoutLap; // which Lap in the workout are we at?
|
||||
double kjoules;
|
||||
double bikescore;
|
||||
double xpower;
|
||||
bool manualOverride; // during an erg woprkout, if the Higher/Lower
|
||||
// signals are called, it switches to manual
|
||||
// until the next lap/interval
|
||||
@@ -197,7 +192,6 @@ class TrainTool : public GcWindow
|
||||
|
||||
QFile *recordFile; // where we record!
|
||||
ErgFile *ergFile; // workout file
|
||||
boost::shared_ptr<RideFile> rideFile; // keeps track of the workout to figure out BikeScore
|
||||
|
||||
long total_msecs,
|
||||
lap_msecs,
|
||||
@@ -210,8 +204,7 @@ class TrainTool : public GcWindow
|
||||
QTimer *gui_timer, // refresh the gui
|
||||
*stream_timer, // send telemetry to server
|
||||
*load_timer, // change the load on the device
|
||||
*disk_timer, // write to .CSV file
|
||||
*metrics_timer; // computational intense metrics
|
||||
*disk_timer; // write to .CSV file
|
||||
|
||||
public:
|
||||
// everyone else wants this
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<property name="title" type="QString" value="Power" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="7.14286" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="1" />
|
||||
<property name="avgType" type="int" value="32634" />
|
||||
@@ -16,107 +16,167 @@
|
||||
<property name="title" type="QString" value="Target Power" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="7.14286" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="9" />
|
||||
<property name="style" type="int" value="102812386" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Cadence" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Cadence" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="7.14286" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="1" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="7" />
|
||||
<property name="style" type="int" value="0" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Speed" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Speed" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="7.14286" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="1" />
|
||||
<property name="avgType" type="int" value="507" />
|
||||
<property name="dataSeries" type="int" value="6" />
|
||||
<property name="style" type="int" value="616" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Cadence" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Cadence" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="1" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="7" />
|
||||
<property name="style" type="int" value="0" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Heartrate" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Heartrate" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="7.14286" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="1" />
|
||||
<property name="avgType" type="int" value="27263016" />
|
||||
<property name="dataSeries" type="int" value="8" />
|
||||
<property name="style" type="int" value="27263071" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="kJoules" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="kJoules" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="18" />
|
||||
<property name="style" type="int" value="61555235" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="BikeScore" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="BikeScore" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="10" />
|
||||
<property name="style" type="int" value="61610355" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="XPower" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="XPower" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="1" />
|
||||
<property name="avgType" type="int" value="1179904" />
|
||||
<property name="dataSeries" type="int" value="13" />
|
||||
<property name="style" type="int" value="0" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Relative Intensity" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Relative Intensity" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="15" />
|
||||
<property name="style" type="int" value="61825331" />
|
||||
</chart>
|
||||
<chart id="25" name="RT Plot" title="Performance" >
|
||||
<property name="instanceName" type="QString" value="RT Plot" />
|
||||
<property name="title" type="QString" value="Performance" />
|
||||
<property name="subtitle" type="QString" value=" 1x60 Minute effort at 56-75% of FTP - Variable Load" />
|
||||
<property name="widthFactor" type="double" value="1.66667" />
|
||||
<property name="heightFactor" type="double" value="1.35135" />
|
||||
<property name="subtitle" type="QString" value=" 1x60 Minute effort at 91-105% of FTP." />
|
||||
<property name="widthFactor" type="double" value="1.72414" />
|
||||
<property name="heightFactor" type="double" value="1.42857" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
</chart>
|
||||
<chart id="24" name="RT Plot" title="Last 10 seconds" >
|
||||
<property name="instanceName" type="QString" value="RT Plot" />
|
||||
<property name="title" type="QString" value="Last 10 seconds" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="2.5" />
|
||||
<property name="heightFactor" type="double" value="1.35135" />
|
||||
<property name="widthFactor" type="double" value="2.38095" />
|
||||
<property name="heightFactor" type="double" value="1.42857" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Elapsed" >
|
||||
<chart id="22" name="Dial" title="Skiba VI" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Elapsed" />
|
||||
<property name="title" type="QString" value="Skiba VI" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="2.94118" />
|
||||
<property name="heightFactor" type="double" value="8.33333" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="1083296314" />
|
||||
<property name="dataSeries" type="int" value="1" />
|
||||
<property name="dataSeries" type="int" value="11" />
|
||||
<property name="style" type="int" value="113" />
|
||||
</chart>
|
||||
<chart id="29" name="Train Controls" title="Realtime Controls" >
|
||||
<property name="instanceName" type="QString" value="Train Controls" />
|
||||
<property name="title" type="QString" value="Realtime Controls" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="4.54545" />
|
||||
<property name="heightFactor" type="double" value="8.33333" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Lap" >
|
||||
<chart id="22" name="Dial" title="Elapsed Time" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Lap" />
|
||||
<property name="title" type="QString" value="Elapsed Time" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="8.33333" />
|
||||
<property name="widthFactor" type="double" value="3.125" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="32526" />
|
||||
<property name="dataSeries" type="int" value="2" />
|
||||
<property name="style" type="int" value="86056163" />
|
||||
<property name="avgType" type="int" value="0" />
|
||||
<property name="dataSeries" type="int" value="1" />
|
||||
<property name="style" type="int" value="74687587" />
|
||||
</chart>
|
||||
<chart id="29" name="Train Controls" title="Controls" >
|
||||
<property name="instanceName" type="QString" value="Train Controls" />
|
||||
<property name="title" type="QString" value="Controls" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="5" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Distance" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Distance" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="4.16667" />
|
||||
<property name="heightFactor" type="double" value="8.33333" />
|
||||
<property name="widthFactor" type="double" value="3.84615" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="1083336304" />
|
||||
<property name="dataSeries" type="int" value="4" />
|
||||
<property name="style" type="int" value="129" />
|
||||
</chart>
|
||||
<chart id="22" name="Dial" title="Lap" >
|
||||
<property name="instanceName" type="QString" value="Dial" />
|
||||
<property name="title" type="QString" value="Lap" />
|
||||
<property name="subtitle" type="QString" value="" />
|
||||
<property name="widthFactor" type="double" value="4.54545" />
|
||||
<property name="heightFactor" type="double" value="10" />
|
||||
<property name="resizable" type="bool" value="1" />
|
||||
<property name="showInstant" type="bool" value="0" />
|
||||
<property name="avgType" type="int" value="32526" />
|
||||
<property name="dataSeries" type="int" value="2" />
|
||||
<property name="style" type="int" value="86056163" />
|
||||
</chart>
|
||||
</layout>
|
||||
|
||||
Reference in New Issue
Block a user