Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: scheibo/fitparse
base: 4e675bcd65
...
head fork: scheibo/fitparse
compare: 1cad402056
Checking mergeability… Don't worry, you can still create the pull request.
  • 3 commits
  • 22 files changed
  • 0 commit comments
  • 1 contributor
View
4 .gitignore
@@ -1,6 +1,2 @@
*.o
-util
gpx
-fit
-tcx
-csv
View
24 activity.c
@@ -3,7 +3,7 @@
#include "activity.h"
-#define alloc_nr(x) (((x)+16)*3/2)
+#define alloc_nr(x) (((x) + 16) * 3 / 2)
/*
* Realloc the buffer pointed at by variable 'x' so that it can hold
@@ -12,16 +12,16 @@
*
* DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
*/
-#define ALLOC_GROW(x, nr, alloc) \
- do { \
- if ((nr) > alloc) { \
- if (alloc_nr(alloc) < (nr)) \
- alloc = (nr); \
- else \
- alloc = alloc_nr(alloc); \
- x = realloc((x), alloc * sizeof(*(x))); \
- } \
- } while (0)
+#define ALLOC_GROW(x, nr, alloc) \
+ do { \
+ if ((nr) > alloc) { \
+ if (alloc_nr(alloc) < (nr)) \
+ alloc = (nr); \
+ else \
+ alloc = alloc_nr(alloc); \
+ x = realloc((x), alloc * sizeof(*(x))); \
+ } \
+ } while (0)
Activity *activity_new(void) {
Activity *a;
@@ -67,7 +67,7 @@ int activity_add_point(Activity *a, DataPoint *dp) {
/* TODO fill in inferred missing values a la gpx/tcx */
for (i = 0; i < DataFieldCount; i++) {
- a->data_points[a->num_points].data[i] = dp->data[i];;
+ a->data_points[a->num_points].data[i] = dp->data[i];
}
a->num_points++;
View
19 activity.h
@@ -28,8 +28,13 @@ typedef enum {
DataFieldCount
} DataField;
-typedef struct { double data[DataFieldCount]; } DataPoint;
-static const char *DATA_FIELDS[DataFieldCount] = { "timestamp", "latitude", "longitude", "altitude", "distance", "speed", "power", "grade", "heart_rate", "cadence", "lr_balance", "temperature" };
+typedef struct {
+ double data[DataFieldCount];
+} DataPoint;
+static const char *DATA_FIELDS[DataFieldCount] = {
+ "timestamp", "latitude", "longitude", "altitude",
+ "distance", "speed", "power", "grade",
+ "heart_rate", "cadence", "lr_balance", "temperature"};
/* TODO convert to array...
//typedef struct Summary {
@@ -70,10 +75,12 @@ static inline void unset_data_point(DataPoint *dp) {
static inline void print_data_point(DataPoint *dp) {
double *d = dp->data;
fprintf(stderr,
- "time: %.0f, lat: %.15f, lon: %.15f, alt: %.2f, dist: %.2f, speed: %.2f, pow: %.0f, "
+ "time: %.0f, lat: %.15f, lon: %.15f, alt: %.2f, dist: %.2f, speed: "
+ "%.2f, pow: %.0f, "
"grd: %.2f, hr: %.0f, cad: %.0f, bal: %.0f, temp: %.0f\n",
- d[Timestamp], d[Latitude], d[Longitude], d[Altitude], d[Distance], d[Speed], d[Power],
- d[Grade], d[HeartRate], d[Cadence], d[LRBalance], d[Temperature]);
+ d[Timestamp], d[Latitude], d[Longitude], d[Altitude], d[Distance],
+ d[Speed], d[Power], d[Grade], d[HeartRate], d[Cadence], d[LRBalance],
+ d[Temperature]);
}
typedef enum {
@@ -83,7 +90,6 @@ typedef enum {
UnknownSport
} Sport;
-
typedef enum {
InvalidGPS,
Dropouts,
@@ -93,7 +99,6 @@ typedef enum {
DataErrorCount
} DataError;
-
/*****************
* TODO Read all individual points and compare it to summary data
*/
View
11 csv.c
@@ -10,7 +10,7 @@ static DataField name_to_field(char *name) {
!strcmp(name, "long")) {
return Longitude;
} else if (!strcmp(name, "altitude") || !strcmp(name, "elevation") ||
- !strcmp(name, "alt") || !strcmp(name, "ele") ) {
+ !strcmp(name, "alt") || !strcmp(name, "ele")) {
return Altitude;
} else if (!strcmp(name, "distance") || !strcmp(name, "dist")) {
return Distance;
@@ -32,7 +32,7 @@ static DataField name_to_field(char *name) {
} else if (!strcmp(name, "temperature") || !strcmp(name, "atemp") ||
!strcmp(name, "temp")) {
return Cadence;
- } else { /* not found */
+ } else {/* not found */
return DataFieldCount;
}
}
@@ -41,7 +41,8 @@ static DataField name_to_field(char *name) {
* all are doubles in base SI unit which we then convert into our format */
int csv_read(char *filename, Activity *a) {
FILE *f = NULL;
- DataField data_fields[DataFieldCount]; // TODO worry all set to zero which is Timestamp
+ DataField data_fields[DataFieldCount]; // TODO worry all set to zero which is
+ // Timestamp
DataPoint point;
char buf[CSV_BUFSIZ];
@@ -70,7 +71,8 @@ int csv_read(char *filename, Activity *a) {
return 0;
}
-static int write_csv(FILE *f, const char *format, size_t i, DataField f, Activity *a, CSVOptions o, bool *first) {
+static int write_csv(FILE *f, const char *format, size_t i, DataField f,
+ Activity *a, CSVOptions o, bool *first) {
double d = a->data_points[i].data[field];
if (!o.remove_unset || a->has_data[field]) {
if (d == UNSET_FIELD) {
@@ -85,7 +87,6 @@ static int write_csv(FILE *f, const char *format, size_t i, DataField f, Activit
}
}
-
int csv_write(char *filename, Activity *a, CSVOptions o) {
FILE *f = NULL;
unsigned i;
View
3  csv.h
@@ -3,7 +3,8 @@
#include "activity.h"
-#define DEFAULT_CSV_OPTIONS { false, "NA" }
+#define DEFAULT_CSV_OPTIONS \
+ { false, "NA" }
#define CSV_BUFSIZ 4096
typedef struct {
View
4 fitparse.c
@@ -22,10 +22,10 @@
*/
/* indexed by FileFormat */
-static ReadFn *readers[] = { csv_read, fit_read, gpx_read, tcx_read };
+static ReadFn *readers[] = {csv_read, fit_read, gpx_read, tcx_read};
/* indexed by FileFormat */
-static WriteFn *writers[] = { csv_write, fit_write, gpx_write, tcx_write };
+static WriteFn *writers[] = {csv_write, fit_write, gpx_write, tcx_write};
static FileFormat file_format_from_name(char *filename) {
char ext[4];
View
108 fix.c
@@ -7,71 +7,71 @@
/* used to handle gaps in recording by inserting interpolated/zero samples to
* ensure dataPoints are contiguous in time */
int fix_invalid_gps(Activity *a);
- DataPoint *fill_data, *last_good = NULL;
- int errors = 0, last_good_index = -1, index = 0, fill_index;
- double delta_latitude, delta_longitude;
+DataPoint *fill_data, *last_good = NULL;
+int errors = 0, last_good_index = -1, index = 0, fill_index;
+double delta_latitude, delta_longitude;
- // ignore null or files without GPS data
- if (!a || !a->data_points || !a->has_data[Latitude] ||
- !a->has_data[Longitude]) {
- return -1;
- }
+// ignore null or files without GPS data
+if (!a || !a->data_points || !a->has_data[Latitude] ||
+ !a->has_data[Longitude]) {
+ return -1;
+}
- for (data = a->data_points; data; data = data->next, index++) {
- /* is this one decent? */
- if (data->latitude >= -90 && data->latitude <= 90 &&
- data->longitude >= -180 && data->longitude <= 180) {
- if (last_good && (last_good->next != data)) {
- /* interpolate from last_good to here then set last_good to here */
- delta_latitude = (data->latitude - last_good->latitude) /
- (double)(index - last_good_index);
- delta_longitude = (data->longitude - last_good->longitude) /
- (double)(index - last_good_index);
- for (fill_data = last_good->next, fill_index = last_good_index + 1;
- fill_data != data; fill_data = fill_data->next, fill_index++) {
- fill_data->latitude =
- last_good->latitude +
- (double)((fill_index - last_good_index) * delta_latitude);
- fill_data->longitude =
- last_good->longitude +
- (double)((fill_index - last_good_index) * delta_longitude);
- errors++;
- }
- } else if (!last_good) {
- /* fill to front */
- for (fill_data = a->data_points; fill_data != data;
- fill_data = fill_data->next) {
- fill_data->latitude = data->latitude;
- fill_data->longitude = data->longitude;
- errors++;
- }
+for (data = a->data_points; data; data = data->next, index++) {
+ /* is this one decent? */
+ if (data->latitude >= -90 && data->latitude <= 90 &&
+ data->longitude >= -180 && data->longitude <= 180) {
+ if (last_good && (last_good->next != data)) {
+ /* interpolate from last_good to here then set last_good to here */
+ delta_latitude = (data->latitude - last_good->latitude) /
+ (double)(index - last_good_index);
+ delta_longitude = (data->longitude - last_good->longitude) /
+ (double)(index - last_good_index);
+ for (fill_data = last_good->next, fill_index = last_good_index + 1;
+ fill_data != data; fill_data = fill_data->next, fill_index++) {
+ fill_data->latitude =
+ last_good->latitude +
+ (double)((fill_index - last_good_index) * delta_latitude);
+ fill_data->longitude =
+ last_good->longitude +
+ (double)((fill_index - last_good_index) * delta_longitude);
+ errors++;
+ }
+ } else if (!last_good) {
+ /* fill to front */
+ for (fill_data = a->data_points; fill_data != data;
+ fill_data = fill_data->next) {
+ fill_data->latitude = data->latitude;
+ fill_data->longitude = data->longitude;
+ errors++;
}
- last_good = data;
- last_good_index = index;
}
+ last_good = data;
+ last_good_index = index;
}
+}
- /* fill to end... */
- if (last_good && last_good->next) {
- /* fill from last_good to end with last_good */
- for (fill_data = last_good->next; fill_data; fill_data = fill_data->next) {
- fill_data->latitude = last_good->latitude;
- fill_data->longitude = last_good->longitude;
- errors++;
- }
+/* fill to end... */
+if (last_good && last_good->next) {
+ /* fill from last_good to end with last_good */
+ for (fill_data = last_good->next; fill_data; fill_data = fill_data->next) {
+ fill_data->latitude = last_good->latitude;
+ fill_data->longitude = last_good->longitude;
+ errors++;
}
+}
- if (errors) {
- a->errors[InvalidGPS] = errors;
- return errors;
- } else {
- return 0;
- }
+if (errors) {
+ a->errors[InvalidGPS] = errors;
+ return errors;
+} else {
+ return 0;
+}
}
/*
int fix_dropouts(Activity *a);
int fix_power(Activity *a);
-int fix_heart_rate(Activity *a); // TODO probably needs HR max from athletes and other
+int fix_heart_rate(Activity *a); // TODO probably needs HR max from athletes and
+other
*/
-
View
3  fix.h
@@ -7,7 +7,8 @@ int fix_invalid_gps(Activity *a);
/*
int fix_dropouts(Activity *a);
int fix_power(Activity *a);
-int fix_heart_rate(Activity *a); // TODO probably needs HR max from athletes and other
+int fix_heart_rate(Activity *a); // TODO probably needs HR max from athletes and
+other
values?
*/
View
9 gpx.c
@@ -20,7 +20,7 @@ int allspace(const char *str) {
return !*str;
}
-static inline void parse_field(DataField field, State* state, const char *str) {
+static inline void parse_field(DataField field, State *state, const char *str) {
char *end;
state->dp.data[field] = strtod(str, &end);
if (*end) state->dp.data[field] = UNSET_FIELD;
@@ -38,7 +38,7 @@ static int sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *sax_data) {
if (state->first_element && strcmp(name, "gpx")) {
fprintf(stderr, "error\n"); /* TODO */
- return 1; /* stop reading the file */
+ return 1; /* stop reading the file */
}
if (!strcmp(name, "metadata")) {
@@ -70,7 +70,8 @@ static int sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *sax_data) {
parse_field(HeartRate, state, data);
} else if (!strcmp(name, "gpxdata:temp") || !strcmp(name, "gpxtpx:atemp")) {
parse_field(Temperature, state, data);
- } else if (!strcmp(name, "gpxdata:cadence") || !strcmp(name, "gpxtpx:cad")) {
+ } else if (!strcmp(name, "gpxdata:cadence") ||
+ !strcmp(name, "gpxtpx:cad")) {
parse_field(Cadence, state, data);
} else if (!strcmp(name, "gpxdata:bikepower")) {
parse_field(Power, state, data);
@@ -88,7 +89,7 @@ static int sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *sax_data) {
int gpx_read(char *filename, Activity *activity) {
FILE *f = NULL;
- State state = { false, true, true, {{0}} };
+ State state = {false, true, true, {{0}}};
unset_data_point(&(state.dp));
if (!(f = fopen(filename, "r"))) {
View
18 lib/fit/Makefile
@@ -1,11 +1,13 @@
-decode: decode.o fit_convert.o fit_crc.o fit.o fit_sdk.o
-encode: encode.o fit.o fit_sdk.o fit_crc.o
-decode.o: decode.c fit_convert.h fit_config.h fit_sdk.h fit.h
-encode.o: encode.c fit_config.h fit_sdk.h fit.h fit_crc.h
-fit.o: fit.c fit_config.h fit_sdk.h fit.h
-fit_convert.o: fit_convert.c fit_convert.h fit_config.h fit_sdk.h fit.h fit_crc.h
-fit_crc.o: fit_crc.c fit_crc.h fit.h fit_config.h
-fit_sdk.o: fit_sdk.c fit_config.h fit_sdk.h fit.h
+decode: decode.o fit_sdk.o
+encode: encode.o fit_sdk.o
+
+decode.o: fit.c fit_sdk.h
+ $(CC) -DDECODE -c fit.c -o $@
+
+encode.o: fit.c fit_sdk.h
+ $(CC) -DENCODE -c fit.c -o $@
+
+fit_sdk.o: fit_sdk.c fit_sdk.h
clean:
-rm -f *.o
View
206 lib/fit/decode.c
@@ -1,206 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// The following FIT Protocol software provided may be used with FIT protocol
-// devices only and remains the copyrighted property of Dynastream Innovations Inc.
-// The software is being provided on an "as-is" basis and as an accommodation,
-// and therefore all warranties, representations, or guarantees of any kind
-// (whether express, implied or statutory) including, without limitation,
-// warranties of merchantability, non-infringement, or fitness for a particular
-// purpose, are specifically disclaimed.
-//
-// Copyright 2008 Dynastream Innovations Inc.
-////////////////////////////////////////////////////////////////////////////////
-
-#define _CRT_SECURE_NO_WARNINGS
-
-#include "stdio.h"
-#include "string.h"
-
-#include "fit_convert.h"
-
-int main(int argc, char* argv[])
-{
- FILE *file;
- FIT_UINT8 buf[8];
- FIT_CONVERT_RETURN convert_return = FIT_CONVERT_CONTINUE;
- FIT_UINT32 buf_size;
- FIT_UINT32 mesg_index = 0;
- #if defined(FIT_CONVERT_MULTI_THREAD)
- FIT_CONVERT_STATE state;
- #endif
-
- printf("Testing file conversion using %s file...\n", argv[1]);
-
- #if defined(FIT_CONVERT_MULTI_THREAD)
- FitConvert_Init(&state, FIT_TRUE);
- #else
- FitConvert_Init(FIT_TRUE);
- #endif
-
- if((file = fopen(argv[1], "rb")) == NULL)
- {
- printf("Error opening file %s.\n", argv[1]);
- return FIT_FALSE;
- }
-
- while(!feof(file) && (convert_return == FIT_CONVERT_CONTINUE))
- {
- for(buf_size=0;(buf_size < sizeof(buf)) && !feof(file); buf_size++)
- {
- buf[buf_size] = getc(file);
- }
-
- do
- {
- #if defined(FIT_CONVERT_MULTI_THREAD)
- convert_return = FitConvert_Read(&state, buf, buf_size);
- #else
- convert_return = FitConvert_Read(buf, buf_size);
- #endif
-
- switch (convert_return)
- {
- case FIT_CONVERT_MESSAGE_AVAILABLE:
- {
- #if defined(FIT_CONVERT_MULTI_THREAD)
- const FIT_UINT8 *mesg = FitConvert_GetMessageData(&state);
- FIT_UINT16 mesg_num = FitConvert_GetMessageNumber(&state);
- #else
- const FIT_UINT8 *mesg = FitConvert_GetMessageData();
- FIT_UINT16 mesg_num = FitConvert_GetMessageNumber();
- #endif
-
- printf("Mesg %d (%d) - ", mesg_index++, mesg_num);
-
- switch(mesg_num)
- {
- case FIT_MESG_NUM_FILE_ID:
- {
- const FIT_FILE_ID_MESG *id = (FIT_FILE_ID_MESG *) mesg;
- printf("File ID: type=%u, number=%u\n", id->type, id->number);
- break;
- }
-
- case FIT_MESG_NUM_USER_PROFILE:
- {
- const FIT_USER_PROFILE_MESG *user_profile = (FIT_USER_PROFILE_MESG *) mesg;
- printf("User Profile: weight=%0.1fkg\n", user_profile->weight / 10.0f);
- break;
- }
-
- case FIT_MESG_NUM_ACTIVITY:
- {
- const FIT_ACTIVITY_MESG *activity = (FIT_ACTIVITY_MESG *) mesg;
- printf("Activity: timestamp=%u, type=%u, event=%u, event_type=%u, num_sessions=%u\n", activity->timestamp, activity->type, activity->event, activity->event_type, activity->num_sessions);
- {
- FIT_ACTIVITY_MESG old_mesg;
- old_mesg.num_sessions = 1;
- #if defined(FIT_CONVERT_MULTI_THREAD)
- FitConvert_RestoreFields(&state, &old_mesg);
- #else
- FitConvert_RestoreFields(&old_mesg);
- #endif
- printf("Restored num_sessions=1 - Activity: timestamp=%u, type=%u, event=%u, event_type=%u, num_sessions=%u\n", activity->timestamp, activity->type, activity->event, activity->event_type, activity->num_sessions);
- }
- break;
- }
-
- case FIT_MESG_NUM_SESSION:
- {
- const FIT_SESSION_MESG *session = (FIT_SESSION_MESG *) mesg;
- printf("Session: timestamp=%u\n", session->timestamp);
- break;
- }
-
- case FIT_MESG_NUM_LAP:
- {
- const FIT_LAP_MESG *lap = (FIT_LAP_MESG *) mesg;
- printf("Lap: timestamp=%u\n", lap->timestamp);
- break;
- }
-
- case FIT_MESG_NUM_RECORD:
- {
- const FIT_RECORD_MESG *record = (FIT_RECORD_MESG *) mesg;
-
- printf("Record: timestamp=%u", record->timestamp);
-
- if (
- (record->compressed_speed_distance[0] != FIT_BYTE_INVALID) ||
- (record->compressed_speed_distance[1] != FIT_BYTE_INVALID) ||
- (record->compressed_speed_distance[2] != FIT_BYTE_INVALID)
- )
- {
- static FIT_UINT32 accumulated_distance16 = 0;
- static FIT_UINT32 last_distance16 = 0;
- FIT_UINT16 speed100;
- FIT_UINT32 distance16;
-
- speed100 = record->compressed_speed_distance[0] | ((record->compressed_speed_distance[1] & 0x0F) << 8);
- printf(", speed = %0.2fm/s", speed100/100.0f);
-
- distance16 = (record->compressed_speed_distance[1] >> 4) | (record->compressed_speed_distance[2] << 4);
- accumulated_distance16 += (distance16 - last_distance16) & 0x0FFF;
- last_distance16 = distance16;
-
- printf(", distance = %0.3fm", accumulated_distance16/16.0f);
- }
-
- printf("\n");
- break;
- }
-
- case FIT_MESG_NUM_EVENT:
- {
- const FIT_EVENT_MESG *event = (FIT_EVENT_MESG *) mesg;
- printf("Event: timestamp=%u\n", event->timestamp);
- break;
- }
-
- case FIT_MESG_NUM_DEVICE_INFO:
- {
- const FIT_DEVICE_INFO_MESG *device_info = (FIT_DEVICE_INFO_MESG *) mesg;
- printf("Device Info: timestamp=%u\n", device_info->timestamp);
- break;
- }
-
- default:
- printf("Unknown\n");
- break;
- }
- break;
- }
-
- default:
- break;
- }
- } while (convert_return == FIT_CONVERT_MESSAGE_AVAILABLE);
- }
-
- if (convert_return == FIT_CONVERT_ERROR)
- {
- printf("Error decoding file.\n");
- fclose(file);
- return 1;
- }
-
- if (convert_return == FIT_CONVERT_CONTINUE)
- {
- printf("Unexpected end of file.\n");
- fclose(file);
- return 1;
- }
-
- if (convert_return == FIT_CONVERT_PROTOCOL_VERSION_NOT_SUPPORTED)
- {
- printf("Protocol version not supported.\n");
- fclose(file);
- return 1;
- }
-
- if (convert_return == FIT_CONVERT_END_OF_FILE)
- printf("File converted successfully.\n");
-
- fclose(file);
-
- return 0;
-}
View
131 lib/fit/encode.c
@@ -1,131 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// The following FIT Protocol software provided may be used with FIT protocol
-// devices only and remains the copyrighted property of Dynastream Innovations Inc.
-// The software is being provided on an "as-is" basis and as an accommodation,
-// and therefore all warranties, representations, or guarantees of any kind
-// (whether express, implied or statutory) including, without limitation,
-// warranties of merchantability, non-infringement, or fitness for a particular
-// purpose, are specifically disclaimed.
-//
-// Copyright 2008 Dynastream Innovations Inc.
-////////////////////////////////////////////////////////////////////////////////
-
-#define _CRT_SECURE_NO_WARNINGS
-
-#include "stdio.h"
-#include "string.h"
-
-#include "fit_config.h"
-#include "fit_crc.h"
-#include "fit_sdk.h"
-
-///////////////////////////////////////////////////////////////////////
-// Private Function Prototypes
-///////////////////////////////////////////////////////////////////////
-
-void WriteFileHeader(FILE *fp);
-///////////////////////////////////////////////////////////////////////
-// Creates a FIT file. Puts a place-holder for the file header on top of the file.
-///////////////////////////////////////////////////////////////////////
-
-void WriteMessageDefinition(FIT_UINT8 local_mesg_number, const void *mesg_def_pointer, FIT_UINT8 mesg_def_size, FILE *fp);
-///////////////////////////////////////////////////////////////////////
-// Appends a FIT message definition (including the definition header) to the end of a file.
-///////////////////////////////////////////////////////////////////////
-
-void WriteMessage(FIT_UINT8 local_mesg_number, const void *mesg_pointer, FIT_UINT8 mesg_size, FILE *fp);
-///////////////////////////////////////////////////////////////////////
-// Appends a FIT message (including the message header) to the end of a file.
-///////////////////////////////////////////////////////////////////////
-
-void WriteData(const void *data, FIT_UINT8 data_size, FILE *fp);
-///////////////////////////////////////////////////////////////////////
-// Writes data to the file and updates the data CRC.
-///////////////////////////////////////////////////////////////////////
-
-
-///////////////////////////////////////////////////////////////////////
-// Private Variables
-///////////////////////////////////////////////////////////////////////
-
-static FIT_UINT16 data_crc;
-
-
-int main(void)
-{
- FILE *fp;
-
- data_crc = 0;
- fp = fopen("test.fit", "w+b");
-
- WriteFileHeader(fp);
-
- // Write file id message.
- {
- FIT_FILE_ID_MESG file_id;
- Fit_InitMesg(fit_mesg_defs[FIT_MESG_FILE_ID], &file_id);
- file_id.type = FIT_FILE_SETTINGS;
- file_id.manufacturer = FIT_MANUFACTURER_GARMIN;
- WriteMessageDefinition(0, fit_mesg_defs[FIT_MESG_FILE_ID], FIT_FILE_ID_MESG_DEF_SIZE, fp);
- WriteMessage(0, &file_id, FIT_FILE_ID_MESG_SIZE, fp);
- }
-
- // Write user profile message.
- {
- FIT_USER_PROFILE_MESG user_profile;
- Fit_InitMesg(fit_mesg_defs[FIT_MESG_USER_PROFILE], &user_profile);
- user_profile.gender = FIT_GENDER_FEMALE;
- user_profile.age = 35;
- WriteMessageDefinition(0, fit_mesg_defs[FIT_MESG_USER_PROFILE], FIT_USER_PROFILE_MESG_DEF_SIZE, fp);
- WriteMessage(0, &user_profile, FIT_USER_PROFILE_MESG_SIZE, fp);
- }
-
- // Write CRC.
- fwrite(&data_crc, 1, sizeof(FIT_UINT16), fp);
-
- // Update file header with data size.
- WriteFileHeader(fp);
-
- fclose(fp);
-
- return 0;
-}
-
-void WriteFileHeader(FILE *fp)
-{
- FIT_FILE_HDR file_header;
-
- file_header.header_size = FIT_FILE_HDR_SIZE;
- file_header.profile_version = FIT_PROFILE_VERSION;
- file_header.protocol_version = FIT_PROTOCOL_VERSION;
- memcpy((FIT_UINT8 *)&file_header.data_type, ".FIT", 4);
- fseek (fp , 0 , SEEK_END);
- file_header.data_size = ftell(fp) - FIT_FILE_HDR_SIZE - sizeof(FIT_UINT16);
- file_header.crc = FitCRC_Calc16(&file_header, FIT_STRUCT_OFFSET(crc, FIT_FILE_HDR));
-
- fseek (fp , 0 , SEEK_SET);
- fwrite((void *)&file_header, 1, FIT_FILE_HDR_SIZE, fp);
-}
-
-void WriteMessageDefinition(FIT_UINT8 local_mesg_number, const void *mesg_def_pointer, FIT_UINT8 mesg_def_size, FILE *fp)
-{
- FIT_UINT8 header = local_mesg_number | FIT_HDR_TYPE_DEF_BIT;
- WriteData(&header, FIT_HDR_SIZE, fp);
- WriteData(mesg_def_pointer, mesg_def_size, fp);
-}
-
-void WriteMessage(FIT_UINT8 local_mesg_number, const void *mesg_pointer, FIT_UINT8 mesg_size, FILE *fp)
-{
- WriteData(&local_mesg_number, FIT_HDR_SIZE, fp);
- WriteData(mesg_pointer, mesg_size, fp);
-}
-
-void WriteData(const void *data, FIT_UINT8 data_size, FILE *fp)
-{
- FIT_UINT8 offset;
-
- fwrite(data, 1, data_size, fp);
-
- for (offset = 0; offset < data_size; offset++)
- data_crc = FitCRC_Get16(data_crc, *((FIT_UINT8 *)data + offset));
-}
View
959 lib/fit/fit.c
@@ -1,25 +1,11 @@
-////////////////////////////////////////////////////////////////////////////////
-// The following FIT Protocol software provided may be used with FIT protocol
-// devices only and remains the copyrighted property of Dynastream Innovations Inc.
-// The software is being provided on an "as-is" basis and as an accommodation,
-// and therefore all warranties, representations, or guarantees of any kind
-// (whether express, implied or statutory) including, without limitation,
-// warranties of merchantability, non-infringement, or fitness for a particular
-// purpose, are specifically disclaimed.
-//
-// Copyright 2014 Dynastream Innovations Inc.
-////////////////////////////////////////////////////////////////////////////////
-
+#include <stdio.h>
+#include <string.h>
-#include "string.h"
-#include "fit_config.h"
#include "fit_sdk.h"
-
-///////////////////////////////////////////////////////////////////////
-// Public Constants
-///////////////////////////////////////////////////////////////////////
-
+/**********
+ * FIT C
+ **********/
const FIT_UINT8 fit_base_type_sizes[FIT_BASE_TYPES] =
{
sizeof(FIT_ENUM),
@@ -71,17 +57,20 @@ const FIT_CONST_UINT8_PTR fit_base_type_invalids[FIT_BASE_TYPES] =
(FIT_CONST_UINT8_PTR)&fit_byte_invalid,
};
-
///////////////////////////////////////////////////////////////////////
-// Public Functions
+// Returns architecture type.
+// Includes runtime check for little or big endian.
+// See FIT_MESG_DEF->arch and FIT_ARCH_*.
///////////////////////////////////////////////////////////////////////
-
FIT_UINT8 Fit_GetArch(void)
{
const FIT_UINT16 arch = 0x0100;
return (*(FIT_UINT8 *)&arch);
}
+///////////////////////////////////////////////////////////////////////
+// Returns message definition corresponding to global message number.
+///////////////////////////////////////////////////////////////////////
const FIT_MESG_DEF *Fit_GetMesgDef(FIT_UINT16 global_mesg_num)
{
FIT_UINT8 index;
@@ -95,6 +84,9 @@ const FIT_MESG_DEF *Fit_GetMesgDef(FIT_UINT16 global_mesg_num)
return (FIT_MESG_DEF *) FIT_NULL;
}
+///////////////////////////////////////////////////////////////////////
+// Returns the size of message definition.
+///////////////////////////////////////////////////////////////////////
FIT_UINT16 Fit_GetMesgDefSize(const FIT_MESG_DEF *mesg_def)
{
if (mesg_def == FIT_NULL)
@@ -103,6 +95,9 @@ FIT_UINT16 Fit_GetMesgDefSize(const FIT_MESG_DEF *mesg_def)
return FIT_STRUCT_OFFSET(fields, FIT_MESG_DEF) + (FIT_UINT16)mesg_def->num_fields * FIT_FIELD_DEF_SIZE;
}
+///////////////////////////////////////////////////////////////////////
+// Returns the size of message corresponding to global message number.
+///////////////////////////////////////////////////////////////////////
FIT_UINT8 Fit_GetMesgSize(FIT_UINT16 global_mesg_num)
{
const FIT_MESG_DEF *mesg_def;
@@ -122,6 +117,10 @@ FIT_UINT8 Fit_GetMesgSize(FIT_UINT16 global_mesg_num)
return size;
}
+///////////////////////////////////////////////////////////////////////
+// Initializes message with invalids.
+// Returns 1 if successful, otherwise 0.
+///////////////////////////////////////////////////////////////////////
FIT_BOOL Fit_InitMesg(const FIT_MESG_DEF *mesg_def, void *mesg)
{
FIT_UINT8 *mesg_buf = (FIT_UINT8 *) mesg;
@@ -151,6 +150,9 @@ FIT_BOOL Fit_InitMesg(const FIT_MESG_DEF *mesg_def, void *mesg)
return FIT_TRUE;
}
+///////////////////////////////////////////////////////////////////////
+// Returns the byte offset of a field in a message.
+///////////////////////////////////////////////////////////////////////
FIT_UINT8 Fit_GetFieldOffset(const FIT_MESG_DEF *mesg_def, FIT_UINT8 field_def_num)
{
FIT_UINT8 offset = 0;
@@ -170,6 +172,10 @@ FIT_UINT8 Fit_GetFieldOffset(const FIT_MESG_DEF *mesg_def, FIT_UINT8 field_def_n
return FIT_UINT8_INVALID;
}
+///////////////////////////////////////////////////////////////////////
+// Returns the definition for a field in a message.
+// field_def_num is set to FIT_FIELD_NUM_INVALID if field is not found in message.
+///////////////////////////////////////////////////////////////////////
FIT_FIELD_DEF Fit_GetFieldDef(const FIT_MESG_DEF *mesg_def, FIT_UINT8 field_def_num)
{
FIT_FIELD_DEF field_def;
@@ -196,6 +202,11 @@ FIT_FIELD_DEF Fit_GetFieldDef(const FIT_MESG_DEF *mesg_def, FIT_UINT8 field_def_
return field_def;
}
+///////////////////////////////////////////////////////////////////////
+// Finds the byte offset of a message with the given message index in a FIT file
+// Requires a pointer to a function which can read a given number of bytes from a provided offset in a FIT file.
+// Returns the local message number if successful, or FIT_UINT8_INVALID if unsuccessful.
+///////////////////////////////////////////////////////////////////////
FIT_UINT8 Fit_LookupMessage(FIT_UINT16 global_mesg_num, FIT_UINT16 message_index, FIT_UINT32 *offset, FIT_READ_BYTES_FUNC read_bytes_func, FIT_BOOL read_header)
{
FIT_UINT16 global_mesg_nums[FIT_MAX_LOCAL_MESGS];
@@ -312,6 +323,9 @@ FIT_UINT8 Fit_LookupMessage(FIT_UINT16 global_mesg_num, FIT_UINT16 message_index
}
}
+///////////////////////////////////////////////////////////////////////
+// Returns the byte offset of a message within a file.
+///////////////////////////////////////////////////////////////////////
FIT_UINT32 Fit_GetFileMesgOffset(const FIT_FILE_DEF *file_def, FIT_UINT16 mesg_num, FIT_UINT16 mesg_index)
{
FIT_UINT32 offset;
@@ -344,3 +358,904 @@ FIT_UINT32 Fit_GetFileMesgOffset(const FIT_FILE_DEF *file_def, FIT_UINT16 mesg_n
return offset;
}
+
+/**********
+ * FIT CRC
+ **********/
+
+FIT_UINT16 FitCRC_Get16(FIT_UINT16 crc, FIT_UINT8 byte)
+{
+ static const FIT_UINT16 crc_table[16] =
+ {
+ 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
+ 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
+ };
+ FIT_UINT16 tmp;
+
+ // compute checksum of lower four bits of byte
+ tmp = crc_table[crc & 0xF];
+ crc = (crc >> 4) & 0x0FFF;
+ crc = crc ^ tmp ^ crc_table[byte & 0xF];
+
+ // now compute checksum of upper four bits of byte
+ tmp = crc_table[crc & 0xF];
+ crc = (crc >> 4) & 0x0FFF;
+ crc = crc ^ tmp ^ crc_table[(byte >> 4) & 0xF];
+
+ return crc;
+}
+
+FIT_UINT16 FitCRC_Update16(FIT_UINT16 crc, const volatile void *data, FIT_UINT8 size)
+{
+ FIT_BYTE *data_ptr = (FIT_BYTE *)data;
+
+ while (size)
+ {
+ crc = FitCRC_Get16(crc, *data_ptr);
+ data_ptr++;
+ size--;
+ }
+
+ return crc;
+}
+
+FIT_UINT16 FitCRC_Calc16(const volatile void *data, FIT_UINT8 size)
+{
+ return FitCRC_Update16(0, data, size);
+}
+
+/**********
+ * FIT CONVERT
+ **********/
+
+typedef enum
+{
+ FIT_CONVERT_CONTINUE = 0,
+ FIT_CONVERT_MESSAGE_AVAILABLE,
+ FIT_CONVERT_ERROR,
+ FIT_CONVERT_END_OF_FILE,
+ FIT_CONVERT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ FIT_CONVERT_DATA_TYPE_NOT_SUPPORTED
+} FIT_CONVERT_RETURN;
+
+typedef enum
+{
+ FIT_CONVERT_DECODE_FILE_HDR,
+ FIT_CONVERT_DECODE_RECORD,
+ FIT_CONVERT_DECODE_RESERVED1,
+ FIT_CONVERT_DECODE_ARCH,
+ FIT_CONVERT_DECODE_GTYPE_1,
+ FIT_CONVERT_DECODE_GTYPE_2,
+ FIT_CONVERT_DECODE_NUM_FIELD_DEFS,
+ FIT_CONVERT_DECODE_FIELD_DEF,
+ FIT_CONVERT_DECODE_FIELD_DEF_SIZE,
+ FIT_CONVERT_DECODE_FIELD_BASE_TYPE,
+ FIT_CONVERT_DECODE_FIELD_DATA
+} FIT_CONVERT_DECODE_STATE;
+
+typedef struct
+{
+ FIT_UINT32 file_bytes_left;
+ FIT_UINT32 data_offset;
+ #if defined(FIT_CONVERT_TIME_RECORD)
+ FIT_UINT32 timestamp;
+ #endif
+ union
+ {
+ FIT_FILE_HDR file_hdr;
+ FIT_UINT8 mesg[FIT_MESG_SIZE];
+ }u;
+ FIT_MESG_CONVERT convert_table[FIT_LOCAL_MESGS];
+ const FIT_MESG_DEF *mesg_def;
+ #if defined(FIT_CONVERT_CHECK_CRC)
+ FIT_UINT16 crc;
+ #endif
+ FIT_CONVERT_DECODE_STATE decode_state;
+ FIT_UINT8 mesg_index;
+ FIT_UINT8 mesg_sizes[FIT_MAX_LOCAL_MESGS];
+ FIT_UINT8 mesg_offset;
+ FIT_UINT8 num_fields;
+ FIT_UINT8 field_num;
+ FIT_UINT8 field_index;
+ FIT_UINT8 field_offset;
+ #if defined(FIT_CONVERT_TIME_RECORD)
+ FIT_UINT8 last_time_offset;
+ #endif
+} FIT_CONVERT_STATE;
+
+#if !defined(FIT_CONVERT_MULTI_THREAD)
+ static FIT_CONVERT_STATE state_struct;
+ #define state (&state_struct)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////
+// Initialize the state of the converter to start parsing the file.
+///////////////////////////////////////////////////////////////////////
+#if defined(FIT_CONVERT_MULTI_THREAD)
+ void FitConvert_Init(FIT_CONVERT_STATE *state, FIT_BOOL read_file_header)
+#else
+ void FitConvert_Init(FIT_BOOL read_file_header)
+#endif
+{
+ state->mesg_offset = 0;
+ state->data_offset = 0;
+
+#if defined(FIT_CONVERT_CHECK_CRC)
+ state->crc = 0;
+#endif
+#if defined(FIT_CONVERT_TIME_RECORD)
+ state->timestamp = 0;
+ state->last_time_offset = 0;
+#endif
+
+ if (read_file_header)
+ {
+ state->file_bytes_left = 3; // Header size byte + CRC.
+ state->decode_state = FIT_CONVERT_DECODE_FILE_HDR;
+ }
+ else
+ {
+ state->file_bytes_left = 0; // Don't read header or check CRC.
+ state->decode_state = FIT_CONVERT_DECODE_RECORD;
+ }
+}
+///////////////////////////////////////////////////////////////////////
+// Convert a stream of bytes.
+// Parameters:
+// state Pointer to converter state.
+// data Pointer to a buffer containing bytes from the file stream.
+// size Number of bytes in the data buffer.
+//
+// Returns FIT_CONVERT_CONTINUE when the all bytes in data have
+// been decoded successfully and ready to accept next bytes in the
+// file stream. No message is available yet.
+// Returns FIT_CONVERT_MESSAGE_AVAILABLE when a message is
+// complete. The message is valid until this function is called
+// again.
+// Returns FIT_CONVERT_ERROR if a decoding error occurs.
+// Returns FIT_CONVERT_END_OF_FILE when the file has been decoded successfully.
+///////////////////////////////////////////////////////////////////////
+#if defined(FIT_CONVERT_MULTI_THREAD)
+ FIT_CONVERT_RETURN FitConvert_Read(FIT_CONVERT_STATE *state, const void *data, FIT_UINT32 size)
+#else
+ FIT_CONVERT_RETURN FitConvert_Read(const void *data, FIT_UINT32 size)
+#endif
+{
+ while (state->data_offset < size)
+ {
+ FIT_UINT8 datum = *((FIT_UINT8 *) data + state->data_offset);
+ state->data_offset++;
+
+ //printf("fit_convert: 0x%02X - %d\n",datum, state->decode_state);
+
+ if (state->file_bytes_left > 0)
+ {
+ #if defined(FIT_CONVERT_CHECK_CRC)
+ state->crc = FitCRC_Get16(state->crc, datum);
+ #endif
+
+ state->file_bytes_left--;
+
+ if (state->file_bytes_left == 1) // CRC low byte.
+ {
+ if (state->decode_state != FIT_CONVERT_DECODE_RECORD)
+ return FIT_CONVERT_ERROR;
+
+ continue; // Next byte.
+ }
+ else if (state->file_bytes_left == 0) // CRC high byte.
+ {
+ #if defined(FIT_CONVERT_CHECK_CRC)
+ if (state->crc != 0)
+ return FIT_CONVERT_ERROR;
+ #endif
+
+ return FIT_CONVERT_END_OF_FILE;
+ }
+ }
+
+ switch (state->decode_state)
+ {
+ case FIT_CONVERT_DECODE_FILE_HDR:
+ if (state->mesg_offset < FIT_FILE_HDR_SIZE)
+ *((FIT_UINT8 *) &state->u.file_hdr + state->mesg_offset) = datum;
+
+ if (state->mesg_offset == 0)
+ state->file_bytes_left = state->u.file_hdr.header_size + 2; // Increase to read header and CRC.
+
+ state->mesg_offset++;
+
+ if (state->mesg_offset >= state->u.file_hdr.header_size)
+ {
+ state->file_bytes_left = *((FIT_UINT8 *) &state->u.file_hdr.data_size);
+ state->file_bytes_left |= (FIT_UINT32)*((FIT_UINT8 *) &state->u.file_hdr.data_size + 1) << 8;
+ state->file_bytes_left |= (FIT_UINT32)*((FIT_UINT8 *) &state->u.file_hdr.data_size + 2) << 16;
+ state->file_bytes_left |= (FIT_UINT32)*((FIT_UINT8 *) &state->u.file_hdr.data_size + 3) << 24;
+ state->file_bytes_left += 2; // CRC.
+
+ #if defined(FIT_CONVERT_CHECK_FILE_HDR_DATA_TYPE)
+ if (memcmp(state->u.file_hdr.data_type, ".FIT", 4) != 0)
+ return FIT_CONVERT_DATA_TYPE_NOT_SUPPORTED;
+ #endif
+
+ if ((state->u.file_hdr.protocol_version & FIT_PROTOCOL_VERSION_MAJOR_MASK) > (FIT_PROTOCOL_VERSION_MAJOR << FIT_PROTOCOL_VERSION_MAJOR_SHIFT))
+ return FIT_CONVERT_PROTOCOL_VERSION_NOT_SUPPORTED;
+
+ state->decode_state = FIT_CONVERT_DECODE_RECORD;
+ }
+ break;
+
+ case FIT_CONVERT_DECODE_RECORD:
+ if (datum & FIT_HDR_TIME_REC_BIT)
+ {
+ // This is a message data record with time.
+ state->mesg_index = (datum & FIT_HDR_TIME_TYPE_MASK) >> FIT_HDR_TIME_TYPE_SHIFT;
+
+ #if defined(FIT_CONVERT_TIME_RECORD)
+ {
+ FIT_UINT8 time_offset = datum & FIT_HDR_TIME_OFFSET_MASK;
+ state->timestamp += (time_offset - state->last_time_offset) & FIT_HDR_TIME_OFFSET_MASK;
+ state->last_time_offset = time_offset;
+ }
+ #endif
+
+ state->decode_state = FIT_CONVERT_DECODE_FIELD_DATA;
+ }
+ else
+ {
+ state->mesg_index = datum & FIT_HDR_TYPE_MASK;
+
+ if ((datum & FIT_HDR_TYPE_DEF_BIT) == 0)
+ {
+ // This is a message data record.
+ state->decode_state = FIT_CONVERT_DECODE_FIELD_DATA;
+ }
+ else
+ {
+ // This is a message definition record.
+ state->mesg_sizes[state->mesg_index] = 0;
+ state->decode_state = FIT_CONVERT_DECODE_RESERVED1;
+ }
+ }
+
+ if (state->decode_state == FIT_CONVERT_DECODE_FIELD_DATA)
+ {
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ {
+ state->mesg_def = Fit_GetMesgDef(state->convert_table[state->mesg_index].global_mesg_num);
+ Fit_InitMesg(state->mesg_def, state->u.mesg);
+
+ #if defined(FIT_CONVERT_TIME_RECORD)
+ if (datum & FIT_HDR_TIME_REC_BIT)
+ {
+ FIT_UINT8 field_offset = Fit_GetFieldOffset(state->mesg_def, FIT_FIELD_NUM_TIMESTAMP);
+
+ if (field_offset != FIT_UINT8_INVALID)
+ memcpy(&state->u.mesg[field_offset], &state->timestamp, sizeof(state->timestamp));
+ }
+ #endif
+ }
+
+ if (state->mesg_sizes[state->mesg_index] == 0)
+ state->decode_state = FIT_CONVERT_DECODE_RECORD;
+ }
+
+ state->mesg_offset = 0; // Reset the message byte count.
+ state->field_index = 0;
+ state->field_offset = 0;
+ break;
+
+ case FIT_CONVERT_DECODE_RESERVED1:
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ state->convert_table[state->mesg_index].reserved_1 = datum;
+
+ state->decode_state = FIT_CONVERT_DECODE_ARCH;
+ break;
+
+ case FIT_CONVERT_DECODE_ARCH:
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ state->convert_table[state->mesg_index].arch = datum;
+
+ state->decode_state = FIT_CONVERT_DECODE_GTYPE_1;
+ break;
+
+ case FIT_CONVERT_DECODE_GTYPE_1:
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ state->convert_table[state->mesg_index].global_mesg_num = datum;
+
+ state->decode_state = FIT_CONVERT_DECODE_GTYPE_2;
+ break;
+
+ case FIT_CONVERT_DECODE_GTYPE_2:
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ {
+ if ((state->convert_table[state->mesg_index].arch & FIT_ARCH_ENDIAN_MASK) == FIT_ARCH_ENDIAN_BIG)
+ {
+ state->convert_table[state->mesg_index].global_mesg_num <<= 8;
+ state->convert_table[state->mesg_index].global_mesg_num |= datum;
+ }
+ else
+ {
+ state->convert_table[state->mesg_index].global_mesg_num |= ((FIT_UINT16) datum << 8);
+ }
+
+ state->convert_table[state->mesg_index].num_fields = 0; // Initialize.
+ state->mesg_def = Fit_GetMesgDef(state->convert_table[state->mesg_index].global_mesg_num);
+ }
+
+ state->decode_state = FIT_CONVERT_DECODE_NUM_FIELD_DEFS;
+ break;
+
+ case FIT_CONVERT_DECODE_NUM_FIELD_DEFS:
+ state->num_fields = datum;
+
+ if (state->num_fields == 0)
+ {
+ state->decode_state = FIT_CONVERT_DECODE_RECORD;
+ break;
+ }
+
+ state->field_index = 0;
+ state->decode_state = FIT_CONVERT_DECODE_FIELD_DEF;
+ break;
+
+ case FIT_CONVERT_DECODE_FIELD_DEF:
+ state->field_num = FIT_FIELD_NUM_INVALID;
+
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ {
+ if (state->mesg_def != FIT_NULL)
+ {
+ FIT_UINT8 local_field_index;
+ FIT_UINT8 local_field_offset = 0;
+
+ // Search for the field definition in the local mesg definition.
+ for (local_field_index = 0; local_field_index < state->mesg_def->num_fields; local_field_index++)
+ {
+ FIT_UINT8 field_size = state->mesg_def->fields[FIT_MESG_DEF_FIELD_OFFSET(size, local_field_index)];
+
+ if (state->mesg_def->fields[FIT_MESG_DEF_FIELD_OFFSET(field_def_num, local_field_index)] == datum)
+ {
+ state->field_num = datum;
+ state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].offset_in = state->mesg_offset;
+ state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].offset_local = local_field_offset;
+ state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].size = field_size;
+ break;
+ }
+
+ local_field_offset += field_size;
+ }
+ }
+ }
+
+ state->decode_state = FIT_CONVERT_DECODE_FIELD_DEF_SIZE;
+ break;
+
+ case FIT_CONVERT_DECODE_FIELD_DEF_SIZE:
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ {
+ state->mesg_offset += datum;
+
+ if (state->field_num != FIT_FIELD_NUM_INVALID)
+ {
+ if (datum < state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].size)
+ state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].size = datum;
+ }
+ }
+
+ state->mesg_sizes[state->mesg_index] += datum;
+
+ state->decode_state = FIT_CONVERT_DECODE_FIELD_BASE_TYPE;
+ break;
+
+ case FIT_CONVERT_DECODE_FIELD_BASE_TYPE:
+ if (state->field_num != FIT_FIELD_NUM_INVALID)
+ {
+ state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].base_type = datum;
+ state->convert_table[state->mesg_index].num_fields++;
+ }
+
+ state->field_index++;
+
+ if (state->field_index >= state->num_fields)
+ state->decode_state = FIT_CONVERT_DECODE_RECORD;
+ else
+ state->decode_state = FIT_CONVERT_DECODE_FIELD_DEF;
+ break;
+
+ case FIT_CONVERT_DECODE_FIELD_DATA:
+ state->mesg_offset++;
+
+ if (state->mesg_offset >= state->mesg_sizes[state->mesg_index])
+ {
+ state->decode_state = FIT_CONVERT_DECODE_RECORD;
+ }
+
+ if (state->mesg_index < FIT_LOCAL_MESGS)
+ {
+ if ((state->mesg_def != FIT_NULL) && (state->field_index < state->convert_table[state->mesg_index].num_fields))
+ {
+ if (state->mesg_offset == (state->convert_table[state->mesg_index].fields[state->field_index].offset_in + state->field_offset + 1))
+ {
+ FIT_UINT8 *field = &state->u.mesg[state->convert_table[state->mesg_index].fields[state->field_index].offset_local];
+
+ field[state->field_offset] = datum; // Store the incoming byte in the local mesg buffer.
+ state->field_offset++;
+
+ if (state->field_offset >= state->convert_table[state->mesg_index].fields[state->field_index].size)
+ {
+ if (
+ (state->convert_table[state->mesg_index].fields[state->field_index].base_type & FIT_BASE_TYPE_ENDIAN_FLAG) &&
+ ((state->convert_table[state->mesg_index].arch & FIT_ARCH_ENDIAN_MASK) != (Fit_GetArch() & FIT_ARCH_ENDIAN_MASK))
+ )
+ {
+ FIT_UINT8 type_size;
+ FIT_UINT8 element_size;
+ FIT_UINT8 element;
+ FIT_UINT8 index;
+
+ index = state->convert_table[state->mesg_index].fields[state->field_index].base_type & FIT_BASE_TYPE_NUM_MASK;
+
+ if (index >= FIT_BASE_TYPES)
+ return FIT_CONVERT_ERROR;
+
+ type_size = fit_base_type_sizes[index];
+ element_size = state->convert_table[state->mesg_index].fields[state->field_index].size / type_size;
+
+ for (element = 0; element < element_size; element++)
+ {
+ for (index = 0; index < (type_size / 2); index++)
+ {
+ FIT_UINT8 tmp = field[element * type_size + index];
+ field[element * type_size + index] = field[element * type_size + type_size - 1 - index];
+ field[element * type_size + type_size - 1 - index] = tmp;
+ }
+ }
+ }
+
+ // Null terminate last character if multi-byte beyond end of field.
+ if (state->convert_table[state->mesg_index].fields[state->field_index].base_type == FIT_BASE_TYPE_STRING)
+ {
+ FIT_UINT8 length = state->convert_table[state->mesg_index].fields[state->field_index].size;
+ FIT_UINT8 index = 0;
+
+ while (index < length)
+ {
+ FIT_UINT8 char_size;
+ FIT_UINT8 size_mask = 0x80;
+
+ if (field[index] & size_mask)
+ {
+ char_size = 0;
+
+ while (field[index] & size_mask) // # of bytes in character = # of MSBits
+ {
+ char_size++;
+ size_mask >>= 1;
+ }
+ }
+ else
+ {
+ char_size = 1;
+ }
+
+ if ((FIT_UINT16)(index + char_size) > length)
+ {
+ while (index < length)
+ {
+ field[index++] = 0;
+ }
+ break;
+ }
+
+ index += char_size;
+ }
+ }
+
+ state->field_offset = 0; // Reset the offset.
+ state->field_index++; // Move on to the next field.
+
+ if (state->field_index >= state->convert_table[state->mesg_index].num_fields)
+ {
+ #if defined(FIT_CONVERT_TIME_RECORD)
+ {
+ FIT_UINT8 timestamp_offset = Fit_GetFieldOffset(state->mesg_def, FIT_FIELD_NUM_TIMESTAMP);
+
+ if (timestamp_offset != FIT_UINT8_INVALID)
+ {
+ if (*((FIT_UINT32 *)&state->u.mesg[timestamp_offset]) != FIT_DATE_TIME_INVALID)
+ {
+ memcpy(&state->timestamp, &state->u.mesg[timestamp_offset], sizeof(state->timestamp));
+ state->last_time_offset = (FIT_UINT8)(state->timestamp & FIT_HDR_TIME_OFFSET_MASK);
+ }
+ }
+ }
+ #endif
+
+ // We have successfully decoded a mesg.
+ return FIT_CONVERT_MESSAGE_AVAILABLE;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ // This shouldn't happen.
+ return FIT_CONVERT_ERROR;
+ }
+ }
+
+ state->data_offset = 0;
+ return FIT_CONVERT_CONTINUE;
+}
+
+///////////////////////////////////////////////////////////////////////
+// Returns the global message number of the decoded message.
+///////////////////////////////////////////////////////////////////////
+#if defined(FIT_CONVERT_MULTI_THREAD)
+ FIT_UINT16 FitConvert_GetMessageNumber(FIT_CONVERT_STATE *state)
+#else
+ FIT_UINT16 FitConvert_GetMessageNumber(void)
+#endif
+{
+ return state->convert_table[state->mesg_index].global_mesg_num;
+}
+
+///////////////////////////////////////////////////////////////////////
+// Returns a pointer to the data of the decoded message.
+// Copy or cast to FIT_*_MESG structure.
+///////////////////////////////////////////////////////////////////////
+#if defined(FIT_CONVERT_MULTI_THREAD)
+ const FIT_UINT8 *FitConvert_GetMessageData(FIT_CONVERT_STATE *state)
+#else
+ const FIT_UINT8 *FitConvert_GetMessageData(void)
+#endif
+{
+ return state->u.mesg;
+}
+
+///////////////////////////////////////////////////////////////////////
+// Restores fields that are not in decoded message from mesg_data.
+// Use when modifying an existing file.
+///////////////////////////////////////////////////////////////////////
+#if defined(FIT_CONVERT_MULTI_THREAD)
+ void FitConvert_RestoreFields(FIT_CONVERT_STATE *state, const void *mesg)
+#else
+ void FitConvert_RestoreFields(const void *mesg)
+#endif
+{
+ FIT_UINT8 offset = 0;
+ FIT_UINT8 field_index;
+ FIT_UINT8 convert_field;
+
+ if (state->mesg_def == FIT_NULL)
+ return;
+
+ for (field_index = 0; field_index < state->mesg_def->num_fields; field_index++)
+ {
+ for (convert_field=0; convert_field < state->convert_table[state->mesg_index].num_fields; convert_field++)
+ {
+ if (state->convert_table[state->mesg_index].fields[convert_field].offset_local == offset)
+ break;
+ }
+
+ if (convert_field == state->convert_table[state->mesg_index].num_fields)
+ memcpy(&state->u.mesg[offset], &((FIT_UINT8 *)mesg)[offset], state->mesg_def->fields[FIT_MESG_DEF_FIELD_OFFSET(size, field_index)]);
+
+ offset += state->mesg_def->fields[FIT_MESG_DEF_FIELD_OFFSET(size, field_index)];
+ }
+}
+
+
+/**********
+ * ENCODE
+ **********/
+
+///////////////////////////////////////////////////////////////////////
+// Private Variables
+///////////////////////////////////////////////////////////////////////
+
+static FIT_UINT16 data_crc;
+
+///////////////////////////////////////////////////////////////////////
+// Writes data to the file and updates the data CRC.
+///////////////////////////////////////////////////////////////////////
+void WriteData(const void *data, FIT_UINT8 data_size, FILE *fp)
+{
+ FIT_UINT8 offset;
+
+ fwrite(data, 1, data_size, fp);
+
+ for (offset = 0; offset < data_size; offset++)
+ data_crc = FitCRC_Get16(data_crc, *((FIT_UINT8 *)data + offset));
+}
+
+///////////////////////////////////////////////////////////////////////
+// Creates a FIT file. Puts a place-holder for the file header on top of the file.
+///////////////////////////////////////////////////////////////////////
+void WriteFileHeader(FILE *fp)
+{
+ FIT_FILE_HDR file_header;
+
+ file_header.header_size = FIT_FILE_HDR_SIZE;
+ file_header.profile_version = FIT_PROFILE_VERSION;
+ file_header.protocol_version = FIT_PROTOCOL_VERSION;
+ memcpy((FIT_UINT8 *)&file_header.data_type, ".FIT", 4);
+ fseek (fp , 0 , SEEK_END);
+ file_header.data_size = ftell(fp) - FIT_FILE_HDR_SIZE - sizeof(FIT_UINT16);
+ file_header.crc = FitCRC_Calc16(&file_header, FIT_STRUCT_OFFSET(crc, FIT_FILE_HDR));
+
+ fseek (fp , 0 , SEEK_SET);
+ fwrite((void *)&file_header, 1, FIT_FILE_HDR_SIZE, fp);
+}
+
+///////////////////////////////////////////////////////////////////////
+// Appends a FIT message definition (including the definition header) to the end of a file.
+///////////////////////////////////////////////////////////////////////
+void WriteMessageDefinition(FIT_UINT8 local_mesg_number, const void *mesg_def_pointer, FIT_UINT8 mesg_def_size, FILE *fp)
+{
+ FIT_UINT8 header = local_mesg_number | FIT_HDR_TYPE_DEF_BIT;
+ WriteData(&header, FIT_HDR_SIZE, fp);
+ WriteData(mesg_def_pointer, mesg_def_size, fp);
+}
+
+void WriteMessage(FIT_UINT8 local_mesg_number, const void *mesg_pointer, FIT_UINT8 mesg_size, FILE *fp);
+///////////////////////////////////////////////////////////////////////
+// Appends a FIT message (including the message header) to the end of a file.
+///////////////////////////////////////////////////////////////////////
+void WriteMessage(FIT_UINT8 local_mesg_number, const void *mesg_pointer, FIT_UINT8 mesg_size, FILE *fp)
+{
+ WriteData(&local_mesg_number, FIT_HDR_SIZE, fp);
+ WriteData(mesg_pointer, mesg_size, fp);
+}
+
+int encode(void)
+{
+ FILE *fp;
+
+ data_crc = 0;
+ fp = fopen("test.fit", "w+b");
+
+ WriteFileHeader(fp);
+
+ // Write file id message.
+ {
+ FIT_FILE_ID_MESG file_id;
+ Fit_InitMesg(fit_mesg_defs[FIT_MESG_FILE_ID], &file_id);
+ file_id.type = FIT_FILE_SETTINGS;
+ file_id.manufacturer = FIT_MANUFACTURER_GARMIN;
+ WriteMessageDefinition(0, fit_mesg_defs[FIT_MESG_FILE_ID], FIT_FILE_ID_MESG_DEF_SIZE, fp);
+ WriteMessage(0, &file_id, FIT_FILE_ID_MESG_SIZE, fp);
+ }
+
+ // Write user profile message.
+ {
+ FIT_USER_PROFILE_MESG user_profile;
+ Fit_InitMesg(fit_mesg_defs[FIT_MESG_USER_PROFILE], &user_profile);
+ user_profile.gender = FIT_GENDER_FEMALE;
+ user_profile.age = 35;
+ WriteMessageDefinition(0, fit_mesg_defs[FIT_MESG_USER_PROFILE], FIT_USER_PROFILE_MESG_DEF_SIZE, fp);
+ WriteMessage(0, &user_profile, FIT_USER_PROFILE_MESG_SIZE, fp);
+ }
+
+ // Write CRC.
+ fwrite(&data_crc, 1, sizeof(FIT_UINT16), fp);
+
+ // Update file header with data size.
+ WriteFileHeader(fp);
+
+ fclose(fp);
+
+ return 0;
+}
+
+/**********
+ * DECODE
+ **********/
+
+int decode(int argc, char* argv[])
+{
+ FILE *file;
+ FIT_UINT8 buf[8];
+ FIT_CONVERT_RETURN convert_return = FIT_CONVERT_CONTINUE;
+ FIT_UINT32 buf_size;
+ FIT_UINT32 mesg_index = 0;
+ #if defined(FIT_CONVERT_MULTI_THREAD)
+ FIT_CONVERT_STATE state;
+ #endif
+
+ printf("Testing file conversion using %s file...\n", argv[1]);
+
+ #if defined(FIT_CONVERT_MULTI_THREAD)
+ FitConvert_Init(&state, FIT_TRUE);
+ #else
+ FitConvert_Init(FIT_TRUE);
+ #endif
+
+ if((file = fopen(argv[1], "rb")) == NULL)
+ {
+ printf("Error opening file %s.\n", argv[1]);
+ return FIT_FALSE;
+ }
+
+ while(!feof(file) && (convert_return == FIT_CONVERT_CONTINUE))
+ {
+ for(buf_size=0;(buf_size < sizeof(buf)) && !feof(file); buf_size++)
+ {
+ buf[buf_size] = getc(file);
+ }
+
+ do
+ {
+ #if defined(FIT_CONVERT_MULTI_THREAD)
+ convert_return = FitConvert_Read(&state, buf, buf_size);
+ #else
+ convert_return = FitConvert_Read(buf, buf_size);
+ #endif
+
+ switch (convert_return)
+ {
+ case FIT_CONVERT_MESSAGE_AVAILABLE:
+ {
+ #if defined(FIT_CONVERT_MULTI_THREAD)
+ const FIT_UINT8 *mesg = FitConvert_GetMessageData(&state);
+ FIT_UINT16 mesg_num = FitConvert_GetMessageNumber(&state);
+ #else
+ const FIT_UINT8 *mesg = FitConvert_GetMessageData();
+ FIT_UINT16 mesg_num = FitConvert_GetMessageNumber();
+ #endif
+
+ printf("Mesg %d (%d) - ", mesg_index++, mesg_num);
+
+ switch(mesg_num)
+ {
+ case FIT_MESG_NUM_FILE_ID:
+ {
+ const FIT_FILE_ID_MESG *id = (FIT_FILE_ID_MESG *) mesg;
+ printf("File ID: type=%u, number=%u\n", id->type, id->number);
+ break;
+ }
+
+ case FIT_MESG_NUM_USER_PROFILE:
+ {
+ const FIT_USER_PROFILE_MESG *user_profile = (FIT_USER_PROFILE_MESG *) mesg;
+ printf("User Profile: weight=%0.1fkg\n", user_profile->weight / 10.0f);
+ break;
+ }
+
+ case FIT_MESG_NUM_ACTIVITY:
+ {
+ const FIT_ACTIVITY_MESG *activity = (FIT_ACTIVITY_MESG *) mesg;
+ printf("Activity: timestamp=%u, type=%u, event=%u, event_type=%u, num_sessions=%u\n", activity->timestamp, activity->type, activity->event, activity->event_type, activity->num_sessions);
+ {
+ FIT_ACTIVITY_MESG old_mesg;
+ old_mesg.num_sessions = 1;
+ #if defined(FIT_CONVERT_MULTI_THREAD)
+ FitConvert_RestoreFields(&state, &old_mesg);
+ #else
+ FitConvert_RestoreFields(&old_mesg);
+ #endif
+ printf("Restored num_sessions=1 - Activity: timestamp=%u, type=%u, event=%u, event_type=%u, num_sessions=%u\n", activity->timestamp, activity->type, activity->event, activity->event_type, activity->num_sessions);
+ }
+ break;
+ }
+
+ case FIT_MESG_NUM_SESSION:
+ {
+ const FIT_SESSION_MESG *session = (FIT_SESSION_MESG *) mesg;
+ printf("Session: timestamp=%u\n", session->timestamp);
+ break;
+ }
+
+ case FIT_MESG_NUM_LAP:
+ {
+ const FIT_LAP_MESG *lap = (FIT_LAP_MESG *) mesg;
+ printf("Lap: timestamp=%u\n", lap->timestamp);
+ break;
+ }
+
+ case FIT_MESG_NUM_RECORD:
+ {
+ const FIT_RECORD_MESG *record = (FIT_RECORD_MESG *) mesg;
+
+ printf("Record: timestamp=%u", record->timestamp);
+
+ if (
+ (record->compressed_speed_distance[0] != FIT_BYTE_INVALID) ||
+ (record->compressed_speed_distance[1] != FIT_BYTE_INVALID) ||
+ (record->compressed_speed_distance[2] != FIT_BYTE_INVALID)
+ )
+ {
+ static FIT_UINT32 accumulated_distance16 = 0;
+ static FIT_UINT32 last_distance16 = 0;
+ FIT_UINT16 speed100;
+ FIT_UINT32 distance16;
+
+ speed100 = record->compressed_speed_distance[0] | ((record->compressed_speed_distance[1] & 0x0F) << 8);
+ printf(", speed = %0.2fm/s", speed100/100.0f);
+
+ distance16 = (record->compressed_speed_distance[1] >> 4) | (record->compressed_speed_distance[2] << 4);
+ accumulated_distance16 += (distance16 - last_distance16) & 0x0FFF;
+ last_distance16 = distance16;
+
+ printf(", distance = %0.3fm", accumulated_distance16/16.0f);
+ }
+
+ printf("\n");
+ break;
+ }
+
+ case FIT_MESG_NUM_EVENT:
+ {
+ const FIT_EVENT_MESG *event = (FIT_EVENT_MESG *) mesg;
+ printf("Event: timestamp=%u\n", event->timestamp);
+ break;
+ }
+
+ case FIT_MESG_NUM_DEVICE_INFO:
+ {
+ const FIT_DEVICE_INFO_MESG *device_info = (FIT_DEVICE_INFO_MESG *) mesg;
+ printf("Device Info: timestamp=%u\n", device_info->timestamp);
+ break;
+ }
+
+ default:
+ printf("Unknown\n");
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ } while (convert_return == FIT_CONVERT_MESSAGE_AVAILABLE);
+ }
+
+ if (convert_return == FIT_CONVERT_ERROR)
+ {
+ printf("Error decoding file.\n");
+ fclose(file);
+ return 1;
+ }
+
+ if (convert_return == FIT_CONVERT_CONTINUE)
+ {
+ printf("Unexpected end of file.\n");
+ fclose(file);
+ return 1;
+ }
+
+ if (convert_return == FIT_CONVERT_PROTOCOL_VERSION_NOT_SUPPORTED)
+ {
+ printf("Protocol version not supported.\n");
+ fclose(file);
+ return 1;
+ }
+
+ if (convert_return == FIT_CONVERT_END_OF_FILE)
+ printf("File converted successfully.\n");
+
+ fclose(file);
+
+ return 0;
+}
+
+
+int main(int argc, char* argv[]) {
+#ifdef ENCODE
+ return encode();
+#else
+ return decode(argc, argv);
+#endif
+}
+
+
+
+
+
+
+
View
330 lib/fit/fit.h
@@ -1,330 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// The following FIT Protocol software provided may be used with FIT protocol
-// devices only and remains the copyrighted property of Dynastream Innovations Inc.
-// The software is being provided on an "as-is" basis and as an accommodation,
-// and therefore all warranties, representations, or guarantees of any kind
-// (whether express, implied or statutory) including, without limitation,
-// warranties of merchantability, non-infringement, or fitness for a particular
-// purpose, are specifically disclaimed.
-//
-// Copyright 2014 Dynastream Innovations Inc.
-////////////////////////////////////////////////////////////////////////////////
-
-
-#if !defined(FIT_H)
-#define FIT_H
-
-#include "fit_config.h"
-
-#if defined(FIT_USE_STDINT_H)
- #include <stdint.h>
-#else
- typedef unsigned char uint8_t;
- typedef signed char int8_t;
- typedef unsigned short uint16_t;
- typedef signed short int16_t;
- typedef unsigned long uint32_t;
- typedef signed long int32_t;
-#endif
-
-#if defined(__cplusplus)
- extern "C" {
-#endif
-
-
-///////////////////////////////////////////////////////////////////////
-// Version
-///////////////////////////////////////////////////////////////////////
-
-#define FIT_PROTOCOL_VERSION_MAJOR 1 // Non-backwards compatible changes. Decode compatible with this version and earlier.
-#define FIT_PROTOCOL_VERSION_MINOR 0 // Backwards compatible changes.
-#define FIT_PROTOCOL_VERSION_MAJOR_SHIFT 4
-#define FIT_PROTOCOL_VERSION_MAJOR_MASK ((FIT_UINT8) (0x0F << FIT_PROTOCOL_VERSION_MAJOR_SHIFT))
-#define FIT_PROTOCOL_VERSION_MINOR_MASK ((FIT_UINT8) 0x0F)
-#define FIT_PROTOCOL_VERSION ((FIT_UINT8) (FIT_PROTOCOL_VERSION_MAJOR << FIT_PROTOCOL_VERSION_MAJOR_SHIFT) | FIT_PROTOCOL_VERSION_MINOR)
-
-#define FIT_PROFILE_VERSION_MAJOR 10
-#define FIT_PROFILE_VERSION_MINOR 2
-#define FIT_PROFILE_VERSION_SCALE 100
-#define FIT_PROFILE_VERSION ((FIT_UINT16) (FIT_PROFILE_VERSION_MAJOR * 100 + FIT_PROFILE_VERSION_MINOR))
-
-
-///////////////////////////////////////////////////////////////////////
-// Type Definitions
-///////////////////////////////////////////////////////////////////////
-
-#define FIT_ANTFS_FILE_DATA_TYPE 128
-
-#define FIT_BASE_TYPE_ENDIAN_FLAG ((FIT_UINT8)0x80)
-#define FIT_BASE_TYPE_RESERVED ((FIT_UINT8)0x60)
-#define FIT_BASE_TYPE_NUM_MASK ((FIT_UINT8)0x1F)
-
-typedef uint8_t FIT_ENUM;
-typedef const FIT_ENUM * FIT_CONST_ENUM_PTR;
-#define FIT_ENUM_INVALID ((FIT_ENUM)0xFF)
-#define FIT_BASE_TYPE_ENUM ((FIT_UINT8)0x00)
-
-typedef int8_t FIT_SINT8;
-typedef const FIT_SINT8 * FIT_CONST_SINT8_PTR;
-#define FIT_SINT8_INVALID ((FIT_SINT8)0x7F)
-#define FIT_BASE_TYPE_SINT8 ((FIT_UINT8)0x01)
-
-typedef uint8_t FIT_UINT8;
-typedef const FIT_UINT8 * FIT_CONST_UINT8_PTR;
-#define FIT_UINT8_INVALID ((FIT_UINT8)0xFF)
-#define FIT_BASE_TYPE_UINT8 ((FIT_UINT8)0x02)
-
-typedef int16_t FIT_SINT16;
-typedef const FIT_SINT16 * FIT_CONST_SINT16_PTR;
-#define FIT_SINT16_INVALID ((FIT_SINT16)0x7FFF)
-#define FIT_BASE_TYPE_SINT16 ((FIT_UINT8)0x83)
-
-typedef uint16_t FIT_UINT16;
-typedef const FIT_UINT16 * FIT_CONST_UINT16_PTR;
-#define FIT_UINT16_INVALID ((FIT_UINT16)0xFFFF)
-#define FIT_BASE_TYPE_UINT16 ((FIT_UINT8)0x84)
-
-typedef int32_t FIT_SINT32;
-typedef const FIT_SINT32 * FIT_CONST_SINT32_PTR;
-#define FIT_SINT32_INVALID ((FIT_SINT32)0x7FFFFFFF)
-#define FIT_BASE_TYPE_SINT32 ((FIT_UINT8)0x85)
-
-typedef uint32_t FIT_UINT32;
-typedef const FIT_UINT32 * FIT_CONST_UINT32_PTR;
-#define FIT_UINT32_INVALID ((FIT_UINT32)0xFFFFFFFF)
-#define FIT_BASE_TYPE_UINT32 ((FIT_UINT8)0x86)
-
-typedef char FIT_STRING; // UTF-8 null terminated string
-typedef const FIT_STRING * FIT_CONST_STRING_PTR;
-#define FIT_STRING_INVALID ((FIT_STRING)0x00)
-#define FIT_BASE_TYPE_STRING ((FIT_UINT8)0x07)
-
-typedef float FIT_FLOAT32;
-typedef const FIT_FLOAT32 * FIT_CONST_FLOAT32_PTR;
-#define FIT_FLOAT32_INVALID ((FIT_FLOAT32)0xFFFFFFFF)
-#define FIT_BASE_TYPE_FLOAT32 ((FIT_UINT8)0x88)
-
-typedef double FIT_FLOAT64;
-typedef const FIT_FLOAT64 * FIT_CONST_FLOAT64_PTR;
-#if defined (_BORLANDC_)
- #define FIT_FLOAT64_INVALID ((FIT_FLOAT64)0xFFFFFFFFFFFFFFFFui64)
-#else
- #define FIT_FLOAT64_INVALID ((FIT_FLOAT64)0xFFFFFFFFFFFFFFFFull)
-#endif
-#define FIT_BASE_TYPE_FLOAT64 ((FIT_UINT8)0x89)
-
-typedef uint8_t FIT_UINT8Z;
-typedef const FIT_UINT8Z * FIT_CONST_UINT8Z_PTR;
-#define FIT_UINT8Z_INVALID ((FIT_UINT8Z)0x00)
-#define FIT_BASE_TYPE_UINT8Z ((FIT_UINT8)0x0A)
-
-typedef uint16_t FIT_UINT16Z;
-typedef const FIT_UINT16Z * FIT_CONST_UINT16Z_PTR;
-#define FIT_UINT16Z_INVALID ((FIT_UINT16Z)0x0000)
-#define FIT_BASE_TYPE_UINT16Z ((FIT_UINT8)0x8B)
-
-typedef uint32_t FIT_UINT32Z;
-typedef const FIT_UINT32Z * FIT_CONST_UINT32Z_PTR;
-#define FIT_UINT32Z_INVALID ((FIT_UINT32Z)0x00000000)
-#define FIT_BASE_TYPE_UINT32Z ((FIT_UINT8)0x8C)
-
-typedef uint8_t FIT_BYTE;
-typedef const FIT_BYTE * FIT_CONST_BYTE_PTR;
-#define FIT_BYTE_INVALID ((FIT_BYTE)0xFF) // Field is invalid if all bytes are invalid.
-#define FIT_BASE_TYPE_BYTE ((FIT_UINT8)0x0D)
-
-#define FIT_BASE_TYPES 14
-
-typedef FIT_ENUM FIT_BOOL;
-#define FIT_BOOL_INVALID FIT_ENUM_INVALID
-#define FIT_BOOL_FALSE ((FIT_BOOL)0)
-#define FIT_BOOL_TRUE ((FIT_BOOL)1)
-#define FIT_FALSE FIT_BOOL_FALSE
-#define FIT_TRUE FIT_BOOL_TRUE
-#define FIT_NULL 0
-
-typedef FIT_UINT32 (*FIT_READ_BYTES_FUNC)(void *, FIT_UINT32, FIT_UINT32);
-#define FIT_MESG_DEF_HEADER_SIZE FIT_STRUCT_OFFSET(fields, FIT_MESG_DEF)
-
-
-///////////////////////////////////////////////////////////////////////
-// File Header
-///////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- FIT_UINT8 header_size; // FIT_FILE_HDR_SIZE (size of this structure)
- FIT_UINT8 protocol_version; // FIT_PROTOCOL_VERSION
- FIT_UINT16 profile_version; // FIT_PROFILE_VERSION
- FIT_UINT32 data_size; // Does not include file header or crc. Little endian format.
- FIT_UINT8 data_type[4]; // ".FIT"
- FIT_UINT16 crc; // CRC of this file header in little endian format.
-} FIT_FILE_HDR;
-
-#define FIT_FILE_HDR_SIZE 14
-
-
-///////////////////////////////////////////////////////////////////////
-// Record Definitions
-///////////////////////////////////////////////////////////////////////
-
-#define FIT_HDR_SIZE 1
-#define FIT_HDR_TIME_REC_BIT ((FIT_UINT8) 0x80)
-#define FIT_HDR_TIME_TYPE_MASK ((FIT_UINT8) 0x60)
-#define FIT_HDR_TIME_TYPE_SHIFT 5
-#define FIT_HDR_TIME_OFFSET_MASK ((FIT_UINT8) 0x1F)
-#define FIT_HDR_TYPE_DEF_BIT ((FIT_UINT8) 0x40)
-#define FIT_HDR_TYPE_MASK ((FIT_UINT8) 0x0F)
-#define FIT_MAX_LOCAL_MESGS (FIT_HDR_TYPE_MASK + 1)
-
-///////////////////////////////////////////////////////////////////////
-// File Definitions
-///////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- FIT_UINT32 def_file_offset;
- FIT_UINT32 data_file_offset;
- FIT_UINT16 num;
- FIT_UINT16 count;
- FIT_UINT16 mesg;
-} FIT_FILE_MESG;
-
-typedef struct
-{
- FIT_UINT32 data_size;
- const FIT_FILE_MESG *mesgs;
- FIT_UINT16 mesg_count;
- FIT_UINT8 type;
- FIT_UINT8 count;
-} FIT_FILE_DEF;
-
-
-///////////////////////////////////////////////////////////////////////
-// Message Definitions
-///////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- FIT_UINT8 field_def_num;
- FIT_UINT8 size;
- FIT_UINT8 base_type;
-} FIT_FIELD_DEF;
-
-#define FIT_FIELD_DEF_SIZE 3
-
-typedef struct
-{
- FIT_UINT8 reserved_1;
- FIT_UINT8 arch;
- FIT_UINT16 global_mesg_num;
- FIT_UINT8 num_fields;
- FIT_UINT8 fields[1];
-} FIT_MESG_DEF;
-
-#define FIT_MAX_MESG_SIZE ((FIT_UINT8)255)
-
-#define FIT_ARCH_ENDIAN_MASK ((FIT_UINT8)0x01)
-#define FIT_ARCH_ENDIAN_LITTLE 0
-#define FIT_ARCH_ENDIAN_BIG 1
-
-
-///////////////////////////////////////////////////////////////////////
-// Field Definitions
-///////////////////////////////////////////////////////////////////////
-
-#define FIT_MAX_FIELD_SIZE ((FIT_UINT8)255)
-#define FIT_FIELD_NUM_INVALID ((FIT_UINT8)0xFF)
-#define FIT_FIELD_NUM_MESSAGE_INDEX ((FIT_UINT8)254)
-#define FIT_FIELD_NUM_TIMESTAMP ((FIT_UINT8)253)
-
-#define FIT_MESSAGE_INDEX_FIELD_NUM FIT_FIELD_NUM_MESSAGE_INDEX // For reverse compatibility only. Use FIT_FIELD_NUM_MESSAGE_INDEX instead.
-#define FIT_TIMESTAMP_FIELD_NUM FIT_FIELD_NUM_TIMESTAMP // For reverse compatibility only. Use FIT_FIELD_NUM_TIMESTAMP instead.
-
-
-///////////////////////////////////////////////////////////////////////
-// Macros
-///////////////////////////////////////////////////////////////////////
-
-#define FIT_STRUCT_OFFSET(MEMBER, STRUCT_TYPE) ( ((FIT_UINT8 *) &(((STRUCT_TYPE *) FIT_NULL)->MEMBER)) - ((FIT_UINT8 *) (FIT_NULL)) ) // Computes the byte offset of a member in a file. Compiles to a constant.
-
-#define FIT_MESG_DEF_FIELD_OFFSET(FIELD_MEMBER, FIELD_INDEX) (FIT_STRUCT_OFFSET(FIELD_MEMBER, FIT_FIELD_DEF) + FIT_FIELD_DEF_SIZE * FIELD_INDEX) // Computes the byte offset of a field definition member. Compiles to a constant.
-
-// Offset of message in file including file header.
-#define FIT_MESG_OFFSET(MESG_MEMBER, MESG_INDEX, MESG_SIZE, FILE) (FIT_STRUCT_OFFSET(MESG_MEMBER, FILE) + MESG_INDEX * (FIT_HDR_SIZE + MESG_SIZE) + FIT_HDR_SIZE) // Computes the byte offset of a message in a file structure. Compiles to a constant.
-#define FIT_MESG_DEF_OFFSET(MESG_DEF_MEMBER, FILE) (FIT_STRUCT_OFFSET(MESG_DEF_MEMBER, FILE) + FIT_HDR_SIZE) // Computes the byte offset of a message definition in a file structure. Compiles to a constant.
-
-// Below macros are obsolete because C file structures now include file header. For reverse compatibility only.
-#define FIT_FILE_MESG_OFFSET(MESG_MEMBER, MESG_INDEX, MESG_SIZE, FILE) FIT_MESG_OFFSET(MESG_MEMBER, MESG_INDEX, MESG_SIZE, FILE)
-#define FIT_FILE_MESG_DEF_OFFSET(MESG_DEF_MEMBER, FILE) FIT_MESG_DEF_OFFSET(MESG_DEF_MEMBER, FILE)
-#define FIT_FILE_MESG_DEF_FIELD_OFFSET(FIELD_MEMBER, FIELD_INDEX) FIT_MESG_DEF_FIELD_OFFSET(FIELD_MEMBER, FIELD_INDEX)
-
-
-///////////////////////////////////////////////////////////////////////
-// Public Constants
-///////////////////////////////////////////////////////////////////////
-
-const extern FIT_UINT8 fit_base_type_sizes[FIT_BASE_TYPES];
-const extern FIT_CONST_UINT8_PTR fit_base_type_invalids[FIT_BASE_TYPES];
-
-
-///////////////////////////////////////////////////////////////////////
-// Public Functions
-///////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////
-// Returns architecture type.
-// Includes runtime check for little or big endian.
-// See FIT_MESG_DEF->arch and FIT_ARCH_*.
-///////////////////////////////////////////////////////////////////////
-FIT_UINT8 Fit_GetArch(void);
-
-///////////////////////////////////////////////////////////////////////
-// Returns message definition corresponding to global message number.
-///////////////////////////////////////////////////////////////////////
-const FIT_MESG_DEF *Fit_GetMesgDef(FIT_UINT16 global_mesg_num);
-
-///////////////////////////////////////////////////////////////////////
-// Returns the size of message definition.
-///////////////////////////////////////////////////////////////////////
-FIT_UINT16 Fit_GetMesgDefSize(const FIT_MESG_DEF *mesg_def);
-
-///////////////////////////////////////////////////////////////////////
-// Returns the size of message corresponding to global message number.
-///////////////////////////////////////////////////////////////////////
-FIT_UINT8 Fit_GetMesgSize(FIT_UINT16 global_mesg_num);
-
-///////////////////////////////////////////////////////////////////////
-// Initializes message with invalids.
-// Returns 1 if successful, otherwise 0.
-///////////////////////////////////////////////////////////////////////
-FIT_BOOL Fit_InitMesg(const FIT_MESG_DEF *mesg_def, void *mesg);
-
-///////////////////////////////////////////////////////////////////////
-// Returns the byte offset of a field in a message.
-///////////////////////////////////////////////////////////////////////
-FIT_UINT8 Fit_GetFieldOffset(const FIT_MESG_DEF *mesg_def, FIT_UINT8 field_def_num);
-
-///////////////////////////////////////////////////////////////////////
-// Returns the definition for a field in a message.
-// field_def_num is set to FIT_FIELD_NUM_INVALID if field is not found in message.
-///////////////////////////////////////////////////////////////////////
-FIT_FIELD_DEF Fit_GetFieldDef(const FIT_MESG_DEF *mesg_def, FIT_UINT8 field_def_num);
-
-///////////////////////////////////////////////////////////////////////
-// Finds the byte offset of a message with the given message index in a FIT file
-// Requires a pointer to a function which can read a given number of bytes from a provided offset in a FIT file.
-// Returns the local message number if successful, or FIT_UINT8_INVALID if unsuccessful.
-///////////////////////////////////////////////////////////////////////
-FIT_UINT8 Fit_LookupMessage(FIT_UINT16 global_mesg_num, FIT_UINT16 message_index, FIT_UINT32 *offset, FIT_READ_BYTES_FUNC read_bytes_func, FIT_BOOL read_header);
-
-///////////////////////////////////////////////////////////////////////
-// Returns the byte offset of a message within a file.
-///////////////////////////////////////////////////////////////////////
-FIT_UINT32 Fit_GetFileMesgOffset(const FIT_FILE_DEF *file_def, FIT_UINT16 mesg_num, FIT_UINT16 mesg_index);
-
-#if defined(__cplusplus)
- }
-#endif
-
-#endif // !defined(FIT_H)
View
36 lib/fit/fit_config.h
@@ -1,36 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// The following FIT Protocol software provided may be used with FIT protocol
-// devices only and remains the copyrighted property of Dynastream Innovations Inc.
-// The software is being provided on an "as-is" basis and as an accommodation,
-// and therefore all warranties, representations, or guarantees of any kind
-// (whether express, implied or statutory) including, without limitation,
-// warranties of merchantability, non-infringement, or fitness for a particular
-// purpose, are specifically disclaimed.
-//
-// Copyright 2014 Dynastream Innovations Inc.
-////////////////////////////////////////////////////////////////////////////////
-
-
-#if !defined(FIT_CONFIG_H)
-#define FIT_CONFIG_H
-
-
-#if defined(__cplusplus)
- extern "C" {
-#endif
-
-#define FIT_USE_STDINT_H // Define to use stdint.h types. By default size in bytes of integer types assumed to be char=1, short=2, long=4.
-
-#define FIT_LOCAL_MESGS 16 // 1-16. Sets maximum number of local messages that can be decoded. Lower to minimize RAM requirements.
-#define FIT_ARCH_ENDIAN FIT_ARCH_ENDIAN_LITTLE // Set to correct endian for build architecture.
-
-#define FIT_CONVERT_CHECK_CRC // Define to check file crc.
-//#define FIT_CONVERT_CHECK_FILE_HDR_DATA_TYPE // Define to check file header for FIT data type. Verifies file is FIT format before starting decode.
-#define FIT_CONVERT_TIME_RECORD // Define to support time records (compressed timestamp).
-//#define FIT_CONVERT_MULTI_THREAD // Define to support multiple conversion threads.
-
-#if defined(__cplusplus)
- }
-#endif
-
-#endif // !defined(FIT_CONFIG_H)
View
486 lib/fit/fit_convert.c
@@ -1,486 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// The following FIT Protocol software provided may be used with FIT protocol
-// devices only and remains the copyrighted property of Dynastream Innovations Inc.
-// The software is being provided on an "as-is" basis and as an accommodation,
-// and therefore all warranties, representations, or guarantees of any kind
-// (whether express, implied or statutory) including, without limitation,
-// warranties of merchantability, non-infringement, or fitness for a particular
-// purpose, are specifically disclaimed.
-//
-// Copyright 2014 Dynastream Innovations Inc.
-////////////////////////////////////////////////////////////////////////////////
-
-
-#include <string.h>
-
-#include "fit_convert.h"
-#include "fit_crc.h"
-
-//////////////////////////////////////////////////////////////////////////////////
-// Private Variables
-//////////////////////////////////////////////////////////////////////////////////
-
-#if !defined(FIT_CONVERT_MULTI_THREAD)
- static FIT_CONVERT_STATE state_struct;
- #define state (&state_struct)
-#endif
-
-//////////////////////////////////////////////////////////////////////////////////
-// Public Functions
-//////////////////////////////////////////////////////////////////////////////////
-
-///////////////////////////////////////////////////////////////////////
-#if defined(FIT_CONVERT_MULTI_THREAD)
- void FitConvert_Init(FIT_CONVERT_STATE *state, FIT_BOOL read_file_header)
-#else
- void FitConvert_Init(FIT_BOOL read_file_header)
-#endif
-{
- state->mesg_offset = 0;
- state->data_offset = 0;
-
-#if defined(FIT_CONVERT_CHECK_CRC)
- state->crc = 0;
-#endif
-#if defined(FIT_CONVERT_TIME_RECORD)
- state->timestamp = 0;
- state->last_time_offset = 0;
-#endif
-
- if (read_file_header)
- {
- state->file_bytes_left = 3; // Header size byte + CRC.
- state->decode_state = FIT_CONVERT_DECODE_FILE_HDR;
- }
- else
- {
- state->file_bytes_left = 0; // Don't read header or check CRC.
- state->decode_state = FIT_CONVERT_DECODE_RECORD;
- }
-}
-
-///////////////////////////////////////////////////////////////////////
-#if defined(FIT_CONVERT_MULTI_THREAD)
- FIT_CONVERT_RETURN FitConvert_Read(FIT_CONVERT_STATE *state, const void *data, FIT_UINT32 size)
-#else
- FIT_CONVERT_RETURN FitConvert_Read(const void *data, FIT_UINT32 size)
-#endif
-{
- while (state->data_offset < size)
- {
- FIT_UINT8 datum = *((FIT_UINT8 *) data + state->data_offset);
- state->data_offset++;
-
- //printf("fit_convert: 0x%02X - %d\n",datum, state->decode_state);
-
- if (state->file_bytes_left > 0)
- {
- #if defined(FIT_CONVERT_CHECK_CRC)
- state->crc = FitCRC_Get16(state->crc, datum);
- #endif
-
- state->file_bytes_left--;
-
- if (state->file_bytes_left == 1) // CRC low byte.
- {
- if (state->decode_state != FIT_CONVERT_DECODE_RECORD)
- return FIT_CONVERT_ERROR;
-
- continue; // Next byte.
- }
- else if (state->file_bytes_left == 0) // CRC high byte.
- {
- #if defined(FIT_CONVERT_CHECK_CRC)
- if (state->crc != 0)
- return FIT_CONVERT_ERROR;
- #endif
-
- return FIT_CONVERT_END_OF_FILE;
- }
- }
-
- switch (state->decode_state)
- {
- case FIT_CONVERT_DECODE_FILE_HDR:
- if (state->mesg_offset < FIT_FILE_HDR_SIZE)
- *((FIT_UINT8 *) &state->u.file_hdr + state->mesg_offset) = datum;
-
- if (state->mesg_offset == 0)
- state->file_bytes_left = state->u.file_hdr.header_size + 2; // Increase to read header and CRC.
-
- state->mesg_offset++;
-
- if (state->mesg_offset >= state->u.file_hdr.header_size)
- {
- state->file_bytes_left = *((FIT_UINT8 *) &state->u.file_hdr.data_size);
- state->file_bytes_left |= (FIT_UINT32)*((FIT_UINT8 *) &state->u.file_hdr.data_size + 1) << 8;
- state->file_bytes_left |= (FIT_UINT32)*((FIT_UINT8 *) &state->u.file_hdr.data_size + 2) << 16;
- state->file_bytes_left |= (FIT_UINT32)*((FIT_UINT8 *) &state->u.file_hdr.data_size + 3) << 24;
- state->file_bytes_left += 2; // CRC.
-
- #if defined(FIT_CONVERT_CHECK_FILE_HDR_DATA_TYPE)
- if (memcmp(state->u.file_hdr.data_type, ".FIT", 4) != 0)
- return FIT_CONVERT_DATA_TYPE_NOT_SUPPORTED;
- #endif
-
- if ((state->u.file_hdr.protocol_version & FIT_PROTOCOL_VERSION_MAJOR_MASK) > (FIT_PROTOCOL_VERSION_MAJOR << FIT_PROTOCOL_VERSION_MAJOR_SHIFT))
- return FIT_CONVERT_PROTOCOL_VERSION_NOT_SUPPORTED;
-
- state->decode_state = FIT_CONVERT_DECODE_RECORD;
- }
- break;
-
- case FIT_CONVERT_DECODE_RECORD:
- if (datum & FIT_HDR_TIME_REC_BIT)
- {
- // This is a message data record with time.
- state->mesg_index = (datum & FIT_HDR_TIME_TYPE_MASK) >> FIT_HDR_TIME_TYPE_SHIFT;
-
- #if defined(FIT_CONVERT_TIME_RECORD)
- {
- FIT_UINT8 time_offset = datum & FIT_HDR_TIME_OFFSET_MASK;
- state->timestamp += (time_offset - state->last_time_offset) & FIT_HDR_TIME_OFFSET_MASK;
- state->last_time_offset = time_offset;
- }
- #endif
-
- state->decode_state = FIT_CONVERT_DECODE_FIELD_DATA;
- }
- else
- {
- state->mesg_index = datum & FIT_HDR_TYPE_MASK;
-
- if ((datum & FIT_HDR_TYPE_DEF_BIT) == 0)
- {
- // This is a message data record.
- state->decode_state = FIT_CONVERT_DECODE_FIELD_DATA;
- }
- else
- {
- // This is a message definition record.
- state->mesg_sizes[state->mesg_index] = 0;
- state->decode_state = FIT_CONVERT_DECODE_RESERVED1;
- }
- }
-
- if (state->decode_state == FIT_CONVERT_DECODE_FIELD_DATA)
- {
- if (state->mesg_index < FIT_LOCAL_MESGS)
- {
- state->mesg_def = Fit_GetMesgDef(state->convert_table[state->mesg_index].global_mesg_num);
- Fit_InitMesg(state->mesg_def, state->u.mesg);
-
- #if defined(FIT_CONVERT_TIME_RECORD)
- if (datum & FIT_HDR_TIME_REC_BIT)
- {
- FIT_UINT8 field_offset = Fit_GetFieldOffset(state->mesg_def, FIT_FIELD_NUM_TIMESTAMP);
-
- if (field_offset != FIT_UINT8_INVALID)
- memcpy(&state->u.mesg[field_offset], &state->timestamp, sizeof(state->timestamp));
- }
- #endif
- }
-
- if (state->mesg_sizes[state->mesg_index] == 0)
- state->decode_state = FIT_CONVERT_DECODE_RECORD;
- }
-
- state->mesg_offset = 0; // Reset the message byte count.
- state->field_index = 0;
- state->field_offset = 0;
- break;
-
- case FIT_CONVERT_DECODE_RESERVED1:
- if (state->mesg_index < FIT_LOCAL_MESGS)
- state->convert_table[state->mesg_index].reserved_1 = datum;
-
- state->decode_state = FIT_CONVERT_DECODE_ARCH;
- break;
-
- case FIT_CONVERT_DECODE_ARCH:
- if (state->mesg_index < FIT_LOCAL_MESGS)
- state->convert_table[state->mesg_index].arch = datum;
-
- state->decode_state = FIT_CONVERT_DECODE_GTYPE_1;
- break;
-
- case FIT_CONVERT_DECODE_GTYPE_1:
- if (state->mesg_index < FIT_LOCAL_MESGS)
- state->convert_table[state->mesg_index].global_mesg_num = datum;
-
- state->decode_state = FIT_CONVERT_DECODE_GTYPE_2;
- break;
-
- case FIT_CONVERT_DECODE_GTYPE_2:
- if (state->mesg_index < FIT_LOCAL_MESGS)
- {
- if ((state->convert_table[state->mesg_index].arch & FIT_ARCH_ENDIAN_MASK) == FIT_ARCH_ENDIAN_BIG)
- {
- state->convert_table[state->mesg_index].global_mesg_num <<= 8;
- state->convert_table[state->mesg_index].global_mesg_num |= datum;
- }
- else
- {
- state->convert_table[state->mesg_index].global_mesg_num |= ((FIT_UINT16) datum << 8);
- }
-
- state->convert_table[state->mesg_index].num_fields = 0; // Initialize.
- state->mesg_def = Fit_GetMesgDef(state->convert_table[state->mesg_index].global_mesg_num);
- }
-
- state->decode_state = FIT_CONVERT_DECODE_NUM_FIELD_DEFS;
- break;
-
- case FIT_CONVERT_DECODE_NUM_FIELD_DEFS:
- state->num_fields = datum;
-
- if (state->num_fields == 0)
- {
- state->decode_state = FIT_CONVERT_DECODE_RECORD;
- break;
- }
-
- state->field_index = 0;
- state->decode_state = FIT_CONVERT_DECODE_FIELD_DEF;
- break;
-
- case FIT_CONVERT_DECODE_FIELD_DEF:
- state->field_num = FIT_FIELD_NUM_INVALID;
-
- if (state->mesg_index < FIT_LOCAL_MESGS)
- {
- if (state->mesg_def != FIT_NULL)
- {
- FIT_UINT8 local_field_index;
- FIT_UINT8 local_field_offset = 0;
-
- // Search for the field definition in the local mesg definition.
- for (local_field_index = 0; local_field_index < state->mesg_def->num_fields; local_field_index++)
- {
- FIT_UINT8 field_size = state->mesg_def->fields[FIT_MESG_DEF_FIELD_OFFSET(size, local_field_index)];
-
- if (state->mesg_def->fields[FIT_MESG_DEF_FIELD_OFFSET(field_def_num, local_field_index)] == datum)
- {
- state->field_num = datum;
- state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].offset_in = state->mesg_offset;
- state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].offset_local = local_field_offset;
- state->convert_table[state->mesg_index].fields[state->convert_table[state->mesg_index].num_fields].size = field_size;
- break;
- }
-
- local_field_offset += field_size;
- }
- }