From 12b03ebc6171155e4c06e77ad902b5dcaebc3914 Mon Sep 17 00:00:00 2001 From: Eketh Date: Sun, 20 Apr 2014 10:27:22 +0200 Subject: [PATCH 01/33] Added support for GST frame --- minmea.c | 25 +++++++++++++++++++++++++ minmea.h | 13 +++++++++++++ 2 files changed, 38 insertions(+) diff --git a/minmea.c b/minmea.c index 8bbbc59..459c6ad 100644 --- a/minmea.c +++ b/minmea.c @@ -287,6 +287,8 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence) return MINMEA_SENTENCE_GGA; if (!strcmp(type+2, "GSA")) return MINMEA_SENTENCE_GSA; + if (!strcmp(type+2,"GST")) + return MINMEA_SENTENCE_GST; return MINMEA_UNKNOWN; } @@ -385,6 +387,29 @@ bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence) return true; } +bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) +{ + // $GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58 + char type[6]; + + if (!minmea_scan(sentence, "tTfffffff", + type, + &frame->time, + &frame->RMS_deviation,&frame->RMS_deviation_scale, + &frame->semi_major_sd,&frame->semi_major_sd_scale, + &frame->semi_minor_sd,&frame->semi_minor_sd_scale, + &frame->semi_major_orientation,&frame->semi_major_orientation_scale, + &frame->lattitude_error_deviation,&frame->lattitude_error_deviation_scale, + &frame->longitude_error_deviation,&frame->longitude_error_deviation_scale, + &frame->altitude_error_deviation,&frame->altitude_error_deviation_scale)) + return false; + if (strcmp(type+2, "GST")) + return false; + + return true; +} + + int minmea_gettimeofday(struct timeval *tv, const struct minmea_date *date, const struct minmea_time *time) { if (date->year == -1 || time->hours == -1) diff --git a/minmea.h b/minmea.h index 1caa0b3..0e25ec6 100644 --- a/minmea.h +++ b/minmea.h @@ -29,6 +29,7 @@ enum minmea_sentence_id { MINMEA_SENTENCE_RMC, MINMEA_SENTENCE_GGA, MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GST }; struct minmea_date { @@ -67,6 +68,17 @@ struct minmea_sentence_gga { int dgps_age; }; +struct minmea_sentence_gst { +struct minmea_time time; +int RMS_deviation, RMS_deviation_scale; +int semi_major_sd, semi_major_sd_scale; +int semi_minor_sd, semi_minor_sd_scale; +int semi_major_orientation, semi_major_orientation_scale; +int lattitude_error_deviation, lattitude_error_deviation_scale; +int longitude_error_deviation, longitude_error_deviation_scale; +int altitude_error_deviation, altitude_error_deviation_scale; +}; + enum minmea_gsa_mode { MINMEA_GPGSA_MODE_AUTO = 'A', MINMEA_GPGSA_MODE_FORCED = 'M', @@ -121,6 +133,7 @@ bool minmea_scan(const char *sentence, const char *format, ...); bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence); bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence); bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence); +bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence); /** * Convert GPS UTC date/time representation to a UNIX timestamp. From ae6195af1eeabd96d952284d9ab1de1b46aa72b6 Mon Sep 17 00:00:00 2001 From: Eketh Date: Sun, 20 Apr 2014 19:21:05 +0200 Subject: [PATCH 02/33] Removed tabulatures from minmea.c and minmea.h --- minmea.c | 6 +++--- minmea.h | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/minmea.c b/minmea.c index 459c6ad..9cb585f 100644 --- a/minmea.c +++ b/minmea.c @@ -287,8 +287,8 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence) return MINMEA_SENTENCE_GGA; if (!strcmp(type+2, "GSA")) return MINMEA_SENTENCE_GSA; - if (!strcmp(type+2,"GST")) - return MINMEA_SENTENCE_GST; + if (!strcmp(type+2,"GST")) + return MINMEA_SENTENCE_GST; return MINMEA_UNKNOWN; } @@ -396,7 +396,7 @@ bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) type, &frame->time, &frame->RMS_deviation,&frame->RMS_deviation_scale, - &frame->semi_major_sd,&frame->semi_major_sd_scale, + &frame->semi_major_sd,&frame->semi_major_sd_scale, &frame->semi_minor_sd,&frame->semi_minor_sd_scale, &frame->semi_major_orientation,&frame->semi_major_orientation_scale, &frame->lattitude_error_deviation,&frame->lattitude_error_deviation_scale, diff --git a/minmea.h b/minmea.h index 0e25ec6..65c6e9f 100644 --- a/minmea.h +++ b/minmea.h @@ -29,7 +29,7 @@ enum minmea_sentence_id { MINMEA_SENTENCE_RMC, MINMEA_SENTENCE_GGA, MINMEA_SENTENCE_GSA, - MINMEA_SENTENCE_GST + MINMEA_SENTENCE_GST }; struct minmea_date { @@ -69,14 +69,14 @@ struct minmea_sentence_gga { }; struct minmea_sentence_gst { -struct minmea_time time; -int RMS_deviation, RMS_deviation_scale; -int semi_major_sd, semi_major_sd_scale; -int semi_minor_sd, semi_minor_sd_scale; -int semi_major_orientation, semi_major_orientation_scale; -int lattitude_error_deviation, lattitude_error_deviation_scale; -int longitude_error_deviation, longitude_error_deviation_scale; -int altitude_error_deviation, altitude_error_deviation_scale; + struct minmea_time time; + int RMS_deviation, RMS_deviation_scale; + int semi_major_sd, semi_major_sd_scale; + int semi_minor_sd, semi_minor_sd_scale; + int semi_major_orientation, semi_major_orientation_scale; + int lattitude_error_deviation, lattitude_error_deviation_scale; + int longitude_error_deviation, longitude_error_deviation_scale; + int altitude_error_deviation, altitude_error_deviation_scale; }; enum minmea_gsa_mode { From 8a356438709fbc3b8d4609d9a26857bc1c636bab Mon Sep 17 00:00:00 2001 From: Eketh Date: Mon, 21 Apr 2014 10:35:22 +0200 Subject: [PATCH 03/33] Added support of GST frames in example.c --- example.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/example.c b/example.c index e973814..5ae6036 100644 --- a/example.c +++ b/example.c @@ -42,7 +42,24 @@ int main() printf("$GPGGA: fix quality: %d\n", frame.fix_quality); } } break; - + case MINMEA_SENTENCE_GST: { + struct minmea_sentence_gst frame; + if (minmea_parse_gst(&frame, line)) { + printf("+++ raw lattitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/d)\n", + frame.lattitude_error_deviation, frame.lattitude_error_deviation_scale, + frame.longitude_error_deviation, frame.longitude_error_deviation_scale, + frame.altitude_error_deviation, frame.altitude_error_deviation_scale); + printf("+++ fixed point lattitude,longitude and altitude error deviation \ + scaled to three decimal places: (%d,%d,%d)\n", + minmea_rescale(frame.lattitude_error_deviation, frame.lattitude_error_deviation_scale,1000), + minmea_rescale(frame.longitude_error_deviation, frame.longitude_error_deviation_scale,1000), + minmea_rescale(frame.altitude_error_deviation, frame.altitude_error_deviation_scale,1000)); + printf("+++ floating point degree lattitude,longitude and altitude error deviation: (%f,%f,%f)", + minmea_coord(frame.lattitude_error_deviation, frame.lattitude_error_deviation_scale), + minmea_coord(frame.longitude_error_deviation, frame.longitude_error_deviation_scale), + minmea_coord(frame.altitude_error_deviation, frame.altitude_error_deviation_scale)); + } + } break; default: { } break; } From d6a02269d41d525ef553bc6ced85cdae632615ca Mon Sep 17 00:00:00 2001 From: Eketh Date: Wed, 23 Apr 2014 13:23:24 +0200 Subject: [PATCH 04/33] Names unification for GPGST --- minmea.c | 6 +++--- minmea.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/minmea.c b/minmea.c index 9cb585f..5ec6452 100644 --- a/minmea.c +++ b/minmea.c @@ -395,9 +395,9 @@ bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) if (!minmea_scan(sentence, "tTfffffff", type, &frame->time, - &frame->RMS_deviation,&frame->RMS_deviation_scale, - &frame->semi_major_sd,&frame->semi_major_sd_scale, - &frame->semi_minor_sd,&frame->semi_minor_sd_scale, + &frame->rms_deviation,&frame->rms_deviation_scale, + &frame->semi_major_deviation,&frame->semi_major_deviation_scale, + &frame->semi_minor_deviation,&frame->semi_minor_deviation_scale, &frame->semi_major_orientation,&frame->semi_major_orientation_scale, &frame->lattitude_error_deviation,&frame->lattitude_error_deviation_scale, &frame->longitude_error_deviation,&frame->longitude_error_deviation_scale, diff --git a/minmea.h b/minmea.h index 65c6e9f..5f84857 100644 --- a/minmea.h +++ b/minmea.h @@ -70,9 +70,9 @@ struct minmea_sentence_gga { struct minmea_sentence_gst { struct minmea_time time; - int RMS_deviation, RMS_deviation_scale; - int semi_major_sd, semi_major_sd_scale; - int semi_minor_sd, semi_minor_sd_scale; + int rms_deviation, rms_deviation_scale; + int semi_major_deviation, semi_major_deviation_scale; + int semi_minor_deviation, semi_minor_deviation_scale; int semi_major_orientation, semi_major_orientation_scale; int lattitude_error_deviation, lattitude_error_deviation_scale; int longitude_error_deviation, longitude_error_deviation_scale; From dd2f2f20ff56be2549fe3fc8c5c19cc6e4098f99 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Wed, 23 Apr 2014 18:15:26 +0200 Subject: [PATCH 05/33] add support for optional fields (minmea_scan ";" modifier) --- minmea.c | 158 +++++++++++++++++++++++++++++++++---------------------- tests.c | 70 +++++++++++++++++++++++- 2 files changed, 163 insertions(+), 65 deletions(-) diff --git a/minmea.c b/minmea.c index 8bbbc59..72f1049 100644 --- a/minmea.c +++ b/minmea.c @@ -72,27 +72,45 @@ static inline bool minmea_isfield(char c) { bool minmea_scan(const char *sentence, const char *format, ...) { bool result = false; + bool optional = false; va_list ap; va_start(ap, format); const char *field = sentence; #define next_field() \ do { \ - while (minmea_isfield(*sentence++)) {} \ - field = sentence; \ + /* Progress to the next field. */ \ + while (minmea_isfield(*sentence)) \ + sentence++; \ + /* Make sure there is a field there. */ \ + if (*sentence == ',') { \ + sentence++; \ + field = sentence; \ + } else { \ + field = NULL; \ + } \ } while (0) while (*format) { char type = *format++; + if (type == ';') { + // All further fields are optional. + optional = true; + continue; + } + + if (!field && !optional) { + // Field requested but we ran out if input. Bail out. + goto parse_error; + } + switch (type) { case 'c': { // Single character field (char). char value = '\0'; - if (minmea_isfield(*field)) + if (field && minmea_isfield(*field)) value = *field; - else - value = '\0'; *va_arg(ap, char *) = value; } break; @@ -100,7 +118,7 @@ bool minmea_scan(const char *sentence, const char *format, ...) case 'd': { // Single character direction field (int). int value = 0; - if (minmea_isfield(*field)) { + if (field && minmea_isfield(*field)) { switch (*field) { case 'N': case 'E': @@ -111,7 +129,7 @@ bool minmea_scan(const char *sentence, const char *format, ...) value = -1; break; default: - goto end; + goto parse_error; } } @@ -123,27 +141,29 @@ bool minmea_scan(const char *sentence, const char *format, ...) int value = -1; int scale = 0; - while (minmea_isfield(*field)) { - if (*field == '+' && !sign && value == -1) { - sign = 1; - } else if (*field == '-' && !sign && value == -1) { - sign = -1; - } else if (isdigit((unsigned char) *field)) { - if (value == -1) - value = 0; - value = (10 * value) + (*field - '0'); - if (scale) - scale *= 10; - } else if (*field == '.' && scale == 0) { - scale = 1; - } else { - goto end; + if (field) { + while (minmea_isfield(*field)) { + if (*field == '+' && !sign && value == -1) { + sign = 1; + } else if (*field == '-' && !sign && value == -1) { + sign = -1; + } else if (isdigit((unsigned char) *field)) { + if (value == -1) + value = 0; + value = (10 * value) + (*field - '0'); + if (scale) + scale *= 10; + } else if (*field == '.' && scale == 0) { + scale = 1; + } else { + goto parse_error; + } + field++; } - field++; } if ((sign || scale) && value == -1) - goto end; + goto parse_error; if (value == -1) { value = 0; @@ -157,12 +177,14 @@ bool minmea_scan(const char *sentence, const char *format, ...) } break; case 'i': { // Integer value, default 0 (int). - int value; + int value = 0; - char *endptr; - value = strtol(field, &endptr, 10); - if (minmea_isfield(*endptr)) - goto end; + if (field) { + char *endptr; + value = strtol(field, &endptr, 10); + if (minmea_isfield(*endptr)) + goto parse_error; + } *va_arg(ap, int *) = value; } break; @@ -170,17 +192,24 @@ bool minmea_scan(const char *sentence, const char *format, ...) case 's': { // String value (char *). char *buf = va_arg(ap, char *); - while (minmea_isfield(*field)) - *buf++ = *field++; + if (field) { + while (minmea_isfield(*field)) + *buf++ = *field++; + } + *buf = '\0'; } break; case 't': { // NMEA talker+sentence identifier (char *). + // This field is always mandatory. + if (!field) + goto parse_error; + if (field[0] != '$') - goto end; + goto parse_error; for (int i=0; i<5; i++) if (!minmea_isfield(field[1+i])) - goto end; + goto parse_error; char *buf = va_arg(ap, char *); memcpy(buf, field+1, 5); @@ -191,16 +220,18 @@ bool minmea_scan(const char *sentence, const char *format, ...) struct minmea_date *date = va_arg(ap, struct minmea_date *); int d = -1, m = -1, y = -1; - // Always six digits. - for (int i=0; i<6; i++) - if (!isdigit((unsigned char) field[i])) - goto end_D; - d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); - m = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); - y = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + if (field && minmea_isfield(*field)) { + // Always six digits. + for (int i=0; i<6; i++) + if (!isdigit((unsigned char) field[i])) + goto parse_error; + + d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); + m = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); + y = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + } - end_D: date->day = d; date->month = m; date->year = y; @@ -210,30 +241,32 @@ bool minmea_scan(const char *sentence, const char *format, ...) struct minmea_time *time = va_arg(ap, struct minmea_time *); int h = -1, i = -1, s = -1, u = -1; - // Minimum required: integer time. - for (int i=0; i<6; i++) - if (!isdigit((unsigned char) field[i])) - goto end_T; - h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); - i = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); - s = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); - field += 6; + if (field && minmea_isfield(*field)) { + // Minimum required: integer time. + for (int i=0; i<6; i++) + if (!isdigit((unsigned char) field[i])) + goto parse_error; - // Extra: fractional time. Saved as microseconds. - if (*field++ == '.') { - int value = 0; - int scale = 1000000; - while (isdigit((unsigned char) *field) && scale > 1) { - value = (value * 10) + (*field++ - '0'); - scale /= 10; + h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); + i = strtol((char[]) {field[2], field[3], '\0'}, NULL, 10); + s = strtol((char[]) {field[4], field[5], '\0'}, NULL, 10); + field += 6; + + // Extra: fractional time. Saved as microseconds. + if (*field++ == '.') { + int value = 0; + int scale = 1000000; + while (isdigit((unsigned char) *field) && scale > 1) { + value = (value * 10) + (*field++ - '0'); + scale /= 10; + } + u = value * scale; + } else { + u = 0; } - u = value * scale; - } else { - u = 0; } - end_T: time->hours = h; time->minutes = i; time->seconds = s; @@ -244,17 +277,16 @@ bool minmea_scan(const char *sentence, const char *format, ...) } break; default: { // Unknown. - goto end; + goto parse_error; } break; } - // Advance to next field. next_field(); } result = true; -end: +parse_error: va_end(ap); return result; } diff --git a/tests.c b/tests.c index c45bde3..8ec9e3a 100644 --- a/tests.c +++ b/tests.c @@ -61,7 +61,7 @@ END_TEST START_TEST(test_minmea_scan_c) { - char ch; + char ch, extra; ck_assert(minmea_scan("A,123.45", "c", &ch) == true); ck_assert_int_eq(ch, 'A'); @@ -71,6 +71,19 @@ START_TEST(test_minmea_scan_c) ck_assert(minmea_scan(",123.45", "c", &ch) == true); ck_assert_int_eq(ch, '\0'); + + ck_assert(minmea_scan("A,B", "cc", &ch, &extra) == true); + ck_assert_int_eq(ch, 'A'); + ck_assert_int_eq(extra, 'B'); + + ck_assert(minmea_scan("C", "cc", &ch, &extra) == false); + + ck_assert(minmea_scan("D", "c;c", &ch, &extra) == true); + ck_assert_int_eq(ch, 'D'); + ck_assert_int_eq(extra, '\0'); + ck_assert(minmea_scan("E,F", "c;c", &ch, &extra) == true); + ck_assert_int_eq(ch, 'E'); + ck_assert_int_eq(extra, 'F'); } END_TEST @@ -131,6 +144,45 @@ START_TEST(test_minmea_scan_f) ck_assert(minmea_scan("-18000.00000", "f", &value, &scale) == true); ck_assert_int_eq(value, -1800000000); ck_assert_int_eq(scale, 100000); + + /* optional values */ + ck_assert(minmea_scan("foo", "_;f", &value, &scale) == true); + ck_assert_int_eq(scale, 0); + ck_assert(minmea_scan("foo,", "_;f", &value, &scale) == true); + ck_assert_int_eq(scale, 0); + ck_assert(minmea_scan("foo,12.3", "_;f", &value, &scale) == true); + ck_assert_int_eq(value, 123); + ck_assert_int_eq(scale, 10); +} +END_TEST + +START_TEST(test_minmea_scan_i) +{ + int value, extra; + + // valid parses + ck_assert(minmea_scan("14", "i", &value) == true); + ck_assert_int_eq(value, 14); + ck_assert(minmea_scan("-1234", "i", &value) == true); + ck_assert_int_eq(value, -1234); + + // empty field + ck_assert(minmea_scan("", "i", &value) == true); + ck_assert_int_eq(value, 0); + + // invalid value + ck_assert(minmea_scan("foo", "i", &value) == false); + + // missing field + ck_assert(minmea_scan("41", "ii", &value, &extra) == false); + + /* optional values */ + ck_assert(minmea_scan("10", "i;i", &value, &extra) == true); + ck_assert_int_eq(value, 10); + ck_assert(minmea_scan("20,30", "i;i", &value, &extra) == true); + ck_assert_int_eq(value, 20); + ck_assert_int_eq(extra, 30); + ck_assert(minmea_scan("42,foo", "i;i", &value, &extra) == false); } END_TEST @@ -138,9 +190,16 @@ START_TEST(test_minmea_scan_s) { char value[MINMEA_MAX_LENGTH]; + ck_assert(minmea_scan(",bar,baz", "s", value) == true); + ck_assert_str_eq(value, ""); ck_assert(minmea_scan("foo,bar,baz", "s", value) == true); ck_assert_str_eq(value, "foo"); - ck_assert(minmea_scan(",bar,baz", "s", value) == true); + + ck_assert(minmea_scan("dummy", "_;s", value) == true); + ck_assert_str_eq(value, ""); + ck_assert(minmea_scan("dummy,foo", "_;s", value) == true); + ck_assert_str_eq(value, "foo"); + ck_assert(minmea_scan("dummy,", "_;s", value) == true); ck_assert_str_eq(value, ""); } END_TEST @@ -164,6 +223,9 @@ START_TEST(test_minmea_scan_D) { struct minmea_date date; + ck_assert(minmea_scan("$GPXXX,3112", "_D", &date) == false); + ck_assert(minmea_scan("$GPXXX,foobar", "_D", &date) == false); + ck_assert(minmea_scan("$GPXXX,311299", "_D", &date) == true); ck_assert_int_eq(date.day, 31); ck_assert_int_eq(date.month, 12); @@ -180,6 +242,9 @@ START_TEST(test_minmea_scan_T) { struct minmea_time time; + ck_assert(minmea_scan("$GPXXX,2359", "_T", &time) == false); + ck_assert(minmea_scan("$GPXXX,foobar", "_T", &time) == false); + ck_assert(minmea_scan("$GPXXX,235960", "_T", &time) == true); ck_assert_int_eq(time.hours, 23); ck_assert_int_eq(time.minutes, 59); @@ -482,6 +547,7 @@ Suite *minmea_suite(void) tcase_add_test(tc_scan, test_minmea_scan_c); tcase_add_test(tc_scan, test_minmea_scan_d); tcase_add_test(tc_scan, test_minmea_scan_f); + tcase_add_test(tc_scan, test_minmea_scan_i); tcase_add_test(tc_scan, test_minmea_scan_s); tcase_add_test(tc_scan, test_minmea_scan_t); tcase_add_test(tc_scan, test_minmea_scan_D); From 24968cb3bac466d2c9369aec566acc518427c160 Mon Sep 17 00:00:00 2001 From: Eketh Date: Wed, 23 Apr 2014 19:57:30 +0200 Subject: [PATCH 06/33] Fixed spelling/codestyle --- example.c | 18 ++++++------ minmea.c | 16 +++++------ minmea.h | 4 +-- tests.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 100 insertions(+), 23 deletions(-) diff --git a/example.c b/example.c index 5ae6036..8842328 100644 --- a/example.c +++ b/example.c @@ -45,17 +45,17 @@ int main() case MINMEA_SENTENCE_GST: { struct minmea_sentence_gst frame; if (minmea_parse_gst(&frame, line)) { - printf("+++ raw lattitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/d)\n", - frame.lattitude_error_deviation, frame.lattitude_error_deviation_scale, + printf("+++ raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/d)\n", + frame.latitude_error_deviation, frame.latitude_error_deviation_scale, frame.longitude_error_deviation, frame.longitude_error_deviation_scale, frame.altitude_error_deviation, frame.altitude_error_deviation_scale); - printf("+++ fixed point lattitude,longitude and altitude error deviation \ - scaled to three decimal places: (%d,%d,%d)\n", - minmea_rescale(frame.lattitude_error_deviation, frame.lattitude_error_deviation_scale,1000), - minmea_rescale(frame.longitude_error_deviation, frame.longitude_error_deviation_scale,1000), - minmea_rescale(frame.altitude_error_deviation, frame.altitude_error_deviation_scale,1000)); - printf("+++ floating point degree lattitude,longitude and altitude error deviation: (%f,%f,%f)", - minmea_coord(frame.lattitude_error_deviation, frame.lattitude_error_deviation_scale), + printf("+++ fixed point latitude,longitude and altitude error deviation \ + scaled to one decimal place: (%d,%d,%d)\n", + minmea_rescale(frame.latitude_error_deviation, frame.latitude_error_deviation_scale,10), + minmea_rescale(frame.longitude_error_deviation, frame.longitude_error_deviation_scale,10), + minmea_rescale(frame.altitude_error_deviation, frame.altitude_error_deviation_scale,10)); + printf("+++ floating point degree latitude,longitude and altitude error deviation: (%f,%f,%f)", + minmea_coord(frame.latitude_error_deviation, frame.latitude_error_deviation_scale), minmea_coord(frame.longitude_error_deviation, frame.longitude_error_deviation_scale), minmea_coord(frame.altitude_error_deviation, frame.altitude_error_deviation_scale)); } diff --git a/minmea.c b/minmea.c index 5ec6452..84be55e 100644 --- a/minmea.c +++ b/minmea.c @@ -287,7 +287,7 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence) return MINMEA_SENTENCE_GGA; if (!strcmp(type+2, "GSA")) return MINMEA_SENTENCE_GSA; - if (!strcmp(type+2,"GST")) + if (!strcmp(type+2, "GST")) return MINMEA_SENTENCE_GST; return MINMEA_UNKNOWN; @@ -395,13 +395,13 @@ bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) if (!minmea_scan(sentence, "tTfffffff", type, &frame->time, - &frame->rms_deviation,&frame->rms_deviation_scale, - &frame->semi_major_deviation,&frame->semi_major_deviation_scale, - &frame->semi_minor_deviation,&frame->semi_minor_deviation_scale, - &frame->semi_major_orientation,&frame->semi_major_orientation_scale, - &frame->lattitude_error_deviation,&frame->lattitude_error_deviation_scale, - &frame->longitude_error_deviation,&frame->longitude_error_deviation_scale, - &frame->altitude_error_deviation,&frame->altitude_error_deviation_scale)) + &frame->rms_deviation, &frame->rms_deviation_scale, + &frame->semi_major_deviation, &frame->semi_major_deviation_scale, + &frame->semi_minor_deviation, &frame->semi_minor_deviation_scale, + &frame->semi_major_orientation, &frame->semi_major_orientation_scale, + &frame->latitude_error_deviation, &frame->latitude_error_deviation_scale, + &frame->longitude_error_deviation, &frame->longitude_error_deviation_scale, + &frame->altitude_error_deviation, &frame->altitude_error_deviation_scale)) return false; if (strcmp(type+2, "GST")) return false; diff --git a/minmea.h b/minmea.h index 5f84857..7f5a315 100644 --- a/minmea.h +++ b/minmea.h @@ -29,7 +29,7 @@ enum minmea_sentence_id { MINMEA_SENTENCE_RMC, MINMEA_SENTENCE_GGA, MINMEA_SENTENCE_GSA, - MINMEA_SENTENCE_GST + MINMEA_SENTENCE_GST, }; struct minmea_date { @@ -74,7 +74,7 @@ struct minmea_sentence_gst { int semi_major_deviation, semi_major_deviation_scale; int semi_minor_deviation, semi_minor_deviation_scale; int semi_major_orientation, semi_major_orientation_scale; - int lattitude_error_deviation, lattitude_error_deviation_scale; + int latitude_error_deviation, latitude_error_deviation_scale; int longitude_error_deviation, longitude_error_deviation_scale; int altitude_error_deviation, altitude_error_deviation_scale; }; diff --git a/tests.c b/tests.c index c45bde3..32ff6b2 100644 --- a/tests.c +++ b/tests.c @@ -32,6 +32,7 @@ static const char *valid_sequences[] = { "$GPGLL,5106.94086,N,01701.51680,E,123204.00,A,A*63", "$GPRMC,123205.00,A,5106.94085,N,01701.51689,E,0.016,,280214,,,A*7B", "$GPVTG,,T,,M,0.016,N,0.030,K,A*27", + "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58". NULL, }; @@ -284,6 +285,50 @@ START_TEST(test_minmea_scan_complex2) } END_TEST +START_TEST(test_minmea_scan_complex3) +{ + const char *sentence = "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58"; + char type[6]; + struct minmea_time time; + int rms_deviation, rms_deviation_scale; + int semi_major_deviation, semi_major_deviation_scale; + int semi_minor_deviation, semi_minor_deviation_scale; + int semi_major_orientation, semi_major_orientation_scale; + int latitude_error_deviation, latitude_error_deviation_scale; + int longitude_error_deviation, longitude_error_deviation_scale; + int altitude_error_deviation, altitude_error_deviation_scale; + ck_assert(minmea_scan(sentence, "tTfffffff", + type, + &time, + &rms_deviation, &rms_deviation_scale, + &semi_major_deviation, &semi_major_deviation_scale, + &semi_minor_deviation, &semi_minor_deviation_scale, + &semi_major_orientation, &semi_major_orientation_scale, + &latitude_error_deviation, &latitude_error_deviation_scale, + &longitude_error_deviation, &longitude_error_deviation_scale, + &altitude_error_deviation, &altitude_error_deviation_scale) == true); + ck_assert_str_eq(type, "GPGST"); + ck_assert_int_eq(time.hours, 2); + ck_assert_int_eq(time.minutes, 46); + ck_assert_int_eq(time.seconds, 3); + ck_assert_int_eq(time.microseconds, 0); + ck_assert_int_eq(rms_deviation, 32); + ck_assert_int_eq(rms_deviation_scale, 10); + ck_assert_int_eq(semi_major_deviation, 66); + ck_assert_int_eq(semi_major_deviation_scale, 10); + ck_assert_int_eq(semi_minor_deviation, 47); + ck_assert_int_eq(semi_minor_deviation_scale, 10); + ck_assert_int_eq(semi_major_orientation, 473); + ck_assert_int_eq(semi_major_orientation_scale, 10); + ck_assert_int_eq(latitude_error_deviation, 58); + ck_assert_int_eq(latitude_error_deviation, 10); + ck_assert_int_eq(longitude_error_deviation, 56); + ck_assert_int_eq(longitude_error_deviation, 10); + ck_assert_int_eq(altitude_error_deviation, 220); + ck_assert_int_eq(altitude_error_deviation_scale,10); +} +END_TEST + START_TEST(test_minmea_parse_rmc1) { const char *sentence = "$GPRMC,081836.75,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E"; @@ -356,6 +401,33 @@ START_TEST(test_minmea_parse_gga1) } END_TEST +START_TEST(test_minmea_parse_gst1) +{ + const char *sentence = "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58"; + struct minmea_sentence_gst frame = {}; + struct minmea_sentence_gst expected = { + .time = { 2, 46, 3, 0 }, + .rms_deviation = 32, + .rms_deviation_scale = 10, + .semi_major_deviation = 66, + .semi_major_deviation_scale = 10, + .semi_minor_deviation = 47, + .semi_minor_deviation_scale = 10, + .semi_major_orientation = 473, + .semi_major_orientation_scale = 10, + .latitude_error_deviation = 58, + .latitude_error_deviation_scale = 10, + .longitude_error_deviation = 56, + .longitude_error_deviation_scale = 10, + .altitude_error_deviation = 220, + .altitude_error_deviation_scale = 10, + }; + ck_assert(minmea_check(sentence) == true); + ck_assert(minmea_parse_gst(&frame, sentence) == true); + ck_assert(!memcmp(&frame, &expected, sizeof(frame))); +} +END_TEST + START_TEST(test_minmea_parse_gsa1) { const char *sentence = "$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39"; @@ -383,6 +455,7 @@ START_TEST(test_minmea_usage1) "$GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62", "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47", "$GNGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1", + "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58", NULL, }; @@ -392,17 +465,21 @@ START_TEST(test_minmea_usage1) struct minmea_sentence_rmc frame; ck_assert(minmea_parse_rmc(&frame, *sentence) == true); } break; - + case MINMEA_SENTENCE_GGA: { struct minmea_sentence_gga frame; ck_assert(minmea_parse_gga(&frame, *sentence) == true); } break; - + case MINMEA_SENTENCE_GSA: { struct minmea_sentence_gsa frame; ck_assert(minmea_parse_gsa(&frame, *sentence) == true); } break; + case MINMEA_SENTENCE_GST: { + struct minmea_sentence_gst frame; + ck_assert(minmea_parse_gst(&frame, *sentence) == true); + } break; default: { } break; @@ -473,7 +550,7 @@ END_TEST Suite *minmea_suite(void) { Suite *s = suite_create ("minmea"); - + TCase *tc_check = tcase_create("minmea_check"); tcase_add_test(tc_check, test_minmea_check); suite_add_tcase(s, tc_check); @@ -489,7 +566,7 @@ Suite *minmea_suite(void) tcase_add_test(tc_scan, test_minmea_scan_complex1); tcase_add_test(tc_scan, test_minmea_scan_complex2); suite_add_tcase(s, tc_scan); - + TCase *tc_parse = tcase_create("minmea_parse"); tcase_add_test(tc_parse, test_minmea_parse_rmc1); tcase_add_test(tc_parse, test_minmea_parse_rmc2); From b151ccae51466599cae9286ed667873cf971a377 Mon Sep 17 00:00:00 2001 From: Eketh Date: Wed, 23 Apr 2014 20:03:05 +0200 Subject: [PATCH 07/33] Fixed typo in example.c --- example.c | 1 + 1 file changed, 1 insertion(+) diff --git a/example.c b/example.c index 8842328..4d1f743 100644 --- a/example.c +++ b/example.c @@ -42,6 +42,7 @@ int main() printf("$GPGGA: fix quality: %d\n", frame.fix_quality); } } break; + case MINMEA_SENTENCE_GST: { struct minmea_sentence_gst frame; if (minmea_parse_gst(&frame, line)) { From cd0ee8f9e157c16f6518a77c5c44f83ba74eea46 Mon Sep 17 00:00:00 2001 From: Eketh Date: Wed, 23 Apr 2014 20:04:18 +0200 Subject: [PATCH 08/33] Fixed the last typo in example.c --- example.c | 1 + 1 file changed, 1 insertion(+) diff --git a/example.c b/example.c index 4d1f743..01152b0 100644 --- a/example.c +++ b/example.c @@ -61,6 +61,7 @@ int main() minmea_coord(frame.altitude_error_deviation, frame.altitude_error_deviation_scale)); } } break; + default: { } break; } From 116fb8267098258d9685eda6536283d3afcf8c81 Mon Sep 17 00:00:00 2001 From: mek_x Date: Wed, 23 Apr 2014 20:36:26 +0200 Subject: [PATCH 09/33] fix compilation in gcc --- Makefile | 2 +- minmea.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7c2cf7a..38c8704 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # the terms of the Do What The Fuck You Want To Public License, Version 2, as # published by Sam Hocevar. See the COPYING file for more details. -CFLAGS = -g -Wall -Wextra -Werror +CFLAGS = -g -Wall -Wextra -Werror -std=c99 LDFLAGS = -lcheck all: scan-build test example diff --git a/minmea.h b/minmea.h index 1caa0b3..8fab9c0 100644 --- a/minmea.h +++ b/minmea.h @@ -9,6 +9,8 @@ #ifndef MINMEA_H #define MINMEA_H +#define _BSD_SOURCE + #ifdef __cplusplus extern "C" { #endif From 92a46f24e201113d1ea05525fd747d1c70b013d7 Mon Sep 17 00:00:00 2001 From: mek_x Date: Wed, 23 Apr 2014 23:05:33 +0200 Subject: [PATCH 10/33] fix tests for gcc --- tests.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests.c b/tests.c index c45bde3..0159fc1 100644 --- a/tests.c +++ b/tests.c @@ -6,6 +6,8 @@ * published by Sam Hocevar. See the COPYING file for more details. */ +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + #include #include #include @@ -288,7 +290,7 @@ START_TEST(test_minmea_parse_rmc1) { const char *sentence = "$GPRMC,081836.75,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E"; struct minmea_sentence_rmc frame = {}; - struct minmea_sentence_rmc expected = { + static const struct minmea_sentence_rmc expected = { .time = { 8, 18, 36, 750000 }, .valid = true, .latitude = -375165, @@ -313,7 +315,7 @@ START_TEST(test_minmea_parse_rmc2) { const char *sentence = "$GPRMC,,A,3751.65,N,14507.36,W,,,,,"; struct minmea_sentence_rmc frame = {}; - struct minmea_sentence_rmc expected = { + static const struct minmea_sentence_rmc expected = { .time = { -1, -1, -1, -1 }, .valid = true, .latitude = 375165, @@ -332,7 +334,7 @@ START_TEST(test_minmea_parse_gga1) { const char *sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"; struct minmea_sentence_gga frame = {}; - struct minmea_sentence_gga expected = { + static const struct minmea_sentence_gga expected = { .time = { 12, 35, 19, 0 }, .latitude = 4807038, .latitude_scale = 1000, @@ -360,7 +362,7 @@ START_TEST(test_minmea_parse_gsa1) { const char *sentence = "$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39"; struct minmea_sentence_gsa frame = {}; - struct minmea_sentence_gsa expected = { + static const struct minmea_sentence_gsa expected = { .mode = MINMEA_GPGSA_MODE_AUTO, .fix_type = MINMEA_GPGSA_FIX_3D, .sats = { 4, 5, 0, 9, 12, 0, 0, 24, 0, 0, 0, 0 }, From c273d92a3318c2043e4bfcef85900e15f0a65a1d Mon Sep 17 00:00:00 2001 From: mek_x Date: Wed, 23 Apr 2014 23:43:28 +0200 Subject: [PATCH 11/33] minmea: added minmea_parse_gsv --- minmea.c | 37 +++++++++++++++++++++++++++++++++++++ minmea.h | 16 ++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/minmea.c b/minmea.c index 8bbbc59..231062d 100644 --- a/minmea.c +++ b/minmea.c @@ -287,6 +287,8 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence) return MINMEA_SENTENCE_GGA; if (!strcmp(type+2, "GSA")) return MINMEA_SENTENCE_GSA; + if (!strcmp(type+2, "GSV")) + return MINMEA_SENTENCE_GSV; return MINMEA_UNKNOWN; } @@ -385,6 +387,41 @@ bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence) return true; } +bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence) +{ + // $GPGSV,3,1,11,03,03,111,00,04,15,270,00,06,01,010,00,13,06,292,00*74 + char type[6]; + + if (!minmea_scan(sentence, "tiiiiiiiiiiiiiiiiiii", + type, + &frame->total_msgs, + &frame->msg_nr, + &frame->total_sats, + &frame->sats[0].nr, + &frame->sats[0].elevation, + &frame->sats[0].azimuth, + &frame->sats[0].snr, + &frame->sats[1].nr, + &frame->sats[1].elevation, + &frame->sats[1].azimuth, + &frame->sats[1].snr, + &frame->sats[2].nr, + &frame->sats[2].elevation, + &frame->sats[2].azimuth, + &frame->sats[2].snr, + &frame->sats[3].nr, + &frame->sats[3].elevation, + &frame->sats[3].azimuth, + &frame->sats[3].snr + )) { + return false; + } + if (strcmp(type+2, "GSV")) + return false; + + return true; +} + int minmea_gettimeofday(struct timeval *tv, const struct minmea_date *date, const struct minmea_time *time) { if (date->year == -1 || time->hours == -1) diff --git a/minmea.h b/minmea.h index 8fab9c0..0ca4f40 100644 --- a/minmea.h +++ b/minmea.h @@ -31,6 +31,7 @@ enum minmea_sentence_id { MINMEA_SENTENCE_RMC, MINMEA_SENTENCE_GGA, MINMEA_SENTENCE_GSA, + MINMEA_SENTENCE_GSV }; struct minmea_date { @@ -89,6 +90,20 @@ struct minmea_sentence_gsa { int vdop, vdop_scale; }; +struct minmea_sat_info { + int nr; + int elevation; + int azimuth; + int snr; +}; + +struct minmea_sentence_gsv { + int total_msgs; + int msg_nr; + int total_sats; + struct minmea_sat_info sats[4]; +}; + /** * Check sentence validity and checksum. Returns true for valid sentences. */ @@ -123,6 +138,7 @@ bool minmea_scan(const char *sentence, const char *format, ...); bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence); bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence); bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence); +bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence); /** * Convert GPS UTC date/time representation to a UNIX timestamp. From 6aee1b8bb70049c6e25fe7745e83598e1a397dcd Mon Sep 17 00:00:00 2001 From: mek_x Date: Wed, 23 Apr 2014 23:43:58 +0200 Subject: [PATCH 12/33] example.c: added example for gsv parsing --- example.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/example.c b/example.c index e973814..5a6f48e 100644 --- a/example.c +++ b/example.c @@ -43,6 +43,20 @@ int main() } } break; + case MINMEA_SENTENCE_GSV: { + struct minmea_sentence_gsv frame; + if (minmea_parse_gsv(&frame, line)) { + printf("$GPGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); + printf("$GPGSV: sattelites in view: %d\n", frame.total_sats); + for (int i = 0; i < 4; i++) + printf("$GPGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", + frame.sats[i].nr, + frame.sats[i].elevation, + frame.sats[i].azimuth, + frame.sats[i].snr); + } + } break; + default: { } break; } From daed9fd7079ab4786c9730b62b92553e2c4f4abf Mon Sep 17 00:00:00 2001 From: mek_x Date: Wed, 23 Apr 2014 23:55:02 +0200 Subject: [PATCH 13/33] tests: added unit tests for gsv --- tests.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests.c b/tests.c index 0159fc1..4becf22 100644 --- a/tests.c +++ b/tests.c @@ -379,6 +379,47 @@ START_TEST(test_minmea_parse_gsa1) } END_TEST +START_TEST(test_minmea_parse_gsv1) +{ + const char *sentence = "$GPGSV,3,3,11,22,42,067,42,24,14,311,43,27,05,244,00,,,,*4D"; + struct minmea_sentence_gsv frame = {}; + static const struct minmea_sentence_gsv expected = { + .total_msgs = 3, + .msg_nr = 3, + .total_sats = 11, + .sats = { + { + .nr = 22, + .elevation = 42, + .azimuth = 67, + .snr = 42 + }, + { + .nr = 24, + .elevation = 14, + .azimuth = 311, + .snr = 43 + }, + { + .nr = 27, + .elevation = 5, + .azimuth = 244, + .snr = 0 + }, + { + .nr = 0, + .elevation = 0, + .azimuth = 0, + .snr = 0 + } + } + }; + ck_assert(minmea_check(sentence) == true); + ck_assert(minmea_parse_gsv(&frame, sentence) == true); + ck_assert(!memcmp(&frame, &expected, sizeof(frame))); +} +END_TEST + START_TEST(test_minmea_usage1) { const char *sentences[] = { @@ -497,6 +538,7 @@ Suite *minmea_suite(void) tcase_add_test(tc_parse, test_minmea_parse_rmc2); tcase_add_test(tc_parse, test_minmea_parse_gga1); tcase_add_test(tc_parse, test_minmea_parse_gsa1); + tcase_add_test(tc_parse, test_minmea_parse_gsv1); suite_add_tcase(s, tc_parse); TCase *tc_usage = tcase_create("minmea_usage"); From 32f6397c0592cb155e60c0d3b1e9d84c65b4eb6f Mon Sep 17 00:00:00 2001 From: mek_x Date: Thu, 24 Apr 2014 00:03:08 +0200 Subject: [PATCH 14/33] README.md: added info about GSV --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 8d788b0..c1d3271 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ systems. * ``RMC`` (Recommended Minimum: position, velocity, time) * ``GGA`` (Fix Data) * ``GSA`` (DOP and active satellites) +* ``GSV`` (Satellites in view) Adding support for more sentences is trivial; see ``minmea.c`` source. @@ -78,6 +79,20 @@ The library doesn't perform this conversion automatically for the following reas printf("$GPGGA: fix quality: %d\n", frame.fix_quality); } } break; + + case MINMEA_SENTENCE_GSV: { + struct minmea_sentence_gsv frame; + if (minmea_parse_gsv(&frame, line)) { + printf("$GPGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); + printf("$GPGSV: sattelites in view: %d\n", frame.total_sats); + for (int i = 0; i < 4; i++) + printf("$GPGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", + frame.sats[i].nr, + frame.sats[i].elevation, + frame.sats[i].azimuth, + frame.sats[i].snr); + } + } break; } } ``` From 181565066a970257e7ffcf1f56e069b49567a266 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Thu, 24 Apr 2014 11:38:57 +0200 Subject: [PATCH 15/33] Makefile: libraries belong to LDLIBS, not LDFLAGS. Fixes build under Cygwin (and possibly others). --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 38c8704..075219a 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # published by Sam Hocevar. See the COPYING file for more details. CFLAGS = -g -Wall -Wextra -Werror -std=c99 -LDFLAGS = -lcheck +LDLIBS = -lcheck all: scan-build test example @echo "+++ All good.""" From c5efe80f36938fbe275887516f7c6806fe73ba31 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Thu, 24 Apr 2014 11:57:50 +0200 Subject: [PATCH 16/33] README.md: update supported frames list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8d788b0..2178428 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ systems. * ``RMC`` (Recommended Minimum: position, velocity, time) * ``GGA`` (Fix Data) * ``GSA`` (DOP and active satellites) +* ``GST`` (Pseudorange Noise Statistics) Adding support for more sentences is trivial; see ``minmea.c`` source. From e4ce3245813aa73c0d47fa3f801f4b1018dabe95 Mon Sep 17 00:00:00 2001 From: mek-x Date: Thu, 24 Apr 2014 15:44:49 +0200 Subject: [PATCH 17/33] minmea.h: style fix --- minmea.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minmea.h b/minmea.h index 0ca4f40..811a92c 100644 --- a/minmea.h +++ b/minmea.h @@ -31,7 +31,7 @@ enum minmea_sentence_id { MINMEA_SENTENCE_RMC, MINMEA_SENTENCE_GGA, MINMEA_SENTENCE_GSA, - MINMEA_SENTENCE_GSV + MINMEA_SENTENCE_GSV, }; struct minmea_date { From e2e6a41209c32261114d069025dfe0164c70df29 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Thu, 24 Apr 2014 15:57:46 +0200 Subject: [PATCH 18/33] tests.c: fix build errors introduced by #4 My bad for not checking this. --- tests.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests.c b/tests.c index 970c782..6a148a1 100644 --- a/tests.c +++ b/tests.c @@ -34,7 +34,7 @@ static const char *valid_sequences[] = { "$GPGLL,5106.94086,N,01701.51680,E,123204.00,A,A*63", "$GPRMC,123205.00,A,5106.94085,N,01701.51689,E,0.016,,280214,,,A*7B", "$GPVTG,,T,,M,0.016,N,0.030,K,A*27", - "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58". + "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58", NULL, }; @@ -388,9 +388,9 @@ START_TEST(test_minmea_scan_complex3) ck_assert_int_eq(semi_major_orientation, 473); ck_assert_int_eq(semi_major_orientation_scale, 10); ck_assert_int_eq(latitude_error_deviation, 58); - ck_assert_int_eq(latitude_error_deviation, 10); + ck_assert_int_eq(latitude_error_deviation_scale, 10); ck_assert_int_eq(longitude_error_deviation, 56); - ck_assert_int_eq(longitude_error_deviation, 10); + ck_assert_int_eq(longitude_error_deviation_scale, 10); ck_assert_int_eq(altitude_error_deviation, 220); ck_assert_int_eq(altitude_error_deviation_scale,10); } @@ -633,6 +633,7 @@ Suite *minmea_suite(void) tcase_add_test(tc_scan, test_minmea_scan_T); tcase_add_test(tc_scan, test_minmea_scan_complex1); tcase_add_test(tc_scan, test_minmea_scan_complex2); + tcase_add_test(tc_scan, test_minmea_scan_complex3); suite_add_tcase(s, tc_scan); TCase *tc_parse = tcase_create("minmea_parse"); @@ -640,6 +641,7 @@ Suite *minmea_suite(void) tcase_add_test(tc_parse, test_minmea_parse_rmc2); tcase_add_test(tc_parse, test_minmea_parse_gga1); tcase_add_test(tc_parse, test_minmea_parse_gsa1); + tcase_add_test(tc_parse, test_minmea_parse_gst1); suite_add_tcase(s, tc_parse); TCase *tc_usage = tcase_create("minmea_usage"); From 6d758814259dee5752f0a8404f55eb90840834f3 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Thu, 24 Apr 2014 16:05:50 +0200 Subject: [PATCH 19/33] example.c: fix build errors introduced by #4 My bad for not checking this. --- example.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.c b/example.c index abc4371..e7c90f1 100644 --- a/example.c +++ b/example.c @@ -46,7 +46,7 @@ int main() case MINMEA_SENTENCE_GST: { struct minmea_sentence_gst frame; if (minmea_parse_gst(&frame, line)) { - printf("+++ raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/d)\n", + printf("+++ raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/%d)\n", frame.latitude_error_deviation, frame.latitude_error_deviation_scale, frame.longitude_error_deviation, frame.longitude_error_deviation_scale, frame.altitude_error_deviation, frame.altitude_error_deviation_scale); From 14437a963190bc697deea6f286195dce9704eddf Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 16:22:37 +0200 Subject: [PATCH 20/33] [API change] add struct minmea_float w/ int_least32_t This is an API breaker that incorporates the following big changes: 1. The (value, scale) float, as used in minmea, is a compound type. It should be declared and used as such. 2. Some platforms, notably avr-gcc, have 16-bit ints. Make life easier for them by using int_least32_t for floating point values. 3. As a side effect, the following functions were renamed: * minmea_coord -> minmea_tocoord * minmea_float -> minmea_tofloat Migration guide for user code (easy unless you do fancy stuff): 1. Replace calls to minmea_{coord,float}. 2. Replace int32_t with int_least32_t in user code if your platform requires this. --- minmea.c | 60 +++++------- minmea.h | 75 +++++++------- tests.c | 292 +++++++++++++++++++++++++------------------------------ 3 files changed, 202 insertions(+), 225 deletions(-) diff --git a/minmea.c b/minmea.c index 6cf4457..4d748a5 100644 --- a/minmea.c +++ b/minmea.c @@ -136,10 +136,10 @@ bool minmea_scan(const char *sentence, const char *format, ...) *va_arg(ap, int *) = value; } break; - case 'f': { // Fractional value with scale (int, int). + case 'f': { // Fractional value with scale (struct minmea_float). int sign = 0; - int value = -1; - int scale = 0; + int_least32_t value = -1; + int_least32_t scale = 0; if (field) { while (minmea_isfield(*field)) { @@ -172,8 +172,7 @@ bool minmea_scan(const char *sentence, const char *format, ...) if (sign) value *= sign; - *va_arg(ap, int *) = value; - *va_arg(ap, int *) = scale; + *va_arg(ap, struct minmea_float *) = (struct minmea_float) {value, scale}; } break; case 'i': { // Integer value, default 0 (int). @@ -339,20 +338,20 @@ bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence) type, &frame->time, &validity, - &frame->latitude, &frame->latitude_scale, &latitude_direction, - &frame->longitude, &frame->longitude_scale, &longitude_direction, - &frame->speed, &frame->speed_scale, - &frame->course, &frame->course_scale, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, + &frame->speed, + &frame->course, &frame->date, - &frame->variation, &frame->variation_scale, &variation_direction)) + &frame->variation, &variation_direction)) return false; if (strcmp(type+2, "RMC")) return false; frame->valid = (validity == 'A'); - frame->latitude *= latitude_direction; - frame->longitude *= longitude_direction; - frame->variation *= variation_direction; + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; + frame->variation.value *= variation_direction; return true; } @@ -367,20 +366,20 @@ bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence) if (!minmea_scan(sentence, "tTfdfdiiffcfci_", type, &frame->time, - &frame->latitude, &frame->latitude_scale, &latitude_direction, - &frame->longitude, &frame->longitude_scale, &longitude_direction, + &frame->latitude, &latitude_direction, + &frame->longitude, &longitude_direction, &frame->fix_quality, &frame->satellites_tracked, - &frame->hdop, &frame->hdop_scale, - &frame->altitude, &frame->altitude_scale, &frame->altitude_units, - &frame->height, &frame->height_scale, &frame->height_units, + &frame->hdop, + &frame->altitude, &frame->altitude_units, + &frame->height, &frame->height_units, &frame->dgps_age)) return false; if (strcmp(type+2, "GGA")) return false; - frame->latitude *= latitude_direction; - frame->longitude *= longitude_direction; + frame->latitude.value *= latitude_direction; + frame->longitude.value *= longitude_direction; return true; } @@ -407,14 +406,9 @@ bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence) &frame->sats[10], &frame->sats[11], &frame->pdop, - &frame->pdop_scale, &frame->hdop, - &frame->hdop_scale, - &frame->vdop, - &frame->vdop_scale - )){ + &frame->vdop)) return false; - } if (strcmp(type+2, "GSA")) return false; @@ -429,13 +423,13 @@ bool minmea_parse_gst(struct minmea_sentence_gst *frame, const char *sentence) if (!minmea_scan(sentence, "tTfffffff", type, &frame->time, - &frame->rms_deviation, &frame->rms_deviation_scale, - &frame->semi_major_deviation, &frame->semi_major_deviation_scale, - &frame->semi_minor_deviation, &frame->semi_minor_deviation_scale, - &frame->semi_major_orientation, &frame->semi_major_orientation_scale, - &frame->latitude_error_deviation, &frame->latitude_error_deviation_scale, - &frame->longitude_error_deviation, &frame->longitude_error_deviation_scale, - &frame->altitude_error_deviation, &frame->altitude_error_deviation_scale)) + &frame->rms_deviation, + &frame->semi_major_deviation, + &frame->semi_minor_deviation, + &frame->semi_major_orientation, + &frame->latitude_error_deviation, + &frame->longitude_error_deviation, + &frame->altitude_error_deviation)) return false; if (strcmp(type+2, "GST")) return false; diff --git a/minmea.h b/minmea.h index ddb417b..65fe7e9 100644 --- a/minmea.h +++ b/minmea.h @@ -35,6 +35,11 @@ enum minmea_sentence_id { MINMEA_SENTENCE_GSV, }; +struct minmea_float { + int_least32_t value; + int_least32_t scale; +}; + struct minmea_date { int day; int month; @@ -51,35 +56,35 @@ struct minmea_time { struct minmea_sentence_rmc { struct minmea_time time; bool valid; - int latitude, latitude_scale; - int longitude, longitude_scale; - int speed, speed_scale; - int course, course_scale; + struct minmea_float latitude; + struct minmea_float longitude; + struct minmea_float speed; + struct minmea_float course; struct minmea_date date; - int variation, variation_scale; + struct minmea_float variation; }; struct minmea_sentence_gga { struct minmea_time time; - int latitude, latitude_scale; - int longitude, longitude_scale; + struct minmea_float latitude; + struct minmea_float longitude; int fix_quality; int satellites_tracked; - int hdop, hdop_scale; - int altitude, altitude_scale; char altitude_units; - int height, height_scale; char height_units; + struct minmea_float hdop; + struct minmea_float altitude; char altitude_units; + struct minmea_float height; char height_units; int dgps_age; }; struct minmea_sentence_gst { struct minmea_time time; - int rms_deviation, rms_deviation_scale; - int semi_major_deviation, semi_major_deviation_scale; - int semi_minor_deviation, semi_minor_deviation_scale; - int semi_major_orientation, semi_major_orientation_scale; - int latitude_error_deviation, latitude_error_deviation_scale; - int longitude_error_deviation, longitude_error_deviation_scale; - int altitude_error_deviation, altitude_error_deviation_scale; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; }; enum minmea_gsa_mode { @@ -97,9 +102,9 @@ struct minmea_sentence_gsa { char mode; int fix_type; int sats[12]; - int pdop, pdop_scale; - int hdop, hdop_scale; - int vdop, vdop_scale; + struct minmea_float pdop; + struct minmea_float hdop; + struct minmea_float vdop; }; struct minmea_sat_info { @@ -161,40 +166,40 @@ int minmea_gettimeofday(struct timeval *tv, const struct minmea_date *date, cons /** * Rescale a fixed-point value to a different scale. Rounds towards zero. */ -static inline int minmea_rescale(int value, int from, int to) +static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t new_scale) { - if (from == 0) + if (f->scale == 0) return 0; - if (from == to) - return value; - if (from > to) - return (value + ((value > 0) - (value < 0)) * from/to/2) / (from/to); + if (f->scale == new_scale) + return f->value; + if (f->scale > new_scale) + return (f->value + ((f->value > 0) - (f->value < 0)) * f->scale/new_scale/2) / (f->scale/new_scale); else - return value * (to/from); + return f->value * (new_scale/f->scale); } /** * Convert a fixed-point value to a floating-point value. * Returns NaN for "unknown" values. */ -static inline float minmea_float(int value, int scale) +static inline float minmea_tofloat(struct minmea_float *f) { - if (scale == 0) + if (f->scale == 0) return NAN; - return (float) value / (float) scale; + return (float) f->value / (float) f->scale; } /** * Convert a raw coordinate to a floating point DD.DDD... value. * Returns NaN for "unknown" values. */ -static inline float minmea_coord(int value, int scale) +static inline float minmea_tocoord(struct minmea_float *f) { - if (scale == 0) + if (f->scale == 0) return NAN; - int degrees = value / (scale * 100); - int minutes = value % (scale * 100); - return (float) degrees + (float) minutes / (60 * scale); + int_least32_t degrees = f->value / (f->scale * 100); + int_least32_t minutes = f->value % (f->scale * 100); + return (float) degrees + (float) minutes / (60 * f->scale); } #ifdef __cplusplus diff --git a/tests.c b/tests.c index 6e07252..7dbafdd 100644 --- a/tests.c +++ b/tests.c @@ -112,50 +112,50 @@ END_TEST START_TEST(test_minmea_scan_f) { - int value, scale; + struct minmea_float f; - ck_assert(minmea_scan("-", "f", &value, &scale) == false); - ck_assert(minmea_scan("10-", "f", &value, &scale) == false); - ck_assert(minmea_scan("+-10", "f", &value, &scale) == false); - ck_assert(minmea_scan("12..45", "f", &value, &scale) == false); - ck_assert(minmea_scan("blah", "f", &value, &scale) == false); - ck_assert(minmea_scan("12.3.4", "f", &value, &scale) == false); + ck_assert(minmea_scan("-", "f", &f) == false); + ck_assert(minmea_scan("10-", "f", &f) == false); + ck_assert(minmea_scan("+-10", "f", &f) == false); + ck_assert(minmea_scan("12..45", "f", &f) == false); + ck_assert(minmea_scan("blah", "f", &f) == false); + ck_assert(minmea_scan("12.3.4", "f", &f) == false); - ck_assert(minmea_scan(",", "f", &value, &scale) == true); - ck_assert_int_eq(scale, 0); - ck_assert(minmea_scan("", "f", &value, &scale) == true); - ck_assert_int_eq(scale, 0); + ck_assert(minmea_scan(",", "f", &f) == true); + ck_assert_int_eq(f.scale, 0); + ck_assert(minmea_scan("", "f", &f) == true); + ck_assert_int_eq(f.scale, 0); - ck_assert(minmea_scan("15.345", "f", &value, &scale) == true); - ck_assert_int_eq(value, 15345); - ck_assert_int_eq(scale, 1000); + ck_assert(minmea_scan("15.345", "f", &f) == true); + ck_assert_int_eq(f.value, 15345); + ck_assert_int_eq(f.scale, 1000); - ck_assert(minmea_scan("-1.23,V", "f", &value, &scale) == true); - ck_assert_int_eq(value, -123); - ck_assert_int_eq(scale, 100); + ck_assert(minmea_scan("-1.23,V", "f", &f) == true); + ck_assert_int_eq(f.value, -123); + ck_assert_int_eq(f.scale, 100); /* some GPS units have absurdly big precision. handle whatever int handles. */ - ck_assert(minmea_scan("5106.94091", "f", &value, &scale) == true); - ck_assert_int_eq(value, 510694091); - ck_assert_int_eq(scale, 100000); + ck_assert(minmea_scan("5106.94091", "f", &f) == true); + ck_assert_int_eq(f.value, 510694091); + ck_assert_int_eq(f.scale, 100000); /* for now we support +-180 degrees with 5 decimal digits; anything * more will overflow. */ - ck_assert(minmea_scan("18000.00000", "f", &value, &scale) == true); - ck_assert_int_eq(value, 1800000000); - ck_assert_int_eq(scale, 100000); - ck_assert(minmea_scan("-18000.00000", "f", &value, &scale) == true); - ck_assert_int_eq(value, -1800000000); - ck_assert_int_eq(scale, 100000); + ck_assert(minmea_scan("18000.00000", "f", &f) == true); + ck_assert_int_eq(f.value, 1800000000); + ck_assert_int_eq(f.scale, 100000); + ck_assert(minmea_scan("-18000.00000", "f", &f) == true); + ck_assert_int_eq(f.value, -1800000000); + ck_assert_int_eq(f.scale, 100000); - /* optional values */ - ck_assert(minmea_scan("foo", "_;f", &value, &scale) == true); - ck_assert_int_eq(scale, 0); - ck_assert(minmea_scan("foo,", "_;f", &value, &scale) == true); - ck_assert_int_eq(scale, 0); - ck_assert(minmea_scan("foo,12.3", "_;f", &value, &scale) == true); - ck_assert_int_eq(value, 123); - ck_assert_int_eq(scale, 10); + /* optional f.values */ + ck_assert(minmea_scan("foo", "_;f", &f) == true); + ck_assert_int_eq(f.scale, 0); + ck_assert(minmea_scan("foo,", "_;f", &f) == true); + ck_assert_int_eq(f.scale, 0); + ck_assert(minmea_scan("foo,12.3", "_;f", &f) == true); + ck_assert_int_eq(f.value, 123); + ck_assert_int_eq(f.scale, 10); } END_TEST @@ -273,42 +273,42 @@ START_TEST(test_minmea_scan_complex1) const char *sentence = "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47\r\n"; char type[6]; struct minmea_time time; - int latitude, latitude_scale, latitude_direction; - int longitude, longitude_scale, longitude_direction; + struct minmea_float latitude; int latitude_direction; + struct minmea_float longitude; int longitude_direction; int fix_quality; int satellites; - int hdop, hdop_scale; - int altitude, altitude_scale; char altitude_units; - int height, height_scale; char height_units; + struct minmea_float hdop; + struct minmea_float altitude; char altitude_units; + struct minmea_float height; char height_units; ck_assert(minmea_scan(sentence, "tTfdfdiiffcfc__", type, &time, - &latitude, &latitude_scale, &latitude_direction, - &longitude, &longitude_scale, &longitude_direction, + &latitude, &latitude_direction, + &longitude, &longitude_direction, &fix_quality, &satellites, - &hdop, &hdop_scale, - &altitude, &altitude_scale, &altitude_units, - &height, &height_scale, &height_units) == true); + &hdop, + &altitude, &altitude_units, + &height, &height_units) == true); ck_assert_str_eq(type, "GPGGA"); ck_assert_int_eq(time.hours, 12); ck_assert_int_eq(time.minutes, 35); ck_assert_int_eq(time.seconds, 19); - ck_assert_int_eq(latitude, 4807038); - ck_assert_int_eq(latitude_scale, 1000); + ck_assert_int_eq(latitude.value, 4807038); + ck_assert_int_eq(latitude.scale, 1000); ck_assert_int_eq(latitude_direction, 1); - ck_assert_int_eq(longitude, 1131000); - ck_assert_int_eq(longitude_scale, 1000); + ck_assert_int_eq(longitude.value, 1131000); + ck_assert_int_eq(longitude.scale, 1000); ck_assert_int_eq(longitude_direction, 1); ck_assert_int_eq(fix_quality, 1); ck_assert_int_eq(satellites, 8); - ck_assert_int_eq(hdop, 9); - ck_assert_int_eq(hdop_scale, 10); - ck_assert_int_eq(altitude, 5454); - ck_assert_int_eq(altitude_scale, 10); + ck_assert_int_eq(hdop.value, 9); + ck_assert_int_eq(hdop.scale, 10); + ck_assert_int_eq(altitude.value, 5454); + ck_assert_int_eq(altitude.scale, 10); ck_assert_int_eq(altitude_units, 'M'); - ck_assert_int_eq(height, 469); - ck_assert_int_eq(height_scale, 10); + ck_assert_int_eq(height.value, 469); + ck_assert_int_eq(height.scale, 10); ck_assert_int_eq(height_units, 'M'); } @@ -319,34 +319,34 @@ START_TEST(test_minmea_scan_complex2) const char *sentence = "$GPBWC,081837,,,,,,T,,M,,N,*13"; char type[6]; struct minmea_time time; - int latitude, latitude_scale, latitude_direction; - int longitude, longitude_scale, longitude_direction; - int bearing_true, bearing_true_scale; char bearing_true_mark; - int bearing_magnetic, bearing_magnetic_scale; char bearing_magnetic_mark; - int distance, distance_scale; char distance_units; + struct minmea_float latitude; int latitude_direction; + struct minmea_float longitude; int longitude_direction; + struct minmea_float bearing_true; char bearing_true_mark; + struct minmea_float bearing_magnetic; char bearing_magnetic_mark; + struct minmea_float distance; char distance_units; char name[MINMEA_MAX_LENGTH]; ck_assert(minmea_scan(sentence, "tTfdfdfcfcfcs", type, &time, - &latitude, &latitude_scale, &latitude_direction, - &longitude, &longitude_scale, &longitude_direction, - &bearing_true, &bearing_true_scale, &bearing_true_mark, - &bearing_magnetic, &bearing_magnetic_scale, &bearing_magnetic_mark, - &distance, &distance_scale, &distance_units, + &latitude, &latitude_direction, + &longitude, &longitude_direction, + &bearing_true, &bearing_true_mark, + &bearing_magnetic, &bearing_magnetic_mark, + &distance, &distance_units, name) == true); ck_assert_str_eq(type, "GPBWC"); ck_assert_int_eq(time.hours, 8); ck_assert_int_eq(time.minutes, 18); ck_assert_int_eq(time.seconds, 37); - ck_assert_int_eq(latitude_scale, 0); + ck_assert_int_eq(latitude.scale, 0); ck_assert_int_eq(latitude_direction, 0); - ck_assert_int_eq(longitude_scale, 0); + ck_assert_int_eq(longitude.scale, 0); ck_assert_int_eq(longitude_direction, 0); - ck_assert_int_eq(bearing_true_scale, 0); + ck_assert_int_eq(bearing_true.scale, 0); ck_assert_int_eq(bearing_true_mark, 'T'); - ck_assert_int_eq(bearing_magnetic_scale, 0); + ck_assert_int_eq(bearing_magnetic.scale, 0); ck_assert_int_eq(bearing_magnetic_mark, 'M'); - ck_assert_int_eq(distance_scale, 0); + ck_assert_int_eq(distance.scale, 0); ck_assert_int_eq(distance_units, 'N'); ck_assert_str_eq(name, ""); } @@ -357,42 +357,42 @@ START_TEST(test_minmea_scan_complex3) const char *sentence = "$GPGST,024603.00,3.2,6.6,4.7,47.3,5.8,5.6,22.0*58"; char type[6]; struct minmea_time time; - int rms_deviation, rms_deviation_scale; - int semi_major_deviation, semi_major_deviation_scale; - int semi_minor_deviation, semi_minor_deviation_scale; - int semi_major_orientation, semi_major_orientation_scale; - int latitude_error_deviation, latitude_error_deviation_scale; - int longitude_error_deviation, longitude_error_deviation_scale; - int altitude_error_deviation, altitude_error_deviation_scale; + struct minmea_float rms_deviation; + struct minmea_float semi_major_deviation; + struct minmea_float semi_minor_deviation; + struct minmea_float semi_major_orientation; + struct minmea_float latitude_error_deviation; + struct minmea_float longitude_error_deviation; + struct minmea_float altitude_error_deviation; ck_assert(minmea_scan(sentence, "tTfffffff", type, &time, - &rms_deviation, &rms_deviation_scale, - &semi_major_deviation, &semi_major_deviation_scale, - &semi_minor_deviation, &semi_minor_deviation_scale, - &semi_major_orientation, &semi_major_orientation_scale, - &latitude_error_deviation, &latitude_error_deviation_scale, - &longitude_error_deviation, &longitude_error_deviation_scale, - &altitude_error_deviation, &altitude_error_deviation_scale) == true); + &rms_deviation, + &semi_major_deviation, + &semi_minor_deviation, + &semi_major_orientation, + &latitude_error_deviation, + &longitude_error_deviation, + &altitude_error_deviation) == true); ck_assert_str_eq(type, "GPGST"); ck_assert_int_eq(time.hours, 2); ck_assert_int_eq(time.minutes, 46); ck_assert_int_eq(time.seconds, 3); ck_assert_int_eq(time.microseconds, 0); - ck_assert_int_eq(rms_deviation, 32); - ck_assert_int_eq(rms_deviation_scale, 10); - ck_assert_int_eq(semi_major_deviation, 66); - ck_assert_int_eq(semi_major_deviation_scale, 10); - ck_assert_int_eq(semi_minor_deviation, 47); - ck_assert_int_eq(semi_minor_deviation_scale, 10); - ck_assert_int_eq(semi_major_orientation, 473); - ck_assert_int_eq(semi_major_orientation_scale, 10); - ck_assert_int_eq(latitude_error_deviation, 58); - ck_assert_int_eq(latitude_error_deviation_scale, 10); - ck_assert_int_eq(longitude_error_deviation, 56); - ck_assert_int_eq(longitude_error_deviation_scale, 10); - ck_assert_int_eq(altitude_error_deviation, 220); - ck_assert_int_eq(altitude_error_deviation_scale,10); + ck_assert_int_eq(rms_deviation.value, 32); + ck_assert_int_eq(rms_deviation.scale, 10); + ck_assert_int_eq(semi_major_deviation.value, 66); + ck_assert_int_eq(semi_major_deviation.scale, 10); + ck_assert_int_eq(semi_minor_deviation.value, 47); + ck_assert_int_eq(semi_minor_deviation.scale, 10); + ck_assert_int_eq(semi_major_orientation.value, 473); + ck_assert_int_eq(semi_major_orientation.scale, 10); + ck_assert_int_eq(latitude_error_deviation.value, 58); + ck_assert_int_eq(latitude_error_deviation.scale, 10); + ck_assert_int_eq(longitude_error_deviation.value, 56); + ck_assert_int_eq(longitude_error_deviation.scale, 10); + ck_assert_int_eq(altitude_error_deviation.value, 220); + ck_assert_int_eq(altitude_error_deviation.scale, 10); } END_TEST @@ -403,17 +403,12 @@ START_TEST(test_minmea_parse_rmc1) static const struct minmea_sentence_rmc expected = { .time = { 8, 18, 36, 750000 }, .valid = true, - .latitude = -375165, - .latitude_scale = 100, - .longitude = 1450736, - .longitude_scale = 100, - .speed = 0, - .speed_scale = 10, - .course = 3600, - .course_scale = 10, + .latitude = { -375165, 100 }, + .longitude = { 1450736, 100 }, + .speed = { 0, 10 }, + .course = { 3600, 10 }, .date = { 13, 9, 98 }, - .variation = 113, - .variation_scale = 10, + .variation = { 113, 10 }, }; ck_assert(minmea_check(sentence) == true); ck_assert(minmea_parse_rmc(&frame, sentence) == true); @@ -428,10 +423,8 @@ START_TEST(test_minmea_parse_rmc2) static const struct minmea_sentence_rmc expected = { .time = { -1, -1, -1, -1 }, .valid = true, - .latitude = 375165, - .latitude_scale = 100, - .longitude = -1450736, - .longitude_scale = 100, + .latitude = { 375165, 100 }, + .longitude = { -1450736, 100 }, .date = { -1, -1, -1 }, }; ck_assert(minmea_check(sentence) == true); @@ -446,19 +439,14 @@ START_TEST(test_minmea_parse_gga1) struct minmea_sentence_gga frame = {}; static const struct minmea_sentence_gga expected = { .time = { 12, 35, 19, 0 }, - .latitude = 4807038, - .latitude_scale = 1000, - .longitude = 1131000, - .longitude_scale = 1000, + .latitude = { 4807038, 1000 }, + .longitude = { 1131000, 1000 }, .fix_quality = 1, .satellites_tracked = 8, - .hdop = 9, - .hdop_scale = 10, - .altitude = 5454, - .altitude_scale = 10, + .hdop = { 9, 10 }, + .altitude = { 5454, 10 }, .altitude_units = 'M', - .height = 469, - .height_scale = 10, + .height = { 469, 10 }, .height_units = 'M', .dgps_age = 0, }; @@ -474,20 +462,13 @@ START_TEST(test_minmea_parse_gst1) struct minmea_sentence_gst frame = {}; struct minmea_sentence_gst expected = { .time = { 2, 46, 3, 0 }, - .rms_deviation = 32, - .rms_deviation_scale = 10, - .semi_major_deviation = 66, - .semi_major_deviation_scale = 10, - .semi_minor_deviation = 47, - .semi_minor_deviation_scale = 10, - .semi_major_orientation = 473, - .semi_major_orientation_scale = 10, - .latitude_error_deviation = 58, - .latitude_error_deviation_scale = 10, - .longitude_error_deviation = 56, - .longitude_error_deviation_scale = 10, - .altitude_error_deviation = 220, - .altitude_error_deviation_scale = 10, + .rms_deviation = { 32, 10 }, + .semi_major_deviation = { 66, 10 }, + .semi_minor_deviation = { 47, 10 }, + .semi_major_orientation = { 473, 10 }, + .latitude_error_deviation = { 58, 10 }, + .longitude_error_deviation = { 56, 10 }, + .altitude_error_deviation = { 220, 10 }, }; ck_assert(minmea_check(sentence) == true); ck_assert(minmea_parse_gst(&frame, sentence) == true); @@ -503,12 +484,9 @@ START_TEST(test_minmea_parse_gsa1) .mode = MINMEA_GPGSA_MODE_AUTO, .fix_type = MINMEA_GPGSA_FIX_3D, .sats = { 4, 5, 0, 9, 12, 0, 0, 24, 0, 0, 0, 0 }, - .pdop = 25, - .pdop_scale = 10, - .hdop = 13, - .hdop_scale = 10, - .vdop = 21, - .vdop_scale = 10 + .pdop = { 25, 10 }, + .hdop = { 13, 10 }, + .vdop = { 21, 10 }, }; ck_assert(minmea_check(sentence) == true); ck_assert(minmea_parse_gsa(&frame, sentence) == true); @@ -622,36 +600,36 @@ END_TEST START_TEST(test_minmea_rescale) { /* basic and edge cases. */ - ck_assert_int_eq(minmea_rescale(42, 0, 3), 0); - ck_assert_int_eq(minmea_rescale(1234, 10, 1), 123); - ck_assert_int_eq(minmea_rescale(1235, 10, 1), 124); - ck_assert_int_eq(minmea_rescale(1234, 10, 1000), 123400); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { 42, 0 }, 3), 0); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { 1234, 10 }, 1), 123); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { 1235, 10 }, 1), 124); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { 1234, 10 }, 1000), 123400); /* round towards zero. */ - ck_assert_int_eq(minmea_rescale(-1234, 10, 1), -123); - ck_assert_int_eq(minmea_rescale(-1235, 10, 1), -124); - ck_assert_int_eq(minmea_rescale(-1236, 10, 1), -124); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { -1234, 10 }, 1), -123); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { -1235, 10 }, 1), -124); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { -1236, 10 }, 1), -124); /* shouldn't overflow on large numbers. */ - ck_assert_int_eq(minmea_rescale(510693608, 100000, 10000), 51069361); + ck_assert_int_eq(minmea_rescale(&(struct minmea_float) { 510693608, 100000 }, 10000), 51069361); } END_TEST START_TEST(test_minmea_float) { - ck_assert(isnan(minmea_float(42, 0))); - ck_assert(minmea_float(7, 1) == 7.0); - ck_assert(minmea_float(-200, 100) == -2.0); - ck_assert(minmea_float(15, 10) == 1.5); + ck_assert(isnan(minmea_tofloat(&(struct minmea_float) { 42, 0 }))); + ck_assert(minmea_tofloat(&(struct minmea_float) { 7, 1}) == 7.0); + ck_assert(minmea_tofloat(&(struct minmea_float) { -200, 100}) == -2.0); + ck_assert(minmea_tofloat(&(struct minmea_float) { 15, 10}) == 1.5); } END_TEST START_TEST(test_minmea_coord) { - ck_assert(isnan(minmea_coord(42, 0))); - ck_assert(minmea_coord(4200, 1) == 42.0); - ck_assert(minmea_coord(420000, 100) == 42.0); - ck_assert(minmea_coord(423000, 100) == 42.5); + ck_assert(isnan(minmea_tocoord(&(struct minmea_float) { 42, 0 }))); + ck_assert(minmea_tocoord(&(struct minmea_float) { 4200, 1 }) == 42.0); + ck_assert(minmea_tocoord(&(struct minmea_float) { 420000, 100 }) == 42.0); + ck_assert(minmea_tocoord(&(struct minmea_float) { 423000, 100 }) == 42.5); } END_TEST From 870d64d1d5a94834139224f57a8b8c9fe8c29c56 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 16:36:40 +0200 Subject: [PATCH 21/33] example.c: update for the new API --- example.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/example.c b/example.c index e7c90f1..e5b9fbb 100644 --- a/example.c +++ b/example.c @@ -22,17 +22,17 @@ int main() struct minmea_sentence_rmc frame; if (minmea_parse_rmc(&frame, line)) { printf("+++ raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", - frame.latitude, frame.latitude_scale, - frame.longitude, frame.longitude_scale, - frame.speed, frame.speed_scale); + frame.latitude.value, frame.latitude.scale, + frame.longitude.value, frame.longitude.scale, + frame.speed.value, frame.speed.scale); printf("+++ fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", - minmea_rescale(frame.latitude, frame.latitude_scale, 1000), - minmea_rescale(frame.longitude, frame.longitude_scale, 1000), - minmea_rescale(frame.speed, frame.speed_scale, 1000)); + minmea_rescale(&frame.latitude, 1000), + minmea_rescale(&frame.longitude, 1000), + minmea_rescale(&frame.speed, 1000)); printf("+++ floating point degree coordinates and speed: (%f,%f) %f\n", - minmea_coord(frame.latitude, frame.latitude_scale), - minmea_coord(frame.longitude, frame.longitude_scale), - minmea_float(frame.speed, frame.speed_scale)); + minmea_tocoord(&frame.latitude), + minmea_tocoord(&frame.longitude), + minmea_tofloat(&frame.speed)); } } break; @@ -47,18 +47,18 @@ int main() struct minmea_sentence_gst frame; if (minmea_parse_gst(&frame, line)) { printf("+++ raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/%d)\n", - frame.latitude_error_deviation, frame.latitude_error_deviation_scale, - frame.longitude_error_deviation, frame.longitude_error_deviation_scale, - frame.altitude_error_deviation, frame.altitude_error_deviation_scale); + frame.latitude_error_deviation.value, frame.latitude_error_deviation.scale, + frame.longitude_error_deviation.value, frame.longitude_error_deviation.scale, + frame.altitude_error_deviation.value, frame.altitude_error_deviation.scale); printf("+++ fixed point latitude,longitude and altitude error deviation \ scaled to one decimal place: (%d,%d,%d)\n", - minmea_rescale(frame.latitude_error_deviation, frame.latitude_error_deviation_scale,10), - minmea_rescale(frame.longitude_error_deviation, frame.longitude_error_deviation_scale,10), - minmea_rescale(frame.altitude_error_deviation, frame.altitude_error_deviation_scale,10)); - printf("+++ floating point degree latitude,longitude and altitude error deviation: (%f,%f,%f)", - minmea_coord(frame.latitude_error_deviation, frame.latitude_error_deviation_scale), - minmea_coord(frame.longitude_error_deviation, frame.longitude_error_deviation_scale), - minmea_coord(frame.altitude_error_deviation, frame.altitude_error_deviation_scale)); + minmea_rescale(&frame.latitude_error_deviation, 10), + minmea_rescale(&frame.longitude_error_deviation, 10), + minmea_rescale(&frame.altitude_error_deviation, 10)); + printf("+++ floating point degree latitude, longitude and altitude error deviation: (%f,%f,%f)", + minmea_tofloat(&frame.latitude_error_deviation), + minmea_tofloat(&frame.longitude_error_deviation), + minmea_tofloat(&frame.altitude_error_deviation)); } } break; From e510d9912eee4e7efc93981f292dfad0931c6039 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 16:38:06 +0200 Subject: [PATCH 22/33] example.c: fix printed frame names --- example.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/example.c b/example.c index e5b9fbb..68fb873 100644 --- a/example.c +++ b/example.c @@ -21,15 +21,15 @@ int main() case MINMEA_SENTENCE_RMC: { struct minmea_sentence_rmc frame; if (minmea_parse_rmc(&frame, line)) { - printf("+++ raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", + printf("$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", frame.latitude.value, frame.latitude.scale, frame.longitude.value, frame.longitude.scale, frame.speed.value, frame.speed.scale); - printf("+++ fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", + printf("$xxRMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", minmea_rescale(&frame.latitude, 1000), minmea_rescale(&frame.longitude, 1000), minmea_rescale(&frame.speed, 1000)); - printf("+++ floating point degree coordinates and speed: (%f,%f) %f\n", + printf("$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n", minmea_tocoord(&frame.latitude), minmea_tocoord(&frame.longitude), minmea_tofloat(&frame.speed)); @@ -39,23 +39,23 @@ int main() case MINMEA_SENTENCE_GGA: { struct minmea_sentence_gga frame; if (minmea_parse_gga(&frame, line)) { - printf("$GPGGA: fix quality: %d\n", frame.fix_quality); + printf("$xxGGA: fix quality: %d\n", frame.fix_quality); } } break; case MINMEA_SENTENCE_GST: { struct minmea_sentence_gst frame; if (minmea_parse_gst(&frame, line)) { - printf("+++ raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/%d)\n", + printf("$xxGST: raw latitude,longitude and altitude error deviation: (%d/%d,%d/%d,%d/%d)\n", frame.latitude_error_deviation.value, frame.latitude_error_deviation.scale, frame.longitude_error_deviation.value, frame.longitude_error_deviation.scale, frame.altitude_error_deviation.value, frame.altitude_error_deviation.scale); - printf("+++ fixed point latitude,longitude and altitude error deviation \ + printf("$xxGST fixed point latitude,longitude and altitude error deviation \ scaled to one decimal place: (%d,%d,%d)\n", minmea_rescale(&frame.latitude_error_deviation, 10), minmea_rescale(&frame.longitude_error_deviation, 10), minmea_rescale(&frame.altitude_error_deviation, 10)); - printf("+++ floating point degree latitude, longitude and altitude error deviation: (%f,%f,%f)", + printf("$xxGST floating point degree latitude, longitude and altitude error deviation: (%f,%f,%f)", minmea_tofloat(&frame.latitude_error_deviation), minmea_tofloat(&frame.longitude_error_deviation), minmea_tofloat(&frame.altitude_error_deviation)); @@ -65,10 +65,10 @@ int main() case MINMEA_SENTENCE_GSV: { struct minmea_sentence_gsv frame; if (minmea_parse_gsv(&frame, line)) { - printf("$GPGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); - printf("$GPGSV: sattelites in view: %d\n", frame.total_sats); + printf("$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); + printf("$xxGSV: sattelites in view: %d\n", frame.total_sats); for (int i = 0; i < 4; i++) - printf("$GPGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", + printf("$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", frame.sats[i].nr, frame.sats[i].elevation, frame.sats[i].azimuth, From a5f67aa5003b65bd14666ad7c1cc124c036d58ea Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 16:44:56 +0200 Subject: [PATCH 23/33] README.md: reflect the API changes --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 90aee1f..01569b2 100644 --- a/README.md +++ b/README.md @@ -26,22 +26,22 @@ Adding support for more sentences is trivial; see ``minmea.c`` source. ## Fractional number format -Internally, minmea stores fractional numbers as pairs of two integers: ``(value, scale)``. -For example, a value of ``"-123.456"`` would be parsed as ``(-123456, 1000)``. As this -format is quite unwieldy, minmea provides the following convenience macros for converting +Internally, minmea stores fractional numbers as pairs of two integers: ``{value, scale}``. +For example, a value of ``"-123.456"`` would be parsed as ``{-123456, 1000}``. As this +format is quite unwieldy, minmea provides the following convenience functions for converting to either fixed-point or floating-point format: -* ``minmea_rescale(-123456, 1000, 10) => -1235`` -* ``minmea_float(-123456, 1000) => -123.456`` +* ``minmea_rescale({-123456, 1000}, 10) => -1235`` +* ``minmea_float({-123456, 1000}) => -123.456`` ## Coordinate format NMEA uses the clunky ``DDMM.MMMM`` format which, honestly, is not good in the internet era. Internally, minmea stores it as a fractional number (see above); for practical uses, the value should be probably converted to the DD.DDDDD floating point format using the -following macro: +following function: -* ``minmea_coord(-375165, 100) => -37.860832`` +* ``minmea_tocoord({-375165, 100}) => -37.860832`` The library doesn't perform this conversion automatically for the following reasons: @@ -59,35 +59,35 @@ The library doesn't perform this conversion automatically for the following reas case MINMEA_SENTENCE_RMC: { struct minmea_sentence_rmc frame; if (minmea_parse_rmc(&frame, line)) { - printf("+++ raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", - frame.latitude, frame.latitude_scale, - frame.longitude, frame.longitude_scale, - frame.speed, frame.speed_scale); - printf("+++ fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", - minmea_rescale(frame.latitude, frame.latitude_scale, 1000), - minmea_rescale(frame.longitude, frame.longitude_scale, 1000), - minmea_rescale(frame.speed, frame.speed_scale, 1000)); - printf("+++ floating point degree coordinates and speed: (%f,%f) %f\n", - minmea_coord(frame.latitude, frame.latitude_scale), - minmea_coord(frame.longitude, frame.longitude_scale), - minmea_float(frame.speed, frame.speed_scale)); + printf("$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", + frame.latitude.value, frame.latitude.scale, + frame.longitude.value, frame.longitude.scale, + frame.speed.value, frame.speed.scale); + printf("$xxRMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", + minmea_rescale(&frame.latitude, 1000), + minmea_rescale(&frame.longitude, 1000), + minmea_rescale(&frame.speed, 1000)); + printf("$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n", + minmea_tocoord(&frame.latitude), + minmea_tocoord(&frame.longitude), + minmea_tofloat(&frame.speed)); } } break; case MINMEA_SENTENCE_GGA: { struct minmea_sentence_gga frame; if (minmea_parse_gga(&frame, line)) { - printf("$GPGGA: fix quality: %d\n", frame.fix_quality); + printf("$xxGGA: fix quality: %d\n", frame.fix_quality); } } break; case MINMEA_SENTENCE_GSV: { struct minmea_sentence_gsv frame; if (minmea_parse_gsv(&frame, line)) { - printf("$GPGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); - printf("$GPGSV: sattelites in view: %d\n", frame.total_sats); + printf("$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); + printf("$xxGSV: sattelites in view: %d\n", frame.total_sats); for (int i = 0; i < 4; i++) - printf("$GPGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", + printf("$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", frame.sats[i].nr, frame.sats[i].elevation, frame.sats[i].azimuth, From 334e63d599952d58e27b2d62e29181b2c30d51dd Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 17:05:21 +0200 Subject: [PATCH 24/33] tests.c: add minmea_scan("f") overflow tests --- tests.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/tests.c b/tests.c index 7dbafdd..da7683f 100644 --- a/tests.c +++ b/tests.c @@ -126,6 +126,10 @@ START_TEST(test_minmea_scan_f) ck_assert(minmea_scan("", "f", &f) == true); ck_assert_int_eq(f.scale, 0); + ck_assert(minmea_scan("42", "f", &f) == true); + ck_assert_int_eq(f.value, 42); + ck_assert_int_eq(f.scale, 1); + ck_assert(minmea_scan("15.345", "f", &f) == true); ck_assert_int_eq(f.value, 15345); ck_assert_int_eq(f.scale, 1000); @@ -134,13 +138,8 @@ START_TEST(test_minmea_scan_f) ck_assert_int_eq(f.value, -123); ck_assert_int_eq(f.scale, 100); - /* some GPS units have absurdly big precision. handle whatever int handles. */ - ck_assert(minmea_scan("5106.94091", "f", &f) == true); - ck_assert_int_eq(f.value, 510694091); - ck_assert_int_eq(f.scale, 100000); - - /* for now we support +-180 degrees with 5 decimal digits; anything - * more will overflow. */ + /* the guaranteed range is 32 bits which translates to +-180 degrees + * with 5 decimal digits. make sure we support that. */ ck_assert(minmea_scan("18000.00000", "f", &f) == true); ck_assert_int_eq(f.value, 1800000000); ck_assert_int_eq(f.scale, 100000); @@ -148,6 +147,32 @@ START_TEST(test_minmea_scan_f) ck_assert_int_eq(f.value, -1800000000); ck_assert_int_eq(f.scale, 100000); + if (sizeof(int_least32_t) == 4) { + /* fits in 32 bits */ + ck_assert(minmea_scan("2147483647", "f", &f) == true); + ck_assert_int_eq(f.value, 2147483647); + ck_assert_int_eq(f.scale, 1); + /* doesn't fit, truncate precision */ + ck_assert(minmea_scan("2147483.648", "f", &f) == true); + ck_assert_int_eq(f.value, 214748364); + ck_assert_int_eq(f.scale, 100); + /* doesn't fit, bail out */ + ck_assert(minmea_scan("2147483648", "f", &f) == false); + } else if (sizeof(int_least32_t) == 8) { + /* fits in 64 bits */ + ck_assert(minmea_scan("9223372036854775807", "f", &f) == true); + ck_assert_int_eq(f.value, 9223372036854775807); + ck_assert_int_eq(f.scale, 1); + /* doesn't fit, truncate precision */ + ck_assert(minmea_scan("9223372036854775.808", "f", &f) == true); + ck_assert_int_eq(f.value, 922337203685477580); + ck_assert_int_eq(f.scale, 100); + /* doesn't fit, bail out */ + ck_assert(minmea_scan("9223372036854775808", "f", &f) == false); + } else { + ck_abort_msg("your platform is esoteric. please fix this unit test."); + } + /* optional f.values */ ck_assert(minmea_scan("foo", "_;f", &f) == true); ck_assert_int_eq(f.scale, 0); From 1e802f6d00ebb12ea639d8a4254efdac57caba2f Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 17:06:10 +0200 Subject: [PATCH 25/33] fix minmea_scan("f") when no decimal point is present --- minmea.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/minmea.c b/minmea.c index 4d748a5..e5f1f28 100644 --- a/minmea.c +++ b/minmea.c @@ -166,8 +166,12 @@ bool minmea_scan(const char *sentence, const char *format, ...) goto parse_error; if (value == -1) { + /* No digits were scanned. */ value = 0; scale = 0; + } else if (scale == 0) { + /* No decimal point. */ + scale = 1; } if (sign) value *= sign; From 3e3420d1abeca33e86e48e9b4102a3e86a1909d0 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 17:19:29 +0200 Subject: [PATCH 26/33] fix minmea_scan("f") overflow --- minmea.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/minmea.c b/minmea.c index e5f1f28..b40e07f 100644 --- a/minmea.c +++ b/minmea.c @@ -148,9 +148,20 @@ bool minmea_scan(const char *sentence, const char *format, ...) } else if (*field == '-' && !sign && value == -1) { sign = -1; } else if (isdigit((unsigned char) *field)) { + int digit = *field - '0'; if (value == -1) value = 0; - value = (10 * value) + (*field - '0'); + if (value > (INT_LEAST32_MAX-digit) / 10) { + /* we ran out of bits, what do we do? */ + if (scale) { + /* truncate extra precision */ + break; + } else { + /* integer overflow. bail out. */ + goto parse_error; + } + } + value = (10 * value) + digit; if (scale) scale *= 10; } else if (*field == '.' && scale == 0) { From f9f9bed1480c36615d7e7735af13279efa7f0bdc Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 17:30:41 +0200 Subject: [PATCH 27/33] README.md: remove extra indentation in example --- README.md | 82 +++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 01569b2..28dd4bf 100644 --- a/README.md +++ b/README.md @@ -52,50 +52,50 @@ The library doesn't perform this conversion automatically for the following reas ## Example ```c - char line[MINMEA_MAX_LENGTH]; - while (fgets(line, sizeof(line), stdin) != NULL) { - printf("%s", line); - switch (minmea_sentence_id(line)) { - case MINMEA_SENTENCE_RMC: { - struct minmea_sentence_rmc frame; - if (minmea_parse_rmc(&frame, line)) { - printf("$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", - frame.latitude.value, frame.latitude.scale, - frame.longitude.value, frame.longitude.scale, - frame.speed.value, frame.speed.scale); - printf("$xxRMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", - minmea_rescale(&frame.latitude, 1000), - minmea_rescale(&frame.longitude, 1000), - minmea_rescale(&frame.speed, 1000)); - printf("$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n", - minmea_tocoord(&frame.latitude), - minmea_tocoord(&frame.longitude), - minmea_tofloat(&frame.speed)); - } - } break; +char line[MINMEA_MAX_LENGTH]; +while (fgets(line, sizeof(line), stdin) != NULL) { + printf("%s", line); + switch (minmea_sentence_id(line)) { + case MINMEA_SENTENCE_RMC: { + struct minmea_sentence_rmc frame; + if (minmea_parse_rmc(&frame, line)) { + printf("$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", + frame.latitude.value, frame.latitude.scale, + frame.longitude.value, frame.longitude.scale, + frame.speed.value, frame.speed.scale); + printf("$xxRMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", + minmea_rescale(&frame.latitude, 1000), + minmea_rescale(&frame.longitude, 1000), + minmea_rescale(&frame.speed, 1000)); + printf("$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n", + minmea_tocoord(&frame.latitude), + minmea_tocoord(&frame.longitude), + minmea_tofloat(&frame.speed)); + } + } break; - case MINMEA_SENTENCE_GGA: { - struct minmea_sentence_gga frame; - if (minmea_parse_gga(&frame, line)) { - printf("$xxGGA: fix quality: %d\n", frame.fix_quality); - } - } break; + case MINMEA_SENTENCE_GGA: { + struct minmea_sentence_gga frame; + if (minmea_parse_gga(&frame, line)) { + printf("$xxGGA: fix quality: %d\n", frame.fix_quality); + } + } break; - case MINMEA_SENTENCE_GSV: { - struct minmea_sentence_gsv frame; - if (minmea_parse_gsv(&frame, line)) { - printf("$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); - printf("$xxGSV: sattelites in view: %d\n", frame.total_sats); - for (int i = 0; i < 4; i++) - printf("$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", - frame.sats[i].nr, - frame.sats[i].elevation, - frame.sats[i].azimuth, - frame.sats[i].snr); - } - } break; - } + case MINMEA_SENTENCE_GSV: { + struct minmea_sentence_gsv frame; + if (minmea_parse_gsv(&frame, line)) { + printf("$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); + printf("$xxGSV: sattelites in view: %d\n", frame.total_sats); + for (int i = 0; i < 4; i++) + printf("$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", + frame.sats[i].nr, + frame.sats[i].elevation, + frame.sats[i].azimuth, + frame.sats[i].snr); + } + } break; } +} ``` ## Integration with your project From f8729352449e7026eee9dd2d25851ceb4fc99a36 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 17:31:54 +0200 Subject: [PATCH 28/33] README.md: even nicer example --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 28dd4bf..4cf6090 100644 --- a/README.md +++ b/README.md @@ -54,20 +54,19 @@ The library doesn't perform this conversion automatically for the following reas ```c char line[MINMEA_MAX_LENGTH]; while (fgets(line, sizeof(line), stdin) != NULL) { - printf("%s", line); switch (minmea_sentence_id(line)) { case MINMEA_SENTENCE_RMC: { struct minmea_sentence_rmc frame; if (minmea_parse_rmc(&frame, line)) { - printf("$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", + printf("$RMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n", frame.latitude.value, frame.latitude.scale, frame.longitude.value, frame.longitude.scale, frame.speed.value, frame.speed.scale); - printf("$xxRMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", + printf("$RMC fixed-point coordinates and speed scaled to three decimal places: (%d,%d) %d\n", minmea_rescale(&frame.latitude, 1000), minmea_rescale(&frame.longitude, 1000), minmea_rescale(&frame.speed, 1000)); - printf("$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n", + printf("$RMC floating point degree coordinates and speed: (%f,%f) %f\n", minmea_tocoord(&frame.latitude), minmea_tocoord(&frame.longitude), minmea_tofloat(&frame.speed)); @@ -77,17 +76,17 @@ while (fgets(line, sizeof(line), stdin) != NULL) { case MINMEA_SENTENCE_GGA: { struct minmea_sentence_gga frame; if (minmea_parse_gga(&frame, line)) { - printf("$xxGGA: fix quality: %d\n", frame.fix_quality); + printf("$GGA: fix quality: %d\n", frame.fix_quality); } } break; case MINMEA_SENTENCE_GSV: { struct minmea_sentence_gsv frame; if (minmea_parse_gsv(&frame, line)) { - printf("$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); - printf("$xxGSV: sattelites in view: %d\n", frame.total_sats); + printf("$GSV: message %d of %d\n", frame.msg_nr, frame.total_msgs); + printf("$GSV: sattelites in view: %d\n", frame.total_sats); for (int i = 0; i < 4; i++) - printf("$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", + printf("$GSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n", frame.sats[i].nr, frame.sats[i].elevation, frame.sats[i].azimuth, From d19839cc7eac497845794141e3c698e61c6c9283 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Sun, 27 Apr 2014 17:38:49 +0200 Subject: [PATCH 29/33] README.md: precision is no longer a limitation, hooray! --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4cf6090..c8908d8 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ to either fixed-point or floating-point format: * ``minmea_rescale({-123456, 1000}, 10) => -1235`` * ``minmea_float({-123456, 1000}) => -123.456`` +The compound type ``struct minmea_float`` uses ``int_least32_t`` internally. Therefore, +the coordinate precision is guaranteed to be at least ``[+-]DDDMM.MMMMM`` (five decimal digits) +or ±20cm LSB at the equator. + ## Coordinate format NMEA uses the clunky ``DDMM.MMMM`` format which, honestly, is not good in the internet era. @@ -114,10 +118,6 @@ typing ``make``. ## Limitations -* Fractional numbers are represented as ``int`` internally, which is 32 bits on - most embedded platforms. Therefore, the maximum supported coordinate precision - is ``[+-]DDDMM.MMMMM``. The library does not check for integer overflow at the - moment; coordinates with more precision will not parse correctly. * Only a handful of frames is supported right now. * There's no support for omitting parts of the library from building. As a workaround, use the ``-ffunction-sections -Wl,--gc-sections`` linker flags From 2329d55ff67258653791a6fe6f21b2c008124b72 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Wed, 28 May 2014 15:38:23 +0200 Subject: [PATCH 30/33] minmea_scan: avoid shadowing local variables --- minmea.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/minmea.c b/minmea.c index b40e07f..19957ed 100644 --- a/minmea.c +++ b/minmea.c @@ -221,8 +221,8 @@ bool minmea_scan(const char *sentence, const char *format, ...) if (field[0] != '$') goto parse_error; - for (int i=0; i<5; i++) - if (!minmea_isfield(field[1+i])) + for (int f=0; f<5; f++) + if (!minmea_isfield(field[f+i])) goto parse_error; char *buf = va_arg(ap, char *); @@ -237,8 +237,8 @@ bool minmea_scan(const char *sentence, const char *format, ...) if (field && minmea_isfield(*field)) { // Always six digits. - for (int i=0; i<6; i++) - if (!isdigit((unsigned char) field[i])) + for (int f=0; f<6; f++) + if (!isdigit((unsigned char) field[f])) goto parse_error; d = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); @@ -258,8 +258,8 @@ bool minmea_scan(const char *sentence, const char *format, ...) if (field && minmea_isfield(*field)) { // Minimum required: integer time. - for (int i=0; i<6; i++) - if (!isdigit((unsigned char) field[i])) + for (int f=0; f<6; f++) + if (!isdigit((unsigned char) field[f])) goto parse_error; h = strtol((char[]) {field[0], field[1], '\0'}, NULL, 10); From 03d977280711d44e791115a59ffff271f5f9df87 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Wed, 28 May 2014 15:41:42 +0200 Subject: [PATCH 31/33] always run tests before committing, kiddo. --- minmea.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minmea.c b/minmea.c index 19957ed..a005c97 100644 --- a/minmea.c +++ b/minmea.c @@ -222,7 +222,7 @@ bool minmea_scan(const char *sentence, const char *format, ...) if (field[0] != '$') goto parse_error; for (int f=0; f<5; f++) - if (!minmea_isfield(field[f+i])) + if (!minmea_isfield(field[1+f])) goto parse_error; char *buf = va_arg(ap, char *); From c5bc12f8cc303754a04ff6991503f8dc5f70cd7c Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Wed, 28 May 2014 16:06:32 +0200 Subject: [PATCH 32/33] minmea_gettimeofday: workaround missing tm_isdst on some systems --- minmea.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minmea.c b/minmea.c index a005c97..584880c 100644 --- a/minmea.c +++ b/minmea.c @@ -493,15 +493,15 @@ int minmea_gettimeofday(struct timeval *tv, const struct minmea_date *date, cons return -1; struct tm tm; + memset(&tm, 0, sizeof(tm)); tm.tm_year = 2000 + date->year - 1900; tm.tm_mon = date->month - 1; tm.tm_mday = date->day; tm.tm_hour = time->hours; tm.tm_min = time->minutes; tm.tm_sec = time->seconds; - tm.tm_isdst = 0; - time_t timestamp = timegm(&tm); + time_t timestamp = timegm(&tm); if (timestamp != -1) { tv->tv_sec = timestamp; tv->tv_usec = time->microseconds; From df695c4933cfe807c4889dce30a27800e2e2dc85 Mon Sep 17 00:00:00 2001 From: Kosma Moczek Date: Tue, 17 Jun 2014 12:22:48 +0200 Subject: [PATCH 33/33] tests: prevent compiler warning when comparing floats --- tests.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests.c b/tests.c index da7683f..6ee4228 100644 --- a/tests.c +++ b/tests.c @@ -640,6 +640,11 @@ START_TEST(test_minmea_rescale) } END_TEST +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +/* The float values used in tests should be exactly representable under IEEE754; + * false negatives will occur otherwise. */ + START_TEST(test_minmea_float) { ck_assert(isnan(minmea_tofloat(&(struct minmea_float) { 42, 0 }))); @@ -658,6 +663,8 @@ START_TEST(test_minmea_coord) } END_TEST +#pragma GCC diagnostic pop + Suite *minmea_suite(void) { Suite *s = suite_create ("minmea");