├── 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
--------------------------------------------------------------------------------