2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
31 #include "build/build_config.h"
32 #include "build/debug.h"
34 #include "common/axis.h"
35 #include "common/gps_conversion.h"
36 #include "common/maths.h"
37 #include "common/utils.h"
39 #include "config/feature.h"
41 #include "pg/pg_ids.h"
43 #include "drivers/light_led.h"
44 #include "drivers/time.h"
46 #include "io/dashboard.h"
48 #include "io/serial.h"
50 #include "config/config.h"
51 #include "fc/runtime_config.h"
53 #include "flight/imu.h"
54 #include "flight/pid.h"
55 #include "flight/gps_rescue.h"
57 #include "sensors/sensors.h"
60 #define LOG_IGNORED '!'
61 #define LOG_SKIPPED '>'
62 #define LOG_NMEA_GGA 'g'
63 #define LOG_NMEA_RMC 'r'
64 #define LOG_UBLOX_SOL 'O'
65 #define LOG_UBLOX_STATUS 'S'
66 #define LOG_UBLOX_SVINFO 'I'
67 #define LOG_UBLOX_POSLLH 'P'
68 #define LOG_UBLOX_VELNED 'V'
70 #define GPS_SV_MAXSATS 16
72 char gpsPacketLog
[GPS_PACKET_LOG_ENTRY_COUNT
];
73 static char *gpsPacketLogChar
= gpsPacketLog
;
74 // **********************
76 // **********************
78 uint16_t GPS_distanceToHome
; // distance to home point in meters
79 int16_t GPS_directionToHome
; // direction to home or hol point in degrees
80 uint32_t GPS_distanceFlownInCm
; // distance flown since armed in centimeters
81 int16_t GPS_verticalSpeedInCmS
; // vertical speed in cm/s
82 float dTnav
; // Delta Time in milliseconds for navigation computations, updated with every good GPS read
83 int16_t nav_takeoff_bearing
;
85 #define GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S 15 // 5.4Km/h 3.35mph
87 gpsSolutionData_t gpsSol
;
88 uint32_t GPS_packetCount
= 0;
89 uint32_t GPS_svInfoReceivedCount
= 0; // SV = Space Vehicle, counter increments each time SV info is received.
90 uint8_t GPS_update
= 0; // toogle to distinct a GPS position update (directly or via MSP)
92 uint8_t GPS_numCh
; // Number of channels
93 uint8_t GPS_svinfo_chn
[GPS_SV_MAXSATS
]; // Channel number
94 uint8_t GPS_svinfo_svid
[GPS_SV_MAXSATS
]; // Satellite ID
95 uint8_t GPS_svinfo_quality
[GPS_SV_MAXSATS
]; // Bitfield Qualtity
96 uint8_t GPS_svinfo_cno
[GPS_SV_MAXSATS
]; // Carrier to Noise Ratio (Signal Strength)
98 // GPS timeout for wrong baud rate/disconnection/etc in milliseconds (default 2.5second)
99 #define GPS_TIMEOUT (2500)
100 // How many entries in gpsInitData array below
101 #define GPS_INIT_ENTRIES (GPS_BAUDRATE_MAX + 1)
102 #define GPS_BAUDRATE_CHANGE_DELAY (200)
104 static serialPort_t
*gpsPort
;
106 typedef struct gpsInitData_s
{
108 uint8_t baudrateIndex
; // see baudRate_e
113 // NMEA will cycle through these until valid data is received
114 static const gpsInitData_t gpsInitData
[] = {
115 { GPS_BAUDRATE_115200
, BAUD_115200
, "$PUBX,41,1,0003,0001,115200,0*1E\r\n", "$PMTK251,115200*1F\r\n" },
116 { GPS_BAUDRATE_57600
, BAUD_57600
, "$PUBX,41,1,0003,0001,57600,0*2D\r\n", "$PMTK251,57600*2C\r\n" },
117 { GPS_BAUDRATE_38400
, BAUD_38400
, "$PUBX,41,1,0003,0001,38400,0*26\r\n", "$PMTK251,38400*27\r\n" },
118 { GPS_BAUDRATE_19200
, BAUD_19200
, "$PUBX,41,1,0003,0001,19200,0*23\r\n", "$PMTK251,19200*22\r\n" },
119 // 9600 is not enough for 5Hz updates - leave for compatibility to dumb NMEA that only runs at this speed
120 { GPS_BAUDRATE_9600
, BAUD_9600
, "$PUBX,41,1,0003,0001,9600,0*16\r\n", "" }
123 #define GPS_INIT_DATA_ENTRY_COUNT (sizeof(gpsInitData) / sizeof(gpsInitData[0]))
125 #define DEFAULT_BAUD_RATE_INDEX 0
128 static const uint8_t ubloxInit
[] = {
129 //Preprocessor Pedestrian Dynamic Platform Model Option
130 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x03, 0x03, 0x00, // CFG-NAV5 - Set engine settings
131 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, // Collected by resetting a GPS unit to defaults. Changing mode to Pedistrian and
132 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x3C, 0x00, 0x00, 0x00, // capturing the data from the U-Center binary console.
133 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xC2,
135 // DISABLE NMEA messages
136 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x05, 0x00, 0xFF, 0x19, // VGS: Course over ground and Ground speed
137 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x03, 0x00, 0xFD, 0x15, // GSV: GNSS Satellites in View
138 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x01, 0x00, 0xFB, 0x11, // GLL: Latitude and longitude, with time of position fix and status
139 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x00, 0x00, 0xFA, 0x0F, // GGA: Global positioning system fix data
140 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x02, 0x00, 0xFC, 0x13, // GSA: GNSS DOP and Active Satellites
141 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0xF0, 0x04, 0x00, 0xFE, 0x17, // RMC: Recommended Minimum data
143 // Enable UBLOX messages
144 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x02, 0x01, 0x0E, 0x47, // set POSLLH MSG rate
145 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x03, 0x01, 0x0F, 0x49, // set STATUS MSG rate
146 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x06, 0x01, 0x12, 0x4F, // set SOL MSG rate
147 //0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x30, 0x01, 0x3C, 0xA3, // set SVINFO MSG rate (every cycle - high bandwidth)
148 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x30, 0x05, 0x40, 0xA7, // set SVINFO MSG rate (evey 5 cycles - low bandwidth)
149 0xB5, 0x62, 0x06, 0x01, 0x03, 0x00, 0x01, 0x12, 0x01, 0x1E, 0x67, // set VELNED MSG rate
151 0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xC8, 0x00, 0x01, 0x00, 0x01, 0x00, 0xDE, 0x6A, // set rate to 5Hz (measurement period: 200ms, navigation rate: 1 cycle)
154 static const uint8_t ubloxAirborne
[] = {
155 //Preprocessor Airborne_1g Dynamic Platform Model Option
156 #if defined(GPS_UBLOX_MODE_AIRBORNE_1G)
157 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x06, 0x03, 0x00, // CFG-NAV5 - Set engine settings
158 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, // Collected by resetting a GPS unit to defaults. Changing mode to Airborne with
159 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x3C, 0x00, 0x00, 0x00, // <1g acceleration and capturing the data from the U-Center binary console.
160 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x28,
162 //Default Airborne_4g Dynamic Platform Model
164 0xB5, 0x62, 0x06, 0x24, 0x24, 0x00, 0xFF, 0xFF, 0x08, 0x03, 0x00, // CFG-NAV5 - Set engine settings
165 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, // Collected by resetting a GPS unit to defaults. Changing mode to Airborne with
166 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x3C, 0x00, 0x00, 0x00, // <4g acceleration and capturing the data from the U-Center binary console.
167 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x6C,
171 // UBlox 6 Protocol documentation - GPS.G6-SW-10018-F
172 // SBAS Configuration Settings Desciption, Page 4/210
173 // 31.21 CFG-SBAS (0x06 0x16), Page 142/210
174 // A.10 SBAS Configuration (UBX-CFG-SBAS), Page 198/210 - GPS.G6-SW-10018-F
176 #define UBLOX_SBAS_PREFIX_LENGTH 10
178 static const uint8_t ubloxSbasPrefix
[UBLOX_SBAS_PREFIX_LENGTH
] = { 0xB5, 0x62, 0x06, 0x16, 0x08, 0x00, 0x03, 0x07, 0x03, 0x00 };
180 #define UBLOX_SBAS_MESSAGE_LENGTH 6
181 typedef struct ubloxSbas_s
{
183 uint8_t message
[UBLOX_SBAS_MESSAGE_LENGTH
];
188 // Note: these must be defined in the same order is sbasMode_e since no lookup table is used.
189 static const ubloxSbas_t ubloxSbas
[] = {
190 // NOTE this could be optimized to save a few bytes of flash space since the same prefix is used for each command.
191 { SBAS_AUTO
, { 0x00, 0x00, 0x00, 0x00, 0x31, 0xE5}},
192 { SBAS_EGNOS
, { 0x51, 0x08, 0x00, 0x00, 0x8A, 0x41}},
193 { SBAS_WAAS
, { 0x04, 0xE0, 0x04, 0x00, 0x19, 0x9D}},
194 { SBAS_MSAS
, { 0x00, 0x02, 0x02, 0x00, 0x35, 0xEF}},
195 { SBAS_GAGAN
, { 0x80, 0x01, 0x00, 0x00, 0xB2, 0xE8}}
198 // Remove QZSS and add Galileo (only 3 GNSS systems supported simultaneously)
199 // Frame captured from uCenter
200 static const uint8_t ubloxGalileoInit
[] = {
201 0xB5, 0x62, 0x06, 0x3E, 0x3C, // UBX-CGF-GNSS
202 0x00, 0x00, 0x20, 0x20, 0x07, // GNSS
203 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
204 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
205 0x02, 0x04, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, // Galileo
206 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, // BeiDou
207 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x03, // IMES
208 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x05, // QZSS
209 0x06, 0x08, 0x0E, 0x00, 0x01, 0x00, 0x01, 0x01, // GLONASS
212 #endif // USE_GPS_UBLOX
221 GPS_LOST_COMMUNICATION
227 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 0);
229 PG_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
,
230 .provider
= GPS_NMEA
,
231 .sbasMode
= SBAS_AUTO
,
232 .autoConfig
= GPS_AUTOCONFIG_ON
,
233 .autoBaud
= GPS_AUTOBAUD_OFF
,
234 .gps_ublox_use_galileo
= false,
235 .gps_set_home_point_once
= false,
236 .gps_use_3d_speed
= false
239 static void shiftPacketLog(void)
243 for (i
= ARRAYLEN(gpsPacketLog
) - 1; i
> 0 ; i
--) {
244 gpsPacketLog
[i
] = gpsPacketLog
[i
-1];
248 static void gpsNewData(uint16_t c
);
250 static bool gpsNewFrameNMEA(char c
);
253 static bool gpsNewFrameUBLOX(uint8_t data
);
256 static void gpsSetState(gpsState_e state
)
258 gpsData
.state
= state
;
259 gpsData
.state_position
= 0;
260 gpsData
.state_ts
= millis();
261 gpsData
.messageState
= GPS_MESSAGE_STATE_IDLE
;
266 gpsData
.baudrateIndex
= 0;
268 gpsData
.timeouts
= 0;
270 memset(gpsPacketLog
, 0x00, sizeof(gpsPacketLog
));
272 // init gpsData structure. if we're not actually enabled, don't bother doing anything else
273 gpsSetState(GPS_UNKNOWN
);
275 gpsData
.lastMessage
= millis();
277 if (gpsConfig()->provider
== GPS_MSP
) { // no serial ports used when GPS_MSP is configured
278 gpsSetState(GPS_INITIALIZED
);
282 serialPortConfig_t
*gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
283 if (!gpsPortConfig
) {
287 while (gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
!= gpsPortConfig
->gps_baudrateIndex
) {
288 gpsData
.baudrateIndex
++;
289 if (gpsData
.baudrateIndex
>= GPS_INIT_DATA_ENTRY_COUNT
) {
290 gpsData
.baudrateIndex
= DEFAULT_BAUD_RATE_INDEX
;
295 portMode_e mode
= MODE_RXTX
;
296 #if defined(GPS_NMEA_TX_ONLY)
297 if (gpsConfig()->provider
== GPS_NMEA
) {
302 // no callback - buffer will be consumed in gpsUpdate()
303 gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, NULL
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
], mode
, SERIAL_NOT_INVERTED
);
308 // signal GPS "thread" to initialize when it gets to it
309 gpsSetState(GPS_INITIALIZING
);
313 void gpsInitNmea(void)
315 #if !defined(GPS_NMEA_TX_ONLY)
318 switch (gpsData
.state
) {
319 case GPS_INITIALIZING
:
320 #if !defined(GPS_NMEA_TX_ONLY)
322 if (now
- gpsData
.state_ts
< 1000) {
325 gpsData
.state_ts
= now
;
326 if (gpsData
.state_position
< 1) {
327 serialSetBaudRate(gpsPort
, 4800);
328 gpsData
.state_position
++;
329 } else if (gpsData
.state_position
< 2) {
330 // print our FIXED init string for the baudrate we want to be at
331 serialPrint(gpsPort
, "$PSRF100,1,115200,8,1,0*05\r\n");
332 gpsData
.state_position
++;
334 // we're now (hopefully) at the correct rate, next state will switch to it
335 gpsSetState(GPS_CHANGE_BAUD
);
339 case GPS_CHANGE_BAUD
:
340 #if !defined(GPS_NMEA_TX_ONLY)
342 if (now
- gpsData
.state_ts
< 1000) {
345 gpsData
.state_ts
= now
;
346 if (gpsData
.state_position
< 1) {
347 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
348 gpsData
.state_position
++;
349 } else if (gpsData
.state_position
< 2) {
350 serialPrint(gpsPort
, "$PSRF103,00,6,00,0*23\r\n");
351 gpsData
.state_position
++;
355 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
358 gpsSetState(GPS_RECEIVING_DATA
);
362 #endif // USE_GPS_NMEA
365 void gpsInitUblox(void)
368 // UBX will run at the serial port's baudrate, it shouldn't be "autodetected". So here we force it to that rate
370 // Wait until GPS transmit buffer is empty
371 if (!isSerialTransmitBufferEmpty(gpsPort
))
375 switch (gpsData
.state
) {
376 case GPS_INITIALIZING
:
378 if (now
- gpsData
.state_ts
< GPS_BAUDRATE_CHANGE_DELAY
)
381 if (gpsData
.state_position
< GPS_INIT_ENTRIES
) {
382 // try different speed to INIT
383 baudRate_e newBaudRateIndex
= gpsInitData
[gpsData
.state_position
].baudrateIndex
;
385 gpsData
.state_ts
= now
;
387 if (lookupBaudRateIndex(serialGetBaudRate(gpsPort
)) != newBaudRateIndex
) {
388 // change the rate if needed and wait a little
389 serialSetBaudRate(gpsPort
, baudRates
[newBaudRateIndex
]);
393 // print our FIXED init string for the baudrate we want to be at
394 serialPrint(gpsPort
, gpsInitData
[gpsData
.baudrateIndex
].ubx
);
396 gpsData
.state_position
++;
398 // we're now (hopefully) at the correct rate, next state will switch to it
399 gpsSetState(GPS_CHANGE_BAUD
);
402 case GPS_CHANGE_BAUD
:
403 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
404 gpsSetState(GPS_CONFIGURE
);
408 // Either use specific config file for GPS or let dynamically upload config
409 if ( gpsConfig()->autoConfig
== GPS_AUTOCONFIG_OFF
) {
410 gpsSetState(GPS_RECEIVING_DATA
);
414 if (gpsData
.messageState
== GPS_MESSAGE_STATE_IDLE
) {
415 gpsData
.messageState
++;
418 if (gpsData
.messageState
== GPS_MESSAGE_STATE_INIT
) {
420 if (gpsData
.state_position
< sizeof(ubloxInit
)) {
421 serialWrite(gpsPort
, ubloxInit
[gpsData
.state_position
]);
422 gpsData
.state_position
++;
424 gpsData
.state_position
= 0;
425 gpsData
.messageState
++;
429 if (gpsData
.messageState
== GPS_MESSAGE_STATE_SBAS
) {
430 if (gpsData
.state_position
< UBLOX_SBAS_PREFIX_LENGTH
) {
431 serialWrite(gpsPort
, ubloxSbasPrefix
[gpsData
.state_position
]);
432 gpsData
.state_position
++;
433 } else if (gpsData
.state_position
< UBLOX_SBAS_PREFIX_LENGTH
+ UBLOX_SBAS_MESSAGE_LENGTH
) {
434 serialWrite(gpsPort
, ubloxSbas
[gpsConfig()->sbasMode
].message
[gpsData
.state_position
- UBLOX_SBAS_PREFIX_LENGTH
]);
435 gpsData
.state_position
++;
437 gpsData
.state_position
= 0;
438 gpsData
.messageState
++;
442 if (gpsData
.messageState
== GPS_MESSAGE_STATE_GALILEO
) {
443 if ((gpsConfig()->gps_ublox_use_galileo
) && (gpsData
.state_position
< sizeof(ubloxGalileoInit
))) {
444 serialWrite(gpsPort
, ubloxGalileoInit
[gpsData
.state_position
]);
445 gpsData
.state_position
++;
447 gpsData
.state_position
= 0;
448 gpsData
.messageState
++;
452 if (gpsData
.messageState
>= GPS_MESSAGE_STATE_INITIALIZED
) {
453 // ublox should be initialised, try receiving
454 gpsSetState(GPS_RECEIVING_DATA
);
459 #endif // USE_GPS_UBLOX
461 void gpsInitHardware(void)
463 switch (gpsConfig()->provider
) {
480 static void updateGpsIndicator(timeUs_t currentTimeUs
)
482 static uint32_t GPSLEDTime
;
483 if ((int32_t)(currentTimeUs
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
484 GPSLEDTime
= currentTimeUs
+ 150000;
489 void gpsUpdate(timeUs_t currentTimeUs
)
491 // read out available GPS bytes
493 while (serialRxBytesWaiting(gpsPort
))
494 gpsNewData(serialRead(gpsPort
));
495 } else if (GPS_update
& GPS_MSP_UPDATE
) { // GPS data received via MSP
496 gpsSetState(GPS_RECEIVING_DATA
);
497 gpsData
.lastMessage
= millis();
498 sensorsSet(SENSOR_GPS
);
500 GPS_update
&= ~GPS_MSP_UPDATE
;
503 switch (gpsData
.state
) {
505 case GPS_INITIALIZED
:
508 case GPS_INITIALIZING
:
509 case GPS_CHANGE_BAUD
:
514 case GPS_LOST_COMMUNICATION
:
516 if (gpsConfig()->autoBaud
) {
518 gpsData
.baudrateIndex
++;
519 gpsData
.baudrateIndex
%= GPS_INIT_ENTRIES
;
521 gpsData
.lastMessage
= millis();
523 DISABLE_STATE(GPS_FIX
);
524 gpsSetState(GPS_INITIALIZING
);
527 case GPS_RECEIVING_DATA
:
528 // check for no data/gps timeout/cable disconnection etc
529 if (millis() - gpsData
.lastMessage
> GPS_TIMEOUT
) {
530 // remove GPS from capability
531 sensorsClear(SENSOR_GPS
);
532 gpsSetState(GPS_LOST_COMMUNICATION
);
533 #if !defined(GPS_UBLOX_MODE_PEDESTRIAN)
535 if ((gpsData
.messageState
== GPS_MESSAGE_STATE_IDLE
) && STATE(GPS_FIX
)) {
536 gpsData
.messageState
= GPS_MESSAGE_STATE_AIRBORNE
;
537 gpsData
.state_position
= 0;
539 if (gpsData
.messageState
== GPS_MESSAGE_STATE_AIRBORNE
) {
540 if (gpsData
.state_position
< sizeof(ubloxAirborne
)) {
541 serialWrite(gpsPort
, ubloxAirborne
[gpsData
.state_position
]);
542 gpsData
.state_position
++;
544 gpsData
.messageState
= GPS_MESSAGE_STATE_ENTRY_COUNT
;
551 if (sensors(SENSOR_GPS
)) {
552 updateGpsIndicator(currentTimeUs
);
554 if (!ARMING_FLAG(ARMED
) && !gpsConfig()->gps_set_home_point_once
) {
555 DISABLE_STATE(GPS_FIX_HOME
);
557 #if defined(USE_GPS_RESCUE)
558 if (gpsRescueIsConfigured()) {
559 updateGPSRescueState();
564 static void gpsNewData(uint16_t c
)
566 if (!gpsNewFrame(c
)) {
570 // new data received and parsed, we're in business
571 gpsData
.lastLastMessage
= gpsData
.lastMessage
;
572 gpsData
.lastMessage
= millis();
573 sensorsSet(SENSOR_GPS
);
575 GPS_update
^= GPS_DIRECT_TICK
;
578 debug
[3] = GPS_update
;
584 bool gpsNewFrame(uint8_t c
)
586 switch (gpsConfig()->provider
) {
587 case GPS_NMEA
: // NMEA
589 return gpsNewFrameNMEA(c
);
592 case GPS_UBLOX
: // UBX binary
594 return gpsNewFrameUBLOX(c
);
603 // Check for healthy communications
606 return (gpsData
.state
== GPS_RECEIVING_DATA
);
609 /* This is a light implementation of a GPS frame decoding
610 This should work with most of modern GPS devices configured to output 5 frames.
611 It assumes there are some NMEA GGA frames to decode on the serial bus
612 Now verifies checksum correctly before applying data
614 Here we use only the following data :
617 - GPS fix is/is not ok
618 - GPS num sat (4 is enough to be +/- reliable)
620 - GPS altitude (for OSD displaying)
621 - GPS speed (for OSD displaying)
630 // This code is used for parsing NMEA data
633 The latitude or longitude is coded this way in NMEA frames
634 dm.f coded as degrees + minutes + minute decimal
636 - d can be 1 or more char long. generally: 2 char long for latitude, 3 char long for longitude
637 - m is always 2 char long
638 - f can be 1 or more char long
639 This function converts this format in a unique unsigned long where 1 degree = 10 000 000
641 EOS increased the precision here, even if we think that the gps is not precise enough, with 10e5 precision it has 76cm resolution
642 with 10e7 it's around 1 cm now. Increasing it further is irrelevant, since even 1cm resolution is unrealistic, however increased
643 resolution also increased precision of nav calculations
644 static uint32_t GPS_coord_to_degrees(char *coordinateString)
647 uint8_t min, deg = 0;
648 uint16_t frac = 0, mult = 10000;
650 while (*p) { // parse the string until its end
652 frac += (*p - '0') * mult; // calculate only fractional part on up to 5 digits (d != s condition is true when the . is located)
656 d = p; // locate '.' char in the string
662 deg *= 10; // convert degrees : all chars before minutes ; for the first iteration, deg = 0
665 min = *(d - 1) - '0' + (*(d - 2) - '0') * 10; // convert minutes : 2 previous char before '.'
666 return deg * 10000000UL + (min * 100000UL + frac) * 10UL / 6;
672 static uint32_t grab_fields(char *src
, uint8_t mult
)
673 { // convert string to uint32
677 for (i
= 0; src
[i
] != 0; i
++) {
678 if ((i
== 0) && (src
[0] == '-')) { // detect negative sign
680 continue; // jump to next character if the first one was a negative sign
691 if (src
[i
] >= '0' && src
[i
] <= '9') {
695 return 0; // out of bounds
698 return isneg
? -tmp
: tmp
; // handle negative altitudes
701 typedef struct gpsDataNmea_s
{
708 uint16_t ground_course
;
713 static bool gpsNewFrameNMEA(char c
)
715 static gpsDataNmea_t gps_Msg
;
718 static uint8_t param
= 0, offset
= 0, parity
= 0;
719 static char string
[15];
720 static uint8_t checksum_param
, gps_frame
= NO_FRAME
;
721 static uint8_t svMessageNum
= 0;
722 uint8_t svSatNum
= 0, svPacketIdx
= 0, svSatParam
= 0;
733 if (param
== 0) { //frame identification
734 gps_frame
= NO_FRAME
;
735 if (0 == strcmp(string
, "GPGGA") || 0 == strcmp(string
, "GNGGA")) {
736 gps_frame
= FRAME_GGA
;
737 } else if (0 == strcmp(string
, "GPRMC") || 0 == strcmp(string
, "GNRMC")) {
738 gps_frame
= FRAME_RMC
;
739 } else if (0 == strcmp(string
, "GPGSV")) {
740 gps_frame
= FRAME_GSV
;
745 case FRAME_GGA
: //************* GPGGA FRAME parsing
747 // case 1: // Time information
750 gps_Msg
.latitude
= GPS_coord_to_degrees(string
);
753 if (string
[0] == 'S')
754 gps_Msg
.latitude
*= -1;
757 gps_Msg
.longitude
= GPS_coord_to_degrees(string
);
760 if (string
[0] == 'W')
761 gps_Msg
.longitude
*= -1;
764 if (string
[0] > '0') {
765 ENABLE_STATE(GPS_FIX
);
767 DISABLE_STATE(GPS_FIX
);
771 gps_Msg
.numSat
= grab_fields(string
, 0);
774 gps_Msg
.hdop
= grab_fields(string
, 1) * 100; // hdop
777 gps_Msg
.altitudeCm
= grab_fields(string
, 1) * 10; // altitude in centimeters. Note: NMEA delivers altitude with 1 or 3 decimals. It's safer to cut at 0.1m and multiply by 10
781 case FRAME_RMC
: //************* GPRMC FRAME parsing
784 gps_Msg
.time
= grab_fields(string
, 2); // UTC time hhmmss.ss
787 gps_Msg
.speed
= ((grab_fields(string
, 1) * 5144L) / 1000L); // speed in cm/s added by Mis
790 gps_Msg
.ground_course
= (grab_fields(string
, 1)); // ground course deg * 10
793 gps_Msg
.date
= grab_fields(string
, 0); // date dd/mm/yy
800 // Total number of messages of this type in this cycle
804 svMessageNum
= grab_fields(string
, 0);
807 // Total number of SVs visible
808 GPS_numCh
= grab_fields(string
, 0);
814 svPacketIdx
= (param
- 4) / 4 + 1; // satellite number in packet, 1-4
815 svSatNum
= svPacketIdx
+ (4 * (svMessageNum
- 1)); // global satellite number
816 svSatParam
= param
- 3 - (4 * (svPacketIdx
- 1)); // parameter number for satellite
818 if (svSatNum
> GPS_SV_MAXSATS
)
821 switch (svSatParam
) {
824 GPS_svinfo_chn
[svSatNum
- 1] = svSatNum
;
825 GPS_svinfo_svid
[svSatNum
- 1] = grab_fields(string
, 0);
828 // Elevation, in degrees, 90 maximum
831 // Azimuth, degrees from True North, 000 through 359
834 // SNR, 00 through 99 dB (null when not tracking)
835 GPS_svinfo_cno
[svSatNum
- 1] = grab_fields(string
, 0);
836 GPS_svinfo_quality
[svSatNum
- 1] = 0; // only used by ublox
840 GPS_svInfoReceivedCount
++;
854 if (checksum_param
) { //parity checksum
856 uint8_t checksum
= 16 * ((string
[0] >= 'A') ? string
[0] - 'A' + 10 : string
[0] - '0') + ((string
[1] >= 'A') ? string
[1] - 'A' + 10 : string
[1] - '0');
857 if (checksum
== parity
) {
858 *gpsPacketLogChar
= LOG_IGNORED
;
862 *gpsPacketLogChar
= LOG_NMEA_GGA
;
864 if (STATE(GPS_FIX
)) {
865 gpsSol
.llh
.lat
= gps_Msg
.latitude
;
866 gpsSol
.llh
.lon
= gps_Msg
.longitude
;
867 gpsSol
.numSat
= gps_Msg
.numSat
;
868 gpsSol
.llh
.altCm
= gps_Msg
.altitudeCm
;
869 gpsSol
.hdop
= gps_Msg
.hdop
;
873 *gpsPacketLogChar
= LOG_NMEA_RMC
;
874 gpsSol
.groundSpeed
= gps_Msg
.speed
;
875 gpsSol
.groundCourse
= gps_Msg
.ground_course
;
877 // This check will miss 00:00:00.00, but we shouldn't care - next report will be valid
878 if(!rtcHasTime() && gps_Msg
.date
!= 0 && gps_Msg
.time
!= 0) {
879 dateTime_t temp_time
;
880 temp_time
.year
= (gps_Msg
.date
% 100) + 2000;
881 temp_time
.month
= (gps_Msg
.date
/ 100) % 100;
882 temp_time
.day
= (gps_Msg
.date
/ 10000) % 100;
883 temp_time
.hours
= (gps_Msg
.time
/ 1000000) % 100;
884 temp_time
.minutes
= (gps_Msg
.time
/ 10000) % 100;
885 temp_time
.seconds
= (gps_Msg
.time
/ 100) % 100;
886 temp_time
.millis
= (gps_Msg
.time
& 100) * 10;
887 rtcSetDateTime(&temp_time
);
893 *gpsPacketLogChar
= LOG_ERROR
;
900 string
[offset
++] = c
;
906 #endif // USE_GPS_NMEA
919 uint32_t time
; // GPS msToW
922 int32_t altitude_ellipsoid
;
923 int32_t altitudeMslMm
;
924 uint32_t horizontal_accuracy
;
925 uint32_t vertical_accuracy
;
929 uint32_t time
; // GPS msToW
932 uint8_t differential_status
;
934 uint32_t time_to_first_fix
;
935 uint32_t uptime
; // milliseconds
947 uint32_t position_accuracy_3d
;
948 int32_t ecef_x_velocity
;
949 int32_t ecef_y_velocity
;
950 int32_t ecef_z_velocity
;
951 uint32_t speed_accuracy
;
952 uint16_t position_DOP
;
959 uint32_t time
; // GPS msToW
966 uint32_t speed_accuracy
;
967 uint32_t heading_accuracy
;
971 uint8_t chn
; // Channel number, 255 for SVx not assigned to channel
972 uint8_t svid
; // Satellite ID
973 uint8_t flags
; // Bitmask
974 uint8_t quality
; // Bitfield
975 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
976 uint8_t elev
; // Elevation in integer degrees
977 int16_t azim
; // Azimuth in integer degrees
978 int32_t prRes
; // Pseudo range residual in centimetres
979 } ubx_nav_svinfo_channel
;
982 uint32_t time
; // GPS Millisecond time of week
983 uint8_t numCh
; // Number of channels
984 uint8_t globalFlags
; // Bitmask, Chip hardware generation 0:Antaris, 1:u-blox 5, 2:u-blox 6
985 uint16_t reserved2
; // Reserved
986 ubx_nav_svinfo_channel channel
[16]; // 16 satellites * 12 byte
1003 MSG_CFG_RATE
= 0x08,
1004 MSG_CFG_SET_RATE
= 0x01,
1005 MSG_CFG_NAV_SETTINGS
= 0x24
1006 } ubx_protocol_bytes
;
1010 FIX_DEAD_RECKONING
= 1,
1013 FIX_GPS_DEAD_RECKONING
= 4,
1018 NAV_STATUS_FIX_VALID
= 1,
1019 NAV_STATUS_TIME_WEEK_VALID
= 4,
1020 NAV_STATUS_TIME_SECOND_VALID
= 8
1021 } ubx_nav_status_bits
;
1023 // Packet checksum accumulators
1024 static uint8_t _ck_a
;
1025 static uint8_t _ck_b
;
1027 // State machine state
1028 static bool _skip_packet
;
1029 static uint8_t _step
;
1030 static uint8_t _msg_id
;
1031 static uint16_t _payload_length
;
1032 static uint16_t _payload_counter
;
1034 static bool next_fix
;
1035 static uint8_t _class
;
1037 // do we have new position information?
1038 static bool _new_position
;
1040 // do we have new speed information?
1041 static bool _new_speed
;
1043 // Example packet sizes from UBlox u-center from a Glonass capable GPS receiver.
1044 //15:17:55 R -> UBX NAV-STATUS, Size 24, 'Navigation Status'
1045 //15:17:55 R -> UBX NAV-POSLLH, Size 36, 'Geodetic Position'
1046 //15:17:55 R -> UBX NAV-VELNED, Size 44, 'Velocity in WGS 84'
1047 //15:17:55 R -> UBX NAV-CLOCK, Size 28, 'Clock Status'
1048 //15:17:55 R -> UBX NAV-AOPSTATUS, Size 24, 'AOP Status'
1049 //15:17:55 R -> UBX 03-09, Size 208, 'Unknown'
1050 //15:17:55 R -> UBX 03-10, Size 336, 'Unknown'
1051 //15:17:55 R -> UBX NAV-SOL, Size 60, 'Navigation Solution'
1052 //15:17:55 R -> UBX NAV, Size 100, 'Navigation'
1053 //15:17:55 R -> UBX NAV-SVINFO, Size 328, 'Satellite Status and Information'
1055 // from the UBlox6 document, the largest payout we receive i the NAV-SVINFO and the payload size
1056 // is calculated as 8 + 12*numCh. numCh in the case of a Glonass receiver is 28.
1057 #define UBLOX_PAYLOAD_SIZE 344
1062 ubx_nav_posllh posllh
;
1063 ubx_nav_status status
;
1064 ubx_nav_solution solution
;
1065 ubx_nav_velned velned
;
1066 ubx_nav_svinfo svinfo
;
1067 uint8_t bytes
[UBLOX_PAYLOAD_SIZE
];
1070 void _update_checksum(uint8_t *data
, uint8_t len
, uint8_t *ck_a
, uint8_t *ck_b
)
1080 static bool UBLOX_parse_gps(void)
1084 *gpsPacketLogChar
= LOG_IGNORED
;
1088 *gpsPacketLogChar
= LOG_UBLOX_POSLLH
;
1089 //i2c_dataset.time = _buffer.posllh.time;
1090 gpsSol
.llh
.lon
= _buffer
.posllh
.longitude
;
1091 gpsSol
.llh
.lat
= _buffer
.posllh
.latitude
;
1092 gpsSol
.llh
.altCm
= _buffer
.posllh
.altitudeMslMm
/ 10; //alt in cm
1094 ENABLE_STATE(GPS_FIX
);
1096 DISABLE_STATE(GPS_FIX
);
1098 _new_position
= true;
1101 *gpsPacketLogChar
= LOG_UBLOX_STATUS
;
1102 next_fix
= (_buffer
.status
.fix_status
& NAV_STATUS_FIX_VALID
) && (_buffer
.status
.fix_type
== FIX_3D
);
1104 DISABLE_STATE(GPS_FIX
);
1107 *gpsPacketLogChar
= LOG_UBLOX_SOL
;
1108 next_fix
= (_buffer
.solution
.fix_status
& NAV_STATUS_FIX_VALID
) && (_buffer
.solution
.fix_type
== FIX_3D
);
1110 DISABLE_STATE(GPS_FIX
);
1111 gpsSol
.numSat
= _buffer
.solution
.satellites
;
1112 gpsSol
.hdop
= _buffer
.solution
.position_DOP
;
1114 //set clock, when gps time is available
1115 if(!rtcHasTime() && (_buffer
.solution
.fix_status
& NAV_STATUS_TIME_SECOND_VALID
) && (_buffer
.solution
.fix_status
& NAV_STATUS_TIME_WEEK_VALID
)) {
1116 //calculate rtctime: week number * ms in a week + ms of week + fractions of second + offset to UNIX reference year - 18 leap seconds
1117 rtcTime_t temp_time
= (((int64_t) _buffer
.solution
.week
)*7*24*60*60*1000) + _buffer
.solution
.time
+ (_buffer
.solution
.time_nsec
/1000000) + 315964800000LL - 18000;
1123 *gpsPacketLogChar
= LOG_UBLOX_VELNED
;
1124 gpsSol
.speed3d
= _buffer
.velned
.speed_3d
; // cm/s
1125 gpsSol
.groundSpeed
= _buffer
.velned
.speed_2d
; // cm/s
1126 gpsSol
.groundCourse
= (uint16_t) (_buffer
.velned
.heading_2d
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
1130 *gpsPacketLogChar
= LOG_UBLOX_SVINFO
;
1131 GPS_numCh
= _buffer
.svinfo
.numCh
;
1134 for (i
= 0; i
< GPS_numCh
; i
++) {
1135 GPS_svinfo_chn
[i
]= _buffer
.svinfo
.channel
[i
].chn
;
1136 GPS_svinfo_svid
[i
]= _buffer
.svinfo
.channel
[i
].svid
;
1137 GPS_svinfo_quality
[i
]=_buffer
.svinfo
.channel
[i
].quality
;
1138 GPS_svinfo_cno
[i
]= _buffer
.svinfo
.channel
[i
].cno
;
1140 GPS_svInfoReceivedCount
++;
1146 // we only return true when we get new position and speed data
1147 // this ensures we don't use stale data
1148 if (_new_position
&& _new_speed
) {
1149 _new_speed
= _new_position
= false;
1155 static bool gpsNewFrameUBLOX(uint8_t data
)
1157 bool parsed
= false;
1160 case 0: // Sync char 1 (0xB5)
1161 if (PREAMBLE1
== data
) {
1162 _skip_packet
= false;
1166 case 1: // Sync char 2 (0x62)
1167 if (PREAMBLE2
!= data
) {
1176 _ck_b
= _ck_a
= data
; // reset the checksum accumulators
1180 _ck_b
+= (_ck_a
+= data
); // checksum byte
1183 case 4: // Payload length (part 1)
1185 _ck_b
+= (_ck_a
+= data
); // checksum byte
1186 _payload_length
= data
; // payload length low byte
1188 case 5: // Payload length (part 2)
1190 _ck_b
+= (_ck_a
+= data
); // checksum byte
1191 _payload_length
+= (uint16_t)(data
<< 8);
1192 if (_payload_length
> UBLOX_PAYLOAD_SIZE
) {
1193 _skip_packet
= true;
1195 _payload_counter
= 0; // prepare to receive payload
1196 if (_payload_length
== 0) {
1201 _ck_b
+= (_ck_a
+= data
); // checksum byte
1202 if (_payload_counter
< UBLOX_PAYLOAD_SIZE
) {
1203 _buffer
.bytes
[_payload_counter
] = data
;
1205 if (++_payload_counter
>= _payload_length
) {
1211 if (_ck_a
!= data
) {
1212 _skip_packet
= true; // bad checksum
1221 if (_ck_b
!= data
) {
1222 *gpsPacketLogChar
= LOG_ERROR
;
1224 break; // bad checksum
1230 *gpsPacketLogChar
= LOG_SKIPPED
;
1234 if (UBLOX_parse_gps()) {
1240 #endif // USE_GPS_UBLOX
1242 static void gpsHandlePassthrough(uint8_t data
)
1245 #ifdef USE_DASHBOARD
1246 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
1247 dashboardUpdate(micros());
1253 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
1255 waitForSerialPortToFinishTransmitting(gpsPort
);
1256 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
1258 if (!(gpsPort
->mode
& MODE_TX
))
1259 serialSetMode(gpsPort
, gpsPort
->mode
| MODE_TX
);
1261 #ifdef USE_DASHBOARD
1262 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
1263 dashboardShowFixedPage(PAGE_GPS
);
1267 serialPassthrough(gpsPort
, gpsPassthroughPort
, &gpsHandlePassthrough
, NULL
);
1270 float GPS_scaleLonDown
= 1.0f
; // this is used to offset the shrinking longitude as we go towards the poles
1272 void GPS_calc_longitude_scaling(int32_t lat
)
1274 float rads
= (fabsf((float)lat
) / 10000000.0f
) * 0.0174532925f
;
1275 GPS_scaleLonDown
= cos_approx(rads
);
1278 ////////////////////////////////////////////////////////////////////////////////////
1279 // Calculate the distance flown and vertical speed from gps position data
1281 static void GPS_calculateDistanceFlownVerticalSpeed(bool initialize
)
1283 static int32_t lastCoord
[2] = { 0, 0 };
1284 static int32_t lastAlt
;
1285 static int32_t lastMillis
;
1287 int currentMillis
= millis();
1290 GPS_distanceFlownInCm
= 0;
1291 GPS_verticalSpeedInCmS
= 0;
1293 if (STATE(GPS_FIX_HOME
) && ARMING_FLAG(ARMED
)) {
1294 uint16_t speed
= gpsConfig()->gps_use_3d_speed
? gpsSol
.speed3d
: gpsSol
.groundSpeed
;
1295 // Only add up movement when speed is faster than minimum threshold
1296 if (speed
> GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S
) {
1299 GPS_distance_cm_bearing(&gpsSol
.llh
.lat
, &gpsSol
.llh
.lon
, &lastCoord
[LAT
], &lastCoord
[LON
], &dist
, &dir
);
1300 if (gpsConfig()->gps_use_3d_speed
) {
1301 dist
= sqrtf(powf(gpsSol
.llh
.altCm
- lastAlt
, 2.0f
) + powf(dist
, 2.0f
));
1303 GPS_distanceFlownInCm
+= dist
;
1306 GPS_verticalSpeedInCmS
= (gpsSol
.llh
.altCm
- lastAlt
) * 1000 / (currentMillis
- lastMillis
);
1307 GPS_verticalSpeedInCmS
= constrain(GPS_verticalSpeedInCmS
, -1500, 1500);
1309 lastCoord
[LON
] = gpsSol
.llh
.lon
;
1310 lastCoord
[LAT
] = gpsSol
.llh
.lat
;
1311 lastAlt
= gpsSol
.llh
.altCm
;
1312 lastMillis
= currentMillis
;
1315 void GPS_reset_home_position(void)
1317 if (!STATE(GPS_FIX_HOME
) || !gpsConfig()->gps_set_home_point_once
) {
1318 if (STATE(GPS_FIX
) && gpsSol
.numSat
>= 5) {
1319 GPS_home
[LAT
] = gpsSol
.llh
.lat
;
1320 GPS_home
[LON
] = gpsSol
.llh
.lon
;
1321 GPS_calc_longitude_scaling(gpsSol
.llh
.lat
); // need an initial value for distance and bearing calc
1322 // Set ground altitude
1323 ENABLE_STATE(GPS_FIX_HOME
);
1326 GPS_calculateDistanceFlownVerticalSpeed(true); //Initialize
1329 ////////////////////////////////////////////////////////////////////////////////////
1330 #define DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR_IN_HUNDREDS_OF_KILOMETERS 1.113195f
1331 #define TAN_89_99_DEGREES 5729.57795f
1332 // Get distance between two points in cm
1333 // Get bearing from pos1 to pos2, returns an 1deg = 100 precision
1334 void GPS_distance_cm_bearing(int32_t *currentLat1
, int32_t *currentLon1
, int32_t *destinationLat2
, int32_t *destinationLon2
, uint32_t *dist
, int32_t *bearing
)
1336 float dLat
= *destinationLat2
- *currentLat1
; // difference of latitude in 1/10 000 000 degrees
1337 float dLon
= (float)(*destinationLon2
- *currentLon1
) * GPS_scaleLonDown
;
1338 *dist
= sqrtf(sq(dLat
) + sq(dLon
)) * DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR_IN_HUNDREDS_OF_KILOMETERS
;
1340 *bearing
= 9000.0f
+ atan2_approx(-dLat
, dLon
) * TAN_89_99_DEGREES
; // Convert the output radians to 100xdeg
1345 void GPS_calculateDistanceAndDirectionToHome(void)
1347 if (STATE(GPS_FIX_HOME
)) { // If we don't have home set, do not display anything
1350 GPS_distance_cm_bearing(&gpsSol
.llh
.lat
, &gpsSol
.llh
.lon
, &GPS_home
[LAT
], &GPS_home
[LON
], &dist
, &dir
);
1351 GPS_distanceToHome
= dist
/ 100;
1352 GPS_directionToHome
= dir
/ 100;
1354 GPS_distanceToHome
= 0;
1355 GPS_directionToHome
= 0;
1359 void onGpsNewData(void)
1361 if (!(STATE(GPS_FIX
) && gpsSol
.numSat
>= 5)) {
1366 // Calculate time delta for navigation loop, range 0-1.0f, in seconds
1368 // Time for calculating x,y speed and navigation pids
1369 static uint32_t nav_loopTimer
;
1370 dTnav
= (float)(millis() - nav_loopTimer
) / 1000.0f
;
1371 nav_loopTimer
= millis();
1372 // prevent runup from bad GPS
1373 dTnav
= MIN(dTnav
, 1.0f
);
1375 GPS_calculateDistanceAndDirectionToHome();
1376 if (ARMING_FLAG(ARMED
)) {
1377 GPS_calculateDistanceFlownVerticalSpeed(false);
1380 #ifdef USE_GPS_RESCUE