GPS Differential Vector Pointer
Contributors:
Dustin Larsen
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);
}