From 98aa5a409afde56356493b0a6a7708cbb0c6f8f2 Mon Sep 17 00:00:00 2001 From: Kirk Scheibelhut Date: Sat, 1 Mar 2014 23:00:13 -0800 Subject: [PATCH] Get gpx write working --- TODO | 8 +++--- activity.h | 2 +- csv.c | 4 +-- fitparse.c | 2 +- gpx.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++---- test.c | 6 ++-- util.h | 2 ++ 7 files changed, 92 insertions(+), 16 deletions(-) diff --git a/TODO b/TODO index 2353578..bd57735 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,11 @@ csv read - make sure to exit if invalid -gpx write -activity clean/verify points -activity laps -activity summary modify fix.c for doubles +* gpx write - mostly done, modulo laps and metadata time proper testing fix Makefile +activity clean/verify points +activity laps +activity summary additional fixes (gaps, hr, power) tcx + fit data diff --git a/activity.h b/activity.h index e8c5ea2..a05efb8 100644 --- a/activity.h +++ b/activity.h @@ -117,7 +117,7 @@ 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: " + "time: %.0f, lat: %.7f, lon: %.7f, 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], diff --git a/csv.c b/csv.c index 7116b7b..831b93d 100644 --- a/csv.c +++ b/csv.c @@ -164,8 +164,8 @@ int csv_write_options(char *filename, Activity *a, CSVOptions o) { /* print data points - must be at least one non empty */ for (i = 0, first = true; i < a->num_points; i++, first = true) { write_field(f, "%.0f", i, Timestamp, a, o, &first); - write_field(f, "%.15f", i, Latitude, a, o, &first); - write_field(f, "%.15f", i, Longitude, a, o, &first); + write_field(f, "%.7f", i, Latitude, a, o, &first); + write_field(f, "%.7f", i, Longitude, a, o, &first); write_field(f, "%.3f", i, Altitude, a, o, &first); write_field(f, "%.2f", i, Distance, a, o, &first); write_field(f, "%.2f", i, Speed, a, o, &first); diff --git a/fitparse.c b/fitparse.c index b09d356..4255987 100644 --- a/fitparse.c +++ b/fitparse.c @@ -79,7 +79,7 @@ Activity *fitparse_read(char *filename) { int fitparse_write(char *filename, Activity *activity) { FileFormat format = file_format_from_name(filename); - if (format != UnknownFileFormat) { + if (format == UnknownFileFormat) { format = DEFAULT_WRITE_FORMAT; } return fitparse_write_format(filename, format, activity); diff --git a/gpx.c b/gpx.c index dc67deb..6031756 100644 --- a/gpx.c +++ b/gpx.c @@ -73,8 +73,7 @@ static int sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *sax_data) { return 1; /* stop reading the file */ } - /* TODO confirm no memory leak */ - /* We must retain an element so that mxmlSAXLoadFile can return it */ + /* we must retain an element so that mxmlSAXLoadFile can return it */ mxmlRetain(node); } @@ -118,7 +117,6 @@ static int sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *sax_data) { parse_field(Power, state, data); } else if (!strcmp(name, "trkpt")) { activity_add_point(state->activity, &(state->dp)); - /*print_data_point(&(state->dp)); [> TODO <]*/ unset_data_point(&(state->dp)); } } else if (event == MXML_SAX_DATA) { @@ -161,13 +159,83 @@ Activity *gpx_read(char *filename) { } static mxml_node_t *to_gpx_xml(Activity *a) { + char buf[TIME_BUFSIZ]; unsigned i; - mxml_node_t *xml; /*, *gpx, *trk, *trkseg, *trkpt, *ele, *time, *hr, *temp, -*cadence, *bikepower; */ + mxml_node_t *xml, *gpx, *metadata, *trk, *time, *name, *trkseg, *trkpt, *ele, + *extensions, *gpxtpx, *atemp, *hr, *cad; xml = mxmlNewXML("1.0"); + + /* create gpx root */ + gpx = mxmlNewElement(xml, "gpx"); + mxmlElementSetAttr(gpx, "creator", "fitparse"); + mxmlElementSetAttr(gpx, "version", "1.1"); + mxmlElementSetAttr(gpx, "xmlns", "http://www.topografix.com/GPX/1/1"); + mxmlElementSetAttr(gpx, "xmlns:xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + mxmlElementSetAttr(gpx, "xmlns:gpxtpx", + "http://www.garmin.com/xmlschemas/TrackPointExtension/v1"); + mxmlElementSetAttr(gpx, "xmlns:gpxx", + "http://www.garmin.com/xmlschemas/GpxExtensions/v3"); + mxmlElementSetAttr(gpx, "xsi:schemaLocation", + "http://www.topografix.com/GPX/1/1 " + "http://www.topografix.com/GPX/1/1/gpx.xsd " + "http://www.garmin.com/xmlschemas/GpxExtensions/v3 " + "http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd " + "http://www.garmin.com/xmlschemas/TrackPointExtension/v1 " + "http://www.garmin.com/xmlschemas/" + "TrackPointExtensionv1.xsd"); + + /* write metadata element */ + metadata = mxmlNewElement(gpx, "metadata"); + time = mxmlNewElement(metadata, "time"); + format_timestamp(buf, 1393740341); /* TODO */ + mxmlNewText(time, 0, buf); + memset(buf, '\0', TIME_BUFSIZ); + + /* TODO lap waypoints? or lap as metadata? */ + + trk = mxmlNewElement(gpx, "trk"); + name = mxmlNewElement(trk, "name"); + mxmlNewText(name, 0, "Untitled"); + + trkseg = mxmlNewElement(trk, "trkseg"); for (i = 0; i < a->num_points; i++) { - /* TODO */ + trkpt = mxmlNewElement(trkseg, "trkpt"); + mxmlElementSetAttrf(trkpt, "lat", "%.7f", a->data_points[i].data[Latitude]); + mxmlElementSetAttrf(trkpt, "lon", "%.7f", + a->data_points[i].data[Longitude]); + + if (a->data_points[i].data[Altitude] != UNSET_FIELD) { + ele = mxmlNewElement(trkpt, "ele"); + mxmlNewTextf(ele, 0, "%.2f", a->data_points[i].data[Altitude]); + } + if (a->data_points[i].data[Timestamp] != UNSET_FIELD) { + time = mxmlNewElement(trkpt, "time"); + format_timestamp(buf, a->data_points[i].data[Timestamp]); + mxmlNewText(time, 0, buf); + memset(buf, '\0', TIME_BUFSIZ); + } + + if ((a->data_points[i].data[HeartRate] != UNSET_FIELD) || + (a->data_points[i].data[Cadence] != UNSET_FIELD) || + (a->data_points[i].data[Temperature] != UNSET_FIELD)) { + extensions = mxmlNewElement(trkpt, "extensions"); + gpxtpx = mxmlNewElement(extensions, "gpxtpx:TrackPointExtension"); + + if (a->data_points[i].data[HeartRate] != UNSET_FIELD) { + hr = mxmlNewElement(gpxtpx, "gpxtpx:hr"); + mxmlNewInteger(hr, a->data_points[i].data[HeartRate]); + } + if (a->data_points[i].data[Cadence] != UNSET_FIELD) { + cad = mxmlNewElement(gpxtpx, "gpxtpx:cad"); + mxmlNewInteger(cad, a->data_points[i].data[Cadence]); + } + if (a->data_points[i].data[Temperature] != UNSET_FIELD) { + atemp = mxmlNewElement(gpxtpx, "gpxtpx:atemp"); + mxmlNewInteger(atemp, a->data_points[i].data[Temperature]); + } + } } return xml; } @@ -176,6 +244,10 @@ int gpx_write(char *filename, Activity *a) { FILE *f; mxml_node_t *tree; + if (!(a->has_data[Latitude] && a->has_data[Longitude])) { + return 1; + } + f = fopen(filename, "w"); if (!(f = fopen(filename, "w")) || !(tree = to_gpx_xml(a))) { return 1; diff --git a/test.c b/test.c index cbf37b8..fced883 100644 --- a/test.c +++ b/test.c @@ -60,8 +60,10 @@ bool test(const char *filename, const char *dir) { strncpy(namebuf + len, PREFIX, sizeof(PREFIX) - 1); strcpy(namebuf + len + (sizeof(PREFIX) - 1), strrchr(filename, '/') + 1); - change_extension(namebuf, "csv"), print("writing to file %s\n", namebuf); - fitparse_write_format(namebuf, CSV, a); + /*change_extension(namebuf, "csv");*/ + print("writing to file %s\n", namebuf); + /*fitparse_write_format(namebuf, GPX, a);*/ + fitparse_write(namebuf, a); activity_destroy(a); return 0; #if 0 diff --git a/util.h b/util.h index fd1be0e..5981f05 100644 --- a/util.h +++ b/util.h @@ -22,6 +22,8 @@ #include #include +#define TIME_BUFSIZ 32 + static inline char *extension(char *filename) { char *s = strrchr(filename, '.'); return s ? s + 1 : s;