├── README.md ├── code └── Arduino_GPS_BT_Spoofer │ ├── Arduino_GPS_BT_Spoofer.ino │ ├── LCD.ino │ ├── do_crc.ino │ ├── do_segment.ino │ ├── fscale.ino │ ├── output_NEMA.ino │ ├── testButtons.ino │ ├── update_alt.ino │ ├── update_wind.ino │ └── update_wind_walk.ino └── images └── by-sa.png /README.md: -------------------------------------------------------------------------------- 1 | # Arduino_GPS_Spoofer 2 | 3 | Warning: This is a proof of concept 4 | 5 | 6 | ##License 7 | 8 | 9 | 10 | All these products are released under [Creative Commons Attribution-ShareAlike 4.0 International License](http:creativecommons.org/licenses/by-sa/4.0/). 11 | 12 | Todos estos productos están liberados mediante [Creative Commons Attribution-ShareAlike 4.0 International License](http:creativecommons.org/licenses/by-sa/4.0/). 13 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/Arduino_GPS_BT_Spoofer.ino: -------------------------------------------------------------------------------- 1 | // GPS Generator and Emulator 2 | // Based on the UKHAS GPSgen Code: http://ukhas.org.uk/code:emulator 3 | // Ported to arduino by Jim Blackhurst 4 | // 5 | // 6 | // ****************************************************** 7 | // * 24/AUG/2016 * 8 | // * adapted by G4lile0 for the proof of concept * 9 | // * Arduino bluetooth GPS spoofer for Pockemon Go * 10 | // * added custom character for the bearing and * 11 | // * added Joystick management * 12 | // * https://youtu.be/VqLSKPYKeXU * 13 | // ****************************************************** 14 | // Original code of the great HABsim at https://github.com/jimthree/HABsim 15 | // 16 | 17 | 18 | #include // from http://arduiniana.org/libraries/pstring/ 19 | #include // from https://github.com/PaulStoffregen/Time 20 | #include 21 | #include 22 | 23 | // radians to degrees 24 | #define DEGREES(x) ((x) * 57.295779513082320877) 25 | // degrees to radians 26 | #define RADIANS(x) ((x) / 57.295779513082320877) 27 | 28 | #define LOG_BASE 1.4142135623730950488 29 | #define LOG_POWER 5300.0 30 | 31 | //#define PI 3.141592653589793 32 | 33 | #define WIND_TURBULENCE 10 // chance of the wind changing the bearing by up to 1 degree per cycle of the simulation 34 | 35 | //#define LAUNCH_LAT 52.213389 36 | //#define LAUNCH_LON 0.096834 37 | #define LAUNCH_LAT 40.4165 38 | #define LAUNCH_LON -3.7025 39 | #define LAUNCH_ALT 600 40 | 41 | #define TERMINAL_VELOCITY 40 // meters per second 42 | #define ASCENT_RATE 5 // meters per second 43 | #define DESCENT_RATE 5 // meters per second 44 | 45 | //should take 20secs to do 100m 46 | //currently takes 50sec to do 100m 47 | 48 | //http://www.engineeringtoolbox.com/air-altitude-pressure-d_462.html 49 | //http://en.wikipedia.org/wiki/Atmospheric_pressure 50 | #define LAUNCH_KPA 100 51 | #define BURST_KPA 0.02 52 | #define GPS_HZ 1 53 | 54 | // SIM_HZ - the internal speed of the simulation 55 | // if you are going to change this then you will need to scale simAccel acordingly 56 | // Faster updates result in smaller distances per step being fed into the horizontal speed 57 | // Equation. Due to the ATMEGA's 8bit arcitecture and 4 byte floats, it looses precision 58 | // and Lon stops updating. 20Hz is the reccomneded value. 59 | #define SIM_HZ 10 60 | 61 | 62 | // DEBUG status - output either the intended NMEA sentances, or debug data 63 | // 1 = debug, 0 = NMEA 64 | #define DEBUG 0 65 | 66 | // Define the pins used by the Liquid Crystal Display 67 | //LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 68 | LiquidCrystal lcd(12, 11, 5, 4, 3, 2); 69 | 70 | // Joystick definition 71 | 72 | #define AXES_X_PIN A3 73 | #define AXES_Y_PIN A4 74 | 75 | int Axes_X = 0 ; 76 | int Axes_Y = 0 ; 77 | 78 | 79 | // Status hold the current state of the sim. 80 | // 0 = Ascent, 1 = Descent, 2 = Landed 81 | int Status =0; 82 | 83 | // simAccel - an artifical acceleration factor for speeds. 84 | // This value can be used to maintain a acurate simulation of both the vertical and 85 | // horizontal speeds by compensating for the time it takes to process the calculations 86 | // It can also be used to run the simulation at a fast-forward speed 87 | // With the sim running at 20Hz, a simAccel value of 1.4 will give accurate speeds 88 | float simAccel = 50; 89 | 90 | time_t Now; 91 | char buf[128]; 92 | 93 | float CurLon,CurLat,CurAlt,CurKPA,CurSpeed; 94 | unsigned long update_counter; 95 | unsigned long output_counter; 96 | float ascent_rate_mod = 0; 97 | float tropo_wind_rate_mod = 0; 98 | float strat_wind_rate_mod = 0; 99 | //float lat_wind = 0; 100 | //float lon_wind = 0; 101 | float Bearing = 0.0; 102 | 103 | float walk_Speed = 10.0; 104 | 105 | float distancePerStep = 0.0; 106 | float Drag; 107 | 108 | 109 | long speedTest =0; 110 | float msTest =0; 111 | float speedTestResult = 0; 112 | 113 | byte balloon[8] = {B01110, B11111, B11111, B11111, B01110, B00100, B01110, B01110}; 114 | byte chute[8] = {B11111, B11111, B10001, B10001, B01010, B00100, B01110, B01110}; 115 | byte land[8] = {B00000, B00000, B00000, B11111, B10101, B00100, B01110, B01110}; 116 | byte NE[8] = {0b00000,0b00111,0b00011,0b00101,0b01000,0b10000,0b00000,0b00000}; 117 | byte SEast[8] = {0b00000,0b00000,0b10000,0b01000,0b00101,0b00011,0b00111,0b00000}; 118 | byte SW[8] = {0b00000,0b00000,0b00001,0b00010,0b10100,0b11000,0b11100,0b00000}; 119 | byte NW[8] = {0b00000,0b11100,0b11000,0b10100,0b00010,0b00001,0b00000,0b00000}; 120 | byte scale_4[8] = {B11111, B00000, B00100, B00000, B00100, B00000, B00100, B00000}; 121 | 122 | 123 | void setup() 124 | { 125 | // open the serial port at 9600 bps: 126 | Serial.begin(115200); 127 | 128 | 129 | // set up the LCD's number of columns and rows: 130 | lcd.begin(16, 2); 131 | lcd.createChar(0, balloon); 132 | lcd.createChar(1, chute); 133 | lcd.createChar(2, land); 134 | lcd.createChar(3, NE); 135 | lcd.createChar(4, SEast); 136 | lcd.createChar(5, SW); 137 | lcd.createChar(6, NW); 138 | lcd.createChar(7, scale_4); 139 | 140 | long tempBits = 0; // create a long of random bits to use as seed 141 | for (int i=1; i<=32 ; i++) 142 | { 143 | tempBits = ( tempBits | ( analogRead( 0 ) & 1 ) ) << 1; 144 | } 145 | randomSeed(tempBits); // seed 146 | 147 | Bearing = random(359); 148 | 149 | 150 | setTime(14,14,14,23,8,2016); //Hardcode the datetime, this is effectivly the 'launch' time 151 | Now = now(); //assign the current time to the variable 'Now' 152 | 153 | // get first waypoint co-ordinate as launch position 154 | CurLon = LAUNCH_LON; 155 | CurLat = LAUNCH_LAT; 156 | CurAlt = LAUNCH_ALT; 157 | CurKPA = LAUNCH_KPA; 158 | 159 | update_counter = millis(); 160 | output_counter = millis(); 161 | 162 | lcd.clear(); 163 | 164 | speedTest = millis(); 165 | msTest = CurAlt; 166 | } 167 | 168 | 169 | void loop() 170 | { 171 | 172 | 173 | 174 | 175 | Axes_X=map(analogRead(AXES_X_PIN),0,1023,-256,256); 176 | // this small pause is needed between reading 177 | // analog pins, otherwise we get the same value twice 178 | delay(100); 179 | Axes_Y=map(analogRead(AXES_Y_PIN),0,1023,256,-256); 180 | 181 | 182 | if (abs(Axes_X)<5 & abs(Axes_Y)<5) { 183 | 184 | Axes_X=0; 185 | Axes_Y=0; 186 | Bearing=0; 187 | } 188 | 189 | walk_Speed = hypot((Axes_X/100),(Axes_Y/100)); 190 | 191 | // walk_Speed = sqrt((Axes_X*Axes_X/20)+(Axes_Y*Axes_Y/20)); 192 | 193 | // delay(1000); 194 | 195 | 196 | Bearing=atan2(Axes_X,Axes_Y)*4068/71; 197 | 198 | // if joystick is in the center.. doesn't move. 199 | 200 | if (Bearing<0) Bearing=360+Bearing; // to adjust the degrees to Bearing 201 | 202 | if (abs(Axes_X)<10 & abs(Axes_Y)<10) { 203 | 204 | Axes_X=0; 205 | Axes_Y=0; 206 | Bearing=0; 207 | } 208 | 209 | 210 | if(DEBUG) Serial.println(); 211 | if(DEBUG) Serial.print(Axes_X); 212 | if(DEBUG) Serial.println(); 213 | if(DEBUG) Serial.print(Axes_Y); 214 | if(DEBUG) Serial.println(); 215 | if(DEBUG) Serial.print(Bearing); 216 | 217 | if(DEBUG) Serial.println(); 218 | 219 | 220 | 221 | Now = now(); 222 | float windOffset = random(10); 223 | if (random(WIND_TURBULENCE)==1) Bearing += (windOffset-5)/10; 224 | if (Bearing>359) Bearing = 0; 225 | if (Bearing<0) Bearing = 359; 226 | 227 | if(DEBUG) Serial.println(); 228 | if(DEBUG) Serial.print(speedTestResult); 229 | if(DEBUG) Serial.println(); 230 | if(DEBUG) Serial.print(speedTestResult); 231 | 232 | if(DEBUG) Serial.println(); 233 | 234 | if (CurAlt>2000 && CurAlt<2100) Bearing = random(359); 235 | 236 | 237 | 238 | LCDFlight(); 239 | 240 | if (Status != 2) 241 | { 242 | if (millis() > (update_counter + (1000/SIM_HZ))) 243 | { 244 | update_alt(); 245 | 246 | 247 | distancePerStep = (walk_Speed/SIM_HZ)*simAccel; 248 | 249 | updateWindWalk(CurLat, CurLon, Bearing, distancePerStep); 250 | 251 | if(DEBUG) Serial.print("[t"); 252 | if(DEBUG) Serial.print(millis()-update_counter); 253 | if(DEBUG) Serial.print(" :VsA "); 254 | if(DEBUG) Serial.print(speedTestResult); 255 | if(DEBUG) Serial.print("] "); 256 | 257 | update_counter = millis(); 258 | 259 | if(DEBUG) Serial.println(); 260 | } 261 | 262 | if (millis() > (output_counter + (1000/GPS_HZ))) 263 | { 264 | if (!DEBUG) Output_NEMA(Now,CurLat,CurLon,CurAlt,Bearing,CurSpeed); 265 | output_counter = millis(); 266 | } 267 | } 268 | 269 | 270 | 271 | 272 | if (millis()>=(speedTest+10000)) 273 | { 274 | speedTestResult = (CurAlt-msTest)/10; 275 | msTest=CurAlt; 276 | speedTest = millis(); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/LCD.ino: -------------------------------------------------------------------------------- 1 | void LCDFlight() 2 | { 3 | //Lat & lon 4 | if(CurLat>0&&CurLat<10) lcd.setCursor(2, 0); //eg. 2.345 5 | if(CurLat>10) lcd.setCursor(1, 0); //eg. 23.456 6 | if(CurLat<0&&CurLat>-10) lcd.setCursor(1, 0); //eg. -2.345 7 | if(CurLat<-10) lcd.setCursor(0, 0); //eg. -23.456 8 | lcd.print(CurLat,3); 9 | 10 | if(CurLon>0&&CurLon<10) lcd.setCursor(2, 1); //eg. 2.345 11 | if(CurLon>10) lcd.setCursor(1, 1); //eg. 23.456 12 | if(CurLon<0&&CurLon>-10) lcd.setCursor(1, 1); //eg. -2.345 13 | if(CurLon<-10) lcd.setCursor(0, 1); //eg. -23.456 14 | lcd.print(CurLon,3); 15 | 16 | 17 | //Current Alt 18 | if(walk_Speed>10000) 19 | { 20 | lcd.setCursor(8, 0); 21 | } 22 | 23 | if(walk_Speed<10000) 24 | { 25 | lcd.setCursor(8, 0); 26 | lcd.print(" "); 27 | lcd.setCursor(9, 0); 28 | } 29 | 30 | if(walk_Speed<1000) 31 | { 32 | lcd.setCursor(8, 0); 33 | lcd.print(" "); 34 | lcd.setCursor(10, 0); 35 | } 36 | 37 | if(walk_Speed<100) 38 | { 39 | lcd.setCursor(8, 0); 40 | lcd.print(" "); 41 | lcd.setCursor(10, 0); 42 | } 43 | 44 | if(walk_Speed<10) 45 | { 46 | lcd.setCursor(8, 0); 47 | lcd.print(" "); 48 | lcd.setCursor(11, 0); 49 | } 50 | 51 | if(walk_Speed<1) 52 | { 53 | lcd.setCursor(8, 0); 54 | lcd.print(" "); 55 | lcd.setCursor(12, 0); 56 | } 57 | 58 | lcd.print(walk_Speed,0); 59 | 60 | 61 | 62 | // Current Speed ////////////BUG HERE//////////// 63 | 64 | // if(CurSpeed>9) 65 | // { 66 | // lcd.setCursor(14, 0); 67 | // } 68 | // if(CurSpeed<10) 69 | // { 70 | // lcd.setCursor(14, 0); 71 | // lcd.print(" "); 72 | // lcd.setCursor(15, 0); 73 | // } 74 | // lcd.print(CurSpeed,0); 75 | 76 | 77 | 78 | 79 | 80 | // Current Bearing 81 | 82 | if(Bearing>100) 83 | { 84 | lcd.setCursor(10, 1); 85 | } 86 | 87 | if(Bearing<100) 88 | { 89 | lcd.setCursor(10, 1); 90 | lcd.print(" "); 91 | lcd.setCursor(11, 1); 92 | } 93 | 94 | if(Bearing<10) 95 | { 96 | lcd.setCursor(10, 1); 97 | lcd.print(" "); 98 | lcd.setCursor(12, 1); 99 | } 100 | lcd.print(Bearing,0); 101 | 102 | 103 | 104 | // Status Icon 105 | 106 | // lcd.setCursor(15, 1); 107 | 108 | // if(Status==0) lcd.print((char)0); 109 | // if(Status==1) lcd.print((char)1); 110 | // if(Status==2) lcd.print((char)2); 111 | 112 | 113 | 114 | // graph Bearing 115 | float graph_pos = fscale(0,360,0,9,Bearing,0); 116 | 117 | 118 | lcd.setCursor(8, 0); 119 | if(int(graph_pos)==0) lcd.print((char)0b00011000); // N 120 | if(int(graph_pos)==1) lcd.print((char)3); // NE 121 | if(int(graph_pos)==2) lcd.print((char)0b00011010); // E --> 122 | if(int(graph_pos)==3) lcd.print((char)4); // SE 123 | if(int(graph_pos)==4) lcd.print((char)0b00011001); // S 124 | if(int(graph_pos)==5) lcd.print((char)5); // SW 125 | if(int(graph_pos)==6) lcd.print((char)0b00011011); // W 126 | if(int(graph_pos)==7) lcd.print((char)6); // NW 127 | if(int(graph_pos)==8) lcd.print((char)0b00011000); // N 128 | 129 | lcd.setCursor(8, 1); 130 | 131 | if(int(graph_pos)==0) lcd.print((char)0b00011000); // N 132 | if(int(graph_pos)==1) lcd.print((char)3); // NE 133 | if(int(graph_pos)==2) lcd.print((char)0b00011010); // E --> 134 | if(int(graph_pos)==3) lcd.print((char)4); // SE 135 | if(int(graph_pos)==4) lcd.print((char)0b00011001); // S 136 | if(int(graph_pos)==5) lcd.print((char)5); // SW 137 | if(int(graph_pos)==6) lcd.print((char)0b00011011); // W 138 | if(int(graph_pos)==7) lcd.print((char)6); // NW 139 | if(int(graph_pos)==8) lcd.print((char)0b00011000); // N 140 | 141 | 142 | } 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/do_crc.ino: -------------------------------------------------------------------------------- 1 | // calculate a CRC for the line of input 2 | // tested against http://nmeachecksum.eqth.net/ 3 | 4 | void do_crc(char *pch) 5 | { 6 | unsigned char crc; 7 | 8 | //PString CRC_buf(buf, sizeof(buf)); 9 | 10 | if (*pch != '$') 11 | return; // does not start with '$' - so can't CRC 12 | 13 | pch++; // skip '$' 14 | crc = 0; 15 | 16 | // scan between '$' and '*' (or until CR LF or EOL) 17 | while ((*pch != '*') && (*pch != '\0') && (*pch != '\r') && (*pch != '\n')) 18 | { // checksum calcualtion done over characters between '$' and '*' 19 | crc ^= *pch; 20 | pch++; 21 | } 22 | 23 | // add or re-write checksum 24 | //sprintf(pch,"*%02X\r\n",(unsigned int)crc); 25 | 26 | Serial.println((unsigned int)crc,HEX); 27 | 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/do_segment.ino: -------------------------------------------------------------------------------- 1 | void do_segment(double FromLat, double FromLon, double FromAlt, double ToLat, double ToLon, double ToAlt) 2 | { 3 | double Rate; // ascent(+ve) / decent(-ve) rate meters per second (for segment) 4 | int Duration; // calculated segment duration in seconds 5 | double Elapsed; // floating point duration 6 | double Lat,Lon,Alt; // clacualted for each step 7 | double Course,Speed; // calculated for entire segment 8 | double Distance; // distance between coordinates 9 | double DeltaLat,DeltaLon,DeltaAlt; // Latitude, Longtitude and Altitude differences 10 | double AdjLon; // Adjusted Longtitude difference 11 | int i; // counter 12 | 13 | DeltaAlt = (ToAlt - FromAlt); 14 | DeltaLat = (ToLat - FromLat); 15 | DeltaLon = (ToLon - FromLon); 16 | 17 | 18 | if (DeltaAlt >= 0) 19 | { // ascending 20 | Rate = 200.0; 21 | } 22 | else 23 | { // descending - clculate the geometric mean of the expected velocities at To and From altitude 24 | Rate = -200.0 * sqrt(pow(LOG_BASE,(FromAlt / LOG_POWER)) * pow(LOG_BASE,(ToAlt / LOG_POWER))); 25 | } 26 | 27 | // calcualte time (secs) between co-ordinates 28 | Elapsed = DeltaAlt / Rate; // always positive 29 | Duration = (int)Elapsed; 30 | if ((Elapsed - (float)Duration) >= 0.5) 31 | Duration++; // round duration of segment to nearest integer 32 | 33 | // Calculate Course (degrees relative to north) for entire segment 34 | Course = atan2(DeltaLat,DeltaLon); // result is +PI radians (cw) to -PI radians (ccw) from x (Longtitude) axis 35 | 36 | Course = DEGREES(Course); // convert radians to degrees 37 | if (Course <= 90.0) 38 | Course = 90.0 - Course; 39 | else 40 | Course = 450.0 - Course; // convert to 0 - 360 clockwise from north 41 | 42 | // Calculate Speed (m/sec) for entire segment 43 | AdjLon = cos(RADIANS((FromLat + ToLat) / 2.0)) * DeltaLon; 44 | Distance = (sqrt((DeltaLat * DeltaLat) + (AdjLon * AdjLon)) * 111194.9266); // result in meters 45 | 46 | Speed = Distance / (double)Duration; // meters per second 47 | 48 | // calculate 1 second "step" sizes 49 | DeltaAlt /= (double)Duration; 50 | DeltaLat /= (double)Duration; 51 | DeltaLon /= (double)Duration; 52 | 53 | // now output the NMEA for each step between From and To (but excluding To - which is picked up on next segment) 54 | Lat = FromLat; 55 | Lon = FromLon; 56 | Alt = FromAlt; 57 | 58 | for (i = 0; i < Duration; i++) 59 | { 60 | delay(1000); //simulate GPS at 1hz 61 | Output_NEMA(Now,Lat,Lon,Alt,Course,Speed); 62 | Now++; // 1 second steps 63 | Lat += DeltaLat; 64 | Lon += DeltaLon; 65 | Alt += DeltaAlt; 66 | 67 | 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/fscale.ino: -------------------------------------------------------------------------------- 1 | float fscale( float originalMin, float originalMax, float newBegin, float newEnd, float inputValue, float curve){ 2 | 3 | float OriginalRange = 0; 4 | float NewRange = 0; 5 | float zeroRefCurVal = 0; 6 | float normalizedCurVal = 0; 7 | float rangedValue = 0; 8 | int invFlag = 0; 9 | 10 | 11 | // condition curve parameter 12 | // limit range 13 | 14 | if (curve > 10) curve = 10; 15 | if (curve < -10) curve = -10; 16 | 17 | curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output 18 | curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function 19 | 20 | /* 21 | println(curve * 100, DEC); // multply by 100 to preserve resolution 22 | println(); 23 | */ 24 | 25 | // Check for out of range inputValues 26 | if (inputValue < originalMin) { 27 | inputValue = originalMin; 28 | } 29 | if (inputValue > originalMax) { 30 | inputValue = originalMax; 31 | } 32 | 33 | // Zero Refference the values 34 | OriginalRange = originalMax - originalMin; 35 | 36 | if (newEnd > newBegin){ 37 | NewRange = newEnd - newBegin; 38 | } 39 | else 40 | { 41 | NewRange = newBegin - newEnd; 42 | invFlag = 1; 43 | } 44 | 45 | zeroRefCurVal = inputValue - originalMin; 46 | normalizedCurVal = zeroRefCurVal / OriginalRange; // normalize to 0 - 1 float 47 | 48 | /* 49 | print(OriginalRange, DEC); 50 | print(" "); 51 | print(NewRange); 52 | print(" "); 53 | println(zeroRefCurVal); 54 | println(); 55 | */ 56 | 57 | // Check for originalMin > originalMax - the math for all other cases i.e. negative numbers seems to work out fine 58 | if (originalMin > originalMax ) { 59 | return 0; 60 | } 61 | 62 | if (invFlag == 0){ 63 | rangedValue = (pow(normalizedCurVal, curve) * NewRange) + newBegin; 64 | 65 | } 66 | else // invert the ranges 67 | { 68 | rangedValue = newBegin - (pow(normalizedCurVal, curve) * NewRange); 69 | } 70 | 71 | return rangedValue; 72 | } 73 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/output_NEMA.ino: -------------------------------------------------------------------------------- 1 | // Speed in Kph 2 | // Course over ground relative to North 3 | // 4 | // In normal operation the GPS emulated sends the following sequence of messages 5 | // $GPGGA, $GPRMC, $GPVTG 6 | // $GPGGA, $GPGSA, $GPRMC, $GPVTG 7 | // $GPGGA, $GPGSV ... $GPGSV, $GPRMC, $GPVTG 8 | 9 | 10 | // Output_NEMA - Output the position in NMEA 11 | // Latitude & Longtitude in degrees, Altitude in meters Speed in meters/sec 12 | 13 | void Output_NEMA(time_t Time, double Lat, double Lon, double Alt, double Course, double Speed) 14 | { 15 | int LatDeg; // latitude - degree part 16 | double LatMin; // latitude - minute part 17 | char LatDir; // latitude - direction N/S 18 | int LonDeg; // longtitude - degree part 19 | double LonMin; // longtitude - minute part 20 | char LonDir; // longtitude - direction E/W 21 | 22 | if (Lat >= 0) 23 | LatDir = 'N'; 24 | else 25 | LatDir = 'S'; 26 | 27 | Lat = fabs(Lat); 28 | LatDeg = (int)(Lat); 29 | LatMin = (Lat - (double)LatDeg) * 60.0; 30 | 31 | if (Lon >= 0) 32 | LonDir = 'E'; 33 | else 34 | LonDir = 'W'; 35 | 36 | Lon = fabs(Lon); 37 | LonDeg = (int)(Lon); 38 | LonMin = (Lon - (double)LonDeg) * 60.0; 39 | 40 | // $GPGGA - 1st in epoc - 5 satellites in view, FixQual = 1, 45m Geoidal separation HDOP = 2.4 41 | 42 | PString GPGGA(buf, sizeof(buf)); 43 | 44 | GPGGA.print("$GPGGA,"); 45 | GPGGA.print(hour(Time)); 46 | GPGGA.print(minute(Time)); 47 | GPGGA.print(second(Time)); 48 | GPGGA.print(","); 49 | GPGGA.print(LatDeg); 50 | if (LatMin < 10) GPGGA.print("0"); 51 | GPGGA.print(LatMin); 52 | GPGGA.print(","); 53 | GPGGA.print(LatDir); 54 | GPGGA.print(","); 55 | GPGGA.print(LonDeg); 56 | if (LonMin < 10) GPGGA.print("0"); 57 | GPGGA.print(LonMin); 58 | GPGGA.print(","); 59 | GPGGA.print(LonDir); 60 | GPGGA.print(",1,05,02.4,"); 61 | GPGGA.print(Alt); 62 | GPGGA.print(",M,45.0,M,,*"); 63 | 64 | Serial.print(buf); // Print Buf 65 | do_crc(buf); // Print CRC 66 | 67 | switch((int)Time % 3) 68 | { // include 'none' or $GPGSA or $GPGSV in 3 second cycle 69 | case 0: 70 | { 71 | break; 72 | } 73 | case -1: 74 | { 75 | // 3D fix - 5 satellites (3,7,18,19 & 22) in view. PDOP = 3.3,HDOP = 2.4, VDOP = 2.3 76 | PString GPGSA(buf, sizeof(buf),"$GPGSA,A,3,03,07,18,19,22,,,,,,,,3.3,2.4,2.3*"); 77 | 78 | Serial.print(buf); // Print Buf 79 | do_crc(buf); // Print CRC 80 | break; 81 | } 82 | case -2: 83 | { 84 | // two lines of GPGSV messages - 1st line of 2, 8 satellites being tracked in total 85 | // 03,07 in view 11,12 being tracked 86 | PString GPGSV1(buf, sizeof(buf),"$GPGSV,2,1,08,03,89,276,30,07,63,181,22,11,,,,12,,,*"); 87 | Serial.print(buf); // Print Buf 88 | do_crc(buf); // Print CRC 89 | // GPGSV 2nd line of 2, 8 satellites being tracked in total 90 | // 18,19,22 in view 27 being tracked 91 | PString GPGSV2(buf, sizeof(buf),"$GPGSV,2.2,08,18,73,111,35,19,33,057,27,22,57,173,37,27,,,*"); 92 | Serial.print(buf); // Print Buf 93 | do_crc(buf); // Print CRC 94 | break; 95 | } 96 | } 97 | 98 | //$GPRMC 99 | //sprintf(buf,"$GPRMC,%02d%02d%02d.000,A,%02d%07.4f,%c,%03d%07.4f,%c,%.2f,%.2f,%02d%02d%02d,,,A*", 100 | // hour(Time),minute(Time),second(Time),LatDeg,LatMin,LatDir,LonDeg,LonMin,LonDir,Speed * 1.943844,Course,day(Time),month(Time) + 1,year(Time) % 100); 101 | 102 | PString GPRMC(buf, sizeof(buf)); 103 | 104 | GPRMC.print("$GPRMC,"); 105 | GPRMC.print(hour(Time)); 106 | GPRMC.print(minute(Time)); 107 | GPRMC.print(second(Time)); 108 | GPRMC.print(",A,"); 109 | GPRMC.print(LatDeg); 110 | if (LatMin < 10) GPRMC.print("0"); 111 | GPRMC.print(LatMin); 112 | GPRMC.print(","); 113 | GPRMC.print(LatDir); 114 | GPRMC.print(","); 115 | GPRMC.print(LonDeg); 116 | if (LonMin < 10) GPRMC.print("0"); 117 | GPRMC.print(LonMin); 118 | GPRMC.print(","); 119 | GPRMC.print(LonDir); 120 | 121 | GPRMC.print(","); 122 | GPRMC.print(Speed * 1.943844); 123 | GPRMC.print(","); 124 | GPRMC.print(Course); 125 | GPRMC.print(","); 126 | GPRMC.print(day(Time)); 127 | GPRMC.print(month(Time) + 1); 128 | GPRMC.print(year(Time) % 100); 129 | GPRMC.print(",,,A*"); 130 | 131 | Serial.print(buf); // Print Buf 132 | do_crc(buf); // Print CRC 133 | 134 | // $GPVTG message last in epoc 135 | //sprintf(buf,"$GPVTG,%.2f,T,,,%.2f,N,%.2f,K,A*",Course,Speed * 1.943844,Speed * 3.6); 136 | 137 | PString GPVTG(buf, sizeof(buf)); 138 | 139 | GPVTG.print("$GPVTG,"); 140 | GPVTG.print(Course); 141 | GPVTG.print(",T,,,"); 142 | GPVTG.print(Speed * 1.943844); 143 | GPVTG.print(",N,"); 144 | GPVTG.print(Speed * 3.6); 145 | GPVTG.print(",K,A*"); 146 | 147 | Serial.print(buf); // Print Buf 148 | do_crc(buf); // Print CRC 149 | 150 | 151 | } 152 | 153 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/testButtons.ino: -------------------------------------------------------------------------------- 1 | /* 2 | void testButtons() 3 | { 4 | int value = analogRead(0); 5 | 6 | if (value>1020) return; 7 | 8 | if (value>700&&value<800) // select 9 | { 10 | return; 11 | } 12 | 13 | if (value>0&&value<100) // right 14 | { 15 | lastMenuScreen++; 16 | LCDMenu(lastMenuScreen); 17 | } 18 | 19 | if (value>400&&value<550) // right 20 | { 21 | lastMenuScreen--; 22 | LCDMenu(lastMenuScreen); 23 | } 24 | } 25 | */ 26 | 27 | 28 | /* 29 | // select 722 30 | // left 480 31 | // right 0 32 | // up 131 33 | // down 307/8 34 | 35 | int adc_key_val[5] ={0, 150, 360, 535, 760 }; 36 | 37 | */ 38 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/update_alt.ino: -------------------------------------------------------------------------------- 1 | void update_alt() 2 | { 3 | 4 | 5 | // If the Current altitude ever falls below the LAUNCH _ALT, then the payload is deemed to have landed 6 | if (CurAlt < LAUNCH_ALT) 7 | { 8 | Status = 2; 9 | return; 10 | } 11 | 12 | // http://artvb.oatmeal.dhs.org/?c=Glider 13 | // update altitude 14 | 15 | if (Status == 0) // Ascent 16 | { 17 | // while Ascending, the speed is simply the ASCENT_RATE 18 | CurSpeed = ASCENT_RATE; 19 | 20 | // We currently use a linear model for asscent, based on the ASCENT_RATE 21 | // modificado para que no suba. 22 | // CurAlt += (CurSpeed*simAccel)/SIM_HZ; 23 | 24 | // As we ascend the atmospheric pressure reduces. Again we are using a linear aproximation 25 | // although there are well published equations for how KpA falls off with altitude. 26 | CurKPA = getPressure(CurAlt); 27 | 28 | // If the outside pressure falls below that of what is defined as BURST_KPA, the balloon is 29 | // deemed to have popped, and the payload starts it's descent 30 | if (CurKPA < BURST_KPA) Status = 1; 31 | } 32 | 33 | if (Status == 1) //Descent 34 | { 35 | // As we descend back though the atmospheric pressure increases. 36 | // Again we are using a linear aproximation which needs to be fixed 37 | CurKPA = getPressure(CurAlt); 38 | 39 | // The payload initaly falls at it's terminal velocity 40 | // (although it should really accelerate towards it's TV at 9.8m/s sq - ignored for simplicity) 41 | // as the KpA increases, so does drag, eventualy slowing 42 | // the payload to it's ideal descent rate. 43 | 44 | 45 | // Drag is a normalised value bsed on the current KPA where the KPA at burst altitude 46 | // results in a vale for Drag ~1 and the KPA at sea level is gives a value ~= 0. 47 | // this is effectivly saying how far removed CurSpeed is from TERMINAL_VELOCITY 48 | 49 | // Note: these are (obviously) not real equations for calculating Drag, they simply simulate the behaviour. 50 | 51 | Drag = 1-fscale(0,100,0,1,CurKPA,0); 52 | CurSpeed = TERMINAL_VELOCITY*Drag; 53 | 54 | // the payload is not able to Fall at speeds slower than it's targeted DESCENT_RATE 55 | if (CurSpeed 1e-12) 65 | { 66 | cos2SigmaM = cos(2*sigma1 + sigma); 67 | sinSigma = sin(sigma); 68 | cosSigma = cos(sigma); 69 | deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM) - B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM))); 70 | sigmaP = sigma; 71 | sigma = s / (b*A) + deltaSigma; 72 | } 73 | 74 | float tmp = sinU1*sinSigma - cosU1*cosSigma*cosAlpha1; 75 | float lat2 = atan2(sinU1*cosSigma + cosU1*sinSigma*cosAlpha1,(1-f)*sqrt(sinAlpha*sinAlpha + tmp*tmp)); 76 | float lambda = atan2(sinSigma*sinAlpha1, cosU1*cosSigma - sinU1*sinSigma*cosAlpha1); 77 | float C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha)); 78 | float L = lambda - (1-C) * f * sinAlpha * (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM))); 79 | 80 | float lon2 = fmod((radians(lon1)+L+3*PI) , (2*PI)) - PI; // normalise to -180...+180 81 | 82 | float revAz = atan2(sinAlpha, -tmp); // final bearing, if required 83 | 84 | CurLat = degrees(lat2); 85 | CurLon = degrees(lon2); 86 | 87 | // if(DEBUG) Serial.print(" :: WindBearing:",6); 88 | // if(DEBUG) Serial.print(Bearing); 89 | if(DEBUG) Serial.print(":: bearing:"); 90 | if(DEBUG) Serial.print(brng, 2); 91 | if(DEBUG) Serial.print(" :: d: "); 92 | if(DEBUG) Serial.print(distancePerStep,4); 93 | if(DEBUG) Serial.print(" :: CurLat: "); 94 | if(DEBUG) Serial.print(CurLat,8); 95 | if(DEBUG) Serial.print(" :: CurLon: "); 96 | if(DEBUG) Serial.print(CurLon,8); 97 | 98 | } 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | // would not have been possible if it wasn't for: 121 | // http://www.movable-type.co.uk/scripts/latlong.html 122 | // Destination point given distance and bearing from start point 123 | 124 | // useful blending function: 125 | // http://danthompsonsblog.blogspot.com/2009/02/smoothstep-interpolation-with-arduino.html 126 | 127 | 128 | void update_wind() 129 | { 130 | 131 | /** 132 | * Returns the destination point from this point having travelled the given distance (in km) on the 133 | * given initial bearing (bearing may vary before destination is reached) 134 | * 135 | * see http://williams.best.vwh.net/avform.htm#LL 136 | * 137 | * @param {Number} brng: Initial bearing in degrees 138 | * @param {Number} dist: Distance in km 139 | * @returns {LatLon} Destination point 140 | 141 | 142 | //LatLon.prototype.destinationPoint = function(brng, dist) { 143 | dist = typeof(dist)=='number' ? dist : typeof(dist)=='string' && dist.trim()!='' ? +dist : NaN; 144 | dist = dist/this._radius; // convert dist to angular distance in radians 145 | brng = brng.toRad(); // 146 | 147 | 148 | var lat1 = this._lat.toRad(), lon1 = this._lon.toRad(); 149 | 150 | var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) ); 151 | 152 | var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1), Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2)); 153 | 154 | 155 | lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180º 156 | 157 | return new LatLon(lat2.toDeg(), lon2.toDeg()); 158 | //} 159 | */ 160 | 161 | /* 162 | int radius = 6371; // mean radius of the earth 163 | int Bearing = 90; // Bearing of travel 164 | int Dist = 1; // km per update 165 | Bearing = radians(Bearing); // convert Bearing to Radians 166 | Dist = Dist/radius; // convert dist to angular distance in radians 167 | 168 | float DestLat = asin(sin(CurLat)*cos(Dist)+cos(CurLat)*sin(Dist)*cos(Bearing)); 169 | float DestLon = CurLon + atan2(sin(Bearing)*sin(Dist)*cos(CurLat),cos(Dist)-sin(CurLat)*sin(DestLat)); 170 | 171 | 172 | DestLon = fmod((DestLon+3*PI),(2*PI)) - PI; // normalise to -180..+180º 173 | 174 | if(DEBUG) Serial.print(":: bearing:"); 175 | if(DEBUG) Serial.print(Bearing); 176 | if(DEBUG) Serial.print(" :: CurLat: "); 177 | if(DEBUG) Serial.print(CurLat,4); 178 | if(DEBUG) Serial.print(" :: DestLat: "); 179 | if(DEBUG) Serial.print(DestLat,4); 180 | if(DEBUG) Serial.print(" :: CurLon: "); 181 | if(DEBUG) Serial.print(CurLon,4); 182 | if(DEBUG) Serial.print(" :: DestLon: "); 183 | if(DEBUG) Serial.print(DestLon,4); 184 | if(DEBUG) Serial.print(" :: "); 185 | 186 | CurLat = DestLat; 187 | CurLon = DestLon; 188 | */ 189 | } 190 | -------------------------------------------------------------------------------- /code/Arduino_GPS_BT_Spoofer/update_wind_walk.ino: -------------------------------------------------------------------------------- 1 | // A function to update the horizontal posisiton of the balloon 2 | // Designed to overcome some of the difficulties of using more 3 | // demanding mathematcial functions on a 8bit uC 4 | 5 | /* 6 | 7 | After doing a little bit of research, I have decided to make the 8 | wild assumption that a delta of 1m of from a position translates 9 | into 0.000009 units of lon or lat 10 | 11 | 1m @ 0° = 000°00'00.0326?N, 000°00'00.0000?E = 0.000009,0 12 | 1m @ 45° = 000°00'00.0230?N, 000°00'00.0229?E = 0.000006,0.000006 13 | 1m @ 90° = 000°00'00.0000?N, 000°00'00.0323?E = 0.0,0.000009 14 | 10m@ 90° = 000°00'00.0000?N, 000°00'00.3234?E = 0.0,0.000090 15 | */ 16 | 17 | void updateWindWalk(float oldLat, float oldLon, float brng, float s) 18 | { 19 | 20 | float dlat1 = (cos(radians(brng))*s)*0.000009; 21 | float dlon1 = (sin(radians(brng))*s)*0.000009; 22 | 23 | CurLat += dlat1; 24 | CurLon += dlon1; 25 | 26 | if(DEBUG) Serial.print("[dps:"); 27 | if(DEBUG) Serial.print(distancePerStep,2); 28 | if(DEBUG) Serial.print(" :: brng:"); 29 | if(DEBUG) Serial.print(brng, 2); 30 | if(DEBUG) Serial.print(" :: CurLat:"); 31 | if(DEBUG) Serial.print(CurLat,8); 32 | if(DEBUG) Serial.print(" :: CurLon:"); 33 | if(DEBUG) Serial.print(CurLon,8); 34 | if(DEBUG) Serial.print("] "); 35 | } 36 | -------------------------------------------------------------------------------- /images/by-sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/G4lile0/Arduino_GPS_Spoofer/5173e3bfd728ac21b546375d2e48c89061802c54/images/by-sa.png --------------------------------------------------------------------------------