GPS Differential Vector Pointer

Pages
Contributors: Dustin Larsen
Favorited Favorite 10

Code

Here is the basic code to get this system functioning. You will need to upload the files to their respective systems. There is a lot of room for you to play around with these to improve the speed or accuracy of the system. You will also need to include these libraries, which, depending on what version of the Arduino IDE you have installed, may already be included as a default library.

For help installing external Arduino libraries, visit this tutorial. Once all the libraies are installed, program each sketch from below on to the respective RedBoard.

Target Code

language:c
/*GPS Vector Pointer Target
   This sketch simiulates any system that has a GPS beacon and has the ability to
   broadcast this information for other systems to pick up. This could be a plane/drone
   a car/rover or even a solar panel on a space elevator climber. This recieves updating GPS
   coordinates and from an attached GPS reciever, parses the incoming NEMA data and
   send that information using an Xbee connection to the base station.
*/


/*We need to include a couple outside librarys. TinyGPS.h enables a set of tools to
   parse NEMA data into usable GPS info such as latitude and longitude. SoftwareSerial.h gives
   this sketch the ability to use standard DIO pins as serial ports to communicate between
   other microcontrollers. in this case we need one for reciving NEMA data and one
   to communicate using the Xbee. The standard UART on the redboard is reserved for programming
   and as a serial connection to the consol for Control and information purposes.
*/
#include <TinyGPS.h>
#include <SoftwareSerial.h>

//At this point we will define the pins used for serial communication, we
// do this so its easy to change them if needed.
#define gpsRXPIN 5
#define gpsTXPIN 4//this one is unused and doesnt have a conection
#define XbeeRXPIN 2//in this case we will also not use this pin
#define XbeeTXPIN 3

//Next we define the objects used in this sketch this includes two SoftwareSerial objects
//one for refering to the GPS connection and one for the Xbee connection. we also create an
//object that allows us to access the TinyGPS library
SoftwareSerial tGPS (gpsRXPIN, gpsTXPIN);
SoftwareSerial XBee (XbeeRXPIN, XbeeTXPIN); // RX, TX
TinyGPS gps;

//Global variables and functions are declared here, this allows them to be called anywhere
//within the code and is helpful for passing data out of functions. Dont get in the habit \
// of using these though because as your code gets longer its easy to lose track of where
//you are changing these variables and can lead to a headach when a problem arises.
float TargetLat;
float TargetLon;
int Status = 0;
//Function headers can be placed here so that functions can be placed below your setup
//and loop function for a more logical flow of information.
void SendGPSLocation(float Lat, float Lon, int Status);
void getGPS( float* lat, float* lon, int* Status);

void setup() {
  //This function is run before the your program begins to loop, here we define the status
  //of pins that are used for inputs and outputs
  pinMode(gpsRXPIN, INPUT);
  pinMode(XbeeTXPIN, OUTPUT);
  //Next communication begins between the three systems along for the baud rate for each
  //some of these can handle a larger baud rate but you need to make sure they match what
  //they are communicating with
  tGPS.begin(9600);
  Serial.begin(9600);
  XBee.begin(9600);
  while (!Serial) {}
  //a message is printed to the console showing that everything is initailized and
  //the program is now functioning
  Serial.println("Hello");

}

void loop()
//This
{
  // Recieves NEMA data from GPS reciever and Parses Latitude and longitude data
  //returns information using pointers including info on stagnant data

  //because only one Software serial port can be read at the same time we must tell
  //the redboard what to listen to. Here we tell it to listen to the tGPS serial object
  //then call the function that will recieve and parse the signal from the GPS reciver
  tGPS.listen();
  getGPS(&TargetLat, &TargetLon, &Status);
  //Print status to console to know if you are getting good data or not.
  //No Lock = 0, Old Data(>5 sec old) = 1, Good Data = 2
  Serial.println(Status);
  Serial.println(TargetLon);
  Serial.println(TargetLat);
  //we dont want to waste precious processing space or battery power by sending bad data
  //back to the base station so only when the Status variable = 2 for good data we will send
  //the information to the base station
  if (Status == 2)
    //if the data is up to data, send it to base station
  {
    //Switch Software serial port to the Xbee
    XBee.listen();
    //wait for it to become availible(might be unneccesary
    while (!XBee) {}
    //call the function that turns the info into a string and sends it to base station
    SendGPSLocation(TargetLat, TargetLon, Status);
    //wait for 2 seconds to ensure base station does not get overloaded
    delay(1000);
  }



}


void SendGPSLocation(float Lat, float Lon, int Status)
/* this sketch will send values from one modual to another(starting by sending values to XTCU)
  This will eventually send parsed GPS data from the target modual to the base station modual
*/
{
  // Convert GPS data from float into string
  String latData = String(Lat, 5);
  String lonData = String(Lon, 5);


  //Concatinate needed data into one string that begins with $ so the parser knows when data starts
  String GPSdata = "$";

  GPSdata += latData;
  GPSdata += ",";
  GPSdata += lonData;
  //Send Data as string "xx.yyyyy,-xxx,yyyyy" not able to handle changing signs 
  //but since this is a demo project thats ok
  XBee.println(GPSdata);
  //Print GPS string to console for debugging
  Serial.println(GPSdata);

}


void getGPS( float* lat, float* lon, int* Status)
/*This function switches the softserial pin to the one used for GPS then recieves NEMA data from a GPS
  reciver which is passed into a TinyGPS Object and parsed using its internal functions for $GPRMC info. This function uses
  pointers to pass infomation to pass back to parent function which includes Latitude, longitude,( velocity,
  heading) and the status of the GPS signal.

  function call where variables can be nammed whatever they want as long as they have &:
  getGPS(&latitude, &longitude, &Status);
*/
{
  //Initilize pin to recieve NEMA (have to do it here because we need to switch between
  //software serial pins (if time permits interrupts could be used)
  //pinMode(gpsRXPIN, INPUT);
  // tGPS.begin(9600);
  //define local variables
  float flat;
  float flon;
  unsigned long fix_age;

  //look for serial data from GPS and loop untill the end of NEMA string
  while (tGPS.available())
  {
    //Serial.println("YO");
    int c = tGPS.read();
    if (gps.encode(c));
    {}
  }
  //(is this the correct order for this?^^)

  //Pulled parsed data from gps object
  gps.f_get_position(&flat, &flon, &fix_age);
  *lat = flat;
  *lon = flon;
  //  float falt = gps.f_altitude(); // +/- altitude in meters
  //  float fc = gps.f_course(); // course in degrees
  //  float fmps = gps.f_speed_mps(); // speed in m/sec

  // check if data is relavent
  if (fix_age == TinyGPS::GPS_INVALID_AGE)
    //No fix detected;
  {
    *Status = 0;
  }

  else if (fix_age > 5000)
    //Warning: possible stale data!;
  {
    *Status = 1;
  }

  else
    //Data is current;
  {
    *Status = 2;
  }

}

Base Code

language:c
/* GPS Vector Pointer Base Station
  By: Dustin Larsen
  Abstract Eagle Technologies
  Date: March 20th, 2016
  (Beerware license): This code is public domain but you enjoy the project and we meet someday buy me a beer.

    This sketch controls the Base station functionality and includes the ability to
    recieve GPS coordinates from the target object and compare it to the GPS coordinates
    of the Base station GPS Location. The location of the base station will be calculated
    using a function that increases its accuracy over time by storing all previous values.
    The direction vector between the base station and the target object will then be calculated
    and a servo will be commanded to point a LASER in the direction of the target object.
*/

// gives the ability to use standard DIO pins as serial ports to communicate between other microcontrollers.
#include <SoftwareSerial.h>
//enables a set of tools to parse NEMA data into usable GPS info such as latitude and longitude
#include <TinyGPS.h>
//Enables the use of the servo Library to easily control an analog servo
#include <Servo.h>;


//pins are defined here so they can be easily referenced and changed if needed 
#define gpsRXPIN 5
#define gpsTXPIN 4
#define XbeeRXPIN 2
#define XbeeTXPIN 3
#define ServoPIN 9


//define the objects for the included librarys used in this sketch 
SoftwareSerial bGPS (gpsRXPIN, gpsTXPIN);
SoftwareSerial XBee (XbeeRXPIN, XbeeTXPIN);
TinyGPS gps;
Servo servo1;



//Global variables are declared here, this allows them to be called anywhere
//within the code and is helpful for passing data out of functions. 
float baseLat = 0;
float baseLon = 0;
float baseavgLat;
float baseavgLon;
float targetLat = 0;
float targetLon = 0;
int Status = 0;

//Function headers introduced so functions can be called from anywhere in your sketch for a more logical flow of information.
void getGPS( float* lat, float* lon, int* Status);
void RecieveGPSLocation(float* latData, float* lonData);
void ServoPointCommand(double baseLat, double baseLon, double targetLat, double targetLon);

void setup() {


 //define the status of pins that are used for inputs and outputs
  pinMode(XbeeRXPIN, INPUT);
  pinMode(gpsRXPIN, INPUT);

 //Begin communication with the XBee, the GPS reciver and the Serial console
  XBee.begin(9600);
  bGPS.begin(9600);
  Serial.begin(9600);
  //while (!Serial) {}
  //a message is printed to the console showing that everything is initailized and
  //the program is now functioning
  Serial.println("goodnight moon!");
 }

void loop() {

  //Software serial can only look at one input at a time so we must tell it what to listen to
  //here the sketch switches software serial to Xbee
  XBee.listen();
  //wait for buffer to fill
  delay(2);
  //recieve and parse target GPS location
  RecieveGPSLocation(&targetLat, &targetLon);


  //Switches software serial to GPS
  bGPS.listen();
  //wait for buffer to fill
  delay(2);
  //loops through getGPS function so that it does not stop listening before 
  //current data can be gathered
  int  count = 15;
  while ( count > 0) {
    getGPS( &baseLat, &baseLon, &Status);
    count-- ;
    //if data up to date break out of the loop and reset the count
    if (Status == 2)
    {
      bothGPSknown();
      count = 0;
      break;//redundant?
    }
  }
}

void bothGPSknown()
{
  //once both sets of coordinates are known stop listening to GPS and attach
  //the servo so that it does not recieve noise from GPS reciver(It will twich randomly if you dont do this)
  XBee.listen();
  servo1.attach(ServoPIN);
  //Average the the current base data with the existing, this will increase the accuracy over time since
  //the base hardware will not move even if the GPS data jumps around
  baseavgLat = (baseLat + baseavgLat) / 2;
  baseavgLon = (baseLon + baseavgLon) / 2;

//Print out info to Serial Console
  Serial.print("Base Latitude:");
  Serial.println(baseavgLat, 5);
  Serial.print("Base Longitude:");
  Serial.println(baseavgLon, 5);
  Serial.print("Target Latitude:");
  Serial.println(targetLat, 5);
  Serial.print("Target Longitude:");
  Serial.println(targetLon, 5);
  //passes this data GPS data to control servo
  ServoPointCommand(baseavgLat, baseavgLon, targetLat, targetLon);
  //wait after pointing angle commanded
  delay(1000);
  //Detach servo to prevent jittering while not in use
  servo1.detach();
  //Define status of data as stale 
  Status = 1;
}

void RecieveGPSLocation(float* latData, float* lonData)
/*
   This function looks for a current GPS value to be recieved from target which then reads nthe 
   compound string and places latitude and longitude data into their respective strings
   then converts those strings to float to pass variables back to main loop using pointers
*/
{
  //declares and clears variables used in this function
  String GPSdata = "";
  char inChar;
  String A = "";

  //reads buffer till string identifier is seen
  while ( inChar != '$')
  {
    inChar = XBee.read();
  }
  //once data string is identified loop till "\n" is seen which identifies the end of the data

  while ( inChar != '\n' ) {
    //wait for buffer to fill 
    delay(1);
    //read next character
    inChar = XBee.read();
    //concatinate into GPSdata string
    GPSdata += inChar;
  }



  //Parse Latitude data from string and send it back to main loop
  A = GPSdata.substring(0,  9);
  *latData = A.toFloat();
  //Clear string A
  A = "";
  //Parse Longitude data from string and send it back to main loop
  A = GPSdata.substring(9);
  *lonData = A.toFloat();

}

void getGPS( float* lat, float* lon, int* Status)
/*This function switches the softwareserial pin to the one used for the GPS reciver then recieves the NEMA data 
  which is passed into a TinyGPS Object and parsed using its internal functions for $GPRMC info. This function uses
  pointers to pass infomation to pass back to parent function which includes Latitude, longitude,and Status of the GPS signal.

  function call where variables can be nammed whatever they want as long as they have &:
  getGPS(&latitude, &longitude, &Status);
*/
{
  //define local variables
  float flat;
  float flon;
  unsigned long fix_age;

  //look for serial data from GPS and loop untill the end of NEMA string
  while (bGPS.available())
  {
    int c = bGPS.read();
    if (gps.encode(c));
    {
    }
    delay(2); //this delay is a fine lie, may have problems later
  }

  // Use tinyGPS library to parse information
  gps.f_get_position(&flat, &flon, &fix_age);
  *lat = flat;
  *lon = flon;

  // check if data is relavent and print update to serial port
  if (fix_age == TinyGPS::GPS_INVALID_AGE)
    //No fix detected;
  {
    *Status = 0;
    //delay(10);
    //Serial.println("");
    Serial.println("no GPS data recieved from base");//doesnt work when I take out print functions
  }

  else if (fix_age > 5000)
    //Warning: possible stale data!;
  {
    *Status = 1;
    // delay(10);
    Serial.println("Stale GPS data recieved from base");
  }

  else
    //Data is current;
  {
    *Status = 2;
    //delay(10);
    //Serial.println("");
    Serial.println("GPS data recieved from base");
  }

}

void ServoPointCommand(float baseLat, float baseLon, float targetLat, float targetLon)
/* THis function will take the cordinates of the base station and the target object
    then calculate the angle between then using the inverse tangent function.
*/
{
  //Define Local variables
  float latDiff, lonDiff, theta;
  double servoPosition;

  //Calculate difference
  latDiff = (targetLat - baseLat);
  lonDiff = (targetLon - baseLon);
  //Calculate angle
  theta =   atan2 (latDiff, lonDiff);
  theta = theta * 180 / 3.145;

  //Print result to console
  Serial.println("latDiff");
  Serial.println(latDiff);
  Serial.println("lonDiff");
  Serial.println(lonDiff);
  Serial.println("theta");
  Serial.println(theta);

  //Map the calculated angle into a value the servo will understand
  servoPosition = map(theta, -180, 0, 0, 180);
  servoPosition = constrain(servoPosition, 0, 180);
  servo1.write(servoPosition);

}