GPS Modul as a second module on Quadrocopter (using same antenna) Suggestion

Hi all,

I need a good and "simple" GPS modul:

  • accuraccy about 1-2m (I can also live with < 3m)
  • desired sample rate about 10 Hz
  • use of the GPS antenna already installed for the autopilot with an Y-cable (or ist that not possible?)
  • interface can be standard serial (I2C would be a great option if availabe)
  • only need the position (log, lat)
  • shall be integrated as a second GPS in a quadrocopter (size approx below: 30mm x 30mm x 10mm)
  • price is not the driver (nevertheless below 50$ would be good)

Since I recently good a great recommendation for an IMU AFTER I had a lot of trouble with my first choice this time I'm asking in advance :).

Does anyone have good experiences/preferences or a favourite for a GPS module?

I found a promissing one at adafruit:

Thanks
Tim

The ublox Neo series seems popular. I have used them (Neo-6M) with good results... so I wrote the fastest, smallest GPS library ever, NeoGPS. It handles generic NMEA messages from any GPS, and also the ublox-specific NMEA messages and UBX binary protocol. It also implements "coherency", which could be required for the quadcopter. Some ublox models provide 10Hz updates. NeoGPS will even support two GPS devices at the same time, although I think you are saying the current GPS device is connected to something else. I don't think you can share antennas... antennae?

I've seen no complaints for the ublox devices. They are also popular because they're fairly cheap for the quality you get.

Cheers,
/dev

I'm using the GY-NEO-6M V2 with the TinyGPSPlus lib - it's amazing sensitive and accurate!

http://www.ebay.de/itm/311296066259
http://arduiniana.org/libraries/tinygpsplus/

Hi ArthurD,

thanks for your great advice! I bought Ublox NEO-7M GPS Modul (GY-GPSV3-7MGPS-Modul) which supposed to be the new version of yours. Works great so far!The TinyGPSPlus is great, especially reading out other sentences with the TinyGPSCustom-Objects.

Just one (or two) short questions:
I could not figure out in which units the hdop values are comming. I get a vlaue of around 100 when the GPS is tracking 10-11 satellites. Is the right? What does it mean :)?

Second one:
When I want to read the number of satellites by:

Serial.print(gps.satellites.value());

I had the change the code in the DeviceExample from

  while (ss.available() > 0)
    if (gps.encode(ss.read()))
      displayInfo();

to

while (ss.available()){
    gps.encode(ss.read());
    gpsUpdated = true;
  }
  if (gpsUpdated==true){
    displayInfo();  
    gpsUpdated = false;
  }

But honestly I did not 100%ly why. Did someone of you know?

Sorry for the maybe studpid question and cheers
Tim

hi,
unfortunately I cant help you for your issues, I don't encounter them actually by my own device - the tinyGPSplus example works fine for me!
Are your latt and long values (double) working correctly?

I could not figure out in which units the hdop

They're in units of "goodness". :slight_smile: Less than 1 is "really good", see here for other values. It is computed internally from many sources, and really can't be used to know how far off the longitude might be. Lots of app display it that way, though. They might draw a circle on a map around your position to show the "vagueness". Big HDOP means big circle, etc.

There is a separate field for the number of satellites being tracked. HDOP can be lower when there are more satellites, but the really mean different things.

If you really want to know the error estimate in meters, you have to use the GST message, fields 6-8.

Cheers,
/dev

I first of all was using the example code I once published here:

I didn't know how to poll the error by the sensor, so I calculated the error by myself - I used it in an example for reading the values and passing them to a Lego EV3:

the calculation is done by comparing 2 successive latt + long readings by

//calculate haversine distance for linear distance

double dist( double lat1, double lat2, double long1, double long2 )
{
   double dlong = (long2 - long1) * d2r;
   double dlat = (lat2 - lat1) * d2r;
   double a = pow(sin(dlat/2.0), 2) + cos(lat1*d2r) * cos(lat2*d2r) * pow(sin(dlong/2.0), 2);
   double c = 2 * atan2(sqrt(a), sqrt(1-a));
   double d = 6367 * c;

   return d;
}

the current/actual position is calculated by a lowpass filter which filters out all noisy peaks:

static   double   fLatt=0, fLong=0, fmin, fdecsec,
                    fLattold=0, fLongold=0, fLattmean, fLongmean, fdist=0, ETA=0.7;
  if (gps.location.isValid())
  {
    fLattold = fLatt ;
    fLongold = fLong ;
       
    fLatt = (double)gps.location.lat();
    fLong = (double)gps.location.lng();
   
    fLattmean = ETA*fLatt + (1-ETA)*fLattold;  // Lowpass-Filter
    fLongmean = ETA*fLong + (1-ETA)*fLongold;
   
    fdist = dist ( fLattmean, fLattold, fLongmean, fLongold );

//...

When I want to read the number of satellites by:

    gps.satellites.value()

I had to change the code... Why?

Post the complete code. Otherwise, we're just speculating.

I can tell you that, from the Arduino's perspective in loop(), the GPS characters are coming in veeeeerrrrry slooooowwwwly. Thousands of iterations happen between each character. Eventually, the last character of one GPS sentence finally comes in, and gps.encode finally returns true.

So this is not the correct way to use TinyGPSPlus:

while (ss.available()){
    gps.encode(ss.read());
    gpsUpdated = true;
  }
  if (gpsUpdated==true){
    displayInfo();  
    gpsUpdated = false;
  }

Every time you receive one character, you set gpsUpdated = true, and that's not true. It may have just received the 3rd character of the latitude, so it doesn't even have the whole value yet.

Normally, ss.available() is a small number, like 1. It can never be bigger than 64. If the Arduino has been busy doing something else (like printing in displayInfo), serial characters get stored in the input buffer until the code gets back to the ss.available() statement. In fact, you can spend too much time elsewhere (printing >:( ), and the input buffer overflows. The 65th character and everything after is lost. That sentence from the GPS device will also be lost, so the lat, lon, satellite count, etc. from the sentence will also be lost. No fields will be updated from that sentence. Bad juju. See here for some pretty pics describing the printing problem.

As to why the original code doesn't work, I can make up two reasons without seeing the code:

1) When the last character for a sentence finally comes in, encode returns true. At this point the TinyGPSPlus example thoughtfully displays everything. -_- However, during each 1-second interval, 5 or more sentences are being sent by the GPS device. Only one of those sentences will have the satellites number in it (refer to this table do see which sentences have which pieces). So only one out of 5 calls to displayInfo will actually have an updated satellites.value().

2) TinyGPSPlus uses an "updated" flag to track which pieces change. But the instant you ask for the value, that flag is cleared. For example, the following would not work:

if (gps.altitude.valid()) {
  Serial.print( gps.altitude.value() );
  if (gps.altitude.updated) {
    // we moved!    [never happens]
    stabilize_vertical_motion();
  }
}

Printing the value clears the flag, and the next if condition is never true. Just a guess.

Cheers,
/dev

Are your latt and long values (double) working correctly?

On the Arduino, double is actually the same as the single-precision float.

no, not on Arduino.
But maybe just on AVRs which is != Arduino.
I personally am using a DUE, but for the Zero or the Yun it's supposed to be by double precision, too.

I first of all was using the example code I once published here:
mindstormsforum.de

also you may wish to again have a look at this post:
https://forum.arduino.cc/index.php?topic=348790.msg2431144#msg2431144

I first of all was using the example code I once published here:
mindstormsforum.de

Um, Ok. My posts are directed at the source of the quotes. I also wanted to him to know there is no double on most Arduinos, although I don't know which one he is using, or what his complete code looks like.

Are you trying to "Modify" your posts? You can either do the Quick Edit, in place, or the "More.../Modify" choice, on a separate page.

Cheers,
/dev

no, I didn't try to modify my posts, I just wanted to point out to some of my remarks again.
Like for double, which is NOT the same as floats "on Arduinos " :wink:

I didn't know how to poll the error by the sensor, so I calculated the error by myself...
the calculation is done by comparing 2 successive latt + long readings

That's just the jitter in the last interval. The jitter could be very small (e.g., 2m), but you could be getting a location that is 10 times further away from the actual location (e.g., 20m away from actual location).

If you are stationary and average a bunch of readings, you can reduce error in the stationary location. When you know the stationary location within a small error, you can calculate the error in one reading by comparing it.

I just wanted to point out to some of my remarks again.
Like for double, which is NOT the same as floats "on Arduinos "

Um, Ok. My posts are directed at the source of the quotes.

Now you've got me doing it. :smiling_imp:
/dev

Hi ArthurD and \dev,

yes, latt and long values (double) are working correctly rigth from the beginning. But thank you so much for your explanation, it really helped a lot and now it works incl. the velocity and the error estimate :).

Also thanks a lot because now I see the problem. I DO want to do a lot inbetween. So I think I will have a closer look to the NeoGPS lib as well and see if I can use the quite time.

The lowpassfilter may be a good idea;)!

Thanks and cheers
Tim

you're welcome, wish you good luck!
I also will have to do a lot in between, so I will transfer the GPS polling function into a low-priority seperate Due Scheduler task (loop1), slowed down by a delay(50);

BTW,
for AVRs and ARMs, there are additional pre-emptive multitasking libs available though which will make yor programming life a lot easier for the future:

for Arduino Due:
http://forum.arduino.cc/index.php?topic=318084.0
http://francois.pessaux.perso.sfr.fr/arduino.html#Babix

for AVRs (e.g., Arduino Mega):
http://forum.arduino.cc/index.php?topic=347188.0
http://www.rtos48.com/

Thanks for the good idea of using a realtime OS :), but thats a whole new world for me :)!

Currentyl I'm thinking about how to "trigger" to GPS data and having 2 different idea in mind:

1:

  • Using a second Arduino which is "constantly" reading the GPS data and stores/update them in variables.
  • Connect this arduino as a slave to my Master Arduino via I2C
  • Upon event triggered by the master send the data to the master (clearly the same data will be send when no gps update has happend inbetween...but thats no issue for me)

2:
Using something like this to directly use a GPS via I2C --> unfortunately I did not found something like this for Neo 7M and the documentation seems to be not too good let's say :wink:

This would solve all the (my) problems in cases where you have to do "lots of other stuff". What do you think :)?

Furthermore I thinking about also buying a Due (currently I use Mega) to increase speed because of some necessary calculation on the µC in my project. Is the switch from Mega to Due very easy? So I only have to change Pin allocations and so on?! Due has no EEPROM, but this is not a problem, or am I wrong?

All the best
Tim

Currentyl I'm thinking about how to "trigger" to GPS data and having 2 different idea in mind:

1: - Using a second Arduino which is "constantly" reading the GPS data and stores/update them in variables.

  • Connect this arduino as a slave to my Master Arduino via I2C
  • Upon event triggered by the master send the data to the master (clearly the same data will be send when no gps update has happend inbetween...but thats no issue for me)

Well, I'm not sure a second Arduino helps. As I mentioned over here, the parsing of NMEA GPS sentences isn't that time-consuming. Receiving something from the slave Arduino isn't going to save you much time, because there's less than 1ms to save!

The real problem is the other stuff. If you spend too much time doing the other stuff, and not reading data, the input buffer overflows.

BTW, there is already a NeoGPS data structure that is updated with the latest GPS information as it comes in... you can use that whenever you want, independent of the GPS activity. See the fused structure in NMEAfused.ino.

All the examples have two basic parts: a GPSloop for reading characters, and a data structure that can be used anywhere (e.g., fused). The doSomeWork routine is just a place-holder for something that takes longer to do. You can do whatever you want in loop, including looking at the fused data, as long as you call GPSloop frequently enough.

Furthermore I thinking about also buying a Due (currently I use Mega) to increase speed because of some necessary calculation on the µC in my project.

How long does it take to perform this calculation? Can it be performed in several steps?

FWIW, I was able to get about 500 loop() iterations per second on a Mega that was reading an IMU, compass, GPS, temp, barometer and one ADC input. It also saved to an SD card once per second (during the quiet time), but there weren't many calculations except for averaging the IMU, compass and ADC readings. YMMV.

Cheers,
/dev

Hi,

thanks for the explanations. I think I understood most of it correctly. Also thank for the answer in this post. I will try to answer to both here :slight_smile:

First: the 10Hz is more about the other values. It's OK for me if GPS only updates once a second and I get 10 times the same position in each second. The most critical operation I have in terms of time is a logging to SD.

Second:
Thanks for the explantion of the data sheet sections...I actually read those section but did not understand them without your explanation :slight_smile:

Third:

Well, I'm not sure a second Arduino helps. As I mentioned over here, the parsing of NMEA GPS sentences isn't that time-consuming. Receiving something from the slave Arduino isn't going to save you much time, because there's less than 1ms to save

The idea of a second Arduino is not so much about speed. It more about to trigger when I want to get GPS data. NeoGPS seems to be a graet lib and the concept of using the quite time would be an option. But I would prefer to trigger with the following idea:

  • Second (slave) arduino make the serial.read() very often (e.g. more often than 6ms)
  • Second arduino stores the last update of the GPS position in its own variables
  • Connect the second arduino via I2C to the Master arduino
  • Master ask the slave (whenever he want) for data

This is my point :wink:

Furthermore
Reading just 2 NMEA sentence (I need GGA and RMC -->time, lat, lon, sat, hdop, speed) is a further idea. Can be configure by NeoGPS as you have shown, correctly?! :slight_smile:

The usage of the "Due" is just an option. It will not help in terms of datalogging to the SD. But it may help when we do more calculation on the µC rather then do them later on the PC with the gathered data ;).

All the best
Tim

The most critical operation I have in terms of time is a logging to SD.

Do you mean that logging to the SD takes the most time? Or do you mean "I must write to the SD card quickly."

I think you mean the first thing... logging to the SD card takes a long time, and you can't break that into smaller steps. I would suggest a main loop() something like this:

static bool stuff_to_write = false;
static bool gps_quiet_time = false;

static void GPSloop()
{
  while (gps_port.available()) {
    gps_quiet_time = false;
    if (gps.decode( gps_port.read() ) == NMEAGPS::DECODE_COMPLETED) {
      if (gps.nmeaMessage == LAST_SENTENCE_IN_INTERVAL)
        gps_quiet_time = true;
    }
  }
}

void loop()
{
  calculateSomeStuff();
  GPSloop();
  calculateSomeMoreStuff();
  GPSloop();
  calculateTheRest();
  GPSloop();

  if (stuff_to_write && gps_quiet_time) {
    writeStuff();
    stuff_to_write = false;
  }

This keeps the input buffer from overflowing, continuously does your "other things", and only writes to the SD during the quiet time. I am assuming that the calculate routines set

    stuff_to_write = true;

and that they don't take longer than 6ms.

Note: The writeStuff() routine can safely use all the gps.fix() information:

void writeStuff()
{
   SD.print( gps.fix().satellites );
     ...

If you need to use the fix data anywhere else, like one of the calculate routines, then GPSloop must make a copy:

static bool    stuff_to_write = false;
static bool    gps_quiet_time = false;
static gps_fix latest_fix;

static void GPSloop()
{
  while (gps_port.available()) {
    gps_quiet_time = false;
    if (gps.decode( gps_port.read() ) == NMEAGPS::DECODE_COMPLETED) {
      if (gps.nmeaMessage == LAST_SENTENCE_IN_INTERVAL) {
        latest_fix = gps.fix(); //  <-- make a safe copy
        gps_quiet_time = true;
      }
    }
  }
}

The calculate routines have to use latest_fix; they cannot use gps.fix(), as it may be half-formed.

I apologize if I'm explaining something you already know. Perhaps it will help other readers.

And I apologize for pressing the 6ms issue... I just haven't seen that many reasons to dedicate the processor for that long of a time. I will say that there are plenty of libraries that do a delay or some kind of while loop that takes more than 6ms. Beware.

More in the next post...