72 d->parseFileHeader<QByteArray>();
92 d->parseFileHeader<QIODevice>();
127 if (d->device !=
nullptr) {
128 qWarning(
"FitStreamReader: addData() with device()");
129 Q_ASSERT(d->device !=
nullptr);
163 d->expectedDataSize = 0;
164 d->protocolVersion = QVersionNumber();
165 d->profileVersion = QVersionNumber();
166 d->dataDefinitions.clear();
167 d->recordSizes.clear();
200 if (
device) d->parseFileHeader<QIODevice>();
214 return d->profileVersion;
228 return d->protocolVersion;
249 return (d->device ==
nullptr)
250 ? d->readNextDataMessage<QByteArray>() : d->readNextDataMessage<QIODevice>();
268 : dataOffset(0), device(nullptr), q_ptr(q)
296 Q_ASSERT(
device ==
nullptr);
307 Q_ASSERT(
device !=
nullptr);
308 return device->bytesAvailable();
325 const QByteArray header = readFileHeader<T>();
326 qDebug() <<
"Header size" << header.size() <<
"bytes";
327 if (header.isEmpty()) {
329 qDebug() <<
"not enough bytes for header";
332 if (header.size() < 12) {
334 qDebug() <<
"invalid header size";
339 const quint8 version = header.at(1);
345 const quint16 version = qFromLittleEndian<quint16>(header.mid(2,2).data());
353 const QByteArray dataType = header.mid(8,4);
354 qDebug() <<
"Data type" << dataType;
355 if (dataType != QByteArray(
".FIT")) {
361 if (header.size() >= 14) {
362 const quint16 expectedChecksum = qFromLittleEndian<quint16>(
data.mid(12,2).data());
363 if (expectedChecksum == 0x0000) {
364 qDebug() <<
"FIT file has (optional) checksum 0x0000; ignoring.";
366 const quint16 calculatedChecksum =
fitChecksum(header.mid(0,12));
367 qDebug() <<
"Header checksum" << expectedChecksum << calculatedChecksum;
368 if (calculatedChecksum != expectedChecksum) {
369 qWarning() <<
"Checksum failure:" << calculatedChecksum <<
"!=" << expectedChecksum;
372 qDebug() <<
"Checkums match";
391 qDebug() <<
"parsing definition message";
392 Q_ASSERT(bytesAvailable<T>());
393 const quint8 recordHeader = peekByte<T>();
394 qDebug() << __func__ <<
"recordHeader" << recordHeader;
396 "FIT record header does not indiciate a definition message");
397 const bool hasDevFields = (recordHeader & (1 << 5));
398 qDebug() << __func__ <<
"has dev fields" << hasDevFields;
399 qDebug() << __func__ <<
"reserved" << (recordHeader & (1 << 4));
400 const quint8 localMessageType = (recordHeader & 0xF);
401 qDebug() << __func__ <<
"local message type" << localMessageType;
404 if (bytesAvailable<T>() < 6) {
407 const quint8 numberOfFields = peekByte<T>(5);
408 qDebug() << __func__ <<
"number of fields" << numberOfFields;
409 int numberOfDevFields = 0;
411 const int offsetToNumberOfDevFields = 6 + (numberOfFields * 3);
412 qDebug() << __func__ <<
"offset to number of dev fields" << offsetToNumberOfDevFields;
413 if (bytesAvailable<T>() < offsetToNumberOfDevFields) {
416 numberOfDevFields = peekByte<T>(offsetToNumberOfDevFields);
418 const int recordSize = 6 + (numberOfFields * 3) + (hasDevFields ? 1 + (numberOfDevFields * 3) : 0);
419 qDebug() << __func__ <<
"number of dev fields" << numberOfDevFields;
420 qDebug() << __func__ <<
"record size" << recordSize;
421 const QByteArray record = readBytes<T>(recordSize);
422 if (record.isEmpty()) {
430 qDebug() << __func__ <<
"architecture" << (int)defn.
architecture;
431 qDebug() << record.mid(3,2);
433 ? qFromBigEndian<quint16>(record.mid(3,2))
434 : qFromLittleEndian<quint16>(record.mid(3,2)));
435 qDebug() << __func__ <<
"header" << (quint8)record.at(0);
436 qDebug() << __func__ <<
"reserved" << (quint8)record.at(1);
440 for (
int i=0; i < numberOfFields; ++i) {
441 const int pos = 6 + (i * 3);
443 field.
number = record.at(pos);
444 field.
size = record.at(pos+1);
446 qDebug() << __func__ <<
"field" << i <<
"number" << field.
number;
447 qDebug() << __func__ <<
"field" << i <<
"size" << field.
size;
448 qDebug() << __func__ <<
"field" << i <<
"type" <<
static_cast<quint8
>(field.
baseType);
453 qDebug() << __func__ <<
"defn record size" << defn.
recordSize;
456 Q_ASSERT(hasDevFields || (numberOfDevFields == 0));
457 for (
int i=0; i < numberOfDevFields; ++i) {
458 const int pos = 6 + (numberOfFields * 3) + 1 + (i * 3);
461 field.
size = record.at(pos+1);
463 qDebug() << __func__ <<
"dev field" << i <<
"number" << field.
fieldNumber;
464 qDebug() << __func__ <<
"dev field" << i <<
"size" << field.
size;
465 qDebug() << __func__ <<
"dev field" << i <<
"dataIndex" << field.
devDataIndex;
470 qDebug() << __func__ <<
"defn record size" << defn.
recordSize;
472 qWarning() <<
"defintion record size is zero";
495 Q_ASSERT(bytesAvailable<T>());
496 qDebug() <<
"parsing data message";
497 const quint8 recordHeader = peekByte<T>();
498 qDebug() << __func__ <<
"recordHeader" << recordHeader;
500 "FIT record header does not indiciate a data message");
503 quint8 localMessageType;
504 if (recordHeader & (1 << 7)) {
505 qDebug() << __func__ <<
"Compressed Timestamp Header";
506 localMessageType = ((recordHeader >> 4) & 0x7);
507 const quint8 timeOffset = (recordHeader & 0xF);
508 qDebug() <<
"time offset" << timeOffset;
511 qDebug() << __func__ <<
"Normal (Data Message) Header";
512 qDebug() << __func__ <<
"reserved5" << (recordHeader & (1 << 5));
513 qDebug() << __func__ <<
"reserved4" << (recordHeader & (1 << 4));
514 localMessageType = (recordHeader & 0xF);
516 qDebug() << __func__ <<
"local message type" << localMessageType;
520 qWarning() <<
"No definition for local message type" << localMessageType;
527 qDebug() << __func__ <<
"record size" << defn.
recordSize;
529 qWarning() <<
"record size is zero";
532 const QByteArray record = readBytes<T>(defn.
recordSize+1);
533 if (record.isEmpty()) {
536 qDebug() <<
"record" << record.mid(1);
566 template<> quint8 FitStreamReaderPrivate::peekByte<QByteArray>(
const int pos)
const
568 Q_ASSERT(
device ==
nullptr);
579 template<> quint8 FitStreamReaderPrivate::peekByte<QIODevice>(
const int pos)
const
581 Q_ASSERT(
device !=
nullptr);
582 const QByteArray bytes =
device->peek(1+pos);
583 return (bytes.size() > pos) ? bytes.at(pos) : 0;
603 template<> QByteArray FitStreamReaderPrivate::readBytes<QByteArray>(
const size_t size)
605 Q_ASSERT(
device ==
nullptr);
616 template<> QByteArray FitStreamReaderPrivate::readBytes<QIODevice>(
const size_t size)
618 Q_ASSERT(
device !=
nullptr);
619 return (
device->bytesAvailable() < size) ? QByteArray() :
device->read(size);
633 return (bytesAvailable<T>()) ? readBytes<T>(peekByte<T>()) : QByteArray();
657 while (bytesAvailable<T>()) {
658 const quint8 recordHeader = peekByte<T>();
660 if (!parseDefinitionMessage<T>())
return nullptr;
662 }
else return parseDataMessage<T>();
681 for (
const auto &
byte:
data) {
682 static const quint16 crcTable[16] = {
683 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
684 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
686 for (
int byteShift=0; byteShift<=4; byteShift+=4) {
687 const quint16 tmp = crcTable[checksum & 0xF];
688 checksum = (checksum >> 4) & 0x0FFF;
689 checksum = checksum ^ tmp ^ crcTable[(
byte >> byteShift) & 0xF];
707 return ((recordHeader >> 6) == 1);
#define QTFIT_END_NAMESPACE
Macro for ending the QtFit library's top-most namespace (if one is defined).
#define QTFIT_BEGIN_NAMESPACE
Macro for starting the QtFit library's top-most namespace (if one is defined).
Declares the AbstractDataMessage class.
The AbstractDataMessage class is the polymorphic base class for all FIT Data Message classes.
static AbstractDataMessage * fromData(const DataDefinition *const defn, const QByteArray &record)
Constructs the relevant AbstractDataMessage-derived class to parse record according to defn.
Provides private implementation for FitStreamReader.
quint32 expectedDataSize
Total size, in bytes, expected to comprise the FIT file.
AbstractDataMessage * readNextDataMessage()
Reads up to, and including, the next FIT Data Message.
QIODevice * device
FIT File IO stream (alternative to data and dataOffset).
static bool isDefinitionMessage(const quint8 recordHeader)
Returns true if recordHeader indicates a Definition Message, otherwise false.
bool parseDefinitionMessage()
Reads and parses a FIT Definition Message.
QByteArray data
FIT File data (alternative to device).
static quint16 fitChecksum(const QByteArray &data)
Calculates a checksum, as per the algorithm used by FIT file headers.
AbstractDataMessage * parseDataMessage()
Reads and parses a FIT Data Message.
QVersionNumber profileVersion
Protocol version read from the parsed FIT file header.
QByteArray readFileHeader()
Reads all bytes of the FIT stream file header.
QVersionNumber protocolVersion
Protocol version read from the parsed FIT file header.
size_t dataOffset
Current position within data.
QHash< int, DataDefinition > dataDefinitions
Local message types to current data definitions.
FitStreamReaderPrivate(FitStreamReader *const q)
Constructs a FitStreamReaderPrivate object, with public implementation q.
qsizetype size_t
Size type for size-related operations.
bool parseFileHeader()
Reads and parses the FIT stream's file header.
virtual ~FitStreamReaderPrivate()
Destroys the FitStreamReaderPrivate object.
The FitStreamReader class provides a streaming parser for reading Garmin FIT files.
AbstractDataMessage * readNext()
Returns the next FIT data message from the underlying stream, or a null data message if none could be...
QVersionNumber profileVersion() const
Returns the profile version read from the FIT file header, otherwise a null QVersionNumber.
FitStreamReader()
Constructs a FIT stream reader with no initial data.
bool atEnd() const
Returns true if the reader has reached the end of the underlying data or device, or an error has occu...
~FitStreamReader()
Destroys the FIT stream reader.
void addData(const QByteArray &data)
Adds more data to read.
QIODevice * device() const
Returns the current device associated with the reader, or nullptr if no device is assigned.
void clear()
Clears the FIT stream reader.
void setDevice(QIODevice *device)
Sets the current device to device, and resets the stream to its initial state.
FitStreamReaderPrivate *const d_ptr
Internal d-pointer.
QVersionNumber protocolVersion() const
Returns the protocol version read from the FIT file header, otherwise a null QVersionNumber.
Declares the FitStreamReader class.
Declares the FitStreamReaderPrivate class.
QList< FieldDefinition > fieldDefinitions
Definitons list of all fields, if any, present in the described Data Message.
QList< DeveloperFieldDefinition > developerFieldDefinitions
Definitions list of all custom fields, if any, present in the described Data Message.
int recordSize
Total size, in byetes, of all fields in the described Data Message.
Architecture architecture
Architecture type for any multi-byte fields.
MesgNum globalMessageNumber
FIT Global Message Number the Data Message represents.
Custom developer field definition.
quint8 fieldNumber
Maps to the field_definition_number of a field_description Message.
quint8 size
Size (in bytes) of the specified FIT message’s field.
quint8 devDataIndex
Maps to the developer_data_index of a developer_data_id Message.
Field Definition for FIT Data Messages.
FitBaseType baseType
Base type for interpreting unknown fields.
quint8 number
Unique ID for the FIT field within a given FIT data message.
quint8 size
Size (in bytes) of the field.
FitBaseType
Garmin FIT FitBaseType type.
MesgNum
Garmin FIT MesgNum type.
Architecture
Architecture Type for FIT Data Messages.
@ BigEndian
Little-endian byte ordering.