Skip to content
This repository has been archived by the owner on May 26, 2024. It is now read-only.

Add decoding support for RMC messages from Galileo, GLONASS, GNSS #22

Merged
merged 9 commits into from
Nov 9, 2020
Merged
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![General Formatting Checks](https://github.com/107-systems/107-Arduino-NMEA-Parser/workflows/General%20Formatting%20Checks/badge.svg)](https://github.com/107-systems/107-Arduino-NMEA-Parser/actions?workflow=General+Formatting+Checks)
[![Spell Check](https://github.com/107-systems/107-Arduino-NMEA-Parser/workflows/Spell%20Check/badge.svg)](https://github.com/107-systems/107-Arduino-NMEA-Parser/actions?workflow=Spell+Check)

Arduino library for interfacing with any GPS module and interpreting its NMEA messages.
Arduino library for interfacing with any GPS, GLONASS, Galileo or GNSS module and interpreting its NMEA messages.

<p align="center">
<a href="https://github.com/107-systems/107-Arduino-Viper"><img src="extras/logo/viper-logo.jpg" width="40%"></a>
Expand All @@ -17,7 +17,7 @@ Arduino library for interfacing with any GPS module and interpreting its NMEA me
```C++
#include <ArduinoNmeaParser.h>
/* ... */
void onGprmcUpdate(nmea::RmcData const rmc)
void onRmcUpdate(nmea::RmcData const rmc)
{
Serial.print(rmc.time_utc.hour);
Serial.print(":");
Expand All @@ -43,7 +43,7 @@ void onGprmcUpdate(nmea::RmcData const rmc)
Serial.println();
}
/* ... */
ArduinoNmeaParser parser(onGprmcUpdate);
ArduinoNmeaParser parser(onRmcUpdate);
/* ... */
void setup() {
Serial.begin(9600);
Expand Down
12 changes: 9 additions & 3 deletions examples/NMEA-Basic/NMEA-Basic.ino
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
* FUNCTION DECLARATION
**************************************************************************************/

void onGprmcUpdate(nmea::RmcData const);
void onRmcUpdate(nmea::RmcData const);

/**************************************************************************************
* GLOBAL VARIABLES
**************************************************************************************/

ArduinoNmeaParser parser(onGprmcUpdate);
ArduinoNmeaParser parser(onRmcUpdate);

/**************************************************************************************
* SETUP/LOOP
Expand All @@ -53,8 +53,14 @@ void loop()
* FUNCTION DEFINITION
**************************************************************************************/

void onGprmcUpdate(nmea::RmcData const rmc)
void onRmcUpdate(nmea::RmcData const rmc)
{
if (rmc.source == nmea::RmcSource::GPS) Serial.print("GPS");
else if (rmc.source == nmea::RmcSource::GLONASS) Serial.print("GLONASS");
else if (rmc.source == nmea::RmcSource::Galileo) Serial.print("Galileo");
else if (rmc.source == nmea::RmcSource::GNSS) Serial.print("GNSS");

Serial.print(" ");
Serial.print(rmc.time_utc.hour);
Serial.print(":");
Serial.print(rmc.time_utc.minute);
Expand Down
7 changes: 4 additions & 3 deletions extras/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ set(TEST_TARGET testNmeaParser)
set(TEST_SRCS
src/test_ArduinoNmeaParser.cpp
src/test_checksum.cpp
src/test_GPRMC.cpp
src/test_GxRMC.cpp
src/test_main.cpp
src/test_rmc.cpp

../../src/nmea/util/checksum.cpp
../../src/nmea/util/gprmc.cpp
../../src/nmea/GPRMC.cpp
../../src/nmea/util/rmc.cpp
../../src/nmea/GxRMC.cpp
../../src/ArduinoNmeaParser.cpp
)

Expand Down
12 changes: 12 additions & 0 deletions extras/test/src/test_ArduinoNmeaParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ TEST_CASE("No NMEA message received", "[Parser-01]")

REQUIRE(parser.error() == ArduinoNmeaParser::Error::None);
REQUIRE(parser.rmc().is_valid == false);
REQUIRE(parser.rmc().source == nmea::RmcSource::Unknown);
REQUIRE(std::isnan(parser.rmc().latitude) == true);
REQUIRE(std::isnan(parser.rmc().longitude) == true);
REQUIRE(parser.rmc().time_utc.hour == -1);
Expand All @@ -65,6 +66,7 @@ TEST_CASE("RMC message after startup, no satellites", "[Parser-02]")

REQUIRE(parser.error() == ArduinoNmeaParser::Error::None);
REQUIRE(parser.rmc().is_valid == false);
REQUIRE(parser.rmc().source == nmea::RmcSource::GPS);
REQUIRE(std::isnan(parser.rmc().latitude) == true);
REQUIRE(std::isnan(parser.rmc().longitude) == true);
REQUIRE(parser.rmc().time_utc.hour == -1);
Expand All @@ -89,6 +91,7 @@ TEST_CASE("RMC message after startup, time fix available", "[Parser-03]")

REQUIRE(parser.error() == ArduinoNmeaParser::Error::None);
REQUIRE(parser.rmc().is_valid == false);
REQUIRE(parser.rmc().source == nmea::RmcSource::GPS);
REQUIRE(std::isnan(parser.rmc().latitude) == true);
REQUIRE(std::isnan(parser.rmc().longitude) == true);
REQUIRE(parser.rmc().time_utc.hour == 14);
Expand All @@ -112,6 +115,7 @@ TEST_CASE("Decoding starts mid-message", "[Parser-04]")
encode(parser, GPRMC);

REQUIRE(parser.rmc().is_valid == true);
REQUIRE(parser.rmc().source == nmea::RmcSource::GPS);
REQUIRE(parser.rmc().latitude == Approx(52.514467));
REQUIRE(parser.rmc().longitude == Approx(13.349300));
REQUIRE(parser.rmc().speed == Approx(39.6122));
Expand Down Expand Up @@ -157,6 +161,11 @@ TEST_CASE("Multiple NMEA messages received", "[Parser-07]")
std::string("$GPRMC,142600.00,A,4837.99474,N,01301.53452,E,27.920,247.03,121020,,,A*5A\r\n")
};

std::vector<nmea::RmcSource> const SOURCE_EXPECTED =
{
nmea::RmcSource::GPS, nmea::RmcSource::GPS, nmea::RmcSource::GPS, nmea::RmcSource::GPS, nmea::RmcSource::GPS
};

std::vector<float> const LATITUDE_EXPECTED =
{
52.514467f, 52.514800f, 52.515017f, 52.514900f, 48.633246f
Expand Down Expand Up @@ -198,6 +207,7 @@ TEST_CASE("Multiple NMEA messages received", "[Parser-07]")
};


auto source = SOURCE_EXPECTED.begin();
auto latitude = LATITUDE_EXPECTED.begin();
auto longitude = LONGITUDE_EXPECTED.begin();
auto speed = SPEED_EXPECTED.begin();
Expand All @@ -214,6 +224,7 @@ TEST_CASE("Multiple NMEA messages received", "[Parser-07]")
encode(parser, gprmc);

REQUIRE(parser.error() == ArduinoNmeaParser::Error::None);
REQUIRE(parser.rmc().source == *source);
REQUIRE(parser.rmc().latitude == Approx(*latitude));
REQUIRE(parser.rmc().longitude == Approx(*longitude));
REQUIRE(parser.rmc().speed == Approx(*speed));
Expand All @@ -225,6 +236,7 @@ TEST_CASE("Multiple NMEA messages received", "[Parser-07]")
if (std::isnan(*mag_var)) REQUIRE(std::isnan(parser.rmc().magnetic_variation) == true);
else REQUIRE(parser.rmc().magnetic_variation == Approx(*mag_var));

source = std::next(source);
latitude = std::next(latitude);
longitude = std::next(longitude);
speed = std::next(speed);
Expand Down
52 changes: 34 additions & 18 deletions extras/test/src/test_GPRMC.cpp → extras/test/src/test_GxRMC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

#include <catch.hpp>

#include <nmea/GPRMC.h>
#include <nmea/GxRMC.h>

/**************************************************************************************
* GLOBAL VARIABLES
Expand All @@ -27,13 +27,13 @@ static nmea::RmcData data;
* TEST CODE
**************************************************************************************/

SCENARIO("Extracting latitude/longiture from valid GPRMC message", "[GPRMC-01]")
SCENARIO("Extracting latitude/longiture from valid GPRMC message", "[GxRMC-01]")
{
WHEN("north/east")
{
std::string const GPRMC = "$GPRMC,062101.714,A,5001.869,N,01912.114,E,955535.7,116.2,290520,000.0,W*45\r\n";

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.latitude == Approx(50.03114442));
REQUIRE(data.longitude == Approx(19.20189679));
}
Expand All @@ -42,7 +42,7 @@ SCENARIO("Extracting latitude/longiture from valid GPRMC message", "[GPRMC-01]")
{
std::string const GPRMC = "$GPRMC,122311.239,A,4056.748,N,11212.614,W,,,290620,000.0,W*63\r\n";

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.latitude == Approx(40.9458060446613));
REQUIRE(data.longitude == Approx(-112.210235595703));
}
Expand All @@ -51,7 +51,7 @@ SCENARIO("Extracting latitude/longiture from valid GPRMC message", "[GPRMC-01]")
{
std::string const GPRMC = "$GPRMC,122311.239,A,2727.069,S,05859.190,W,,,290620,000.0,W*76\r\n";

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.latitude == Approx(-27.4511422937699));
REQUIRE(data.longitude == Approx(-58.986502289772));
}
Expand All @@ -60,67 +60,83 @@ SCENARIO("Extracting latitude/longiture from valid GPRMC message", "[GPRMC-01]")
{
std::string const GPRMC = "$GPRMC,122311.239,A,0610.522,S,10649.632,E,,,290620,000.0,W*6D\r\n";

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.latitude == Approx(-6.17536097471491));
REQUIRE(data.longitude == Approx(106.827192306519));
}
}

TEST_CASE("Extracting speed over ground from valid GPRMC message", "[GPRMC-02]")
SCENARIO("Extracting satellite system from valid GxRMC message", "[GxRMC-02]")
{
WHEN("GPS")
{
std::string const GPRMC = "$GPRMC,122311.239,A,0610.522,S,10649.632,E,,,290620,000.0,W*6D\r\n";
nmea::GxRMC::parse(GPRMC.c_str(), data);
REQUIRE(data.source == nmea::RmcSource::GPS);
}
WHEN("GNSS")
{
std::string const GNSS = "$GNRMC,090348.80,A,4838.00682,N,01301.61381,E,1.533,,301020,,,A*61\r\n";
nmea::GxRMC::parse(GNSS.c_str(), data);
REQUIRE(data.source == nmea::RmcSource::GNSS);
}
}

TEST_CASE("Extracting speed over ground from valid GPRMC message", "[GxRMC-03]")
{
std::string const GPRMC = ("$GPRMC,052856.105,A,5230.874,N,01321.056,E,085.7,206.4,080720,000.0,W*78\r\n");

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
/* 85.7 kts ~= 44.088 m/s */
REQUIRE(data.speed == Approx(44.088f));
}

TEST_CASE("Extracting track angle from valid GPRMC message", "[GPRMC-03]")
TEST_CASE("Extracting track angle from valid GPRMC message", "[GxRMC-04]")
{
std::string const GPRMC = ("$GPRMC,052856.105,A,5230.874,N,01321.056,E,085.7,206.4,080720,000.0,W*78\r\n");

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.course == Approx(206.4f));
}

TEST_CASE("Extracting position time from valid GPRMC message", "[GPRMC-04]")
TEST_CASE("Extracting position time from valid GPRMC message", "[GxRMC-05]")
{
std::string const GPRMC = ("$GPRMC,052856.105,A,5230.874,N,01321.056,E,085.7,206.4,080720,000.0,W*78\r\n");

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
/* 052856.105 -> 05:28:56.105 -> */
REQUIRE(data.time_utc.hour == 5);
REQUIRE(data.time_utc.minute == 28);
REQUIRE(data.time_utc.second == 56);
REQUIRE(data.time_utc.microsecond == 105);
}

TEST_CASE("Extracting date from valid GPRMC message", "[GPRMC-05]")
TEST_CASE("Extracting date from valid GPRMC message", "[GxRMC-06]")
{
std::string const GPRMC = ("$GPRMC,052856.105,A,5230.874,N,01321.056,E,085.7,206.4,080720,000.0,W*78\r\n");

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.date.day == 8);
REQUIRE(data.date.month == 7);
REQUIRE(data.date.year == 2020);
}

TEST_CASE("Extracting magnetic variation from valid GPRMC message", "[GPRMC-06]")
TEST_CASE("Extracting magnetic variation from valid GPRMC message", "[GxRMC-07]")
{
std::string const GPRMC = ("$GPRMC,052856.105,A,5230.874,N,01321.056,E,085.7,206.4,080720,000.0,W*78\r\n");

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.magnetic_variation == Approx(0.0));
}

TEST_CASE("Extracted status indicates void ('V') position data", "[GPRMC-07]")
TEST_CASE("Extracted status indicates void ('V') position data", "[GxRMC-08]")
{
std::string const GPRMC = ("$GPRMC,052856.105,V,5230.874,N,01321.056,E,085.7,206.4,080720,000.0,W*78\r\n");

data.latitude = 1.0f; data.longitude = 2.0f; data.speed = 3.0f; data.course = 4.0f;
data.time_utc.hour = -1; data.time_utc.minute = -1; data.time_utc.second = -1; data.time_utc.microsecond = -1;

REQUIRE(nmea::GPRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(nmea::GxRMC::parse(GPRMC.c_str(), data) == true);
REQUIRE(data.latitude == 1.0f);
REQUIRE(data.longitude == 2.0f);
REQUIRE(data.speed == 3.0f);
Expand Down
63 changes: 63 additions & 0 deletions extras/test/src/test_rmc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* This software is distributed under the terms of the MIT License.
* Copyright (c) 2020 LXRobotics.
* Author: Alexander Entinger <alexander.entinger@lxrobotics.com>
* Contributors: https://github.com/107-systems/107-Arduino-NMEA-Parser/graphs/contributors.
*/

/**************************************************************************************
* INCLUDE
**************************************************************************************/

#include <catch.hpp>

#include <nmea/util/rmc.h>

/**************************************************************************************
* TEST CODE
**************************************************************************************/

TEST_CASE ("Testing 'rmc_isGPRMC(...)' with valid and invalid GPRMC NMEA messages", "[rmc_isGPRMC-01]")
{
WHEN ("a valid GPRMC NMEA message")
REQUIRE (nmea::util::rmc_isGPRMC("$GPRMC") == true);
WHEN ("a invalid GPRMC NMEA message")
REQUIRE (nmea::util::rmc_isGPRMC("$GARMC") == false);
}

TEST_CASE ("Testing 'rmc_isGLRMC(...)' with valid and invalid GLRMC NMEA messages", "[rmc_isGLRMC-01]")
{
WHEN ("a valid GLRMC NMEA message")
REQUIRE (nmea::util::rmc_isGLRMC("$GLRMC") == true);
WHEN ("a invalid GLRMC NMEA message")
REQUIRE (nmea::util::rmc_isGLRMC("$GARMC") == false);
}

TEST_CASE ("Testing 'rmc_isGARMC(...)' with valid and invalid GARMC NMEA messages", "[rmc_isGARMC-01]")
{
WHEN ("a valid GARMC NMEA message")
REQUIRE (nmea::util::rmc_isGARMC("$GARMC") == true);
WHEN ("a invalid GARMC NMEA message")
REQUIRE (nmea::util::rmc_isGARMC("$GLRMC") == false);
}

TEST_CASE ("Testing 'rmc_isGNRMC(...)' with valid and invalid GNRMC NMEA messages", "[rmc_isGNRMC-01]")
{
WHEN ("a valid GNRMC NMEA message")
REQUIRE (nmea::util::rmc_isGNRMC("$GNRMC") == true);
WHEN ("a invalid GNRMC NMEA message")
REQUIRE (nmea::util::rmc_isGNRMC("$GARMC") == false);
}

TEST_CASE ("Testing 'rmc_isGxRMC(...)' with valid and invalid G(P|L|A|N)RMC NMEA messages", "[rmc_isGxRMC-01]")
{
WHEN ("a valid G(P|L|A|N)RMC NMEA message")
{
REQUIRE (nmea::util::rmc_isGxRMC("$GPRMC") == true);
REQUIRE (nmea::util::rmc_isGxRMC("$GLRMC") == true);
REQUIRE (nmea::util::rmc_isGxRMC("$GARMC") == true);
REQUIRE (nmea::util::rmc_isGxRMC("$GNRMC") == true);
}
WHEN ("a invalid G(P|L|A|N)RMC NMEA message")
REQUIRE (nmea::util::rmc_isGxRMC("$GIRMC") == false);
}
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name=107-Arduino-NMEA-Parser
version=1.0.3
author=Alexander Entinger <consulting@lxrobotics.com>
maintainer=Alexander Entinger <consulting@lxrobotics.com>
sentence=Arduino library for interfacing with any GPS module and interpreting its NMEA messages.
sentence=Arduino library for interfacing with any GPS, GLONASS, Galileo or GNSS module and interpreting its NMEA messages.
paragraph=
category=Communication
url=https://github.com/107-systems/107-Arduino-NMEA-Parser
Expand Down
Loading