NeoGPS formatting output and using 10Hz

Hi,

I'm using an Arduino Nano (pins 0 and 1) and GPS 6MU2 and am building a program to track a motorbike drive. TinyGPS++ works, but not quickly enough (1 second at 70 mph = 40 meters between positions, I'd like to aim for 4 meters). I've found neoGPS and have spent days reading some cracking, if complex, code and examples.

I am an Engineer with good visual basic coding skills, but limited Arduino skills. I mainly learn by modifying existing code and searching forums, which I have done BUT cannot answer my current issues.

Question 1

The below code runs well but latitude (and longitude) report as 50123, not 50.123.
I'm using fix.latitudeL for maximum accuracy.

Also, fix.altitude_cm() reports as 10880, not 108.80 which I would prefer. I guess both these issues have one solution. I have tried sprintf and a variety of other functions but nothing that works so far.

Question 2

There are several very detailed notes on running the system at 5Hz or 10Hz, instead of the 1Hz I think it is running now but all WAY above my knowledge to interpret. Does anyone have some simple example of how I can achieve this?

Question 3

My code (scroll down to doSomeWork, I've included everything as I notice some folk ask for that) feels a bit lengthy - I've used 1/3 of the program space just to log the GPS without adding the code of the SD card etc. Am I making some newbie mistakes and can it be streamlined?

#include <NMEAGPS.h>

//======================================================================
//  Program: NMEA_Paul.ino v1.4
//
//  Description:  This program is a cut-down of NMEA.ino to suit a Nano.
//
//  License: Copyright (C) 2014-2017, SlashDevin
//======================================================================

// Next actions;
// Code runs, but how to normalise Lat and Long? i.e. 50.123 not 50123
// Same question for altitude 108.80 not 10880
// GPS running at 1Hz, it can run at 10Hz - How?

//======================================================================

//White wire - GPS Tx to Nano Rx pin 0 (DISCONNECT (0) to upload a new Sketch)
//Blue wire  - GPS Rx to Nano Tx pin 1

// To get the examples working the GPSport.h file has been edited to the below.
// When testing finished this note and the next line can be deleted and the
// following three lines uncommented.
#include <GPSport.h>

//#define gpsPort Serial
//#define GPS_PORT_NAME "Serial"
//#define DEBUG_PORT Serial

// **WARNING:
// ERROR: LAST_SENTENCE_IN_INTERVAL is incorrectly set to NMEAGPS::NMEA_RMC!
// You must change this line in NMEAGPS_cfg.h:   
// #define LAST_SENTENCE_IN_INTERVAL NMEAGPS::NMEA_GLL  <<<<  Changed
//
// There are 7 instances of NMEAGPS_cfg.h  The one you want is libraries/NeoGPS/extras/configs/Nominal

// GGA has altitude and satellite fixes
// RMC has status, date, time, lat/long, speed and heading
// therefore no other words are needed. Comment out in NMEAGPS_cfg.h

//------------------------------------------------------------
// This object parses received characters into the gps.fix() data structure

static NMEAGPS  gps;

//------------------------------------------------------------
//  Define a set of GPS fix information.  It will hold on to the various pieces
//  as they are received from an RMC sentence.  It can be used anywhere in your sketch.

static gps_fix  fix;

//----------------------------------------------------------------
//  This function gets called about once per second, during the GPS
//  quiet time. By doing the "hard" work during the quiet time, the CPU can get back to
//  reading the GPS chars as they come in, so that no chars are lost.

String s_SD_Card; // Storage to build up the CSV word to be written to the SD card
String s_header = ("Date,Time,Lat,Long,Height,Speed,Fixes");

void setup()
{
  Serial.println (s_header);
  gpsPort.begin( 9600 );
} // end setup

//--------------------------

void loop()
{
  GPSloop();
} // end loop
//---------------------------

//  This is the main GPS parsing loop.

static void GPSloop()
{
  while (gps.available( gpsPort )) {
    fix = gps.read();

    doSomeWork();
  }
} // end GPSloop
//-------------------------

static void doSomeWork()
{
  // Create a string to write CSV data to an SD card
  s_SD_Card = "";  //Empty any previous data

  if (fix.valid.date) {
    s_SD_Card = String(fix.dateTime.full_year() ) + "-";
    s_SD_Card = s_SD_Card + String(fix.dateTime.month) + "-";
    s_SD_Card = s_SD_Card + String(fix.dateTime.date) + ",";
  }
  else {
    s_SD_Card = ", ";
  } //end if valid date

  if (fix.valid.time) {
    if ( fix.dateTime.hours < 10 ) {s_SD_Card = s_SD_Card + "0";}
    s_SD_Card = s_SD_Card +  String(fix.dateTime.hours) + ":" ;
    if ( fix.dateTime.minutes < 10 ) {s_SD_Card = s_SD_Card + "0";}
    s_SD_Card = s_SD_Card +  String(fix.dateTime.minutes) + ":";
    if ( fix.dateTime.seconds < 10 ) {s_SD_Card = s_SD_Card + "0";}
    s_SD_Card = s_SD_Card +  String(fix.dateTime.seconds) + ".";
    if ( fix.dateTime_cs < 10 ) {s_SD_Card = s_SD_Card + "0";}
    s_SD_Card = s_SD_Card +  String(fix.dateTime_cs) + ", ";
  }
  else {
    s_SD_Card = s_SD_Card +  ", ";
  } //end if valid time

  if (fix.valid.location) {
    s_SD_Card = s_SD_Card +  String(fix.latitudeL()) + ", ";
  }
  else {
    s_SD_Card = s_SD_Card +  ", ";
  } //end if valid lat

  if (fix.valid.location) {
    s_SD_Card = s_SD_Card +  String(fix.longitudeL()) + ", ";
  }
  else {
    s_SD_Card = s_SD_Card +  ", ";
  } //end if valid lat

  if (fix.valid.altitude) {
    s_SD_Card = s_SD_Card +  String( fix.altitude_cm() ) + ", "; //in integer centimeters
  }
  else {
    s_SD_Card = s_SD_Card +  ", ";
  } //end if valid height in cm

  if (fix.valid.speed) {
    s_SD_Card = s_SD_Card +  String( fix.speed_mph() ) + ", "; //in floating-point miles per hour
  }
  else {
    s_SD_Card = s_SD_Card +  ", ";
  } //end if valid speed

  if (fix.valid.satellites) {
    s_SD_Card = s_SD_Card +  String( fix.satellites ) + ", ";
  }
  else {
    s_SD_Card = s_SD_Card +  ", ";
  } //end if valid fixes

  //This will be saved to an SD card, printing for now to debug
  Serial.print (s_SD_Card );

  //Section break
  Serial.println(" ");

} // enddoSomeWork

For question one, the easiest thing to do would be to convert the number to a float and divide by 1000. You may lose a little precision though - try it and see.

Alternatively, you could convert it to a String and use substring functions to pull out the components and then recombine them with a period glued in the middle.

You're using String objects a lot. If your code misbehaves or starts crashing after a while, that would be a good thing to eliminate.

For question two, there is a sentence that you can use to set the rate, but it's not obvious (to me) what the parameter fields and their formats are.

I did see a suggestion that there's some configuration software for windows that you can use to set it. Not sure if it survives a power cycle though.

Your code is asking for the 32 bit integer value of the latitude so that is what you are getting

fix.latitudeL() and fix.longitudeL() for the higher-precision integer degrees, scaled by 10,000,000 (10 significant digits)
fix.latitude() and fix.longitude() for the lower-precision floating-point degrees (~7 significant digits)

I'm not sure how you have debug Serial.print() statements in your code since your have your GPS wired to Tx/Rx. You can't have both.

@Wildbill. My understanding is floats are 7 significant digits, I have 10. Glue the period does not (easily) work if you want it to be valid across the world 1E, 10E 100E. I've just found fix.longitudeDMS.degrees and will have a look in that area. I'm using 2 strings - is that a lot? Re q2 - I'm a Mac user. I think you can set the baud rate of the GPS unit from Arduino but have not yet found a simple code to do that.

@blh64 Thanks. Not using debug - the two instances are commented out (I like to comment out before I delete to see if anything breaks first :slight_smile: ). However note this line; //White wire - GPS Tx to Nano Rx pin 0 (DISCONNECT (0) to upload a new Sketch) which may explain why I have both? (I have no clue :-)) Yeah, I want the higher precision but both options come as a string of numbers with no decimal. See note above ref fix.longitudeDMS.degrees which will be my focus tomorrow.

OK, Q1 solved.

Fixed altitude formatting by using decimal meters i.e. Altitude moved from cm to floating-point meters
Fixed how to normalise Lat and Long? i.e. 50.123, not 50123 - the solution is within NMEAloc.ino

Would LOVE to get some feedback on Q2

These lines are not commented out

Serial.println (s_header);
...
  Serial.println(" ");

Since you are running on a nano, you should also avoid Strings and just C strings (char arrays)

as for Q2 above, can you share a link to these detailed notes about running faster. From what I've seen, the chip run at a max of 5Hz.

@blh64. OK, I accept what you say but as long as I disconnect the GPS Tx wire before uploading a sketch, then reconnect it everything works. I'll look into char arrays but I thought you had to know how long they would be before you used them?
If you google 'neoGPS 10Hz' there are lots of hits. This one goes into some depth - clearly an ocean whereas I am more struggling to use a bath! 10hz GNSS with NEOGPS library - Networking, Protocols, and Devices - Arduino Forum. I would be happy to run at 5Hz if the code is easy to understand?

AndrewPaulCooper:
If you google 'neoGPS 10Hz' there are lots of hits. This one goes into some depth - clearly an ocean whereas I am more struggling to use a bath! 10hz GNSS with NEOGPS library - Networking, Protocols, and Devices - Arduino Forum.

And the discussion there is about a Quectel GPS, the configuration and setup for that is not related to your GPS 6MU2 (which sounds like a Ublox GPS) in any way at all.

would be happy to run at 5Hz if the code is easy to understand?

I think you missunderstand the extent of what is needed, to get the GPS to send out rapid updates to fixes, you have to conifigure the GPS to;

  1. Disable most of the NMEA senetences it would normally transmit, there is not enough time to send out them all.
  2. Change the baudrate of the GPS, from the default of 9600 so there enough time to send out the remaining NMEA sentences.
  3. Change the default update rate for sending out the remaining NMEA sentences.

How to do this is all documented in the GPS data sheets of course, but its quite a bit of work ....................

Thanks srnet. Ref your first point - Just proves how little I know :-). I did declare I'm a total newbie.

Ref your second point; 1) I have done this, I only use two words. 2) Yes, I understand 3) That's where I am running aground. Your right, there is a LOT of documentation, and a lot of nested libraries - sorting the useful-to-me from interesting-fo-another-day is taking days. Almost there, I hope.

Since you are running on a nano, you should also avoid Strings and just C strings (char arrays)

Definately NOT.
String are fine if you follow the guidelines. See my tutorial on Taming Arduino Strings

Re high speed GPS handling, I have a detailed tutorial on this as part of
Arduino Serial I/O for the Real World which covers non-blocking Serial prints, increasing the RX buffer and checking the time it takes loop to run so you can track down what is slowing you down.
It also includes a detailed example of parsing GPS data.

@drmpf. Wow! Seriously Wow!
I'm at that stage in life when adding anything new to my memory something old has to fall out. I think I've just lost a year's worth of memories but those two links look to be EXACTLY what I wanted.
I've just read them for now, but I plan to go back to the (many) strings examples and work them into my code. When that is completed I will pick up the GPS handling article and try that as well.
Thank you a) for the pointers and b) for writing such detailed and useful help files.

The tool to use for checking out the config required for the GPS is the Ublox Windows Ucenter tool.

You can use it to try out all the commands and it will list the hex characters to send for a particular configuration command. You can use it to save the configuration to the GPS as well.

If you turn off all bar the GPGGA and GPRMC senetences, set the GPS baud rate to 115200, the update rate to 10z then TinyGPS++ is quite happy to decode it.

Thanks srnet. Looked at that and it appears to be a great program BUT I am a Mac user and they don't have a Mac version.

I think, from many forums, the code I want is this but although it compiles it does not change the GPS rate so I am missing something else.

Void setup();{

gpsPort.begin( 9600 );  //Once working try higher speeds eg 115200
gpsPort.println("$PMTK220,200*2C”);  // 5Hx
//gpsPort.println("$PMTK220,100*2F");  // 10Hx
}

I used to use TinyGPS++ but moved across to neoGPS as it appears to be faster (once you get your head around all the associated files and configurations). If I find a way to oomph GPS up to 5 or 10Hz I may go back to that code and do some testing. Thanks for your feedback.

Commands such as '$PMTK220,200*2C' are for Mediatek\Quectel GPSs.

It would avoid any further confusion if you were to reveal which GPS you are actually using. Note my prevous comment;

And the discussion there is about a Quectel GPS, the configuration and setup for that is not related to your GPS 6MU2 (which sounds like a Ublox GPS) in any way at all.

It's a GY-GPS6NU2. It has a large chip which says; ublox | NEO-6M-0-001 | 63202072408 | 1736 | 1300 25 there is pb in a circle crossed out and e4 in a circle not crossed out.

It only has four wires, +Vcc, Gnd then
GPS Tx connected to Nano Rx pin 0
GPS Rx connected to Nano Tx pin 1
NB I have to disconnect the wire GPS Tx to Nano Rx pin 0 to upload a sketch then reconnect it to see data, however, I do get good data and even inside (testing and building) I am seeing 9 satellites and not dropping any data. Outside I have seen 11 satellites.
Off out shortly, but will check back later.

drmpf:
Definately NOT.
String are fine if you follow the guidelines. See my tutorial on Taming Arduino Strings

Yes, you should. You say they are safe but then have a long list of things to do and not do to make them "safe" and the guidelines are your suggestions, not part of the String class itself.
Just my $0.02.

If you don't like Strings or don't want to follow the guidelines, you can always use my SafeString library instead which provides the same functionality as Strings (and more) but is based on fixed sized char[] and has error msgs to let you know when the char[]s run out of space.

See this example of using SafeString to read and parse GPS input

drmpf:
If you don't like Strings or don't want to follow the guidelines, you can always use my SafeString library instead which provides the same functionality as Strings (and more) but is based on fixed sized char[] and has error msgs to let you know when the char[]s run out of space.

See this example of using SafeString to read and parse GPS input

Or, if you don't want to wear suspenders with your belt, just learn how to use regular char arrays and know the language and move on....