Support for Joule BIN File Format

The new source files, missed from previous commit.
This commit is contained in:
Damien Grauser
2010-08-10 18:55:55 +01:00
committed by Mark Liversedge
parent 861fa5ee38
commit 5e575beaff
2 changed files with 733 additions and 0 deletions

704
src/BinRideFile.cpp Normal file
View File

@@ -0,0 +1,704 @@
/*
* Copyright (c) 2010 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 "BinRideFile.h"
#include <QSharedPointer>
#include <QMap>
#include <QSet>
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
#define RECORD_TYPE__META 0
#define RECORD_TYPE__RIDE_DATA 1
#define RECORD_TYPE__RAW_DATA 2
#define RECORD_TYPE__SPARSE_DATA 3
#define RECORD_TYPE__INTERVAL_DATA 4
#define RECORD_TYPE__DATA_ERROR 5
#define RECORD_TYPE__HISTORY 6
#define FORMAT_ID__RIDE_START 0
#define FORMAT_ID__RIDE_SAMPLE_RATE 1
#define FORMAT_ID__FIRMWARE_VERSION 2
#define FORMAT_ID__LAST_UPDATE 3
#define FORMAT_ID__ODOMETER 4
#define FORMAT_ID__PRIMARY_POWER_ID 5
#define FORMAT_ID__SECONDARY_POWER_ID 6
#define FORMAT_ID__CHEST_STRAP_ID 7
#define FORMAT_ID__CADENCE_ID 8
#define FORMAT_ID__SPEED_ID 9
#define FORMAT_ID__RESISTANCE_UNITID 10
#define FORMAT_ID__WORKOUT_ID 11
#define FORMAT_ID__USER_WEIGHT 12
#define FORMAT_ID__USER_CATEGORY 13
#define FORMAT_ID__USER_HR_ZONE_1 14
#define FORMAT_ID__USER_HR_ZONE_2 15
#define FORMAT_ID__USER_HR_ZONE_3 16
#define FORMAT_ID__USER_HR_ZONE_4 17
#define FORMAT_ID__USER_POWER_ZONE_1 18
#define FORMAT_ID__USER_POWER_ZONE_2 19
#define FORMAT_ID__USER_POWER_ZONE_3 20
#define FORMAT_ID__USER_POWER_ZONE_4 21
#define FORMAT_ID__USER_POWER_ZONE_5 22
#define FORMAT_ID__WHEEL_CIRC 23
#define FORMAT_ID__RIDE_DISTANCE 24
#define FORMAT_ID__RIDE_TIME 25
#define FORMAT_ID__POWER 26
#define FORMAT_ID__TORQUE 27
#define FORMAT_ID__SPEED 28
#define FORMAT_ID__CADENCE 29
#define FORMAT_ID__HEART_RATE 30
#define FORMAT_ID__GRADE 31
#define FORMAT_ID__ALTITUDE_OLD 32
#define FORMAT_ID__RAW_DATA 33
#define FORMAT_ID__TEMPERATURE 34
#define FORMAT_ID__INTERVAL_NUM 35
#define FORMAT_ID__DROPOUT_FLAGS 36
#define FORMAT_ID__RAW_DATA_FORMAT 37
#define FORMAT_ID__RAW_BARO_SENSOR 38
#define FORMAT_ID__ALTITUDE 39
#define FORMAT_ID__THRESHOLD_POWER 40
static int binFileReaderRegistered =
RideFileFactory::instance().registerReader(
"bin", "Joule Bin File", new BinFileReader());
struct BinField {
int num;
int id;
int size; // in bytes
};
struct BinDefinition {
int format_identifier;
std::vector<BinField> fields;
};
struct BinFileReaderState
{
static QMap<int,QString> global_record_types;
static QMap<int,QString> global_format_identifiers;
QMap<int, BinDefinition> local_format_identifiers;
QSet<int> unknown_record_types, unknown_format_identifiers;
QSet<int> unused_record_types;
QSet<int> unexpected_record_types;
QMap<int, QSet<int> > unknown_format_identifiers_for_record_types;
QMap<int, QSet<int> > unused_format_identifiers_for_record_types;
QMap<int, QSet<int> > unexpected_format_identifiers_for_record_types;
QFile &file;
QStringList &errors;
RideFile *rideFile;
time_t start_time;
int interval;
double last_interval_secs;
bool stopped;
BinFileReaderState(QFile &file, QStringList &errors) :
file(file), errors(errors), rideFile(NULL), start_time(0),
interval(0), last_interval_secs(0.0), stopped(true)
{
if (global_record_types.isEmpty()) {
global_record_types.insert(RECORD_TYPE__META, "Meta Data");
global_record_types.insert(RECORD_TYPE__RIDE_DATA, "1 sec detail ride data");
global_record_types.insert(RECORD_TYPE__RAW_DATA, "Raw data packet");
global_record_types.insert(RECORD_TYPE__SPARSE_DATA, "Sparse ride data");
global_record_types.insert(RECORD_TYPE__INTERVAL_DATA, "Interval");
global_record_types.insert(RECORD_TYPE__DATA_ERROR, "Data error");
global_record_types.insert(RECORD_TYPE__HISTORY, "History summary record");
}
if (global_format_identifiers.isEmpty()) {
global_format_identifiers.insert(FORMAT_ID__RIDE_START, "Ride start");
global_format_identifiers.insert(FORMAT_ID__RIDE_SAMPLE_RATE, "Ride sample rate");
global_format_identifiers.insert(FORMAT_ID__FIRMWARE_VERSION, "Firmware Version");
global_format_identifiers.insert(FORMAT_ID__LAST_UPDATE, "Last update");
global_format_identifiers.insert(FORMAT_ID__ODOMETER, "Odometer");
global_format_identifiers.insert(FORMAT_ID__PRIMARY_POWER_ID, "Primary Power Radio ID");
global_format_identifiers.insert(FORMAT_ID__SECONDARY_POWER_ID, "Secondary Power Radio ID");
global_format_identifiers.insert(FORMAT_ID__CHEST_STRAP_ID, "Chest strap Radio ID");
global_format_identifiers.insert(FORMAT_ID__CADENCE_ID, "Cadense sensor Radio ID");
global_format_identifiers.insert(FORMAT_ID__SPEED_ID, "Speed sensor Radio ID");
global_format_identifiers.insert(FORMAT_ID__RESISTANCE_UNITID, "Resistance Unit Radio ID");
global_format_identifiers.insert(FORMAT_ID__WORKOUT_ID, "Workout ID");
global_format_identifiers.insert(FORMAT_ID__USER_WEIGHT, "User weight");
global_format_identifiers.insert(FORMAT_ID__USER_CATEGORY, "User training category");
global_format_identifiers.insert(FORMAT_ID__USER_HR_ZONE_1, "User HR Zone 1");
global_format_identifiers.insert(FORMAT_ID__USER_HR_ZONE_2, "User HR Zone 2");
global_format_identifiers.insert(FORMAT_ID__USER_HR_ZONE_3, "User HR Zone 3");
global_format_identifiers.insert(FORMAT_ID__USER_HR_ZONE_4, "User HR Zone 4");
global_format_identifiers.insert(FORMAT_ID__USER_POWER_ZONE_1, "User Power Zone 1");
global_format_identifiers.insert(FORMAT_ID__USER_POWER_ZONE_2, "User Power Zone 2");
global_format_identifiers.insert(FORMAT_ID__USER_POWER_ZONE_3, "User Power Zone 3");
global_format_identifiers.insert(FORMAT_ID__USER_POWER_ZONE_4, "User Power Zone 4");
global_format_identifiers.insert(FORMAT_ID__USER_POWER_ZONE_5, "User Power Zone 5");
global_format_identifiers.insert(FORMAT_ID__WHEEL_CIRC, "Wheel circumference");
global_format_identifiers.insert(FORMAT_ID__RIDE_DISTANCE, "Ride distance");
global_format_identifiers.insert(FORMAT_ID__RIDE_TIME, "Ride time");
global_format_identifiers.insert(FORMAT_ID__POWER, "Power");
global_format_identifiers.insert(FORMAT_ID__TORQUE, "Torque");
global_format_identifiers.insert(FORMAT_ID__SPEED, "Speed");
global_format_identifiers.insert(FORMAT_ID__CADENCE, "Cadence");
global_format_identifiers.insert(FORMAT_ID__HEART_RATE, "Heart rate");
global_format_identifiers.insert(FORMAT_ID__GRADE, "Grade");
global_format_identifiers.insert(FORMAT_ID__ALTITUDE_OLD, "Altitude");
global_format_identifiers.insert(FORMAT_ID__RAW_DATA, "Raw Data");
global_format_identifiers.insert(FORMAT_ID__TEMPERATURE, "Temperature");
global_format_identifiers.insert(FORMAT_ID__INTERVAL_NUM, "Interval number");
global_format_identifiers.insert(FORMAT_ID__DROPOUT_FLAGS, "Dropout flags");
global_format_identifiers.insert(FORMAT_ID__RAW_DATA_FORMAT, "Raw Data Format ID");
global_format_identifiers.insert(FORMAT_ID__RAW_BARO_SENSOR, "Raw Baro Sensor Value");
global_format_identifiers.insert(FORMAT_ID__ALTITUDE, "Altitude");
global_format_identifiers.insert(FORMAT_ID__THRESHOLD_POWER, "Threshold power");
}
}
struct TruncatedRead {};
int read_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 read_double_byte(int *count = NULL, int *sum = NULL) {
char c1,c2;
if (file.read(&c1, 1) != 1)
throw TruncatedRead();
if (file.read(&c2, 1) != 1)
throw TruncatedRead();
if (sum)
*sum += (0xff & c1) + (0xff & c2);
if (count)
*count += 2;
return 256*(0xff & c1) + (0xff & c2);
}
int read_four_byte(int *count = NULL, int *sum = NULL) {
char c1,c2,c3,c4;
if (file.read(&c1, 1) != 1)
throw TruncatedRead();
if (file.read(&c2, 1) != 1)
throw TruncatedRead();
if (file.read(&c3, 1) != 1)
throw TruncatedRead();
if (file.read(&c4, 1) != 1)
throw TruncatedRead();
if (sum)
*sum += (0xff & c1) + (0xff & c2) + (0xff & c3) + (0xff & c4);
if (count)
*count += 4;
return 256*256*256*(0xff & c1) + 256*256*(0xff & c2) + 256*(0xff & c3) + (0xff & c4);
}
int read_date(int *count = NULL, int *sum = NULL) {
char c1,c2,c3,c4,c5,c6,c7;
if (file.read(&c1, 1) != 1)
throw TruncatedRead();
if (file.read(&c2, 1) != 1)
throw TruncatedRead();
if (file.read(&c3, 1) != 1)
throw TruncatedRead();
if (file.read(&c4, 1) != 1)
throw TruncatedRead();
if (file.read(&c5, 1) != 1)
throw TruncatedRead();
if (file.read(&c6, 1) != 1)
throw TruncatedRead();
if (file.read(&c7, 1) != 1)
throw TruncatedRead();
if (sum)
*sum += (0xff & c1) + (0xff & c2) + (0xff & c3) + (0xff & c4) + (0xff & c5) + (0xff & c6) + (0xff & c7);
if (count)
*count += 7;
QDateTime dateTime(QDate((0xff & c1)*256+(0xff & c2), (0xff & c3), (0xff & c4)), QTime((0xff & c5), (0xff & c6), (0xff & c7)), Qt::UTC);
return dateTime.toTime_t();
}
void decodeMetaData(const BinDefinition &def, const std::vector<int> values) {
int i = 0;
QString deviceInfo = "";
QDateTime t;
foreach(const BinField &field, def.fields) {
if (!global_format_identifiers.contains(field.id)) {
unknown_format_identifiers.insert(field.id);
} else {
int value = values[i++];
switch (field.id) {
case FORMAT_ID__RIDE_START : {
start_time = value;
t.setTime_t(value);
rideFile->setStartTime(t);
break;
}
case FORMAT_ID__RIDE_SAMPLE_RATE :
rideFile->setRecIntSecs(value/1000.0);
break;
case FORMAT_ID__FIRMWARE_VERSION :
//rideFile->setDeviceType(rideFile->deviceType()+ QString(" (%1)").arg(value));
deviceInfo += rideFile->deviceType()+QString(" Version %1\n").arg(value);
break;
case FORMAT_ID__LAST_UPDATE :
//t.setTime_t(value);
//deviceInfo += QString("Last update %1\n").arg(t.toString());
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__ODOMETER :
if (value>0)
deviceInfo += QString("Odometer %1km\n").arg(value/10.0);
break;
case FORMAT_ID__PRIMARY_POWER_ID :
if (value>0)
deviceInfo += QString("Primary Power Id %1\n").arg(value);
break;
case FORMAT_ID__SECONDARY_POWER_ID :
if (value>0)
deviceInfo += QString("Secondary Power Id %1\n").arg(value);
break;
case FORMAT_ID__CHEST_STRAP_ID :
if (value>0)
deviceInfo += QString("Chest strap Id %1\n").arg(value);
break;
case FORMAT_ID__CADENCE_ID :
if (value>0)
deviceInfo += QString("Cadence Id %1\n").arg(value);
break;
case FORMAT_ID__SPEED_ID :
if (value>0)
deviceInfo += QString("Speed Id %1\n").arg(value);
break;
case FORMAT_ID__RESISTANCE_UNITID :
if (value>0)
deviceInfo += QString("Resistance Unit Id %1\n").arg(value);
break;
case FORMAT_ID__WORKOUT_ID :
rideFile->setTag("Workout Code", QString(value));
break;
case FORMAT_ID__USER_WEIGHT :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_CATEGORY :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_HR_ZONE_1 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_HR_ZONE_2 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_HR_ZONE_3 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_HR_ZONE_4 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_POWER_ZONE_1 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_POWER_ZONE_2 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_POWER_ZONE_3 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_POWER_ZONE_4 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__USER_POWER_ZONE_5 :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__WHEEL_CIRC :
deviceInfo += QString("Wheel Circ. %1mm\n").arg(value);
break;
case FORMAT_ID__THRESHOLD_POWER :
deviceInfo += QString("Threshold Power %1W\n").arg(value);
break;
default:
unexpected_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
}
}
}
rideFile->setTag("Device Info", deviceInfo);
}
void decodeRideData(const BinDefinition &def, const std::vector<int> values) {
int i = 0;
double secs = 0, alt = 0, cad = 0, km = 0, grade = 0, hr = 0;
double nm = 0, kph = 0, watts = 0;
foreach(const BinField &field, def.fields) {
if (!global_format_identifiers.contains(field.id)) {
unknown_format_identifiers.insert(field.id);
} else {
int value = values[i++];
switch (field.id) {
case FORMAT_ID__RIDE_DISTANCE :
km = value/10000.0;
case FORMAT_ID__RIDE_TIME :
secs = value / 1000.0;
break;
case FORMAT_ID__POWER :
if (value <= 2999) // Limit from definition
watts = value;
break;
case FORMAT_ID__TORQUE :
nm = value;
break;
case FORMAT_ID__SPEED :
value = value*3.6/100.0;
if (value < 145) // Limit for data error
kph = value;
break;
case FORMAT_ID__CADENCE :
if (value < 255) // Limit for data error
cad = value;
break;
case FORMAT_ID__HEART_RATE :
if (value < 255) // Limit for data error
hr = value;
break;
case FORMAT_ID__GRADE :
grade = value;
break;
case FORMAT_ID__ALTITUDE :
alt = value/10.0;
break;
case FORMAT_ID__ALTITUDE_OLD :
alt = value/10.0;
break;
default:
unexpected_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
}
}
}
double headwind = 0.0;
int interval = 0;
int lng = 0;
int lat = 0;
rideFile->appendPoint(secs, cad, hr, km, kph, nm, watts, alt, lng, lat, headwind, interval);
//printf("addPoint time %f hr %f speed %f dist %f alt %f\n", secs, hr, kph, km, alt);
}
void decodeSparseData(const BinDefinition &def, const std::vector<int> values) {
int i = 0;
int temperature_count = 0;
double temperature = 0.0;
foreach(const BinField &field, def.fields) {
if (!global_format_identifiers.contains(field.id)) {
unknown_format_identifiers.insert(field.id);
} else {
int value = values[i++];
switch (field.id) {
case FORMAT_ID__TEMPERATURE :
// use for average
temperature += value/10.0;
temperature_count ++;
break;
case FORMAT_ID__RIDE_TIME :
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
default:
unexpected_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
}
}
}
rideFile->setTag("Temperature", QString("%1").arg(temperature/temperature_count));
}
void decodeIntervalData(const BinDefinition &def, const std::vector<int> values) {
int i = 0;
double secs = 0;
foreach(const BinField &field, def.fields) {
if (!global_format_identifiers.contains(field.id)) {
unknown_format_identifiers.insert(field.id);
} else {
int value = values[i++];
switch (field.id) {
case FORMAT_ID__INTERVAL_NUM :
interval = value;
break;
case FORMAT_ID__RIDE_TIME :
secs = value / 1000.0;;
break;
default:
unexpected_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
}
}
}
if (interval>1) {
rideFile->addInterval(last_interval_secs, secs, QString("%1").arg(interval-1));
}
last_interval_secs = secs;
}
void decodeDataError(const BinDefinition &def, const std::vector<int> values) {
int i = 0;
foreach(const BinField &field, def.fields) {
if (!global_format_identifiers.contains(field.id)) {
unknown_format_identifiers.insert(field.id);
} else {
int value = values[i++];
bool b0 = (value % 2);
bool b1 = (value / 2>1);
bool b2 = (value / 4>1);
bool b3 = (value / 8>1);
bool b4 = (value / 16>1);
bool b5 = (value / 32>1);
bool b6 = (value / 64>1);
bool b7 = (value / 128>1);
QString b = QString("DataError : %1 %2 %3 %4 %5 %6 %7 %8").arg(b0?"0":"").arg(b1?"1":"").arg(b2?"2":"").arg(b3?"3":"").arg(b4?"4":"").arg(b5?"5":"").arg(b6?"6":"").arg(b7?"7":"");
switch (field.id) {
case FORMAT_ID__DROPOUT_FLAGS :
//errors << QString("DataError field.id %1 value %2").arg(field.id).arg(b);
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
case FORMAT_ID__RIDE_TIME :
//errors << QString("DataError time field.id %1 value %2").arg(field.id).arg(value/1000);
unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
break;
default:
unexpected_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
}
}
}
}
void decodeHistoryData(const BinDefinition &def, const std::vector<int> /*values*/) {
//int i = 0;
foreach(const BinField &field, def.fields) {
if (!global_format_identifiers.contains(field.id)) {
unknown_format_identifiers.insert(field.id);
} else {
//int value = values[i++];
switch (field.id) {
default:
unexpected_format_identifiers_for_record_types[def.format_identifier].insert(field.id);
}
}
}
}
int read_record(bool &stop, QStringList &errors) {
int sum = 0;
int bytes_read = 0;
int record_type = read_byte(&bytes_read, &sum); // Always 0xFF
if (record_type == 255) {
int format_identifier = read_byte(&bytes_read, &sum);
if (!global_record_types.contains(format_identifier)) {
errors << QString("unknown format_identifier %1").arg(format_identifier);
stop = true;
return bytes_read;
}
int nb_meta = read_double_byte(&bytes_read, &sum);
local_format_identifiers.insert(format_identifier, BinDefinition());
//printf("- format_identifier : %d\n", format_identifier);
BinDefinition &def = local_format_identifiers[format_identifier];
def.format_identifier = format_identifier;
for (int i = 0; i < nb_meta; ++i) {
def.fields.push_back(BinField());
BinField &field = def.fields.back();
int field_id = read_double_byte(&bytes_read,&sum);
field.id = field_id;
int field_size = read_double_byte(&bytes_read,&sum);
field.size = field_size;
//printf("- field %d : %d\n", field_id, field_size);
}
int checksum = read_double_byte(&bytes_read);
//printf("- checksum %d : %d\n", checksum, sum);
if (checksum != sum) {
errors << QString("bad checksum: %1").arg(sum);
stop = true;
return bytes_read;
}
}
else {
int format_identifier = record_type;
//printf("- data for format identifier : %d\n", format_identifier);
if (!local_format_identifiers.contains(format_identifier)) {
errors << QString("undefined format_identifier: %1").arg(format_identifier);
stop = true;
return bytes_read;
} else {
const BinDefinition &def = local_format_identifiers[format_identifier];
//printf("- fields type : %d\n", def.format_identifier);
std::vector<int> values;
foreach(const BinField &field, def.fields) {
//printf("- field : %d \n", field.id);
int v;
switch (field.size) {
case 1: v = read_byte(&bytes_read,&sum); break;
case 2: v = read_double_byte(&bytes_read,&sum); break;
case 4: v = read_four_byte(&bytes_read,&sum); break;
case 7: v = read_date(&bytes_read,&sum); break;
default:
for (int i = 0; i < field.size; ++i)
read_byte(&bytes_read,&sum);
errors << QString("unsupported field size %1").arg(field.size);
}
values.push_back(v);
//printf("- %d : %d\n", field.id, v);
}
int checksum = read_double_byte(&bytes_read);
//printf("- checksum %d : %d\n", checksum, sum);
if (checksum != sum) {
errors << QString("bad checksum: %1").arg(sum);
stop = true;
return bytes_read;
}
switch (def.format_identifier) {
case RECORD_TYPE__META: decodeMetaData(def, values); break;
case RECORD_TYPE__RIDE_DATA: decodeRideData(def, values); break;
case RECORD_TYPE__RAW_DATA:
unused_record_types.insert(def.format_identifier);
break;
case RECORD_TYPE__SPARSE_DATA: decodeSparseData(def, values); break;
case RECORD_TYPE__INTERVAL_DATA: decodeIntervalData(def, values); break;
case RECORD_TYPE__DATA_ERROR: decodeDataError(def, values); break;
case RECORD_TYPE__HISTORY: decodeHistoryData(def, values); break;
default:
unexpected_record_types.insert(def.format_identifier);
}
}
}
return bytes_read;
}
RideFile * run() {
errors.clear();
rideFile = new RideFile;
rideFile->setDeviceType("Joule");
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_record(stop, errors);
}
if (last_interval_secs>0) {
rideFile->addInterval(last_interval_secs, rideFile->dataPoints().last()->secs, QString("%1").arg(interval));
}
if (stop) {
delete rideFile;
return NULL;
}
else {
foreach(int num, unknown_record_types) {
errors << QString("unknow record type %1; ignoring it").arg(num);
}
foreach(int num, unknown_format_identifiers) {
errors << QString("unknow format identifier %1; ignoring it").arg(num);
}
foreach(int num, unused_record_types) {
errors << QString("unused record type \"%1\" (%2)\n").arg(global_record_types[num].toAscii().constData())
.arg(num);
}
foreach(QSet<int> set, unused_format_identifiers_for_record_types) {
foreach(int num, set) {
int record_type = unused_format_identifiers_for_record_types.keys(set).takeFirst();
errors << QString("unused format identifier \"%1\" (%2) in \"%3\" (%4)\n")
.arg(global_format_identifiers[num].toAscii().constData())
.arg(num)
.arg(global_record_types[record_type].toAscii().constData())
.arg(record_type);
}
}
foreach(int num, unexpected_record_types) {
errors << QString("unexpected record type %1 (%2)\n").arg(global_record_types[num]).arg(num);
}
foreach(QSet<int> set, unexpected_format_identifiers_for_record_types) {
foreach(int num, set) {
int record_type = unexpected_format_identifiers_for_record_types.keys(set).takeFirst();
errors << QString("unexpected format identifier \"%1\" (%2) in \"%3\" (%4)\n")
.arg(global_format_identifiers[num].toAscii().constData())
.arg(num)
.arg(global_record_types[record_type].toAscii().constData())
.arg(record_type);
}
}
return rideFile;
}
}
};
QMap<int,QString> BinFileReaderState::global_record_types;
QMap<int,QString> BinFileReaderState::global_format_identifiers;
RideFile *BinFileReader::openRideFile(QFile &file, QStringList &errors) const
{
QSharedPointer<BinFileReaderState> state(new BinFileReaderState(file, errors));
return state->run();
}

29
src/BinRideFile.h Normal file
View 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 _BinRideFile_h
#define _BinRideFile_h
#include "RideFile.h"
struct BinFileReader : public RideFileReader {
virtual RideFile *openRideFile(QFile &file, QStringList &errors) const;
};
#endif // _BinRideFile_h