-
Notifications
You must be signed in to change notification settings - Fork 128
/
garmin_fit.h
353 lines (312 loc) · 12.9 KB
/
garmin_fit.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
/*
Support for FIT track files.
Copyright (C) 2011 Paul Brook, [email protected]
Copyright (C) 2003-2011 Robert Lipe, [email protected]
Copyright (C) 2019 Martin Buck, [email protected]
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 GARMIN_FIT_H_INCLUDED_
#define GARMIN_FIT_H_INCLUDED_
#include <cstdint> // for uint8_t, uint16_t, uint32_t
#include <deque> // for deque
#include <stdexcept> // for runtime_error
#include <utility> // for pair
#include <vector> // for vector
#include <QHash> // for QHash
#include <QList> // for QList
#include <QString> // for QString
#include <QVariant> // for QVariant
#include <QVector> // for QVector
#include "defs.h"
#include "format.h" // for Format
#include "gbfile.h" // for gbfile
#include "option.h" // for OptionBool
#include "src/core/datetime.h" // for DateTime
class GarminFitFormat : public Format
{
public:
QVector<arglist_t>* get_args() override
{
return &fit_args;
}
ff_type get_type() const override
{
return ff_type_file;
}
QVector<ff_cap> get_cap() const override
{
return {
ff_cap_write, /* waypoints */
(ff_cap)(ff_cap_read | ff_cap_write), /* tracks */
ff_cap_none /* routes */
};
}
void rd_init(const QString& fname) override;
void read() override;
void rd_deinit() override;
void wr_init(const QString& fname) override;
void write() override;
void wr_deinit() override;
private:
/* Types */
struct fit_field_t {
int id {};
int size{};
int type{};
};
struct fit_message_def {
int endian{};
int global_id{};
QList<fit_field_t> fields;
};
struct fit_data_t {
int len{};
int endian{};
route_head* track{nullptr};
uint32_t last_timestamp{};
uint32_t global_utc_offset{};
QHash<int, fit_message_def> message_def;
};
struct FitCourseRecordPoint {
FitCourseRecordPoint(const Waypoint& wpt, bool is_course_point, unsigned int course_point_type = kCoursePointTypeGeneric)
: lat(wpt.latitude),
lon(wpt.longitude),
altitude(wpt.altitude),
speed(wpt.speed_value_or(-1)),
odometer_distance(wpt.odometer_distance),
creation_time(wpt.creation_time),
shortname(wpt.shortname),
is_course_point(is_course_point),
course_point_type(course_point_type) {}
double lat, lon, altitude;
double speed, odometer_distance;
gpsbabel::DateTime creation_time;
QString shortname;
bool is_course_point;
unsigned int course_point_type;
};
class ReaderException : public std::runtime_error
{
using std::runtime_error::runtime_error;
};
/* Constants */
// constants for global IDs
static constexpr int kIdFileId = 0;
static constexpr int kIdDeviceSettings = 0;
static constexpr int kIdLap = 19;
static constexpr int kIdRecord = 20;
static constexpr int kIdEvent = 21;
static constexpr int kIdLocations = 29;
static constexpr int kIdCourse = 31;
static constexpr int kIdCoursePoint = 32;
// constants for local IDs (for writing)
static constexpr int kWriteLocalIdFileId = 0;
static constexpr int kWriteLocalIdCourse = 1;
static constexpr int kWriteLocalIdLap = 2;
static constexpr int kWriteLocalIdEvent = 3;
static constexpr int kWriteLocalIdCoursePoint = 4;
static constexpr int kWriteLocalIdRecord = 5;
// constants for message fields
// for all global IDs
static constexpr int kFieldTimestamp = 253;
static constexpr int kFieldMessageIndex = 254;
// for global ID: file id
static constexpr int kFieldType = 0;
static constexpr int kFieldManufacturer = 1;
static constexpr int kFieldProduct = 2;
static constexpr int kFieldTimeCreated = 4;
// for global ID: device settings
static constexpr int kFieldGlobalUtcOffset = 4;
// for global ID: lap
static constexpr int kFieldStartTime = 2;
static constexpr int kFieldStartLatitude = 3;
static constexpr int kFieldStartLongitude = 4;
static constexpr int kFieldEndLatitude = 5;
static constexpr int kFieldEndLongitude = 6;
static constexpr int kFieldElapsedTime = 7;
static constexpr int kFieldTotalTimerTime = 8;
static constexpr int kFieldTotalDistance = 9;
static constexpr int kFieldAvgSpeed = 13;
static constexpr int kFieldMaxSpeed = 14;
// for global ID: record
static constexpr int kFieldLatitude = 0;
static constexpr int kFieldLongitude = 1;
static constexpr int kFieldAltitude = 2;
static constexpr int kFieldHeartRate = 3;
static constexpr int kFieldCadence = 4;
static constexpr int kFieldDistance = 5;
static constexpr int kFieldSpeed = 6;
static constexpr int kFieldPower = 7;
static constexpr int kFieldTemperature = 13;
static constexpr int kFieldEnhancedSpeed = 73;
static constexpr int kFieldEnhancedAltitude = 78;
// for global ID: event
static constexpr int kFieldEvent = 0;
static constexpr int kEnumEventTimer = 0;
static constexpr int kFieldEventType = 1;
static constexpr int kEnumEventTypeStart = 0;
static constexpr int kFieldEventGroup = 4;
// for global ID: locations
static constexpr int kFieldLocationName = 0;
static constexpr int kFieldLocLatitude = 1;
static constexpr int kFieldLocLongitude = 2;
static constexpr int kEnumLocationIcon = 3;
static constexpr int kFieldLocAltitude = 4;
static constexpr int kFieldLocationDescription = 6;
// for global ID: course
static constexpr int kFieldSport = 4;
static constexpr int kFieldName = 5;
// for global ID: course point
static constexpr int kFieldCPTimeStamp = 1;
static constexpr int kFieldCPPositionLat = 2;
static constexpr int kFieldCPPositionLong = 3;
static constexpr int kFieldCPDistance = 4;
static constexpr int kFieldCPName = 6;
static constexpr int kFieldCPType = 5;
// For developer fields as a non conflicting id
static constexpr int kFieldInvalid = 255;
// types for message definitions
static constexpr int kTypeEnum = 0x00;
static constexpr int kTypeUint8 = 0x02;
static constexpr int kTypeString = 0x07;
static constexpr int kTypeUint16 = 0x84;
static constexpr int kTypeSint32 = 0x85;
static constexpr int kTypeUint32 = 0x86;
// misc. constants for message fields
static constexpr int kFileCourse = 0x06;
static constexpr int kEventTimer = 0x00;
static constexpr int kEventTypeStart = 0x00;
static constexpr int kEventTypeStopDisableAll = 0x09;
static constexpr int kCoursePointTypeGeneric = 0x00;
static constexpr int kCoursePointTypeLeft = 0x06;
static constexpr int kCoursePointTypeRight = 0x07;
static constexpr int kWriteHeaderLen = 12;
static constexpr int kWriteHeaderCrcLen = 14;
static constexpr int kReadHeaderCrcLen = 14;
static constexpr double kSynthSpeed = 10.0 * 1000 / 3600; /* speed in m/s */
/* Member Functions */
void fit_parse_header();
uint8_t fit_getuint8();
uint16_t fit_getuint16();
uint32_t fit_getuint32();
QString fit_getstring(int size);
void fit_parse_definition_message(uint8_t header);
QVariant fit_read_field(const fit_field_t& f);
void fit_parse_data(const fit_message_def& def, int time_offset);
void fit_parse_data_message(uint8_t header);
void fit_parse_compressed_message(uint8_t header);
void fit_parse_record();
void fit_check_file_crc() const;
void fit_write_message_def(uint8_t local_id, uint16_t global_id, const std::vector<fit_field_t>& fields) const;
static uint16_t fit_crc16(uint8_t data, uint16_t crc);
void fit_write_timestamp(const gpsbabel::DateTime& t) const;
void fit_write_fixed_string(const QString& s, unsigned int len) const;
void fit_write_position(double pos) const;
void fit_write_msg_file_id(uint8_t type, uint16_t manufacturer, uint16_t product, const gpsbabel::DateTime& time_created) const;
void fit_write_msg_course(const QString& name, uint8_t sport) const;
void fit_write_msg_lap(const gpsbabel::DateTime& timestamp, const gpsbabel::DateTime& start_time, double start_position_lat, double start_position_long, double end_position_lat, double end_position_long, uint32_t total_elapsed_time_s, double total_distance_m, double avg_speed_ms, double max_speed_ms) const;
void fit_write_msg_event(const gpsbabel::DateTime& timestamp, uint8_t event, uint8_t event_type, uint8_t event_group) const;
void fit_write_msg_course_point(const gpsbabel::DateTime& timestamp, double position_lat, double position_long, double distance_m, const QString& name, uint8_t type) const;
void fit_write_msg_record(const gpsbabel::DateTime& timestamp, double position_lat, double position_long, double distance_m, double altitude, double speed_ms) const;
void fit_write_file_header(uint32_t file_size, uint16_t crc) const;
void fit_write_header_msgs(const gpsbabel::DateTime& ctime, const QString& name) const;
void fit_write_file_finish() const;
void fit_collect_track_hdr(const route_head* rte);
void fit_collect_trackpt(const Waypoint* waypointp);
void fit_collect_track_tlr(const route_head* rte);
void fit_collect_waypt(const Waypoint* waypointp);
/* Data Members */
OptionBool opt_allpoints;
OptionBool opt_recoverymode;
int lap_ct = 0;
bool new_trkseg = false;
bool write_header_msgs = false;
QVector<arglist_t> fit_args = {
{
"allpoints", &opt_allpoints,
"Read all points even if latitude or longitude is missing",
nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
},
{
"recoverymode", &opt_recoverymode,
"Attempt to recovery data from corrupt file",
nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
},
};
const std::vector<std::pair<QString, int> > kCoursePointTypeMapping = {
{"left", kCoursePointTypeLeft},
{"links", kCoursePointTypeLeft},
{"gauche", kCoursePointTypeLeft},
{"izquierda", kCoursePointTypeLeft},
{"sinistra", kCoursePointTypeLeft},
{"right", kCoursePointTypeRight},
{"rechts", kCoursePointTypeRight},
{"droit", kCoursePointTypeRight},
{"derecha", kCoursePointTypeRight},
{"destro", kCoursePointTypeRight},
};
fit_data_t fit_data;
std::deque<FitCourseRecordPoint> course, waypoints;
gbfile* fin{nullptr};
gbfile* fout{nullptr};
/*******************************************************************************
* FIT writing
*******************************************************************************/
const std::vector<fit_field_t> fit_msg_fields_file_id = {
// field id, size, type
{ kFieldType, 0x01, kTypeEnum },
{ kFieldManufacturer, 0x02, kTypeUint16 },
{ kFieldProduct, 0x02, kTypeUint16 },
{ kFieldTimeCreated, 0x04, kTypeUint32 },
};
const std::vector<fit_field_t> fit_msg_fields_course = {
{ kFieldName, 0x10, kTypeString },
{ kFieldSport, 0x01, kTypeEnum },
};
const std::vector<fit_field_t> fit_msg_fields_lap = {
{ kFieldTimestamp, 0x04, kTypeUint32 },
{ kFieldStartTime, 0x04, kTypeUint32 },
{ kFieldStartLatitude, 0x04, kTypeSint32 },
{ kFieldStartLongitude, 0x04, kTypeSint32 },
{ kFieldEndLatitude, 0x04, kTypeSint32 },
{ kFieldEndLongitude, 0x04, kTypeSint32 },
{ kFieldElapsedTime, 0x04, kTypeUint32 },
{ kFieldTotalTimerTime, 0x04, kTypeUint32 },
{ kFieldTotalDistance, 0x04, kTypeUint32 },
{ kFieldAvgSpeed, 0x02, kTypeUint16 },
{ kFieldMaxSpeed, 0x02, kTypeUint16 },
};
const std::vector<fit_field_t> fit_msg_fields_event = {
{ kFieldTimestamp, 0x04, kTypeUint32 },
{ kFieldEvent, 0x01, kTypeEnum },
{ kFieldEventType, 0x01, kTypeEnum },
{ kFieldEventGroup, 0x01, kTypeUint8 },
};
const std::vector<fit_field_t> fit_msg_fields_course_point = {
{ kFieldCPTimeStamp, 0x04, kTypeUint32 },
{ kFieldCPPositionLat, 0x04, kTypeSint32 },
{ kFieldCPPositionLong, 0x04, kTypeSint32 },
{ kFieldCPDistance, 0x04, kTypeUint32 },
{ kFieldCPName, 0x10, kTypeString },
{ kFieldCPType, 0x01, kTypeEnum },
};
const std::vector<fit_field_t> fit_msg_fields_record = {
{ kFieldTimestamp, 0x04, kTypeUint32 },
{ kFieldLatitude, 0x04, kTypeSint32 },
{ kFieldLongitude, 0x04, kTypeSint32 },
{ kFieldDistance, 0x04, kTypeUint32 },
{ kFieldAltitude, 0x02, kTypeUint16 },
{ kFieldSpeed, 0x02, kTypeUint16 },
};
};
#endif // GARMIN_FIT_H_INCLUDED_