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"
32 #include "common/maths.h"
33 #include "common/axis.h"
34 #include "common/utils.h"
36 #include "drivers/system.h"
37 #include "drivers/serial.h"
38 #include "drivers/serial_uart.h"
39 #include "drivers/gpio.h"
40 #include "drivers/light_led.h"
41 #include "drivers/sensor.h"
42 #include "drivers/compass.h"
44 #include "sensors/sensors.h"
45 #include "sensors/compass.h"
47 #include "io/serial.h"
48 #include "io/display.h"
50 #include "io/gps_private.h"
52 #include "flight/navigation_rewrite.h"
54 #include "config/config.h"
55 #include "fc/runtime_config.h"
57 // GPS timeout for wrong baud rate/disconnection/etc in milliseconds (default 2000 ms)
58 #define GPS_TIMEOUT (2000)
59 #define GPS_BAUD_CHANGE_DELAY (200)
60 #define GPS_INIT_DELAY (500)
61 #define GPS_BUS_INIT_DELAY (500)
62 #define GPS_BOOT_DELAY (2000)
65 GPS_TYPE_NA
, // Not available
66 GPS_TYPE_SERIAL
, // Serial connection (UART)
67 GPS_TYPE_BUS
// Bus connection (I2C/SPI)
71 gpsProviderType_e type
;
72 portMode_t portMode
; // Port mode RX/TX (only for serial based)
73 bool hasCompass
; // Has a compass (NAZA)
76 } gpsProviderDescriptor_t
;
79 gpsReceiverData_t gpsState
;
80 gpsStatistics_t gpsStats
;
81 gpsSolutionData_t gpsSol
;
83 // Map gpsBaudRate_e index to baudRate_e
84 baudRate_e gpsToSerialBaudRate
[GPS_BAUDRATE_COUNT
] = { BAUD_115200
, BAUD_57600
, BAUD_38400
, BAUD_19200
, BAUD_9600
};
86 static gpsProviderDescriptor_t gpsProviders
[GPS_PROVIDER_COUNT
] = {
89 { GPS_TYPE_SERIAL
, MODE_RX
, false, NULL
, &gpsHandleNMEA
},
91 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
95 #ifdef GPS_PROTO_UBLOX
96 { GPS_TYPE_SERIAL
, MODE_RXTX
, false, NULL
, &gpsHandleUBLOX
},
98 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
101 /* MultiWii I2C-NAV module */
102 #ifdef GPS_PROTO_I2C_NAV
103 { GPS_TYPE_BUS
, 0, false, &gpsDetectI2CNAV
, &gpsHandleI2CNAV
},
105 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
108 /* NAZA GPS module */
109 #ifdef GPS_PROTO_NAZA
110 { GPS_TYPE_SERIAL
, MODE_RX
, true, NULL
, &gpsHandleNAZA
},
112 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
116 void gpsSetState(gpsState_e state
)
118 gpsState
.state
= state
;
119 gpsState
.lastStateSwitchMs
= millis();
122 static void gpsHandleProtocol(void)
124 bool newDataReceived
= false;
126 // Call protocol-specific code
127 if (gpsProviders
[gpsState
.gpsConfig
->provider
].read
) {
128 newDataReceived
= gpsProviders
[gpsState
.gpsConfig
->provider
].read();
131 // Received new update for solution data
132 if (newDataReceived
) {
133 // Set GPS fix flag only if we have 3D fix
134 if (gpsSol
.fixType
== GPS_FIX_3D
) {
135 ENABLE_STATE(GPS_FIX
);
138 DISABLE_STATE(GPS_FIX
);
141 // Update GPS coordinates etc
142 sensorsSet(SENSOR_GPS
);
146 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
147 gpsState
.lastMessageMs
= millis();
150 gpsStats
.lastMessageDt
= gpsState
.lastMessageMs
- gpsState
.lastLastMessageMs
;
154 static void gpsResetSolution(void)
159 gpsSol
.fixType
= GPS_NO_FIX
;
161 gpsSol
.flags
.validVelNE
= 0;
162 gpsSol
.flags
.validVelD
= 0;
163 gpsSol
.flags
.validMag
= 0;
166 void gpsPreInit(gpsConfig_t
*initialGpsConfig
)
168 // Make sure gpsProvider is known when gpsMagDetect is called
169 gpsState
.gpsConfig
= initialGpsConfig
;
172 void gpsInit(serialConfig_t
*initialSerialConfig
, gpsConfig_t
*initialGpsConfig
)
174 gpsState
.serialConfig
= initialSerialConfig
;
175 gpsState
.gpsConfig
= initialGpsConfig
;
176 gpsState
.baudrateIndex
= 0;
179 gpsStats
.timeouts
= 0;
183 // init gpsData structure. if we're not actually enabled, don't bother doing anything else
184 gpsState
.autoConfigStep
= 0;
185 gpsState
.lastMessageMs
= millis();
186 gpsSetState(GPS_UNKNOWN
);
188 if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_BUS
) {
189 gpsSetState(GPS_INITIALIZING
);
193 if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
) {
194 serialPortConfig_t
* gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
195 if (!gpsPortConfig
) {
196 featureClear(FEATURE_GPS
);
199 while (gpsToSerialBaudRate
[gpsState
.baudrateIndex
] != gpsPortConfig
->gps_baudrateIndex
) {
200 gpsState
.baudrateIndex
++;
201 if (gpsState
.baudrateIndex
>= GPS_BAUDRATE_COUNT
) {
202 gpsState
.baudrateIndex
= 0;
207 portMode_t mode
= gpsProviders
[gpsState
.gpsConfig
->provider
].portMode
;
209 // no callback - buffer will be consumed in gpsThread()
210 gpsState
.gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, gpsToSerialBaudRate
[gpsState
.baudrateIndex
], mode
, SERIAL_NOT_INVERTED
);
212 if (!gpsState
.gpsPort
) {
213 featureClear(FEATURE_GPS
);
216 gpsSetState(GPS_INITIALIZING
);
224 static void gpsFakeGPSUpdate(void)
226 if (millis() - gpsState
.lastMessageMs
> 100) {
227 gpsSol
.fixType
= GPS_FIX_3D
;
229 gpsSol
.llh
.lat
= 509102311;
230 gpsSol
.llh
.lon
= -15349744;
232 gpsSol
.groundSpeed
= 0;
233 gpsSol
.groundCourse
= 0;
234 gpsSol
.velNED
[X
] = 0;
235 gpsSol
.velNED
[Y
] = 0;
236 gpsSol
.velNED
[Z
] = 0;
237 gpsSol
.flags
.validVelNE
= 1;
238 gpsSol
.flags
.validVelD
= 1;
239 gpsSol
.flags
.validEPE
= 1;
243 ENABLE_STATE(GPS_FIX
);
244 sensorsSet(SENSOR_GPS
);
247 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
248 gpsState
.lastMessageMs
= millis();
250 gpsSetState(GPS_RECEIVING_DATA
);
255 // Finish baud rate change sequence - wait for TX buffer to empty and switch to the desired port speed
256 void gpsFinalizeChangeBaud(void)
258 if ((gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
) && (gpsState
.gpsPort
!= NULL
)) {
259 // Wait for GPS_INIT_DELAY before switching to required baud rate
260 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_BAUD_CHANGE_DELAY
&& isSerialTransmitBufferEmpty(gpsState
.gpsPort
)) {
261 // Switch to required serial port baud
262 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]]);
263 gpsState
.lastMessageMs
= millis();
264 gpsSetState(GPS_CHECK_VERSION
);
269 uint16_t gpsConstrainEPE(uint32_t epe
)
271 return (epe
> 99999) ? 9999 : epe
; // max 99.99m error
274 uint16_t gpsConstrainHDOP(uint32_t hdop
)
276 return (hdop
> 99999) ? 9999 : hdop
; // max 99.99m error
281 /* Extra delay for at least 2 seconds after booting to give GPS time to initialise */
282 if (!isMPUSoftReset() && (millis() < GPS_BOOT_DELAY
)) {
283 sensorsClear(SENSOR_GPS
);
284 DISABLE_STATE(GPS_FIX
);
293 if ((gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
) && (gpsState
.gpsPort
!= NULL
)) {
294 switch (gpsState
.state
) {
296 case GPS_INITIALIZING
:
297 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_INIT_DELAY
) {
299 DISABLE_STATE(GPS_FIX
);
300 gpsSol
.fixType
= GPS_NO_FIX
;
302 gpsState
.hwVersion
= 0;
303 gpsState
.autoConfigStep
= 0;
304 gpsState
.autoConfigPosition
= 0;
305 gpsState
.autoBaudrateIndex
= 0;
310 // Call protocol handler - switch to next state is done there
315 case GPS_CHANGE_BAUD
:
316 // Call protocol handler - switch to next state is done there
320 case GPS_CHECK_VERSION
:
322 case GPS_RECEIVING_DATA
:
324 if ((millis() - gpsState
.lastMessageMs
) > GPS_TIMEOUT
) {
325 // Check for GPS timeout
326 sensorsClear(SENSOR_GPS
);
327 DISABLE_STATE(GPS_FIX
);
328 gpsSol
.fixType
= GPS_NO_FIX
;
330 gpsSetState(GPS_LOST_COMMUNICATION
);
334 case GPS_LOST_COMMUNICATION
:
336 // Handle autobaud - switch to next port baud rate
337 if (gpsState
.gpsConfig
->autoBaud
!= GPS_AUTOBAUD_OFF
) {
338 gpsState
.baudrateIndex
++;
339 gpsState
.baudrateIndex
%= GPS_BAUDRATE_COUNT
;
341 gpsSetState(GPS_INITIALIZING
);
345 // Driver-based GPS (I2C)
346 else if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_BUS
) {
347 switch (gpsState
.state
) {
349 case GPS_INITIALIZING
:
351 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_BUS_INIT_DELAY
) {
354 if (gpsProviders
[gpsState
.gpsConfig
->provider
].detect
&& gpsProviders
[gpsState
.gpsConfig
->provider
].detect()) {
355 gpsState
.hwVersion
= 0;
356 gpsState
.autoConfigStep
= 0;
357 gpsState
.autoConfigPosition
= 0;
358 gpsState
.lastMessageMs
= millis();
359 sensorsSet(SENSOR_GPS
);
360 gpsSetState(GPS_CHANGE_BAUD
);
363 sensorsClear(SENSOR_GPS
);
368 case GPS_CHANGE_BAUD
:
369 case GPS_CHECK_VERSION
:
371 case GPS_RECEIVING_DATA
:
373 if (millis() - gpsState
.lastMessageMs
> GPS_TIMEOUT
) {
374 // remove GPS from capability
375 gpsSetState(GPS_LOST_COMMUNICATION
);
379 case GPS_LOST_COMMUNICATION
:
380 // No valid data from GPS unit, cause re-init and re-detection
382 DISABLE_STATE(GPS_FIX
);
383 gpsSol
.fixType
= GPS_NO_FIX
;
385 gpsSetState(GPS_INITIALIZING
);
395 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
397 waitForSerialPortToFinishTransmitting(gpsState
.gpsPort
);
398 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
400 if(!(gpsState
.gpsPort
->mode
& MODE_TX
))
401 serialSetMode(gpsState
.gpsPort
, gpsState
.gpsPort
->mode
| MODE_TX
);
408 if (serialRxBytesWaiting(gpsState
.gpsPort
)) {
410 c
= serialRead(gpsState
.gpsPort
);
411 serialWrite(gpsPassthroughPort
, c
);
414 if (serialRxBytesWaiting(gpsPassthroughPort
)) {
416 c
= serialRead(gpsPassthroughPort
);
417 serialWrite(gpsState
.gpsPort
, c
);
423 void updateGpsIndicator(uint32_t currentTime
)
425 static uint32_t GPSLEDTime
;
426 if ((int32_t)(currentTime
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
427 GPSLEDTime
= currentTime
+ 150000;
432 /* Support for built-in magnetometer accessible via the native GPS protocol (i.e. NAZA) */
433 bool gpsMagInit(void)
438 bool gpsMagRead(int16_t *magData
)
440 magData
[X
] = gpsSol
.magData
[0];
441 magData
[Y
] = gpsSol
.magData
[1];
442 magData
[Z
] = gpsSol
.magData
[2];
443 return gpsSol
.flags
.validMag
;
446 bool gpsMagDetect(mag_t
*mag
)
448 if (!(feature(FEATURE_GPS
) && gpsProviders
[gpsState
.gpsConfig
->provider
].hasCompass
))
451 if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
&& (!findSerialPortConfig(FUNCTION_GPS
))) {
455 mag
->init
= gpsMagInit
;
456 mag
->read
= gpsMagRead
;