From b0e1c934d4947f8410d561a1fa13f52fd8d4c84c Mon Sep 17 00:00:00 2001 From: Dominic Clifton Date: Sat, 24 May 2014 17:54:57 +0100 Subject: [PATCH] Adding unit test for some gps conversion code to demystify it. --- Makefile | 1 + src/gps_common.c | 42 +-------------------------- src/gps_conversion.c | 46 ++++++++++++++++++++++++++++++ src/gps_conversion.h | 1 + test/Makefile | 15 ++++++++-- test/gps_conversion_unittest.cc | 50 +++++++++++++++++++++++++++++++++ 6 files changed, 112 insertions(+), 43 deletions(-) create mode 100644 src/gps_conversion.c create mode 100644 src/gps_conversion.h create mode 100644 test/gps_conversion_unittest.cc diff --git a/Makefile b/Makefile index 828402bc8..68d47c857 100644 --- a/Makefile +++ b/Makefile @@ -116,6 +116,7 @@ COMMON_SRC = build_config.c \ flight_imu.c \ flight_mixer.c \ gps_common.c \ + gps_conversion.c \ runtime_config.c \ rc_controls.c \ rc_curves.c \ diff --git a/src/gps_common.c b/src/gps_common.c index 3d913693a..3b63e7725 100644 --- a/src/gps_common.c +++ b/src/gps_common.c @@ -25,6 +25,7 @@ #include "config.h" #include "runtime_config.h" +#include "gps_conversion.h" #include "gps_common.h" extern int16_t debug[4]; @@ -925,47 +926,6 @@ static uint32_t GPS_coord_to_degrees(char *s) } */ -#define DIGIT_TO_VAL(_x) (_x - '0') -uint32_t GPS_coord_to_degrees(char* s) -{ - char *p, *q; - uint8_t deg = 0, min = 0; - unsigned int frac_min = 0; - int i; - - // scan for decimal point or end of field - for (p = s; isdigit((unsigned char)*p); p++) { - if (p >= s + 15) - return 0; // stop potential fail - } - q = s; - - // convert degrees - while ((p - q) > 2) { - if (deg) - deg *= 10; - deg += DIGIT_TO_VAL(*q++); - } - // convert minutes - while (p > q) { - if (min) - min *= 10; - min += DIGIT_TO_VAL(*q++); - } - // convert fractional minutes - // expect up to four digits, result is in - // ten-thousandths of a minute - if (*p == '.') { - q = p + 1; - for (i = 0; i < 4; i++) { - frac_min *= 10; - if (isdigit((unsigned char)*q)) - frac_min += *q++ - '0'; - } - } - return deg * 10000000UL + (min * 1000000UL + frac_min * 100UL) / 6; -} - // helper functions static uint32_t grab_fields(char *src, uint8_t mult) { // convert string to uint32 diff --git a/src/gps_conversion.c b/src/gps_conversion.c new file mode 100644 index 000000000..fd52d38aa --- /dev/null +++ b/src/gps_conversion.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + + +#define DIGIT_TO_VAL(_x) (_x - '0') +uint32_t GPS_coord_to_degrees(char* coordinateString) +{ + char *fieldSeparator, *remainingString; + uint8_t degress = 0, minutes = 0; + uint16_t fractionalMinutes = 0; + uint8_t digitIndex; + + // scan for decimal point or end of field + for (fieldSeparator = coordinateString; isdigit((unsigned char)*fieldSeparator); fieldSeparator++) { + if (fieldSeparator >= coordinateString + 15) + return 0; // stop potential fail + } + remainingString = coordinateString; + + // convert degrees + while ((fieldSeparator - remainingString) > 2) { + if (degress) + degress *= 10; + degress += DIGIT_TO_VAL(*remainingString++); + } + // convert minutes + while (fieldSeparator > remainingString) { + if (minutes) + minutes *= 10; + minutes += DIGIT_TO_VAL(*remainingString++); + } + // convert fractional minutes + // expect up to four digits, result is in + // ten-thousandths of a minute + if (*fieldSeparator == '.') { + remainingString = fieldSeparator + 1; + for (digitIndex = 0; digitIndex < 4; digitIndex++) { + fractionalMinutes *= 10; + if (isdigit((unsigned char)*remainingString)) + fractionalMinutes += *remainingString++ - '0'; + } + } + return degress * 10000000UL + (minutes * 1000000UL + fractionalMinutes * 100UL) / 6; +} diff --git a/src/gps_conversion.h b/src/gps_conversion.h new file mode 100644 index 000000000..6c7c09017 --- /dev/null +++ b/src/gps_conversion.h @@ -0,0 +1 @@ +uint32_t GPS_coord_to_degrees(char* s); diff --git a/test/Makefile b/test/Makefile index 197a801ab..64b8bd619 100644 --- a/test/Makefile +++ b/test/Makefile @@ -26,11 +26,11 @@ TEST_DIR = . CPPFLAGS += -isystem $(GTEST_DIR)/inc # Flags passed to the C++ compiler. -CXXFLAGS += -g -Wall -Wextra -pthread +CXXFLAGS += -g -Wall -Wextra -pthread -ggdb -O0 # All tests produced by this Makefile. Remember to add new tests you # created to the list. -TESTS = battery_unittest flight_imu_unittest +TESTS = battery_unittest flight_imu_unittest gps_conversion_unittest # All Google Test headers. Usually you shouldn't change this # definition. @@ -96,3 +96,14 @@ flight_imu_unittest.o : $(TEST_DIR)/flight_imu_unittest.cc \ flight_imu_unittest : flight_imu.o flight_imu_unittest.o gtest_main.a $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + +gps_conversion.o : $(USER_DIR)/gps_conversion.c $(USER_DIR)/gps_conversion.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/gps_conversion.c -I$(TEST_DIR) + +gps_conversion_unittest.o : $(TEST_DIR)/gps_conversion_unittest.cc \ + $(USER_DIR)/gps_conversion.h $(GTEST_HEADERS) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(TEST_DIR)/gps_conversion_unittest.cc -I$(USER_DIR) + +gps_conversion_unittest : gps_conversion.o gps_conversion_unittest.o gtest_main.a + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@ + diff --git a/test/gps_conversion_unittest.cc b/test/gps_conversion_unittest.cc new file mode 100644 index 000000000..4c3d3d53f --- /dev/null +++ b/test/gps_conversion_unittest.cc @@ -0,0 +1,50 @@ +#include + +#include +#include "gps_conversion.h" + +#include "unittest_macros.h" +#include "gtest/gtest.h" + +// See http://en.wikipedia.org/wiki/Geographic_coordinate_conversion + +TEST(GpsConversionTest, GPSCoordToDegrees_BadString) +{ + // expect + uint32_t result = GPS_coord_to_degrees("diediedie"); + EXPECT_EQ(result, 0); +} + +typedef struct gpsConversionExpectation_s { + char *coord; + uint32_t degrees; +} gpsConversionExpectation_t; + +TEST(GpsConversionTest, GPSCoordToDegrees_NMEA_Values) +{ + gpsConversionExpectation_t gpsConversionExpectations[] = { + {"0.0", 0}, + {"000.0", 0}, + {"00000.0000", 0}, + {"0.0001", 16}, // smallest value + {"25599.9999", 2566666650}, // largest value + {"25599.99999", 2566666650}, // too many fractional digits + {"25699.9999", 16666650}, // overflowed without detection + {"5321.6802", 533613366}, + {"00630.3372", 65056200}, + }; + + // expect + + uint8_t testIterationCount = sizeof(gpsConversionExpectations) / sizeof(gpsConversionExpectation_t); + + // expect + + for (uint8_t index = 0; index < testIterationCount; index ++) { + gpsConversionExpectation_t *expectation = &gpsConversionExpectations[index]; + printf("iteration: %d\n", index); + + uint32_t result = GPS_coord_to_degrees(expectation->coord); + EXPECT_EQ(result, expectation->degrees); + } +}