Rev 96 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/***************************************************************************
* Copyright (C) 2007 by Andreas Theofilu *
* andreas@theosys.at *
* *
* 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 version 3 of the License. *
* *
* 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., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "managefile.h"
#include "sportwatcherwidget.h"
#include "settingswidget.h"
#include <iostream.h>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <ksimpleconfig.h>
#include <klocale.h>
#include <klistview.h>
#include <qstring.h>
#include <qdatetime.h>
sportwatcherWidget::sportwatcherWidget(QWidget* parent, const char* name, WFlags fl)
: sportwatcherWidgetBase(parent,name,fl)
{
mama = parent;
gmn = 0;
min_hr = max_hr = avg_hr = 0;
min_height = max_height = 0.0;
max_time = 0;
index = 0;
// Load the config parameters
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"), true);
cfg->setGroup(QString("SportWatcher"));
lower1 = cfg->readNumEntry("lower1");
lower2 = cfg->readNumEntry("lower2");
lower3 = cfg->readNumEntry("lower3");
upper1 = cfg->readNumEntry("upper1");
upper2 = cfg->readNumEntry("upper2");
upper3 = cfg->readNumEntry("upper3");
MaxHr = cfg->readNumEntry("maxHr");
restHr = cfg->readNumEntry("restHr");
vo2max = cfg->readNumEntry("vo2max");
weight = cfg->readNumEntry("weight");
sampleTime = cfg->readNumEntry("seconds");
Device = cfg->readEntry("Device", "/dev/ttyUSB0");
Data = cfg->readEntry("Data");
HRM = cfg->readEntry("HRM");
delete cfg;
// Fill the activities
getActivities();
}
sportwatcherWidget::~sportwatcherWidget()
{
if (gmn)
garmin_free_data (gmn);
if (index)
{
INDEX *n, *akt = index;
while (akt)
{
n = akt;
akt = akt->next;
delete n;
}
}
}
/*
* Search for a directory named .sportwatcher in the home directory of
* the user and search for *.gmn files. Open the files and read the header
* to find the basic data of activities. Then add the information into
* the activities KListView.
*/
void sportwatcherWidget::getActivities()
{
QString path, txt;
QDir mdir, dir = QDir::home();
QFileInfo *entries;
QStringList years, months;
QListViewItem *running, *biking, *other;
QListViewItem *el;
int anz;
RUN_NODE *rn;
LAP *lap;
if (Data.isEmpty())
{
path = dir.path();
path.append("/.sportwatcher");
}
else
path = Data;
dir.setPath(path);
if (!dir.exists())
{
dir.mkdir(path);
return;
}
liActivities->setRootIsDecorated(true);
liActivities->setSortColumn(-1);
other = new QListViewItem(liActivities, i18n("Others"));
biking = new QListViewItem(liActivities, i18n("Biking"));
running = new QListViewItem(liActivities, i18n("Running"));
liActivities->insertItem(other);
liActivities->insertItem(biking);
liActivities->insertItem(running);
dir.cd(path);
dir.setFilter(QDir::Dirs | QDir::NoSymLinks);
dir.setSorting(QDir::Name);
const QFileInfoList *list = dir.entryInfoList();
if (!list)
return;
QFileInfoListIterator it(*list);
while ((entries = it.current()) != 0) // Years
{
if (entries->fileName() == QString(".") || entries->fileName() == QString(".."))
{
++it;
continue;
}
years += entries->absFilePath();
++it;
}
for (QStringList::Iterator strit = years.begin(); strit != years.end(); ++strit)
{
if (months.count() > 0)
months.clear();
dir.setPath(*strit);
list = dir.entryInfoList();
list = dir.entryInfoList();
if (!list)
continue;
it = QFileInfoListIterator (*list);
while ((entries = it.current()) != 0) // Months
{
if (entries->fileName() == QString(".") || entries->fileName() == QString(".."))
{
++it;
continue;
}
months += entries->absFilePath();
++it;
}
for (QStringList::Iterator strit1 = months.begin(); strit1 != months.end(); ++strit1)
{
mdir.setPath(*strit1);
mdir.cd(*strit1);
mdir.setFilter(QDir::Files | QDir::NoSymLinks);
mdir.setNameFilter(QString("*.gmn"));
list = mdir.entryInfoList();
if (!list)
continue;
it = QFileInfoListIterator (*list);
anz = 0;
while ((entries = it.current()) != 0) // Files
{
files += entries->absFilePath();
++it;
}
}
}
INDEX *akt, *n;
// Open every file and read its head
for (QStringList::Iterator strfl = files.begin(); strfl != files.end(); ++strfl)
{
spw.destroy();
if (spw.setFileName(*strfl) == -1)
return;
if (gmn)
garmin_free_data (gmn);
gmn = spw.readFile();
ds.destroy();
ds.garmin_print_data(gmn);
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
const QDateTime *qt = garmin_dtime (lap->start_time);
QString idx = qt->toString("dd.MM.yyyy hh:mm.ss");
if (!index)
{
index = new INDEX;
index->path = *strfl;
index->activ = idx;
index->next = 0;
}
else
{
n = new INDEX;
n->path = *strfl;
n->activ = idx;
n->next = 0;
akt = index;
while (akt->next)
akt = akt->next;
akt->next = n;
}
switch (rn->run->sport_type)
{
case D1000_running:
el = new QListViewItem(running, idx);
running->insertItem(el);
break;
case D1000_biking:
el = new QListViewItem(biking, idx);
biking->insertItem(el);
break;
case D1000_other:
el = new QListViewItem(other, idx);
other->insertItem(el);
break;
default:
el = new QListViewItem(other, idx);
other->insertItem(el);
}
delete qt;
}
running->setOpen(true);
if (gmn)
garmin_free_data (gmn);
gmn = 0;
}
/*$SPECIALIZATION$*/
void sportwatcherWidget::btFullscreenSlot()
{
}
void sportwatcherWidget::btGlasMinusSlot()
{
}
void sportwatcherWidget::btGlasPlusSlot()
{
}
void sportwatcherWidget::btHandSlot()
{
}
void sportwatcherWidget::btGlasSlot()
{
}
void sportwatcherWidget::btFlagSlot()
{
}
void sportwatcherWidget::liLapsSlot(QListViewItem */* item */)
{
}
void sportwatcherWidget::liActivitiesSlot(QListViewItem *item)
{
INDEX *akt;
if (!item)
return;
akt = index;
while (akt)
{
if (akt->activ == item->text(0))
{
spw.destroy();
if (spw.setFileName(akt->path.ascii()) == -1)
return;
if (gmn)
garmin_free_data (gmn);
gmn = spw.readFile();
showLaps();
showCurves();
return;
}
akt = akt->next;
}
}
void sportwatcherWidget::helpAbout()
{
}
void sportwatcherWidget::helpContents()
{
}
void sportwatcherWidget::helpIndex()
{
}
void sportwatcherWidget::fileExit()
{
if (mama)
mama->close();
}
void sportwatcherWidget::filePrint()
{
}
void sportwatcherWidget::fileSaveAs()
{
}
void sportwatcherWidget::fileSave()
{
}
void sportwatcherWidget::fileOpen()
{
QString fname = KFileDialog::getOpenFileName(Data, QString("*.gmn"), this, QString("SportWatcher"));
if (fname.isEmpty())
return;
spw.destroy();
if (spw.setFileName(fname.ascii()) == -1)
return;
if (gmn)
garmin_free_data (gmn);
gmn = spw.readFile();
showLaps();
showCurves();
}
void sportwatcherWidget::fileNew()
{
}
/*
* This function is called, when the user clicks at the menu point
* "Save Heart Rate".
* First, a file dialog box is displayed, where the user can choose a
* directory and a file name to save the heart rate.
* If the file could successfully be created, the heart rate is saved
* in the "HRM"-format. This is the native format polar uses to store
* heart rate data. I've choosen this format, because it's popular and
* used by many other software too.
*/
void sportwatcherWidget::extrasSaveHR()
{
QString fname, str1, str2;
QFile fdfile;
QDateTime *qt, *oldqt;
QDate dat;
QTime t;
QDir dir = QDir::home();
char hv0[256];
RUN_NODE *rn;
LAP *lap, *alap;
POINT *point;
int samples, smp, seconds, anz, nsec, samsec;
int avgHeart, minHeart, maxHeart, aktHeart;
int secRange1, secRange2, secRange3, secAbove, secBeyond;
if (!gmn)
{
KMessageBox::information(this, i18n("There is no activity open"));
return;
}
if (HRM.isEmpty())
str1 = dir.path();
else
str1 = HRM;
str1 += "/" + StartTime.toString("yyyyMMddThhmmss.zzz.hrm");
fname = KFileDialog::getSaveFileName(str1, QString("*.hrm"), this, QString("SportWatcher"));
if (fname.isEmpty())
return;
fdfile.setName(fname);
if (fdfile.exists())
{
if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
return;
}
if (!fdfile.open(IO_ReadWrite | IO_Truncate))
{
KMessageBox::error(this, i18n("Error creating a file!\nPlease check permissions."));
return;
}
rn = ds.getRunNode();
lap = ds.getLap(rn->run->first_lap_index);
t = StartTime.time();
dat = StartTime.date();
if ((point = ds.getPoint(lap->start_time)) == 0)
{
fdfile.close();
return;
}
strcpy (hv0, "[Params]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
str1 = dat.toString("yyyyMMdd");
str2 = t.toString("hh:mm:ss.z");
sprintf(hv0, "Version=106\nMonitor=11\nSMode=000000000\nDate=%s\nStartTime=%s\n",
str1.ascii(), str2.ascii());
write(fdfile.handle(), &hv0[0], strlen(hv0));
t.setHMS(0, 0, 0);
t = t.addSecs(max_time);
str2 = t.toString("hh:mm:ss.z");
switch (sampleTime)
{
case 0: samsec = 5; break;
case 1: samsec = 15; break;
case 2: samsec = 30; break;
case 3: samsec = 60; break;
default:
samsec = 15;
}
sprintf(hv0, "Length=%s\nInterval=%d\nUpper1=%d\nLower1=%d\nUpper2=%d\n",
str2.ascii(), samsec, upper1, lower1, upper2);
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "Lower2=%d\nUpper3=%d\nLower3=%d\nTimer1=00:00:00.0\n",
lower2, upper3, lower3);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "Timer2=00:00:00.0\nTimer3=00:00:00.0\nActiveLimit=0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "MaxHR=%d\nRestHR=%d\nStartDelay=0\nVO2max=%d\nWeight=%d\n\n",
MaxHr, restHr, vo2max, weight);
write(fdfile.handle(), &hv0[0], strlen(hv0));
// Write the intervall times. One block for every lap
secRange1 = secRange2 = secRange3 = secAbove = secBeyond = 0;
strcpy(hv0, "[IntTimes]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
t.setHMS(0, 0, 0);
for (unsigned int i = rn->run->first_lap_index; i < rn->run->last_lap_index; i++)
{
alap = ds.getLap(i);
point = ds.getPoint(alap->start_time);
oldqt = garmin_dtime(point->time);
avgHeart = minHeart = maxHeart = aktHeart = 0;
anz = 0;
unsigned long lastTime = point->time;
int totSec = 0;
while (point)
{
if (point->time > (alap->start_time + (alap->total_time / 100)))
break;
if (point->heart_rate > 0)
{
avgHeart += point->heart_rate;
nsec = point->time - lastTime;
totSec += nsec;
if (minHeart == 0 || minHeart > point->heart_rate)
minHeart = point->heart_rate;
if (maxHeart < point->heart_rate)
maxHeart = point->heart_rate;
if (aktHeart == 0 && totSec >= samsec)
aktHeart = avgHeart / (anz + 1);
if (point->heart_rate < lower1)
secBeyond += nsec;
else if (point->heart_rate < lower2)
secRange1 += nsec;
else if (point->heart_rate < lower3)
secRange2 += nsec;
else if (point->heart_rate < upper3)
secRange3 += nsec;
else
secAbove += nsec;
lastTime = point->time;
anz++;
}
point = ds.getPoint(point->time+1);
}
t = t.addSecs(alap->total_time / 100);
str1 = t.toString("hh:mm:ss.z");
sprintf(hv0, "%s\t %d\t %d\t %d\t %d\n",
str1.ascii(), aktHeart, minHeart, avgHeart / anz, maxHeart);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "32\t 0\t 0\t 0\t 0\t 0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "0\t 0\t 0\t 0\t 0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "0\t %d\t 0\t 0\t 0\t 0\n", (int)alap->total_distance);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "0\t 0\t 0\t 0\t 0\t 0\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
}
strcpy(hv0, "\n[IntNotes]\n\n[ExtraData]\n\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "[Summary-123]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 1
smp = max_time - secBeyond - secRange1 - secRange2 - secRange3 - secAbove;
sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
max_time, secRange1, secRange2 + secRange3,
secAbove + secBeyond, smp);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // limits 1
sprintf(hv0, "%d\t %d\t %d\t %d\n",
MaxHr, upper1, lower1, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 1
sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
max_time, secRange2, secRange1 + secRange3,
secAbove + secBeyond, smp);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // limits 2
sprintf(hv0, "%d\t %d\t %d\t %d\n",
MaxHr, upper2, lower2, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 2
sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
max_time, secRange3, secRange1 + secRange2,
secAbove + secBeyond, smp);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // limits 3
sprintf(hv0, "%d\t %d\t %d\t %d\n",
MaxHr, upper3, lower3, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0)); // Time limits 3
samples = max_time / samsec;
sprintf(hv0, "0\t %u\n\n", samples); // samples
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "[Summary-TH]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "%u\t 0\t %u\t %d\t %d\t 0\n", max_time, max_time - max_hr - restHr, max_hr, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "%d\t %d\t %d\t %d\n", MaxHr, upper3, lower1, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "0\t %u\n\n", samples); // samples
write(fdfile.handle(), &hv0[0], strlen(hv0));
sprintf(hv0, "[HRZones]\n%d\n%d\n%d\n%d\n%d\n%d\n0\n0\n0\n0\n0\n\n",
MaxHr, upper3, upper2, upper1, lower1, restHr);
write(fdfile.handle(), &hv0[0], strlen(hv0));
strcpy(hv0, "[HRData]\n");
write(fdfile.handle(), &hv0[0], strlen(hv0));
smp = 0; // average heart rate of 15 seconds
seconds = 0;
anz = 0;
nsec = samsec;
oldqt = garmin_dtime(lap->start_time);
qt = 0;
point = ds.getPoint(lap->start_time);
while (point)
{
if (seconds >= nsec)
{
if (anz > 0)
{
sprintf(hv0, "%d\n", smp / anz);
write(fdfile.handle(), &hv0[0], strlen(hv0));
}
if (smp > 0 && seconds >= (nsec + samsec))
{
if (anz <= 0)
anz = 0;
for (int x = nsec; x < seconds; x += samsec)
{
sprintf(hv0, "%d\n", smp / anz);
write(fdfile.handle(), &hv0[0], strlen(hv0));
nsec += samsec;
}
}
anz = 0;
smp = 0;
nsec += samsec;
}
qt = garmin_dtime (point->time);
seconds += oldqt->secsTo(*qt);
if (point->heart_rate > 0)
{
smp += point->heart_rate;
anz++;
}
delete oldqt;
oldqt = qt;
point = ds.getPoint(point->time + 1);
}
fdfile.close();
KMessageBox::information(this, i18n("File successfully written."));
}
void sportwatcherWidget::extrasSettings()
{
settingsWidget *dlg = new settingsWidget(this, "settingsWidgetBase", TRUE, 0);
if (dlg->exec() == QDialog::Accepted)
{
KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"), true);
cfg->setGroup(QString("SportWatcher"));
lower1 = cfg->readNumEntry("lower1");
lower2 = cfg->readNumEntry("lower2");
lower3 = cfg->readNumEntry("lower3");
upper1 = cfg->readNumEntry("upper1");
upper2 = cfg->readNumEntry("upper2");
upper3 = cfg->readNumEntry("upper3");
MaxHr = cfg->readNumEntry("maxHr");
restHr = cfg->readNumEntry("restHr");
vo2max = cfg->readNumEntry("vo2max");
weight = cfg->readNumEntry("weight");
sampleTime = cfg->readNumEntry("seconds");
Device = cfg->readEntry("Device");
Data = cfg->readEntry("Data");
HRM = cfg->readEntry("HRM");
delete cfg;
}
delete dlg;
}
/*
* Functions to fill in the boxes of the main mask.
*/
void sportwatcherWidget::showLaps()
{
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
QDateTime dt;
QTime t, st;
QDateTime *qt;
LAP *lap;
POINT *point;
RUN_NODE *rakt, *rn;
int laps, i, anz, men;
double alt_asc, alt_dsc;
if (!gmn)
{
KMessageBox::error(0, i18n("No data were loaded!"));
return;
}
if (gmn->type == data_Dnil)
{
KMessageBox::error(0, i18n("No data found!"));
return;
}
if (gmn->type != data_Dlist) /* List of data */
{
KMessageBox::error(0, QString("Found unexpected data type %1!").arg(gmn->type));
return;
}
ds.destroy();
min_hr = max_hr = 0;
min_height = max_height = 0.0;
liLaps->clear();
ds.garmin_print_data(gmn);
rn = ds.getRunNode();
rakt = rn;
liLaps->setRootIsDecorated(true);
liLaps->setColumnAlignment(1, Qt::AlignRight);
liLaps->setColumnAlignment(2, Qt::AlignRight);
liLaps->setColumnAlignment(3, Qt::AlignRight);
liLaps->setColumnAlignment(4, Qt::AlignRight);
liLaps->setColumnAlignment(5, Qt::AlignRight);
liLaps->setColumnAlignment(6, Qt::AlignRight);
liLaps->setColumnAlignment(7, Qt::AlignRight);
liLaps->setColumnAlignment(8, Qt::AlignRight);
liLaps->setColumnAlignment(9, Qt::AlignRight);
liLaps->setColumnAlignment(10, Qt::AlignRight);
liLaps->setColumnAlignment(11, Qt::AlignRight);
qs_name = qs_distance = qs_etime = qs_avgpace = qs_avgspeed = qs_maxspeed = QString("");
qs_calories = qs_avghr = qs_maxhr = qs_avgcadence = qs_ascent = qs_descent = QString("");
men = 0;
while (rakt)
{
if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
rakt->run->type == data_D1010)
{
int lt, cal, ahr, mhr;
double distance, speed, mspeed;
QDate dat;
switch (rakt->run->sport_type)
{
case D1000_running: qs_name = QString("Running: "); break;
case D1000_biking: qs_name = QString("Biking: "); break;
case D1000_other: qs_name = QString("Other: "); break;
default:
qs_name = QString("Unknown: ");
}
lap = ds.getLap(rakt->run->first_lap_index);
qt = garmin_dtime (lap->start_time);
StartTime = *qt;
st = qt->time();
dat = qt->date();
delete qt;
lap = ds.getLap(rakt->run->last_lap_index);
qt = garmin_dtime (lap->start_time);
t = qt->addSecs(lap->total_time / 100).time();
lt = st.secsTo(t);
t.setHMS(0, 0, 0);
t = t.addSecs(lt);
qt->setDate(dat);
qt->setTime(t);
qs_name.append(dat.toString("dd.MM.yyyy"));
qs_name.append(" ");
qs_name.append(st.toString("hh:mm:ss"));
max_time = lt;
qs_etime = QString(qt->toString("hh:mm:ss.zzz"));
distance = 0.0;
cal = 0;
mspeed = 0;
ahr = mhr = 0;
anz = 0;
for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
{
if ((lap = ds.getLap(i)) == NULL)
continue;
distance += lap->total_distance;
cal += lap->calories;
ahr += lap->avg_heart_rate;
anz++;
if (lap->max_speed > mspeed)
mspeed = lap->max_speed;
if (lap->max_heart_rate > mhr)
mhr = lap->max_heart_rate;
}
qs_distance.sprintf("%.2f m", distance);
if (distance > 0)
{
QTime tt = qt->time();
long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0 * (1000.0 / distance * 100.0);
int h = secs / 3600;
int m = (secs - (h * 3600)) / 60;
int s = secs - ((h * 3600) + (m * 60));
t = QTime(h, m, s, 0);
qs_avgpace = t.toString(" hh:mm:ss");
qs_avgpace.append(QString(" /km"));
}
speed = distance / lt * 3.6;
qs_avgspeed.sprintf("%.2f km/h", speed);
qs_maxspeed.sprintf("%.2f km/h", mspeed * 3.6);
qs_calories.sprintf("%d", cal);
qs_avghr.sprintf("%d bpm", ahr / anz);
qs_maxhr.sprintf("%d bpm", mhr);
QListViewItem *element = new QListViewItem(liLaps, qs_name, qs_distance,
qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed, qs_calories, qs_avghr);
element->setText(8, qs_maxhr);
element->setText(9, qs_avgcadence);
element->setText(10, qs_ascent);
element->setText(11, qs_descent);
element->sortChildItems(0, false);
element->setOpen(true);
liLaps->insertItem(element);
delete qt;
/* Get the laps. */
laps = 1;
for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
{
if ((lap = ds.getLap(i)) == NULL)
continue;
qt = garmin_dtime (lap->start_time);
qs_name.sprintf("Lap %03d - ", laps);
qs_name.append(qt->toString("hh:mm:ss"));
qs_distance.sprintf("%.2f m", lap->total_distance);
t = QTime(0, 0, 0, 0);
t = t.addMSecs(lap->total_time * 10);
qs_etime = t.toString("hh:mm:ss.zzz");
qs_avgspeed.sprintf("%.2f km/h", lap->total_distance / (lap->total_time / 100.0) * 3.6);
qs_maxspeed.sprintf("%.2f km/h", lap->max_speed * 3.6);
qs_calories.sprintf("%d", lap->calories);
if (lap->total_distance > 0 && lap->total_time != 0)
{
long secs = (double)lap->total_time / 10000.0 * (1000.0 / lap->total_distance * 100.0);
int h = secs / 3600;
int m = (secs - (h * 3600)) / 60;
int s = secs - ((h * 3600) + (m * 60));
t = QTime(h, m, s, 0);
qs_avgpace = t.toString("hh:mm:ss");
qs_avgpace.append(QString(" /km"));
}
qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);
qs_maxhr.sprintf("%d bpm", lap->max_heart_rate);
anz = 0;
alt_asc = alt_dsc = 0;
if ((point = ds.getPoint(lap->start_time)) != 0)
{
if (point->alt < 20000)
alt_dsc = alt_asc = point->alt;
else
alt_dsc = alt_asc = 0;
while (point)
{
if (point->time > (lap->start_time + (lap->total_time / 100)))
break;
if (point->alt > alt_asc && point->alt < 20000)
{
alt_asc = point->alt;
if (alt_dsc == 0)
alt_dsc = point->alt;
}
if (point->alt < alt_dsc)
alt_dsc = point->alt;
// save the min and max values. We need this information to
// build the graphics.
if (point->heart_rate > max_hr)
max_hr = point->heart_rate;
if ((min_hr == 0 && point->heart_rate > 0) || (point->heart_rate > 0 && point->heart_rate < min_hr))
min_hr = point->heart_rate;
if (point->alt < 20000 && max_height < point->alt)
max_height = point->alt;
if (point->alt < 20000 && (min_height == 0.0 || min_height > point->alt))
min_height = point->alt;
if (point->heart_rate > 0)
{
avg_hr += point->heart_rate;
men++;
}
point = ds.getPoint(point->time + 1);
}
qs_ascent.sprintf("%.2f m", alt_asc);
qs_descent.sprintf("%.2f m", alt_dsc);
}
if (lap->avg_cadence != 0xff)
qs_avgcadence.sprintf("%d", lap->avg_cadence);
QListViewItem *edetail = new QListViewItem(element, qs_name, qs_distance,
qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed, qs_calories, qs_avghr);
edetail->setText(8, qs_maxhr);
edetail->setText(9, qs_avgcadence);
edetail->setText(10, qs_ascent);
edetail->setText(11, qs_descent);
liLaps->clearSelection();
element->insertItem(edetail);
delete qt;
laps++;
}
}
rakt = rakt->next;
}
if (men > 0)
avg_hr /= men;
}
void sportwatcherWidget::showCurves()
{
QPainter paint;
int width, height;
int i;
int lineHeight, margin_left, margin_right, margin_bottom;
int x1, y1, x2, y2; // Coordinates
bool meter;
double maxHeight, minHeight;
int maxHr, minHr, rh;
POINT *point;
double w_tick, h_tick; // Number of pixels one "tick" has;
// This depends on the width and height
// of the image.
// First we draw a grid based on the min and max
// values detected in the function showLap(). In case
// all values are 0, we exit here.
if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
return;
width = imgProfile->width() - 4;
height = imgProfile->height() - 4;
pmProfile.resize(width + 2, height + 4);
paint.begin(&pmProfile);
// we need a somewhat bigger area to draw our curves than
// we have with the real min and max values.
if (max_height > 0.0)
{
double add = (max_height - min_height) / 100.0 * 5.0; // Perzent
maxHeight = max_height + add;
minHeight = min_height - add;
if (minHeight < 0.0) // make sure, we are not too deep
minHeight = 0.0;
}
if (max_hr > 0)
{
maxHr = max_hr + 10;
minHr = min_hr - 10;
if (minHr < 0)
minHr = 0;
}
// Define colors
QColor background(220, 220, 220);
QColor mark(255, 255, 255);
QColor frame(0, 0, 0);
QColor barcol(151, 190, 13);
QColor barcol2(190, 151, 13);
QColor red(220, 128, 128);
QColor blue(0, 0, 240);
QFont fntNormal("Helvetica");
// QFont fntBold("Helvetica", 10, QFont::Bold);
fntNormal.setPixelSize(10);
fntNormal.setStyleHint(QFont::Helvetica);
// fntBold.setPixelSize(10);
// fntBold.setStyleHint(QFont::Helvetica);
// Calculate ticks
margin_left = 52;
margin_right = 40;
margin_bottom = 12;
lineHeight = 10;
rh = height - margin_bottom - 1;
w_tick = (double)(width - (margin_left + margin_right)) / max_time; // 1 tick = 1 second
if ((maxHeight - minHeight) > (double)(maxHr - minHr))
{
h_tick = (double)rh / (maxHeight - minHeight); // 1 tick = 1 meter
meter = true;
}
else
{
h_tick = (double)rh / ((double)maxHr - (double)minHr); // 1 tick = 1 bpm
meter = false;
}
// Fill background with background colors
paint.fillRect(0, 0, width + 4, height + 4, background);
// Draw a grid with markers at every 10 minutes
paint.setPen(QPen(frame, 1, QPen::SolidLine));
// Bottom border line
x1 = margin_left;
y1 = height - margin_bottom;
x2 = width - margin_right;
y2 = y1;
paint.drawLine(x1, y1, x2, y2);
// Left border line
x1 = x2 = margin_left;
y1 = 2;
y2 = height - margin_bottom;
paint.drawLine(x1, y1, x2, y2);
// right border line
x1 = x2 = width - margin_right;
paint.drawLine(x1, y1, x2, y2);
// Grid vertical
paint.setPen(QPen(frame, 1, QPen::SolidLine));
paint.setFont(fntNormal);
paint.drawText(margin_left - 20, height - lineHeight, 40, lineHeight, Qt::AlignCenter, QString("00:00"));
paint.save();
paint.rotate(270);
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n("Elevation (m)"));
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
paint.restore();
paint.setPen(QPen(mark, 1, QPen::SolidLine));
// Draw the time scale
for (i = 0; (unsigned int)i < max_time; i++)
{
if (i > 0 && !(i % 600)) // every 10 minutes
{
x1 = x2 = margin_left + w_tick * i;
if (x1 == (width - margin_right))
continue;
y1 = 2;
y2 = height - margin_bottom;
paint.drawLine(x1, y1, x2, y2);
QTime tm(0, 0, 0);
tm = tm.addSecs(i);
paint.setPen(QPen(frame, 1, QPen::SolidLine));
paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((i >= 3600) ? "hh:mm:ss" : "mm:ss"));
paint.setPen(QPen(mark, 1, QPen::SolidLine));
}
}
QTime tm(0, 0, 0);
QString qs;
tm = tm.addSecs(max_time);
paint.setPen(QPen(frame, 1, QPen::SolidLine));
paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((max_time >= 3600) ? "hh:mm:ss" : "mm:ss"));
if (max_height > 0.0)
{
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight));
}
if (max_hr > 0)
{
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
}
paint.setPen(QPen(mark, 1, QPen::SolidLine));
// Grid horizontal
int factor = (meter) ? (maxHeight - minHeight) / (rh / 12) : (maxHr - minHr) / (rh / 12);
int target = (meter) ? (int)(maxHeight - minHeight) : (maxHr - minHr);
int oldy = height;
for (i = 0; i < target; i++)
{
if (i > 0 && !(i % factor))
{
x1 = margin_left + 1;
x2 = width - margin_right - 1;
y1 = y2 = rh - h_tick * i;
if (y1 < 12)
break;
paint.drawLine(x1, y1, x2, y2);
if (y1 < (oldy - lineHeight))
{
if (meter)
{
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + i));
if (maxHr > 0)
{
double hrscale = (double)(maxHr - minHr) / (double)target;
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", (int)((double)minHr + hrscale * (double)i)));
}
}
else
{
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
if (max_height > 0)
{
double hrscale = (maxHeight - minHeight) / (double)target;
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + hrscale * (double)i));
}
}
paint.setPen(QPen(mark, 1, QPen::SolidLine));
oldy = y1;
}
}
}
// To make out graphics mor beautiful, we draw lines for the
// heart rate limits and the average heart rate.
if (max_hr > 0)
{
int ay1, ay2, ay3, ay4, ay5;
x1 = margin_left + 1;
x2 = width - margin_right - 1;
if (meter)
{
double hrscale = rh / (double)(maxHr - minHr);
ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
}
else
{
ay1 = (double)rh - (double)(lower1 - minHr) * h_tick;
ay2 = (double)rh - (double)(lower2 - minHr) * h_tick;
ay3 = (double)rh - (double)(lower3 - minHr) * h_tick;
ay4 = (double)rh - (double)(upper3 - minHr) * h_tick;
ay5 = (double)rh - (double)(avg_hr - minHr) * h_tick;
}
paint.setPen(QPen(barcol2, 1, QPen::DashLine)); // color for limits
if (lower1 > minHr && lower1 < maxHr)
paint.drawLine(x1, ay1, x2, ay1);
if (lower2 > minHr && lower2 < maxHr)
paint.drawLine(x1, ay2, x2, ay2);
if (lower3 > minHr && lower3 < maxHr)
paint.drawLine(x1, ay3, x2, ay3);
if (upper3 > minHr && upper3 < maxHr)
paint.drawLine(x1, ay4, x2, ay4);
paint.setPen(QPen(red, 1, QPen::DashDotLine)); // color for average heart rate
if (avg_hr > minHr && avg_hr < maxHr)
paint.drawLine(x1, ay5, x2, ay5);
}
// Now we have a grid and we've done the scaling.
// This is the point where we draw the curves itself.
// We use different colors to draw the lines.
i = 0;
x1 = x2 = y1 = y2 = 0;
QDateTime *qt;
QTime zeit = StartTime.time();
int secs, hy1, hy2, hx1, hx2;
hy1 = hy2 = hx1 = hx2 = 0;
while ((point = ds.getPoint(i)) != 0)
{
// calculate the y position based on the time
qt = garmin_dtime(point->time);
secs = zeit.secsTo(qt->time());
delete qt;
x2 = secs * w_tick + margin_left + 1;
hx2 = x2;
if (x1 == 0)
x1 = x2;
if (hx1 == 0)
hx1 = hx2;
if (point->alt < 20000 && point->alt > 0.0)
{
if (meter)
y2 = (double)rh - (point->alt - minHeight) * h_tick;
else
{
double hrscale = rh / (maxHeight - minHeight);
y2 = (double)rh - (point->alt - minHeight) * hrscale;
}
if (y1 == 0)
y1 = y2;
paint.setPen(QPen(barcol, 1, QPen::SolidLine));
paint.drawLine(x1, y1, x2, y2);
y1 = y2;
x1 = x2;
}
if (point->heart_rate > 0)
{
if (meter)
{
double hrscale = rh / (double)(maxHr - minHr);
hy2 = (double)rh - (double)(point->heart_rate - minHr) * hrscale;
}
else
hy2 = (double)rh - (double)(point->heart_rate - minHr) * h_tick;
if (hy1 == 0)
hy1 = hy2;
paint.setPen(QPen(blue, 1, QPen::SolidLine));
paint.drawLine(hx1, hy1, hx2, hy2);
hy1 = hy2;
hx1 = hx2;
}
i++;
}
paint.end();
imgProfile->setPixmap(pmProfile);
// if (bProfile)
// bitBlt(imgProfile, 0, 0, &pmProfile);
}
void sportwatcherWidget::resizeEvent(QResizeEvent */* *e */)
{
showCurves();
}
void sportwatcherWidget::paintEvent(QPaintEvent */* *e */)
{
showCurves();
}
/*
* Private functions to help decode the data
*/
QDateTime *sportwatcherWidget::garmin_dtime (uint32 t)
{
time_t tval;
struct tm tmval;
QTime ti;
QDate dt;
QDateTime *qt;
if (t == 0)
return new QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0));
tval = t + TIME_OFFSET;
localtime_r (&tval, &tmval);
qt = new QDateTime();
qt->setDate(QDate(tmval.tm_year+1900, tmval.tm_mon+1, tmval.tm_mday));
qt->setTime(QTime(tmval.tm_hour, tmval.tm_min, tmval.tm_sec, 0));
/* OK. Done. */
return qt;
}
#include "sportwatcherwidget.moc"