mirror of
https://github.com/tumic0/GPXSee.git
synced 2024-11-28 05:34:47 +01:00
Proper fix for Bryton Rider FIT files
Refactoring
This commit is contained in:
parent
3424b3e265
commit
366e84c9fc
@ -1,4 +1,3 @@
|
|||||||
#include <cstring>
|
|
||||||
#include <QtEndian>
|
#include <QtEndian>
|
||||||
#include "common/staticassert.h"
|
#include "common/staticassert.h"
|
||||||
#include "fitparser.h"
|
#include "fitparser.h"
|
||||||
@ -10,50 +9,64 @@
|
|||||||
#define EVENT_MESSAGE 21
|
#define EVENT_MESSAGE 21
|
||||||
#define TIMESTAMP_FIELD 253
|
#define TIMESTAMP_FIELD 253
|
||||||
|
|
||||||
struct Event {
|
class Event {
|
||||||
|
public:
|
||||||
|
Event() : id(0), type(0), data(0) {}
|
||||||
|
|
||||||
quint8 id;
|
quint8 id;
|
||||||
quint8 type;
|
quint8 type;
|
||||||
quint32 data;
|
quint32 data;
|
||||||
|
};
|
||||||
|
|
||||||
Event() : id(0), type(0), data(0) {}
|
struct FileHeader {
|
||||||
|
quint8 headerSize;
|
||||||
|
quint8 protocolVersion;
|
||||||
|
quint16 profileVersion;
|
||||||
|
quint32 dataSize;
|
||||||
|
quint32 magic;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FITParser::Field {
|
||||||
|
quint8 id;
|
||||||
|
quint8 size;
|
||||||
|
quint8 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FITParser::MessageDefinition {
|
||||||
|
public:
|
||||||
|
MessageDefinition() : endian(0), globalId(0), numFields(0), fields(0),
|
||||||
|
numDevFields(0), devFields(0) {}
|
||||||
|
~MessageDefinition() {delete[] fields; delete[] devFields;}
|
||||||
|
|
||||||
|
quint8 endian;
|
||||||
|
quint16 globalId;
|
||||||
|
quint8 numFields;
|
||||||
|
FITParser::Field *fields;
|
||||||
|
quint8 numDevFields;
|
||||||
|
FITParser::Field *devFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FITParser::CTX {
|
||||||
|
public:
|
||||||
|
CTX(QFile *file) : file(file), len(0), endian(0), timestamp(0),
|
||||||
|
lastWrite(0), ratio(NAN) {}
|
||||||
|
|
||||||
|
QFile *file;
|
||||||
|
quint32 len;
|
||||||
|
quint8 endian;
|
||||||
|
quint32 timestamp, lastWrite;
|
||||||
|
FITParser::MessageDefinition defs[16];
|
||||||
|
qreal ratio;
|
||||||
|
Trackpoint trackpoint;
|
||||||
|
TrackData track;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
FITParser::FITParser()
|
bool FITParser::readData(QFile *file, char *data, size_t size)
|
||||||
{
|
|
||||||
memset(_defs, 0, sizeof(_defs));
|
|
||||||
|
|
||||||
_device = 0;
|
|
||||||
_endian = 0;
|
|
||||||
_timestamp = 0;
|
|
||||||
_last = 0;
|
|
||||||
_len = 0;
|
|
||||||
_ratio = NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FITParser::clearDefinitions()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
if (_defs[i].fields)
|
|
||||||
delete[] _defs[i].fields;
|
|
||||||
if (_defs[i].devFields)
|
|
||||||
delete[] _defs[i].devFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(_defs, 0, sizeof(_defs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FITParser::warning(const char *text) const
|
|
||||||
{
|
|
||||||
const QFile *file = static_cast<QFile *>(_device);
|
|
||||||
qWarning("%s:%d: %s\n", qPrintable(file->fileName()), _len, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FITParser::readData(char *data, size_t size)
|
|
||||||
{
|
{
|
||||||
qint64 n;
|
qint64 n;
|
||||||
|
|
||||||
n = _device->read(data, size);
|
n = file->read(data, size);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
_errorString = "I/O error";
|
_errorString = "I/O error";
|
||||||
return false;
|
return false;
|
||||||
@ -65,17 +78,17 @@ bool FITParser::readData(char *data, size_t size)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T> bool FITParser::readValue(T &val)
|
template<class T> bool FITParser::readValue(CTX &ctx, T &val)
|
||||||
{
|
{
|
||||||
T data;
|
T data;
|
||||||
|
|
||||||
if (!readData((char*)&data, sizeof(T)))
|
if (!readData(ctx.file, (char*)&data, sizeof(T)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_len -= sizeof(T);
|
ctx.len -= sizeof(T);
|
||||||
|
|
||||||
if (sizeof(T) > 1) {
|
if (sizeof(T) > 1) {
|
||||||
if (_endian)
|
if (ctx.endian)
|
||||||
val = qFromBigEndian(data);
|
val = qFromBigEndian(data);
|
||||||
else
|
else
|
||||||
val = qFromLittleEndian(data);
|
val = qFromLittleEndian(data);
|
||||||
@ -85,22 +98,16 @@ template<class T> bool FITParser::readValue(T &val)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::skipValue(size_t size)
|
bool FITParser::skipValue(CTX &ctx, size_t size)
|
||||||
{
|
{
|
||||||
size_t i;
|
ctx.len -= size;
|
||||||
quint8 val;
|
return ctx.file->seek(ctx.file->pos() + size);
|
||||||
|
|
||||||
for (i = 0; i < size; i++)
|
|
||||||
if (!readValue(val))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::parseDefinitionMessage(quint8 header)
|
bool FITParser::parseDefinitionMessage(CTX &ctx, quint8 header)
|
||||||
{
|
{
|
||||||
int local_id = header & 0x0f;
|
int local_id = header & 0x0f;
|
||||||
MessageDefinition *def = &_defs[local_id];
|
MessageDefinition *def = &(ctx.defs[local_id]);
|
||||||
quint8 i;
|
quint8 i;
|
||||||
|
|
||||||
|
|
||||||
@ -114,47 +121,48 @@ bool FITParser::parseDefinitionMessage(quint8 header)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// reserved/unused
|
// reserved/unused
|
||||||
if (!readValue(i))
|
if (!readValue(ctx, i))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// endianness
|
// endianness
|
||||||
if (!readValue(def->endian))
|
if (!readValue(ctx, def->endian))
|
||||||
return false;
|
return false;
|
||||||
if (def->endian > 1) {
|
if (def->endian > 1) {
|
||||||
_errorString = "Bad endian field";
|
_errorString = "Bad endian field";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_endian = def->endian;
|
ctx.endian = def->endian;
|
||||||
|
|
||||||
// global message number
|
// global message number
|
||||||
if (!readValue(def->globalId))
|
if (!readValue(ctx, def->globalId))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// number of records
|
// number of records
|
||||||
if (!readValue(def->numFields))
|
if (!readValue(ctx, def->numFields))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// definition records
|
// definition records
|
||||||
def->fields = new Field[def->numFields];
|
def->fields = new Field[def->numFields];
|
||||||
for (i = 0; i < def->numFields; i++) {
|
for (i = 0; i < def->numFields; i++) {
|
||||||
STATIC_ASSERT(sizeof(def->fields[i]) == 3);
|
STATIC_ASSERT(sizeof(def->fields[i]) == 3);
|
||||||
if (!readData((char*)&(def->fields[i]), sizeof(def->fields[i])))
|
if (!readData(ctx.file, (char*)&(def->fields[i]),
|
||||||
|
sizeof(def->fields[i])))
|
||||||
return false;
|
return false;
|
||||||
_len -= sizeof(def->fields[i]);
|
ctx.len -= sizeof(def->fields[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// developer definition records
|
// developer definition records
|
||||||
if (header & 0x20) {
|
if (header & 0x20) {
|
||||||
if (!readValue(def->numDevFields))
|
if (!readValue(ctx, def->numDevFields))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
def->devFields = new Field[def->numDevFields];
|
def->devFields = new Field[def->numDevFields];
|
||||||
for (i = 0; i < def->numDevFields; i++) {
|
for (i = 0; i < def->numDevFields; i++) {
|
||||||
STATIC_ASSERT(sizeof(def->devFields[i]) == 3);
|
STATIC_ASSERT(sizeof(def->devFields[i]) == 3);
|
||||||
if (!readData((char*)&(def->devFields[i]),
|
if (!readData(ctx.file, (char*)&(def->devFields[i]),
|
||||||
sizeof(def->devFields[i])))
|
sizeof(def->devFields[i])))
|
||||||
return false;
|
return false;
|
||||||
_len -= sizeof(def->devFields[i]);
|
ctx.len -= sizeof(def->devFields[i]);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
def->numDevFields = 0;
|
def->numDevFields = 0;
|
||||||
@ -162,7 +170,7 @@ bool FITParser::parseDefinitionMessage(quint8 header)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::readField(Field *f, quint32 &val)
|
bool FITParser::readField(CTX &ctx, Field *field, quint32 &val)
|
||||||
{
|
{
|
||||||
quint8 v8 = (quint8)-1;
|
quint8 v8 = (quint8)-1;
|
||||||
quint16 v16 = (quint16)-1;
|
quint16 v16 = (quint16)-1;
|
||||||
@ -170,65 +178,44 @@ bool FITParser::readField(Field *f, quint32 &val)
|
|||||||
|
|
||||||
val = (quint32)-1;
|
val = (quint32)-1;
|
||||||
|
|
||||||
switch (f->type) {
|
switch (field->type) {
|
||||||
case 0: // enum
|
case 0: // enum
|
||||||
case 1: // sint8
|
case 1: // sint8
|
||||||
case 2: // uint8
|
case 2: // uint8
|
||||||
if (f->size == 1) {
|
if (field->size == 1) {
|
||||||
ret = readValue(v8);
|
ret = readValue(ctx, v8);
|
||||||
val = v8;
|
val = v8;
|
||||||
} else
|
} else
|
||||||
ret = skipValue(f->size);
|
ret = skipValue(ctx, field->size);
|
||||||
break;
|
break;
|
||||||
case 0x83: // sint16
|
case 0x83: // sint16
|
||||||
case 0x84: // uint16
|
case 0x84: // uint16
|
||||||
if (f->size == 2) {
|
if (field->size == 2) {
|
||||||
ret = readValue(v16);
|
ret = readValue(ctx, v16);
|
||||||
val = v16;
|
val = v16;
|
||||||
} else
|
} else
|
||||||
ret = skipValue(f->size);
|
ret = skipValue(ctx, field->size);
|
||||||
break;
|
break;
|
||||||
case 0x85: // sint32
|
case 0x85: // sint32
|
||||||
case 0x86: // uint32
|
case 0x86: // uint32
|
||||||
if (f->size == 4)
|
if (field->size == 4)
|
||||||
ret = readValue(val);
|
ret = readValue(ctx, val);
|
||||||
else
|
else
|
||||||
ret = skipValue(f->size);
|
ret = skipValue(ctx, field->size);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = skipValue(f->size);
|
ret = skipValue(ctx, field->size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::addEntry(TrackData &track)
|
bool FITParser::parseData(CTX &ctx, const MessageDefinition *def)
|
||||||
{
|
|
||||||
if (_trackpoint.coordinates().isValid()) {
|
|
||||||
_trackpoint.setTimestamp(QDateTime::fromTime_t(_timestamp
|
|
||||||
+ 631065600));
|
|
||||||
_trackpoint.setRatio(_ratio);
|
|
||||||
track.append(_trackpoint);
|
|
||||||
} else {
|
|
||||||
if (_trackpoint.coordinates().isNull())
|
|
||||||
warning("Missing coordinates");
|
|
||||||
else {
|
|
||||||
_errorString = "Invalid coordinates";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FITParser::parseData(TrackData &track, MessageDefinition *def,
|
|
||||||
quint8 offset)
|
|
||||||
{
|
{
|
||||||
Field *field;
|
Field *field;
|
||||||
Event event;
|
Event event;
|
||||||
quint32 val;
|
quint32 val;
|
||||||
int i;
|
|
||||||
|
|
||||||
|
|
||||||
if (!def->fields && !def->devFields) {
|
if (!def->fields && !def->devFields) {
|
||||||
@ -236,58 +223,50 @@ bool FITParser::parseData(TrackData &track, MessageDefinition *def,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def->globalId == RECORD_MESSAGE && _last != _timestamp) {
|
ctx.endian = def->endian;
|
||||||
if (!addEntry(track))
|
|
||||||
return false;
|
|
||||||
_last = _timestamp;
|
|
||||||
_trackpoint = Trackpoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
_endian = def->endian;
|
for (int i = 0; i < def->numFields; i++) {
|
||||||
_timestamp += offset;
|
|
||||||
|
|
||||||
for (i = 0; i < def->numFields; i++) {
|
|
||||||
field = &def->fields[i];
|
field = &def->fields[i];
|
||||||
if (!readField(field, val))
|
if (!readField(ctx, field, val))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (field->id == TIMESTAMP_FIELD)
|
if (field->id == TIMESTAMP_FIELD)
|
||||||
_timestamp = val;
|
ctx.timestamp = val;
|
||||||
else if (def->globalId == RECORD_MESSAGE) {
|
else if (def->globalId == RECORD_MESSAGE) {
|
||||||
switch (field->id) {
|
switch (field->id) {
|
||||||
case 0:
|
case 0:
|
||||||
if (val != 0x7fffffff)
|
if (val != 0x7fffffff)
|
||||||
_trackpoint.rcoordinates().setLat(
|
ctx.trackpoint.rcoordinates().setLat(
|
||||||
((qint32)val / (double)0x7fffffff) * 180);
|
((qint32)val / (double)0x7fffffff) * 180);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (val != 0x7fffffff)
|
if (val != 0x7fffffff)
|
||||||
_trackpoint.rcoordinates().setLon(
|
ctx.trackpoint.rcoordinates().setLon(
|
||||||
((qint32)val / (double)0x7fffffff) * 180);
|
((qint32)val / (double)0x7fffffff) * 180);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (val != 0xffff)
|
if (val != 0xffff)
|
||||||
_trackpoint.setElevation((val / 5.0) - 500);
|
ctx.trackpoint.setElevation((val / 5.0) - 500);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (val != 0xff)
|
if (val != 0xff)
|
||||||
_trackpoint.setHeartRate(val);
|
ctx.trackpoint.setHeartRate(val);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if (val != 0xff)
|
if (val != 0xff)
|
||||||
_trackpoint.setCadence(val);
|
ctx.trackpoint.setCadence(val);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
if (val != 0xffff)
|
if (val != 0xffff)
|
||||||
_trackpoint.setSpeed(val / 1000.0f);
|
ctx.trackpoint.setSpeed(val / 1000.0f);
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
if (val != 0xffff)
|
if (val != 0xffff)
|
||||||
_trackpoint.setPower(val);
|
ctx.trackpoint.setPower(val);
|
||||||
break;
|
break;
|
||||||
case 13:
|
case 13:
|
||||||
if (val != 0x7f)
|
if (val != 0x7f)
|
||||||
_trackpoint.setTemperature((qint8)val);
|
ctx.trackpoint.setTemperature((qint8)val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -308,73 +287,85 @@ bool FITParser::parseData(TrackData &track, MessageDefinition *def,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < def->numDevFields; i++) {
|
for (int i = 0; i < def->numDevFields; i++) {
|
||||||
field = &def->devFields[i];
|
field = &def->devFields[i];
|
||||||
if (!readField(field, val))
|
if (!readField(ctx, field, val))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (def->globalId == EVENT_MESSAGE) {
|
if (def->globalId == EVENT_MESSAGE) {
|
||||||
if ((event.id == 42 || event.id == 43) && event.type == 3) {
|
if ((event.id == 42 || event.id == 43) && event.type == 3) {
|
||||||
quint32 front = ((event.data & 0xFF000000) >> 24);
|
quint32 front = ((event.data & 0xFF000000) >> 24);
|
||||||
quint32 rear = ((event.data & 0x0000FF00) >> 8);
|
quint32 rear = ((event.data & 0x0000FF00) >> 8);
|
||||||
_ratio = ((qreal)front / (qreal)rear);
|
ctx.ratio = ((qreal)front / (qreal)rear);
|
||||||
|
}
|
||||||
|
} else if (def->globalId == RECORD_MESSAGE) {
|
||||||
|
if (ctx.timestamp > ctx.lastWrite
|
||||||
|
&& ctx.trackpoint.coordinates().isValid()) {
|
||||||
|
ctx.trackpoint.setTimestamp(QDateTime::fromTime_t(ctx.timestamp
|
||||||
|
+ 631065600));
|
||||||
|
ctx.trackpoint.setRatio(ctx.ratio);
|
||||||
|
ctx.track.append(ctx.trackpoint);
|
||||||
|
ctx.trackpoint = Trackpoint();
|
||||||
|
ctx.lastWrite = ctx.timestamp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::parseDataMessage(TrackData &track, quint8 header)
|
bool FITParser::parseDataMessage(CTX &ctx, quint8 header)
|
||||||
{
|
{
|
||||||
int local_id = header & 0xf;
|
int local_id = header & 0xf;
|
||||||
MessageDefinition *def = &_defs[local_id];
|
MessageDefinition *def = &(ctx.defs[local_id]);
|
||||||
return parseData(track, def, 0);
|
return parseData(ctx, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::parseCompressedMessage(TrackData &track, quint8 header)
|
bool FITParser::parseCompressedMessage(CTX &ctx, quint8 header)
|
||||||
{
|
{
|
||||||
int local_id = (header >> 5) & 3;
|
int local_id = (header >> 5) & 3;
|
||||||
MessageDefinition *def = &_defs[local_id];
|
MessageDefinition *def = &(ctx.defs[local_id]);
|
||||||
return parseData(track, def, header & 0x1f);
|
ctx.timestamp += header & 0x1f;
|
||||||
|
return parseData(ctx, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::parseRecord(TrackData &track)
|
bool FITParser::parseRecord(CTX &ctx)
|
||||||
{
|
{
|
||||||
quint8 header;
|
quint8 header;
|
||||||
|
|
||||||
if (!readValue(header))
|
if (!readValue(ctx, header))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (header & 0x80)
|
if (header & 0x80)
|
||||||
return parseCompressedMessage(track, header);
|
return parseCompressedMessage(ctx, header);
|
||||||
else if (header & 0x40)
|
else if (header & 0x40)
|
||||||
return parseDefinitionMessage(header);
|
return parseDefinitionMessage(ctx, header);
|
||||||
else
|
else
|
||||||
return parseDataMessage(track, header);
|
return parseDataMessage(ctx, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FITParser::parseHeader()
|
bool FITParser::parseHeader(CTX &ctx)
|
||||||
{
|
{
|
||||||
FileHeader hdr;
|
FileHeader hdr;
|
||||||
quint16 crc;
|
quint16 crc;
|
||||||
qint64 len;
|
qint64 len;
|
||||||
|
|
||||||
STATIC_ASSERT(sizeof(hdr) == 12);
|
STATIC_ASSERT(sizeof(hdr) == 12);
|
||||||
len = _device->read((char*)&hdr, sizeof(hdr));
|
len = ctx.file->read((char*)&hdr, sizeof(hdr));
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
_errorString = "I/O error";
|
_errorString = "I/O error";
|
||||||
return false;
|
return false;
|
||||||
} else if ((size_t)len < sizeof(hdr)
|
} else if ((size_t)len < sizeof(hdr)
|
||||||
|| hdr.magic != qToLittleEndian(FIT_MAGIC)) {
|
|| hdr.magic != qToLittleEndian((quint32)FIT_MAGIC)) {
|
||||||
_errorString = "Not a FIT file";
|
_errorString = "Not a FIT file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_len = qFromLittleEndian(hdr.dataSize);
|
ctx.len = qFromLittleEndian(hdr.dataSize);
|
||||||
|
|
||||||
if (hdr.headerSize > sizeof(hdr))
|
if (hdr.headerSize > sizeof(hdr))
|
||||||
if (!readData((char *)&crc, sizeof(crc)))
|
if (!readData(ctx.file, (char *)&crc, sizeof(crc)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -385,28 +376,17 @@ bool FITParser::parse(QFile *file, QList<TrackData> &tracks,
|
|||||||
{
|
{
|
||||||
Q_UNUSED(routes);
|
Q_UNUSED(routes);
|
||||||
Q_UNUSED(waypoints);
|
Q_UNUSED(waypoints);
|
||||||
bool ret = true;
|
CTX ctx(file);
|
||||||
|
|
||||||
_device = file;
|
|
||||||
_endian = 0;
|
|
||||||
_timestamp = 0;
|
|
||||||
_last = 0;
|
|
||||||
_ratio = NAN;
|
|
||||||
_trackpoint = Trackpoint();
|
|
||||||
|
|
||||||
if (!parseHeader())
|
if (!parseHeader(ctx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
tracks.append(TrackData());
|
while (ctx.len)
|
||||||
TrackData &track = tracks.last();
|
if (!parseRecord(ctx))
|
||||||
|
return false;
|
||||||
|
|
||||||
while (_len)
|
tracks.append(ctx.track);
|
||||||
if (!(ret = parseRecord(track)))
|
|
||||||
break;
|
|
||||||
if (ret && _trackpoint.coordinates().isValid())
|
|
||||||
ret = addEntry(track);
|
|
||||||
|
|
||||||
clearDefinitions();
|
return true;
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
@ -3,66 +3,34 @@
|
|||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
|
class QFile;
|
||||||
|
|
||||||
class FITParser : public Parser
|
class FITParser : public Parser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FITParser();
|
|
||||||
|
|
||||||
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
|
bool parse(QFile *file, QList<TrackData> &tracks, QList<RouteData> &routes,
|
||||||
QList<Waypoint> &waypoints);
|
QList<Waypoint> &waypoints);
|
||||||
QString errorString() const {return _errorString;}
|
QString errorString() const {return _errorString;}
|
||||||
int errorLine() const {return 0;}
|
int errorLine() const {return 0;}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct FileHeader {
|
struct Field;
|
||||||
quint8 headerSize;
|
class MessageDefinition;
|
||||||
quint8 protocolVersion;
|
class CTX;
|
||||||
quint16 profileVersion;
|
|
||||||
quint32 dataSize;
|
|
||||||
quint32 magic;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Field {
|
bool readData(QFile *file, char *data, size_t size);
|
||||||
quint8 id;
|
template<class T> bool readValue(CTX &ctx, T &val);
|
||||||
quint8 size;
|
bool skipValue(CTX &ctx, size_t size);
|
||||||
quint8 type;
|
bool readField(CTX &ctx, Field *field, quint32 &val);
|
||||||
};
|
|
||||||
|
|
||||||
struct MessageDefinition {
|
bool parseHeader(CTX &ctx);
|
||||||
quint8 endian;
|
bool parseRecord(CTX &ctx);
|
||||||
quint16 globalId;
|
bool parseDefinitionMessage(CTX &ctx, quint8 header);
|
||||||
quint8 numFields;
|
bool parseCompressedMessage(CTX &ctx, quint8 header);
|
||||||
Field *fields;
|
bool parseDataMessage(CTX &ctx, quint8 header);
|
||||||
quint8 numDevFields;
|
bool parseData(CTX &ctx, const MessageDefinition *def);
|
||||||
Field *devFields;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void warning(const char *text) const;
|
|
||||||
void clearDefinitions();
|
|
||||||
|
|
||||||
bool readData(char *data, size_t size);
|
|
||||||
template<class T> bool readValue(T &val);
|
|
||||||
bool skipValue(size_t size);
|
|
||||||
|
|
||||||
bool parseHeader();
|
|
||||||
bool parseRecord(TrackData &track);
|
|
||||||
bool parseDefinitionMessage(quint8 header);
|
|
||||||
bool parseCompressedMessage(TrackData &track, quint8 header);
|
|
||||||
bool parseDataMessage(TrackData &track, quint8 header);
|
|
||||||
bool parseData(TrackData &track, MessageDefinition *def, quint8 offset);
|
|
||||||
bool readField(Field *f, quint32 &val);
|
|
||||||
bool addEntry(TrackData &track);
|
|
||||||
|
|
||||||
QIODevice *_device;
|
|
||||||
QString _errorString;
|
QString _errorString;
|
||||||
|
|
||||||
quint32 _len;
|
|
||||||
quint8 _endian;
|
|
||||||
quint32 _timestamp, _last;
|
|
||||||
MessageDefinition _defs[16];
|
|
||||||
qreal _ratio;
|
|
||||||
Trackpoint _trackpoint;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FITPARSER_H
|
#endif // FITPARSER_H
|
||||||
|
@ -67,7 +67,8 @@ inline QDebug operator<<(QDebug dbg, const Trackpoint &trackpoint)
|
|||||||
dbg.nospace() << "Trackpoint(" << trackpoint.coordinates() << ", "
|
dbg.nospace() << "Trackpoint(" << trackpoint.coordinates() << ", "
|
||||||
<< trackpoint.timestamp() << ", " << trackpoint.elevation() << ", "
|
<< trackpoint.timestamp() << ", " << trackpoint.elevation() << ", "
|
||||||
<< trackpoint.speed() << ", " << trackpoint.heartRate() << ", "
|
<< trackpoint.speed() << ", " << trackpoint.heartRate() << ", "
|
||||||
<< trackpoint.temperature() << ")";
|
<< trackpoint.temperature() << ", " << trackpoint.cadence() << ", "
|
||||||
|
<< trackpoint.power() << ", " << trackpoint.ratio() << ")";
|
||||||
return dbg.space();
|
return dbg.space();
|
||||||
}
|
}
|
||||||
#endif // QT_NO_DEBUG
|
#endif // QT_NO_DEBUG
|
||||||
|
Loading…
Reference in New Issue
Block a user