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 "scheduler/scheduler.h"
59 #include "sensors/sensors.h"
62 #define LOG_IGNORED '!'
63 #define LOG_SKIPPED '>'
64 #define LOG_NMEA_GGA 'g'
65 #define LOG_NMEA_RMC 'r'
66 #define LOG_UBLOX_SOL 'O'
67 #define LOG_UBLOX_STATUS 'S'
68 #define LOG_UBLOX_SVINFO 'I'
69 #define LOG_UBLOX_POSLLH 'P'
70 #define LOG_UBLOX_VELNED 'V'
72 #define DEBUG_SERIAL_BAUD 0 // set to 1 to debug serial port baud config (/100)
73 #define DEBUG_UBLOX_INIT 0 // set to 1 to debug ublox initialization
74 #define DEBUG_UBLOX_FRAMES 0 // set to 1 to debug ublox received frames
76 char gpsPacketLog
[GPS_PACKET_LOG_ENTRY_COUNT
];
77 static char *gpsPacketLogChar
= gpsPacketLog
;
78 // **********************
80 // **********************
82 uint16_t GPS_distanceToHome
; // distance to home point in meters
83 int16_t GPS_directionToHome
; // direction to home or hol point in degrees
84 uint32_t GPS_distanceFlownInCm
; // distance flown since armed in centimeters
85 int16_t GPS_verticalSpeedInCmS
; // vertical speed in cm/s
86 float dTnav
; // Delta Time in milliseconds for navigation computations, updated with every good GPS read
87 int16_t nav_takeoff_bearing
;
89 #define GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S 15 // 5.4Km/h 3.35mph
91 gpsSolutionData_t gpsSol
;
92 uint32_t GPS_packetCount
= 0;
93 uint32_t GPS_svInfoReceivedCount
= 0; // SV = Space Vehicle, counter increments each time SV info is received.
94 uint8_t GPS_update
= 0; // toogle to distinct a GPS position update (directly or via MSP)
96 uint8_t GPS_numCh
; // Details on numCh/svinfo in gps.h
97 uint8_t GPS_svinfo_chn
[GPS_SV_MAXSATS_M8N
];
98 uint8_t GPS_svinfo_svid
[GPS_SV_MAXSATS_M8N
];
99 uint8_t GPS_svinfo_quality
[GPS_SV_MAXSATS_M8N
];
100 uint8_t GPS_svinfo_cno
[GPS_SV_MAXSATS_M8N
];
102 // GPS timeout for wrong baud rate/disconnection/etc in milliseconds (default 2.5second)
103 #define GPS_TIMEOUT (2500)
104 // How many entries in gpsInitData array below
105 #define GPS_INIT_ENTRIES (GPS_BAUDRATE_MAX + 1)
106 #define GPS_BAUDRATE_CHANGE_DELAY (200)
107 // Timeout for waiting ACK/NAK in GPS task cycles (0.25s at 100Hz)
108 #define UBLOX_ACK_TIMEOUT_MAX_COUNT (25)
110 static serialPort_t
*gpsPort
;
112 typedef struct gpsInitData_s
{
114 uint8_t baudrateIndex
; // see baudRate_e
119 // NMEA will cycle through these until valid data is received
120 static const gpsInitData_t gpsInitData
[] = {
121 { GPS_BAUDRATE_115200
, BAUD_115200
, "$PUBX,41,1,0003,0001,115200,0*1E\r\n", "$PMTK251,115200*1F\r\n" },
122 { GPS_BAUDRATE_57600
, BAUD_57600
, "$PUBX,41,1,0003,0001,57600,0*2D\r\n", "$PMTK251,57600*2C\r\n" },
123 { GPS_BAUDRATE_38400
, BAUD_38400
, "$PUBX,41,1,0003,0001,38400,0*26\r\n", "$PMTK251,38400*27\r\n" },
124 { GPS_BAUDRATE_19200
, BAUD_19200
, "$PUBX,41,1,0003,0001,19200,0*23\r\n", "$PMTK251,19200*22\r\n" },
125 // 9600 is not enough for 5Hz updates - leave for compatibility to dumb NMEA that only runs at this speed
126 { GPS_BAUDRATE_9600
, BAUD_9600
, "$PUBX,41,1,0003,0001,9600,0*16\r\n", "" }
129 #define GPS_INIT_DATA_ENTRY_COUNT (sizeof(gpsInitData) / sizeof(gpsInitData[0]))
131 #define DEFAULT_BAUD_RATE_INDEX 0
152 MSG_CFG_SET_RATE
= 0x01,
154 MSG_CFG_NAV_SETTINGS
= 0x24,
156 } ubx_protocol_bytes
;
158 #define UBLOX_MODE_ENABLED 0x1
159 #define UBLOX_MODE_TEST 0x2
161 #define UBLOX_USAGE_RANGE 0x1
162 #define UBLOX_USAGE_DIFFCORR 0x2
163 #define UBLOX_USAGE_INTEGRITY 0x4
165 #define UBLOX_GNSS_ENABLE 0x1
166 #define UBLOX_GNSS_DEFAULT_SIGCFGMASK 0x10000
168 #define UBLOX_DYNMODE_PEDESTRIAN 3
169 #define UBLOX_DYNMODE_AIRBORNE_1G 6
170 #define UBLOX_DYNMODE_AIRBORNE_4G 8
217 uint8_t numConfigBlocks
;
218 ubx_configblock configblocks
[7];
226 uint32_t fixedAltVar
;
233 uint8_t staticHoldThresh
;
234 uint8_t dgnssTimeout
;
235 uint8_t cnoThreshNumSVs
;
237 uint8_t reserved0
[2];
238 uint16_t staticHoldMaxDist
;
240 uint8_t reserved1
[5];
244 ubx_poll_msg poll_msg
;
246 ubx_cfg_rate cfg_rate
;
247 ubx_cfg_nav5 cfg_nav5
;
248 ubx_cfg_sbas cfg_sbas
;
249 ubx_cfg_gnss cfg_gnss
;
255 } __attribute__((packed
)) ubx_message
;
257 #endif // USE_GPS_UBLOX
261 GPS_STATE_INITIALIZING
,
262 GPS_STATE_INITIALIZED
,
263 GPS_STATE_CHANGE_BAUD
,
265 GPS_STATE_RECEIVING_DATA
,
266 GPS_STATE_LOST_COMMUNICATION
,
270 // Max time to wait for received data
271 #define GPS_MAX_WAIT_DATA_RX 30
275 PG_REGISTER_WITH_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
, PG_GPS_CONFIG
, 0);
277 PG_RESET_TEMPLATE(gpsConfig_t
, gpsConfig
,
278 .provider
= GPS_NMEA
,
279 .sbasMode
= SBAS_NONE
,
280 .autoConfig
= GPS_AUTOCONFIG_ON
,
281 .autoBaud
= GPS_AUTOBAUD_OFF
,
282 .gps_ublox_use_galileo
= false,
283 .gps_ublox_mode
= UBLOX_AIRBORNE
,
284 .gps_set_home_point_once
= false,
285 .gps_use_3d_speed
= false,
286 .sbas_integrity
= false
289 static void shiftPacketLog(void)
293 for (i
= ARRAYLEN(gpsPacketLog
) - 1; i
> 0 ; i
--) {
294 gpsPacketLog
[i
] = gpsPacketLog
[i
-1];
298 static bool isConfiguratorConnected() {
299 return (getArmingDisableFlags() & ARMING_DISABLED_MSP
);
302 static void gpsNewData(uint16_t c
);
304 static bool gpsNewFrameNMEA(char c
);
307 static bool gpsNewFrameUBLOX(uint8_t data
);
310 static void gpsSetState(gpsState_e state
)
312 gpsData
.lastMessage
= millis();
313 sensorsClear(SENSOR_GPS
);
315 gpsData
.state
= state
;
316 gpsData
.state_position
= 0;
317 gpsData
.state_ts
= millis();
318 gpsData
.ackState
= UBLOX_ACK_IDLE
;
323 gpsData
.baudrateIndex
= 0;
325 gpsData
.timeouts
= 0;
327 memset(gpsPacketLog
, 0x00, sizeof(gpsPacketLog
));
329 // init gpsData structure. if we're not actually enabled, don't bother doing anything else
330 gpsSetState(GPS_STATE_UNKNOWN
);
332 gpsData
.lastMessage
= millis();
334 if (gpsConfig()->provider
== GPS_MSP
) { // no serial ports used when GPS_MSP is configured
335 gpsSetState(GPS_STATE_INITIALIZED
);
339 const serialPortConfig_t
*gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
340 if (!gpsPortConfig
) {
344 while (gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
!= gpsPortConfig
->gps_baudrateIndex
) {
345 gpsData
.baudrateIndex
++;
346 if (gpsData
.baudrateIndex
>= GPS_INIT_DATA_ENTRY_COUNT
) {
347 gpsData
.baudrateIndex
= DEFAULT_BAUD_RATE_INDEX
;
352 portMode_e mode
= MODE_RXTX
;
353 #if defined(GPS_NMEA_TX_ONLY)
354 if (gpsConfig()->provider
== GPS_NMEA
) {
359 // no callback - buffer will be consumed in gpsUpdate()
360 gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, NULL
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
], mode
, SERIAL_NOT_INVERTED
);
365 // signal GPS "thread" to initialize when it gets to it
366 gpsSetState(GPS_STATE_INITIALIZING
);
370 void gpsInitNmea(void)
372 #if !defined(GPS_NMEA_TX_ONLY)
375 switch (gpsData
.state
) {
376 case GPS_STATE_INITIALIZING
:
377 #if !defined(GPS_NMEA_TX_ONLY)
379 if (now
- gpsData
.state_ts
< 1000) {
382 gpsData
.state_ts
= now
;
383 if (gpsData
.state_position
< 1) {
384 serialSetBaudRate(gpsPort
, 4800);
385 gpsData
.state_position
++;
386 } else if (gpsData
.state_position
< 2) {
387 // print our FIXED init string for the baudrate we want to be at
388 serialPrint(gpsPort
, "$PSRF100,1,115200,8,1,0*05\r\n");
389 gpsData
.state_position
++;
391 // we're now (hopefully) at the correct rate, next state will switch to it
392 gpsSetState(GPS_STATE_CHANGE_BAUD
);
396 case GPS_STATE_CHANGE_BAUD
:
397 #if !defined(GPS_NMEA_TX_ONLY)
399 if (now
- gpsData
.state_ts
< 1000) {
402 gpsData
.state_ts
= now
;
403 if (gpsData
.state_position
< 1) {
404 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
405 gpsData
.state_position
++;
406 } else if (gpsData
.state_position
< 2) {
407 serialPrint(gpsPort
, "$PSRF103,00,6,00,0*23\r\n");
408 gpsData
.state_position
++;
412 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
415 gpsSetState(GPS_STATE_RECEIVING_DATA
);
419 #endif // USE_GPS_NMEA
422 static void ubloxSendByteUpdateChecksum(const uint8_t data
, uint8_t *checksumA
, uint8_t *checksumB
)
425 *checksumB
+= *checksumA
;
426 serialWrite(gpsPort
, data
);
429 static void ubloxSendDataUpdateChecksum(const uint8_t *data
, uint8_t len
, uint8_t *checksumA
, uint8_t *checksumB
)
432 ubloxSendByteUpdateChecksum(*data
, checksumA
, checksumB
);
437 static void ubloxSendMessage(const uint8_t *data
, uint8_t len
)
439 uint8_t checksumA
= 0, checksumB
= 0;
440 serialWrite(gpsPort
, data
[0]);
441 serialWrite(gpsPort
, data
[1]);
442 ubloxSendDataUpdateChecksum(&data
[2], len
- 2, &checksumA
, &checksumB
);
443 serialWrite(gpsPort
, checksumA
);
444 serialWrite(gpsPort
, checksumB
);
446 // Save state for ACK waiting
447 gpsData
.ackWaitingMsgId
= data
[3]; //save message id for ACK
448 gpsData
.ackTimeoutCounter
= 0;
449 gpsData
.ackState
= UBLOX_ACK_WAITING
;
452 static void ubloxSendConfigMessage(ubx_message
*message
, uint8_t msg_id
, uint8_t length
)
454 message
->header
.preamble1
= PREAMBLE1
;
455 message
->header
.preamble2
= PREAMBLE2
;
456 message
->header
.msg_class
= CLASS_CFG
;
457 message
->header
.msg_id
= msg_id
;
458 message
->header
.length
= length
;
459 ubloxSendMessage((const uint8_t *) message
, length
+ 6);
462 static void ubloxSendPollMessage(uint8_t msg_id
)
464 ubx_message tx_buffer
;
465 tx_buffer
.header
.preamble1
= PREAMBLE1
;
466 tx_buffer
.header
.preamble2
= PREAMBLE2
;
467 tx_buffer
.header
.msg_class
= CLASS_CFG
;
468 tx_buffer
.header
.msg_id
= msg_id
;
469 tx_buffer
.header
.length
= 0;
470 ubloxSendMessage((const uint8_t *) &tx_buffer
, 6);
473 static void ubloxSendNAV5Message(bool airborne
) {
474 ubx_message tx_buffer
;
475 tx_buffer
.payload
.cfg_nav5
.mask
= 0xFFFF;
477 #if defined(GPS_UBLOX_MODE_AIRBORNE_1G)
478 tx_buffer
.payload
.cfg_nav5
.dynModel
= UBLOX_DYNMODE_AIRBORNE_1G
;
480 tx_buffer
.payload
.cfg_nav5
.dynModel
= UBLOX_DYNMODE_AIRBORNE_4G
;
483 tx_buffer
.payload
.cfg_nav5
.dynModel
= UBLOX_DYNMODE_PEDESTRIAN
;
485 tx_buffer
.payload
.cfg_nav5
.fixMode
= 3;
486 tx_buffer
.payload
.cfg_nav5
.fixedAlt
= 0;
487 tx_buffer
.payload
.cfg_nav5
.fixedAltVar
= 10000;
488 tx_buffer
.payload
.cfg_nav5
.minElev
= 5;
489 tx_buffer
.payload
.cfg_nav5
.drLimit
= 0;
490 tx_buffer
.payload
.cfg_nav5
.pDOP
= 250;
491 tx_buffer
.payload
.cfg_nav5
.tDOP
= 250;
492 tx_buffer
.payload
.cfg_nav5
.pAcc
= 100;
493 tx_buffer
.payload
.cfg_nav5
.tAcc
= 300;
494 tx_buffer
.payload
.cfg_nav5
.staticHoldThresh
= 0;
495 tx_buffer
.payload
.cfg_nav5
.dgnssTimeout
= 60;
496 tx_buffer
.payload
.cfg_nav5
.cnoThreshNumSVs
= 0;
497 tx_buffer
.payload
.cfg_nav5
.cnoThresh
= 0;
498 tx_buffer
.payload
.cfg_nav5
.reserved0
[0] = 0;
499 tx_buffer
.payload
.cfg_nav5
.reserved0
[1] = 0;
500 tx_buffer
.payload
.cfg_nav5
.staticHoldMaxDist
= 200;
501 tx_buffer
.payload
.cfg_nav5
.utcStandard
= 0;
502 tx_buffer
.payload
.cfg_nav5
.reserved1
[0] = 0;
503 tx_buffer
.payload
.cfg_nav5
.reserved1
[1] = 0;
504 tx_buffer
.payload
.cfg_nav5
.reserved1
[2] = 0;
505 tx_buffer
.payload
.cfg_nav5
.reserved1
[3] = 0;
506 tx_buffer
.payload
.cfg_nav5
.reserved1
[4] = 0;
508 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_NAV_SETTINGS
, sizeof(ubx_cfg_nav5
));
511 static void ubloxSetMessageRate(uint8_t messageClass
, uint8_t messageID
, uint8_t rate
) {
512 ubx_message tx_buffer
;
513 tx_buffer
.payload
.cfg_msg
.msgClass
= messageClass
;
514 tx_buffer
.payload
.cfg_msg
.msgID
= messageID
;
515 tx_buffer
.payload
.cfg_msg
.rate
= rate
;
516 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_MSG
, sizeof(ubx_cfg_msg
));
519 static void ubloxSetNavRate(uint16_t measRate
, uint16_t navRate
, uint16_t timeRef
) {
520 ubx_message tx_buffer
;
521 tx_buffer
.payload
.cfg_rate
.measRate
= measRate
;
522 tx_buffer
.payload
.cfg_rate
.navRate
= navRate
;
523 tx_buffer
.payload
.cfg_rate
.timeRef
= timeRef
;
524 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_RATE
, sizeof(ubx_cfg_rate
));
527 static void ubloxSetSbas() {
528 ubx_message tx_buffer
;
530 //NOTE: default ublox config for sbas mode is: UBLOX_MODE_ENABLED, test is disabled
531 tx_buffer
.payload
.cfg_sbas
.mode
= UBLOX_MODE_TEST
;
532 if (gpsConfig()->sbasMode
!= SBAS_NONE
) {
533 tx_buffer
.payload
.cfg_sbas
.mode
|= UBLOX_MODE_ENABLED
;
536 //NOTE: default ublox config for sbas mode is: UBLOX_USAGE_RANGE |Â UBLOX_USAGE_DIFFCORR, integrity is disabled
537 tx_buffer
.payload
.cfg_sbas
.usage
= UBLOX_USAGE_RANGE
| UBLOX_USAGE_DIFFCORR
;
538 if (gpsConfig()->sbas_integrity
) {
539 tx_buffer
.payload
.cfg_sbas
.usage
|= UBLOX_USAGE_INTEGRITY
;
542 tx_buffer
.payload
.cfg_sbas
.maxSBAS
= 3;
543 tx_buffer
.payload
.cfg_sbas
.scanmode2
= 0;
544 switch (gpsConfig()->sbasMode
) {
546 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0;
549 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00010048; //PRN123, PRN126, PRN136
552 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x0004A800; //PRN131, PRN133, PRN135, PRN138
555 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00020200; //PRN129, PRN137
558 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00001180; //PRN127, PRN128, PRN132
561 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0;
564 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_SBAS
, sizeof(ubx_cfg_sbas
));
567 void gpsInitUblox(void)
570 // UBX will run at the serial port's baudrate, it shouldn't be "autodetected". So here we force it to that rate
572 // Wait until GPS transmit buffer is empty
573 if (!isSerialTransmitBufferEmpty(gpsPort
))
576 switch (gpsData
.state
) {
577 case GPS_STATE_INITIALIZING
:
579 if (now
- gpsData
.state_ts
< GPS_BAUDRATE_CHANGE_DELAY
)
582 if (gpsData
.state_position
< GPS_INIT_ENTRIES
) {
583 // try different speed to INIT
584 baudRate_e newBaudRateIndex
= gpsInitData
[gpsData
.state_position
].baudrateIndex
;
586 gpsData
.state_ts
= now
;
588 if (lookupBaudRateIndex(serialGetBaudRate(gpsPort
)) != newBaudRateIndex
) {
589 // change the rate if needed and wait a little
590 serialSetBaudRate(gpsPort
, baudRates
[newBaudRateIndex
]);
591 #if DEBUG_SERIAL_BAUD
592 debug
[0] = baudRates
[newBaudRateIndex
] / 100;
597 // print our FIXED init string for the baudrate we want to be at
598 serialPrint(gpsPort
, gpsInitData
[gpsData
.baudrateIndex
].ubx
);
600 gpsData
.state_position
++;
602 // we're now (hopefully) at the correct rate, next state will switch to it
603 gpsSetState(GPS_STATE_CHANGE_BAUD
);
607 case GPS_STATE_CHANGE_BAUD
:
608 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
]);
609 #if DEBUG_SERIAL_BAUD
610 debug
[0] = baudRates
[gpsInitData
[gpsData
.baudrateIndex
].baudrateIndex
] / 100;
612 gpsSetState(GPS_STATE_CONFIGURE
);
615 case GPS_STATE_CONFIGURE
:
616 // Either use specific config file for GPS or let dynamically upload config
617 if (gpsConfig()->autoConfig
== GPS_AUTOCONFIG_OFF
) {
618 gpsSetState(GPS_STATE_RECEIVING_DATA
);
622 if (gpsData
.ackState
== UBLOX_ACK_IDLE
) {
623 switch (gpsData
.state_position
) {
625 gpsData
.ubloxUsePVT
= true;
626 gpsData
.ubloxUseSAT
= true;
627 ubloxSendNAV5Message(gpsConfig()->gps_ublox_mode
== UBLOX_AIRBORNE
);
629 case 1: //Disable NMEA Messages
630 ubloxSetMessageRate(0xF0, 0x05, 0); // VGS: Course over ground and Ground speed
633 ubloxSetMessageRate(0xF0, 0x03, 0); // GSV: GNSS Satellites in View
636 ubloxSetMessageRate(0xF0, 0x01, 0); // GLL: Latitude and longitude, with time of position fix and status
639 ubloxSetMessageRate(0xF0, 0x00, 0); // GGA: Global positioning system fix data
642 ubloxSetMessageRate(0xF0, 0x02, 0); // GSA: GNSS DOP and Active Satellites
645 ubloxSetMessageRate(0xF0, 0x04, 0); // RMC: Recommended Minimum data
647 case 7: //Enable UBLOX Messages
648 if (gpsData
.ubloxUsePVT
) {
649 ubloxSetMessageRate(CLASS_NAV
, MSG_PVT
, 1); // set PVT MSG rate
651 ubloxSetMessageRate(CLASS_NAV
, MSG_SOL
, 1); // set SOL MSG rate
655 if (gpsData
.ubloxUsePVT
) {
656 gpsData
.state_position
++;
658 ubloxSetMessageRate(CLASS_NAV
, MSG_POSLLH
, 1); // set POSLLH MSG rate
662 if (gpsData
.ubloxUsePVT
) {
663 gpsData
.state_position
++;
665 ubloxSetMessageRate(CLASS_NAV
, MSG_STATUS
, 1); // set STATUS MSG rate
669 if (gpsData
.ubloxUsePVT
) {
670 gpsData
.state_position
++;
672 ubloxSetMessageRate(CLASS_NAV
, MSG_VELNED
, 1); // set VELNED MSG rate
676 if (gpsData
.ubloxUseSAT
) {
677 ubloxSetMessageRate(CLASS_NAV
, MSG_SAT
, 5); // set SAT MSG rate (every 5 cycles)
679 ubloxSetMessageRate(CLASS_NAV
, MSG_SVINFO
, 5); // set SVINFO MSG rate (every 5 cycles)
683 ubloxSetNavRate(0xC8, 1, 1); // set rate to 5Hz (measurement period: 200ms, navigation rate: 1 cycle)
689 if ((gpsConfig()->sbasMode
== SBAS_NONE
) || (gpsConfig()->gps_ublox_use_galileo
)) {
690 ubloxSendPollMessage(MSG_CFG_GNSS
);
692 gpsSetState(GPS_STATE_RECEIVING_DATA
);
700 switch (gpsData
.ackState
) {
703 case UBLOX_ACK_WAITING
:
704 if ((++gpsData
.ackTimeoutCounter
) == UBLOX_ACK_TIMEOUT_MAX_COUNT
) {
705 gpsSetState(GPS_STATE_LOST_COMMUNICATION
);
708 case UBLOX_ACK_GOT_ACK
:
709 if (gpsData
.state_position
== 14) {
710 // ublox should be initialised, try receiving
711 gpsSetState(GPS_STATE_RECEIVING_DATA
);
713 gpsData
.state_position
++;
714 gpsData
.ackState
= UBLOX_ACK_IDLE
;
717 case UBLOX_ACK_GOT_NACK
:
718 if (gpsData
.state_position
== 7) { // If we were asking for NAV-PVT...
719 gpsData
.ubloxUsePVT
= false; // ...retry asking for NAV-SOL
720 gpsData
.ackState
= UBLOX_ACK_IDLE
;
722 if (gpsData
.state_position
== 11) { // If we were asking for NAV-SAT...
723 gpsData
.ubloxUseSAT
= false; // ...retry asking for NAV-SVINFO
724 gpsData
.ackState
= UBLOX_ACK_IDLE
;
726 gpsSetState(GPS_STATE_CONFIGURE
);
735 #endif // USE_GPS_UBLOX
737 void gpsInitHardware(void)
739 switch (gpsConfig()->provider
) {
756 static void updateGpsIndicator(timeUs_t currentTimeUs
)
758 static uint32_t GPSLEDTime
;
759 if ((int32_t)(currentTimeUs
- GPSLEDTime
) >= 0 && (gpsSol
.numSat
>= 5)) {
760 GPSLEDTime
= currentTimeUs
+ 150000;
765 void gpsUpdate(timeUs_t currentTimeUs
)
767 static gpsState_e gpsStateDurationUs
[GPS_STATE_COUNT
];
768 timeUs_t executeTimeUs
;
769 gpsState_e gpsCurState
= gpsData
.state
;
771 // read out available GPS bytes
773 while (serialRxBytesWaiting(gpsPort
)) {
774 if (cmpTimeUs(micros(), currentTimeUs
) > GPS_MAX_WAIT_DATA_RX
) {
775 // Wait 1ms and come back
776 rescheduleTask(TASK_SELF
, TASK_PERIOD_HZ(TASK_GPS_RATE_FAST
));
779 gpsNewData(serialRead(gpsPort
));
781 // Restore default task rate
782 rescheduleTask(TASK_SELF
, TASK_PERIOD_HZ(TASK_GPS_RATE
));
783 } else if (GPS_update
& GPS_MSP_UPDATE
) { // GPS data received via MSP
784 gpsSetState(GPS_STATE_RECEIVING_DATA
);
786 GPS_update
&= ~GPS_MSP_UPDATE
;
790 debug
[0] = gpsData
.state
;
791 debug
[1] = gpsData
.state_position
;
792 debug
[2] = gpsData
.ackState
;
795 switch (gpsData
.state
) {
796 case GPS_STATE_UNKNOWN
:
797 case GPS_STATE_INITIALIZED
:
800 case GPS_STATE_INITIALIZING
:
801 case GPS_STATE_CHANGE_BAUD
:
802 case GPS_STATE_CONFIGURE
:
806 case GPS_STATE_LOST_COMMUNICATION
:
808 if (gpsConfig()->autoBaud
) {
810 gpsData
.baudrateIndex
++;
811 gpsData
.baudrateIndex
%= GPS_INIT_ENTRIES
;
814 DISABLE_STATE(GPS_FIX
);
815 gpsSetState(GPS_STATE_INITIALIZING
);
818 case GPS_STATE_RECEIVING_DATA
:
819 // check for no data/gps timeout/cable disconnection etc
820 if (millis() - gpsData
.lastMessage
> GPS_TIMEOUT
) {
821 gpsSetState(GPS_STATE_LOST_COMMUNICATION
);
824 if (gpsConfig()->autoConfig
== GPS_AUTOCONFIG_ON
) { // Only if autoconfig is enabled
825 switch (gpsData
.state_position
) {
827 if (!isConfiguratorConnected()) {
828 if (gpsData
.ubloxUseSAT
) {
829 ubloxSetMessageRate(CLASS_NAV
, MSG_SAT
, 0); // disable SAT MSG
831 ubloxSetMessageRate(CLASS_NAV
, MSG_SVINFO
, 0); // disable SVINFO MSG
833 gpsData
.state_position
= 1;
837 if (STATE(GPS_FIX
) && (gpsConfig()->gps_ublox_mode
== UBLOX_DYNAMIC
)) {
838 ubloxSendNAV5Message(true);
839 gpsData
.state_position
= 2;
841 if (isConfiguratorConnected()) {
842 gpsData
.state_position
= 2;
846 if (isConfiguratorConnected()) {
847 if (gpsData
.ubloxUseSAT
) {
848 ubloxSetMessageRate(CLASS_NAV
, MSG_SAT
, 5); // set SAT MSG rate (every 5 cycles)
850 ubloxSetMessageRate(CLASS_NAV
, MSG_SVINFO
, 5); // set SVINFO MSG rate (every 5 cycles)
852 gpsData
.state_position
= 0;
857 #endif //USE_GPS_UBLOX
862 executeTimeUs
= micros() - currentTimeUs
;
864 if (executeTimeUs
> gpsStateDurationUs
[gpsCurState
]) {
865 gpsStateDurationUs
[gpsCurState
] = executeTimeUs
;
867 schedulerSetNextStateTime(gpsStateDurationUs
[gpsData
.state
]);
869 if (sensors(SENSOR_GPS
)) {
870 updateGpsIndicator(currentTimeUs
);
872 if (!ARMING_FLAG(ARMED
) && !gpsConfig()->gps_set_home_point_once
) {
873 DISABLE_STATE(GPS_FIX_HOME
);
875 #if defined(USE_GPS_RESCUE)
876 if (gpsRescueIsConfigured()) {
877 updateGPSRescueState();
882 static void gpsNewData(uint16_t c
)
884 if (!gpsNewFrame(c
)) {
888 if (gpsData
.state
== GPS_STATE_RECEIVING_DATA
) {
889 // new data received and parsed, we're in business
890 gpsData
.lastLastMessage
= gpsData
.lastMessage
;
891 gpsData
.lastMessage
= millis();
892 sensorsSet(SENSOR_GPS
);
895 GPS_update
^= GPS_DIRECT_TICK
;
898 debug
[3] = GPS_update
;
904 bool gpsNewFrame(uint8_t c
)
906 switch (gpsConfig()->provider
) {
907 case GPS_NMEA
: // NMEA
909 return gpsNewFrameNMEA(c
);
912 case GPS_UBLOX
: // UBX binary
914 return gpsNewFrameUBLOX(c
);
923 // Check for healthy communications
926 return (gpsData
.state
== GPS_STATE_RECEIVING_DATA
);
929 /* This is a light implementation of a GPS frame decoding
930 This should work with most of modern GPS devices configured to output 5 frames.
931 It assumes there are some NMEA GGA frames to decode on the serial bus
932 Now verifies checksum correctly before applying data
934 Here we use only the following data :
937 - GPS fix is/is not ok
938 - GPS num sat (4 is enough to be +/- reliable)
940 - GPS altitude (for OSD displaying)
941 - GPS speed (for OSD displaying)
950 // This code is used for parsing NMEA data
953 The latitude or longitude is coded this way in NMEA frames
954 dm.f coded as degrees + minutes + minute decimal
956 - d can be 1 or more char long. generally: 2 char long for latitude, 3 char long for longitude
957 - m is always 2 char long
958 - f can be 1 or more char long
959 This function converts this format in a unique unsigned long where 1 degree = 10 000 000
961 EOS increased the precision here, even if we think that the gps is not precise enough, with 10e5 precision it has 76cm resolution
962 with 10e7 it's around 1 cm now. Increasing it further is irrelevant, since even 1cm resolution is unrealistic, however increased
963 resolution also increased precision of nav calculations
964 static uint32_t GPS_coord_to_degrees(char *coordinateString)
967 uint8_t min, deg = 0;
968 uint16_t frac = 0, mult = 10000;
970 while (*p) { // parse the string until its end
972 frac += (*p - '0') * mult; // calculate only fractional part on up to 5 digits (d != s condition is true when the . is located)
976 d = p; // locate '.' char in the string
982 deg *= 10; // convert degrees : all chars before minutes ; for the first iteration, deg = 0
985 min = *(d - 1) - '0' + (*(d - 2) - '0') * 10; // convert minutes : 2 previous char before '.'
986 return deg * 10000000UL + (min * 100000UL + frac) * 10UL / 6;
992 static uint32_t grab_fields(char *src
, uint8_t mult
)
993 { // convert string to uint32
997 for (i
= 0; src
[i
] != 0; i
++) {
998 if ((i
== 0) && (src
[0] == '-')) { // detect negative sign
1000 continue; // jump to next character if the first one was a negative sign
1002 if (src
[i
] == '.') {
1011 if (src
[i
] >= '0' && src
[i
] <= '9') {
1012 tmp
+= src
[i
] - '0';
1015 return 0; // out of bounds
1018 return isneg
? -tmp
: tmp
; // handle negative altitudes
1021 typedef struct gpsDataNmea_s
{
1028 uint16_t ground_course
;
1033 static bool gpsNewFrameNMEA(char c
)
1035 static gpsDataNmea_t gps_Msg
;
1037 uint8_t frameOK
= 0;
1038 static uint8_t param
= 0, offset
= 0, parity
= 0;
1039 static char string
[15];
1040 static uint8_t checksum_param
, gps_frame
= NO_FRAME
;
1041 static uint8_t svMessageNum
= 0;
1042 uint8_t svSatNum
= 0, svPacketIdx
= 0, svSatParam
= 0;
1053 if (param
== 0) { //frame identification
1054 gps_frame
= NO_FRAME
;
1055 if (0 == strcmp(string
, "GPGGA") || 0 == strcmp(string
, "GNGGA")) {
1056 gps_frame
= FRAME_GGA
;
1057 } else if (0 == strcmp(string
, "GPRMC") || 0 == strcmp(string
, "GNRMC")) {
1058 gps_frame
= FRAME_RMC
;
1059 } else if (0 == strcmp(string
, "GPGSV")) {
1060 gps_frame
= FRAME_GSV
;
1064 switch (gps_frame
) {
1065 case FRAME_GGA
: //************* GPGGA FRAME parsing
1067 // case 1: // Time information
1070 gps_Msg
.latitude
= GPS_coord_to_degrees(string
);
1073 if (string
[0] == 'S')
1074 gps_Msg
.latitude
*= -1;
1077 gps_Msg
.longitude
= GPS_coord_to_degrees(string
);
1080 if (string
[0] == 'W')
1081 gps_Msg
.longitude
*= -1;
1084 gpsSetFixState(string
[0] > '0');
1087 gps_Msg
.numSat
= grab_fields(string
, 0);
1090 gps_Msg
.hdop
= grab_fields(string
, 1) * 100; // hdop
1093 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
1097 case FRAME_RMC
: //************* GPRMC FRAME parsing
1100 gps_Msg
.time
= grab_fields(string
, 2); // UTC time hhmmss.ss
1103 gps_Msg
.speed
= ((grab_fields(string
, 1) * 5144L) / 1000L); // speed in cm/s added by Mis
1106 gps_Msg
.ground_course
= (grab_fields(string
, 1)); // ground course deg * 10
1109 gps_Msg
.date
= grab_fields(string
, 0); // date dd/mm/yy
1116 // Total number of messages of this type in this cycle
1120 svMessageNum
= grab_fields(string
, 0);
1123 // Total number of SVs visible
1124 GPS_numCh
= grab_fields(string
, 0);
1125 if (GPS_numCh
> GPS_SV_MAXSATS_LEGACY
) {
1126 GPS_numCh
= GPS_SV_MAXSATS_LEGACY
;
1133 svPacketIdx
= (param
- 4) / 4 + 1; // satellite number in packet, 1-4
1134 svSatNum
= svPacketIdx
+ (4 * (svMessageNum
- 1)); // global satellite number
1135 svSatParam
= param
- 3 - (4 * (svPacketIdx
- 1)); // parameter number for satellite
1137 if (svSatNum
> GPS_SV_MAXSATS_LEGACY
)
1140 switch (svSatParam
) {
1143 GPS_svinfo_chn
[svSatNum
- 1] = svSatNum
;
1144 GPS_svinfo_svid
[svSatNum
- 1] = grab_fields(string
, 0);
1147 // Elevation, in degrees, 90 maximum
1150 // Azimuth, degrees from True North, 000 through 359
1153 // SNR, 00 through 99 dB (null when not tracking)
1154 GPS_svinfo_cno
[svSatNum
- 1] = grab_fields(string
, 0);
1155 GPS_svinfo_quality
[svSatNum
- 1] = 0; // only used by ublox
1159 GPS_svInfoReceivedCount
++;
1173 if (checksum_param
) { //parity checksum
1175 uint8_t checksum
= 16 * ((string
[0] >= 'A') ? string
[0] - 'A' + 10 : string
[0] - '0') + ((string
[1] >= 'A') ? string
[1] - 'A' + 10 : string
[1] - '0');
1176 if (checksum
== parity
) {
1177 *gpsPacketLogChar
= LOG_IGNORED
;
1179 switch (gps_frame
) {
1181 *gpsPacketLogChar
= LOG_NMEA_GGA
;
1183 if (STATE(GPS_FIX
)) {
1184 gpsSol
.llh
.lat
= gps_Msg
.latitude
;
1185 gpsSol
.llh
.lon
= gps_Msg
.longitude
;
1186 gpsSol
.numSat
= gps_Msg
.numSat
;
1187 gpsSol
.llh
.altCm
= gps_Msg
.altitudeCm
;
1188 gpsSol
.hdop
= gps_Msg
.hdop
;
1192 *gpsPacketLogChar
= LOG_NMEA_RMC
;
1193 gpsSol
.groundSpeed
= gps_Msg
.speed
;
1194 gpsSol
.groundCourse
= gps_Msg
.ground_course
;
1196 // This check will miss 00:00:00.00, but we shouldn't care - next report will be valid
1197 if(!rtcHasTime() && gps_Msg
.date
!= 0 && gps_Msg
.time
!= 0) {
1198 dateTime_t temp_time
;
1199 temp_time
.year
= (gps_Msg
.date
% 100) + 2000;
1200 temp_time
.month
= (gps_Msg
.date
/ 100) % 100;
1201 temp_time
.day
= (gps_Msg
.date
/ 10000) % 100;
1202 temp_time
.hours
= (gps_Msg
.time
/ 1000000) % 100;
1203 temp_time
.minutes
= (gps_Msg
.time
/ 10000) % 100;
1204 temp_time
.seconds
= (gps_Msg
.time
/ 100) % 100;
1205 temp_time
.millis
= (gps_Msg
.time
& 100) * 10;
1206 rtcSetDateTime(&temp_time
);
1212 *gpsPacketLogChar
= LOG_ERROR
;
1219 string
[offset
++] = c
;
1220 if (!checksum_param
)
1225 #endif // USE_GPS_NMEA
1227 #ifdef USE_GPS_UBLOX
1230 uint32_t time
; // GPS msToW
1233 int32_t altitude_ellipsoid
;
1234 int32_t altitudeMslMm
;
1235 uint32_t horizontal_accuracy
;
1236 uint32_t vertical_accuracy
;
1240 uint32_t time
; // GPS msToW
1243 uint8_t differential_status
;
1245 uint32_t time_to_first_fix
;
1246 uint32_t uptime
; // milliseconds
1258 uint32_t position_accuracy_3d
;
1259 int32_t ecef_x_velocity
;
1260 int32_t ecef_y_velocity
;
1261 int32_t ecef_z_velocity
;
1262 uint32_t speed_accuracy
;
1263 uint16_t position_DOP
;
1299 uint8_t reserved0
[5];
1306 uint32_t time
; // GPS msToW
1313 uint32_t speed_accuracy
;
1314 uint32_t heading_accuracy
;
1318 uint8_t chn
; // Channel number, 255 for SVx not assigned to channel
1319 uint8_t svid
; // Satellite ID
1320 uint8_t flags
; // Bitmask
1321 uint8_t quality
; // Bitfield
1322 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
1323 uint8_t elev
; // Elevation in integer degrees
1324 int16_t azim
; // Azimuth in integer degrees
1325 int32_t prRes
; // Pseudo range residual in centimetres
1326 } ubx_nav_svinfo_channel
;
1330 uint8_t svId
; // Satellite ID
1331 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
1332 int8_t elev
; // Elevation in integer degrees
1333 int16_t azim
; // Azimuth in integer degrees
1334 int16_t prRes
; // Pseudo range residual in decimetres
1335 uint32_t flags
; // Bitmask
1339 uint32_t time
; // GPS Millisecond time of week
1340 uint8_t numCh
; // Number of channels
1341 uint8_t globalFlags
; // Bitmask, Chip hardware generation 0:Antaris, 1:u-blox 5, 2:u-blox 6
1342 uint16_t reserved2
; // Reserved
1343 ubx_nav_svinfo_channel channel
[GPS_SV_MAXSATS_M8N
]; // 32 satellites * 12 byte
1347 uint32_t time
; // GPS Millisecond time of week
1350 uint8_t reserved0
[2];
1351 ubx_nav_sat_sv svs
[GPS_SV_MAXSATS_M9N
];
1355 uint8_t clsId
; // Class ID of the acknowledged message
1356 uint8_t msgId
; // Message ID of the acknowledged message
1361 FIX_DEAD_RECKONING
= 1,
1364 FIX_GPS_DEAD_RECKONING
= 4,
1369 NAV_STATUS_FIX_VALID
= 1,
1370 NAV_STATUS_TIME_WEEK_VALID
= 4,
1371 NAV_STATUS_TIME_SECOND_VALID
= 8
1372 } ubx_nav_status_bits
;
1377 } ubx_nav_pvt_valid
;
1379 // Packet checksum accumulators
1380 static uint8_t _ck_a
;
1381 static uint8_t _ck_b
;
1383 // State machine state
1384 static bool _skip_packet
;
1385 static uint8_t _step
;
1386 static uint8_t _msg_id
;
1387 static uint16_t _payload_length
;
1388 static uint16_t _payload_counter
;
1390 static bool next_fix
;
1391 static uint8_t _class
;
1393 // do we have new position information?
1394 static bool _new_position
;
1396 // do we have new speed information?
1397 static bool _new_speed
;
1399 // Example packet sizes from UBlox u-center from a Glonass capable GPS receiver.
1400 //15:17:55 R -> UBX NAV-STATUS, Size 24, 'Navigation Status'
1401 //15:17:55 R -> UBX NAV-POSLLH, Size 36, 'Geodetic Position'
1402 //15:17:55 R -> UBX NAV-VELNED, Size 44, 'Velocity in WGS 84'
1403 //15:17:55 R -> UBX NAV-CLOCK, Size 28, 'Clock Status'
1404 //15:17:55 R -> UBX NAV-AOPSTATUS, Size 24, 'AOP Status'
1405 //15:17:55 R -> UBX 03-09, Size 208, 'Unknown'
1406 //15:17:55 R -> UBX 03-10, Size 336, 'Unknown'
1407 //15:17:55 R -> UBX NAV-SOL, Size 60, 'Navigation Solution'
1408 //15:17:55 R -> UBX NAV, Size 100, 'Navigation'
1409 //15:17:55 R -> UBX NAV-SVINFO, Size 328, 'Satellite Status and Information'
1411 // from the UBlox9 document, the largest payout we receive is the NAV-SAT and the payload size
1412 // is calculated as 8 + 12*numCh. numCh in the case of a M9N is 42.
1413 #define UBLOX_PAYLOAD_SIZE (8 + 12 * 42)
1418 ubx_nav_posllh posllh
;
1419 ubx_nav_status status
;
1420 ubx_nav_solution solution
;
1421 ubx_nav_velned velned
;
1423 ubx_nav_svinfo svinfo
;
1427 uint8_t bytes
[UBLOX_PAYLOAD_SIZE
];
1430 void _update_checksum(uint8_t *data
, uint8_t len
, uint8_t *ck_a
, uint8_t *ck_b
)
1439 static bool UBLOX_parse_gps(void)
1443 *gpsPacketLogChar
= LOG_IGNORED
;
1447 *gpsPacketLogChar
= LOG_UBLOX_POSLLH
;
1448 //i2c_dataset.time = _buffer.posllh.time;
1449 gpsSol
.llh
.lon
= _buffer
.posllh
.longitude
;
1450 gpsSol
.llh
.lat
= _buffer
.posllh
.latitude
;
1451 gpsSol
.llh
.altCm
= _buffer
.posllh
.altitudeMslMm
/ 10; //alt in cm
1452 gpsSetFixState(next_fix
);
1453 _new_position
= true;
1456 *gpsPacketLogChar
= LOG_UBLOX_STATUS
;
1457 next_fix
= (_buffer
.status
.fix_status
& NAV_STATUS_FIX_VALID
) && (_buffer
.status
.fix_type
== FIX_3D
);
1459 DISABLE_STATE(GPS_FIX
);
1462 *gpsPacketLogChar
= LOG_UBLOX_SOL
;
1463 next_fix
= (_buffer
.solution
.fix_status
& NAV_STATUS_FIX_VALID
) && (_buffer
.solution
.fix_type
== FIX_3D
);
1465 DISABLE_STATE(GPS_FIX
);
1466 gpsSol
.numSat
= _buffer
.solution
.satellites
;
1467 gpsSol
.hdop
= _buffer
.solution
.position_DOP
;
1469 //set clock, when gps time is available
1470 if(!rtcHasTime() && (_buffer
.solution
.fix_status
& NAV_STATUS_TIME_SECOND_VALID
) && (_buffer
.solution
.fix_status
& NAV_STATUS_TIME_WEEK_VALID
)) {
1471 //calculate rtctime: week number * ms in a week + ms of week + fractions of second + offset to UNIX reference year - 18 leap seconds
1472 rtcTime_t temp_time
= (((int64_t) _buffer
.solution
.week
)*7*24*60*60*1000) + _buffer
.solution
.time
+ (_buffer
.solution
.time_nsec
/1000000) + 315964800000LL - 18000;
1478 *gpsPacketLogChar
= LOG_UBLOX_VELNED
;
1479 gpsSol
.speed3d
= _buffer
.velned
.speed_3d
; // cm/s
1480 gpsSol
.groundSpeed
= _buffer
.velned
.speed_2d
; // cm/s
1481 gpsSol
.groundCourse
= (uint16_t) (_buffer
.velned
.heading_2d
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
1485 *gpsPacketLogChar
= LOG_UBLOX_SOL
;
1486 next_fix
= (_buffer
.pvt
.flags
& NAV_STATUS_FIX_VALID
) && (_buffer
.pvt
.fixType
== FIX_3D
);
1487 gpsSol
.llh
.lon
= _buffer
.pvt
.lon
;
1488 gpsSol
.llh
.lat
= _buffer
.pvt
.lat
;
1489 gpsSol
.llh
.altCm
= _buffer
.pvt
.hMSL
/ 10; //alt in cm
1490 gpsSetFixState(next_fix
);
1491 _new_position
= true;
1492 gpsSol
.numSat
= _buffer
.pvt
.numSV
;
1493 gpsSol
.hdop
= _buffer
.pvt
.pDOP
;
1494 gpsSol
.speed3d
= (uint16_t) sqrtf(powf(_buffer
.pvt
.gSpeed
/ 10, 2.0f
) + powf(_buffer
.pvt
.velD
/ 10, 2.0f
));
1495 gpsSol
.groundSpeed
= _buffer
.pvt
.gSpeed
/ 10; // cm/s
1496 gpsSol
.groundCourse
= (uint16_t) (_buffer
.pvt
.headMot
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
1499 //set clock, when gps time is available
1500 if (!rtcHasTime() && (_buffer
.pvt
.valid
& NAV_VALID_DATE
) && (_buffer
.pvt
.valid
& NAV_VALID_TIME
)) {
1502 dt
.year
= _buffer
.pvt
.year
;
1503 dt
.month
= _buffer
.pvt
.month
;
1504 dt
.day
= _buffer
.pvt
.day
;
1505 dt
.hours
= _buffer
.pvt
.hour
;
1506 dt
.minutes
= _buffer
.pvt
.min
;
1507 dt
.seconds
= _buffer
.pvt
.sec
;
1508 dt
.millis
= (_buffer
.pvt
.nano
> 0) ? _buffer
.pvt
.nano
/ 1000 : 0; //up to 5ms of error
1509 rtcSetDateTime(&dt
);
1514 *gpsPacketLogChar
= LOG_UBLOX_SVINFO
;
1515 GPS_numCh
= _buffer
.svinfo
.numCh
;
1516 // 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
1517 // save up to GPS_SV_MAXSATS_LEGACY channels so the BF Configurator knows it's receiving the old sat list info format.
1518 if (GPS_numCh
> GPS_SV_MAXSATS_LEGACY
)
1519 GPS_numCh
= GPS_SV_MAXSATS_LEGACY
;
1520 for (i
= 0; i
< GPS_numCh
; i
++) {
1521 GPS_svinfo_chn
[i
] = _buffer
.svinfo
.channel
[i
].chn
;
1522 GPS_svinfo_svid
[i
] = _buffer
.svinfo
.channel
[i
].svid
;
1523 GPS_svinfo_quality
[i
] =_buffer
.svinfo
.channel
[i
].quality
;
1524 GPS_svinfo_cno
[i
] = _buffer
.svinfo
.channel
[i
].cno
;
1526 for (i
= GPS_numCh
; i
< GPS_SV_MAXSATS_LEGACY
; i
++) {
1527 GPS_svinfo_chn
[i
] = 0;
1528 GPS_svinfo_svid
[i
] = 0;
1529 GPS_svinfo_quality
[i
] = 0;
1530 GPS_svinfo_cno
[i
] = 0;
1532 GPS_svInfoReceivedCount
++;
1535 *gpsPacketLogChar
= LOG_UBLOX_SVINFO
; // The logger won't show this is NAV-SAT instead of NAV-SVINFO
1536 GPS_numCh
= _buffer
.sat
.numSvs
;
1537 // We can receive here upto GPS_SV_MAXSATS_M9N channels, but since the majority of receivers currently in use are M8N or older,
1538 // 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
1539 // all their channel information on BF Configurator. When M9N's are more widespread it would be a good time to increase those arrays.
1540 if (GPS_numCh
> GPS_SV_MAXSATS_M8N
)
1541 GPS_numCh
= GPS_SV_MAXSATS_M8N
;
1542 for (i
= 0; i
< GPS_numCh
; i
++) {
1543 GPS_svinfo_chn
[i
] = _buffer
.sat
.svs
[i
].gnssId
;
1544 GPS_svinfo_svid
[i
] = _buffer
.sat
.svs
[i
].svId
;
1545 GPS_svinfo_cno
[i
] = _buffer
.sat
.svs
[i
].cno
;
1546 GPS_svinfo_quality
[i
] =_buffer
.sat
.svs
[i
].flags
;
1548 for (i
= GPS_numCh
; i
< GPS_SV_MAXSATS_M8N
; i
++) {
1549 GPS_svinfo_chn
[i
] = 255;
1550 GPS_svinfo_svid
[i
] = 0;
1551 GPS_svinfo_quality
[i
] = 0;
1552 GPS_svinfo_cno
[i
] = 0;
1555 // Setting the number of channels higher than GPS_SV_MAXSATS_LEGACY is the only way to tell BF Configurator we're sending the
1556 // enhanced sat list info without changing the MSP protocol. Also, we're sending the complete list each time even if it's empty, so
1557 // BF Conf can erase old entries shown on screen when channels are removed from the list.
1558 GPS_numCh
= GPS_SV_MAXSATS_M8N
;
1559 GPS_svInfoReceivedCount
++;
1563 bool isSBASenabled
= false;
1564 bool isM8NwithDefaultConfig
= false;
1566 if ((_buffer
.gnss
.numConfigBlocks
>= 2) &&
1567 (_buffer
.gnss
.configblocks
[1].gnssId
== 1) && //SBAS
1568 (_buffer
.gnss
.configblocks
[1].flags
& UBLOX_GNSS_ENABLE
)) { //enabled
1570 isSBASenabled
= true;
1573 if ((_buffer
.gnss
.numTrkChHw
== 32) && //M8N
1574 (_buffer
.gnss
.numTrkChUse
== 32) &&
1575 (_buffer
.gnss
.numConfigBlocks
== 7) &&
1576 (_buffer
.gnss
.configblocks
[2].gnssId
== 2) && //Galileo
1577 (_buffer
.gnss
.configblocks
[2].resTrkCh
== 4) && //min channels
1578 (_buffer
.gnss
.configblocks
[2].maxTrkCh
== 8) && //max channels
1579 !(_buffer
.gnss
.configblocks
[2].flags
& UBLOX_GNSS_ENABLE
)) { //disabled
1581 isM8NwithDefaultConfig
= true;
1584 const uint16_t messageSize
= 4 + (_buffer
.gnss
.numConfigBlocks
* sizeof(ubx_configblock
));
1586 ubx_message tx_buffer
;
1587 memcpy(&tx_buffer
.payload
, &_buffer
, messageSize
);
1589 if (isSBASenabled
&& (gpsConfig()->sbasMode
== SBAS_NONE
)) {
1590 tx_buffer
.payload
.cfg_gnss
.configblocks
[1].flags
&= ~UBLOX_GNSS_ENABLE
; //Disable SBAS
1593 if (isM8NwithDefaultConfig
&& gpsConfig()->gps_ublox_use_galileo
) {
1594 tx_buffer
.payload
.cfg_gnss
.configblocks
[2].flags
|= UBLOX_GNSS_ENABLE
; //Enable Galileo
1597 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_GNSS
, messageSize
);
1601 if ((gpsData
.ackState
== UBLOX_ACK_WAITING
) && (_buffer
.ack
.msgId
== gpsData
.ackWaitingMsgId
)) {
1602 gpsData
.ackState
= UBLOX_ACK_GOT_ACK
;
1606 if ((gpsData
.ackState
== UBLOX_ACK_WAITING
) && (_buffer
.ack
.msgId
== gpsData
.ackWaitingMsgId
)) {
1607 gpsData
.ackState
= UBLOX_ACK_GOT_NACK
;
1614 // we only return true when we get new position and speed data
1615 // this ensures we don't use stale data
1616 if (_new_position
&& _new_speed
) {
1617 _new_speed
= _new_position
= false;
1623 static bool gpsNewFrameUBLOX(uint8_t data
)
1625 bool parsed
= false;
1628 case 0: // Sync char 1 (0xB5)
1629 if (PREAMBLE1
== data
) {
1630 _skip_packet
= false;
1634 case 1: // Sync char 2 (0x62)
1635 if (PREAMBLE2
!= data
) {
1644 _ck_b
= _ck_a
= data
; // reset the checksum accumulators
1648 _ck_b
+= (_ck_a
+= data
); // checksum byte
1650 #if DEBUG_UBLOX_FRAMES
1654 case 4: // Payload length (part 1)
1656 _ck_b
+= (_ck_a
+= data
); // checksum byte
1657 _payload_length
= data
; // payload length low byte
1659 case 5: // Payload length (part 2)
1661 _ck_b
+= (_ck_a
+= data
); // checksum byte
1662 _payload_length
+= (uint16_t)(data
<< 8);
1663 #if DEBUG_UBLOX_FRAMES
1664 debug
[3] = _payload_length
;
1666 if (_payload_length
> UBLOX_PAYLOAD_SIZE
) {
1667 _skip_packet
= true;
1669 _payload_counter
= 0; // prepare to receive payload
1670 if (_payload_length
== 0) {
1675 _ck_b
+= (_ck_a
+= data
); // checksum byte
1676 if (_payload_counter
< UBLOX_PAYLOAD_SIZE
) {
1677 _buffer
.bytes
[_payload_counter
] = data
;
1679 if (++_payload_counter
>= _payload_length
) {
1685 if (_ck_a
!= data
) {
1686 _skip_packet
= true; // bad checksum
1695 if (_ck_b
!= data
) {
1696 *gpsPacketLogChar
= LOG_ERROR
;
1698 break; // bad checksum
1704 *gpsPacketLogChar
= LOG_SKIPPED
;
1708 if (UBLOX_parse_gps()) {
1714 #endif // USE_GPS_UBLOX
1716 static void gpsHandlePassthrough(uint8_t data
)
1719 #ifdef USE_DASHBOARD
1720 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
1721 dashboardUpdate(micros());
1727 void gpsEnablePassthrough(serialPort_t
*gpsPassthroughPort
)
1729 waitForSerialPortToFinishTransmitting(gpsPort
);
1730 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
1732 if (!(gpsPort
->mode
& MODE_TX
))
1733 serialSetMode(gpsPort
, gpsPort
->mode
| MODE_TX
);
1735 #ifdef USE_DASHBOARD
1736 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
1737 dashboardShowFixedPage(PAGE_GPS
);
1741 serialPassthrough(gpsPort
, gpsPassthroughPort
, &gpsHandlePassthrough
, NULL
);
1744 float GPS_scaleLonDown
= 1.0f
; // this is used to offset the shrinking longitude as we go towards the poles
1746 void GPS_calc_longitude_scaling(int32_t lat
)
1748 float rads
= (fabsf((float)lat
) / 10000000.0f
) * 0.0174532925f
;
1749 GPS_scaleLonDown
= cos_approx(rads
);
1752 ////////////////////////////////////////////////////////////////////////////////////
1753 // Calculate the distance flown and vertical speed from gps position data
1755 static void GPS_calculateDistanceFlownVerticalSpeed(bool initialize
)
1757 static int32_t lastCoord
[2] = { 0, 0 };
1758 static int32_t lastAlt
;
1759 static int32_t lastMillis
;
1761 int currentMillis
= millis();
1764 GPS_distanceFlownInCm
= 0;
1765 GPS_verticalSpeedInCmS
= 0;
1767 if (STATE(GPS_FIX_HOME
) && ARMING_FLAG(ARMED
)) {
1768 uint16_t speed
= gpsConfig()->gps_use_3d_speed
? gpsSol
.speed3d
: gpsSol
.groundSpeed
;
1769 // Only add up movement when speed is faster than minimum threshold
1770 if (speed
> GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S
) {
1773 GPS_distance_cm_bearing(&gpsSol
.llh
.lat
, &gpsSol
.llh
.lon
, &lastCoord
[GPS_LATITUDE
], &lastCoord
[GPS_LONGITUDE
], &dist
, &dir
);
1774 if (gpsConfig()->gps_use_3d_speed
) {
1775 dist
= sqrtf(powf(gpsSol
.llh
.altCm
- lastAlt
, 2.0f
) + powf(dist
, 2.0f
));
1777 GPS_distanceFlownInCm
+= dist
;
1780 GPS_verticalSpeedInCmS
= (gpsSol
.llh
.altCm
- lastAlt
) * 1000 / (currentMillis
- lastMillis
);
1781 GPS_verticalSpeedInCmS
= constrain(GPS_verticalSpeedInCmS
, -1500, 1500);
1783 lastCoord
[GPS_LONGITUDE
] = gpsSol
.llh
.lon
;
1784 lastCoord
[GPS_LATITUDE
] = gpsSol
.llh
.lat
;
1785 lastAlt
= gpsSol
.llh
.altCm
;
1786 lastMillis
= currentMillis
;
1789 void GPS_reset_home_position(void)
1791 if (!STATE(GPS_FIX_HOME
) || !gpsConfig()->gps_set_home_point_once
) {
1792 if (STATE(GPS_FIX
) && gpsSol
.numSat
>= 5) {
1793 GPS_home
[GPS_LATITUDE
] = gpsSol
.llh
.lat
;
1794 GPS_home
[GPS_LONGITUDE
] = gpsSol
.llh
.lon
;
1795 GPS_calc_longitude_scaling(gpsSol
.llh
.lat
); // need an initial value for distance and bearing calc
1796 // Set ground altitude
1797 ENABLE_STATE(GPS_FIX_HOME
);
1800 GPS_calculateDistanceFlownVerticalSpeed(true); //Initialize
1803 ////////////////////////////////////////////////////////////////////////////////////
1804 #define DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR_IN_HUNDREDS_OF_KILOMETERS 1.113195f
1805 #define TAN_89_99_DEGREES 5729.57795f
1806 // Get distance between two points in cm
1807 // Get bearing from pos1 to pos2, returns an 1deg = 100 precision
1808 void GPS_distance_cm_bearing(int32_t *currentLat1
, int32_t *currentLon1
, int32_t *destinationLat2
, int32_t *destinationLon2
, uint32_t *dist
, int32_t *bearing
)
1810 float dLat
= *destinationLat2
- *currentLat1
; // difference of latitude in 1/10 000 000 degrees
1811 float dLon
= (float)(*destinationLon2
- *currentLon1
) * GPS_scaleLonDown
;
1812 *dist
= sqrtf(sq(dLat
) + sq(dLon
)) * DISTANCE_BETWEEN_TWO_LONGITUDE_POINTS_AT_EQUATOR_IN_HUNDREDS_OF_KILOMETERS
;
1814 *bearing
= 9000.0f
+ atan2_approx(-dLat
, dLon
) * TAN_89_99_DEGREES
; // Convert the output radians to 100xdeg
1819 void GPS_calculateDistanceAndDirectionToHome(void)
1821 if (STATE(GPS_FIX_HOME
)) { // If we don't have home set, do not display anything
1824 GPS_distance_cm_bearing(&gpsSol
.llh
.lat
, &gpsSol
.llh
.lon
, &GPS_home
[GPS_LATITUDE
], &GPS_home
[GPS_LONGITUDE
], &dist
, &dir
);
1825 GPS_distanceToHome
= dist
/ 100;
1826 GPS_directionToHome
= dir
/ 100;
1828 GPS_distanceToHome
= 0;
1829 GPS_directionToHome
= 0;
1833 void onGpsNewData(void)
1835 if (!(STATE(GPS_FIX
) && gpsSol
.numSat
>= 5)) {
1840 // Calculate time delta for navigation loop, range 0-1.0f, in seconds
1842 // Time for calculating x,y speed and navigation pids
1843 static uint32_t nav_loopTimer
;
1844 dTnav
= (float)(millis() - nav_loopTimer
) / 1000.0f
;
1845 nav_loopTimer
= millis();
1846 // prevent runup from bad GPS
1847 dTnav
= MIN(dTnav
, 1.0f
);
1849 GPS_calculateDistanceAndDirectionToHome();
1850 if (ARMING_FLAG(ARMED
)) {
1851 GPS_calculateDistanceFlownVerticalSpeed(false);
1854 #ifdef USE_GPS_RESCUE
1859 void gpsSetFixState(bool state
)
1862 ENABLE_STATE(GPS_FIX
);
1863 ENABLE_STATE(GPS_FIX_EVER
);
1865 DISABLE_STATE(GPS_FIX
);