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_config.h"
27 #include "common/maths.h"
28 #include "common/axis.h"
29 #include "common/utils.h"
31 #include "drivers/system.h"
32 #include "drivers/serial.h"
33 #include "drivers/serial_uart.h"
34 #include "drivers/gpio.h"
35 #include "drivers/light_led.h"
36 #include "drivers/sensor.h"
37 #include "drivers/compass.h"
39 #include "sensors/sensors.h"
40 #include "sensors/compass.h"
42 #include "io/serial.h"
43 #include "io/display.h"
45 #include "io/gps_private.h"
47 #include "flight/navigation_rewrite.h"
49 #include "config/config.h"
50 #include "config/runtime_config.h"
54 // GPS timeout for wrong baud rate/disconnection/etc in milliseconds (default 2000 ms)
55 #define GPS_TIMEOUT (2000)
56 #define GPS_BAUD_CHANGE_DELAY (200)
57 #define GPS_INIT_DELAY (500)
58 #define GPS_BUS_INIT_DELAY (500)
61 GPS_TYPE_NA
, // Not available
62 GPS_TYPE_SERIAL
, // Serial connection (UART)
63 GPS_TYPE_BUS
// Bus connection (I2C/SPI)
67 gpsProviderType_e type
;
68 portMode_t portMode
; // Port mode RX/TX (only for serial based)
69 bool hasCompass
; // Has a compass (NAZA)
72 } gpsProviderDescriptor_t
;
75 gpsReceiverData_t gpsState
;
76 gpsStatistics_t gpsStats
;
77 gpsSolutionData_t gpsSol
;
79 // Map gpsBaudRate_e index to baudRate_e
80 baudRate_e gpsToSerialBaudRate
[GPS_BAUDRATE_COUNT
] = { BAUD_115200
, BAUD_57600
, BAUD_38400
, BAUD_19200
, BAUD_9600
};
82 static gpsProviderDescriptor_t gpsProviders
[GPS_PROVIDER_COUNT
] = {
85 { GPS_TYPE_SERIAL
, MODE_RX
, false, NULL
, &gpsHandleNMEA
},
87 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
91 #ifdef GPS_PROTO_UBLOX
92 { GPS_TYPE_SERIAL
, MODE_RXTX
, false, NULL
, gpsHandleUBLOX
},
94 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
97 /* MultiWii I2C-NAV module */
98 #ifdef GPS_PROTO_I2C_NAV
99 { GPS_TYPE_BUS
, 0, false, &gpsDetectI2CNAV
, &gpsHandleI2CNAV
},
101 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
104 /* NAZA GPS module */
105 #ifdef GPS_PROTO_NAZA
106 { GPS_TYPE_SERIAL
, MODE_RX
, true, NULL
, gpsHandleNAZA
},
108 { GPS_TYPE_NA
, 0, false, NULL
, NULL
},
112 void gpsSetState(gpsState_e state
)
114 gpsState
.state
= state
;
115 gpsState
.lastStateSwitchMs
= millis();
118 static void gpsHandleProtocol(void)
120 bool newDataReceived
= false;
122 // Call protocol-specific code
123 if (gpsProviders
[gpsState
.gpsConfig
->provider
].read
) {
124 newDataReceived
= gpsProviders
[gpsState
.gpsConfig
->provider
].read();
127 // Received new update for solution data
128 if (newDataReceived
) {
130 if (gpsSol
.flags
.fix3D
)
131 ENABLE_STATE(GPS_FIX
);
133 DISABLE_STATE(GPS_FIX
);
135 // Update GPS coordinates etc
136 sensorsSet(SENSOR_GPS
);
140 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
141 gpsState
.lastMessageMs
= millis();
144 gpsStats
.lastMessageDt
= gpsState
.lastMessageMs
- gpsState
.lastLastMessageMs
;
148 static void gpsResetSolution(void)
150 // Clear satellites in view information, if we use I2C driver this is not used
152 for (int i
= 0; i
< GPS_SV_MAXSATS
; i
++){
153 gpsSol
.svInfo
[i
].chn
= 0;
154 gpsSol
.svInfo
[i
].svid
= 0;
155 gpsSol
.svInfo
[i
].quality
= 0;
156 gpsSol
.svInfo
[i
].cno
= 0;
162 gpsSol
.flags
.fix3D
= 0;
163 gpsSol
.flags
.validVelNE
= 0;
164 gpsSol
.flags
.validVelD
= 0;
165 gpsSol
.flags
.validMag
= 0;
168 void gpsPreInit(gpsConfig_t
*initialGpsConfig
)
170 // Make sure gpsProvider is known when gpsMagDetect is called
171 gpsState
.gpsConfig
= initialGpsConfig
;
174 void gpsInit(serialConfig_t
*initialSerialConfig
, gpsConfig_t
*initialGpsConfig
)
176 gpsState
.serialConfig
= initialSerialConfig
;
177 gpsState
.gpsConfig
= initialGpsConfig
;
178 gpsState
.baudrateIndex
= 0;
181 gpsStats
.timeouts
= 0;
185 // init gpsData structure. if we're not actually enabled, don't bother doing anything else
186 gpsState
.autoConfigStep
= 0;
187 gpsState
.lastMessageMs
= millis();
188 gpsSetState(GPS_UNKNOWN
);
190 if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_BUS
) {
191 gpsSetState(GPS_INITIALIZING
);
195 if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
) {
196 serialPortConfig_t
* gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
197 if (!gpsPortConfig
) {
198 featureClear(FEATURE_GPS
);
201 while (gpsToSerialBaudRate
[gpsState
.baudrateIndex
] != gpsPortConfig
->gps_baudrateIndex
) {
202 gpsState
.baudrateIndex
++;
203 if (gpsState
.baudrateIndex
>= GPS_BAUDRATE_COUNT
) {
204 gpsState
.baudrateIndex
= 0;
209 portMode_t mode
= gpsProviders
[gpsState
.gpsConfig
->provider
].portMode
;
211 // no callback - buffer will be consumed in gpsThread()
212 gpsState
.gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, gpsToSerialBaudRate
[gpsState
.baudrateIndex
], mode
, SERIAL_NOT_INVERTED
);
214 if (!gpsState
.gpsPort
) {
215 featureClear(FEATURE_GPS
);
218 gpsSetState(GPS_INITIALIZING
);
226 static void gpsFakeGPSUpdate(void)
228 if (millis() - gpsState
.lastMessageMs
> 100) {
230 gpsSol
.llh
.lat
= 509102311;
231 gpsSol
.llh
.lon
= -15349744;
233 gpsSol
.groundSpeed
= 0;
234 gpsSol
.groundCourse
= 0;
235 gpsSol
.velNED
[X
] = 0;
236 gpsSol
.velNED
[Y
] = 0;
237 gpsSol
.velNED
[Z
] = 0;
238 gpsSol
.flags
.validVelNE
= 1;
239 gpsSol
.flags
.validVelD
= 1;
240 gpsSol
.flags
.validEPE
= 1;
244 ENABLE_STATE(GPS_FIX
);
245 sensorsSet(SENSOR_GPS
);
248 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
249 gpsState
.lastMessageMs
= millis();
251 gpsSetState(GPS_RECEIVING_DATA
);
256 // Finish baud rate change sequence - wait for TX buffer to empty and switch to the desired port speed
257 void gpsFinalizeChangeBaud(void)
259 if ((gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
) && (gpsState
.gpsPort
!= NULL
)) {
260 // Wait for GPS_INIT_DELAY before switching to required baud rate
261 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_BAUD_CHANGE_DELAY
&& isSerialTransmitBufferEmpty(gpsState
.gpsPort
)) {
262 // Switch to required serial port baud
263 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]]);
264 gpsState
.lastMessageMs
= millis();
265 gpsSetState(GPS_CHECK_VERSION
);
270 uint16_t gpsConstrainEPE(uint32_t epe
)
272 return (epe
> 9999) ? 9999 : epe
;
281 debug
[0] = gpsSol
.eph
;
282 debug
[1] = gpsSol
.epv
;
285 if ((gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
) && (gpsState
.gpsPort
!= NULL
)) {
286 switch (gpsState
.state
) {
288 case GPS_INITIALIZING
:
289 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_INIT_DELAY
) {
291 DISABLE_STATE(GPS_FIX
);
292 gpsState
.hwVersion
= 0;
293 gpsState
.autoConfigStep
= 0;
294 gpsState
.autoConfigPosition
= 0;
295 gpsState
.autoBaudrateIndex
= 0;
300 // Call protocol handler - switch to next state is done there
305 case GPS_CHANGE_BAUD
:
306 // Call protocol handler - switch to next state is done there
310 case GPS_CHECK_VERSION
:
312 case GPS_RECEIVING_DATA
:
314 if ((millis() - gpsState
.lastMessageMs
) > GPS_TIMEOUT
) {
315 // Check for GPS timeout
316 sensorsClear(SENSOR_GPS
);
317 DISABLE_STATE(GPS_FIX
);
318 gpsSetState(GPS_LOST_COMMUNICATION
);
322 case GPS_LOST_COMMUNICATION
:
324 // Handle autobaud - switch to next port baud rate
325 if (gpsState
.gpsConfig
->autoBaud
!= GPS_AUTOBAUD_OFF
) {
326 gpsState
.baudrateIndex
++;
327 gpsState
.baudrateIndex
%= GPS_BAUDRATE_COUNT
;
329 gpsSetState(GPS_INITIALIZING
);
333 // Driver-based GPS (I2C)
334 else if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_BUS
) {
335 switch (gpsState
.state
) {
337 case GPS_INITIALIZING
:
339 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_BUS_INIT_DELAY
) {
342 if (gpsProviders
[gpsState
.gpsConfig
->provider
].detect
&& gpsProviders
[gpsState
.gpsConfig
->provider
].detect()) {
343 gpsState
.hwVersion
= 0;
344 gpsState
.autoConfigStep
= 0;
345 gpsState
.autoConfigPosition
= 0;
346 gpsState
.lastMessageMs
= millis();
347 sensorsSet(SENSOR_GPS
);
348 gpsSetState(GPS_CHANGE_BAUD
);
351 sensorsClear(SENSOR_GPS
);
356 case GPS_CHANGE_BAUD
:
357 case GPS_CHECK_VERSION
:
359 case GPS_RECEIVING_DATA
:
361 if (millis() - gpsState
.lastMessageMs
> GPS_TIMEOUT
) {
362 // remove GPS from capability
363 gpsSetState(GPS_LOST_COMMUNICATION
);
367 case GPS_LOST_COMMUNICATION
:
368 // No valid data from GPS unit, cause re-init and re-detection
370 DISABLE_STATE(GPS_FIX
);
371 gpsSetState(GPS_INITIALIZING
);
381 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
383 waitForSerialPortToFinishTransmitting(gpsState
.gpsPort
);
384 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
386 if(!(gpsState
.gpsPort
->mode
& MODE_TX
))
387 serialSetMode(gpsState
.gpsPort
, gpsState
.gpsPort
->mode
| MODE_TX
);
394 if (serialRxBytesWaiting(gpsState
.gpsPort
)) {
396 c
= serialRead(gpsState
.gpsPort
);
397 serialWrite(gpsPassthroughPort
, c
);
400 if (serialRxBytesWaiting(gpsPassthroughPort
)) {
402 c
= serialRead(gpsPassthroughPort
);
403 serialWrite(gpsState
.gpsPort
, c
);
409 void updateGpsIndicator(uint32_t currentTime
)
411 static uint32_t GPSLEDTime
;
412 if ((int32_t)(currentTime
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
413 GPSLEDTime
= currentTime
+ 150000;
418 /* Support for built-in magnetometer accessible via the native GPS protocol (i.e. NAZA) */
419 void gpsMagInit(void)
423 bool gpsMagRead(int16_t *magData
)
425 magData
[X
] = gpsSol
.magData
[0];
426 magData
[Y
] = gpsSol
.magData
[1];
427 magData
[Z
] = gpsSol
.magData
[2];
428 return gpsSol
.flags
.validMag
;
431 bool gpsMagDetect(mag_t
*mag
)
433 if (!(feature(FEATURE_GPS
) && gpsProviders
[gpsState
.gpsConfig
->provider
].hasCompass
))
436 if (gpsProviders
[gpsState
.gpsConfig
->provider
].type
== GPS_TYPE_SERIAL
&& (!findSerialPortConfig(FUNCTION_GPS
))) {
440 mag
->init
= gpsMagInit
;
441 mag
->read
= gpsMagRead
;