2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
24 #include "build/build_config.h"
29 #include "build/debug.h"
31 #include "common/maths.h"
32 #include "common/axis.h"
33 #include "common/utils.h"
35 #include "config/parameter_group.h"
36 #include "config/parameter_group_ids.h"
38 #include "drivers/compass/compass.h"
39 #include "drivers/light_led.h"
40 #include "drivers/serial.h"
41 #include "drivers/system.h"
42 #include "drivers/time.h"
44 #if defined(USE_FAKE_GPS)
45 #include "fc/runtime_config.h"
48 #include "sensors/sensors.h"
49 #include "sensors/compass.h"
51 #include "io/serial.h"
53 #include "io/gps_private.h"
55 #include "navigation/navigation.h"
57 #include "config/feature.h"
59 #include "fc/config.h"
60 #include "fc/runtime_config.h"
63 portMode_t portMode
; // Port mode RX/TX (only for serial based)
64 bool hasCompass
; // Has a compass (NAZA)
65 void (*restart
)(void); // Restart protocol driver thread
66 void (*protocol
)(void); // Process protocol driver thread
67 } gpsProviderDescriptor_t
;
70 gpsReceiverData_t gpsState
;
71 gpsStatistics_t gpsStats
;
72 gpsSolutionData_t gpsSol
;
74 // Map gpsBaudRate_e index to baudRate_e
75 baudRate_e gpsToSerialBaudRate
[GPS_BAUDRATE_COUNT
] = { BAUD_115200
, BAUD_57600
, BAUD_38400
, BAUD_19200
, BAUD_9600
};
77 static gpsProviderDescriptor_t gpsProviders
[GPS_PROVIDER_COUNT
] = {
79 #ifdef USE_GPS_PROTO_NMEA
80 { MODE_RX
, false, &gpsRestartNMEA_MTK
, &gpsHandleNMEA
},
82 { 0, false, NULL
, NULL
},
86 #ifdef USE_GPS_PROTO_UBLOX
87 { MODE_RXTX
, false, &gpsRestartUBLOX
, &gpsHandleUBLOX
},
89 { 0, false, NULL
, NULL
},
93 { 0, false, NULL
, NULL
},
96 #ifdef USE_GPS_PROTO_NAZA
97 { MODE_RX
, true, &gpsRestartNAZA
, &gpsHandleNAZA
},
99 { 0, false, NULL
, NULL
},
102 /* UBLOX7PLUS binary */
103 #ifdef USE_GPS_PROTO_UBLOX
104 { MODE_RXTX
, false, &gpsRestartUBLOX
, &gpsHandleUBLOX
},
106 { 0, false, NULL
, NULL
},
110 #ifdef USE_GPS_PROTO_MTK
111 { MODE_RXTX
, false, &gpsRestartNMEA_MTK
, &gpsHandleMTK
},
113 { 0, false, NULL
, NULL
},
118 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 0);
120 PG_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
,
121 .provider
= GPS_UBLOX
,
122 .sbasMode
= SBAS_NONE
,
123 .autoConfig
= GPS_AUTOCONFIG_ON
,
124 .autoBaud
= GPS_AUTOBAUD_ON
,
125 .dynModel
= GPS_DYNMODEL_AIR_1G
,
127 .ubloxUseGalileo
= false
130 void gpsSetState(gpsState_e state
)
132 gpsState
.state
= state
;
133 gpsState
.lastStateSwitchMs
= millis();
136 static void gpsUpdateTime(void)
138 if (!rtcHasTime() && gpsSol
.flags
.validTime
) {
139 rtcSetDateTime(&gpsSol
.time
);
143 void gpsSetProtocolTimeout(timeMs_t timeoutMs
)
145 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
146 gpsState
.lastMessageMs
= millis();
147 gpsState
.timeoutMs
= timeoutMs
;
150 void gpsProcessNewSolutionData(void)
152 // Set GPS fix flag only if we have 3D fix
153 if (gpsSol
.fixType
== GPS_FIX_3D
&& gpsSol
.numSat
>= gpsConfig()->gpsMinSats
) {
154 ENABLE_STATE(GPS_FIX
);
157 /* When no fix available - reset flags as well */
158 gpsSol
.flags
.validVelNE
= 0;
159 gpsSol
.flags
.validVelD
= 0;
160 gpsSol
.flags
.validEPE
= 0;
161 DISABLE_STATE(GPS_FIX
);
164 // Set sensor as ready and available
165 sensorsSet(SENSOR_GPS
);
167 // Pass on GPS update to NAV and IMU
174 gpsSetProtocolTimeout(GPS_TIMEOUT
);
177 gpsStats
.lastMessageDt
= gpsState
.lastMessageMs
- gpsState
.lastLastMessageMs
;
178 gpsSol
.flags
.hasNewData
= true;
181 gpsSol
.flags
.gpsHeartbeat
= !gpsSol
.flags
.gpsHeartbeat
;
184 static void gpsResetSolution(void)
189 gpsSol
.fixType
= GPS_NO_FIX
;
191 gpsSol
.flags
.validVelNE
= 0;
192 gpsSol
.flags
.validVelD
= 0;
193 gpsSol
.flags
.validMag
= 0;
194 gpsSol
.flags
.validEPE
= 0;
195 gpsSol
.flags
.validTime
= 0;
198 void gpsPreInit(void)
200 // Make sure gpsProvider is known when gpsMagDetect is called
201 gpsState
.gpsConfig
= gpsConfig();
206 gpsState
.serialConfig
= serialConfig();
207 gpsState
.gpsConfig
= gpsConfig();
210 gpsStats
.timeouts
= 0;
212 // Reset solution, timeout and prepare to start
214 gpsSetProtocolTimeout(GPS_TIMEOUT
);
215 gpsSetState(GPS_UNKNOWN
);
217 // If given GPS provider has protocol() function not defined - we can't use it
218 if (!gpsProviders
[gpsState
.gpsConfig
->provider
].protocol
) {
219 featureClear(FEATURE_GPS
);
223 serialPortConfig_t
* gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
224 if (!gpsPortConfig
) {
225 featureClear(FEATURE_GPS
);
229 // Start with baud rate index as configured for serial port
231 for (gpsState
.baudrateIndex
= 0, baudrateIndex
= 0; baudrateIndex
< GPS_BAUDRATE_COUNT
; baudrateIndex
++) {
232 if (gpsToSerialBaudRate
[baudrateIndex
] == gpsPortConfig
->gps_baudrateIndex
) {
233 gpsState
.baudrateIndex
= baudrateIndex
;
238 // Start with the same baud for autodetection
239 gpsState
.autoBaudrateIndex
= gpsState
.baudrateIndex
;
242 portMode_t mode
= gpsProviders
[gpsState
.gpsConfig
->provider
].portMode
;
243 gpsState
.gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, NULL
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]], mode
, SERIAL_NOT_INVERTED
);
245 // Check if we have a serial port opened
246 if (!gpsState
.gpsPort
) {
247 featureClear(FEATURE_GPS
);
251 gpsSetState(GPS_INITIALIZING
);
255 static bool gpsFakeGPSUpdate(void)
257 #define FAKE_GPS_INITIAL_LAT 509102311
258 #define FAKE_GPS_INITIAL_LON -15349744
259 #define FAKE_GPS_GROUND_ARMED_SPEED 350 // In cm/s
260 #define FAKE_GPS_GROUND_UNARMED_SPEED 0
261 #define FAKE_GPS_GROUND_COURSE_DECIDEGREES 300 //30deg
263 // Each degree in latitude corresponds to 111km.
264 // Each degree in longitude at the equator is 111km,
265 // going down to zero as latitude gets close to 90ยบ.
266 // We approximate it linearly.
268 static int32_t lat
= FAKE_GPS_INITIAL_LAT
;
269 static int32_t lon
= FAKE_GPS_INITIAL_LON
;
271 timeMs_t now
= millis();
272 uint32_t delta
= now
- gpsState
.lastMessageMs
;
274 int32_t speed
= ARMING_FLAG(ARMED
) ? FAKE_GPS_GROUND_ARMED_SPEED
: FAKE_GPS_GROUND_UNARMED_SPEED
;
275 int32_t cmDelta
= speed
* (delta
/ 1000.0f
);
276 int32_t latCmDelta
= cmDelta
* cos_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
277 int32_t lonCmDelta
= cmDelta
* sin_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
278 int32_t latDelta
= ceilf((float)latCmDelta
/ (111 * 1000 * 100 / 1e7
));
279 int32_t lonDelta
= ceilf((float)lonCmDelta
/ (111 * 1000 * 100 / 1e7
));
280 if (speed
> 0 && latDelta
== 0 && lonDelta
== 0) {
285 gpsSol
.fixType
= GPS_FIX_3D
;
287 gpsSol
.llh
.lat
= lat
;
288 gpsSol
.llh
.lon
= lon
;
290 gpsSol
.groundSpeed
= speed
;
291 gpsSol
.groundCourse
= FAKE_GPS_GROUND_COURSE_DECIDEGREES
;
292 gpsSol
.velNED
[X
] = speed
* cos_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
293 gpsSol
.velNED
[Y
] = speed
* sin_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
294 gpsSol
.velNED
[Z
] = 0;
295 gpsSol
.flags
.validVelNE
= 1;
296 gpsSol
.flags
.validVelD
= 1;
297 gpsSol
.flags
.validEPE
= 1;
298 gpsSol
.flags
.validTime
= 1;
301 gpsSol
.time
.year
= 1983;
302 gpsSol
.time
.month
= 1;
304 gpsSol
.time
.hours
= 3;
305 gpsSol
.time
.minutes
= 15;
306 gpsSol
.time
.seconds
= 42;
308 ENABLE_STATE(GPS_FIX
);
309 sensorsSet(SENSOR_GPS
);
313 gpsSetProtocolTimeout(GPS_TIMEOUT
);
315 gpsSetState(GPS_RUNNING
);
322 uint16_t gpsConstrainEPE(uint32_t epe
)
324 return (epe
> 9999) ? 9999 : epe
; // max 99.99m error
327 uint16_t gpsConstrainHDOP(uint32_t hdop
)
329 return (hdop
> 9999) ? 9999 : hdop
; // max 99.99m error
335 if (!feature(FEATURE_GPS
)) {
336 sensorsClear(SENSOR_GPS
);
337 DISABLE_STATE(GPS_FIX
);
341 /* Extra delay for at least 2 seconds after booting to give GPS time to initialise */
342 if (!isMPUSoftReset() && (millis() < GPS_BOOT_DELAY
)) {
343 sensorsClear(SENSOR_GPS
);
344 DISABLE_STATE(GPS_FIX
);
349 return gpsFakeGPSUpdate();
352 // Assume that we don't have new data this run
353 gpsSol
.flags
.hasNewData
= false;
355 switch (gpsState
.state
) {
357 case GPS_INITIALIZING
:
358 // Wait for GPS_INIT_DELAY before starting the GPS protocol thread
359 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_INIT_DELAY
) {
361 DISABLE_STATE(GPS_FIX
);
362 gpsSol
.fixType
= GPS_NO_FIX
;
367 // Call GPS protocol reset handler
368 gpsProviders
[gpsState
.gpsConfig
->provider
].restart();
370 // Switch to GPS_RUNNING state (mind the timeout)
371 gpsSetProtocolTimeout(GPS_TIMEOUT
);
372 gpsSetState(GPS_RUNNING
);
377 // Call GPS protocol thread
378 gpsProviders
[gpsState
.gpsConfig
->provider
].protocol();
380 // Check for GPS timeout
381 if ((millis() - gpsState
.lastMessageMs
) > GPS_TIMEOUT
) {
382 sensorsClear(SENSOR_GPS
);
383 DISABLE_STATE(GPS_FIX
);
384 gpsSol
.fixType
= GPS_NO_FIX
;
385 gpsSetState(GPS_LOST_COMMUNICATION
);
389 case GPS_LOST_COMMUNICATION
:
391 gpsSetState(GPS_INITIALIZING
);
395 return gpsSol
.flags
.hasNewData
;
399 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
401 waitForSerialPortToFinishTransmitting(gpsState
.gpsPort
);
402 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
404 if (!(gpsState
.gpsPort
->mode
& MODE_TX
))
405 serialSetMode(gpsState
.gpsPort
, gpsState
.gpsPort
->mode
| MODE_TX
);
412 if (serialRxBytesWaiting(gpsState
.gpsPort
)) {
414 c
= serialRead(gpsState
.gpsPort
);
415 serialWrite(gpsPassthroughPort
, c
);
418 if (serialRxBytesWaiting(gpsPassthroughPort
)) {
420 c
= serialRead(gpsPassthroughPort
);
421 serialWrite(gpsState
.gpsPort
, c
);
427 void updateGpsIndicator(timeUs_t currentTimeUs
)
429 static timeUs_t GPSLEDTime
;
430 if ((int32_t)(currentTimeUs
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
431 GPSLEDTime
= currentTimeUs
+ 150000;
436 /* Support for built-in magnetometer accessible via the native GPS protocol (i.e. NAZA) */
437 bool gpsMagInit(magDev_t
*magDev
)
443 bool gpsMagRead(magDev_t
*magDev
)
445 magDev
->magADCRaw
[X
] = gpsSol
.magData
[0];
446 magDev
->magADCRaw
[Y
] = gpsSol
.magData
[1];
447 magDev
->magADCRaw
[Z
] = gpsSol
.magData
[2];
448 return gpsSol
.flags
.validMag
;
451 bool gpsMagDetect(magDev_t
*mag
)
453 if (!(feature(FEATURE_GPS
) && gpsProviders
[gpsState
.gpsConfig
->provider
].hasCompass
)) {
457 if (!gpsProviders
[gpsState
.gpsConfig
->provider
].protocol
|| !findSerialPortConfig(FUNCTION_GPS
)) {
461 mag
->init
= gpsMagInit
;
462 mag
->read
= gpsMagRead
;
466 bool isGPSHealthy(void)
471 bool isGPSHeadingValid(void)
473 return sensors(SENSOR_GPS
) && STATE(GPS_FIX
) && gpsSol
.numSat
>= 6 && gpsSol
.groundSpeed
>= 300;