From 1db29a3fa49eca5929ba03346eac7cd49f58e86d Mon Sep 17 00:00:00 2001 From: "Gary E. Miller" Date: Fri, 22 Feb 2019 14:59:37 -0800 Subject: deg_to_s(): Add rounding for dd, ddmm, and ddmmss. Update tests. --- gpsdclient.c | 37 +++++++++++++++++++++++--- tests/test_gpsdclient.c | 70 +++++++++++++++++++++++++++++++------------------ 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/gpsdclient.c b/gpsdclient.c index c34ea06c..3705f6f2 100644 --- a/gpsdclient.c +++ b/gpsdclient.c @@ -29,6 +29,7 @@ static struct exportmethod_t exportmethods[] = { }; /* convert double degrees to a static string and return a pointer to it + * WARNING: not thread safe! * * deg_str_type: * deg_dd : return DD.ddddddd @@ -38,8 +39,10 @@ static struct exportmethod_t exportmethods[] = { * returns 'nan' for 0 > f or 360 < f * * NOTE: degrees must be positive. + * 360.0 is rolled over to 0.0 * - * for cm level accuracy we need degrees to 7+ decimal places + * for cm level accuracy, at sea level, we need degrees + * to 7+ decimal places * Ref: https://en.wikipedia.org/wiki/Decimal_degrees * */ @@ -54,17 +57,43 @@ char *deg_to_str(enum deg_str_type type, double f) return str; } - /* FIXME: s/b rounding */ + /* add rounding quanta */ + /* IEEE 754 wants round to nearest even. + * We cheat and just round to nearest. + * Intel trying to kill off round to nearest even. */ + switch (type) { + default: + /* huh? */ + type = deg_dd; + /* FALLTHROUGH */ + case deg_dd: + /* DD.dddddddd */ + f += 0.5 * 1e-8; /* round up */ + break; + case deg_ddmm: + /* DD MM.mmmmmm */ + f += (0.5 * 1e-6) / 60; /* round up */ + break; + case deg_ddmmss: + f += (0.5 * 1e-5) / 3600; /* round up */ + break; + } fmin = modf(f, &fdeg); deg = (int)fdeg; + if (360 == deg) { + /* fix round-up roll-over */ + deg = 0; + fmin = 0.0; + } if (deg_dd == type) { /* DD.dddddddd */ + long frac_deg = (long)(fmin * 100000000.0); /* cm level accuracy requires the %08ld */ - long frac_deg = (long)(fmin * 100000000); (void)snprintf(str, sizeof(str), "%3d.%08ld", deg, frac_deg); return str; } + fsec = modf(fmin * 60, &fmin); min = (int)fmin; @@ -75,7 +104,7 @@ char *deg_to_str(enum deg_str_type type, double f) return str; } /* else DD MM SS.sss */ - fdsec = modf(fsec * 60, &fsec); + 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, diff --git a/tests/test_gpsdclient.c b/tests/test_gpsdclient.c index 6f81633f..c65b35f6 100644 --- a/tests/test_gpsdclient.c +++ b/tests/test_gpsdclient.c @@ -24,39 +24,57 @@ struct test { }; struct test tests[] = { - /* 1 degree, 1 arcminute, 1.999 arcsec */ - {(1.0 + 1.0/60.0 + 1.999/3600.0), - " 1.01722194", - " 1 01.033316'", - " 1 01' 01.99899\""}, - /* 1 deg, 2 min, 2.0999 sec */ - {(1.0 + 2.0/60.0 + 2.999/3600.0), - " 1.03416638", - " 1 02.049983'", - " 1 02' 02.99900\""}, - /* 44.99999994, should not be rounded up */ - {44.99999994, - " 44.99999994", - " 44 59.999996'", - " 44 59' 59.99978\""}, - /* 44.99999999999 */ - /* FIXME: s/b rounded */ - {44.99999999999, - " 44.99999999", - " 44 59.999999'", - " 44 59' 59.99999\""}, + /* 1.999999995 sec */ + {(1.999999995), + " 2.00000000", /* rounded up */ + " 2 00.000000'", /* rounded up */ + " 1 59' 59.99998\""}, + /* 3.999999999 sec */ + {(3.999999994), + " 3.99999999", /* not rounded up */ + " 4 00.000000'", /* rounded up */ + " 3 59' 59.99998\""}, + /* 5 degree, 1.99999960 arcmin */ + {(5.0 + 1.999999600/60.0), + " 5.03333333", + " 5 02.000000'", /* rounded up */ + " 5 01' 59.99998\""}, + /* 6 degree, 1.99999940 arcmin */ + {(6.0 + 1.999999400/60.0), + " 6.03333332", + " 6 01.999999'", /* not rounded up */ + " 6 01' 59.99996\""}, + /* 7 degree, 59.99999960 arcmin */ + {(7.0 + 59.999999600/60.0), + " 7.99999999", + " 8 00.000000'", /* rounded up */ + " 7 59' 59.99998\""}, + /* 9 degree, 59.99999940 arcmin */ + {(9.0 + 59.999999400/60.0), + " 9.99999999", + " 9 59.999999'", /* not rounded up */ + " 9 59' 59.99996\""}, + /* 11 degree, 1 arcminute, 1.99999600 arcsec */ + {(11.0 + 1.0/60.0 + 1.99999600/3600.0), + " 11.01722222", + " 11 01.033333'", + " 11 01' 02.00000\""}, /* rounded up */ + /* 12 deg, 2 min, 2.99999400 sec */ + {(12.0 + 2.0/60.0 + 2.99999400/3600.0), + " 12.03416667", + " 12 02.050000'", + " 12 02' 02.99999\""}, /* not rounded up */ /* -44.99999999999 */ - /* FIXME: should not be nan? */ + /* nan because not positive degrees */ {-44.99999999999, "nan", "nan", "nan"}, /* 359.99999999999 */ - /* FIXME: s/b rounded */ {359.99999999999, - "359.99999999", - "359 59.999999'", - "359 59' 59.99999\""}, + " 0.00000000", /* rounded up, and rolled over */ + " 0 00.000000'", + " 0 00' 00.00000\""}, }; -- cgit v1.2.1