mirror of
https://github.com/eclipse/upm.git
synced 2025-07-01 17:31:13 +03:00
nmea_gps: Update to handle parsing into structures
NMEA GPS class now uses threads to handle parsing NMEA sentences. Currently only handles GGA, GSV, and GLL types. Added queues for holding parsed data. * Parsing thread * Parse GPS Fix from GGA and GLL * Parse satellite info from GSV * Provide a queue for GPS fix data as well as raw sentences (for debugging) * Target structures and NMEAGPS class have a __str__() method which summarizes the objects into a std::string. These are tied to the __str__ method in python * Added google unit test for the above functionality Signed-off-by: Noel Eck <noel.eck@intel.com>
This commit is contained in:
@ -18,6 +18,11 @@ target_link_libraries(json_tests GTest::GTest GTest::Main)
|
||||
target_include_directories(json_tests PRIVATE "${UPM_COMMON_HEADER_DIRS}/")
|
||||
gtest_add_tests(json_tests "" AUTO)
|
||||
|
||||
# Unit tests - nmea_gps library
|
||||
add_executable(nmea_gps_tests nmea_gps/nmea_gps_tests.cxx)
|
||||
target_link_libraries(nmea_gps_tests nmea_gps GTest::GTest GTest::Main)
|
||||
gtest_add_tests(nmea_gps_tests "" AUTO)
|
||||
|
||||
# Add a custom target for unit tests
|
||||
add_custom_target(tests-unit ALL
|
||||
DEPENDS
|
||||
|
246
tests/unit/nmea_gps/nmea_gps_tests.cxx
Normal file
246
tests/unit/nmea_gps/nmea_gps_tests.cxx
Normal file
@ -0,0 +1,246 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "nmea_gps.hpp"
|
||||
#include "mraa.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
|
||||
/* NMEA GPS test fixture */
|
||||
class nmea_gps_unit : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
/* One-time setup logic if needed */
|
||||
nmea_gps_unit() = default;
|
||||
|
||||
/* One-time tear-down logic if needed */
|
||||
~nmea_gps_unit() override = default;
|
||||
|
||||
/* Fail if not MOCK platform */
|
||||
void SetUp() override
|
||||
{
|
||||
ASSERT_EQ(mraa::getPlatformType(), mraa::MOCK_PLATFORM) <<
|
||||
"NMEA_GPS test requires mraa compiled with mraa::MOCK_PLATFORM";
|
||||
}
|
||||
|
||||
/* Per-test tear-down logic if needed */
|
||||
void TearDown() override {}
|
||||
};
|
||||
|
||||
/* Basic tests */
|
||||
TEST_F(nmea_gps_unit, DOA)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
|
||||
/* Min queue size is 1 */
|
||||
gps.setMaxQueueDepth(0);
|
||||
ASSERT_EQ(gps.getMaxQueueDepth(), 1);
|
||||
|
||||
/* Max queue size is 1000 */
|
||||
gps.setMaxQueueDepth(1001);
|
||||
ASSERT_EQ(gps.getMaxQueueDepth(), 1000);
|
||||
|
||||
/* Queues should be empty */
|
||||
ASSERT_EQ(gps.fixQueueSize(), 0);
|
||||
ASSERT_EQ(gps.rawSentenceQueueSize(), 0);
|
||||
}
|
||||
|
||||
/* Check parsing varying length sentences */
|
||||
TEST_F(nmea_gps_unit, parse_max_size)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
|
||||
/* Parse an sentence that is too long to be NMEA */
|
||||
gps.parseNMEASentence("$GPGSV,2,1,08,07,64,079,,08,39,066,,09,25,159,,09,25,159,,09,25,159,,09,25,159,,09,25,159,,09,25,159,,09,25,159,,07,64,079,*73");
|
||||
ASSERT_EQ(gps.rawSentenceQueueSize(), 1);
|
||||
ASSERT_EQ(gps.satellites().size(), 0);
|
||||
}
|
||||
|
||||
/* Basic test */
|
||||
TEST_F(nmea_gps_unit, parse_basic)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
|
||||
/* Parse an invalid sentence */
|
||||
gps.parseNMEASentence("$GPGGA,182333.50,,,,,0,00,99.99,,,,,,*6B");
|
||||
}
|
||||
|
||||
/* Parse an invalid sentence */
|
||||
TEST_F(nmea_gps_unit, parse_gps_fix_invalid)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
|
||||
/* Parse an invalid sentence */
|
||||
gps.parseNMEASentence("$GPGGA,182333.50,,,,,0,00,99.99,,,,,,*6B");
|
||||
|
||||
/* Should be 1 entry in the raw queue */
|
||||
ASSERT_EQ(gps.rawSentenceQueueSize(), 1);
|
||||
/* Should be 0 in GPS fix queue */
|
||||
ASSERT_EQ(gps.fixQueueSize(), 0);
|
||||
|
||||
/* Get the GPS fix */
|
||||
upm::gps_fix f = gps.getFix();
|
||||
ASSERT_EQ(f.valid, false);
|
||||
ASSERT_EQ(f.quality, upm::gps_fix_quality::no_fix);
|
||||
/* Call the string method for coverage */
|
||||
f.__str__();
|
||||
|
||||
/* Get the 1 raw sentence */
|
||||
gps.getRawSentence();
|
||||
|
||||
/* Should be 0 entries in each queue */
|
||||
ASSERT_EQ(gps.rawSentenceQueueSize(), 0);
|
||||
ASSERT_EQ(gps.fixQueueSize(), 0);
|
||||
}
|
||||
|
||||
std::string randstr(size_t size)
|
||||
{
|
||||
std::string retstr(".", size);
|
||||
|
||||
std::random_device rd;
|
||||
std::default_random_engine eng(rd());
|
||||
std::uniform_int_distribution<char> distr(32, 126);
|
||||
for (size_t i = 0; i < size; i++)
|
||||
retstr[i] = distr(eng);
|
||||
return retstr;
|
||||
}
|
||||
|
||||
/* Parse bogus sentences */
|
||||
TEST_F(nmea_gps_unit, parse_gps_fix_bogus)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
|
||||
/* Parse some bogus sentences */
|
||||
std::string gga = "$GPGGA,182333.50,,,,,0,00,99.99,,,,,,*6B";
|
||||
gps.parseNMEASentence(gga);
|
||||
|
||||
for (size_t i = 0; i < gga.size(); i++)
|
||||
{
|
||||
std::string tmp = gga;
|
||||
gps.parseNMEASentence(tmp.replace(i, 1, "x"));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
gps.parseNMEASentence(randstr(40));
|
||||
|
||||
/* Still no GPS fix */
|
||||
ASSERT_EQ(gps.fixQueueSize(), 0);
|
||||
}
|
||||
|
||||
/* Parse valid gga sentences */
|
||||
TEST_F(nmea_gps_unit, parse_gps_fix_valid)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
|
||||
/* Parse a valid sentence */
|
||||
gps.parseNMEASentence("$GPGGA,172814.0,3723.46587704,N,12202.26957864,W,"
|
||||
+ std::string("2,6,1.2,18.893,M,-25.669,M,2.0,0031*4F"));
|
||||
|
||||
/* Should be 1 entry in GPS fix queue */
|
||||
ASSERT_EQ(gps.fixQueueSize(), 1);
|
||||
|
||||
upm::gps_fix f = gps.getFix();
|
||||
ASSERT_EQ(f.valid, true) << f.__str__();
|
||||
}
|
||||
|
||||
/* Parse valid gsv sentences */
|
||||
TEST_F(nmea_gps_unit, parse_gsv_valid)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
std::vector<std::string> snts =
|
||||
{
|
||||
"$GPGSV,2,1,08,07,64,079,,08,39,066,,09,25,159,,11,15,117,*7B",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,22,48,37,194,,51,35,158,*75",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,21,48,37,194,,51,35,158,*76",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,,48,37,194,,51,35,158,*75",
|
||||
"$GPGSV,2,1,08,07,64,079,,08,39,066,,09,25,159,,11,15,117,*7B",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,21,48,37,194,,51,35,158,*76",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,20,48,37,194,,51,35,158,*77",
|
||||
"$GPGSV,2,1,08,07,64,079,,08,39,066,,09,25,159,,11,15,117,*7B",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,18,48,37,194,,51,35,158,*7C",
|
||||
"$GPGSV,2,2,08,13,25,313,,30,78,336,17,48,37,194,,51,35,158,*73",
|
||||
"$GPGSV,2,1,08,07,64,080,,08,39,066,,09,25,159,,11,15,117,*7D",
|
||||
"$GPGSV,2,2,08,13,26,313,,30,78,336,13,48,37,194,,51,35,158,*74",
|
||||
"$GPGSV,2,1,08,07,64,080,,08,39,066,,09,25,159,,11,15,117,*7D",
|
||||
"$GPGSV,2,2,08,13,26,313,,30,78,336,09,48,37,194,,51,35,158,*7F",
|
||||
"$GPGSV,2,1,08,07,64,080,,08,39,066,,09,25,160,,11,15,117,*77",
|
||||
"$GPGSV,2,2,08,13,26,313,,30,78,336,23,48,37,194,,51,35,158,*77",
|
||||
"$GPGSV,3,3,09,51,35,158,*4E",
|
||||
"$GPGSV,2,1,08,07,64,080,22,08,39,066,,09,25,160,,11,15,117,*77",
|
||||
"$GPGSV,2,1,08,07,64,080,21,08,39,066,,09,25,160,,11,15,117,*74"
|
||||
};
|
||||
|
||||
/* Parse the first sentence */
|
||||
gps.parseNMEASentence(snts.front());
|
||||
|
||||
/* Should have 4 satellites */
|
||||
auto sats = gps.satellites();
|
||||
ASSERT_EQ(sats.size(), 4);
|
||||
|
||||
ASSERT_EQ(sats[0].prn, "07");
|
||||
ASSERT_EQ(sats[0].elevation_deg, 64);
|
||||
ASSERT_EQ(sats[0].azimuth_deg, 79);
|
||||
ASSERT_EQ(sats[0].snr, 0);
|
||||
|
||||
ASSERT_EQ(sats[1].prn, "08");
|
||||
ASSERT_EQ(sats[1].elevation_deg, 39);
|
||||
ASSERT_EQ(sats[1].azimuth_deg, 66);
|
||||
ASSERT_EQ(sats[1].snr, 0);
|
||||
|
||||
ASSERT_EQ(sats[2].prn, "09");
|
||||
ASSERT_EQ(sats[2].elevation_deg, 25);
|
||||
ASSERT_EQ(sats[2].azimuth_deg, 159);
|
||||
ASSERT_EQ(sats[2].snr, 0);
|
||||
|
||||
ASSERT_EQ(sats[3].prn, "11");
|
||||
ASSERT_EQ(sats[3].elevation_deg, 15);
|
||||
ASSERT_EQ(sats[3].azimuth_deg, 117);
|
||||
ASSERT_EQ(sats[3].snr, 0);
|
||||
|
||||
/* Parse the rest */
|
||||
for(const auto& sentence : snts)
|
||||
gps.parseNMEASentence(sentence);
|
||||
|
||||
/* Finish up with 8 satellites */
|
||||
sats = gps.satellites();
|
||||
ASSERT_EQ(sats.size(), 8);
|
||||
|
||||
/* Verify the last satellite */
|
||||
ASSERT_EQ(sats.back().prn, "11");
|
||||
ASSERT_EQ(sats.back().elevation_deg, 15);
|
||||
ASSERT_EQ(sats.back().azimuth_deg, 117);
|
||||
ASSERT_EQ(sats.back().snr, 0);
|
||||
|
||||
/* The 4th should have a non-zero snr */
|
||||
ASSERT_EQ(sats[4].snr, 21);
|
||||
}
|
||||
|
||||
/* Parse valid gll sentences */
|
||||
TEST_F(nmea_gps_unit, parse_gll_valid)
|
||||
{
|
||||
upm::NMEAGPS gps(0, 115200, -1);
|
||||
std::vector<std::string> snts =
|
||||
{
|
||||
"$GPGLL,4532.55107,N,12257.68422,W,170004.20,A,A*74",
|
||||
"$GPGLL,4532.55008,N,12257.68195,W,170005.00,A,A*70",
|
||||
"$GPGLL,4532.55027,N,12257.68252,W,170006.10,A,A*77",
|
||||
"$GPGLL,4532.54370,N,12257.65873,W,170006.90,A,A*7B",
|
||||
"$GPGLL,4532.54230,N,12257.65302,W,170008.00,A,A*74"
|
||||
};
|
||||
|
||||
/* Parse the first sentence */
|
||||
gps.parseNMEASentence(snts.front());
|
||||
|
||||
/* Get the fix */
|
||||
upm::gps_fix f = gps.getFix();
|
||||
ASSERT_EQ(f.valid, true) << f.__str__();
|
||||
ASSERT_FLOAT_EQ(f.coordinates.latitude, 45.542517833333335) << f.__str__();
|
||||
ASSERT_FLOAT_EQ(f.coordinates.longitude, -122.96140366666667) << f.__str__();
|
||||
ASSERT_EQ(f.time_utc, "170004.20") << f.__str__();
|
||||
|
||||
/* Parse the rest */
|
||||
for(const auto& sentence : snts)
|
||||
gps.parseNMEASentence(sentence);
|
||||
|
||||
/* Should have 5 GPS fixes */
|
||||
ASSERT_EQ(gps.fixQueueSize(), 5);
|
||||
}
|
Reference in New Issue
Block a user