vtx: fix VTX_SETTINGS_POWER_COUNT and add dummy entries to saPowerNames
[inav.git] / src / main / io / gps.c
blobe7aecd3e0d688cd02565901af1d480cf504d1ef9
1 /*
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/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <ctype.h>
21 #include <math.h>
23 #include "platform.h"
24 #include "build/build_config.h"
27 #ifdef USE_GPS
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"
46 #endif
48 #include "fc/rc_modes.h"
50 #include "sensors/sensors.h"
51 #include "sensors/compass.h"
52 #include "sensors/barometer.h"
53 #include "sensors/pitotmeter.h"
55 #include "io/serial.h"
56 #include "io/gps.h"
57 #include "io/gps_private.h"
58 #include "io/gps_ublox.h"
60 #include "navigation/navigation.h"
61 #include "navigation/navigation_private.h"
63 #include "config/feature.h"
65 #include "fc/config.h"
66 #include "fc/runtime_config.h"
67 #include "fc/settings.h"
69 #include "flight/imu.h"
70 #include "flight/wind_estimator.h"
71 #include "flight/pid.h"
72 #include "flight/mixer.h"
74 #include "programming/logic_condition.h"
76 typedef struct {
77 bool isDriverBased;
78 portMode_t portMode; // Port mode RX/TX (only for serial based)
79 void (*restart)(void); // Restart protocol driver thread
80 void (*protocol)(void); // Process protocol driver thread
81 } gpsProviderDescriptor_t;
83 // GPS public data
84 gpsReceiverData_t gpsState;
85 gpsStatistics_t gpsStats;
87 //it is not safe to access gpsSolDRV which is filled in gps thread by driver.
88 //gpsSolDRV can be accessed only after driver signalled that data is ready
89 //we copy gpsSolDRV to gpsSol, process by "Disable GPS logic condition" and "GPS Fix estimation" features
90 //and use it in the rest of code.
91 gpsSolutionData_t gpsSolDRV; //filled by driver
92 gpsSolutionData_t gpsSol; //used in the rest of the code
94 // Map gpsBaudRate_e index to baudRate_e
95 baudRate_e gpsToSerialBaudRate[GPS_BAUDRATE_COUNT] = { BAUD_115200, BAUD_57600, BAUD_38400, BAUD_19200, BAUD_9600, BAUD_230400, BAUD_460800, BAUD_921600 };
97 static gpsProviderDescriptor_t gpsProviders[GPS_PROVIDER_COUNT] = {
98 /* UBLOX binary */
99 #ifdef USE_GPS_PROTO_UBLOX
100 { false, MODE_RXTX, &gpsRestartUBLOX, &gpsHandleUBLOX },
101 #else
102 { false, 0, NULL, NULL },
103 #endif
105 /* MSP GPS */
106 #ifdef USE_GPS_PROTO_MSP
107 { true, 0, &gpsRestartMSP, &gpsHandleMSP },
108 #else
109 { false, 0, NULL, NULL },
110 #endif
112 #ifdef USE_GPS_FAKE
113 {true, 0, &gpsFakeRestart, &gpsFakeHandle},
114 #else
115 { false, 0, NULL, NULL },
116 #endif
120 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t, gpsConfig, PG_GPS_CONFIG, 5);
122 PG_RESET_TEMPLATE(gpsConfig_t, gpsConfig,
123 .provider = SETTING_GPS_PROVIDER_DEFAULT,
124 .sbasMode = SETTING_GPS_SBAS_MODE_DEFAULT,
125 .autoConfig = SETTING_GPS_AUTO_CONFIG_DEFAULT,
126 .autoBaud = SETTING_GPS_AUTO_BAUD_DEFAULT,
127 .dynModel = SETTING_GPS_DYN_MODEL_DEFAULT,
128 .gpsMinSats = SETTING_GPS_MIN_SATS_DEFAULT,
129 .ubloxUseGalileo = SETTING_GPS_UBLOX_USE_GALILEO_DEFAULT,
130 .ubloxUseBeidou = SETTING_GPS_UBLOX_USE_BEIDOU_DEFAULT,
131 .ubloxUseGlonass = SETTING_GPS_UBLOX_USE_GLONASS_DEFAULT,
132 .ubloxNavHz = SETTING_GPS_UBLOX_NAV_HZ_DEFAULT,
133 .autoBaudMax = SETTING_GPS_AUTO_BAUD_MAX_SUPPORTED_DEFAULT
136 int gpsBaudRateToInt(gpsBaudRate_e baudrate)
138 switch(baudrate)
140 case GPS_BAUDRATE_115200:
141 return 115200;
142 case GPS_BAUDRATE_57600:
143 return 57600;
144 case GPS_BAUDRATE_38400:
145 return 38400;
146 case GPS_BAUDRATE_19200:
147 return 19200;
148 case GPS_BAUDRATE_9600:
149 return 9600;
150 case GPS_BAUDRATE_230400:
151 return 230400;
152 case GPS_BAUDRATE_460800:
153 return 460800;
154 case GPS_BAUDRATE_921600:
155 return 921600;
156 default:
157 return 0;
161 int getGpsBaudrate(void)
163 return gpsBaudRateToInt(gpsState.baudrateIndex);
166 const char *getGpsHwVersion(void)
168 switch(gpsState.hwVersion)
170 case UBX_HW_VERSION_UBLOX5:
171 return "UBLOX5";
172 case UBX_HW_VERSION_UBLOX6:
173 return "UBLOX6";
174 case UBX_HW_VERSION_UBLOX7:
175 return "UBLOX7";
176 case UBX_HW_VERSION_UBLOX8:
177 return "UBLOX8";
178 case UBX_HW_VERSION_UBLOX9:
179 return "UBLOX9";
180 case UBX_HW_VERSION_UBLOX10:
181 return "UBLOX10";
182 default:
183 return "Unknown";
187 uint8_t getGpsProtoMajorVersion(void)
189 return gpsState.swVersionMajor;
192 uint8_t getGpsProtoMinorVersion(void)
194 return gpsState.swVersionMinor;
197 void gpsSetState(gpsState_e state)
199 gpsState.state = state;
200 gpsState.lastStateSwitchMs = millis();
203 static void gpsUpdateTime(void)
205 if (!rtcHasTime() && gpsSol.flags.validTime && gpsSol.time.year != 0) {
206 rtcSetDateTime(&gpsSol.time);
210 void gpsSetProtocolTimeout(timeMs_t timeoutMs)
212 gpsState.lastLastMessageMs = gpsState.lastMessageMs;
213 gpsState.lastMessageMs = millis();
214 gpsState.timeoutMs = timeoutMs;
217 #ifdef USE_GPS_FIX_ESTIMATION
218 bool canEstimateGPSFix(void)
220 #if defined(USE_GPS) && defined(USE_BARO)
222 //we do not check neither sensors(SENSOR_GPS) nor FEATURE(FEATURE_GPS) because:
223 //1) checking STATE(GPS_FIX_HOME) is enough to ensure that GPS sensor was initialized once
224 //2) sensors(SENSOR_GPS) is false on GPS timeout. We also want to support GPS timeouts, not just lost fix
225 return positionEstimationConfig()->allow_gps_fix_estimation && STATE(AIRPLANE) &&
226 sensors(SENSOR_BARO) && baroIsHealthy() &&
227 ARMING_FLAG(WAS_EVER_ARMED) && STATE(GPS_FIX_HOME);
229 #else
230 return false;
231 #endif
233 #endif
235 #ifdef USE_GPS_FIX_ESTIMATION
236 void processDisableGPSFix(void)
238 static int32_t last_lat = 0;
239 static int32_t last_lon = 0;
240 static int32_t last_alt = 0;
242 if (LOGIC_CONDITION_GLOBAL_FLAG(LOGIC_CONDITION_GLOBAL_FLAG_DISABLE_GPS_FIX)) {
243 gpsSol.fixType = GPS_NO_FIX;
244 gpsSol.hdop = 9999;
245 gpsSol.numSat = 0;
247 gpsSol.flags.validVelNE = false;
248 gpsSol.flags.validVelD = false;
249 gpsSol.flags.validEPE = false;
250 gpsSol.flags.validTime = false;
252 //freeze coordinates
253 gpsSol.llh.lat = last_lat;
254 gpsSol.llh.lon = last_lon;
255 gpsSol.llh.alt = last_alt;
256 } else {
257 last_lat = gpsSol.llh.lat;
258 last_lon = gpsSol.llh.lon;
259 last_alt = gpsSol.llh.alt;
262 #endif
264 #ifdef USE_GPS_FIX_ESTIMATION
265 //called after gpsSolDRV is copied to gpsSol and processed by "Disable GPS Fix logical condition"
266 void updateEstimatedGPSFix(void)
268 static uint32_t lastUpdateMs = 0;
269 static int32_t estimated_lat = 0;
270 static int32_t estimated_lon = 0;
271 static int32_t estimated_alt = 0;
273 uint32_t t = millis();
274 int32_t dt = t - lastUpdateMs;
275 lastUpdateMs = t;
277 bool sensorHasFix = gpsSol.fixType == GPS_FIX_3D && gpsSol.numSat >= gpsConfig()->gpsMinSats;
279 if (sensorHasFix || !canEstimateGPSFix()) {
280 estimated_lat = gpsSol.llh.lat;
281 estimated_lon = gpsSol.llh.lon;
282 estimated_alt = posControl.gpsOrigin.alt + baro.BaroAlt;
283 return;
286 gpsSol.fixType = GPS_FIX_3D;
287 gpsSol.hdop = 99;
288 gpsSol.numSat = 99;
290 gpsSol.eph = 100;
291 gpsSol.epv = 100;
293 gpsSol.flags.validVelNE = true;
294 gpsSol.flags.validVelD = false; //do not provide velocity.z
295 gpsSol.flags.validEPE = true;
296 gpsSol.flags.validTime = false;
298 float speed = pidProfile()->fixedWingReferenceAirspeed;
300 #ifdef USE_PITOT
301 if (sensors(SENSOR_PITOT) && pitotIsHealthy()) {
302 speed = getAirspeedEstimate();
304 #endif
306 float velX = rMat[0][0] * speed;
307 float velY = -rMat[1][0] * speed;
308 // here (velX, velY) is estimated horizontal speed without wind influence = airspeed, cm/sec in NEU frame
310 if (isEstimatedWindSpeedValid()) {
311 velX += getEstimatedWindSpeed(X);
312 velY += getEstimatedWindSpeed(Y);
314 // here (velX, velY) is estimated horizontal speed with wind influence = ground speed
316 if (STATE(LANDING_DETECTED) || ((posControl.navState == NAV_STATE_RTH_LANDING) && (getThrottlePercent(false) == 0))) {
317 velX = 0;
318 velY = 0;
321 estimated_lat += (int32_t)( velX * dt / (DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR * 1000 ) );
322 estimated_lon += (int32_t)( velY * dt / (DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR * 1000 * posControl.gpsOrigin.scale) );
323 estimated_alt = posControl.gpsOrigin.alt + baro.BaroAlt;
325 gpsSol.llh.lat = estimated_lat;
326 gpsSol.llh.lon = estimated_lon;
327 gpsSol.llh.alt = estimated_alt;
329 gpsSol.groundSpeed = (int16_t)fast_fsqrtf(velX * velX + velY * velY);
331 float groundCourse = atan2_approx(velY, velX); // atan2 returns [-M_PI, M_PI], with 0 indicating the vector points in the X direction
332 if (groundCourse < 0) {
333 groundCourse += 2 * M_PIf;
335 gpsSol.groundCourse = RADIANS_TO_DECIDEGREES(groundCourse);
337 gpsSol.velNED[X] = (int16_t)(velX);
338 gpsSol.velNED[Y] = (int16_t)(velY);
339 gpsSol.velNED[Z] = 0;
341 #endif
344 void gpsProcessNewDriverData(void)
346 gpsSol = gpsSolDRV;
348 #ifdef USE_GPS_FIX_ESTIMATION
349 processDisableGPSFix();
350 updateEstimatedGPSFix();
351 #endif
354 //called after:
355 //1)driver copies gpsSolDRV to gpsSol
356 //2)gpsSol is processed by "Disable GPS logical switch"
357 //3)gpsSol is processed by GPS Fix estimator - updateEstimatedGPSFix()
358 //On GPS sensor timeout - called after updateEstimatedGPSFix()
359 void gpsProcessNewSolutionData(bool timeout)
361 #ifdef USE_GPS_FIX_ESTIMATION
362 if ( gpsSol.numSat == 99 ) {
363 ENABLE_STATE(GPS_ESTIMATED_FIX);
364 DISABLE_STATE(GPS_FIX);
365 } else {
366 DISABLE_STATE(GPS_ESTIMATED_FIX);
367 #endif
369 // Set GPS fix flag only if we have 3D fix
370 if (gpsSol.fixType == GPS_FIX_3D && gpsSol.numSat >= gpsConfig()->gpsMinSats) {
371 ENABLE_STATE(GPS_FIX);
373 else {
374 /* When no fix available - reset flags as well */
375 gpsSol.flags.validVelNE = false;
376 gpsSol.flags.validVelD = false;
377 gpsSol.flags.validEPE = false;
378 DISABLE_STATE(GPS_FIX);
380 #ifdef USE_GPS_FIX_ESTIMATION
382 #endif
384 if (!timeout) {
385 // Data came from GPS sensor - set sensor as ready and available (it may still not have GPS fix)
386 sensorsSet(SENSOR_GPS);
389 // Pass on GPS update to NAV and IMU
390 onNewGPSData();
392 // Update time
393 gpsUpdateTime();
395 // Update timeout
396 gpsSetProtocolTimeout(gpsState.baseTimeoutMs);
398 // Update statistics
399 gpsStats.lastMessageDt = gpsState.lastMessageMs - gpsState.lastLastMessageMs;
400 gpsSol.flags.hasNewData = true;
402 // Toggle heartbeat
403 gpsSol.flags.gpsHeartbeat = !gpsSol.flags.gpsHeartbeat;
406 static void gpsResetSolution(gpsSolutionData_t* gpsSol)
408 gpsSol->eph = 9999;
409 gpsSol->epv = 9999;
410 gpsSol->numSat = 0;
411 gpsSol->hdop = 9999;
413 gpsSol->fixType = GPS_NO_FIX;
415 gpsSol->flags.validVelNE = false;
416 gpsSol->flags.validVelD = false;
417 gpsSol->flags.validEPE = false;
418 gpsSol->flags.validTime = false;
421 void gpsTryEstimateOnTimeout(void)
423 gpsResetSolution(&gpsSol);
424 DISABLE_STATE(GPS_FIX);
426 #ifdef USE_GPS_FIX_ESTIMATION
427 if ( canEstimateGPSFix() ) {
428 updateEstimatedGPSFix();
430 if (gpsSol.fixType == GPS_FIX_3D) { //estimation kicked in
431 gpsProcessNewSolutionData(true);
434 #endif
437 void gpsPreInit(void)
439 // Make sure gpsProvider is known when gpsMagDetect is called
440 gpsState.gpsConfig = gpsConfig();
441 gpsState.baseTimeoutMs = GPS_TIMEOUT;
444 void gpsInit(void)
446 gpsState.serialConfig = serialConfig();
447 gpsState.gpsConfig = gpsConfig();
449 gpsStats.errors = 0;
450 gpsStats.timeouts = 0;
452 // Reset solution, timeout and prepare to start
453 gpsResetSolution(&gpsSolDRV);
454 gpsResetSolution(&gpsSol);
455 gpsSetProtocolTimeout(gpsState.baseTimeoutMs);
456 gpsSetState(GPS_UNKNOWN);
458 // If given GPS provider has protocol() function not defined - we can't use it
459 if (!gpsProviders[gpsState.gpsConfig->provider].protocol) {
460 featureClear(FEATURE_GPS);
461 return;
464 // Shortcut for driver-based GPS (MSP)
465 if (gpsProviders[gpsState.gpsConfig->provider].isDriverBased) {
466 gpsSetState(GPS_INITIALIZING);
467 return;
470 serialPortConfig_t * gpsPortConfig = findSerialPortConfig(FUNCTION_GPS);
471 if (!gpsPortConfig) {
472 featureClear(FEATURE_GPS);
473 return;
476 // Start with baud rate index as configured for serial port
477 int baudrateIndex;
478 gpsState.baudrateIndex = GPS_BAUDRATE_115200;
479 for (baudrateIndex = 0, gpsState.baudrateIndex = 0; baudrateIndex < GPS_BAUDRATE_COUNT; baudrateIndex++) {
480 if (gpsToSerialBaudRate[baudrateIndex] == gpsPortConfig->gps_baudrateIndex) {
481 gpsState.baudrateIndex = baudrateIndex;
482 break;
486 // Start with the same baud for autodetection
487 gpsState.autoBaudrateIndex = gpsState.baudrateIndex;
489 // Open serial port
490 portMode_t mode = gpsProviders[gpsState.gpsConfig->provider].portMode;
491 gpsState.gpsPort = openSerialPort(gpsPortConfig->identifier, FUNCTION_GPS, NULL, NULL, baudRates[gpsToSerialBaudRate[gpsState.baudrateIndex]], mode, SERIAL_NOT_INVERTED);
493 // Check if we have a serial port opened
494 if (!gpsState.gpsPort) {
495 featureClear(FEATURE_GPS);
496 return;
499 gpsSetState(GPS_INITIALIZING);
502 uint16_t gpsConstrainEPE(uint32_t epe)
504 return (epe > 9999) ? 9999 : epe; // max 99.99m error
507 uint16_t gpsConstrainHDOP(uint32_t hdop)
509 return (hdop > 9999) ? 9999 : hdop; // max 99.99m error
512 bool gpsUpdate(void)
514 // Sanity check
515 if (!feature(FEATURE_GPS)) {
516 sensorsClear(SENSOR_GPS);
517 DISABLE_STATE(GPS_FIX);
518 return false;
521 /* Extra delay for at least 2 seconds after booting to give GPS time to initialise */
522 if (!isMPUSoftReset() && (millis() < GPS_BOOT_DELAY)) {
523 sensorsClear(SENSOR_GPS);
524 DISABLE_STATE(GPS_FIX);
525 return false;
528 #ifdef USE_SIMULATOR
529 if (ARMING_FLAG(SIMULATOR_MODE_HITL)) {
530 if ( SIMULATOR_HAS_OPTION(HITL_GPS_TIMEOUT)) {
531 gpsSetState(GPS_LOST_COMMUNICATION);
532 sensorsClear(SENSOR_GPS);
533 gpsStats.timeouts = 5;
534 gpsTryEstimateOnTimeout();
535 } else {
536 gpsSetState(GPS_RUNNING);
537 sensorsSet(SENSOR_GPS);
539 bool res = gpsSol.flags.hasNewData;
540 gpsSol.flags.hasNewData = false;
541 return res;
543 #endif
545 switch (gpsState.state) {
546 default:
547 case GPS_INITIALIZING:
548 // Wait for GPS_INIT_DELAY before starting the GPS protocol thread
549 if ((millis() - gpsState.lastStateSwitchMs) >= GPS_INIT_DELAY) {
550 // Reset internals
551 DISABLE_STATE(GPS_FIX);
553 // Reset solution
554 gpsResetSolution(&gpsSolDRV);
556 // Call GPS protocol reset handler
557 gpsProviders[gpsState.gpsConfig->provider].restart();
559 // Switch to GPS_RUNNING state (mind the timeout)
560 gpsSetProtocolTimeout(gpsState.baseTimeoutMs);
561 gpsSetState(GPS_RUNNING);
563 break;
565 case GPS_RUNNING:
566 // Call GPS protocol thread
567 gpsProviders[gpsState.gpsConfig->provider].protocol();
569 // Check for GPS timeout
570 if ((millis() - gpsState.lastMessageMs) > gpsState.baseTimeoutMs) {
571 sensorsClear(SENSOR_GPS);
572 DISABLE_STATE(GPS_FIX);
573 gpsSol.fixType = GPS_NO_FIX;
574 gpsSetState(GPS_LOST_COMMUNICATION);
576 break;
578 case GPS_LOST_COMMUNICATION:
579 gpsStats.timeouts++;
580 gpsSetState(GPS_INITIALIZING);
581 break;
584 if ( !sensors(SENSOR_GPS) ) {
585 gpsTryEstimateOnTimeout();
588 bool res = gpsSol.flags.hasNewData;
589 gpsSol.flags.hasNewData = false;
590 return res;
593 void gpsEnablePassthrough(serialPort_t *gpsPassthroughPort)
595 waitForSerialPortToFinishTransmitting(gpsState.gpsPort);
596 waitForSerialPortToFinishTransmitting(gpsPassthroughPort);
598 if (!(gpsState.gpsPort->mode & MODE_TX))
599 serialSetMode(gpsState.gpsPort, gpsState.gpsPort->mode | MODE_TX);
601 LED0_OFF;
602 LED1_OFF;
604 char c;
605 while (1) {
606 if (serialRxBytesWaiting(gpsState.gpsPort)) {
607 LED0_ON;
608 c = serialRead(gpsState.gpsPort);
609 serialWrite(gpsPassthroughPort, c);
610 LED0_OFF;
612 if (serialRxBytesWaiting(gpsPassthroughPort)) {
613 LED1_ON;
614 c = serialRead(gpsPassthroughPort);
615 serialWrite(gpsState.gpsPort, c);
616 LED1_OFF;
621 void updateGpsIndicator(timeUs_t currentTimeUs)
623 static timeUs_t GPSLEDTime;
624 if ((int32_t)(currentTimeUs - GPSLEDTime) >= 0 && (gpsSol.numSat>= 5)) {
625 GPSLEDTime = currentTimeUs + 150000;
626 LED1_TOGGLE;
630 bool isGPSHealthy(void)
632 return true;
635 bool isGPSHeadingValid(void)
637 return ((STATE(GPS_FIX) && gpsSol.numSat >= 6)
638 #ifdef USE_GPS_FIX_ESTIMATION
639 || STATE(GPS_ESTIMATED_FIX)
640 #endif
641 ) && gpsSol.groundSpeed >= 300;
644 #endif