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
/*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. */ //At this point we will define the pins used for serial communication, we // do this so its easy to change them if needed. //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
/* 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. //enables a set of tools to parse NEMA data into usable GPS info such as latitude and longitude //Enables the use of the servo Library to easily control an analog servo //pins are defined here so they can be easily referenced and changed if needed //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); }