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/beeper.h"
47 #include "io/dashboard.h"
49 #include "io/serial.h"
51 #include "config/config.h"
52 #include "fc/runtime_config.h"
54 #include "flight/imu.h"
55 #include "flight/pid.h"
56 #include "flight/gps_rescue.h"
58 #include "scheduler/scheduler.h"
60 #include "sensors/sensors.h"
63 #define LOG_IGNORED '!'
64 #define LOG_SKIPPED '>'
65 #define LOG_NMEA_GGA 'g'
66 #define LOG_NMEA_RMC 'r'
67 #define LOG_UBLOX_SOL 'O'
68 #define LOG_UBLOX_STATUS 'S'
69 #define LOG_UBLOX_SVINFO 'I'
70 #define LOG_UBLOX_POSLLH 'P'
71 #define LOG_UBLOX_VELNED 'V'
73 #define DEBUG_SERIAL_BAUD 0 // set to 1 to debug serial port baud config (/100)
74 #define DEBUG_UBLOX_INIT 0 // set to 1 to debug ublox initialization
75 #define DEBUG_UBLOX_FRAMES 0 // set to 1 to debug ublox received frames
77 char gpsPacketLog
[GPS_PACKET_LOG_ENTRY_COUNT
];
78 static char *gpsPacketLogChar
= gpsPacketLog
;
79 // **********************
81 // **********************
83 uint16_t GPS_distanceToHome
; // distance to home point in meters
84 uint32_t GPS_distanceToHomeCm
;
85 int16_t GPS_directionToHome
; // direction to home or hol point in degrees * 10
86 uint32_t GPS_distanceFlownInCm
; // distance flown since armed in centimeters
87 int16_t GPS_verticalSpeedInCmS
; // vertical speed in cm/s
88 int16_t nav_takeoff_bearing
;
90 #define GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S 15 // 0.54 km/h 0.335 mph
92 gpsSolutionData_t gpsSol
;
93 uint32_t GPS_packetCount
= 0;
94 uint32_t GPS_svInfoReceivedCount
= 0; // SV = Space Vehicle, counter increments each time SV info is received.
95 uint8_t GPS_update
= 0; // toogle to distinct a GPS position update (directly or via MSP)
97 uint8_t GPS_numCh
; // Details on numCh/svinfo in gps.h
98 uint8_t GPS_svinfo_chn
[GPS_SV_MAXSATS_M8N
];
99 uint8_t GPS_svinfo_svid
[GPS_SV_MAXSATS_M8N
];
100 uint8_t GPS_svinfo_quality
[GPS_SV_MAXSATS_M8N
];
101 uint8_t GPS_svinfo_cno
[GPS_SV_MAXSATS_M8N
];
103 // GPS timeout for wrong baud rate/disconnection/etc in milliseconds (default 2.5second)
104 #define GPS_TIMEOUT (2500)
105 // How many entries in gpsInitData array below
106 #define GPS_INIT_ENTRIES (GPS_BAUDRATE_MAX + 1)
107 #define GPS_BAUDRATE_CHANGE_DELAY (200)
108 // Timeout for waiting ACK/NAK in GPS task cycles (0.25s at 100Hz)
109 #define UBLOX_ACK_TIMEOUT_MAX_COUNT (25)
111 static serialPort_t
*gpsPort
;
113 typedef struct gpsInitData_s
{
115 uint8_t baudrateIndex
; // see baudRate_e
120 // NMEA will cycle through these until valid data is received
121 static const gpsInitData_t gpsInitData
[] = {
122 { GPS_BAUDRATE_115200
, BAUD_115200
, "$PUBX,41,1,0003,0001,115200,0*1E\r\n", "$PMTK251,115200*1F\r\n" },
123 { GPS_BAUDRATE_57600
, BAUD_57600
, "$PUBX,41,1,0003,0001,57600,0*2D\r\n", "$PMTK251,57600*2C\r\n" },
124 { GPS_BAUDRATE_38400
, BAUD_38400
, "$PUBX,41,1,0003,0001,38400,0*26\r\n", "$PMTK251,38400*27\r\n" },
125 { GPS_BAUDRATE_19200
, BAUD_19200
, "$PUBX,41,1,0003,0001,19200,0*23\r\n", "$PMTK251,19200*22\r\n" },
126 // 9600 is not enough for 5Hz updates - leave for compatibility to dumb NMEA that only runs at this speed
127 { GPS_BAUDRATE_9600
, BAUD_9600
, "$PUBX,41,1,0003,0001,9600,0*16\r\n", "" }
130 #define GPS_INIT_DATA_ENTRY_COUNT (sizeof(gpsInitData) / sizeof(gpsInitData[0]))
132 #define DEFAULT_BAUD_RATE_INDEX 0
153 MSG_CFG_SET_RATE
= 0x01,
155 MSG_CFG_NAV_SETTINGS
= 0x24,
157 } ubx_protocol_bytes
;
159 #define UBLOX_MODE_ENABLED 0x1
160 #define UBLOX_MODE_TEST 0x2
162 #define UBLOX_USAGE_RANGE 0x1
163 #define UBLOX_USAGE_DIFFCORR 0x2
164 #define UBLOX_USAGE_INTEGRITY 0x4
166 #define UBLOX_GNSS_ENABLE 0x1
167 #define UBLOX_GNSS_DEFAULT_SIGCFGMASK 0x10000
169 #define UBLOX_DYNMODE_PEDESTRIAN 3
170 #define UBLOX_DYNMODE_AIRBORNE_1G 6
171 #define UBLOX_DYNMODE_AIRBORNE_4G 8
218 uint8_t numConfigBlocks
;
219 ubx_configblock configblocks
[7];
227 uint32_t fixedAltVar
;
234 uint8_t staticHoldThresh
;
235 uint8_t dgnssTimeout
;
236 uint8_t cnoThreshNumSVs
;
238 uint8_t reserved0
[2];
239 uint16_t staticHoldMaxDist
;
241 uint8_t reserved1
[5];
245 ubx_poll_msg poll_msg
;
247 ubx_cfg_rate cfg_rate
;
248 ubx_cfg_nav5 cfg_nav5
;
249 ubx_cfg_sbas cfg_sbas
;
250 ubx_cfg_gnss cfg_gnss
;
256 } __attribute__((packed
)) ubx_message
;
258 #endif // USE_GPS_UBLOX
262 GPS_STATE_INITIALIZING
,
263 GPS_STATE_INITIALIZED
,
264 GPS_STATE_CHANGE_BAUD
,
266 GPS_STATE_RECEIVING_DATA
,
267 GPS_STATE_LOST_COMMUNICATION
,
271 // Max time to wait for received data
272 #define GPS_MAX_WAIT_DATA_RX 30
276 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 1);
278 PG_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
,
279 .provider
= GPS_UBLOX
,
280 .sbasMode
= SBAS_NONE
,
281 .autoConfig
= GPS_AUTOCONFIG_ON
,
282 .autoBaud
= GPS_AUTOBAUD_OFF
,
283 .gps_ublox_use_galileo
= false,
284 .gps_ublox_mode
= UBLOX_AIRBORNE
,
285 .gps_set_home_point_once
= false,
286 .gps_use_3d_speed
= false,
287 .sbas_integrity
= false,
288 .gpsRequiredSats
= GPS_REQUIRED_SAT_COUNT
,
289 .gpsMinimumSats
= GPS_MINIMUM_SAT_COUNT
292 static void shiftPacketLog(void)
296 for (i
= ARRAYLEN(gpsPacketLog
) - 1; i
> 0 ; i
--) {
297 gpsPacketLog
[i
] = gpsPacketLog
[i
-1];
301 static bool isConfiguratorConnected() {
302 return (getArmingDisableFlags() & ARMING_DISABLED_MSP
);
305 static void gpsNewData(uint16_t c
);
307 static bool gpsNewFrameNMEA(char c
);
310 static bool gpsNewFrameUBLOX(uint8_t data
);
313 static void gpsSetState(gpsState_e state
)
315 gpsData
.lastMessage
= millis();
316 sensorsClear(SENSOR_GPS
);
318 gpsData
.state
= state
;
319 gpsData
.state_position
= 0;
320 gpsData
.state_ts
= millis();
321 gpsData
.ackState
= UBLOX_ACK_IDLE
;
326 gpsData
.baudrateIndex
= 0;
328 gpsData
.timeouts
= 0;
330 memset(gpsPacketLog
, 0x00, sizeof(gpsPacketLog
));
332 // init gpsData structure. if we're not actually enabled, don't bother doing anything else
333 gpsSetState(GPS_STATE_UNKNOWN
);
335 gpsData
.lastMessage
= millis();
337 if (gpsConfig()->provider
== GPS_MSP
) { // no serial ports used when GPS_MSP is configured
338 gpsSetState(GPS_STATE_INITIALIZED
);
342 const serialPortConfig_t
*gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
343 if (!gpsPortConfig
) {
347 while (gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
!= gpsPortConfig
->gps_baudrateIndex
) {
348 gpsData
.baudrateIndex
++;
349 if (gpsData
.baudrateIndex
>= GPS_INIT_DATA_ENTRY_COUNT
) {
350 gpsData
.baudrateIndex
= DEFAULT_BAUD_RATE_INDEX
;
355 portMode_e mode
= MODE_RXTX
;
356 #if defined(GPS_NMEA_TX_ONLY)
357 if (gpsConfig()->provider
== GPS_NMEA
) {
362 // no callback - buffer will be consumed in gpsUpdate()
363 gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, NULL
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
], mode
, SERIAL_NOT_INVERTED
);
368 // signal GPS "thread" to initialize when it gets to it
369 gpsSetState(GPS_STATE_INITIALIZING
);
373 void gpsInitNmea(void)
375 #if !defined(GPS_NMEA_TX_ONLY)
378 switch (gpsData
.state
) {
379 case GPS_STATE_INITIALIZING
:
380 #if !defined(GPS_NMEA_TX_ONLY)
382 if (now
- gpsData
.state_ts
< 1000) {
385 gpsData
.state_ts
= now
;
386 if (gpsData
.state_position
< 1) {
387 serialSetBaudRate(gpsPort
, 4800);
388 gpsData
.state_position
++;
389 } else if (gpsData
.state_position
< 2) {
390 // print our FIXED init string for the baudrate we want to be at
391 serialPrint(gpsPort
, "$PSRF100,1,115200,8,1,0*05\r\n");
392 gpsData
.state_position
++;
394 // we're now (hopefully) at the correct rate, next state will switch to it
395 gpsSetState(GPS_STATE_CHANGE_BAUD
);
399 case GPS_STATE_CHANGE_BAUD
:
400 #if !defined(GPS_NMEA_TX_ONLY)
402 if (now
- gpsData
.state_ts
< 1000) {
405 gpsData
.state_ts
= now
;
406 if (gpsData
.state_position
< 1) {
407 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
408 gpsData
.state_position
++;
409 } else if (gpsData
.state_position
< 2) {
410 serialPrint(gpsPort
, "$PSRF103,00,6,00,0*23\r\n");
411 gpsData
.state_position
++;
415 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
418 gpsSetState(GPS_STATE_RECEIVING_DATA
);
422 #endif // USE_GPS_NMEA
425 static void ubloxSendByteUpdateChecksum(const uint8_t data
, uint8_t *checksumA
, uint8_t *checksumB
)
428 *checksumB
+= *checksumA
;
429 serialWrite(gpsPort
, data
);
432 static void ubloxSendDataUpdateChecksum(const uint8_t *data
, uint8_t len
, uint8_t *checksumA
, uint8_t *checksumB
)
435 ubloxSendByteUpdateChecksum(*data
, checksumA
, checksumB
);
440 static void ubloxSendMessage(const uint8_t *data
, uint8_t len
)
442 uint8_t checksumA
= 0, checksumB
= 0;
443 serialWrite(gpsPort
, data
[0]);
444 serialWrite(gpsPort
, data
[1]);
445 ubloxSendDataUpdateChecksum(&data
[2], len
- 2, &checksumA
, &checksumB
);
446 serialWrite(gpsPort
, checksumA
);
447 serialWrite(gpsPort
, checksumB
);
449 // Save state for ACK waiting
450 gpsData
.ackWaitingMsgId
= data
[3]; //save message id for ACK
451 gpsData
.ackTimeoutCounter
= 0;
452 gpsData
.ackState
= UBLOX_ACK_WAITING
;
455 static void ubloxSendConfigMessage(ubx_message
*message
, uint8_t msg_id
, uint8_t length
)
457 message
->header
.preamble1
= PREAMBLE1
;
458 message
->header
.preamble2
= PREAMBLE2
;
459 message
->header
.msg_class
= CLASS_CFG
;
460 message
->header
.msg_id
= msg_id
;
461 message
->header
.length
= length
;
462 ubloxSendMessage((const uint8_t *) message
, length
+ 6);
465 static void ubloxSendPollMessage(uint8_t msg_id
)
467 ubx_message tx_buffer
;
468 tx_buffer
.header
.preamble1
= PREAMBLE1
;
469 tx_buffer
.header
.preamble2
= PREAMBLE2
;
470 tx_buffer
.header
.msg_class
= CLASS_CFG
;
471 tx_buffer
.header
.msg_id
= msg_id
;
472 tx_buffer
.header
.length
= 0;
473 ubloxSendMessage((const uint8_t *) &tx_buffer
, 6);
476 static void ubloxSendNAV5Message(bool airborne
) {
477 ubx_message tx_buffer
;
478 tx_buffer
.payload
.cfg_nav5
.mask
= 0xFFFF;
480 #if defined(GPS_UBLOX_MODE_AIRBORNE_1G)
481 tx_buffer
.payload
.cfg_nav5
.dynModel
= UBLOX_DYNMODE_AIRBORNE_1G
;
483 tx_buffer
.payload
.cfg_nav5
.dynModel
= UBLOX_DYNMODE_AIRBORNE_4G
;
486 tx_buffer
.payload
.cfg_nav5
.dynModel
= UBLOX_DYNMODE_PEDESTRIAN
;
488 tx_buffer
.payload
.cfg_nav5
.fixMode
= 3;
489 tx_buffer
.payload
.cfg_nav5
.fixedAlt
= 0;
490 tx_buffer
.payload
.cfg_nav5
.fixedAltVar
= 10000;
491 tx_buffer
.payload
.cfg_nav5
.minElev
= 5;
492 tx_buffer
.payload
.cfg_nav5
.drLimit
= 0;
493 tx_buffer
.payload
.cfg_nav5
.pDOP
= 250;
494 tx_buffer
.payload
.cfg_nav5
.tDOP
= 250;
495 tx_buffer
.payload
.cfg_nav5
.pAcc
= 100;
496 tx_buffer
.payload
.cfg_nav5
.tAcc
= 300;
497 tx_buffer
.payload
.cfg_nav5
.staticHoldThresh
= 0;
498 tx_buffer
.payload
.cfg_nav5
.dgnssTimeout
= 60;
499 tx_buffer
.payload
.cfg_nav5
.cnoThreshNumSVs
= 0;
500 tx_buffer
.payload
.cfg_nav5
.cnoThresh
= 0;
501 tx_buffer
.payload
.cfg_nav5
.reserved0
[0] = 0;
502 tx_buffer
.payload
.cfg_nav5
.reserved0
[1] = 0;
503 tx_buffer
.payload
.cfg_nav5
.staticHoldMaxDist
= 200;
504 tx_buffer
.payload
.cfg_nav5
.utcStandard
= 0;
505 tx_buffer
.payload
.cfg_nav5
.reserved1
[0] = 0;
506 tx_buffer
.payload
.cfg_nav5
.reserved1
[1] = 0;
507 tx_buffer
.payload
.cfg_nav5
.reserved1
[2] = 0;
508 tx_buffer
.payload
.cfg_nav5
.reserved1
[3] = 0;
509 tx_buffer
.payload
.cfg_nav5
.reserved1
[4] = 0;
511 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_NAV_SETTINGS
, sizeof(ubx_cfg_nav5
));
514 static void ubloxSetMessageRate(uint8_t messageClass
, uint8_t messageID
, uint8_t rate
) {
515 ubx_message tx_buffer
;
516 tx_buffer
.payload
.cfg_msg
.msgClass
= messageClass
;
517 tx_buffer
.payload
.cfg_msg
.msgID
= messageID
;
518 tx_buffer
.payload
.cfg_msg
.rate
= rate
;
519 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_MSG
, sizeof(ubx_cfg_msg
));
522 static void ubloxSetNavRate(uint16_t measRate
, uint16_t navRate
, uint16_t timeRef
) {
523 ubx_message tx_buffer
;
524 tx_buffer
.payload
.cfg_rate
.measRate
= measRate
;
525 tx_buffer
.payload
.cfg_rate
.navRate
= navRate
;
526 tx_buffer
.payload
.cfg_rate
.timeRef
= timeRef
;
527 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_RATE
, sizeof(ubx_cfg_rate
));
530 static void ubloxSetSbas() {
531 ubx_message tx_buffer
;
533 //NOTE: default ublox config for sbas mode is: UBLOX_MODE_ENABLED, test is disabled
534 tx_buffer
.payload
.cfg_sbas
.mode
= UBLOX_MODE_TEST
;
535 if (gpsConfig()->sbasMode
!= SBAS_NONE
) {
536 tx_buffer
.payload
.cfg_sbas
.mode
|= UBLOX_MODE_ENABLED
;
539 //NOTE: default ublox config for sbas mode is: UBLOX_USAGE_RANGE |Â UBLOX_USAGE_DIFFCORR, integrity is disabled
540 tx_buffer
.payload
.cfg_sbas
.usage
= UBLOX_USAGE_RANGE
| UBLOX_USAGE_DIFFCORR
;
541 if (gpsConfig()->sbas_integrity
) {
542 tx_buffer
.payload
.cfg_sbas
.usage
|= UBLOX_USAGE_INTEGRITY
;
545 tx_buffer
.payload
.cfg_sbas
.maxSBAS
= 3;
546 tx_buffer
.payload
.cfg_sbas
.scanmode2
= 0;
547 switch (gpsConfig()->sbasMode
) {
549 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0;
552 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00010048; //PRN123, PRN126, PRN136
555 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x0004A800; //PRN131, PRN133, PRN135, PRN138
558 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00020200; //PRN129, PRN137
561 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00001180; //PRN127, PRN128, PRN132
564 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0;
567 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_SBAS
, sizeof(ubx_cfg_sbas
));
570 void gpsInitUblox(void)
573 // UBX will run at the serial port's baudrate, it shouldn't be "autodetected". So here we force it to that rate
575 // Wait until GPS transmit buffer is empty
576 if (!isSerialTransmitBufferEmpty(gpsPort
))
579 switch (gpsData
.state
) {
580 case GPS_STATE_INITIALIZING
:
582 if (now
- gpsData
.state_ts
< GPS_BAUDRATE_CHANGE_DELAY
)
585 if (gpsData
.state_position
< GPS_INIT_ENTRIES
) {
586 // try different speed to INIT
587 baudRate_e newBaudRateIndex
= gpsInitData
[gpsData
.state_position
].baudrateIndex
;
589 gpsData
.state_ts
= now
;
591 if (lookupBaudRateIndex(serialGetBaudRate(gpsPort
)) != newBaudRateIndex
) {
592 // change the rate if needed and wait a little
593 serialSetBaudRate(gpsPort
, baudRates
[newBaudRateIndex
]);
594 #if DEBUG_SERIAL_BAUD
595 debug
[0] = baudRates
[newBaudRateIndex
] / 100;
600 // print our FIXED init string for the baudrate we want to be at
601 serialPrint(gpsPort
, gpsInitData
[gpsData
.baudrateIndex
].ubx
);
603 gpsData
.state_position
++;
605 // we're now (hopefully) at the correct rate, next state will switch to it
606 gpsSetState(GPS_STATE_CHANGE_BAUD
);
610 case GPS_STATE_CHANGE_BAUD
:
611 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
612 #if DEBUG_SERIAL_BAUD
613 debug
[0] = baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
] / 100;
615 gpsSetState(GPS_STATE_CONFIGURE
);
618 case GPS_STATE_CONFIGURE
:
619 // Either use specific config file for GPS or let dynamically upload config
620 if (gpsConfig()->autoConfig
== GPS_AUTOCONFIG_OFF
) {
621 gpsSetState(GPS_STATE_RECEIVING_DATA
);
625 if (gpsData
.ackState
== UBLOX_ACK_IDLE
) {
626 switch (gpsData
.state_position
) {
628 gpsData
.ubloxUsePVT
= true;
629 gpsData
.ubloxUseSAT
= true;
630 ubloxSendNAV5Message(gpsConfig()->gps_ublox_mode
== UBLOX_AIRBORNE
);
632 case 1: //Disable NMEA Messages
633 ubloxSetMessageRate(0xF0, 0x05, 0); // VGS: Course over ground and Ground speed
636 ubloxSetMessageRate(0xF0, 0x03, 0); // GSV: GNSS Satellites in View
639 ubloxSetMessageRate(0xF0, 0x01, 0); // GLL: Latitude and longitude, with time of position fix and status
642 ubloxSetMessageRate(0xF0, 0x00, 0); // GGA: Global positioning system fix data
645 ubloxSetMessageRate(0xF0, 0x02, 0); // GSA: GNSS DOP and Active Satellites
648 ubloxSetMessageRate(0xF0, 0x04, 0); // RMC: Recommended Minimum data
650 case 7: //Enable UBLOX Messages
651 if (gpsData
.ubloxUsePVT
) {
652 ubloxSetMessageRate(CLASS_NAV
, MSG_PVT
, 1); // set PVT MSG rate
654 ubloxSetMessageRate(CLASS_NAV
, MSG_SOL
, 1); // set SOL MSG rate
658 if (gpsData
.ubloxUsePVT
) {
659 gpsData
.state_position
++;
661 ubloxSetMessageRate(CLASS_NAV
, MSG_POSLLH
, 1); // set POSLLH MSG rate
665 if (gpsData
.ubloxUsePVT
) {
666 gpsData
.state_position
++;
668 ubloxSetMessageRate(CLASS_NAV
, MSG_STATUS
, 1); // set STATUS MSG rate
672 if (gpsData
.ubloxUsePVT
) {
673 gpsData
.state_position
++;
675 ubloxSetMessageRate(CLASS_NAV
, MSG_VELNED
, 1); // set VELNED MSG rate
679 if (gpsData
.ubloxUseSAT
) {
680 ubloxSetMessageRate(CLASS_NAV
, MSG_SAT
, 5); // set SAT MSG rate (every 5 cycles)
682 ubloxSetMessageRate(CLASS_NAV
, MSG_SVINFO
, 5); // set SVINFO MSG rate (every 5 cycles)
686 ubloxSetNavRate(0x64, 1, 1); // set rate to 10Hz (measurement period: 100ms, navigation rate: 1 cycle) (for 5Hz use 0xC8)
692 if ((gpsConfig()->sbasMode
== SBAS_NONE
) || (gpsConfig()->gps_ublox_use_galileo
)) {
693 ubloxSendPollMessage(MSG_CFG_GNSS
);
695 gpsSetState(GPS_STATE_RECEIVING_DATA
);
703 switch (gpsData
.ackState
) {
706 case UBLOX_ACK_WAITING
:
707 if ((++gpsData
.ackTimeoutCounter
) == UBLOX_ACK_TIMEOUT_MAX_COUNT
) {
708 gpsSetState(GPS_STATE_LOST_COMMUNICATION
);
711 case UBLOX_ACK_GOT_ACK
:
712 if (gpsData
.state_position
== 14) {
713 // ublox should be initialised, try receiving
714 gpsSetState(GPS_STATE_RECEIVING_DATA
);
716 gpsData
.state_position
++;
717 gpsData
.ackState
= UBLOX_ACK_IDLE
;
720 case UBLOX_ACK_GOT_NACK
:
721 if (gpsData
.state_position
== 7) { // If we were asking for NAV-PVT...
722 gpsData
.ubloxUsePVT
= false; // ...retry asking for NAV-SOL
723 gpsData
.ackState
= UBLOX_ACK_IDLE
;
725 if (gpsData
.state_position
== 11) { // If we were asking for NAV-SAT...
726 gpsData
.ubloxUseSAT
= false; // ...retry asking for NAV-SVINFO
727 gpsData
.ackState
= UBLOX_ACK_IDLE
;
729 gpsSetState(GPS_STATE_CONFIGURE
);
738 #endif // USE_GPS_UBLOX
740 void gpsInitHardware(void)
742 switch (gpsConfig()->provider
) {
759 static void updateGpsIndicator(timeUs_t currentTimeUs
)
761 static uint32_t GPSLEDTime
;
762 if ((int32_t)(currentTimeUs
- GPSLEDTime
) >= 0 && STATE(GPS_FIX
) && (gpsSol
.numSat
>= gpsConfig()->gpsRequiredSats
)) {
763 GPSLEDTime
= currentTimeUs
+ 150000;
768 void gpsUpdate(timeUs_t currentTimeUs
)
770 static gpsState_e gpsStateDurationUs
[GPS_STATE_COUNT
];
771 timeUs_t executeTimeUs
;
772 gpsState_e gpsCurrentState
= gpsData
.state
;
774 // read out available GPS bytes
776 while (serialRxBytesWaiting(gpsPort
)) {
777 if (cmpTimeUs(micros(), currentTimeUs
) > GPS_MAX_WAIT_DATA_RX
) {
778 // Wait 1ms and come back
779 rescheduleTask(TASK_SELF
, TASK_PERIOD_HZ(TASK_GPS_RATE_FAST
));
782 gpsNewData(serialRead(gpsPort
));
784 // Restore default task rate
785 rescheduleTask(TASK_SELF
, TASK_PERIOD_HZ(TASK_GPS_RATE
));
786 } else if (GPS_update
& GPS_MSP_UPDATE
) { // GPS data received via MSP
787 gpsSetState(GPS_STATE_RECEIVING_DATA
);
789 GPS_update
&= ~GPS_MSP_UPDATE
;
793 debug
[0] = gpsData
.state
;
794 debug
[1] = gpsData
.state_position
;
795 debug
[2] = gpsData
.ackState
;
798 switch (gpsData
.state
) {
799 case GPS_STATE_UNKNOWN
:
800 case GPS_STATE_INITIALIZED
:
803 case GPS_STATE_INITIALIZING
:
804 case GPS_STATE_CHANGE_BAUD
:
805 case GPS_STATE_CONFIGURE
:
809 case GPS_STATE_LOST_COMMUNICATION
:
811 if (gpsConfig()->autoBaud
) {
813 gpsData
.baudrateIndex
++;
814 gpsData
.baudrateIndex
%= GPS_INIT_ENTRIES
;
817 DISABLE_STATE(GPS_FIX
);
818 gpsSetState(GPS_STATE_INITIALIZING
);
821 case GPS_STATE_RECEIVING_DATA
:
822 // check for no data/gps timeout/cable disconnection etc
823 if (millis() - gpsData
.lastMessage
> GPS_TIMEOUT
) {
824 gpsSetState(GPS_STATE_LOST_COMMUNICATION
);
827 if (gpsConfig()->autoConfig
== GPS_AUTOCONFIG_ON
) { // Only if autoconfig is enabled
828 switch (gpsData
.state_position
) {
830 if (!isConfiguratorConnected()) {
831 if (gpsData
.ubloxUseSAT
) {
832 ubloxSetMessageRate(CLASS_NAV
, MSG_SAT
, 0); // disable SAT MSG
834 ubloxSetMessageRate(CLASS_NAV
, MSG_SVINFO
, 0); // disable SVINFO MSG
836 gpsData
.state_position
= 1;
840 if (STATE(GPS_FIX
) && (gpsConfig()->gps_ublox_mode
== UBLOX_DYNAMIC
)) {
841 ubloxSendNAV5Message(true);
842 gpsData
.state_position
= 2;
844 if (isConfiguratorConnected()) {
845 gpsData
.state_position
= 2;
849 if (isConfiguratorConnected()) {
850 if (gpsData
.ubloxUseSAT
) {
851 ubloxSetMessageRate(CLASS_NAV
, MSG_SAT
, 5); // set SAT MSG rate (every 5 cycles)
853 ubloxSetMessageRate(CLASS_NAV
, MSG_SVINFO
, 5); // set SVINFO MSG rate (every 5 cycles)
855 gpsData
.state_position
= 0;
860 #endif //USE_GPS_UBLOX
865 if (sensors(SENSOR_GPS
)) {
866 updateGpsIndicator(currentTimeUs
);
868 if (!ARMING_FLAG(ARMED
) && !gpsConfig()->gps_set_home_point_once
) {
869 DISABLE_STATE(GPS_FIX_HOME
);
872 #if defined(USE_GPS_RESCUE)
873 if (gpsRescueIsConfigured()) {
874 updateGPSRescueState();
878 static bool hasFix
= false;
879 if (STATE(GPS_FIX_HOME
)) {
880 if (gpsIsHealthy() && gpsSol
.numSat
>= gpsConfig()->gpsRequiredSats
&& !hasFix
) {
881 // ready beep sequence on fix or requirements for gps rescue met.
882 beeper(BEEPER_READY_BEEP
);
889 executeTimeUs
= micros() - currentTimeUs
;
890 if (executeTimeUs
> gpsStateDurationUs
[gpsCurrentState
]) {
891 gpsStateDurationUs
[gpsCurrentState
] = executeTimeUs
;
893 schedulerSetNextStateTime(gpsStateDurationUs
[gpsData
.state
]);
896 static void gpsNewData(uint16_t c
)
898 if (!gpsNewFrame(c
)) {
902 if (gpsData
.state
== GPS_STATE_RECEIVING_DATA
) {
903 // new data received and parsed, we're in business
904 gpsData
.lastLastMessage
= gpsData
.lastMessage
;
905 gpsData
.lastMessage
= millis();
906 sensorsSet(SENSOR_GPS
);
909 GPS_update
^= GPS_DIRECT_TICK
;
912 debug
[3] = GPS_update
;
918 bool gpsNewFrame(uint8_t c
)
920 switch (gpsConfig()->provider
) {
921 case GPS_NMEA
: // NMEA
923 return gpsNewFrameNMEA(c
);
926 case GPS_UBLOX
: // UBX binary
928 return gpsNewFrameUBLOX(c
);
937 // Check for healthy communications
940 return (gpsData
.state
== GPS_STATE_RECEIVING_DATA
);
943 /* This is a light implementation of a GPS frame decoding
944 This should work with most of modern GPS devices configured to output 5 frames.
945 It assumes there are some NMEA GGA frames to decode on the serial bus
946 Now verifies checksum correctly before applying data
948 Here we use only the following data :
951 - GPS fix is/is not ok
952 - GPS num sat (4 is enough to be +/- reliable)
954 - GPS altitude (for OSD displaying)
955 - GPS speed (for OSD displaying)
964 // This code is used for parsing NMEA data
967 The latitude or longitude is coded this way in NMEA frames
968 dm.f coded as degrees + minutes + minute decimal
970 - d can be 1 or more char long. generally: 2 char long for latitude, 3 char long for longitude
971 - m is always 2 char long
972 - f can be 1 or more char long
973 This function converts this format in a unique unsigned long where 1 degree = 10 000 000
975 EOS increased the precision here, even if we think that the gps is not precise enough, with 10e5 precision it has 76cm resolution
976 with 10e7 it's around 1 cm now. Increasing it further is irrelevant, since even 1cm resolution is unrealistic, however increased
977 resolution also increased precision of nav calculations
978 static uint32_t GPS_coord_to_degrees(char *coordinateString)
981 uint8_t min, deg = 0;
982 uint16_t frac = 0, mult = 10000;
984 while (*p) { // parse the string until its end
986 frac += (*p - '0') * mult; // calculate only fractional part on up to 5 digits (d != s condition is true when the . is located)
990 d = p; // locate '.' char in the string
996 deg *= 10; // convert degrees : all chars before minutes ; for the first iteration, deg = 0
999 min = *(d - 1) - '0' + (*(d - 2) - '0') * 10; // convert minutes : 2 previous char before '.'
1000 return deg * 10000000UL + (min * 100000UL + frac) * 10UL / 6;
1006 static uint32_t grab_fields(char *src
, uint8_t mult
)
1007 { // convert string to uint32
1011 for (i
= 0; src
[i
] != 0; i
++) {
1012 if ((i
== 0) && (src
[0] == '-')) { // detect negative sign
1014 continue; // jump to next character if the first one was a negative sign
1016 if (src
[i
] == '.') {
1025 if (src
[i
] >= '0' && src
[i
] <= '9') {
1026 tmp
+= src
[i
] - '0';
1029 return 0; // out of bounds
1032 return isneg
? -tmp
: tmp
; // handle negative altitudes
1035 typedef struct gpsDataNmea_s
{
1042 uint16_t ground_course
;
1047 static bool gpsNewFrameNMEA(char c
)
1049 static gpsDataNmea_t gps_Msg
;
1051 uint8_t frameOK
= 0;
1052 static uint8_t param
= 0, offset
= 0, parity
= 0;
1053 static char string
[15];
1054 static uint8_t checksum_param
, gps_frame
= NO_FRAME
;
1055 static uint8_t svMessageNum
= 0;
1056 uint8_t svSatNum
= 0, svPacketIdx
= 0, svSatParam
= 0;
1067 if (param
== 0) { //frame identification
1068 gps_frame
= NO_FRAME
;
1069 if (0 == strcmp(string
, "GPGGA") || 0 == strcmp(string
, "GNGGA")) {
1070 gps_frame
= FRAME_GGA
;
1071 } else if (0 == strcmp(string
, "GPRMC") || 0 == strcmp(string
, "GNRMC")) {
1072 gps_frame
= FRAME_RMC
;
1073 } else if (0 == strcmp(string
, "GPGSV")) {
1074 gps_frame
= FRAME_GSV
;
1078 switch (gps_frame
) {
1079 case FRAME_GGA
: //************* GPGGA FRAME parsing
1081 // case 1: // Time information
1084 gps_Msg
.latitude
= GPS_coord_to_degrees(string
);
1087 if (string
[0] == 'S')
1088 gps_Msg
.latitude
*= -1;
1091 gps_Msg
.longitude
= GPS_coord_to_degrees(string
);
1094 if (string
[0] == 'W')
1095 gps_Msg
.longitude
*= -1;
1098 gpsSetFixState(string
[0] > '0');
1101 gps_Msg
.numSat
= grab_fields(string
, 0);
1104 gps_Msg
.hdop
= grab_fields(string
, 1) * 100; // hdop
1107 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
1111 case FRAME_RMC
: //************* GPRMC FRAME parsing
1114 gps_Msg
.time
= grab_fields(string
, 2); // UTC time hhmmss.ss
1117 gps_Msg
.speed
= ((grab_fields(string
, 1) * 5144L) / 1000L); // speed in cm/s added by Mis
1120 gps_Msg
.ground_course
= (grab_fields(string
, 1)); // ground course deg * 10
1123 gps_Msg
.date
= grab_fields(string
, 0); // date dd/mm/yy
1130 // Total number of messages of this type in this cycle
1134 svMessageNum
= grab_fields(string
, 0);
1137 // Total number of SVs visible
1138 GPS_numCh
= grab_fields(string
, 0);
1139 if (GPS_numCh
> GPS_SV_MAXSATS_LEGACY
) {
1140 GPS_numCh
= GPS_SV_MAXSATS_LEGACY
;
1147 svPacketIdx
= (param
- 4) / 4 + 1; // satellite number in packet, 1-4
1148 svSatNum
= svPacketIdx
+ (4 * (svMessageNum
- 1)); // global satellite number
1149 svSatParam
= param
- 3 - (4 * (svPacketIdx
- 1)); // parameter number for satellite
1151 if (svSatNum
> GPS_SV_MAXSATS_LEGACY
)
1154 switch (svSatParam
) {
1157 GPS_svinfo_chn
[svSatNum
- 1] = svSatNum
;
1158 GPS_svinfo_svid
[svSatNum
- 1] = grab_fields(string
, 0);
1161 // Elevation, in degrees, 90 maximum
1164 // Azimuth, degrees from True North, 000 through 359
1167 // SNR, 00 through 99 dB (null when not tracking)
1168 GPS_svinfo_cno
[svSatNum
- 1] = grab_fields(string
, 0);
1169 GPS_svinfo_quality
[svSatNum
- 1] = 0; // only used by ublox
1173 GPS_svInfoReceivedCount
++;
1187 if (checksum_param
) { //parity checksum
1189 uint8_t checksum
= 16 * ((string
[0] >= 'A') ? string
[0] - 'A' + 10 : string
[0] - '0') + ((string
[1] >= 'A') ? string
[1] - 'A' + 10 : string
[1] - '0');
1190 if (checksum
== parity
) {
1191 *gpsPacketLogChar
= LOG_IGNORED
;
1193 switch (gps_frame
) {
1195 *gpsPacketLogChar
= LOG_NMEA_GGA
;
1197 if (STATE(GPS_FIX
)) {
1198 gpsSol
.llh
.lat
= gps_Msg
.latitude
;
1199 gpsSol
.llh
.lon
= gps_Msg
.longitude
;
1200 gpsSol
.numSat
= gps_Msg
.numSat
;
1201 gpsSol
.llh
.altCm
= gps_Msg
.altitudeCm
;
1202 gpsSol
.hdop
= gps_Msg
.hdop
;
1206 *gpsPacketLogChar
= LOG_NMEA_RMC
;
1207 gpsSol
.groundSpeed
= gps_Msg
.speed
;
1208 gpsSol
.groundCourse
= gps_Msg
.ground_course
;
1210 // This check will miss 00:00:00.00, but we shouldn't care - next report will be valid
1211 if(!rtcHasTime() && gps_Msg
.date
!= 0 && gps_Msg
.time
!= 0) {
1212 dateTime_t temp_time
;
1213 temp_time
.year
= (gps_Msg
.date
% 100) + 2000;
1214 temp_time
.month
= (gps_Msg
.date
/ 100) % 100;
1215 temp_time
.day
= (gps_Msg
.date
/ 10000) % 100;
1216 temp_time
.hours
= (gps_Msg
.time
/ 1000000) % 100;
1217 temp_time
.minutes
= (gps_Msg
.time
/ 10000) % 100;
1218 temp_time
.seconds
= (gps_Msg
.time
/ 100) % 100;
1219 temp_time
.millis
= (gps_Msg
.time
& 100) * 10;
1220 rtcSetDateTime(&temp_time
);
1226 *gpsPacketLogChar
= LOG_ERROR
;
1233 string
[offset
++] = c
;
1234 if (!checksum_param
)
1239 #endif // USE_GPS_NMEA
1241 #ifdef USE_GPS_UBLOX
1244 uint32_t time
; // GPS msToW
1247 int32_t altitude_ellipsoid
;
1248 int32_t altitudeMslMm
;
1249 uint32_t horizontal_accuracy
;
1250 uint32_t vertical_accuracy
;
1254 uint32_t time
; // GPS msToW
1257 uint8_t differential_status
;
1259 uint32_t time_to_first_fix
;
1260 uint32_t uptime
; // milliseconds
1272 uint32_t position_accuracy_3d
;
1273 int32_t ecef_x_velocity
;
1274 int32_t ecef_y_velocity
;
1275 int32_t ecef_z_velocity
;
1276 uint32_t speed_accuracy
;
1277 uint16_t position_DOP
;
1313 uint8_t reserved0
[5];
1320 uint32_t time
; // GPS msToW
1327 uint32_t speed_accuracy
;
1328 uint32_t heading_accuracy
;
1332 uint8_t chn
; // Channel number, 255 for SVx not assigned to channel
1333 uint8_t svid
; // Satellite ID
1334 uint8_t flags
; // Bitmask
1335 uint8_t quality
; // Bitfield
1336 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
1337 uint8_t elev
; // Elevation in integer degrees
1338 int16_t azim
; // Azimuth in integer degrees
1339 int32_t prRes
; // Pseudo range residual in centimetres
1340 } ubx_nav_svinfo_channel
;
1344 uint8_t svId
; // Satellite ID
1345 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
1346 int8_t elev
; // Elevation in integer degrees
1347 int16_t azim
; // Azimuth in integer degrees
1348 int16_t prRes
; // Pseudo range residual in decimetres
1349 uint32_t flags
; // Bitmask
1353 uint32_t time
; // GPS Millisecond time of week
1354 uint8_t numCh
; // Number of channels
1355 uint8_t globalFlags
; // Bitmask, Chip hardware generation 0:Antaris, 1:u-blox 5, 2:u-blox 6
1356 uint16_t reserved2
; // Reserved
1357 ubx_nav_svinfo_channel channel
[GPS_SV_MAXSATS_M8N
]; // 32 satellites * 12 byte
1361 uint32_t time
; // GPS Millisecond time of week
1364 uint8_t reserved0
[2];
1365 ubx_nav_sat_sv svs
[GPS_SV_MAXSATS_M9N
];
1369 uint8_t clsId
; // Class ID of the acknowledged message
1370 uint8_t msgId
; // Message ID of the acknowledged message
1375 FIX_DEAD_RECKONING
= 1,
1378 FIX_GPS_DEAD_RECKONING
= 4,
1383 NAV_STATUS_FIX_VALID
= 1,
1384 NAV_STATUS_TIME_WEEK_VALID
= 4,
1385 NAV_STATUS_TIME_SECOND_VALID
= 8
1386 } ubx_nav_status_bits
;
1391 } ubx_nav_pvt_valid
;
1393 // Packet checksum accumulators
1394 static uint8_t _ck_a
;
1395 static uint8_t _ck_b
;
1397 // State machine state
1398 static bool _skip_packet
;
1399 static uint8_t _step
;
1400 static uint8_t _msg_id
;
1401 static uint16_t _payload_length
;
1402 static uint16_t _payload_counter
;
1404 static bool next_fix
;
1405 static uint8_t _class
;
1407 // do we have new position information?
1408 static bool _new_position
;
1410 // do we have new speed information?
1411 static bool _new_speed
;
1413 // Example packet sizes from UBlox u-center from a Glonass capable GPS receiver.
1414 //15:17:55 R -> UBX NAV-STATUS, Size 24, 'Navigation Status'
1415 //15:17:55 R -> UBX NAV-POSLLH, Size 36, 'Geodetic Position'
1416 //15:17:55 R -> UBX NAV-VELNED, Size 44, 'Velocity in WGS 84'
1417 //15:17:55 R -> UBX NAV-CLOCK, Size 28, 'Clock Status'
1418 //15:17:55 R -> UBX NAV-AOPSTATUS, Size 24, 'AOP Status'
1419 //15:17:55 R -> UBX 03-09, Size 208, 'Unknown'
1420 //15:17:55 R -> UBX 03-10, Size 336, 'Unknown'
1421 //15:17:55 R -> UBX NAV-SOL, Size 60, 'Navigation Solution'
1422 //15:17:55 R -> UBX NAV, Size 100, 'Navigation'
1423 //15:17:55 R -> UBX NAV-SVINFO, Size 328, 'Satellite Status and Information'
1425 // from the UBlox9 document, the largest payout we receive is the NAV-SAT and the payload size
1426 // is calculated as 8 + 12*numCh. numCh in the case of a M9N is 42.
1427 #define UBLOX_PAYLOAD_SIZE (8 + 12 * 42)
1432 ubx_nav_posllh posllh
;
1433 ubx_nav_status status
;
1434 ubx_nav_solution solution
;
1435 ubx_nav_velned velned
;
1437 ubx_nav_svinfo svinfo
;
1441 uint8_t bytes
[UBLOX_PAYLOAD_SIZE
];
1444 void _update_checksum(uint8_t *data
, uint8_t len
, uint8_t *ck_a
, uint8_t *ck_b
)
1453 static bool UBLOX_parse_gps(void)
1457 *gpsPacketLogChar
= LOG_IGNORED
;
1461 *gpsPacketLogChar
= LOG_UBLOX_POSLLH
;
1462 //i2c_dataset.time = _buffer.posllh.time;
1463 gpsSol
.llh
.lon
= _buffer
.posllh
.longitude
;
1464 gpsSol
.llh
.lat
= _buffer
.posllh
.latitude
;
1465 gpsSol
.llh
.altCm
= _buffer
.posllh
.altitudeMslMm
/ 10; //alt in cm
1466 gpsSetFixState(next_fix
);
1467 _new_position
= true;
1470 *gpsPacketLogChar
= LOG_UBLOX_STATUS
;
1471 next_fix
= (_buffer
.status
.fix_status
& NAV_STATUS_FIX_VALID
) && (_buffer
.status
.fix_type
== FIX_3D
);
1473 DISABLE_STATE(GPS_FIX
);
1476 *gpsPacketLogChar
= LOG_UBLOX_SOL
;
1477 next_fix
= (_buffer
.solution
.fix_status
& NAV_STATUS_FIX_VALID
) && (_buffer
.solution
.fix_type
== FIX_3D
);
1479 DISABLE_STATE(GPS_FIX
);
1480 gpsSol
.numSat
= _buffer
.solution
.satellites
;
1481 gpsSol
.hdop
= _buffer
.solution
.position_DOP
;
1483 //set clock, when gps time is available
1484 if(!rtcHasTime() && (_buffer
.solution
.fix_status
& NAV_STATUS_TIME_SECOND_VALID
) && (_buffer
.solution
.fix_status
& NAV_STATUS_TIME_WEEK_VALID
)) {
1485 //calculate rtctime: week number * ms in a week + ms of week + fractions of second + offset to UNIX reference year - 18 leap seconds
1486 rtcTime_t temp_time
= (((int64_t) _buffer
.solution
.week
)*7*24*60*60*1000) + _buffer
.solution
.time
+ (_buffer
.solution
.time_nsec
/1000000) + 315964800000LL - 18000;
1492 *gpsPacketLogChar
= LOG_UBLOX_VELNED
;
1493 gpsSol
.speed3d
= _buffer
.velned
.speed_3d
; // cm/s
1494 gpsSol
.groundSpeed
= _buffer
.velned
.speed_2d
; // cm/s
1495 gpsSol
.groundCourse
= (uint16_t) (_buffer
.velned
.heading_2d
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
1499 *gpsPacketLogChar
= LOG_UBLOX_SOL
;
1500 next_fix
= (_buffer
.pvt
.flags
& NAV_STATUS_FIX_VALID
) && (_buffer
.pvt
.fixType
== FIX_3D
);
1501 gpsSol
.llh
.lon
= _buffer
.pvt
.lon
;
1502 gpsSol
.llh
.lat
= _buffer
.pvt
.lat
;
1503 gpsSol
.llh
.altCm
= _buffer
.pvt
.hMSL
/ 10; //alt in cm
1504 gpsSetFixState(next_fix
);
1505 _new_position
= true;
1506 gpsSol
.numSat
= _buffer
.pvt
.numSV
;
1507 gpsSol
.hdop
= _buffer
.pvt
.pDOP
;
1508 gpsSol
.speed3d
= (uint16_t) sqrtf(powf(_buffer
.pvt
.gSpeed
/ 10, 2.0f
) + powf(_buffer
.pvt
.velD
/ 10, 2.0f
));
1509 gpsSol
.groundSpeed
= _buffer
.pvt
.gSpeed
/ 10; // cm/s
1510 gpsSol
.groundCourse
= (uint16_t) (_buffer
.pvt
.headMot
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
1513 //set clock, when gps time is available
1514 if (!rtcHasTime() && (_buffer
.pvt
.valid
& NAV_VALID_DATE
) && (_buffer
.pvt
.valid
& NAV_VALID_TIME
)) {
1516 dt
.year
= _buffer
.pvt
.year
;
1517 dt
.month
= _buffer
.pvt
.month
;
1518 dt
.day
= _buffer
.pvt
.day
;
1519 dt
.hours
= _buffer
.pvt
.hour
;
1520 dt
.minutes
= _buffer
.pvt
.min
;
1521 dt
.seconds
= _buffer
.pvt
.sec
;
1522 dt
.millis
= (_buffer
.pvt
.nano
> 0) ? _buffer
.pvt
.nano
/ 1000 : 0; //up to 5ms of error
1523 rtcSetDateTime(&dt
);
1528 *gpsPacketLogChar
= LOG_UBLOX_SVINFO
;
1529 GPS_numCh
= _buffer
.svinfo
.numCh
;
1530 // If we're getting NAV-SVINFO is because we're dealing with an old receiver that does not support NAV-SAT, so we'll only
1531 // save up to GPS_SV_MAXSATS_LEGACY channels so the BF Configurator knows it's receiving the old sat list info format.
1532 if (GPS_numCh
> GPS_SV_MAXSATS_LEGACY
)
1533 GPS_numCh
= GPS_SV_MAXSATS_LEGACY
;
1534 for (i
= 0; i
< GPS_numCh
; i
++) {
1535 GPS_svinfo_chn
[i
] = _buffer
.svinfo
.channel
[i
].chn
;
1536 GPS_svinfo_svid
[i
] = _buffer
.svinfo
.channel
[i
].svid
;
1537 GPS_svinfo_quality
[i
] =_buffer
.svinfo
.channel
[i
].quality
;
1538 GPS_svinfo_cno
[i
] = _buffer
.svinfo
.channel
[i
].cno
;
1540 for (i
= GPS_numCh
; i
< GPS_SV_MAXSATS_LEGACY
; i
++) {
1541 GPS_svinfo_chn
[i
] = 0;
1542 GPS_svinfo_svid
[i
] = 0;
1543 GPS_svinfo_quality
[i
] = 0;
1544 GPS_svinfo_cno
[i
] = 0;
1546 GPS_svInfoReceivedCount
++;
1549 *gpsPacketLogChar
= LOG_UBLOX_SVINFO
; // The logger won't show this is NAV-SAT instead of NAV-SVINFO
1550 GPS_numCh
= _buffer
.sat
.numSvs
;
1551 // We can receive here upto GPS_SV_MAXSATS_M9N channels, but since the majority of receivers currently in use are M8N or older,
1552 // it would be a waste of RAM to size the arrays that big. For now, they're sized GPS_SV_MAXSATS_M8N which means M9N won't show
1553 // all their channel information on BF Configurator. When M9N's are more widespread it would be a good time to increase those arrays.
1554 if (GPS_numCh
> GPS_SV_MAXSATS_M8N
)
1555 GPS_numCh
= GPS_SV_MAXSATS_M8N
;
1556 for (i
= 0; i
< GPS_numCh
; i
++) {
1557 GPS_svinfo_chn
[i
] = _buffer
.sat
.svs
[i
].gnssId
;
1558 GPS_svinfo_svid
[i
] = _buffer
.sat
.svs
[i
].svId
;
1559 GPS_svinfo_cno
[i
] = _buffer
.sat
.svs
[i
].cno
;
1560 GPS_svinfo_quality
[i
] =_buffer
.sat
.svs
[i
].flags
;
1562 for (i
= GPS_numCh
; i
< GPS_SV_MAXSATS_M8N
; i
++) {
1563 GPS_svinfo_chn
[i
] = 255;
1564 GPS_svinfo_svid
[i
] = 0;
1565 GPS_svinfo_quality
[i
] = 0;
1566 GPS_svinfo_cno
[i
] = 0;
1569 // Setting the number of channels higher than GPS_SV_MAXSATS_LEGACY is the only way to tell BF Configurator we're sending the
1570 // enhanced sat list info without changing the MSP protocol. Also, we're sending the complete list each time even if it's empty, so
1571 // BF Conf can erase old entries shown on screen when channels are removed from the list.
1572 GPS_numCh
= GPS_SV_MAXSATS_M8N
;
1573 GPS_svInfoReceivedCount
++;
1577 bool isSBASenabled
= false;
1578 bool isM8NwithDefaultConfig
= false;
1580 if ((_buffer
.gnss
.numConfigBlocks
>= 2) &&
1581 (_buffer
.gnss
.configblocks
[1].gnssId
== 1) && //SBAS
1582 (_buffer
.gnss
.configblocks
[1].flags
& UBLOX_GNSS_ENABLE
)) { //enabled
1584 isSBASenabled
= true;
1587 if ((_buffer
.gnss
.numTrkChHw
== 32) && //M8N
1588 (_buffer
.gnss
.numTrkChUse
== 32) &&
1589 (_buffer
.gnss
.numConfigBlocks
== 7) &&
1590 (_buffer
.gnss
.configblocks
[2].gnssId
== 2) && //Galileo
1591 (_buffer
.gnss
.configblocks
[2].resTrkCh
== 4) && //min channels
1592 (_buffer
.gnss
.configblocks
[2].maxTrkCh
== 8) && //max channels
1593 !(_buffer
.gnss
.configblocks
[2].flags
& UBLOX_GNSS_ENABLE
)) { //disabled
1595 isM8NwithDefaultConfig
= true;
1598 const uint16_t messageSize
= 4 + (_buffer
.gnss
.numConfigBlocks
* sizeof(ubx_configblock
));
1600 ubx_message tx_buffer
;
1601 memcpy(&tx_buffer
.payload
, &_buffer
, messageSize
);
1603 if (isSBASenabled
&& (gpsConfig()->sbasMode
== SBAS_NONE
)) {
1604 tx_buffer
.payload
.cfg_gnss
.configblocks
[1].flags
&= ~UBLOX_GNSS_ENABLE
; //Disable SBAS
1607 if (isM8NwithDefaultConfig
&& gpsConfig()->gps_ublox_use_galileo
) {
1608 tx_buffer
.payload
.cfg_gnss
.configblocks
[2].flags
|= UBLOX_GNSS_ENABLE
; //Enable Galileo
1611 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_GNSS
, messageSize
);
1615 if ((gpsData
.ackState
== UBLOX_ACK_WAITING
) && (_buffer
.ack
.msgId
== gpsData
.ackWaitingMsgId
)) {
1616 gpsData
.ackState
= UBLOX_ACK_GOT_ACK
;
1620 if ((gpsData
.ackState
== UBLOX_ACK_WAITING
) && (_buffer
.ack
.msgId
== gpsData
.ackWaitingMsgId
)) {
1621 gpsData
.ackState
= UBLOX_ACK_GOT_NACK
;
1628 // we only return true when we get new position and speed data
1629 // this ensures we don't use stale data
1630 if (_new_position
&& _new_speed
) {
1631 _new_speed
= _new_position
= false;
1637 static bool gpsNewFrameUBLOX(uint8_t data
)
1639 bool parsed
= false;
1642 case 0: // Sync char 1 (0xB5)
1643 if (PREAMBLE1
== data
) {
1644 _skip_packet
= false;
1648 case 1: // Sync char 2 (0x62)
1649 if (PREAMBLE2
!= data
) {
1658 _ck_b
= _ck_a
= data
; // reset the checksum accumulators
1662 _ck_b
+= (_ck_a
+= data
); // checksum byte
1664 #if DEBUG_UBLOX_FRAMES
1668 case 4: // Payload length (part 1)
1670 _ck_b
+= (_ck_a
+= data
); // checksum byte
1671 _payload_length
= data
; // payload length low byte
1673 case 5: // Payload length (part 2)
1675 _ck_b
+= (_ck_a
+= data
); // checksum byte
1676 _payload_length
+= (uint16_t)(data
<< 8);
1677 #if DEBUG_UBLOX_FRAMES
1678 debug
[3] = _payload_length
;
1680 if (_payload_length
> UBLOX_PAYLOAD_SIZE
) {
1681 _skip_packet
= true;
1683 _payload_counter
= 0; // prepare to receive payload
1684 if (_payload_length
== 0) {
1689 _ck_b
+= (_ck_a
+= data
); // checksum byte
1690 if (_payload_counter
< UBLOX_PAYLOAD_SIZE
) {
1691 _buffer
.bytes
[_payload_counter
] = data
;
1693 if (++_payload_counter
>= _payload_length
) {
1699 if (_ck_a
!= data
) {
1700 _skip_packet
= true; // bad checksum
1709 if (_ck_b
!= data
) {
1710 *gpsPacketLogChar
= LOG_ERROR
;
1712 break; // bad checksum
1718 *gpsPacketLogChar
= LOG_SKIPPED
;
1722 if (UBLOX_parse_gps()) {
1728 #endif // USE_GPS_UBLOX
1730 static void gpsHandlePassthrough(uint8_t data
)
1733 #ifdef USE_DASHBOARD
1734 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
1735 dashboardUpdate(micros());
1741 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
1743 waitForSerialPortToFinishTransmitting(gpsPort
);
1744 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
1746 if (!(gpsPort
->mode
& MODE_TX
))
1747 serialSetMode(gpsPort
, gpsPort
->mode
| MODE_TX
);
1749 #ifdef USE_DASHBOARD
1750 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
1751 dashboardShowFixedPage(PAGE_GPS
);
1755 serialPassthrough(gpsPort
, gpsPassthroughPort
, &gpsHandlePassthrough
, NULL
);
1758 float GPS_scaleLonDown
= 1.0f
; // this is used to offset the shrinking longitude as we go towards the poles
1760 void GPS_calc_longitude_scaling(int32_t lat
)
1762 float rads
= (fabsf((float)lat
) / 10000000.0f
) * 0.0174532925f
;
1763 GPS_scaleLonDown
= cos_approx(rads
);
1766 ////////////////////////////////////////////////////////////////////////////////////
1767 // Calculate the distance flown and vertical speed from gps position data
1769 static void GPS_calculateDistanceFlownVerticalSpeed(bool initialize
)
1771 static int32_t lastCoord
[2] = { 0, 0 };
1772 static int32_t lastAlt
;
1773 static int32_t lastMillis
;
1775 int currentMillis
= millis();
1778 GPS_distanceFlownInCm
= 0;
1779 GPS_verticalSpeedInCmS
= 0;
1781 if (STATE(GPS_FIX_HOME
) && ARMING_FLAG(ARMED
)) {
1782 uint16_t speed
= gpsConfig()->gps_use_3d_speed
? gpsSol
.speed3d
: gpsSol
.groundSpeed
;
1783 // Only add up movement when speed is faster than minimum threshold
1784 if (speed
> GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S
) {
1787 GPS_distance_cm_bearing(&gpsSol
.llh
.lat
, &gpsSol
.llh
.lon
, &lastCoord
[GPS_LATITUDE
], &lastCoord
[GPS_LONGITUDE
], &dist
, &dir
);
1788 if (gpsConfig()->gps_use_3d_speed
) {
1789 dist
= sqrtf(sq(gpsSol
.llh
.altCm
- lastAlt
) + sq(dist
));
1791 GPS_distanceFlownInCm
+= dist
;
1794 GPS_verticalSpeedInCmS
= (gpsSol
.llh
.altCm
- lastAlt
) * 1000 / (currentMillis
- lastMillis
);
1795 GPS_verticalSpeedInCmS
= constrain(GPS_verticalSpeedInCmS
, -1500, 1500);
1797 lastCoord
[GPS_LONGITUDE
] = gpsSol
.llh
.lon
;
1798 lastCoord
[GPS_LATITUDE
] = gpsSol
.llh
.lat
;
1799 lastAlt
= gpsSol
.llh
.altCm
;
1800 lastMillis
= currentMillis
;
1803 void GPS_reset_home_position(void)
1805 if (!STATE(GPS_FIX_HOME
) || !gpsConfig()->gps_set_home_point_once
) {
1806 if (STATE(GPS_FIX
) && gpsSol
.numSat
>= gpsConfig()->gpsRequiredSats
) {
1807 // requires the full sat count to say we have a home fix
1808 GPS_home
[GPS_LATITUDE
] = gpsSol
.llh
.lat
;
1809 GPS_home
[GPS_LONGITUDE
] = gpsSol
.llh
.lon
;
1810 GPS_calc_longitude_scaling(gpsSol
.llh
.lat
);
1811 // need an initial value for distance and bearing calcs, and to set ground altitude
1812 ENABLE_STATE(GPS_FIX_HOME
);
1815 GPS_calculateDistanceFlownVerticalSpeed(true); //Initialize
1818 ////////////////////////////////////////////////////////////////////////////////////
1819 #define DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR_IN_HUNDREDS_OF_KILOMETERS 1.113195f
1820 #define TAN_89_99_DEGREES 5729.57795f
1821 // Get distance between two points in cm
1822 // Get bearing from pos1 to pos2, returns an 1deg = 100 precision
1823 void GPS_distance_cm_bearing(int32_t *currentLat1
, int32_t *currentLon1
, int32_t *destinationLat2
, int32_t *destinationLon2
, uint32_t *dist
, int32_t *bearing
)
1825 float dLat
= *destinationLat2
- *currentLat1
; // difference of latitude in 1/10 000 000 degrees
1826 float dLon
= (float)(*destinationLon2
- *currentLon1
) * GPS_scaleLonDown
;
1827 *dist
= sqrtf(sq(dLat
) + sq(dLon
)) * DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR_IN_HUNDREDS_OF_KILOMETERS
;
1829 *bearing
= 9000.0f
+ atan2_approx(-dLat
, dLon
) * TAN_89_99_DEGREES
; // Convert the output radians to 100xdeg
1834 void GPS_calculateDistanceAndDirectionToHome(void)
1836 if (STATE(GPS_FIX_HOME
)) {
1839 GPS_distance_cm_bearing(&gpsSol
.llh
.lat
, &gpsSol
.llh
.lon
, &GPS_home
[GPS_LATITUDE
], &GPS_home
[GPS_LONGITUDE
], &dist
, &dir
);
1840 GPS_distanceToHome
= dist
/ 100; // m/s
1841 GPS_distanceToHomeCm
= dist
; // cm/sec
1842 GPS_directionToHome
= dir
/ 10; // degrees * 10 or decidegrees
1844 // If we don't have home set, do not display anything
1845 GPS_distanceToHome
= 0;
1846 GPS_distanceToHomeCm
= 0;
1847 GPS_directionToHome
= 0;
1851 void onGpsNewData(void)
1853 if (!(STATE(GPS_FIX
) && gpsSol
.numSat
>= gpsConfig()->gpsMinimumSats
)) {
1854 // if we don't have a 3D fix and the minimum sats, don't give data to GPS rescue
1858 GPS_calculateDistanceAndDirectionToHome();
1859 if (ARMING_FLAG(ARMED
)) {
1860 GPS_calculateDistanceFlownVerticalSpeed(false);
1863 #ifdef USE_GPS_RESCUE
1868 void gpsSetFixState(bool state
)
1871 ENABLE_STATE(GPS_FIX
);
1872 ENABLE_STATE(GPS_FIX_EVER
);
1874 DISABLE_STATE(GPS_FIX
);