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 void (*restart
)(void); // Restart protocol driver thread
67 void (*protocol
)(void); // Process protocol driver thread
68 } gpsProviderDescriptor_t
;
71 gpsReceiverData_t gpsState
;
72 gpsStatistics_t gpsStats
;
73 gpsSolutionData_t gpsSol
;
75 // Map gpsBaudRate_e index to baudRate_e
76 baudRate_e gpsToSerialBaudRate
[GPS_BAUDRATE_COUNT
] = { BAUD_115200
, BAUD_57600
, BAUD_38400
, BAUD_19200
, BAUD_9600
, BAUD_230400
};
78 static gpsProviderDescriptor_t gpsProviders
[GPS_PROVIDER_COUNT
] = {
80 #ifdef USE_GPS_PROTO_NMEA
81 { false, MODE_RX
, gpsRestartNMEA
, &gpsHandleNMEA
},
83 { false, 0, NULL
, NULL
},
87 #ifdef USE_GPS_PROTO_UBLOX
88 { false, MODE_RXTX
, &gpsRestartUBLOX
, &gpsHandleUBLOX
},
90 { false, 0, NULL
, NULL
},
93 /* UBLOX7PLUS binary */
94 #ifdef USE_GPS_PROTO_UBLOX
95 { false, MODE_RXTX
, &gpsRestartUBLOX
, &gpsHandleUBLOX
},
97 { false, 0, NULL
, NULL
},
101 #ifdef USE_GPS_PROTO_MSP
102 { true, 0, &gpsRestartMSP
, &gpsHandleMSP
},
104 { false, 0, NULL
, NULL
},
108 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 2);
110 PG_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
,
111 .provider
= SETTING_GPS_PROVIDER_DEFAULT
,
112 .sbasMode
= SETTING_GPS_SBAS_MODE_DEFAULT
,
113 .autoConfig
= SETTING_GPS_AUTO_CONFIG_DEFAULT
,
114 .autoBaud
= SETTING_GPS_AUTO_BAUD_DEFAULT
,
115 .dynModel
= SETTING_GPS_DYN_MODEL_DEFAULT
,
116 .gpsMinSats
= SETTING_GPS_MIN_SATS_DEFAULT
,
117 .ubloxUseGalileo
= SETTING_GPS_UBLOX_USE_GALILEO_DEFAULT
120 void gpsSetState(gpsState_e state
)
122 gpsState
.state
= state
;
123 gpsState
.lastStateSwitchMs
= millis();
126 static void gpsUpdateTime(void)
128 if (!rtcHasTime() && gpsSol
.flags
.validTime
&& gpsSol
.time
.year
!= 0) {
129 rtcSetDateTime(&gpsSol
.time
);
133 void gpsSetProtocolTimeout(timeMs_t timeoutMs
)
135 gpsState
.lastLastMessageMs
= gpsState
.lastMessageMs
;
136 gpsState
.lastMessageMs
= millis();
137 gpsState
.timeoutMs
= timeoutMs
;
140 void gpsProcessNewSolutionData(void)
142 // Set GPS fix flag only if we have 3D fix
143 if (gpsSol
.fixType
== GPS_FIX_3D
&& gpsSol
.numSat
>= gpsConfig()->gpsMinSats
) {
144 ENABLE_STATE(GPS_FIX
);
147 /* When no fix available - reset flags as well */
148 gpsSol
.flags
.validVelNE
= false;
149 gpsSol
.flags
.validVelD
= false;
150 gpsSol
.flags
.validEPE
= false;
151 DISABLE_STATE(GPS_FIX
);
154 // Set sensor as ready and available
155 sensorsSet(SENSOR_GPS
);
157 // Pass on GPS update to NAV and IMU
164 gpsSetProtocolTimeout(gpsState
.baseTimeoutMs
);
167 gpsStats
.lastMessageDt
= gpsState
.lastMessageMs
- gpsState
.lastLastMessageMs
;
168 gpsSol
.flags
.hasNewData
= true;
171 gpsSol
.flags
.gpsHeartbeat
= !gpsSol
.flags
.gpsHeartbeat
;
174 static void gpsResetSolution(void)
181 gpsSol
.fixType
= GPS_NO_FIX
;
183 gpsSol
.flags
.validVelNE
= false;
184 gpsSol
.flags
.validVelD
= false;
185 gpsSol
.flags
.validMag
= false;
186 gpsSol
.flags
.validEPE
= false;
187 gpsSol
.flags
.validTime
= false;
190 void gpsPreInit(void)
192 // Make sure gpsProvider is known when gpsMagDetect is called
193 gpsState
.gpsConfig
= gpsConfig();
194 gpsState
.baseTimeoutMs
= (gpsState
.gpsConfig
->provider
== GPS_NMEA
) ? GPS_TIMEOUT
*2 : GPS_TIMEOUT
;
199 gpsState
.serialConfig
= serialConfig();
200 gpsState
.gpsConfig
= gpsConfig();
203 gpsStats
.timeouts
= 0;
205 // Reset solution, timeout and prepare to start
207 gpsSetProtocolTimeout(gpsState
.baseTimeoutMs
);
208 gpsSetState(GPS_UNKNOWN
);
210 // If given GPS provider has protocol() function not defined - we can't use it
211 if (!gpsProviders
[gpsState
.gpsConfig
->provider
].protocol
) {
212 featureClear(FEATURE_GPS
);
216 // Shortcut for driver-based GPS (MSP)
217 if (gpsProviders
[gpsState
.gpsConfig
->provider
].isDriverBased
) {
218 gpsSetState(GPS_INITIALIZING
);
222 serialPortConfig_t
* gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
223 if (!gpsPortConfig
) {
224 featureClear(FEATURE_GPS
);
228 // Start with baud rate index as configured for serial port
230 gpsState
.baudrateIndex
= GPS_BAUDRATE_115200
;
231 for (baudrateIndex
= 0, gpsState
.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 speed
= speed
* sin_approx((now
% 1000) / 1000.f
* M_PIf
) * +speed
;
276 int32_t cmDelta
= speed
* (delta
/ 1000.0f
);
277 int32_t latCmDelta
= cmDelta
* cos_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
278 int32_t lonCmDelta
= cmDelta
* sin_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
279 int32_t latDelta
= ceilf((float)latCmDelta
/ (111 * 1000 * 100 / 1e7
));
280 int32_t lonDelta
= ceilf((float)lonCmDelta
/ (111 * 1000 * 100 / 1e7
));
281 if (speed
> 0 && latDelta
== 0 && lonDelta
== 0) {
286 gpsSol
.fixType
= GPS_FIX_3D
;
288 gpsSol
.llh
.lat
= lat
;
289 gpsSol
.llh
.lon
= lon
;
291 gpsSol
.groundSpeed
= speed
;
292 gpsSol
.groundCourse
= FAKE_GPS_GROUND_COURSE_DECIDEGREES
;
293 gpsSol
.velNED
[X
] = speed
* cos_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
294 gpsSol
.velNED
[Y
] = speed
* sin_approx(DECIDEGREES_TO_RADIANS(FAKE_GPS_GROUND_COURSE_DECIDEGREES
));
295 gpsSol
.velNED
[Z
] = 0;
296 gpsSol
.flags
.validVelNE
= true;
297 gpsSol
.flags
.validVelD
= true;
298 gpsSol
.flags
.validEPE
= true;
299 gpsSol
.flags
.validTime
= true;
302 gpsSol
.time
.year
= 1983;
303 gpsSol
.time
.month
= 1;
305 gpsSol
.time
.hours
= 3;
306 gpsSol
.time
.minutes
= 15;
307 gpsSol
.time
.seconds
= 42;
309 ENABLE_STATE(GPS_FIX
);
310 sensorsSet(SENSOR_GPS
);
314 gpsSetProtocolTimeout(gpsState
.baseTimeoutMs
);
316 gpsSetState(GPS_RUNNING
);
317 gpsSol
.flags
.gpsHeartbeat
= !gpsSol
.flags
.gpsHeartbeat
;
324 uint16_t gpsConstrainEPE(uint32_t epe
)
326 return (epe
> 9999) ? 9999 : epe
; // max 99.99m error
329 uint16_t gpsConstrainHDOP(uint32_t hdop
)
331 return (hdop
> 9999) ? 9999 : hdop
; // max 99.99m error
337 if (!feature(FEATURE_GPS
)) {
338 sensorsClear(SENSOR_GPS
);
339 DISABLE_STATE(GPS_FIX
);
343 /* Extra delay for at least 2 seconds after booting to give GPS time to initialise */
344 if (!isMPUSoftReset() && (millis() < GPS_BOOT_DELAY
)) {
345 sensorsClear(SENSOR_GPS
);
346 DISABLE_STATE(GPS_FIX
);
351 if (ARMING_FLAG(SIMULATOR_MODE
)) {
353 gpsSetState(GPS_RUNNING
);
354 sensorsSet(SENSOR_GPS
);
355 return gpsSol
.flags
.hasNewData
;
359 return gpsFakeGPSUpdate();
362 // Assume that we don't have new data this run
363 gpsSol
.flags
.hasNewData
= false;
365 switch (gpsState
.state
) {
367 case GPS_INITIALIZING
:
368 // Wait for GPS_INIT_DELAY before starting the GPS protocol thread
369 if ((millis() - gpsState
.lastStateSwitchMs
) >= GPS_INIT_DELAY
) {
371 DISABLE_STATE(GPS_FIX
);
372 gpsSol
.fixType
= GPS_NO_FIX
;
377 // Call GPS protocol reset handler
378 gpsProviders
[gpsState
.gpsConfig
->provider
].restart();
380 // Switch to GPS_RUNNING state (mind the timeout)
381 gpsSetProtocolTimeout(gpsState
.baseTimeoutMs
);
382 gpsSetState(GPS_RUNNING
);
387 // Call GPS protocol thread
388 gpsProviders
[gpsState
.gpsConfig
->provider
].protocol();
390 // Check for GPS timeout
391 if ((millis() - gpsState
.lastMessageMs
) > gpsState
.baseTimeoutMs
) {
392 sensorsClear(SENSOR_GPS
);
393 DISABLE_STATE(GPS_FIX
);
394 gpsSol
.fixType
= GPS_NO_FIX
;
395 gpsSetState(GPS_LOST_COMMUNICATION
);
399 case GPS_LOST_COMMUNICATION
:
401 gpsSetState(GPS_INITIALIZING
);
405 return gpsSol
.flags
.hasNewData
;
409 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
411 waitForSerialPortToFinishTransmitting(gpsState
.gpsPort
);
412 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
414 if (!(gpsState
.gpsPort
->mode
& MODE_TX
))
415 serialSetMode(gpsState
.gpsPort
, gpsState
.gpsPort
->mode
| MODE_TX
);
422 if (serialRxBytesWaiting(gpsState
.gpsPort
)) {
424 c
= serialRead(gpsState
.gpsPort
);
425 serialWrite(gpsPassthroughPort
, c
);
428 if (serialRxBytesWaiting(gpsPassthroughPort
)) {
430 c
= serialRead(gpsPassthroughPort
);
431 serialWrite(gpsState
.gpsPort
, c
);
437 void updateGpsIndicator(timeUs_t currentTimeUs
)
439 static timeUs_t GPSLEDTime
;
440 if ((int32_t)(currentTimeUs
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
441 GPSLEDTime
= currentTimeUs
+ 150000;
446 bool isGPSHealthy(void)
451 bool isGPSHeadingValid(void)
453 return sensors(SENSOR_GPS
) && STATE(GPS_FIX
) && gpsSol
.numSat
>= 6 && gpsSol
.groundSpeed
>= 300;