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"
61 #include "fc/settings.h"
65 portMode_t portMode
; // Port mode RX/TX (only for serial based)
66 bool hasCompass
; // Has a compass (NAZA)
67 void (*restart
)(void); // Restart protocol driver thread
68 void (*protocol
)(void); // Process protocol driver thread
69 } gpsProviderDescriptor_t
;
72 gpsReceiverData_t gpsState
;
73 gpsStatistics_t gpsStats
;
74 gpsSolutionData_t gpsSol
;
76 // Map gpsBaudRate_e index to baudRate_e
77 baudRate_e gpsToSerialBaudRate
[GPS_BAUDRATE_COUNT
] = { BAUD_115200
, BAUD_57600
, BAUD_38400
, BAUD_19200
, BAUD_9600
, BAUD_230400
};
79 static gpsProviderDescriptor_t gpsProviders
[GPS_PROVIDER_COUNT
] = {
81 #ifdef USE_GPS_PROTO_NMEA
82 { false, MODE_RX
, false, &gpsRestartNMEA_MTK
, &gpsHandleNMEA
},
84 { false, 0, false, NULL
, NULL
},
88 #ifdef USE_GPS_PROTO_UBLOX
89 { false, MODE_RXTX
, false, &gpsRestartUBLOX
, &gpsHandleUBLOX
},
91 { false, 0, false, NULL
, NULL
},
95 #ifdef USE_GPS_PROTO_NAZA
96 { false, MODE_RX
, true, &gpsRestartNAZA
, &gpsHandleNAZA
},
98 { false, 0, false, NULL
, NULL
},
101 /* UBLOX7PLUS binary */
102 #ifdef USE_GPS_PROTO_UBLOX
103 { false, MODE_RXTX
, false, &gpsRestartUBLOX
, &gpsHandleUBLOX
},
105 { false, 0, false, NULL
, NULL
},
109 #ifdef USE_GPS_PROTO_MTK
110 { false, MODE_RXTX
, false, &gpsRestartNMEA_MTK
, &gpsHandleMTK
},
112 { false, 0, false, NULL
, NULL
},
116 #ifdef USE_GPS_PROTO_MSP
117 { true, 0, false, &gpsRestartMSP
, &gpsHandleMSP
},
119 { false, 0, false, NULL
, NULL
},
123 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 1);
125 PG_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
,
126 .provider
= SETTING_GPS_PROVIDER_DEFAULT
,
127 .sbasMode
= SETTING_GPS_SBAS_MODE_DEFAULT
,
128 .autoConfig
= SETTING_GPS_AUTO_CONFIG_DEFAULT
,
129 .autoBaud
= SETTING_GPS_AUTO_BAUD_DEFAULT
,
130 .dynModel
= SETTING_GPS_DYN_MODEL_DEFAULT
,
131 .gpsMinSats
= SETTING_GPS_MIN_SATS_DEFAULT
,
132 .ubloxUseGalileo
= SETTING_GPS_UBLOX_USE_GALILEO_DEFAULT
135 void gpsSetState(gpsState_e state
)
137 gpsState
.state
= state
;
138 gpsState
.lastStateSwitchMs
= millis();
141 static void gpsUpdateTime(void)
143 if (!rtcHasTime() && gpsSol
.flags
.validTime
&& gpsSol
.time
.year
!= 0) {
144 rtcSetDateTime(&gpsSol
.time
);
148 void gpsSetProtocolTimeout(timeMs_t timeoutMs
)
150 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
151 gpsState
.lastMessageMs
= millis();
152 gpsState
.timeoutMs
= timeoutMs
;
155 void gpsProcessNewSolutionData(void)
157 // Set GPS fix flag only if we have 3D fix
158 if (gpsSol
.fixType
== GPS_FIX_3D
&& gpsSol
.numSat
>= gpsConfig()->gpsMinSats
) {
159 ENABLE_STATE(GPS_FIX
);
162 /* When no fix available - reset flags as well */
163 gpsSol
.flags
.validVelNE
= 0;
164 gpsSol
.flags
.validVelD
= 0;
165 gpsSol
.flags
.validEPE
= 0;
166 DISABLE_STATE(GPS_FIX
);
169 // Set sensor as ready and available
170 sensorsSet(SENSOR_GPS
);
172 // Pass on GPS update to NAV and IMU
179 gpsSetProtocolTimeout(GPS_TIMEOUT
);
182 gpsStats
.lastMessageDt
= gpsState
.lastMessageMs
- gpsState
.lastLastMessageMs
;
183 gpsSol
.flags
.hasNewData
= true;
186 gpsSol
.flags
.gpsHeartbeat
= !gpsSol
.flags
.gpsHeartbeat
;
189 static void gpsResetSolution(void)
196 gpsSol
.fixType
= GPS_NO_FIX
;
198 gpsSol
.flags
.validVelNE
= 0;
199 gpsSol
.flags
.validVelD
= 0;
200 gpsSol
.flags
.validMag
= 0;
201 gpsSol
.flags
.validEPE
= 0;
202 gpsSol
.flags
.validTime
= 0;
205 void gpsPreInit(void)
207 // Make sure gpsProvider is known when gpsMagDetect is called
208 gpsState
.gpsConfig
= gpsConfig();
213 gpsState
.serialConfig
= serialConfig();
214 gpsState
.gpsConfig
= gpsConfig();
217 gpsStats
.timeouts
= 0;
219 // Reset solution, timeout and prepare to start
221 gpsSetProtocolTimeout(GPS_TIMEOUT
);
222 gpsSetState(GPS_UNKNOWN
);
224 // If given GPS provider has protocol() function not defined - we can't use it
225 if (!gpsProviders
[gpsState
.gpsConfig
->provider
].protocol
) {
226 featureClear(FEATURE_GPS
);
230 // Shortcut for driver-based GPS (MSP)
231 if (gpsProviders
[gpsState
.gpsConfig
->provider
].isDriverBased
) {
232 gpsSetState(GPS_INITIALIZING
);
236 serialPortConfig_t
* gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
237 if (!gpsPortConfig
) {
238 featureClear(FEATURE_GPS
);
242 // Start with baud rate index as configured for serial port
244 gpsState
.baudrateIndex
= GPS_BAUDRATE_115200
;
245 for (baudrateIndex
= 0, gpsState
.baudrateIndex
= 0; baudrateIndex
< GPS_BAUDRATE_COUNT
; baudrateIndex
++) {
246 if (gpsToSerialBaudRate
[baudrateIndex
] == gpsPortConfig
->gps_baudrateIndex
) {
247 gpsState
.baudrateIndex
= baudrateIndex
;
252 // Start with the same baud for autodetection
253 gpsState
.autoBaudrateIndex
= gpsState
.baudrateIndex
;
256 portMode_t mode
= gpsProviders
[gpsState
.gpsConfig
->provider
].portMode
;
257 gpsState
.gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, NULL
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]], mode
, SERIAL_NOT_INVERTED
);
259 // Check if we have a serial port opened
260 if (!gpsState
.gpsPort
) {
261 featureClear(FEATURE_GPS
);
265 gpsSetState(GPS_INITIALIZING
);
269 static bool gpsFakeGPSUpdate(void)
271 #define FAKE_GPS_INITIAL_LAT 509102311
272 #define FAKE_GPS_INITIAL_LON -15349744
273 #define FAKE_GPS_GROUND_ARMED_SPEED 350 // In cm/s
274 #define FAKE_GPS_GROUND_UNARMED_SPEED 0
275 #define FAKE_GPS_GROUND_COURSE_DECIDEGREES 300 //30deg
277 // Each degree in latitude corresponds to 111km.
278 // Each degree in longitude at the equator is 111km,
279 // going down to zero as latitude gets close to 90ยบ.
280 // We approximate it linearly.
282 static int32_t lat
= FAKE_GPS_INITIAL_LAT
;
283 static int32_t lon
= FAKE_GPS_INITIAL_LON
;
285 timeMs_t now
= millis();
286 uint32_t delta
= now
- gpsState
.lastMessageMs
;
288 int32_t speed
= ARMING_FLAG(ARMED
) ? FAKE_GPS_GROUND_ARMED_SPEED
: FAKE_GPS_GROUND_UNARMED_SPEED
;
289 int32_t cmDelta
= speed
* (delta
/ 1000.0f
);
290 int32_t latCmDelta
= cmDelta
* cos_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
291 int32_t lonCmDelta
= cmDelta
* sin_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
292 int32_t latDelta
= ceilf((float)latCmDelta
/ (111 * 1000 * 100 / 1e7
));
293 int32_t lonDelta
= ceilf((float)lonCmDelta
/ (111 * 1000 * 100 / 1e7
));
294 if (speed
> 0 && latDelta
== 0 && lonDelta
== 0) {
299 gpsSol
.fixType
= GPS_FIX_3D
;
301 gpsSol
.llh
.lat
= lat
;
302 gpsSol
.llh
.lon
= lon
;
304 gpsSol
.groundSpeed
= speed
;
305 gpsSol
.groundCourse
= FAKE_GPS_GROUND_COURSE_DECIDEGREES
;
306 gpsSol
.velNED
[X
] = speed
* cos_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
307 gpsSol
.velNED
[Y
] = speed
* sin_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
308 gpsSol
.velNED
[Z
] = 0;
309 gpsSol
.flags
.validVelNE
= 1;
310 gpsSol
.flags
.validVelD
= 1;
311 gpsSol
.flags
.validEPE
= 1;
312 gpsSol
.flags
.validTime
= 1;
315 gpsSol
.time
.year
= 1983;
316 gpsSol
.time
.month
= 1;
318 gpsSol
.time
.hours
= 3;
319 gpsSol
.time
.minutes
= 15;
320 gpsSol
.time
.seconds
= 42;
322 ENABLE_STATE(GPS_FIX
);
323 sensorsSet(SENSOR_GPS
);
327 gpsSetProtocolTimeout(GPS_TIMEOUT
);
329 gpsSetState(GPS_RUNNING
);
336 uint16_t gpsConstrainEPE(uint32_t epe
)
338 return (epe
> 9999) ? 9999 : epe
; // max 99.99m error
341 uint16_t gpsConstrainHDOP(uint32_t hdop
)
343 return (hdop
> 9999) ? 9999 : hdop
; // max 99.99m error
349 if (!feature(FEATURE_GPS
)) {
350 sensorsClear(SENSOR_GPS
);
351 DISABLE_STATE(GPS_FIX
);
355 /* Extra delay for at least 2 seconds after booting to give GPS time to initialise */
356 if (!isMPUSoftReset() && (millis() < GPS_BOOT_DELAY
)) {
357 sensorsClear(SENSOR_GPS
);
358 DISABLE_STATE(GPS_FIX
);
362 if (ARMING_FLAG(SIMULATOR_MODE
)) {
364 gpsSetState(GPS_RUNNING
);
365 sensorsSet(SENSOR_GPS
);
366 return gpsSol
.flags
.hasNewData
;
369 return gpsFakeGPSUpdate();
372 // Assume that we don't have new data this run
373 gpsSol
.flags
.hasNewData
= false;
375 switch (gpsState
.state
) {
377 case GPS_INITIALIZING
:
378 // Wait for GPS_INIT_DELAY before starting the GPS protocol thread
379 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_INIT_DELAY
) {
381 DISABLE_STATE(GPS_FIX
);
382 gpsSol
.fixType
= GPS_NO_FIX
;
387 // Call GPS protocol reset handler
388 gpsProviders
[gpsState
.gpsConfig
->provider
].restart();
390 // Switch to GPS_RUNNING state (mind the timeout)
391 gpsSetProtocolTimeout(GPS_TIMEOUT
);
392 gpsSetState(GPS_RUNNING
);
397 // Call GPS protocol thread
398 gpsProviders
[gpsState
.gpsConfig
->provider
].protocol();
400 // Check for GPS timeout
401 if ((millis() - gpsState
.lastMessageMs
) > GPS_TIMEOUT
) {
402 sensorsClear(SENSOR_GPS
);
403 DISABLE_STATE(GPS_FIX
);
404 gpsSol
.fixType
= GPS_NO_FIX
;
405 gpsSetState(GPS_LOST_COMMUNICATION
);
409 case GPS_LOST_COMMUNICATION
:
411 gpsSetState(GPS_INITIALIZING
);
415 return gpsSol
.flags
.hasNewData
;
419 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
421 waitForSerialPortToFinishTransmitting(gpsState
.gpsPort
);
422 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
424 if (!(gpsState
.gpsPort
->mode
& MODE_TX
))
425 serialSetMode(gpsState
.gpsPort
, gpsState
.gpsPort
->mode
| MODE_TX
);
432 if (serialRxBytesWaiting(gpsState
.gpsPort
)) {
434 c
= serialRead(gpsState
.gpsPort
);
435 serialWrite(gpsPassthroughPort
, c
);
438 if (serialRxBytesWaiting(gpsPassthroughPort
)) {
440 c
= serialRead(gpsPassthroughPort
);
441 serialWrite(gpsState
.gpsPort
, c
);
447 void updateGpsIndicator(timeUs_t currentTimeUs
)
449 static timeUs_t GPSLEDTime
;
450 if ((int32_t)(currentTimeUs
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
451 GPSLEDTime
= currentTimeUs
+ 150000;
456 /* Support for built-in magnetometer accessible via the native GPS protocol (i.e. NAZA) */
457 bool gpsMagInit(magDev_t
*magDev
)
463 bool gpsMagRead(magDev_t
*magDev
)
465 magDev
->magADCRaw
[X
] = gpsSol
.magData
[0];
466 magDev
->magADCRaw
[Y
] = gpsSol
.magData
[1];
467 magDev
->magADCRaw
[Z
] = gpsSol
.magData
[2];
468 return gpsSol
.flags
.validMag
;
471 bool gpsMagDetect(magDev_t
*mag
)
473 if (!(feature(FEATURE_GPS
) && gpsProviders
[gpsState
.gpsConfig
->provider
].hasCompass
)) {
477 if (!gpsProviders
[gpsState
.gpsConfig
->provider
].protocol
|| !findSerialPortConfig(FUNCTION_GPS
)) {
481 mag
->init
= gpsMagInit
;
482 mag
->read
= gpsMagRead
;
486 bool isGPSHealthy(void)
491 bool isGPSHeadingValid(void)
493 return sensors(SENSOR_GPS
) && STATE(GPS_FIX
) && gpsSol
.numSat
>= 6 && gpsSol
.groundSpeed
>= 300;