GPS vs Barometric Altitude

The following graph was derived by logging GPS and Barometric Altitude while I drove down highway 38 from Sugarloaf CA (near Big Bear) to Mentone, CA.  GPS readings came from a LS20031 GPS Receiver and the barometric readings were made using a Bosch BMP085, both of which I purchased from Sparkfun.  The data was relayed via an XBee radio to my laptop which was sitting on the passenger's seat next to me.  Yeah, a cable would have been simpler, but this was partially a test run for a rocket altimeter project I'm working on, so I added the XBee.

The graph was then constructed in Excel from the logged data after I used a script to reformat it into comma separated values.  The dark blue line shows the GPS altitude and the violet one shows barometric (note: the x axis represents time, not distance.)

The data was collected using a breadboarded circuit I taped to the dashboard of my car so the GPS could get a relatively clear view of the sky (at least when the mountainous terrain didn't interfere.)  The altimeter, on the other hand, just read the ambient pressure in my car with the windows closed, which may account for some of the mismatch between the GPS and barometric readings.  Or, it may be that the GPS data is off, as GPS is less accurate at measuring altitude.  I also didn't make any offset adjustments for the ambient pressure, so the two graphs are offset from each other.  However, the purpose of this test was just to get a feel for relative accuracy, which is what the yellow line shows by subtracting one reading from the other.  If you're interested, the Arduino code I used to run this test is included below.  I used a Sparkfun 3.3 volt Arduino Pro Mini to drive everything using 9 volt battery stepped down to 3.3 volts using a linear regulator.

#include <TinyGPS.h>

#include <NewSoftSerial.h>

#include <Wire.h>

#include <APM_BMP085.h> // ArduPilot Mega BMP085 Library

#define rxPin 10

#define txPin 11

TinyGPS       gps;

NewSoftSerial mySerial(rxPin, txPin);

void sendCmd (char *msg) {

  char cc;

  char chk = 0;

  mySerial.print('$', BYTE);

  while ((cc = *msg++) != '\0') {

    chk ^= cc;

    mySerial.print(cc, BYTE);

  }

  mySerial.print('*', BYTE);

  mySerial.println(chk, HEX);

}

void printFloat (double number, int digits) {

  // Handle negative numbers

  if (number < 0.0) {

     Serial.print('-');

     number = -number;

  }

  // Round correctly so that print(1.999, 2) prints as "2.00"

  double rounding = 0.5;

  for (uint8_t i = 0; i < digits; ++i)

    rounding /= 10.0;

  number += rounding;

  // Extract the integer part of the number and print it

  unsigned long int_part = (unsigned long) number;

  double remainder = number - (double) int_part;

  Serial.print(int_part);

  // Print the decimal point, but only if there are digits beyond

  if (digits > 0)

    Serial.print("."); 

  // Extract digits from the remainder one at a time

  while (digits-- > 0) {

    remainder *= 10.0;

    int toPrint = int(remainder);

    Serial.print(toPrint);

    remainder -= toPrint; 

  } 

}

void setup()  {

  Serial.begin(57600);

  mySerial.begin(9600);

  sendCmd("PMTK314,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");

  sendCmd("PMTK220,200");

  sendCmd("PMTK313,1");

  sendCmd("PMTK301,2");

  // Init BMP085

  APM_BMP085.Init(2);   // APM ADC initialization

  Serial.println("Startup");

}

byte slow;

void loop() {

  while (mySerial.available()) {

    if (gps.encode(mySerial.read()) && (slow++ & 0x0F) == 0) {

      float flat, flon;

      unsigned long age;

      gps.f_get_position(&flat, &flon, &age);

      Serial.print("Lat: ");

      printFloat(flat, 5);

      Serial.print(", Long: ");

      printFloat(flon, 5);

      Serial.print(", Alt: ");

      printFloat(gps.altitude() / 100.0, 2);

      Serial.print(" Meters, Fix age: ");

      Serial.print(age);

      Serial.println(" ms.");

      // Display Atmospheric Pressure

      float tmp_float;

      float altitude;

      APM_BMP085.Read();

      Serial.print("Pressure: ");

      Serial.print(APM_BMP085.Press);              // Pa

      Serial.print(" Pa, Temperature: ");

      Serial.print(APM_BMP085.Temp / 10.0);        // degrees C

      Serial.print(" C, Altitude: ");

      tmp_float = (APM_BMP085.Press / 101325.0);

      tmp_float = pow(tmp_float, 0.190295);

      altitude = 44330 * (1.0 - tmp_float);

      Serial.print(altitude);                      // Meters

      Serial.println(" Meters\n");

    }

  }

}

The code makes use of the ArduPilot Mega BMP085 Library, which is part of the "arducopter" project, and the GPS module is read via the TinyGPS library courtesy of Arduiniana.  The XBee setup is just done inline.  If you don't have an XBee, simple remove this setup code and log the data via the Arduino's monitor command.