From 59935603ccb54f12758672f2c077109523aeeaa7 Mon Sep 17 00:00:00 2001 From: "Gary E. Miller" Date: Fri, 22 Feb 2019 18:17:57 -0800 Subject: deg_to_str2(): Add new function. Thread safe, handles suffix. --- doc/explan_gpsdclient.c.xml | 18 +++++++ gpsdclient.c | 86 +++++++++++++++++++++++-------- gpsdclient.h | 5 +- tests/test_gpsdclient.c | 120 +++++++++++++++++++++++++++++++++----------- 4 files changed, 179 insertions(+), 50 deletions(-) diff --git a/doc/explan_gpsdclient.c.xml b/doc/explan_gpsdclient.c.xml index babd2499..4825fd3d 100644 --- a/doc/explan_gpsdclient.c.xml +++ b/doc/explan_gpsdclient.c.xml @@ -32,6 +32,24 @@ Warning: not thread safe. + + + char *deg_to_str2(enum deg_str_type type, double f, char *buf, + unsigned int buf_size, const char *suffix_pos, const char *suffix_neg) + + + + Convert the absolute value of double degrees to a string + and place in the buffer "buf". Return a pointer to the buffer. + "buf_size" is the size of the buffer. + Makes a simple check on invalid degree values (not more + than 360) and returns "nan" on error. + For valid values, it generates the appropriate string according + to the string type enumeration: dd, ddmm or ddmmss. If the + degrees are positive, append "suffix_pos", else append "suffix_neg". + + + enum unit gpsd_units(void) Simple check of the environment to determine what units are required. If all else fails, use compiled in units. diff --git a/gpsdclient.c b/gpsdclient.c index b97acc03..8b729d85 100644 --- a/gpsdclient.c +++ b/gpsdclient.c @@ -28,34 +28,52 @@ static struct exportmethod_t exportmethods[] = { #endif /* SOCKET_EXPORT_ENABLE */ }; -/* convert double degrees to a static string and return a pointer to it - * WARNING: not thread safe! +/* convert value of double degrees to a buffer. + * add suffix_pos or suffix_neg depending on sign. + * buffer should be at least 20 bytes. + * Return a pointer to the buffer. * * deg_str_type: - * deg_dd : return DD.ddddddd - * deg_ddmm : return DD MM.mmmmmm' - * deg_ddmmss : return DD MM' SS.sssss" + * deg_dd : return DD.ddddddd[suffix] + * deg_ddmm : return DD MM.mmmmmm'[suffix] + * deg_ddmmss : return DD MM' SS.sssss"[suffix] * - * returns 'nan' for 0 > f or 360 < f + * returns 'nan' for 360 < f or -360 > f * - * NOTE: degrees must be positive. - * 360.0 is rolled over to 0.0 + * NOTE: 360.0 is rolled over to 0.0 * * for cm level accuracy, at sea level, we need degrees * to 7+ decimal places * Ref: https://en.wikipedia.org/wiki/Decimal_degrees * */ -char *deg_to_str(enum deg_str_type type, double f) +char *deg_to_str2(enum deg_str_type type, double f, + char *buf, unsigned int buf_size, + const char *suffix_pos, const char *suffix_neg) + { - static char str[40]; int dsec, sec, deg, min; double fdsec, fsec, fdeg, fmin; + const char *suffix = ""; + + if (20 > buf_size) { + (void)strlcpy(buf, "Err", buf_size); + return buf; + } - f = fabs(f); - if (!isfinite(f) || 360.0 < f) { - (void)strlcpy(str, "nan", sizeof(str)); - return str; + if (!isfinite(f) || 360.0 < fabs(f)) { + (void)strlcpy(buf, "nan", buf_size); + return buf; + } + + /* suffix? */ + if (0.0 > f) { + f = -f; + if (NULL != suffix_neg) { + suffix = suffix_neg; + } + } else if (NULL != suffix_pos) { + suffix = suffix_pos; } /* add rounding quanta */ @@ -91,8 +109,8 @@ char *deg_to_str(enum deg_str_type type, double f) /* DD.dddddddd */ long frac_deg = (long)(fmin * 100000000.0); /* cm level accuracy requires the %08ld */ - (void)snprintf(str, sizeof(str), "%3d.%08ld", deg, frac_deg); - return str; + (void)snprintf(buf, buf_size, "%3d.%08ld%s", deg, frac_deg, suffix); + return buf; } fsec = modf(fmin * 60, &fmin); @@ -101,17 +119,43 @@ char *deg_to_str(enum deg_str_type type, double f) if (deg_ddmm == type) { /* DD MM.mmmmmm */ sec = (int)(fsec * 1000000.0); - (void)snprintf(str, sizeof(str), "%3d %02d.%06d'", deg, min, sec); - return str; + (void)snprintf(buf, buf_size, "%3d %02d.%06d'%s", deg, min, sec, + suffix); + return buf; } /* else DD MM SS.sss */ fdsec = modf(fsec * 60.0, &fsec); sec = (int)fsec; dsec = (int)(fdsec * 100000.0); - (void)snprintf(str, sizeof(str), "%3d %02d' %02d.%05d\"", deg, min, sec, - dsec); + (void)snprintf(buf, buf_size, "%3d %02d' %02d.%05d\"%s", deg, min, sec, + dsec, suffix); + + return buf; +} + +/* convert absolute value of double degrees to a static string. + * Return a pointer to the static string. + * WARNING: Not thread safe. + * + * deg_str_type: + * deg_dd : return DD.ddddddd + * deg_ddmm : return DD MM.mmmmmm' + * deg_ddmmss : return DD MM' SS.sssss" + * + * returns 'nan' for 360 < f + * + * NOTE: 360.0 is rolled over to 0.0 + * + * for cm level accuracy, at sea level, we need degrees + * to 7+ decimal places + * Ref: https://en.wikipedia.org/wiki/Decimal_degrees + * + */ +char *deg_to_str(enum deg_str_type type, double f) +{ + static char buf[20]; - return str; + return deg_to_str2(type, f, buf, sizeof(buf), "", ""); } /* diff --git a/gpsdclient.h b/gpsdclient.h index e4e5dae2..e1066611 100644 --- a/gpsdclient.h +++ b/gpsdclient.h @@ -34,8 +34,11 @@ enum unit gpsd_units(void); enum deg_str_type { deg_dd, deg_ddmm, deg_ddmmss }; float true2magnetic(double, double, double); -/* Warning: not thread safe */ +/* Warning: deg_to_str() not thread safe */ extern char *deg_to_str(enum deg_str_type type, double f); +extern char *deg_to_str2(enum deg_str_type type, double f, + char *buf, unsigned int buf_size, + const char *suffix_pos, const char *suffix_neg); extern void gpsd_source_spec(const char *fromstring, struct fixsource_t *source); diff --git a/tests/test_gpsdclient.c b/tests/test_gpsdclient.c index f4c93379..deda6d2c 100644 --- a/tests/test_gpsdclient.c +++ b/tests/test_gpsdclient.c @@ -20,8 +20,11 @@ struct test { double deg; char dd[20]; + char dd2[20]; char ddmm[20]; + char ddmm2[20]; char ddmmss[20]; + char ddmmss2[20]; }; #define NANFLAG 9999 @@ -29,70 +32,109 @@ struct test { struct test tests[] = { /* 1.999999995 sec */ {(1.999999995), - " 2.00000000", /* rounded up */ - " 2 00.000000'", /* rounded up */ - " 1 59' 59.99998\""}, + " 2.00000000", /* rounded up */ + " 2.00000000 E", /* rounded up */ + " 2 00.000000'", /* rounded up */ + " 2 00.000000' E", /* rounded up */ + " 1 59' 59.99998\"", + " 1 59' 59.99998\" N"}, /* 3.999999999 sec */ {(3.999999994), - " 3.99999999", /* not rounded up */ - " 4 00.000000'", /* rounded up */ - " 3 59' 59.99998\""}, + " 3.99999999", /* not rounded up */ + " 3.99999999 E", /* not rounded up */ + " 4 00.000000'", /* rounded up */ + " 4 00.000000' E", /* rounded up */ + " 3 59' 59.99998\"", + " 3 59' 59.99998\" N"}, /* 5 degree, 1.99999960 arcmin */ {(5.0 + 1.999999600/60.0), " 5.03333333", - " 5 02.000000'", /* rounded up */ - " 5 01' 59.99998\""}, + " 5.03333333 E", + " 5 02.000000'", /* rounded up */ + " 5 02.000000' E", /* rounded up */ + " 5 01' 59.99998\"", + " 5 01' 59.99998\" N"}, /* 6 degree, 1.99999940 arcmin */ {(6.0 + 1.999999400/60.0), " 6.03333332", - " 6 01.999999'", /* not rounded up */ - " 6 01' 59.99996\""}, + " 6.03333332 E", + " 6 01.999999'", /* not rounded up */ + " 6 01.999999' E", /* not rounded up */ + " 6 01' 59.99996\"", + " 6 01' 59.99996\" N"}, /* 7 degree, 59.99999960 arcmin */ {(7.0 + 59.999999600/60.0), " 7.99999999", - " 8 00.000000'", /* rounded up */ - " 7 59' 59.99998\""}, + " 7.99999999 E", + " 8 00.000000'", /* rounded up */ + " 8 00.000000' E", /* rounded up */ + " 7 59' 59.99998\"", + " 7 59' 59.99998\" N"}, /* 9 degree, 59.99999940 arcmin */ {(9.0 + 59.999999400/60.0), " 9.99999999", - " 9 59.999999'", /* not rounded up */ - " 9 59' 59.99996\""}, + " 9.99999999 E", + " 9 59.999999'", /* not rounded up */ + " 9 59.999999' E", /* not rounded up */ + " 9 59' 59.99996\"", + " 9 59' 59.99996\" N"}, /* 11 degree, 1 arcminute, 1.99999600 arcsec */ {(11.0 + 1.0/60.0 + 1.99999600/3600.0), " 11.01722222", + " 11.01722222 E", " 11 01.033333'", - " 11 01' 02.00000\""}, /* rounded up */ + " 11 01.033333' E", + " 11 01' 02.00000\"", /* rounded up */ + " 11 01' 02.00000\" N"}, /* rounded up */ /* 12 deg, 2 min, 2.99999400 sec */ {(12.0 + 2.0/60.0 + 2.99999400/3600.0), " 12.03416667", + " 12.03416667 E", " 12 02.050000'", - " 12 02' 02.99999\""}, /* not rounded up */ + " 12 02.050000' E", + " 12 02' 02.99999\"", /* not rounded up */ + " 12 02' 02.99999\" N"}, /* not rounded up */ /* -44.99999999999 */ /* fabs() */ {-44.0, " 44.00000000", + " 44.00000000 W", " 44 00.000000'", - " 44 00' 00.00000\""}, + " 44 00.000000' W", + " 44 00' 00.00000\"", + " 44 00' 00.00000\" S"}, /* 359.99999999999 */ {359.99999999999, - " 0.00000000", /* rounded up, and rolled over */ + " 0.00000000", /* rounded up, and rolled over */ + " 0.00000000 E", /* rounded up, and rolled over */ " 0 00.000000'", - " 0 00' 00.00000\""}, + " 0 00.000000' E", + " 0 00' 00.00000\"", + " 0 00' 00.00000\" N"}, /* 361 */ /* nan because out of range */ {361, + "nan", + "nan", + "nan", "nan", "nan", "nan"}, /* -361 */ /* nan, just because */ {NANFLAG, + "nan", + "nan", + "nan", "nan", "nan", "nan"}, /* FP_INFINITE */ /* gcc too 'smart' to let us put a Nan here */ {9999, + "nan", + "nan", + "nan", "nan", "nan", "nan"}, @@ -101,6 +143,7 @@ struct test tests[] = { int main(int argc, char **argv) { + char buf[20]; char *s; unsigned int i; int verbose = 0; @@ -133,30 +176,51 @@ int main(int argc, char **argv) /* make it a NaN */ tests[i].deg = nan("a"); } - s = deg_to_str (deg_dd, tests[i].deg); + s = deg_to_str(deg_dd, tests[i].deg); if (0 != strcmp(s, tests[i].dd)) { printf("ERROR: %s s/b %s\n", s, tests[i].dd); fail_count++; - } - if (0 < verbose) { + } else if (0 < verbose) { printf("%s s/b %s\n", s, tests[i].dd); } - s = deg_to_str (deg_ddmm, tests[i].deg); + s = deg_to_str2(deg_dd, tests[i].deg, buf, + sizeof(buf), " E", " W"); + if (0 != strcmp(s, tests[i].dd2)) { + printf("ERROR: %s s/b %s\n", s, tests[i].dd2); + fail_count++; + } else if (0 < verbose) { + printf("%s s/b %s\n", s, tests[i].dd2); + } + s = deg_to_str(deg_ddmm, tests[i].deg); if (0 != strcmp(s, tests[i].ddmm)) { printf("ERROR: %s s/b %s\n", s, tests[i].ddmm); fail_count++; - } - if (0 < verbose) { + } else if (0 < verbose) { printf("%s s/b %s\n", s, tests[i].ddmm); } - s = deg_to_str (deg_ddmmss, tests[i].deg); + s = deg_to_str2(deg_ddmm, tests[i].deg, buf, + sizeof(buf), " E", " W"); + if (0 != strcmp(s, tests[i].ddmm2)) { + printf("ERROR: %s s/b %s\n", s, tests[i].ddmm2); + fail_count++; + } else if (0 < verbose) { + printf("%s s/b %s\n", s, tests[i].ddmm2); + } + s = deg_to_str(deg_ddmmss, tests[i].deg); if (0 != strcmp(s, tests[i].ddmmss)) { printf("ERROR: %s s/b %s\n", s, tests[i].ddmmss); fail_count++; - } - if (0 < verbose) { + } else if (0 < verbose) { printf("%s s/b %s\n", s, tests[i].ddmmss); } + s = deg_to_str2(deg_ddmmss, tests[i].deg, buf, + sizeof(buf), " N", " S"); + if (0 != strcmp(s, tests[i].ddmmss2)) { + printf("ERROR: %s s/b %s\n", s, tests[i].ddmmss2); + fail_count++; + } else if (0 < verbose) { + printf("%s s/b %s\n", s, tests[i].ddmmss2); + } } exit(fail_count); -- cgit v1.2.1