element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • 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. 4: We have C++ objects and containers: So what?
  • 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: 10 Aug 2024 9:22 AM Date Created
  • Views 710 views
  • Likes 6 likes
  • Comments 5 comments
  • stl
  • nmea
  • c++23
  • Teseo
  • gps
  • OO
  • teseo_c++
Related
Recommended

C++ parser library for NMEA GPS data - pt. 4: We have C++ objects and containers: So what?

Jan Cumps
Jan Cumps
10 Aug 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.

The fourth post touches on some options you have once the data is available in a structured way. I'm going to count how much data we got from the GLONASS and GPS constellations. Using an STL built-in algorithm.

what do you need to know?

  • I use Teseo library to retrieve data out of an ST Teseo GPS. The data that we get is a container of GPS data strings
    The data in these strings are information on the GPS "brands" (constellations is the term) and their satellites
    The data set is called GSV (GNSS Satellites in View). This is not important, but you'll see this term in the code below.
    The GPS will return multiple data strings for each constellation.
  • I use NMEA library to extract (parse) attributes out of those data strings,
    and build a container with a C++ object that holds those attributes, for each string 
  • Constellation examples are GPS and GLONASS.
  • see all links at the bottom of this post for the Teseo and NMEA libraries used here.

The C++ Standard Template Library (STL) has options to process that structured data. You can filter, search, count, average, group, print ... on the objects and containers. Often by using standard (and already tested for years and years) algorithms.

First a preparation exercise that I showed in previous post: get the GSV data out of the GPS IC, and transfer it into a container of gsv objects:

teseo::teseo gps;
std::vector<std::string> replies(NMEA_MAX_REPLIES); // hold GPS replies as data strings
// vector size is a suggestion. STL will allocate at least NMEA_MAX_REPLIES
uint count; // intentionally uninitialised
bool valid; // intentionally uninitialised
std::array<nmea::gsv, NMEA_MAX_REPLIES> gsv_set = {}; // hold gsv objects

// ...

void retrieve_gsv() {
    // get the GSV data as text strings from the Teseo GPS IC
    // into container "replies"
    valid = gps.ask_gsv(replies, count);
    if (!valid) { return; }
    
    // for each string in "replies", parse the attributes, 
    // and fill a gsv object with it in container "gsv_set" 
    unsigned int index = 0;
	for(auto& r : std::ranges::subrange(replies.begin(), replies.begin() + count)) {
        valid = nmea::gsv::from_data(r, gsv_set[index]);
        if (!valid) {
            break;
        }
        index++;
	}
    for(auto&& r: std::ranges::subrange(gsv_set.begin() + count, gsv_set.end())) {
        r = {}; // clean out unused tail of the container
    }
    return;
}

int main() {
    // ...   

    while (true) {
	    retrieve_gsv();
	    
	    
	    // ...
        sleep_ms(1000);
    }
}

You don't need to understand this code snippet to follow along. As long as you get that:
The result is that we have a container of GSV objects. Each object has the data of one GPS gsv data string.
One of the members of the gsv object is the source. The source tells what constellation served the data.

image

This is that same container in the debugger:

image

Counting with the STL

Now, let's use a STL algorithm to count how many objects we have in the container for GPS and GLONASS. I'll use the std::count_if()algorithm.
count_if() runs over the container, and only counts if a particular condition is true (in STL lingo: predicate). In my case: the source equals the constellation I want to count.

size_t count_constellations(const nmea::nmea::talker_id source) {
    size_t i =  std::count_if(gsv_set.begin(), gsv_set.end(),              
                           [source](const auto& o){ return (o.source == source); });
    return i;
}

That's one of the functionalities of the STL. Its algorithms work on custom containers, such as my "vector of gps objects". It would also work if I put my objects in a classic C style array.

Here's how I use it:

int main() {

    size_t count; // intentionally uninitialised
    while (true) {
	    retrieve_gsv();

        count = count_constellations(nmea::nmea::gps);
        printf("count of gps: %i\r\n", count);
        count = count_constellations(nmea::nmea::glonass);
        printf("count of glonass: %i\r\n", count);

        sleep_ms(1000);
    }
}

Result:

count of gps: 4
count of glonass: 3

That's it.
Except that there are way more algorithms, filters, views, ... available in the STL. Many of those can be useful in this (and your) project.

Link to all posts.

  • Sign in to reply
  • Jan Cumps
    Jan Cumps over 1 year ago in reply to Jan Cumps

    Getting a typical list of all reported satellites, regardless the constellation:

    sat id: 13, elev: 76, azim: 304, snr: 25, source: gps.
    sat id: 5, elev: 54, azim: 218, snr: 0, source: gps.
    sat id: 14, elev: 51, azim: 119, snr: 0, source: gps.
    sat id: 30, elev: 49, azim: 66, snr: 19, source: gps.
    sat id: 15, elev: 42, azim: 295, snr: 30, source: gps.
    sat id: 22, elev: 40, azim: 148, snr: 0, source: gps.
    sat id: 20, elev: 30, azim: 192, snr: 0, source: gps.
    sat id: 7, elev: 18, azim: 63, snr: 0, source: gps.
    sat id: 18, elev: 16, azim: 301, snr: 19, source: gps.
    sat id: 8, elev: 8, azim: 31, snr: 24, source: gps.
    sat id: 68, elev: 81, azim: 93, snr: 0, source: glonass.
    sat id: 78, elev: 77, azim: 140, snr: 0, source: glonass.
    sat id: 69, elev: 41, azim: 317, snr: 31, source: glonass.
    sat id: 77, elev: 30, azim: 38, snr: 28, source: glonass.
    sat id: 67, elev: 30, azim: 128, snr: 19, source: glonass.
    sat id: 79, elev: 28, azim: 201, snr: 0, source: glonass.
    sat id: 85, elev: 14, azim: 335, snr: 36, source: glonass.
    sat id: 85, elev: 14, azim: 334, snr: 0, source: glonass.
    sat id: 65, elev: 13, azim: 130, snr: 0, source: glonass.
    sat id: 84, elev: 7, azim: 284, snr: 0, source: glonass.

            // just print all sattelites with their attributes
            for(auto o : gsv_set | std::views::filter([](const auto& s){ return s.source != nmea::nmea::notset;})) {
                auto satellites = o.sats | std::views::filter([](const nmea::gsv_sat& s){ return s.prn != 0;});
                for (const auto& s : satellites) {
                    printf("sat id: %i, elev: %i, azim: %i, snr: %i, source: ", s.prn, s.elev, s.azim, s.snr);
                    print_talker(o.source);
                    printf(".\r\n");
                }
            }

    This is one of the options. Other options are to

    • gather a container with (pointers to) all satellites. This is useful if you want to do several operations over that same set.
    • create a join view over all satellite containers, that leaves everything in the existing containers but allows you to write a single for loop
    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 1 year ago in reply to shabaz

    It is. The usefulness is dependent on what container you use though.

    For std::array, that has a fixed size, you can easily access all elements.

    For dynamic containers, such as vectors, lists, ... you typically can only sensible drill to the first element

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • shabaz
    shabaz over 1 year ago

    Hi Jan,

    The debugger is STL aware? Very neat. I had expected it not to be capable of revealing the contents of the container at all!

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 1 year ago in reply to Jan Cumps

    For the powerplayers Slight smile

    // only loop over replies that are valid. Don't loop over the empty tail of the container (source == nmea::nmea::notset).
    for(auto o : gsv_set | std::views::filter([](const auto& s){ return s.source != nmea::nmea::notset;})) {
        // loop over all valid satellite objects. NMEA always returns 4. They may not all be valid (prn == 0)
        auto satellites = o.sats | std::views::filter([](const nmea::gsv_sat& s){ return s.prn != 0;});
        print_talker(o.source);
        printf(" sat id: ");
        for (const auto& s : satellites) {
          printf(" %i", s.prn);
        }
        printf(". \r\n");
    }

    In this code, no copies are made of any data. It all compiles to for loops with filters.
    The filters are lazy. They happen while for-looping.

    result is identical:

    gps count: 3
    glonass count: 2
    gps sat id: 9 7 11 6.
    gps sat id: 20 30 4 16.
    gps sat id: 5 29 36 49.
    glonass sat id: 71 72 87 73.
    glonass sat id: 80 88 86 70.
    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 1 year ago

    to get all valid satellites out of one gsv object (can be between 1 and 4):

    o: gsv object
    o.sats: container of sat objects

        auto sts = o.sats | std::views::filter([](const nmea::gsv_sat& s){ return s.prn != 0;});

    prn is the satellite's "id".
    possible code:

    void list_satellites(const nmea::gsv& o) {
        auto sts = o.sats | std::views::filter([](const nmea::gsv_sat& s){ return s.prn != 0;});
        print_talker(o.source);
        printf(" sat id: ");
        for (const auto& s : sts) {
          printf(" %i", s.prn);
        }
        printf(". \r\n");
        return;
    }
    

    looping over all our gsv objects to get their satellites:

            for(const auto& g : std::ranges::subrange(gsv_set.begin(), gsv_set.begin() + count) ) {
                list_satellites(g);
            }
    

    result:

    gps count: 4
    glonass count: 3
    gps sat id: 9 6 4 11.
    gps sat id: 7 3 20 19.
    gps sat id: 26 31 30 49.
    gps sat id: 36.
    glonass sat id: 71 86 87 70.
    glonass sat id: 80 72 73 79.
    glonass sat id: 85 85.

    The last gps object has 1 satellite. The last glonass gsv object had 2. You can see that the std::views::filter (as defined in the predicate s.prn != 0) nicely does not include the full array of 4 possible satellites. But only the ones that match the filter.
    Again, done without writing a custom filter, but using a STL construct.
    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
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