Files
GoldenCheetah/src/RealtimeWindow.cpp
Dag Gruneau e3ac6c799c quarqd - cadence, wheel rotation, error checking
Fixed a number of issues with data from quarqd inf and nan values where
inserted as valid data points and thus destoying all plotting in the
realtime window and in later analysis.

The unit was used to distinguish between the entities, thus rpm was
erroneously used as a cadence, rpm is used as the unit for wheel
rotation and for cadence.  This made the cadence useless together with a
PowerTap hub which reports both cadence and wheel rotation.

No error checking was performed on the received data, bad data is
ignored now.
2010-02-12 06:07:30 -08:00

872 lines
30 KiB
C++

/*
* Copyright (c) 2009 Mark Liversedge (liversedge@gmail.com)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "MainWindow.h"
#include "RealtimeWindow.h"
#include "RealtimeData.h"
#include "RealtimePlot.h"
#include "math.h" // for round()
#include "Units.h" // for MILES_PER_KM
// Three current realtime device types supported are:
#include "ComputrainerController.h"
#include "ANTplusController.h"
#include "SimpleNetworkController.h"
#include "ErgFile.h"
#include "TrainTool.h"
void
RealtimeWindow::configUpdate()
{
// config has been updated! so re-read
// wipe out all the current entries
deviceSelector->clear();
streamSelector->clear();
// metric or imperial changed?
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
QVariant unit = settings->value(GC_UNIT);
useMetricUnits = (unit.toString() == "Metric");
// set labels accordingly
distanceLabel->setText(useMetricUnits ? tr("Distance (KM)") : tr("Distance (Miles)"));
speedLabel->setText(useMetricUnits ? tr("KPH") : tr("MPH"));
avgspeedLabel->setText(useMetricUnits ? tr("Avg KPH") : tr("Avg MPH"));
// get configured devices
DeviceConfigurations all;
Devices.clear();
Devices = all.getList();
streamSelector->addItem("No streaming", -1);
for (int i=0; i<Devices.count(); i++) {
// add streamers
if (Devices.at(i).type == DEV_GSERVER)
streamSelector->addItem(Devices.at(i).name, i);
// add data sources
deviceSelector->addItem(Devices.at(i).name, i);
}
deviceSelector->setCurrentIndex(0);
streamSelector->setCurrentIndex(0);
// reconnect - otherwise after no config they still get an error
if (Devices.count() > 0) {
disconnect(startButton, SIGNAL(clicked()), this, SLOT(warnnoConfig()));
disconnect(pauseButton, SIGNAL(clicked()), this, SLOT(warnnoConfig()));
disconnect(stopButton, SIGNAL(clicked()), this, SLOT(warnnoConfig()));
connect(startButton, SIGNAL(clicked()), this, SLOT(Start()));
connect(pauseButton, SIGNAL(clicked()), this, SLOT(Pause()));
connect(stopButton, SIGNAL(clicked()), this, SLOT(Stop()));
}
}
RealtimeWindow::RealtimeWindow(MainWindow *parent, TrainTool *trainTool, const QDir &home) : QWidget(parent)
{
// set home
this->home = home;
this->trainTool = trainTool;
main = parent;
deviceController = NULL;
streamController = NULL;
ergFile = NULL;
// metric or imperial?
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
QVariant unit = settings->value(GC_UNIT);
useMetricUnits = (unit.toString() == "Metric");
// main layout for the window
main_layout = new QVBoxLayout(this);
timer_layout = new QGridLayout();
// BUTTONS AND LCDS
button_layout = new QHBoxLayout();
option_layout = new QHBoxLayout();
controls_layout = new QVBoxLayout();
deviceSelector = new QComboBox(this);
streamSelector = new QComboBox(this);
// get configured devices
DeviceConfigurations all;
Devices = all.getList();
streamSelector->addItem("No streaming", -1);
for (int i=0; i<Devices.count(); i++) {
// add streamers
if ( Devices.at(i).type == DEV_GSERVER ||
Devices.at(i).type == DEV_GCLIENT )
streamSelector->addItem(Devices.at(i).name, i);
// add data sources
deviceSelector->addItem(Devices.at(i).name, i);
}
deviceSelector->setCurrentIndex(0);
streamSelector->setCurrentIndex(0);
recordSelector = new QCheckBox(this);
recordSelector->setText(tr("Save"));
recordSelector->setChecked(Qt::Checked);
startButton = new QPushButton(tr("Start"), this);
startButton->setMaximumHeight(100);
pauseButton = new QPushButton(tr("Pause"), this);
pauseButton->setMaximumHeight(100);
stopButton = new QPushButton(tr("Stop"), this);
stopButton->setMaximumHeight(100);
button_layout->addWidget(startButton);
button_layout->addWidget(pauseButton);
button_layout->addWidget(stopButton);
option_layout->addWidget(deviceSelector);
option_layout->addSpacing(10);
option_layout->addWidget(recordSelector);
option_layout->addSpacing(10);
option_layout->addWidget(streamSelector);
// XXX NETWORK STREAMING DISABLED IN THIS RELEASE SO HIDE THE COMBO
streamSelector->hide();
option_layout->addSpacing(10);
controls_layout->addItem(option_layout);
controls_layout->addItem(button_layout);
// handle config changes
connect(main, SIGNAL(configChanged()), this, SLOT(configUpdate()));
connect(deviceSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectDevice(int)));
connect(recordSelector, SIGNAL(clicked()), this, SLOT(SelectRecord()));
connect(streamSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectStream(int)));
connect(trainTool, SIGNAL(workoutSelected()), this, SLOT(SelectWorkout()));
if (Devices.count() > 0) {
connect(startButton, SIGNAL(clicked()), this, SLOT(Start()));
connect(pauseButton, SIGNAL(clicked()), this, SLOT(Pause()));
connect(stopButton, SIGNAL(clicked()), this, SLOT(Stop()));
} else {
connect(startButton, SIGNAL(clicked()), this, SLOT(warnnoConfig()));
connect(pauseButton, SIGNAL(clicked()), this, SLOT(warnnoConfig()));
connect(stopButton, SIGNAL(clicked()), this, SLOT(warnnoConfig()));
}
powerLabel = new QLabel(tr("WATTS"), this);
powerLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
heartrateLabel = new QLabel(tr("BPM"), this);
heartrateLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
speedLabel = new QLabel(useMetricUnits ? tr("KPH") : tr("MPH"), this);
speedLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
cadenceLabel = new QLabel(tr("RPM"), this);
cadenceLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
lapLabel = new QLabel(tr("Lap/Interval"), this);
lapLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
loadLabel = new QLabel(tr("Load WATTS"), this);
loadLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
distanceLabel = new QLabel(useMetricUnits ? tr("Distance (KM)") : tr("Distance (Miles)"), this);
distanceLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
avgpowerLabel = new QLabel(tr("Avg WATTS"), this);
avgpowerLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
avgheartrateLabel = new QLabel(tr("Avg BPM"), this);
avgheartrateLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
avgspeedLabel = new QLabel(useMetricUnits ? tr("Avg KPH") : tr("Avg MPH"), this);
avgspeedLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
avgcadenceLabel = new QLabel(tr("Avg RPM"), this);
avgcadenceLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
avgloadLabel = new QLabel(tr("Avg Load WATTS"), this);
avgloadLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
laptimeLabel = new QLabel(tr("LAP TIME"), this);
laptimeLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
timeLabel = new QLabel(tr("TIME"), this);
timeLabel->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
powerLCD = new QLCDNumber(this); powerLCD->setSegmentStyle(QLCDNumber::Filled);
heartrateLCD = new QLCDNumber(this); heartrateLCD->setSegmentStyle(QLCDNumber::Filled);
speedLCD = new QLCDNumber(this); speedLCD->setSegmentStyle(QLCDNumber::Filled);
cadenceLCD = new QLCDNumber(this); cadenceLCD->setSegmentStyle(QLCDNumber::Filled);
lapLCD = new QLCDNumber(this); lapLCD->setSegmentStyle(QLCDNumber::Filled);
loadLCD = new QLCDNumber(this); loadLCD->setSegmentStyle(QLCDNumber::Filled);
distanceLCD = new QLCDNumber(this); distanceLCD->setSegmentStyle(QLCDNumber::Filled);
avgpowerLCD = new QLCDNumber(this); avgpowerLCD->setSegmentStyle(QLCDNumber::Filled);
avgheartrateLCD = new QLCDNumber(this); avgheartrateLCD->setSegmentStyle(QLCDNumber::Filled);
avgspeedLCD = new QLCDNumber(this); avgspeedLCD->setSegmentStyle(QLCDNumber::Filled);
avgcadenceLCD = new QLCDNumber(this); avgcadenceLCD->setSegmentStyle(QLCDNumber::Filled);
avgloadLCD = new QLCDNumber(this); avgloadLCD->setSegmentStyle(QLCDNumber::Filled);
laptimeLCD = new QLCDNumber(this); laptimeLCD->setSegmentStyle(QLCDNumber::Filled);
laptimeLCD->setNumDigits(9);
timeLCD = new QLCDNumber(this); timeLCD->setSegmentStyle(QLCDNumber::Filled);
timeLCD->setNumDigits(9);
gridLayout = new QGridLayout();
gridLayout->addWidget(powerLabel, 1, 0);
gridLayout->addWidget(cadenceLabel, 1, 1);
gridLayout->addWidget(heartrateLabel, 1, 2);
gridLayout->addWidget(speedLabel, 1, 3);
gridLayout->addWidget(distanceLabel, 1, 4);
gridLayout->addWidget(lapLabel, 1, 5);
gridLayout->addWidget(powerLCD, 2, 0);
gridLayout->addWidget(cadenceLCD, 2, 1);
gridLayout->addWidget(heartrateLCD, 2, 2);
gridLayout->addWidget(speedLCD, 2, 3);
gridLayout->addWidget(distanceLCD, 2, 4);
gridLayout->addWidget(lapLCD, 2, 5);
gridLayout->addWidget(avgpowerLabel, 3, 0);
gridLayout->addWidget(avgcadenceLabel, 3, 1);
gridLayout->addWidget(avgheartrateLabel, 3, 2);
gridLayout->addWidget(avgspeedLabel, 3, 3);
gridLayout->addWidget(avgloadLabel, 3, 4);
gridLayout->addWidget(loadLabel, 3, 5);
gridLayout->addWidget(loadLCD, 4, 5);
gridLayout->addWidget(avgpowerLCD, 4, 0);
gridLayout->addWidget(avgcadenceLCD, 4, 1);
gridLayout->addWidget(avgheartrateLCD, 4, 2);
gridLayout->addWidget(avgspeedLCD, 4, 3);
gridLayout->addWidget(avgloadLCD, 4, 4);
gridLayout->setRowStretch(2, 4);
gridLayout->setRowStretch(4, 3);
// timers etc
timer_layout->addWidget(timeLabel, 0, 0);
timer_layout->addWidget(laptimeLabel, 0, 3);
timer_layout->addWidget(timeLCD, 1, 0);
timer_layout->addItem(controls_layout, 1,1);
timer_layout->addWidget(laptimeLCD, 1, 3);
timer_layout->setRowStretch(0, 1);
timer_layout->setRowStretch(1, 4);
// REALTIME PLOT
rtPlot = new RealtimePlot();
// COURSE PLOT
ergPlot = new ErgFilePlot(0);
ergPlot->setVisible(false);
// LAYOUT
main_layout->addWidget(ergPlot);
main_layout->addItem(timer_layout);
main_layout->addItem(gridLayout);
main_layout->addWidget(rtPlot);
displaymode=2;
main_layout->setStretch(0,1);
main_layout->setStretch(1,1);
main_layout->setStretch(2,2);
main_layout->setStretch(3, displaymode);
// now the GUI is setup lets sort our control variables
gui_timer = new QTimer(this);
disk_timer = new QTimer(this);
stream_timer = new QTimer(this);
load_timer = new QTimer(this);
recordFile = NULL;
status = 0;
status |= RT_RECORDING; // recording is on by default! - add others here
status |= RT_MODE_ERGO; // ergo mode by default
displayWorkoutLap = displayLap = 0;
pwrcount = 0;
cadcount = 0;
hrcount = 0;
spdcount = 0;
lodcount = 0;
load_msecs = total_msecs = lap_msecs = 0;
displayWorkoutDistance = displayDistance = displayPower = displayHeartRate =
displaySpeed = displayCadence = displayGradient = displayLoad = 0;
avgPower= avgHeartRate= avgSpeed= avgCadence= avgLoad= 0;
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()));
// setup the controller based upon currently selected device
setDeviceController();
rtPlot->replot();
}
void RealtimeWindow::setDeviceController()
{
int deviceno = this->deviceSelector->currentIndex();
// zap the current one
if (deviceController != NULL) {
delete deviceController;
deviceController = NULL;
}
if (Devices.count() > 0) {
DeviceConfiguration temp = Devices.at(deviceno);
if (Devices.at(deviceno).type == DEV_ANTPLUS) {
deviceController = new ANTplusController(this, &temp);
} else if (Devices.at(deviceno).type == DEV_CT) {
deviceController = new ComputrainerController(this, &temp);
} else if (Devices.at(deviceno).type == DEV_GSERVER) {
deviceController = new SimpleNetworkController(this, &temp);
}
}
}
void RealtimeWindow::setStreamController()
{
int deviceno = streamSelector->itemData(streamSelector->currentIndex()).toInt();
// zap the current one
if (streamController != NULL) {
delete streamController;
streamController = NULL;
}
if (Devices.count() > 0) {
DeviceConfiguration temp = Devices.at(deviceno);
if (Devices.at(deviceno).type == DEV_ANTPLUS) {
streamController = new ANTplusController(this, &temp);
} else if (Devices.at(deviceno).type == DEV_CT) {
streamController = new ComputrainerController(this, &temp);
} else if (Devices.at(deviceno).type == DEV_GSERVER) {
streamController = new SimpleNetworkController(this, &temp);
}
}
}
void RealtimeWindow::SelectDevice(int index)
{
if (index != -1)
setDeviceController();
}
void RealtimeWindow::Start() // when start button is pressed
{
if (status&RT_RUNNING) {
newLap();
} else {
status |=RT_RUNNING;
deviceController->start(); // start device
startButton->setText(tr("Lap/Interval"));
recordSelector->setEnabled(false);
streamSelector->setEnabled(false);
deviceSelector->setEnabled(false);
if (status & RT_WORKOUT) {
load_timer->start(LOADRATE); // start recording
}
if (status & RT_RECORDING) {
// setup file
QDate date = QDate().currentDate();
QTime time = QTime().currentTime();
QDateTime now(date, time);
QChar zero = QLatin1Char ( '0' );
QString filename = QString ( "%1_%2_%3_%4_%5_%6.csv" )
.arg ( now.date().year(), 4, 10, zero )
.arg ( now.date().month(), 2, 10, zero )
.arg ( now.date().day(), 2, 10, zero )
.arg ( now.time().hour(), 2, 10, zero )
.arg ( now.time().minute(), 2, 10, zero )
.arg ( now.time().second(), 2, 10, zero );
QString fulltarget = home.absolutePath() + "/" + filename;
if (recordFile) delete recordFile;
recordFile = new QFile(fulltarget);
if (!recordFile->open(QFile::WriteOnly | QFile::Truncate)) {
status &= ~RT_RECORDING;
} else {
// CSV File header
QTextStream recordFileStream(recordFile);
recordFileStream << "Minutes,Torq (N-m),Km/h,Watts,Km,Cadence,Hrate,ID,Altitude (m)\n";
disk_timer->start(SAMPLERATE); // start screen
}
}
// stream
if (status & RT_STREAMING) {
streamController->start();
stream_timer->start(STREAMRATE);
}
gui_timer->start(REFRESHRATE); // start recording
}
}
void RealtimeWindow::Pause() // pause capture to recalibrate
{
// we're not running fool!
if ((status&RT_RUNNING) == 0) return;
if (status&RT_PAUSED) {
status &=~RT_PAUSED;
deviceController->restart();
pauseButton->setText(tr("Pause"));
gui_timer->start(REFRESHRATE);
if (status & RT_STREAMING) stream_timer->start(STREAMRATE);
if (status & RT_RECORDING) disk_timer->start(SAMPLERATE);
if (status & RT_WORKOUT) load_timer->start(LOADRATE);
} else {
deviceController->pause();
pauseButton->setText(tr("Un-Pause"));
status |=RT_PAUSED;
gui_timer->stop();
if (status & RT_STREAMING) stream_timer->stop();
if (status & RT_RECORDING) disk_timer->stop();
if (status & RT_WORKOUT) load_timer->stop();
}
}
void RealtimeWindow::Stop() // when stop button is pressed
{
if ((status&RT_RUNNING) == 0) return;
status &= ~RT_RUNNING;
startButton->setText(tr("Start"));
deviceController->stop();
gui_timer->stop();
if (status & RT_RECORDING) {
disk_timer->stop();
// close and reset File
recordFile->close();
// add to the view - using basename ONLY
QString name;
name = recordFile->fileName();
main->addRide(QFileInfo(name).fileName(), true);
}
if (status & RT_STREAMING) {
stream_timer->stop();
streamController->stop();
}
if (status & RT_WORKOUT) {
load_timer->stop();
load_msecs = 0;
ergPlot->setNow(load_msecs);
ergPlot->replot();
}
// Re-enable gui elements
recordSelector->setEnabled(true);
streamSelector->setEnabled(true);
deviceSelector->setEnabled(true);
// reset counters etc
pwrcount = 0;
cadcount = 0;
hrcount = 0;
spdcount = 0;
lodcount = 0;
displayWorkoutLap = displayLap =0;
lap_msecs = 0;
total_msecs = 0;
avgPower= avgHeartRate= avgSpeed= avgCadence= avgLoad= 0;
displayWorkoutDistance = displayDistance = 0;
guiUpdate();
return;
}
// Called by push devices (e.g. ANT+)
void RealtimeWindow::updateData(RealtimeData &rtData)
{
displayPower = rtData.getWatts();
displayCadence = rtData.getCadence();
displayHeartRate = rtData.getHr();
displaySpeed = rtData.getSpeed();
displayLoad = rtData.getLoad();
// Gradient not supported
return;
}
//----------------------------------------------------------------------
// SCREEN UPDATE FUNCTIONS
//----------------------------------------------------------------------
void RealtimeWindow::guiUpdate() // refreshes the telemetry
{
RealtimeData rtData;
// get latest telemetry from device (if it is a pull device e.g. Computrainer //
if (status&RT_RUNNING && deviceController->doesPull() == true) {
deviceController->getRealtimeData(rtData);
displayPower = rtData.getWatts();
displayCadence = rtData.getCadence();
displayHeartRate = rtData.getHr();
displaySpeed = rtData.getSpeed();
displayLoad = rtData.getLoad();
}
// Distance assumes current speed for the last second. from km/h to km/sec
displayDistance += displaySpeed / (5 * 3600); // XXX assumes 200ms refreshrate
displayWorkoutDistance += displaySpeed / (5 * 3600); // XXX assumes 200ms refreshrate
// update those LCDs!
timeLCD->display(QString("%1:%2:%3.%4").arg(total_msecs/3600000)
.arg((total_msecs%3600000)/60000,2,10,QLatin1Char('0'))
.arg((total_msecs%60000)/1000,2,10,QLatin1Char('0'))
.arg((total_msecs%1000)/100));
laptimeLCD->display(QString("%1:%2:%3.%4").arg(lap_msecs/3600000,2)
.arg((lap_msecs%3600000)/60000,2,10,QLatin1Char('0'))
.arg((lap_msecs%60000)/1000,2,10,QLatin1Char('0'))
.arg((lap_msecs%1000)/100));
// Cadence, HR and Power needs to be rounded to 0 decimal places
powerLCD->display(round(displayPower));
speedLCD->display(round(displaySpeed * (useMetricUnits ? 1.0 : MILES_PER_KM) * 10.00)/10.00);
cadenceLCD->display(round(displayCadence));
heartrateLCD->display(round(displayHeartRate));
lapLCD->display(displayWorkoutLap+displayLap);
// load or gradient depending on mode we are running
if (status&RT_MODE_ERGO) loadLCD->display(displayLoad);
else loadLCD->display(round(displayGradient*10)/10.00);
// distance
distanceLCD->display(round(displayDistance*(useMetricUnits ? 1.0 : MILES_PER_KM) *10.00) /10.00);
// NZ Averages.....
if (displayPower) { //NZAP is bogus - make it configurable!!!
pwrcount++; if (pwrcount ==1) avgPower = displayPower;
avgPower = ((avgPower * (double)pwrcount) + displayPower) /(double) (pwrcount+1);
}
if (displayCadence) {
cadcount++; if (cadcount ==1) avgCadence = displayCadence;
avgCadence = ((avgCadence * (double)cadcount) + displayCadence) /(double) (cadcount+1);
}
if (displayHeartRate) {
hrcount++; if (hrcount ==1) avgHeartRate = displayHeartRate;
avgHeartRate = ((avgHeartRate * (double)hrcount) + displayHeartRate) /(double) (hrcount+1);
}
if (displaySpeed) {
spdcount++; if (spdcount ==1) avgSpeed = displaySpeed;
avgSpeed = ((avgSpeed * (double)spdcount) + displaySpeed) /(double) (spdcount+1);
}
if (displayLoad && status&RT_MODE_ERGO) {
lodcount++; if (lodcount ==1) avgLoad = displayLoad;
avgLoad = ((avgLoad * (double)lodcount) + displayLoad) /(double) (lodcount+1);
avgloadLCD->display((int)avgLoad);
}
if (status&RT_MODE_SPIN) {
grdcount++; if (grdcount ==1) avgGradient = displayGradient;
avgGradient = ((avgGradient * (double)grdcount) + displayGradient) /(double) (grdcount+1);
avgloadLCD->display((int)avgGradient);
}
avgpowerLCD->display((int)avgPower);
avgspeedLCD->display(round(avgSpeed * (useMetricUnits ? 1.0 : MILES_PER_KM) * 10.00)/10.00);
avgcadenceLCD->display((int)avgCadence);
avgheartrateLCD->display((int)avgHeartRate);
// now that plot....
rtPlot->pwrData.addData(displayPower); // add new data point
rtPlot->cadData.addData(displayCadence); // add new data point
rtPlot->spdData.addData(displaySpeed); // add new data point
rtPlot->hrData.addData(displayHeartRate); // add new data point
//rtPlot->lodData.addData(displayLoad); // add new Load point
rtPlot->replot(); // redraw
this->update();
// add time
total_msecs += REFRESHRATE;
lap_msecs += REFRESHRATE;
}
// can be called from the controller - when user presses "Lap" button
void RealtimeWindow::newLap()
{
displayLap++;
lap_msecs = 0;
pwrcount = 0;
cadcount = 0;
hrcount = 0;
spdcount = 0;
// set avg to current values to ensure averages represent from now onwards
// and not from beginning of workout
avgPower = displayPower;
avgCadence = displayCadence;
avgHeartRate = displayHeartRate;
avgSpeed = displaySpeed;
avgLoad = displayLoad;
}
// can be called from the controller
void RealtimeWindow::nextDisplayMode()
{
if (displaymode == 8) displaymode=2;
else displaymode += 1;
main_layout->setStretch(3,displaymode);
}
void RealtimeWindow::warnnoConfig()
{
QMessageBox::warning(this, tr("No Devices Configured"), "Please configure a device in Preferences.");
}
//----------------------------------------------------------------------
// STREAMING FUNCTION
//----------------------------------------------------------------------
void
RealtimeWindow::SelectStream(int index)
{
if (index > 0) {
status |= RT_STREAMING;
setStreamController();
} else {
status &= ~RT_STREAMING;
}
}
void
RealtimeWindow::streamUpdate()
{
RealtimeData rtData;
// get current telemetry...
rtData.setWatts(displayPower);
rtData.setCadence(displayCadence);
rtData.setHr(displayHeartRate);
rtData.setSpeed(displaySpeed);
rtData.setLoad(displayLoad);
rtData.setTime(0);
// send over the wire...
streamController->pushRealtimeData(rtData);
}
//----------------------------------------------------------------------
// DISK UPDATE FUNCTIONS
//----------------------------------------------------------------------
void
RealtimeWindow::SelectRecord()
{
if (recordSelector->isChecked()) {
status |= RT_RECORDING;
} else {
status &= ~RT_RECORDING;
}
}
void RealtimeWindow::diskUpdate()
{
double Minutes;
long Torq = 0, Altitude = 0;
QTextStream recordFileStream(recordFile);
// convert from milliseconds to minutes
Minutes = total_msecs;
Minutes /= 1000.00;
Minutes *= (1.0/60);
// PowerAgent Format "Minutes,Torq (N-m),Km/h,Watts,Km,Cadence,Hrate,ID,Altitude (m)"
recordFileStream << Minutes
<< "," << Torq
<< "," << displaySpeed
<< "," << displayPower
<< "," << displayDistance
<< "," << displayCadence
<< "," << displayHeartRate
<< "," << (displayLap + displayWorkoutLap)
<< "," << Altitude
<< "," << "\n";
}
//----------------------------------------------------------------------
// WORKOUT MODE
//----------------------------------------------------------------------
void
RealtimeWindow::SelectWorkout()
{
int mode;
// wip away the current selected workout
if (ergFile) {
delete ergFile;
ergFile = NULL;
}
// which one is selected?
if (trainTool->currentWorkout() == NULL || trainTool->currentWorkout()->type() != WORKOUT_TYPE) return;
// is it the auto mode?
int index = trainTool->workoutItems()->indexOfChild((QTreeWidgetItem *)trainTool->currentWorkout());
if (index == 0) {
// ergo mode
mode = ERG;
status &= ~RT_WORKOUT;
ergPlot->setVisible(false);
} else if (index == 1) {
// slope mode
mode = CRS;
status &= ~RT_WORKOUT;
ergPlot->setVisible(false);
} else {
// workout mode
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
QVariant workoutDir = settings->value(GC_WORKOUTDIR);
QString fileName = workoutDir.toString() + "/" + trainTool->currentWorkout()->text(0); // filename
// Get users CP for relative watts calculations
QDate today = QDate::currentDate();
double Cp=285; // default to 285 if zones are not set
int range = main->zones()->whichRange(today);
if (range != -1) Cp = main->zones()->getCP(range);
ergFile = new ErgFile(fileName, mode, Cp);
if (ergFile->isValid()) {
status |= RT_WORKOUT;
// success! we have a load file
// setup the course profile in the
// display!
ergPlot->setData(ergFile);
ergPlot->setVisible(true);
ergPlot->replot();
}
}
// set the device to the right mode
if (mode == ERG) {
status |= RT_MODE_ERGO;
status &= ~RT_MODE_SPIN;
if (deviceController != NULL) deviceController->setMode(RT_MODE_ERGO);
// set the labels on the gui
loadLabel->setText("Load WATTS");
avgloadLabel->setText("Avg Load WATTS");
} else { // SLOPE MODE
status |= RT_MODE_SPIN;
status &= ~RT_MODE_ERGO;
if (deviceController != NULL) deviceController->setMode(RT_MODE_SPIN);
// set the labels on the gui
loadLabel->setText("Gradient PERCENT");
avgloadLabel->setText("Avg Gradient PERCENT");
}
}
void RealtimeWindow::loadUpdate()
{
long load;
double gradient;
load_msecs += LOADRATE;
if (status&RT_MODE_ERGO) {
load = ergFile->wattsAt(load_msecs, displayWorkoutLap);
// we got to the end!
if (load == -100) {
Stop();
} else {
displayLoad = load;
deviceController->setLoad(displayLoad);
ergPlot->setNow(load_msecs);
}
} else {
gradient = ergFile->gradientAt(displayWorkoutDistance*1000, displayWorkoutLap);
// we got to the end!
if (gradient == -100) {
Stop();
} else {
displayGradient = gradient;
deviceController->setGradient(displayGradient);
ergPlot->setNow(displayWorkoutDistance * 1000); // now is in meters we keep it in kilometers
}
}
}
void RealtimeWindow::FFwd()
{
if (status&RT_MODE_ERGO) load_msecs += 10000; // jump forward 10 seconds
else displayWorkoutDistance += 1; // jump forward a kilometer in the workout
}
void RealtimeWindow::Rewind()
{
if (status&RT_MODE_ERGO) {
load_msecs -=10000; // jump back 10 seconds
if (load_msecs < 0) load_msecs = 0;
} else {
displayWorkoutDistance -=1; // jump back a kilometer
if (displayWorkoutDistance < 0) displayWorkoutDistance = 0;
}
}
// jump to next Lap marker (if there is one?)
void RealtimeWindow::FFwdLap()
{
double lapmarker;
if (status&RT_MODE_ERGO) {
lapmarker = ergFile->nextLap(load_msecs);
if (lapmarker != -1) load_msecs = lapmarker; // jump forward to lapmarker
} else {
lapmarker = ergFile->nextLap(displayWorkoutDistance*1000);
if (lapmarker != -1) displayWorkoutDistance = lapmarker/1000; // jump forward to lapmarker
}
}
// higher load/gradient
void RealtimeWindow::Higher()
{
if (status&RT_MODE_ERGO) displayLoad += 5;
else displayGradient += 0.1;
if (displayLoad >1500) displayLoad = 1500;
if (displayGradient >15) displayGradient = 15;
if (status&RT_MODE_ERGO) deviceController->setLoad(displayLoad);
else deviceController->setGradient(displayGradient);
}
// higher load/gradient
void RealtimeWindow::Lower()
{
if (status&RT_MODE_ERGO) displayLoad -= 5;
else displayGradient -= 0.1;
if (displayLoad <0) displayLoad = 0;
if (displayGradient <-10) displayGradient = -10;
if (status&RT_MODE_ERGO) deviceController->setLoad(displayLoad);
else deviceController->setGradient(displayGradient);
}