mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-16 01:19:57 +00:00
Restructure source directory
Introducing a directory structure to make it a bit less daunting for new developers and perhaps even old hands. The main folders all start with an upper character, so src files are now located in; * Core - Core data structures * Gui - Main GUI elements * Metrics - Models and Metrics * FileIO - Device and File I/O * Charts - All the chart types * Cloud - Working with Web Resources * Train - Anything Train View specific * ANT - Our ANT+ Stack * Resources - Images, Translations, Web etc Apologies to anyone who needs to merge across this update.
This commit is contained in:
251
src/FileIO/SyncRideFile.cpp
Normal file
251
src/FileIO/SyncRideFile.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Damien Grauser (Damien.Grauser@pev-geneve.ch)
|
||||
*
|
||||
* 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 "SyncRideFile.h"
|
||||
#include <QSharedPointer>
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
static int syncFileReaderRegistered =
|
||||
RideFileFactory::instance().registerReader(
|
||||
"osyn", "Macro GoldenCheetah Sync File", new SyncFileReader());
|
||||
|
||||
|
||||
#define TRAINING_DETAIL 0x89 //Packet with details about a specific training
|
||||
|
||||
|
||||
struct SyncFileReaderState
|
||||
{
|
||||
QFile &file;
|
||||
QStringList &errors;
|
||||
RideFile *rideFile;
|
||||
|
||||
double secs, km;
|
||||
|
||||
int interval;
|
||||
double last_interval_secs;
|
||||
|
||||
bool stopped;
|
||||
|
||||
|
||||
SyncFileReaderState(QFile &file, QStringList &errors) :
|
||||
file(file), errors(errors), rideFile(NULL), secs(0), km(0),
|
||||
interval(0), last_interval_secs(0.0), stopped(true)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct TruncatedRead {};
|
||||
|
||||
int read_bsd_byte(int *count = NULL, int *sum = NULL) {
|
||||
char c;
|
||||
if (file.read(&c, 1) != 1)
|
||||
throw TruncatedRead();
|
||||
if (sum)
|
||||
*sum += (0xff & c);
|
||||
if (count)
|
||||
*count += 1;
|
||||
|
||||
return (0xff & c) - (int(c/16)*6);
|
||||
}
|
||||
|
||||
int read_bytes(int len, int *count = NULL, int *sum = NULL) {
|
||||
char c;
|
||||
int res = 0;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (file.read(&c, 1) != 1)
|
||||
throw TruncatedRead();
|
||||
if (sum)
|
||||
*sum += (0xff & c);
|
||||
if (count)
|
||||
*count += 1;
|
||||
|
||||
res += pow(256,i) * (0xff & c);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
QString
|
||||
cEscape(char *buf, int len)
|
||||
{
|
||||
char *result = new char[4 * len + 1];
|
||||
char *tmp = result;
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (buf[i] == '"')
|
||||
tmp += sprintf(tmp, "\\\"");
|
||||
else if (isprint(buf[i]))
|
||||
*(tmp++) = buf[i];
|
||||
else
|
||||
tmp += sprintf(tmp, "\\x%02x", 0xff & (unsigned) buf[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void read_graph_data(double *secs, int *count = NULL, int *sum = NULL)
|
||||
{
|
||||
double alt = 0, cad = 0, grade = 0, hr = 0, kph = 0, watts = 0;
|
||||
|
||||
double nm = 0, headwind = 0.0, temp = 0.0;
|
||||
int lng = 0, lat = 0;
|
||||
|
||||
// Graph data (12 bytes)
|
||||
|
||||
temp = read_bytes(2, count, sum) / 100; // Temperature * 100 (in degree C)
|
||||
|
||||
alt = read_bytes(2, count, sum); //Alti (in m)
|
||||
grade = read_bytes(1, count, sum); // Gradient %
|
||||
cad = read_bytes(1, count, sum); // Cadence
|
||||
kph = read_bytes(2, count, sum)/10.0; // Speed * 10 (in Km/h)
|
||||
hr = read_bytes(1, count, sum); // Heart Rate
|
||||
watts = read_bytes(2, count, sum); // Power (in Watt)
|
||||
|
||||
int record_data_flag = read_bytes(1, count, sum); // Data Flag
|
||||
|
||||
int intSecs = 1;
|
||||
if ((record_data_flag & 0x03) == 0)
|
||||
intSecs = 5;
|
||||
else if ((record_data_flag & 0x03) == 1)
|
||||
intSecs = 10;
|
||||
else if ((record_data_flag & 0x03) == 2)
|
||||
intSecs = 20;
|
||||
|
||||
km += intSecs * kph / 3600.0;
|
||||
rideFile->setRecIntSecs(intSecs);
|
||||
rideFile->appendPoint(*secs, cad, hr, km, kph, nm, watts, alt, lng, lat, headwind, grade, temp, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, interval);
|
||||
|
||||
*secs = *secs + intSecs;
|
||||
|
||||
}
|
||||
|
||||
int read_page()
|
||||
{
|
||||
int sum = 0;
|
||||
int bytes_read = 0;
|
||||
|
||||
char record_command = read_bytes(1, &bytes_read, &sum); // Always 0x89
|
||||
|
||||
if ((0xff & record_command) == 0x89)
|
||||
{
|
||||
// 1. page # data
|
||||
int page_number = read_bytes(2, &bytes_read, &sum); // Page #
|
||||
int data_number = read_bytes(1, &bytes_read, &sum); // # of data in page
|
||||
|
||||
|
||||
|
||||
if (page_number == 1 || (page_number == 64010 && secs == 0.0)) {
|
||||
// 2. Training Summary data (60 bytes)";
|
||||
read_bytes(39, &bytes_read, &sum);
|
||||
|
||||
int record_training_flag = read_bytes(1, &bytes_read, &sum); // Training Flag
|
||||
|
||||
if ((record_training_flag & 0x01) == 0) {
|
||||
// Only new lap
|
||||
rideFile->addInterval(RideFileInterval::DEVICE, last_interval_secs, secs, QString("%1").arg(interval));
|
||||
last_interval_secs = secs;
|
||||
interval ++;
|
||||
}
|
||||
|
||||
read_bytes(20, &bytes_read, &sum); // Don't care
|
||||
}
|
||||
|
||||
if (page_number == 1 || (page_number == 64010 && secs == 0.0)) {
|
||||
// Section Start time and date data (12 byte)
|
||||
|
||||
int sec = read_bsd_byte(&bytes_read, &sum); // Section start time sec
|
||||
int min = read_bsd_byte(&bytes_read, &sum); // Section start time min
|
||||
int hour = read_bsd_byte(&bytes_read, &sum); // Section start time hour
|
||||
int day = read_bsd_byte(&bytes_read, &sum); // Section start time day
|
||||
int month = read_bytes(1, &bytes_read, &sum); // Section start time month
|
||||
int year = read_bsd_byte(&bytes_read, &sum); // Section start time year
|
||||
|
||||
QDateTime t = QDateTime(QDate(2000+year,month,day), QTime(hour,min,sec));
|
||||
|
||||
if (secs == 0.0) { // || rideFile->startTime().toTime_t()<0
|
||||
rideFile->setStartTime(t);
|
||||
}
|
||||
else {
|
||||
int gap = (t.toTime_t() - rideFile->startTime().toTime_t()) - secs;
|
||||
secs += gap;
|
||||
}
|
||||
|
||||
read_bytes(5, &bytes_read, &sum); // Don't care
|
||||
|
||||
read_bytes(1, &bytes_read, &sum); // Data Flag
|
||||
data_number--;
|
||||
}
|
||||
|
||||
for (int i = 0; i < data_number; ++i) {
|
||||
read_graph_data(&secs, &bytes_read, &sum);
|
||||
}
|
||||
|
||||
int finish = 259-bytes_read;
|
||||
|
||||
for (int i = 0; i < finish; i++) {
|
||||
read_bytes(1, &bytes_read, &sum); // to finish
|
||||
}
|
||||
|
||||
read_bytes(1, &bytes_read, &sum); // Checksum
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
RideFile * run() {
|
||||
errors.clear();
|
||||
rideFile = new RideFile;
|
||||
rideFile->setDeviceType("o_synce macro/macrox");
|
||||
rideFile->setFileFormat("Macro GoldenCheetah Sync File (osyn)");
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
delete rideFile;
|
||||
return NULL;
|
||||
}
|
||||
bool stop = false;
|
||||
|
||||
int data_size = file.size();
|
||||
int bytes_read = 0;
|
||||
|
||||
while (!stop && (bytes_read < data_size)) {
|
||||
bytes_read += read_page(); // read_page(stop, errors);
|
||||
}
|
||||
|
||||
if (stop) {
|
||||
file.close();
|
||||
delete rideFile;
|
||||
return NULL;
|
||||
|
||||
} else {
|
||||
file.close();
|
||||
return rideFile;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
RideFile *SyncFileReader::openRideFile(QFile &file, QStringList &errors, QList<RideFile*>*) const
|
||||
{
|
||||
QSharedPointer<SyncFileReaderState> state(new SyncFileReaderState(file, errors));
|
||||
return state->run();
|
||||
}
|
||||
Reference in New Issue
Block a user