element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Embedded and Microcontrollers
  • Technologies
  • More
Embedded and Microcontrollers
Blog C++ parser library for NMEA GPS data - pt. 2: parse replies with repeating info
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Embedded and Microcontrollers to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 30 Jul 2024 7:14 PM Date Created
  • Views 435 views
  • Likes 6 likes
  • Comments 0 comments
  • nmea
  • Teseo
  • gps
  • OO
  • teseo_c++
Related
Recommended

C++ parser library for NMEA GPS data - pt. 2: parse replies with repeating info

Jan Cumps
Jan Cumps
30 Jul 2024

I wrote an OO driver for Teseo-LIV3 GPS module (as used in shabaz ' GPS / Galileo / BeiDou / GLONASS receiver). It knows how to retrieve info from the GPS, in NMEA format. In this series, I'm designing an OO lib to parse the payloads and return the data as objects.

Objects to parse and hold a simple reply

The second post shows a slightly more involved (for me, not for the library user) topic: building objects for a command that has multiple repeating sets of data. It's not that complex: the number of repetitions is predefined and fixed. In the example that I use, the GSV reply, 4 different satellite attributes are in the reply message. Repeated 4 times. If there are less than 4 sets of data, the fields are empty.
edit: the parser also supports GSA, where the message reports up to 12 satellite's PRNs.

This example line has data for 4 satellites.

GSV: $GPGSV,3,1,11,13,79,310,,14,53,113,,05,51,214,,30,47,067,*72

The first attribute for the first satellite is the value 13. It's the PRN.

image

I wrote the class gsv to hold generic attributes of the reply. And a class gsv_sat to hold per-satellite data. gsv has a container member to hold 4 of those gsv_sat objects.  I use a std::array<gsv_sat,4> as container type. I could have made gsv_sat an embedded type of gsv. But I erred on the keep-it-simple side.

Why I selected std::array as container in this lib: https://embeddedartistry.com/blog/2017/08/23/choosing-the-right-stl-container-general-rules-of-thumb/

For convenience, I created a typedef:

typedef std::array<gsv_sat, 4> gsv_sat_array;

edit: also the gsa class - available in the library but not discussed in the blog - uses an array, to store up to 12 satellite PRN numbers.

gsv_sat class

This class can hold a set of 4 attributes that the GSV message returns for each satellite. It doesn't have any parsing logic. That's managed by the gsv class.

class gsv_sat {
public:
	unsigned int prn;
	unsigned int elev;
	unsigned int azim;
	unsigned int snr;
};

gsv class

This is the one that can parse the GSV message, retrieve its individual data points and fill up the individual satellite information.

class gsv {
public:
	static bool from_data(const std::string& data, gsv& gsv);
	nmea::talker_id source;
	gsv_sat_array sats;
};

Appreciate that this is all very little code, to retrieve all that info. The from_data() method does all the groundwork.

// $GPGSV,3,1,11,13,79,310,,14,53,113,,05,51,214,,30,47,067,*72
bool gsv::from_data(const std::string& data, gsv& gsv) {
	unsigned int field = 0;
	std::string_view v = std::string_view(data).substr(0, data.find('*'));
    for (const auto word : std::views::split(v, delim)) {
    	switch (field) {
    	case 0: // talker id
    		gsv.source = nmea::get_talker_id(std::string_view(word));
    		break;
    	case 4:
    	case 8:
    	case 12:
    	case 16:
    		if (std::string_view(word).length() == 0) {
    			gsv.sats[(field - 4) / 4 ].prn = 0;
    		} else {
    			gsv.sats[(field - 4) / 4 ].prn =
    					std::stoi(std::string(std::string_view(word)));
    		}
    		break;
    	case 5:
    	case 9:
    	case 13:
    	case 17:
    		if (std::string_view(word).length() == 0) {
    			gsv.sats[(field - 5) / 4 ].elev = 0;
    		} else {
    			gsv.sats[(field - 5) / 4 ].elev =
    					std::stoi(std::string(std::string_view(word)));
    		}
    		break;
    	case 6:
    	case 10:
    	case 14:
    	case 18:
    		if (std::string_view(word).length() == 0) {
    			gsv.sats[(field - 6) / 4 ].azim = 0;
    		} else {
    			gsv.sats[(field - 6) / 4 ].azim =
    					std::stoi(std::string(std::string_view(word)));
    		}
    		break;
    	case 7:
    	case 11:
    	case 15:
    	case 19:
    		if (std::string_view(word).length() == 0) {
    			gsv.sats[(field - 7) / 4 ].snr = 0;
    		} else {
    			gsv.sats[(field - 7) / 4 ].snr =
    					std::stoi(std::string(std::string_view(word)));
    		}
    		break;
    	default: // skip 1, 2, 3
    		break;
    	}
    	field++;
    }
	return (field == 20); // everything parsed
}

Because the satellite is repeated a fixed amount of times, I was able to reuse the case conditions for each field. This code also shows an example that shows the use of std::string_view. It's used here to hide the trailing checksum data from the string tokeniser.

Typical use:

	std::vector<std::string> replies {
		"$GPGSV,3,1,11,13,79,310,,14,53,113,,05,51,214,,30,47,067,*72",
		"$GPGSV,3,2,11,15,45,295,24,22,44,145,,20,27,192,,07,16,064,*7A",
		"$GPGSV,3,3,11,18,16,298,25,24,08,249,,08,08,029,18,,,,*40",
		"$GLGSV,2,1,08,72,79,113,,74,77,084,,75,38,202,,65,37,317,28*68",
		"$GLGSV,2,2,08,73,34,040,35,71,28,130,,81,13,333,24,82,08,017,*68",
	};

	for(auto r : replies) {
		nmea::gsv o;
	    nmea::gsv::from_data(r, o);

	    std::cout <<
	    		"source: " << o.source << ". " << std::endl;
	    for(auto s : o.sats) {
	    	std::cout << "sat prn: " << s.prn << ", elev: " <<
	    			s.elev << ", azim: " << s.azim << ", snr: " << s.snr << "." <<
					std::endl;
	    }

	}

This example shows a typical use for the GSV scenario, where the GPS returns multiple lines. And each line can contain telemetry of 1 .. 4 satellites.

Result:

source: 0.
sat prn: 13, elev: 79, azim: 310, snr: 0.
sat prn: 14, elev: 53, azim: 113, snr: 0.
sat prn: 5, elev: 51, azim: 214, snr: 0.
sat prn: 30, elev: 47, azim: 67, snr: 0.
source: 0.
sat prn: 15, elev: 45, azim: 295, snr: 24.
sat prn: 22, elev: 44, azim: 145, snr: 0.
sat prn: 20, elev: 27, azim: 192, snr: 0.
sat prn: 7, elev: 16, azim: 64, snr: 0.
source: 0.
sat prn: 18, elev: 16, azim: 298, snr: 25.
sat prn: 24, elev: 8, azim: 249, snr: 0.
sat prn: 8, elev: 8, azim: 29, snr: 18.
sat prn: 0, elev: 0, azim: 0, snr: 0.
source: 1.
sat prn: 72, elev: 79, azim: 113, snr: 0.
sat prn: 74, elev: 77, azim: 84, snr: 0.
sat prn: 75, elev: 38, azim: 202, snr: 0.
sat prn: 65, elev: 37, azim: 317, snr: 28.
source: 1.
sat prn: 73, elev: 34, azim: 40, snr: 35.
sat prn: 71, elev: 28, azim: 130, snr: 0.
sat prn: 81, elev: 13, azim: 333, snr: 24.
sat prn: 82, elev: 8, azim: 17, snr: 0.

Next posts

In the follow up blog, I'll finish with the utility classes and data type helpers.

next post:  C++ parser library for NMEA GPS data - pt. 3: utility class, example use and wrap-up 

visit the github repository
Link to 
all posts.

  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube