mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-04-15 05:32:21 +00:00
unified framework for reading in different ride
file types (raw, srm, and csv)
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
//
|
||||
// C++ Implementation: csv
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//
|
||||
// Author: Justin F. Knotzke <jknotzke@shampoo.ca>, (C) 2007
|
||||
//
|
||||
// Copyright: See COPYING file that comes with this distribution
|
||||
//
|
||||
//
|
||||
#include "CsvData.h"
|
||||
#include <QTextStream>
|
||||
#include "RawFile.h"
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
void CsvData::readCsvFile(QFile &file, RawFile *rawFile)
|
||||
{
|
||||
|
||||
QTextStream stream( &file );
|
||||
QString line;
|
||||
std::vector<double> vec;
|
||||
double previousSecs = 0.0;
|
||||
//Flush the first line.
|
||||
line = stream.readLine();
|
||||
while ( !stream.atEnd() )
|
||||
{
|
||||
line = stream.readLine();
|
||||
//Parse the line here
|
||||
secs = line.section(',', 0, 0).toDouble();
|
||||
kph = line.section(',', 2, 2).toDouble();
|
||||
watts = line.section(',', 3, 3).toInt();
|
||||
km = line.section(',', 4, 4).toDouble();
|
||||
cad = line.section(',', 5, 5).toInt();
|
||||
hr = line.section(',', 6, 6).toInt();
|
||||
interval = line.section(',', 7, 7).toInt();
|
||||
|
||||
//To figure out the mean recording interval
|
||||
vec.push_back(secs - previousSecs);
|
||||
previousSecs = secs;
|
||||
|
||||
//For the power histogram
|
||||
if (rawFile->powerHist.contains(watts))
|
||||
rawFile->powerHist[watts] +=secs;
|
||||
else
|
||||
rawFile->powerHist[watts] = secs;
|
||||
|
||||
//Assume that all CSV files have km/h
|
||||
RawFilePoint *p1 = new RawFilePoint(
|
||||
secs, 0, kph * 0.621371192 , watts,
|
||||
km * 0.621371192, cad, hr, interval);
|
||||
|
||||
rawFile->points.append(p1);
|
||||
}
|
||||
//Sort it
|
||||
std::sort(vec.begin(), vec.end());
|
||||
int size = vec.size();
|
||||
//Then grab the middle value (the mean) and covert to milliseconds
|
||||
rawFile->rec_int_ms = int(vec[size/2] * 60 * 1000);
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
//
|
||||
// C++ Interface: csv
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
//
|
||||
// Author: Justin F. Knotzke <jknotzke@shampoo.ca>, (C) 2007
|
||||
//
|
||||
// Copyright: See COPYING file that comes with this distribution
|
||||
//
|
||||
//
|
||||
#ifndef _csv_h
|
||||
#define _csv_h
|
||||
|
||||
#include <QDate>
|
||||
|
||||
class QFile;
|
||||
class RawFile;
|
||||
|
||||
class CsvData
|
||||
{
|
||||
public:
|
||||
int cad, hr, watts, interval;
|
||||
double kph, km, secs;
|
||||
QDateTime startTime;
|
||||
double recint;
|
||||
void readCsvFile(QFile &file, RawFile *rawFile);
|
||||
};
|
||||
#endif // _srm_h
|
||||
122
src/gui/CsvRideFile.cpp
Normal file
122
src/gui/CsvRideFile.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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 "CsvRideFile.h"
|
||||
#include <QRegExp>
|
||||
#include <QTextStream>
|
||||
#include <algorithm> // for std::sort
|
||||
#include <assert.h>
|
||||
#include "math.h"
|
||||
|
||||
#define MILES_TO_KM 1.609344
|
||||
|
||||
static int csvFileReaderRegistered =
|
||||
CombinedFileReader::instance().registerReader("csv", new CsvFileReader());
|
||||
|
||||
RideFile *CsvFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
{
|
||||
QRegExp rideTime("^.*/(\\d\\d\\d\\d)_(\\d\\d)_(\\d\\d)_"
|
||||
"(\\d\\d)_(\\d\\d)_(\\d\\d)\\.csv$");
|
||||
if (rideTime.indexIn(file.fileName()) == -1) {
|
||||
errors << ("file name does not encode ride time: \""
|
||||
+ file.fileName() + "\"");
|
||||
return NULL;
|
||||
}
|
||||
QDateTime datetime(QDate(rideTime.cap(1).toInt(),
|
||||
rideTime.cap(2).toInt(),
|
||||
rideTime.cap(3).toInt()),
|
||||
QTime(rideTime.cap(4).toInt(),
|
||||
rideTime.cap(5).toInt(),
|
||||
rideTime.cap(6).toInt()));
|
||||
QRegExp metricUnits("(km|kph)", Qt::CaseInsensitive);
|
||||
QRegExp englishUnits("(miles|mph)", Qt::CaseInsensitive);
|
||||
QRegExp sample("^\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+|\\d+\\.\\d+)\\s*,\\s*"
|
||||
"(\\d+)\\s*$");
|
||||
bool metric;
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
errors << ("Could not open ride file: \""
|
||||
+ file.fileName() + "\"");
|
||||
return NULL;
|
||||
}
|
||||
int lineno = 1;
|
||||
QTextStream is(&file);
|
||||
RideFile *rideFile = new RideFile();
|
||||
while (!is.atEnd()) {
|
||||
QString line = is.readLine();
|
||||
if (lineno == 1) {
|
||||
if (metricUnits.indexIn(line) != -1)
|
||||
metric = true;
|
||||
else if (englishUnits.indexIn(line) != -1)
|
||||
metric = false;
|
||||
else {
|
||||
errors << ("Can't find units in first line: \"" + line + "\"");
|
||||
delete rideFile;
|
||||
file.close();
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (sample.indexIn(line) != -1) {
|
||||
assert(sample.numCaptures() == 8);
|
||||
double minutes = sample.cap(1).toDouble();
|
||||
double nm = sample.cap(2).toDouble();
|
||||
double kph = sample.cap(3).toDouble();
|
||||
double watts = sample.cap(4).toDouble();
|
||||
double km = sample.cap(5).toDouble();
|
||||
double cad = sample.cap(6).toDouble();
|
||||
double hr = sample.cap(7).toDouble();
|
||||
int interval = sample.cap(8).toInt();
|
||||
if (!metric) {
|
||||
km *= MILES_TO_KM;
|
||||
kph *= MILES_TO_KM;
|
||||
}
|
||||
rideFile->appendPoint(minutes * 60.0, cad, hr, km,
|
||||
kph, nm, watts, interval);
|
||||
}
|
||||
else {
|
||||
errors << ("Couldn't parse line: \"" + line + "\"");
|
||||
}
|
||||
++lineno;
|
||||
}
|
||||
// To estimate the recording interval, take the median of the
|
||||
// first 1000 samples and round to nearest millisecond.
|
||||
int n = rideFile->dataPoints().size();
|
||||
n = qMin(n, 1000);
|
||||
if (n >= 2) {
|
||||
double *secs = new double[n-1];
|
||||
for (int i = 0; i < n-1; ++i) {
|
||||
double now = rideFile->dataPoints()[i]->secs;
|
||||
double then = rideFile->dataPoints()[i+1]->secs;
|
||||
secs[i] = then - now;
|
||||
}
|
||||
std::sort(secs, secs + n - 1);
|
||||
int mid = n / 2 - 1;
|
||||
double recint = round(secs[mid] * 1000.0) / 1000.0;
|
||||
rideFile->setRecIntSecs(recint);
|
||||
}
|
||||
rideFile->setStartTime(datetime);
|
||||
file.close();
|
||||
return rideFile;
|
||||
}
|
||||
|
||||
29
src/gui/CsvRideFile.h
Normal file
29
src/gui/CsvRideFile.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _CsvRideFile_h
|
||||
#define _CsvRideFile_h
|
||||
|
||||
#include "RideFile.h"
|
||||
|
||||
struct CsvFileReader : public RideFileReader {
|
||||
virtual RideFile *openRideFile(QFile &file, QStringList &errors) const;
|
||||
};
|
||||
|
||||
#endif // _CsvRideFile_h
|
||||
|
||||
@@ -20,7 +20,7 @@ HEADERS += \
|
||||
AllPlot.h \
|
||||
ChooseCyclistDialog.h \
|
||||
CpintPlot.h \
|
||||
CsvData.h \
|
||||
CsvRideFile.h \
|
||||
DatePickerDialog.h \
|
||||
DownloadRideDialog.h \
|
||||
LogTimeScaleDraw.h \
|
||||
@@ -28,7 +28,10 @@ HEADERS += \
|
||||
MainWindow.h \
|
||||
PowerHist.h \
|
||||
RawFile.h \
|
||||
RawRideFile.h \
|
||||
RideFile.h \
|
||||
RideItem.h \
|
||||
SrmRideFile.h \
|
||||
TimeUtils.h \
|
||||
Zones.h
|
||||
|
||||
@@ -36,7 +39,7 @@ SOURCES += \
|
||||
AllPlot.cpp \
|
||||
ChooseCyclistDialog.cpp \
|
||||
CpintPlot.cpp \
|
||||
CsvData.cpp \
|
||||
CsvRideFile.cpp \
|
||||
DatePickerDialog.cpp \
|
||||
DownloadRideDialog.cpp \
|
||||
LogTimeScaleDraw.cpp \
|
||||
@@ -44,7 +47,10 @@ SOURCES += \
|
||||
MainWindow.cpp \
|
||||
PowerHist.cpp \
|
||||
RawFile.cpp \
|
||||
RawRideFile.cpp \
|
||||
RideFile.cpp \
|
||||
RideItem.cpp \
|
||||
SrmRideFile.cpp \
|
||||
TimeUtils.cpp \
|
||||
Zones.cpp \
|
||||
\
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "PowerHist.h"
|
||||
#include "RawFile.h"
|
||||
#include "RideItem.h"
|
||||
#include "RideFile.h"
|
||||
#include "Settings.h"
|
||||
#include "TimeUtils.h"
|
||||
#include "Zones.h"
|
||||
@@ -50,14 +51,9 @@ static char *rideFileRegExp = ("^(\\d\\d\\d\\d)_(\\d\\d)_(\\d\\d)"
|
||||
|
||||
QString
|
||||
MainWindow::notesFileName(QString rideFileName) {
|
||||
if (rideFileName.endsWith(".raw"))
|
||||
return rideFileName.left(rideFileName.length() - 4) + ".notes";
|
||||
else if (rideFileName.endsWith(".srm"))
|
||||
return rideFileName.left(rideFileName.length() - 4) + ".notes";
|
||||
else if ( rideFileName.endsWith (".csv"))
|
||||
return rideFileName.left ( rideFileName.length() - 4) + ".notes";
|
||||
else
|
||||
assert(false);
|
||||
int i = rideFileName.lastIndexOf(".");
|
||||
assert(i >= 0);
|
||||
return rideFileName.left(i) + ".notes";
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(const QDir &home) :
|
||||
@@ -100,10 +96,8 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
splitter->addWidget(treeWidget);
|
||||
|
||||
QRegExp rx(rideFileRegExp);
|
||||
QStringList filters;
|
||||
filters << "*.raw" << "*.srm"<<"*.csv";
|
||||
QTreeWidgetItem *last = NULL;
|
||||
QStringListIterator i(home.entryList(filters, QDir::Files));
|
||||
QStringListIterator i(CombinedFileReader::instance().listRideFiles(home));
|
||||
while (i.hasNext()) {
|
||||
QString name = i.next();
|
||||
if (rx.exactMatch(name)) {
|
||||
@@ -585,10 +579,12 @@ MainWindow::rideSelected()
|
||||
RideItem *ride = (RideItem*) which;
|
||||
rideSummary->setHtml(ride->htmlSummary());
|
||||
rideSummary->setAlignment(Qt::AlignCenter);
|
||||
allPlot->setData(ride->raw);
|
||||
if (ride->raw)
|
||||
allPlot->setData(ride->raw);
|
||||
if (tabWidget->currentIndex() == 2)
|
||||
cpintPlot->calculate(ride->fileName, ride->dateTime);
|
||||
powerHist->setData(ride->raw);
|
||||
if (ride->raw)
|
||||
powerHist->setData(ride->raw);
|
||||
|
||||
QDate wstart = ride->dateTime.date();
|
||||
wstart = wstart.addDays(Qt::Monday - wstart.dayOfWeek());
|
||||
|
||||
@@ -19,132 +19,33 @@
|
||||
*/
|
||||
|
||||
#include "RawFile.h"
|
||||
#include <math.h>
|
||||
#include "RideFile.h"
|
||||
#include <assert.h>
|
||||
#include <QMessageBox>
|
||||
#include "srm.h"
|
||||
#include "CsvData.h"
|
||||
#include <math.h>
|
||||
|
||||
extern "C" {
|
||||
#include "pt.h"
|
||||
}
|
||||
|
||||
struct RawFileReadState
|
||||
{
|
||||
QList<RawFilePoint*> &points;
|
||||
QMap<double,double> &powerHist;
|
||||
QStringList &errors;
|
||||
double last_secs, last_miles;
|
||||
unsigned last_interval;
|
||||
time_t start_since_epoch;
|
||||
unsigned rec_int;
|
||||
RawFileReadState(QList<RawFilePoint*> &points,
|
||||
QMap<double,double> &powerHist,
|
||||
QStringList &errors) :
|
||||
points(points), powerHist(powerHist), errors(errors), last_secs(0.0),
|
||||
last_miles(0.0), last_interval(0), start_since_epoch(0), rec_int(0) {}
|
||||
};
|
||||
|
||||
static void
|
||||
config_cb(unsigned /*interval*/, unsigned rec_int,
|
||||
unsigned /*wheel_sz_mm*/, void *context)
|
||||
{
|
||||
RawFileReadState *state = (RawFileReadState*) context;
|
||||
// Assume once set, rec_int should never change.
|
||||
assert((state->rec_int == 0) || (state->rec_int == rec_int));
|
||||
state->rec_int = rec_int;
|
||||
}
|
||||
|
||||
static void
|
||||
time_cb(struct tm *, time_t since_epoch, void *context)
|
||||
{
|
||||
RawFileReadState *state = (RawFileReadState*) context;
|
||||
if (state->start_since_epoch == 0)
|
||||
state->start_since_epoch = since_epoch;
|
||||
double secs = since_epoch - state->start_since_epoch;
|
||||
RawFilePoint *point = new RawFilePoint(secs, -1.0, -1.0, -1.0,
|
||||
state->last_miles, 0, 0,
|
||||
state->last_interval);
|
||||
state->points.append(point);
|
||||
state->last_secs = secs;
|
||||
}
|
||||
|
||||
static void
|
||||
data_cb(double secs, double nm, double mph, double watts, double miles,
|
||||
unsigned cad, unsigned hr, unsigned interval, void *context)
|
||||
{
|
||||
RawFileReadState *state = (RawFileReadState*) context;
|
||||
RawFilePoint *point = new RawFilePoint(secs, nm, mph, watts, miles,
|
||||
cad, hr, interval);
|
||||
state->points.append(point);
|
||||
state->last_secs = secs;
|
||||
state->last_miles = miles;
|
||||
state->last_interval = interval;
|
||||
double sum = state->rec_int * 0.021;
|
||||
if (watts >= 0.0) {
|
||||
if (state->powerHist.contains(watts)) {
|
||||
sum += state->powerHist.value(watts);
|
||||
state->powerHist.remove(watts);
|
||||
}
|
||||
state->powerHist.insert(watts, sum);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
error_cb(const char *msg, void *context)
|
||||
{
|
||||
RawFileReadState *state = (RawFileReadState*) context;
|
||||
state->errors.append(QString(msg));
|
||||
}
|
||||
#define KM_TO_MILES 0.62137119
|
||||
|
||||
RawFile *RawFile::readFile(QFile &file, QStringList &errors)
|
||||
{
|
||||
RideFile *rideFile =
|
||||
CombinedFileReader::instance().openRideFile(file, errors);
|
||||
if (!rideFile)
|
||||
return NULL;
|
||||
RawFile *result = new RawFile(file.fileName());
|
||||
if (file.fileName().indexOf(".srm") == file.fileName().size() - 4) {
|
||||
SrmData data;
|
||||
if (!readSrmFile(file, data, errors)) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
result->startTime = data.startTime;
|
||||
result->rec_int_ms = (unsigned) round(data.recint * 1000.0);
|
||||
|
||||
QListIterator<SrmDataPoint*> i(data.dataPoints);
|
||||
while (i.hasNext()) {
|
||||
SrmDataPoint *p1 = i.next();
|
||||
RawFilePoint *p2 = new RawFilePoint(
|
||||
p1->secs, 0, p1->kph * 0.62137119, p1->watts,
|
||||
p1->km * 0.62137119, p1->cad, p1->hr, p1->interval);
|
||||
if (result->powerHist.contains(p2->watts))
|
||||
result->powerHist[p2->watts] += data.recint;
|
||||
else
|
||||
result->powerHist[p2->watts] = data.recint;
|
||||
result->points.append(p2);
|
||||
}
|
||||
}
|
||||
else if (file.fileName().indexOf(".csv") == file.fileName().size() - 4)
|
||||
{
|
||||
CsvData data;
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
errors << QString("can't open file %1").arg(file.fileName());
|
||||
return false;
|
||||
}
|
||||
|
||||
data.readCsvFile(file, result);
|
||||
}
|
||||
else {
|
||||
if (!result->file.open(QIODevice::ReadOnly)) {
|
||||
delete result;
|
||||
return NULL;
|
||||
}
|
||||
FILE *f = fdopen(result->file.handle(), "r");
|
||||
assert(f);
|
||||
RawFileReadState state(result->points, result->powerHist, errors);
|
||||
pt_read_raw(f, 0 /* not compat */, &state, config_cb,
|
||||
time_cb, data_cb, error_cb);
|
||||
result->rec_int_ms = (unsigned) round(state.rec_int * 1.26 * 1000.0);
|
||||
result->startTime = rideFile->startTime();
|
||||
result->rec_int_ms = (unsigned) round(rideFile->recIntSecs() * 1000.0);
|
||||
QListIterator<RideFilePoint*> i(rideFile->dataPoints());
|
||||
while (i.hasNext()) {
|
||||
const RideFilePoint *p1 = i.next();
|
||||
RawFilePoint *p2 = new RawFilePoint(
|
||||
p1->secs, p1->nm, p1->kph * KM_TO_MILES, p1->watts,
|
||||
p1->km * KM_TO_MILES, (int) p1->cad, (int) p1->hr, p1->interval);
|
||||
if (result->powerHist.contains(p2->watts))
|
||||
result->powerHist[p2->watts] += rideFile->recIntSecs();
|
||||
else
|
||||
result->powerHist[p2->watts] = rideFile->recIntSecs();
|
||||
result->points.append(p2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
107
src/gui/RawRideFile.cpp
Normal file
107
src/gui/RawRideFile.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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 "RawRideFile.h"
|
||||
#include "pt.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#define MILES_TO_KM 1.609344
|
||||
|
||||
static int rawFileReaderRegistered =
|
||||
CombinedFileReader::instance().registerReader("raw", new RawFileReader());
|
||||
|
||||
struct ReadState
|
||||
{
|
||||
RideFile *rideFile;
|
||||
QStringList &errors;
|
||||
double last_secs, last_miles;
|
||||
unsigned last_interval;
|
||||
time_t start_since_epoch;
|
||||
unsigned rec_int;
|
||||
ReadState(RideFile *rideFile,
|
||||
QStringList &errors) :
|
||||
rideFile(rideFile), errors(errors), last_secs(0.0),
|
||||
last_miles(0.0), last_interval(0), start_since_epoch(0), rec_int(0) {}
|
||||
};
|
||||
|
||||
static void
|
||||
config_cb(unsigned interval, unsigned rec_int,
|
||||
unsigned wheel_sz_mm, void *context)
|
||||
{
|
||||
(void) interval;
|
||||
(void) wheel_sz_mm;
|
||||
ReadState *state = (ReadState*) context;
|
||||
// Assume once set, rec_int should never change.
|
||||
double recIntSecs = rec_int * 1.26;
|
||||
assert((state->rideFile->recIntSecs() == 0.0)
|
||||
|| (state->rideFile->recIntSecs() == recIntSecs));
|
||||
state->rideFile->setRecIntSecs(recIntSecs);
|
||||
}
|
||||
|
||||
static void
|
||||
time_cb(struct tm *, time_t since_epoch, void *context)
|
||||
{
|
||||
ReadState *state = (ReadState*) context;
|
||||
if (state->start_since_epoch == 0)
|
||||
state->start_since_epoch = since_epoch;
|
||||
double secs = since_epoch - state->start_since_epoch;
|
||||
state->rideFile->appendPoint(secs, 0.0, 0.0,
|
||||
state->last_miles * MILES_TO_KM, 0.0,
|
||||
0.0, 0.0, state->last_interval);
|
||||
state->last_secs = secs;
|
||||
}
|
||||
|
||||
static void
|
||||
data_cb(double secs, double nm, double mph, double watts, double miles,
|
||||
unsigned cad, unsigned hr, unsigned interval, void *context)
|
||||
{
|
||||
if (nm < 0.0) nm = 0.0;
|
||||
if (mph < 0.0) mph = 0.0;
|
||||
if (watts < 0.0) watts = 0.0;
|
||||
|
||||
ReadState *state = (ReadState*) context;
|
||||
state->rideFile->appendPoint(secs, cad, hr, miles * MILES_TO_KM,
|
||||
mph * MILES_TO_KM, nm, watts, interval);
|
||||
state->last_secs = secs;
|
||||
state->last_miles = miles;
|
||||
state->last_interval = interval;
|
||||
}
|
||||
|
||||
static void
|
||||
error_cb(const char *msg, void *context)
|
||||
{
|
||||
ReadState *state = (ReadState*) context;
|
||||
state->errors.append(QString(msg));
|
||||
}
|
||||
|
||||
RideFile *RawFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
{
|
||||
RideFile *rideFile = new RideFile;
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
delete rideFile;
|
||||
return NULL;
|
||||
}
|
||||
FILE *f = fdopen(file.handle(), "r");
|
||||
assert(f);
|
||||
ReadState state(rideFile, errors);
|
||||
pt_read_raw(f, 0 /* not compat */, &state, config_cb,
|
||||
time_cb, data_cb, error_cb);
|
||||
return rideFile;
|
||||
}
|
||||
|
||||
29
src/gui/RawRideFile.h
Normal file
29
src/gui/RawRideFile.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _RawRideFile_h
|
||||
#define _RawRideFile_h
|
||||
|
||||
#include "RideFile.h"
|
||||
|
||||
struct RawFileReader : public RideFileReader {
|
||||
virtual RideFile *openRideFile(QFile &file, QStringList &errors) const;
|
||||
};
|
||||
|
||||
#endif // _RawRideFile_h
|
||||
|
||||
61
src/gui/RideFile.cpp
Normal file
61
src/gui/RideFile.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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 "RideFile.h"
|
||||
#include <assert.h>
|
||||
|
||||
CombinedFileReader *CombinedFileReader::instance_;
|
||||
|
||||
CombinedFileReader &CombinedFileReader::instance()
|
||||
{
|
||||
if (!instance_)
|
||||
instance_ = new CombinedFileReader();
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
int CombinedFileReader::registerReader(const QString &suffix,
|
||||
RideFileReader *reader)
|
||||
{
|
||||
assert(!readFuncs_.contains(suffix));
|
||||
readFuncs_.insert(suffix, reader);
|
||||
return 1;
|
||||
}
|
||||
|
||||
RideFile *CombinedFileReader::openRideFile(QFile &file,
|
||||
QStringList &errors) const
|
||||
{
|
||||
QString suffix = file.fileName();
|
||||
int dot = suffix.lastIndexOf(".");
|
||||
assert(dot >= 0);
|
||||
suffix.remove(0, dot + 1);
|
||||
RideFileReader *reader = readFuncs_.value(suffix);
|
||||
assert(reader);
|
||||
return reader->openRideFile(file, errors);
|
||||
}
|
||||
|
||||
QStringList CombinedFileReader::listRideFiles(const QDir &dir) const
|
||||
{
|
||||
QStringList filters;
|
||||
QMapIterator<QString,RideFileReader*> i(readFuncs_);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
filters << ("*." + i.key());
|
||||
}
|
||||
return dir.entryList(filters, QDir::Files, QDir::Name);
|
||||
}
|
||||
|
||||
97
src/gui/RideFile.h
Normal file
97
src/gui/RideFile.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _RideFile_h
|
||||
#define _RideFile_h
|
||||
|
||||
#include <QDate>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
|
||||
struct RideFilePoint
|
||||
{
|
||||
double secs, cad, hr, km, kph, nm, watts;
|
||||
int interval;
|
||||
RideFilePoint() : secs(0.0), cad(0.0), hr(0.0), km(0.0), kph(0.0),
|
||||
nm(0.0), watts(0.0), interval(0) {}
|
||||
RideFilePoint(double secs, double cad, double hr, double km, double kph,
|
||||
double nm, double watts, int interval) :
|
||||
secs(secs), cad(cad), hr(hr), km(km), kph(kph), nm(nm),
|
||||
watts(watts), interval(interval) {}
|
||||
};
|
||||
|
||||
class RideFile
|
||||
{
|
||||
private:
|
||||
|
||||
QDateTime startTime_; // time of day that the ride started
|
||||
double recIntSecs_; // recording interval in seconds
|
||||
QList<RideFilePoint*> dataPoints_;
|
||||
|
||||
public:
|
||||
|
||||
RideFile() : recIntSecs_(0.0) {}
|
||||
RideFile(const QDateTime &startTime, double recIntSecs) :
|
||||
startTime_(startTime), recIntSecs_(recIntSecs) {}
|
||||
|
||||
virtual ~RideFile() {
|
||||
QListIterator<RideFilePoint*> i(dataPoints_);
|
||||
while (i.hasNext())
|
||||
delete i.next();
|
||||
}
|
||||
|
||||
const QDateTime &startTime() const { return startTime_; }
|
||||
double recIntSecs() const { return recIntSecs_; }
|
||||
const QList<RideFilePoint*> dataPoints() const { return dataPoints_; }
|
||||
|
||||
void setStartTime(const QDateTime &value) { startTime_ = value; }
|
||||
void setRecIntSecs(double value) { recIntSecs_ = value; }
|
||||
void appendPoint(double secs, double cad, double hr, double km,
|
||||
double kph, double nm, double watts, int interval) {
|
||||
dataPoints_.append(new RideFilePoint(secs, cad, hr, km, kph,
|
||||
nm, watts, interval));
|
||||
}
|
||||
};
|
||||
|
||||
struct RideFileReader {
|
||||
virtual ~RideFileReader() {}
|
||||
virtual RideFile *openRideFile(QFile &file, QStringList &errors) const = 0;
|
||||
};
|
||||
|
||||
class CombinedFileReader : public RideFileReader {
|
||||
|
||||
private:
|
||||
|
||||
static CombinedFileReader *instance_;
|
||||
QMap<QString,RideFileReader*> readFuncs_;
|
||||
|
||||
CombinedFileReader() {}
|
||||
|
||||
public:
|
||||
|
||||
static CombinedFileReader &instance();
|
||||
|
||||
int registerReader(const QString &suffix, RideFileReader *reader);
|
||||
virtual RideFile *openRideFile(QFile &file, QStringList &errors) const;
|
||||
QStringList listRideFiles(const QDir &dir) const;
|
||||
};
|
||||
|
||||
#endif // _RideFile_h
|
||||
|
||||
@@ -131,6 +131,8 @@ double RideItem::timeInZone(int zone)
|
||||
{
|
||||
if (summary.isEmpty())
|
||||
htmlSummary();
|
||||
if (!raw)
|
||||
return 0.0;
|
||||
assert(zone_range >= 0);
|
||||
assert(zone < num_zones);
|
||||
return time_in_zone[zone];
|
||||
@@ -143,6 +145,14 @@ RideItem::htmlSummary()
|
||||
QFile file(path + "/" + fileName);
|
||||
QStringList errors;
|
||||
raw = RawFile::readFile(file, errors);
|
||||
if (!raw) {
|
||||
summary = ("<p>Couldn't read file \"" +
|
||||
file.fileName() + "\":");
|
||||
QListIterator<QString> i(errors);
|
||||
while (i.hasNext())
|
||||
summary += "<br>" + i.next();
|
||||
return summary;
|
||||
}
|
||||
summary = ("<p><center><h2>"
|
||||
+ dateTime.toString("dddd MMMM d, yyyy, h:mm AP")
|
||||
+ "</h2><p><h2>Summary</h2>");
|
||||
|
||||
42
src/gui/SrmRideFile.cpp
Normal file
42
src/gui/SrmRideFile.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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 "SrmRideFile.h"
|
||||
#include "srm.h"
|
||||
|
||||
#define PI 3.14159265
|
||||
|
||||
static int srmFileReaderRegistered =
|
||||
CombinedFileReader::instance().registerReader("srm", new SrmFileReader());
|
||||
|
||||
RideFile *SrmFileReader::openRideFile(QFile &file, QStringList &errors) const
|
||||
{
|
||||
SrmData srmData;
|
||||
if (!readSrmFile(file, srmData, errors))
|
||||
return NULL;
|
||||
RideFile *rideFile = new RideFile(srmData.startTime, srmData.recint);
|
||||
QListIterator<SrmDataPoint*> i(srmData.dataPoints);
|
||||
while (i.hasNext()) {
|
||||
SrmDataPoint *p = i.next();
|
||||
double nm = p->watts / 2.0 / PI / p->cad * 60.0;
|
||||
rideFile->appendPoint(p->secs, p->cad, p->hr, p->km,
|
||||
p->kph, nm, p->watts, p->interval);
|
||||
}
|
||||
return rideFile;
|
||||
}
|
||||
|
||||
29
src/gui/SrmRideFile.h
Normal file
29
src/gui/SrmRideFile.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net)
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _SrmRideFile_h
|
||||
#define _SrmRideFile_h
|
||||
|
||||
#include "RideFile.h"
|
||||
|
||||
struct SrmFileReader : public RideFileReader {
|
||||
virtual RideFile *openRideFile(QFile &file, QStringList &errors) const;
|
||||
};
|
||||
|
||||
#endif // _SrmRideFile_h
|
||||
|
||||
Reference in New Issue
Block a user