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:
Mark Liversedge
2011-11-04 16:28:36 +00:00
parent c977d8aba0
commit 1e8b6edb62
7 changed files with 391 additions and 129 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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);
}
//----------------------------------------------------------------------

View File

@@ -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

View File

@@ -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>