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 "drivers/light_led.h"
42 #include "drivers/time.h"
44 #include "io/beeper.h"
46 #include "io/dashboard.h"
50 #include "io/gps_virtual.h"
52 #include "io/serial.h"
54 #include "config/config.h"
56 #include "fc/gps_lap_timer.h"
57 #include "fc/runtime_config.h"
59 #include "flight/gps_rescue.h"
60 #include "flight/imu.h"
61 #include "flight/pid.h"
63 #include "scheduler/scheduler.h"
65 #include "sensors/sensors.h"
66 #include "common/typeconversion.h"
68 // **********************
70 // **********************
71 gpsLocation_t GPS_home_llh
;
72 uint16_t GPS_distanceToHome
; // distance to home point in meters
73 uint32_t GPS_distanceToHomeCm
;
74 int16_t GPS_directionToHome
; // direction to home or hol point in degrees * 10
75 uint32_t GPS_distanceFlownInCm
; // distance flown since armed in centimeters
77 #define GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S 15 // 0.54 km/h 0.335 mph
79 gpsSolutionData_t gpsSol
;
80 uint8_t GPS_update
= 0; // toogle to distinct a GPS position update (directly or via MSP)
82 uint8_t GPS_numCh
; // Details on numCh/svinfo in gps.h
83 GPS_svinfo_t GPS_svinfo
[GPS_SV_MAXSATS_M8N
];
85 // GPS LOST_COMMUNICATION timeout in ms (max time between received nav solutions)
86 #define GPS_TIMEOUT_MS 2500
87 // Timeout for waiting for an ACK or NAK response to a configuration command
88 #define UBLOX_ACK_TIMEOUT_MS 150
89 // Time allowed for module to respond to baud rate change during initial configuration
90 #define GPS_CONFIG_BAUD_CHANGE_INTERVAL 330 // Time to wait, in ms, between 'test this baud rate' messages
91 #define GPS_CONFIG_CHANGE_INTERVAL 110 // Time to wait, in ms, between CONFIG steps
92 #define GPS_BAUDRATE_TEST_COUNT 3 // Number of times to repeat the test message when setting baudrate
93 #define GPS_RECV_TIME_MAX 25 // Max permitted time, in us, for the Receive Data process
94 // Decay the estimated max task duration by 1/(1 << GPS_TASK_DECAY_SHIFT) on every invocation
95 #define GPS_TASK_DECAY_SHIFT 9 // Smoothing factor for GPS task re-scheduler
97 static serialPort_t
*gpsPort
;
98 static float gpsDataIntervalSeconds
= 0.1f
;
99 static float gpsDataFrequencyHz
= 10.0f
;
101 static uint16_t currentGpsStamp
= 0; // logical timer for received position update
103 typedef struct gpsInitData_s
{
104 uint8_t baudrateIndex
; // see baudRate_e
108 // UBX will cycle through these until valid data is received
109 static const gpsInitData_t gpsInitData
[] = {
110 { BAUD_230400
, "$PUBX,41,1,0003,0001,230400,0*1C\r\n" },
111 { BAUD_115200
, "$PUBX,41,1,0003,0001,115200,0*1E\r\n" },
112 { BAUD_57600
, "$PUBX,41,1,0003,0001,57600,0*2D\r\n" },
113 { BAUD_38400
, "$PUBX,41,1,0003,0001,38400,0*26\r\n" },
114 { BAUD_19200
, "$PUBX,41,1,0003,0001,19200,0*23\r\n" },
115 { BAUD_9600
, "$PUBX,41,1,0003,0001,9600,0*16\r\n" }
118 #define DEFAULT_BAUD_RATE_INDEX 0
121 #define MAX_VALSET_SIZE 128
130 CLASS_NMEA_STD
= 0xf0,
133 MSG_NAV_POSLLH
= 0x02,
134 MSG_NAV_STATUS
= 0x03,
138 MSG_NAV_VELNED
= 0x12,
139 MSG_NAV_SVINFO
= 0x30,
141 MSG_CFG_VALSET
= 0x8a,
142 MSG_CFG_VALGET
= 0x8b,
146 MSG_CFG_SET_RATE
= 0x01,
148 MSG_CFG_NAV_SETTINGS
= 0x24,
149 MSG_CFG_NAVX_SETTINGS
= 0x23,
159 } ubxProtocolBytes_e
;
162 UBX_POWER_MODE_FULL
= 0x00,
163 UBX_POWER_MODE_PSMOO
= 0x01,
164 UBX_POWER_MODE_PSMCT
= 0x02,
167 #define UBLOX_MODE_ENABLED 0x1
168 #define UBLOX_MODE_TEST 0x2
170 #define UBLOX_USAGE_RANGE 0x1
171 #define UBLOX_USAGE_DIFFCORR 0x2
172 #define UBLOX_USAGE_INTEGRITY 0x4
174 #define UBLOX_GNSS_ENABLE 0x1
175 #define UBLOX_GNSS_DEFAULT_SIGCFGMASK 0x10000
177 #define UBLOX_GNSS_GPS 0x00
178 #define UBLOX_GNSS_SBAS 0x01
179 #define UBLOX_GNSS_GALILEO 0x02
180 #define UBLOX_GNSS_BEIDOU 0x03
181 #define UBLOX_GNSS_IMES 0x04
182 #define UBLOX_GNSS_QZSS 0x05
183 #define UBLOX_GNSS_GLONASS 0x06
185 typedef struct ubxHeader_s
{
193 typedef struct ubxConfigblock_s
{
201 typedef struct ubxPollMsg_s
{
206 typedef struct ubxCfgMsg_s
{
212 typedef struct ubxCfgRate_s
{
218 typedef struct ubxCfgValSet_s
{
222 uint8_t cfgData
[MAX_VALSET_SIZE
];
225 typedef struct ubxCfgValGet_s
{
229 uint8_t cfgData
[MAX_VALSET_SIZE
];
232 typedef struct ubxCfgSbas_s
{
240 typedef struct ubxCfgGnss_s
{
244 uint8_t numConfigBlocks
;
245 ubxConfigblock_t configblocks
[7];
248 typedef struct ubxCfgPms_s
{
250 uint8_t powerSetupValue
;
253 uint8_t reserved1
[2];
256 typedef struct ubxCfgNav5_s
{
261 uint32_t fixedAltVar
;
268 uint8_t staticHoldThresh
;
269 uint8_t dgnssTimeout
;
270 uint8_t cnoThreshNumSVs
;
272 uint8_t reserved0
[2];
273 uint16_t staticHoldMaxDist
;
275 uint8_t reserved1
[5];
278 typedef struct ubxCfgNav5x_s
{
282 uint8_t reserved0
[2];
288 uint8_t reserved2
[2];
290 uint16_t wknRollover
;
291 uint8_t sigAttenCompMode
;
293 uint8_t reserved4
[2];
294 uint8_t reserved5
[2];
297 uint8_t reserved6
[2];
298 uint8_t reserved7
[4];
299 uint8_t reserved8
[3];
303 typedef union ubxPayload_s
{
304 ubxPollMsg_t poll_msg
;
306 ubxCfgRate_t cfg_rate
;
307 ubxCfgValSet_t cfg_valset
;
308 ubxCfgValGet_t cfg_valget
;
309 ubxCfgNav5_t cfg_nav5
;
310 ubxCfgNav5x_t cfg_nav5x
;
311 ubxCfgSbas_t cfg_sbas
;
312 ubxCfgGnss_t cfg_gnss
;
316 typedef struct ubxMessage_s
{
318 ubxPayload_t payload
;
319 } __attribute__((packed
)) ubxMessage_t
;
322 UBLOX_DETECT_UNIT
, // 0
323 UBLOX_SLOW_NAV_RATE
, // 1.
324 UBLOX_MSG_DISABLE_NMEA
, // 2. Disable NMEA, config message
325 UBLOX_MSG_VGS
, // 3. VGS: Course over ground and Ground speed
326 UBLOX_MSG_GSV
, // 4. GSV: GNSS Satellites in View
327 UBLOX_MSG_GLL
, // 5. GLL: Latitude and longitude, with time of position fix and status
328 UBLOX_MSG_GGA
, // 6. GGA: Global positioning system fix data
329 UBLOX_MSG_GSA
, // 7. GSA: GNSS DOP and Active Satellites
330 UBLOX_MSG_RMC
, // 8. RMC: Recommended Minimum data
331 UBLOX_ACQUIRE_MODEL
, // 9
332 // UBLOX_CFG_ANA, // . ANA: if M10, enable autonomous mode : temporarily disabled.
333 UBLOX_SET_SBAS
, // 10. Sets SBAS
334 UBLOX_SET_PMS
, // 11. Sets Power Mode
335 UBLOX_MSG_NAV_PVT
, // 12. set NAV-PVT rate
336 UBLOX_MSG_SOL
, // 13. set SOL MSG rate
337 UBLOX_MSG_POSLLH
, // 14. set POSLLH MSG rate
338 UBLOX_MSG_STATUS
, // 15: set STATUS MSG rate
339 UBLOX_MSG_VELNED
, // 16. set VELNED MSG rate
340 UBLOX_MSG_DOP
, // 17. MSG_NAV_DOP
341 UBLOX_SAT_INFO
, // 18. MSG_NAV_SAT message
342 UBLOX_SET_NAV_RATE
, // 19. set to user requested GPS sample rate
343 UBLOX_MSG_CFG_GNSS
, // 20. For not SBAS or GALILEO
344 UBLOX_CONFIG_COMPLETE
// 21. Config finished, start receiving data
345 } ubloxStatePosition_e
;
347 baudRate_e initBaudRateIndex
;
348 size_t initBaudRateCycleCount
;
349 #endif // USE_GPS_UBLOX
354 // Functions & data used *only* in support of the dashboard device (OLED display).
355 // Note this should be refactored to move dashboard functionality to the dashboard module, and only have generic hooks in the gps module...
357 char dashboardGpsPacketLog
[GPS_PACKET_LOG_ENTRY_COUNT
]; // OLED display of a char for each packet type/event received.
358 char *dashboardGpsPacketLogCurrentChar
= dashboardGpsPacketLog
; // Current character of log being updated.
359 uint32_t dashboardGpsPacketCount
= 0; // Packet received count.
360 uint32_t dashboardGpsNavSvInfoRcvCount
= 0; // Count of times sat info updated.
362 static void shiftPacketLog(void)
364 memmove(dashboardGpsPacketLog
+ 1, dashboardGpsPacketLog
, (ARRAYLEN(dashboardGpsPacketLog
) - 1) * sizeof(dashboardGpsPacketLog
[0]));
367 static void logErrorToPacketLog(void)
370 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_ERROR
;
373 #endif // USE_DASHBOARD
375 static void gpsNewData(uint16_t c
);
377 static bool gpsNewFrameNMEA(char c
);
380 static bool gpsNewFrameUBLOX(uint8_t data
);
383 static void gpsSetState(gpsState_e state
)
385 gpsData
.lastNavMessage
= gpsData
.now
;
386 sensorsClear(SENSOR_GPS
);
387 gpsData
.state
= state
;
388 gpsData
.state_position
= 0;
389 gpsData
.state_ts
= gpsData
.now
;
390 gpsData
.ackState
= UBLOX_ACK_IDLE
;
395 gpsDataIntervalSeconds
= 0.1f
;
396 gpsData
.userBaudRateIndex
= 0;
397 gpsData
.timeouts
= 0;
398 gpsData
.state_ts
= millis();
400 gpsData
.ubloxUsingFlightModel
= false;
402 gpsData
.updateRateHz
= 10; // initialise at 10hz
403 gpsData
.platformVersion
= UBX_VERSION_UNDEF
;
407 memset(dashboardGpsPacketLog
, 0x00, sizeof(dashboardGpsPacketLog
));
410 // init gpsData structure. if we're not actually enabled, don't bother doing anything else
411 gpsSetState(GPS_STATE_UNKNOWN
);
413 if (gpsConfig()->provider
== GPS_MSP
|| gpsConfig()->provider
== GPS_VIRTUAL
) { // no serial ports used when GPS_MSP or GPS_VIRTUAL is configured
414 gpsSetState(GPS_STATE_INITIALIZED
);
418 const serialPortConfig_t
*gpsPortConfig
= findSerialPortConfig(FUNCTION_GPS
);
419 if (!gpsPortConfig
) {
423 // set the user's intended baud rate
424 initBaudRateIndex
= BAUD_COUNT
;
425 initBaudRateCycleCount
= 0;
426 gpsData
.userBaudRateIndex
= DEFAULT_BAUD_RATE_INDEX
;
427 for (unsigned i
= 0; i
< ARRAYLEN(gpsInitData
); i
++) {
428 if (gpsInitData
[i
].baudrateIndex
== gpsPortConfig
->gps_baudrateIndex
) {
429 gpsData
.userBaudRateIndex
= i
;
433 // the user's intended baud rate will be used as the initial baud rate when connecting
434 gpsData
.tempBaudRateIndex
= gpsData
.userBaudRateIndex
;
436 portMode_e mode
= MODE_RXTX
;
437 portOptions_e options
= SERIAL_NOT_INVERTED
;
439 #if defined(GPS_NMEA_TX_ONLY)
440 if (gpsConfig()->provider
== GPS_NMEA
) {
444 if (serialType(gpsPortConfig
->identifier
) == SERIALTYPE_UART
445 || serialType(gpsPortConfig
->identifier
) == SERIALTYPE_LPUART
) {
446 // TODO: SERIAL_CHECK_TX is broken on F7, disable it until it is fixed
447 #if !defined(STM32F7) || defined(USE_F7_CHECK_TX)
448 options
|= SERIAL_CHECK_TX
;
452 // no callback - buffer will be consumed in gpsUpdate()
453 gpsPort
= openSerialPort(gpsPortConfig
->identifier
, FUNCTION_GPS
, NULL
, NULL
, baudRates
[gpsInitData
[gpsData
.userBaudRateIndex
].baudrateIndex
], mode
, options
);
458 // signal GPS "thread" to initialize when it gets to it
459 gpsSetState(GPS_STATE_DETECT_BAUD
);
460 // NB gpsData.state_position is set to zero by gpsSetState(), requesting the fastest baud rate option first time around.
464 const uint8_t ubloxUTCStandardConfig_int
[5] = {
465 UBLOX_UTC_STANDARD_AUTO
,
466 UBLOX_UTC_STANDARD_USNO
,
467 UBLOX_UTC_STANDARD_EU
,
468 UBLOX_UTC_STANDARD_SU
,
469 UBLOX_UTC_STANDARD_NTSC
472 struct ubloxVersion_s ubloxVersionMap
[] = {
473 [UBX_VERSION_UNDEF
] = {~0, "UNKNOWN"},
474 [UBX_VERSION_M5
] = {0x00040005, "M5"},
475 [UBX_VERSION_M6
] = {0x00040007, "M6"},
476 [UBX_VERSION_M7
] = {0x00070000, "M7"},
477 [UBX_VERSION_M8
] = {0x00080000, "M8"},
478 [UBX_VERSION_M9
] = {0x00190000, "M9"},
479 [UBX_VERSION_M10
] = {0x000A0000, "M10"},
482 static uint8_t ubloxAddValSet(ubxMessage_t
* tx_buffer
, ubxValGetSetBytes_e key
, const uint8_t * payload
, const uint8_t offset
) {
484 switch((key
>> (8 * 3)) & 0xff) {
501 if (offset
+ 4 + len
> MAX_VALSET_SIZE
)
506 tx_buffer
->payload
.cfg_valset
.cfgData
[offset
+ 0] = (uint8_t)(key
>> (8 * 0));
507 tx_buffer
->payload
.cfg_valset
.cfgData
[offset
+ 1] = (uint8_t)(key
>> (8 * 1));
508 tx_buffer
->payload
.cfg_valset
.cfgData
[offset
+ 2] = (uint8_t)(key
>> (8 * 2));
509 tx_buffer
->payload
.cfg_valset
.cfgData
[offset
+ 3] = (uint8_t)(key
>> (8 * 3));
511 for (size_t i
= 0; i
< len
; ++i
) {
512 tx_buffer
->payload
.cfg_valset
.cfgData
[offset
+ 4 + i
] = payload
[i
];
518 // the following lines are not being used, because we are not currently sending ubloxValGet messages
520 static size_t ubloxAddValGet(ubxMessage_t
* tx_buffer
, ubxValGetSetBytes_e key
, size_t offset
) {
521 const uint8_t zeroes
[8] = {0};
523 return ubloxAddValSet(tx_buffer
, key
, zeroes
, offset
);
526 static size_t ubloxValGet(ubxMessage_t
* tx_buffer
, ubxValGetSetBytes_e key
, ubloxValLayer_e layer
)
528 tx_buffer
->header
.preamble1
= PREAMBLE1
;
529 tx_buffer
->header
.preamble2
= PREAMBLE2
;
530 tx_buffer
->header
.msg_class
= CLASS_CFG
;
531 tx_buffer
->header
.msg_id
= MSG_CFG_VALGET
;
533 tx_buffer
->payload
.cfg_valget
.version
= 1;
534 tx_buffer
->payload
.cfg_valget
.layer
= layer
;
535 tx_buffer
->payload
.cfg_valget
.position
= 0;
537 return ubloxAddValGet(tx_buffer
, key
, 0);
541 static uint8_t ubloxValSet(ubxMessage_t
* tx_buffer
, ubxValGetSetBytes_e key
, uint8_t * payload
, ubloxValLayer_e layer
) {
542 memset(&tx_buffer
->payload
.cfg_valset
, 0, sizeof(ubxCfgValSet_t
));
544 // tx_buffer->payload.cfg_valset.version = 0;
545 tx_buffer
->payload
.cfg_valset
.layer
= layer
;
546 // tx_buffer->payload.cfg_valset.reserved[0] = 0;
547 // tx_buffer->payload.cfg_valset.reserved[1] = 0;
549 return ubloxAddValSet(tx_buffer
, key
, payload
, 0);
552 static void ubloxSendByteUpdateChecksum(const uint8_t data
, uint8_t *checksumA
, uint8_t *checksumB
)
555 *checksumB
+= *checksumA
;
556 serialWrite(gpsPort
, data
);
559 static void ubloxSendDataUpdateChecksum(const ubxMessage_t
*msg
, uint8_t *checksumA
, uint8_t *checksumB
)
561 // CRC includes msg_class, msg_id, length and payload
562 // length is payload length only
563 const uint8_t *data
= (const uint8_t *)&msg
->header
.msg_class
;
564 uint16_t len
= msg
->header
.length
+ sizeof(msg
->header
.msg_class
) + sizeof(msg
->header
.msg_id
) + sizeof(msg
->header
.length
);
567 ubloxSendByteUpdateChecksum(*data
, checksumA
, checksumB
);
572 static void ubloxSendMessage(const ubxMessage_t
*msg
, bool skipAck
)
574 uint8_t checksumA
= 0, checksumB
= 0;
575 serialWrite(gpsPort
, msg
->header
.preamble1
);
576 serialWrite(gpsPort
, msg
->header
.preamble2
);
577 ubloxSendDataUpdateChecksum(msg
, &checksumA
, &checksumB
);
578 serialWrite(gpsPort
, checksumA
);
579 serialWrite(gpsPort
, checksumB
);
580 // Save state for ACK waiting
581 gpsData
.ackWaitingMsgId
= msg
->header
.msg_id
; //save message id for ACK
582 gpsData
.ackState
= skipAck
? UBLOX_ACK_GOT_ACK
: UBLOX_ACK_WAITING
;
583 gpsData
.lastMessageSent
= gpsData
.now
;
586 static void ubloxSendClassMessage(ubxProtocolBytes_e class_id
, ubxProtocolBytes_e msg_id
, uint16_t length
)
589 msg
.header
.preamble1
= PREAMBLE1
;
590 msg
.header
.preamble2
= PREAMBLE2
;
591 msg
.header
.msg_class
= class_id
;
592 msg
.header
.msg_id
= msg_id
;
593 msg
.header
.length
= length
;
594 ubloxSendMessage(&msg
, false);
597 static void ubloxSendConfigMessage(ubxMessage_t
*msg
, uint8_t msg_id
, uint8_t length
, bool skipAck
)
599 msg
->header
.preamble1
= PREAMBLE1
;
600 msg
->header
.preamble2
= PREAMBLE2
;
601 msg
->header
.msg_class
= CLASS_CFG
;
602 msg
->header
.msg_id
= msg_id
;
603 msg
->header
.length
= length
;
604 ubloxSendMessage(msg
, skipAck
);
607 static void ubloxSendPollMessage(uint8_t msg_id
)
610 msg
.header
.preamble1
= PREAMBLE1
;
611 msg
.header
.preamble2
= PREAMBLE2
;
612 msg
.header
.msg_class
= CLASS_CFG
;
613 msg
.header
.msg_id
= msg_id
;
614 msg
.header
.length
= 0;
615 ubloxSendMessage(&msg
, false);
618 static void ubloxSendNAV5Message(uint8_t model
) {
619 DEBUG_SET(DEBUG_GPS_CONNECTION
, 0, model
);
620 ubxMessage_t tx_buffer
;
621 if (gpsData
.ubloxM9orAbove
) {
623 payload
[0] = (model
== 0 ? 0 : model
+ 1);
624 size_t offset
= ubloxValSet(&tx_buffer
, CFG_NAVSPG_DYNMODEL
, payload
, UBX_VAL_LAYER_RAM
); // 5
626 // the commented out payload lines are those which only set the M9 or above module to default values.
628 // payload[0] = 3; // set 2D/3D fix mode to auto, which is the default
629 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_FIXMODE, payload, offset); // 10
631 payload
[0] = ubloxUTCStandardConfig_int
[gpsConfig()->gps_ublox_utc_standard
];
632 offset
+= ubloxAddValSet(&tx_buffer
, CFG_NAVSPG_UTCSTANDARD
, payload
, offset
); // 15
635 // payload[1] = (uint8_t)(0 >> (8 * 1));
636 // payload[2] = (uint8_t)(0 >> (8 * 2));
637 // payload[3] = (uint8_t)(0 >> (8 * 3)); // all payloads are zero, the default MSL for 2D fix
638 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_CONSTR_ALT, payload, offset); // 23
640 // payload[0] = (uint8_t)(10000 >> (8 * 0));
641 // payload[1] = (uint8_t)(10000 >> (8 * 1));
642 // payload[2] = (uint8_t)(10000 >> (8 * 2));
643 // payload[3] = (uint8_t)(10000 >> (8 * 3)); // // all payloads are 1000, the default 2D variance factor
644 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_CONSTR_ALTVAR, payload, offset); // 31
646 // payload[0] = 5; // sets the default minimum elevation in degrees to the default of 5
647 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_INFIL_MINELEV, payload, offset); // 36
649 // payload[0] = (uint8_t)(250 >> (8 * 0));
650 // payload[1] = (uint8_t)(250 >> (8 * 1)); // sets the output filter PDOP mask to default of 250
651 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_OUTFIL_PDOP, payload, offset); // 42
653 // payload[0] = (uint8_t)(250 >> (8 * 0));
654 // payload[1] = (uint8_t)(250 >> (8 * 1));
655 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_OUTFIL_TDOP, payload, offset); // 48
657 // payload[0] = (uint8_t)(100 >> (8 * 0));
658 // payload[1] = (uint8_t)(100 >> (8 * 1));
659 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_OUTFIL_PACC, payload, offset); // 54
661 // payload[0] = (uint8_t)(300 >> (8 * 0));
662 // payload[1] = (uint8_t)(300 >> (8 * 1));
663 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_OUTFIL_TACC, payload, offset); // 60
666 // offset += ubloxAddValSet(&tx_buffer, CFG_MOT_GNSSSPEED_THRS, payload, offset); // 65
668 // payload[0] = (uint8_t)(200 >> (8 * 0));
669 // payload[1] = (uint8_t)(200 >> (8 * 1));
670 // offset += ubloxAddValSet(&tx_buffer, CFG_MOT_GNSSDIST_THRS, payload, offset); // 71
672 // payload[0] = (uint8_t)(60 >> (8 * 0));
673 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_CONSTR_DGNSSTO, payload, offset); // 76
676 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_INFIL_NCNOTHRS, payload, offset); // 81
679 // offset += ubloxAddValSet(&tx_buffer, CFG_NAVSPG_INFIL_CNOTHRS, payload, offset); // 86
681 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_VALSET
, offsetof(ubxCfgValSet_t
, cfgData
) + offset
, true);
683 memset(&tx_buffer
, 0, sizeof(ubxMessage_t
));
684 tx_buffer
.payload
.cfg_nav5
.mask
= 0xFFFF;
685 tx_buffer
.payload
.cfg_nav5
.dynModel
= model
== 0 ? 0 : model
+ 1; //no model with value 1
686 tx_buffer
.payload
.cfg_nav5
.fixMode
= 3;
687 tx_buffer
.payload
.cfg_nav5
.fixedAlt
= 0;
688 tx_buffer
.payload
.cfg_nav5
.fixedAltVar
= 10000;
689 tx_buffer
.payload
.cfg_nav5
.minElev
= 5;
690 tx_buffer
.payload
.cfg_nav5
.drLimit
= 0;
691 tx_buffer
.payload
.cfg_nav5
.pDOP
= 250;
692 tx_buffer
.payload
.cfg_nav5
.tDOP
= 250;
693 tx_buffer
.payload
.cfg_nav5
.pAcc
= 100;
694 tx_buffer
.payload
.cfg_nav5
.tAcc
= 300;
695 tx_buffer
.payload
.cfg_nav5
.staticHoldThresh
= 0;
696 tx_buffer
.payload
.cfg_nav5
.dgnssTimeout
= 60;
697 tx_buffer
.payload
.cfg_nav5
.cnoThreshNumSVs
= 0;
698 tx_buffer
.payload
.cfg_nav5
.cnoThresh
= 0;
699 tx_buffer
.payload
.cfg_nav5
.staticHoldMaxDist
= 200;
700 tx_buffer
.payload
.cfg_nav5
.utcStandard
= ubloxUTCStandardConfig_int
[gpsConfig()->gps_ublox_utc_standard
];
702 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_NAV_SETTINGS
, sizeof(ubxCfgNav5_t
), false);
706 // *** Assist Now Autonomous temporarily disabled until a subsequent PR either includes, or removes it ***
707 // static void ubloxSendNavX5Message(void) {
708 // ubxMessage_t tx_buffer;
710 // if (gpsData.ubloxM9orAbove) {
711 // uint8_t payload[1];
713 // size_t offset = ubloxValSet(&tx_buffer, CFG_ANA_USE_ANA, payload, UBX_VAL_LAYER_RAM); // 5
715 // ubloxSendConfigMessage(&tx_buffer, MSG_CFG_VALSET, offsetof(ubxCfgValSet_t, cfgData) + offset, true);
717 // memset(&tx_buffer, 0, sizeof(ubxMessage_t));
719 // tx_buffer.payload.cfg_nav5x.version = 0x0002;
721 // tx_buffer.payload.cfg_nav5x.mask1 = 0x4000;
722 // tx_buffer.payload.cfg_nav5x.mask2 = 0x0;
723 // tx_buffer.payload.cfg_nav5x.minSVs = 0;
724 // tx_buffer.payload.cfg_nav5x.maxSVs = 0;
725 // tx_buffer.payload.cfg_nav5x.minCNO = 0;
726 // tx_buffer.payload.cfg_nav5x.reserved1 = 0;
727 // tx_buffer.payload.cfg_nav5x.iniFix3D = 0;
728 // tx_buffer.payload.cfg_nav5x.ackAiding = 0;
729 // tx_buffer.payload.cfg_nav5x.wknRollover = 0;
730 // tx_buffer.payload.cfg_nav5x.sigAttenCompMode = 0;
731 // tx_buffer.payload.cfg_nav5x.usePPP = 0;
733 // tx_buffer.payload.cfg_nav5x.aopCfg = 0x1; //bit 0 = useAOP
735 // tx_buffer.payload.cfg_nav5x.useAdr = 0;
737 // ubloxSendConfigMessage(&tx_buffer, MSG_CFG_NAVX_SETTINGS, sizeof(ubxCfgNav5x_t), false);
741 static void ubloxSetPowerModeValSet(uint8_t powerSetupValue
)
743 ubxMessage_t tx_buffer
;
746 payload
[0] = powerSetupValue
;
748 size_t offset
= ubloxValSet(&tx_buffer
, CFG_PM_OPERATEMODE
, payload
, UBX_VAL_LAYER_RAM
);
750 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_VALSET
, offsetof(ubxCfgValSet_t
, cfgData
) + offset
, true);
753 static void ubloxSendPowerMode(void)
755 if (gpsData
.ubloxM9orAbove
) {
756 ubloxSetPowerModeValSet(UBX_POWER_MODE_FULL
);
757 } else if (gpsData
.ubloxM8orAbove
) {
758 ubxMessage_t tx_buffer
;
759 tx_buffer
.payload
.cfg_pms
.version
= 0;
760 tx_buffer
.payload
.cfg_pms
.powerSetupValue
= UBX_POWER_MODE_FULL
;
761 tx_buffer
.payload
.cfg_pms
.period
= 0;
762 tx_buffer
.payload
.cfg_pms
.onTime
= 0;
763 tx_buffer
.payload
.cfg_pms
.reserved1
[0] = 0;
764 tx_buffer
.payload
.cfg_pms
.reserved1
[1] = 0;
765 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_PMS
, sizeof(ubxCfgPms_t
), false);
767 // M7 and below do not support this type of power mode, so we leave at default.
770 static void ubloxSetMessageRate(uint8_t messageClass
, uint8_t messageID
, uint8_t rate
)
772 ubxMessage_t tx_buffer
;
773 tx_buffer
.payload
.cfg_msg
.msgClass
= messageClass
;
774 tx_buffer
.payload
.cfg_msg
.msgID
= messageID
;
775 tx_buffer
.payload
.cfg_msg
.rate
= rate
;
776 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_MSG
, sizeof(ubxCfgMsg_t
), false);
779 static void ubloxSetMessageRateValSet(ubxValGetSetBytes_e msgClass
, uint8_t rate
)
781 ubxMessage_t tx_buffer
;
786 size_t offset
= ubloxValSet(&tx_buffer
, msgClass
, payload
, UBX_VAL_LAYER_RAM
);
788 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_VALSET
, offsetof(ubxCfgValSet_t
, cfgData
) + offset
, true);
791 static void ubloxDisableNMEAValSet(void)
793 ubxMessage_t tx_buffer
;
799 // size_t offset = ubloxValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GGA_I2C, payload, UBX_VAL_LAYER_RAM);
800 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GGA_SPI, payload, offset);
801 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GGA_UART1, payload, offset);
802 size_t offset
= ubloxValSet(&tx_buffer
, CFG_MSGOUT_NMEA_ID_GGA_UART1
, payload
, UBX_VAL_LAYER_RAM
);
804 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_VTG_I2C, payload, offset);
805 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_VTG_SPI, payload, offset);
806 offset
+= ubloxAddValSet(&tx_buffer
, CFG_MSGOUT_NMEA_ID_VTG_UART1
, payload
, offset
);
808 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GSV_I2C, payload, offset);
809 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GSV_SPI, payload, offset);
810 offset
+= ubloxAddValSet(&tx_buffer
, CFG_MSGOUT_NMEA_ID_GSV_UART1
, payload
, offset
);
812 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GLL_I2C, payload, offset);
813 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GLL_SPI, payload, offset);
814 offset
+= ubloxAddValSet(&tx_buffer
, CFG_MSGOUT_NMEA_ID_GLL_UART1
, payload
, offset
);
816 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GSA_I2C, payload, offset);
817 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_GSA_SPI, payload, offset);
818 offset
+= ubloxAddValSet(&tx_buffer
, CFG_MSGOUT_NMEA_ID_GSA_UART1
, payload
, offset
);
820 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_RMC_I2C, payload, offset);
821 // offset += ubloxAddValSet(&tx_buffer, CFG_MSGOUT_NMEA_ID_RMC_SPI, payload, offset);
822 offset
+= ubloxAddValSet(&tx_buffer
, CFG_MSGOUT_NMEA_ID_RMC_UART1
, payload
, offset
);
824 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_VALSET
, offsetof(ubxCfgValSet_t
, cfgData
) + offset
, true);
827 static void ubloxSetNavRate(uint16_t measRate
, uint16_t navRate
, uint8_t timeRef
)
829 uint16_t measRateMilliseconds
= 1000 / measRate
;
831 ubxMessage_t tx_buffer
;
832 if (gpsData
.ubloxM9orAbove
) {
835 payload
[0] = (uint8_t)(measRateMilliseconds
>> (8 * 0));
836 payload
[1] = (uint8_t)(measRateMilliseconds
>> (8 * 1));
838 offset
= ubloxValSet(&tx_buffer
, CFG_RATE_MEAS
, payload
, UBX_VAL_LAYER_RAM
);
840 payload
[0] = (uint8_t)(navRate
>> (8 * 0));
841 payload
[1] = (uint8_t)(navRate
>> (8 * 1));
843 offset
+= ubloxAddValSet(&tx_buffer
, CFG_RATE_NAV
, payload
, 6);
845 payload
[0] = timeRef
;
846 //rate timeref is E1 = U1
847 offset
+= ubloxAddValSet(&tx_buffer
, CFG_RATE_TIMEREF
, payload
, 12);
849 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_VALSET
, offsetof(ubxCfgValSet_t
, cfgData
) + offset
, false);
851 tx_buffer
.payload
.cfg_rate
.measRate
= measRateMilliseconds
;
852 tx_buffer
.payload
.cfg_rate
.navRate
= navRate
;
853 tx_buffer
.payload
.cfg_rate
.timeRef
= timeRef
;
854 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_RATE
, sizeof(ubxCfgRate_t
), true);
858 static void ubloxSetSbas(void)
860 ubxMessage_t tx_buffer
;
862 if (gpsData
.ubloxM9orAbove
) {
864 payload
[0] = gpsConfig()->sbasMode
!= SBAS_NONE
;
866 size_t offset
= ubloxValSet(&tx_buffer
, CFG_SBAS_USE_TESTMODE
, payload
, UBX_VAL_LAYER_RAM
);
869 offset
+= ubloxAddValSet(&tx_buffer
, CFG_SBAS_USE_RANGING
, payload
, offset
);
870 offset
+= ubloxAddValSet(&tx_buffer
, CFG_SBAS_USE_DIFFCORR
, payload
, offset
);
872 if (gpsConfig()->sbas_integrity
) {
873 offset
+= ubloxAddValSet(&tx_buffer
, CFG_SBAS_USE_INTEGRITY
, payload
, offset
);
876 uint64_t mask
= SBAS_SEARCH_ALL
;
877 switch (gpsConfig()->sbasMode
) {
879 mask
= SBAS_SEARCH_PRN(123) | SBAS_SEARCH_PRN(126) | SBAS_SEARCH_PRN(136);
882 mask
= SBAS_SEARCH_PRN(131) | SBAS_SEARCH_PRN(133) | SBAS_SEARCH_PRN(135) | SBAS_SEARCH_PRN(138);
885 mask
= SBAS_SEARCH_PRN(129) | SBAS_SEARCH_PRN(137);
888 mask
= SBAS_SEARCH_PRN(127) | SBAS_SEARCH_PRN(128) | SBAS_SEARCH_PRN(132);
895 payload
[0] = (uint8_t)(mask
>> (8 * 0));
896 payload
[1] = (uint8_t)(mask
>> (8 * 1));
897 payload
[2] = (uint8_t)(mask
>> (8 * 2));
898 payload
[3] = (uint8_t)(mask
>> (8 * 3));
899 payload
[4] = (uint8_t)(mask
>> (8 * 4));
900 payload
[5] = (uint8_t)(mask
>> (8 * 5));
901 payload
[6] = (uint8_t)(mask
>> (8 * 6));
902 payload
[7] = (uint8_t)(mask
>> (8 * 7));
904 offset
+= ubloxAddValSet(&tx_buffer
, CFG_SBAS_PRNSCANMASK
, payload
, offset
);
906 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_VALSET
, offsetof(ubxCfgValSet_t
, cfgData
) + offset
, true);
908 //NOTE: default ublox config for sbas mode is: UBLOX_MODE_ENABLED, test is disabled
909 tx_buffer
.payload
.cfg_sbas
.mode
= UBLOX_MODE_TEST
;
910 if (gpsConfig()->sbasMode
!= SBAS_NONE
) {
911 tx_buffer
.payload
.cfg_sbas
.mode
|= UBLOX_MODE_ENABLED
;
914 // NOTE: default ublox config for sbas mode is: UBLOX_USAGE_RANGE | UBLOX_USAGE_DIFFCORR, integrity is disabled
915 tx_buffer
.payload
.cfg_sbas
.usage
= UBLOX_USAGE_RANGE
| UBLOX_USAGE_DIFFCORR
;
916 if (gpsConfig()->sbas_integrity
) {
917 tx_buffer
.payload
.cfg_sbas
.usage
|= UBLOX_USAGE_INTEGRITY
;
920 tx_buffer
.payload
.cfg_sbas
.maxSBAS
= 3;
921 tx_buffer
.payload
.cfg_sbas
.scanmode2
= 0;
922 switch (gpsConfig()->sbasMode
) {
924 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0;
927 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00010048; //PRN123, PRN126, PRN136
930 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x0004A800; //PRN131, PRN133, PRN135, PRN138
933 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00020200; //PRN129, PRN137
936 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0x00001180; //PRN127, PRN128, PRN132
939 tx_buffer
.payload
.cfg_sbas
.scanmode1
= 0;
942 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_SBAS
, sizeof(ubxCfgSbas_t
), false);
946 static void setSatInfoMessageRate(uint8_t divisor
)
948 // enable satInfoMessage at 1:5 of the nav rate if configurator is connected
949 if (gpsData
.ubloxM9orAbove
) {
950 ubloxSetMessageRateValSet(CFG_MSGOUT_UBX_NAV_SAT_UART1
, divisor
);
951 } else if (gpsData
.ubloxM8orAbove
) {
952 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_SAT
, divisor
);
954 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_SVINFO
, divisor
);
958 #endif // USE_GPS_UBLOX
961 static void gpsConfigureNmea(void)
963 // minimal support for NMEA, we only:
964 // - set the FC's GPS port to the user's configured rate, and
965 // - send any NMEA custom commands to the GPS Module
966 // the user must configure the power-up baud rate of the module to be fast enough for their data rate
967 // Note: we always parse all incoming NMEA messages
968 DEBUG_SET(DEBUG_GPS_CONNECTION
, 4, (gpsData
.state
* 100 + gpsData
.state_position
));
970 // wait 500ms between changes
971 if (cmp32(gpsData
.now
, gpsData
.state_ts
) < 500) {
974 gpsData
.state_ts
= gpsData
.now
;
976 // Check that the GPS transmit buffer is empty
977 if (!isSerialTransmitBufferEmpty(gpsPort
)) {
981 switch (gpsData
.state
) {
983 case GPS_STATE_DETECT_BAUD
:
984 // no attempt to read the baud rate of the GPS module, or change it
985 gpsSetState(GPS_STATE_CHANGE_BAUD
);
988 case GPS_STATE_CHANGE_BAUD
:
989 #if !defined(GPS_NMEA_TX_ONLY)
990 if (gpsData
.state_position
< 1) {
991 // set the FC's baud rate to the user's configured baud rate
992 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.userBaudRateIndex
].baudrateIndex
]);
993 gpsData
.state_position
++;
994 } else if (gpsData
.state_position
< 2) {
995 // send NMEA custom commands to select which messages being sent, data rate etc
996 // use PUBX, MTK, SiRF or GTK format commands, depending on module type
997 static int commandOffset
= 0;
998 const char *commands
= gpsConfig()->nmeaCustomCommands
;
999 const char *cmd
= commands
+ commandOffset
;
1000 // skip leading whitespaces and get first command length
1002 while (*cmd
&& (commandLen
= strcspn(cmd
, " \0")) == 0) {
1003 cmd
++; // skip separators
1006 // Send the current command to the GPS
1007 serialWriteBuf(gpsPort
, (uint8_t *)cmd
, commandLen
);
1008 serialWriteBuf(gpsPort
, (uint8_t *)"\r\n", 2);
1009 // Move to the next command
1012 // skip trailing whitespaces
1013 while (*cmd
&& strcspn(cmd
, " \0") == 0) cmd
++;
1015 // more commands to send
1016 commandOffset
= cmd
- commands
;
1018 gpsData
.state_position
++;
1021 gpsData
.state_position
++;
1022 gpsSetState(GPS_STATE_RECEIVING_DATA
);
1024 #else // !GPS_NMEA_TX_ONLY
1025 gpsSetState(GPS_STATE_RECEIVING_DATA
);
1026 #endif // !GPS_NMEA_TX_ONLY
1030 #endif // USE_GPS_NMEA
1032 #ifdef USE_GPS_UBLOX
1034 static void gpsConfigureUblox(void)
1037 // Wait until GPS transmit buffer is empty
1038 if (!isSerialTransmitBufferEmpty(gpsPort
)) {
1042 switch (gpsData
.state
) {
1043 case GPS_STATE_DETECT_BAUD
:
1045 DEBUG_SET(DEBUG_GPS_CONNECTION
, 3, baudRates
[gpsInitData
[gpsData
.tempBaudRateIndex
].baudrateIndex
] / 100);
1047 // check to see if there has been a response to the version command
1048 // initially the FC will be at the user-configured baud rate.
1049 if (gpsData
.platformVersion
> UBX_VERSION_UNDEF
) {
1050 // set the GPS module's serial port to the user's intended baud rate
1051 serialPrint(gpsPort
, gpsInitData
[gpsData
.userBaudRateIndex
].ubx
);
1052 // use this baud rate for re-connections
1053 gpsData
.tempBaudRateIndex
= gpsData
.userBaudRateIndex
;
1054 // we're done here, let's move the the next state
1055 gpsSetState(GPS_STATE_CHANGE_BAUD
);
1059 // Send MON-VER messages at GPS_CONFIG_BAUD_CHANGE_INTERVAL for GPS_BAUDRATE_TEST_COUNT times
1060 static bool messageSent
= false;
1061 static uint8_t messageCounter
= 0;
1062 DEBUG_SET(DEBUG_GPS_CONNECTION
, 2, initBaudRateCycleCount
* 100 + messageCounter
);
1064 if (messageCounter
< GPS_BAUDRATE_TEST_COUNT
) {
1066 gpsData
.platformVersion
= UBX_VERSION_UNDEF
;
1067 ubloxSendClassMessage(CLASS_MON
, MSG_MON_VER
, 0);
1068 gpsData
.ackState
= UBLOX_ACK_IDLE
; // ignore ACK for this message
1071 if (cmp32(gpsData
.now
, gpsData
.state_ts
) > GPS_CONFIG_BAUD_CHANGE_INTERVAL
) {
1072 gpsData
.state_ts
= gpsData
.now
;
1073 messageSent
= false;
1079 gpsData
.state_ts
= gpsData
.now
;
1081 // failed to connect at that rate after five attempts
1082 // try other GPS baudrates, starting at 9600 and moving up
1083 if (gpsData
.tempBaudRateIndex
== 0) {
1084 gpsData
.tempBaudRateIndex
= ARRAYLEN(gpsInitData
) - 1; // slowest baud rate (9600)
1086 gpsData
.tempBaudRateIndex
--;
1088 // set the FC baud rate to the new temp baud rate
1089 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.tempBaudRateIndex
].baudrateIndex
]);
1090 initBaudRateCycleCount
++;
1094 case GPS_STATE_CHANGE_BAUD
:
1095 // give time for the GPS module's serial port to settle
1096 // very important for M8 to give the module a lot of time before sending commands
1097 // M10 only need about 200ms but M8 and below sometimes need as long as 1000ms
1098 if (cmp32(gpsData
.now
, gpsData
.state_ts
) < (3 * GPS_CONFIG_BAUD_CHANGE_INTERVAL
)) {
1101 // set the FC's serial port to the configured rate
1102 serialSetBaudRate(gpsPort
, baudRates
[gpsInitData
[gpsData
.userBaudRateIndex
].baudrateIndex
]);
1103 DEBUG_SET(DEBUG_GPS_CONNECTION
, 3, baudRates
[gpsInitData
[gpsData
.userBaudRateIndex
].baudrateIndex
] / 100);
1104 // then start sending configuration settings
1105 gpsSetState(GPS_STATE_CONFIGURE
);
1108 case GPS_STATE_CONFIGURE
:
1109 // Either use specific config file for GPS or let dynamically upload config
1110 if (gpsConfig()->autoConfig
== GPS_AUTOCONFIG_OFF
) {
1111 gpsSetState(GPS_STATE_RECEIVING_DATA
);
1115 // Add delay to stabilize the connection
1116 if (cmp32(gpsData
.now
, gpsData
.state_ts
) < 1000) {
1120 if (gpsData
.ackState
== UBLOX_ACK_IDLE
) {
1122 // short delay before between commands, including the first command
1123 static uint32_t last_state_position_time
= 0;
1124 if (last_state_position_time
== 0) {
1125 last_state_position_time
= gpsData
.now
;
1127 if (cmp32(gpsData
.now
, last_state_position_time
) < GPS_CONFIG_CHANGE_INTERVAL
) {
1130 last_state_position_time
= gpsData
.now
;
1132 switch (gpsData
.state_position
) {
1133 // if a UBX command is sent, ack is supposed to give position++ once the reply happens
1134 case UBLOX_DETECT_UNIT
: // 400 in debug
1135 if (gpsData
.platformVersion
== UBX_VERSION_UNDEF
) {
1136 ubloxSendClassMessage(CLASS_MON
, MSG_MON_VER
, 0);
1138 gpsData
.state_position
++;
1141 case UBLOX_SLOW_NAV_RATE
: // 401 in debug
1142 ubloxSetNavRate(1, 1, 1); // throttle nav data rate to one per second, until configured
1144 case UBLOX_MSG_DISABLE_NMEA
:
1145 if (gpsData
.ubloxM9orAbove
) {
1146 ubloxDisableNMEAValSet();
1147 gpsData
.state_position
= UBLOX_MSG_RMC
; // skip UBX NMEA entries - goes to RMC plus one, or ACQUIRE_MODEL
1149 gpsData
.state_position
++;
1152 case UBLOX_MSG_VGS
: //Disable NMEA Messages
1153 ubloxSetMessageRate(CLASS_NMEA_STD
, MSG_NMEA_VTG
, 0); // VGS: Course over ground and Ground speed
1156 ubloxSetMessageRate(CLASS_NMEA_STD
, MSG_NMEA_GSV
, 0); // GSV: GNSS Satellites in View
1159 ubloxSetMessageRate(CLASS_NMEA_STD
, MSG_NMEA_GLL
, 0); // GLL: Latitude and longitude, with time of position fix and status
1162 ubloxSetMessageRate(CLASS_NMEA_STD
, MSG_NMEA_GGA
, 0); // GGA: Global positioning system fix data
1165 ubloxSetMessageRate(CLASS_NMEA_STD
, MSG_NMEA_GSA
, 0); // GSA: GNSS DOP and Active Satellites
1168 ubloxSetMessageRate(CLASS_NMEA_STD
, MSG_NMEA_RMC
, 0); // RMC: Recommended Minimum data
1170 case UBLOX_ACQUIRE_MODEL
:
1171 ubloxSendNAV5Message(gpsConfig()->gps_ublox_acquire_model
);
1173 // *** temporarily disabled
1174 // case UBLOX_CFG_ANA:
1175 // i f (gpsData.ubloxM7orAbove) { // NavX5 support existed in M5 - in theory we could remove that check
1176 // ubloxSendNavX5Message();
1178 // gpsData.state_position++;
1181 case UBLOX_SET_SBAS
:
1185 if (gpsData
.ubloxM8orAbove
) {
1186 ubloxSendPowerMode();
1188 gpsData
.state_position
++;
1191 case UBLOX_MSG_NAV_PVT
: //Enable NAV-PVT Messages
1192 if (gpsData
.ubloxM9orAbove
) {
1193 ubloxSetMessageRateValSet(CFG_MSGOUT_UBX_NAV_PVT_UART1
, 1);
1194 } else if (gpsData
.ubloxM7orAbove
) {
1195 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_PVT
, 1);
1197 gpsData
.state_position
++;
1200 // if NAV-PVT is enabled, we don't need the older nav messages
1202 if (gpsData
.ubloxM9orAbove
) {
1203 // SOL is deprecated above M8
1204 gpsData
.state_position
++;
1205 } else if (gpsData
.ubloxM7orAbove
) {
1206 // use NAV-PVT, so don't use NAV-SOL
1207 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_SOL
, 0);
1209 // Only use NAV-SOL below M7
1210 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_SOL
, 1);
1213 case UBLOX_MSG_POSLLH
:
1214 if (gpsData
.ubloxM7orAbove
) {
1215 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_POSLLH
, 0);
1217 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_POSLLH
, 1);
1220 case UBLOX_MSG_STATUS
:
1221 if (gpsData
.ubloxM7orAbove
) {
1222 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_STATUS
, 0);
1224 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_STATUS
, 1);
1227 case UBLOX_MSG_VELNED
:
1228 if (gpsData
.ubloxM7orAbove
) {
1229 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_VELNED
, 0);
1231 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_VELNED
, 1);
1235 // nav-pvt has what we need and is available M7 and above
1236 if (gpsData
.ubloxM9orAbove
) {
1237 ubloxSetMessageRateValSet(CFG_MSGOUT_UBX_NAV_DOP_UART1
, 0);
1238 } else if (gpsData
.ubloxM7orAbove
) {
1239 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_DOP
, 0);
1241 ubloxSetMessageRate(CLASS_NAV
, MSG_NAV_DOP
, 1);
1244 case UBLOX_SAT_INFO
:
1245 // enable by default, turned off when armed and receiving data to reduce in-flight traffic
1246 setSatInfoMessageRate(5);
1248 case UBLOX_SET_NAV_RATE
:
1249 // set the nav solution rate to the user's configured update rate
1250 gpsData
.updateRateHz
= gpsConfig()->gps_update_rate_hz
;
1251 ubloxSetNavRate(gpsData
.updateRateHz
, 1, 1);
1253 case UBLOX_MSG_CFG_GNSS
:
1254 if ((gpsConfig()->sbasMode
== SBAS_NONE
) || (gpsConfig()->gps_ublox_use_galileo
)) {
1255 ubloxSendPollMessage(MSG_CFG_GNSS
); // poll messages wait for ACK
1257 gpsData
.state_position
++;
1260 case UBLOX_CONFIG_COMPLETE
:
1261 gpsSetState(GPS_STATE_RECEIVING_DATA
);
1263 // TO DO: (separate PR) add steps that remove I2C or SPI data on ValSet aware units, eg
1264 // ubloxSetMessageRateValSet(CFG_MSGOUT_UBX_NAV_SAT_I2C, 0);
1265 // ubloxSetMessageRateValSet(CFG_MSGOUT_UBX_NAV_SAT_SPI, 0);
1271 // check the ackState after changing CONFIG state, or every iteration while not UBLOX_ACK_IDLE
1272 switch (gpsData
.ackState
) {
1273 case UBLOX_ACK_IDLE
:
1275 case UBLOX_ACK_WAITING
:
1276 if (cmp32(gpsData
.now
, gpsData
.lastMessageSent
) > UBLOX_ACK_TIMEOUT_MS
){
1277 // give up, treat it like receiving ack
1278 gpsData
.ackState
= UBLOX_ACK_GOT_ACK
;
1281 case UBLOX_ACK_GOT_ACK
:
1282 // move forward one position, and clear the ack state
1283 gpsData
.state_position
++;
1284 gpsData
.ackState
= UBLOX_ACK_IDLE
;
1286 case UBLOX_ACK_GOT_NACK
:
1287 // this is the tricky bit
1288 // and we absolutely must get the unit type right
1289 if (gpsData
.state_position
== UBLOX_DETECT_UNIT
) {
1290 gpsSetState(GPS_STATE_CONFIGURE
);
1291 gpsData
.ackState
= UBLOX_ACK_IDLE
;
1293 // otherwise, for testing: just ignore nacks
1294 gpsData
.state_position
++;
1295 gpsData
.ackState
= UBLOX_ACK_IDLE
;
1303 #endif // USE_GPS_UBLOX
1305 static void gpsConfigureHardware(void)
1307 switch (gpsConfig()->provider
) {
1315 #ifdef USE_GPS_UBLOX
1316 gpsConfigureUblox();
1324 static void updateGpsIndicator(timeUs_t currentTimeUs
)
1326 static uint32_t GPSLEDTime
;
1327 if (cmp32(currentTimeUs
, GPSLEDTime
) >= 0 && STATE(GPS_FIX
) && (gpsSol
.numSat
>= gpsRescueConfig()->minSats
)) {
1328 GPSLEDTime
= currentTimeUs
+ 150000;
1333 static void calculateNavInterval (void)
1335 // calculate the interval between nav packets, handling iTow wraparound at the end of the week
1336 const uint32_t weekDurationMs
= 7 * 24 * 3600 * 1000;
1337 const uint32_t navDeltaTimeMs
= (weekDurationMs
+ gpsSol
.time
- gpsData
.lastNavSolTs
) % weekDurationMs
;
1338 gpsData
.lastNavSolTs
= gpsSol
.time
;
1339 // constrain the interval between 50ms / 20hz or 2.5s, when we would get a connection failure anyway
1340 gpsSol
.navIntervalMs
= constrain(navDeltaTimeMs
, 50, 2500);
1343 #if defined(USE_VIRTUAL_GPS)
1344 static void updateVirtualGPS(void)
1346 const uint32_t updateInterval
= 100; // 100ms 10Hz update time interval
1347 static uint32_t nextUpdateTime
= 0;
1349 if (cmp32(gpsData
.now
, nextUpdateTime
) > 0) {
1350 if (gpsData
.state
== GPS_STATE_INITIALIZED
) {
1351 gpsSetState(GPS_STATE_RECEIVING_DATA
);
1354 getVirtualGPS(&gpsSol
);
1355 gpsSol
.time
= gpsData
.now
;
1357 gpsData
.lastNavMessage
= gpsData
.now
;
1358 sensorsSet(SENSOR_GPS
);
1360 if (gpsSol
.numSat
> 3) {
1361 gpsSetFixState(GPS_FIX
);
1365 GPS_update
^= GPS_DIRECT_TICK
;
1367 calculateNavInterval();
1370 nextUpdateTime
= gpsData
.now
+ updateInterval
;
1375 void gpsUpdate(timeUs_t currentTimeUs
)
1377 static timeDelta_t gpsStateDurationFractionUs
[GPS_STATE_COUNT
];
1378 timeDelta_t executeTimeUs
;
1379 gpsState_e gpsCurrentState
= gpsData
.state
;
1380 gpsData
.now
= millis();
1382 switch (gpsConfig()->provider
) {
1388 DEBUG_SET(DEBUG_GPS_CONNECTION
, 7, serialRxBytesWaiting(gpsPort
));
1389 static uint8_t wait
= 0;
1390 static bool isFast
= false;
1391 while (serialRxBytesWaiting(gpsPort
)) {
1394 rescheduleTask(TASK_SELF
, TASK_PERIOD_HZ(TASK_GPS_RATE_FAST
));
1397 if (cmpTimeUs(micros(), currentTimeUs
) > GPS_RECV_TIME_MAX
) {
1400 // Add every byte to _buffer, when enough bytes are received, convert data to values
1401 gpsNewData(serialRead(gpsPort
));
1405 } else if (wait
== 1) {
1407 // wait one iteration be sure the buffer is empty, then reset to the slower task interval
1409 rescheduleTask(TASK_SELF
, TASK_PERIOD_HZ(TASK_GPS_RATE
));
1414 if (GPS_update
& GPS_MSP_UPDATE
) { // GPS data received via MSP
1415 if (gpsData
.state
== GPS_STATE_INITIALIZED
) {
1416 gpsSetState(GPS_STATE_RECEIVING_DATA
);
1419 // Data is available
1420 DEBUG_SET(DEBUG_GPS_CONNECTION
, 3, gpsData
.now
- gpsData
.lastNavMessage
); // interval since last Nav data was received
1421 gpsData
.lastNavMessage
= gpsData
.now
;
1422 sensorsSet(SENSOR_GPS
);
1424 GPS_update
^= GPS_DIRECT_TICK
;
1425 calculateNavInterval();
1428 GPS_update
&= ~GPS_MSP_UPDATE
;
1430 DEBUG_SET(DEBUG_GPS_CONNECTION
, 2, gpsData
.now
- gpsData
.lastNavMessage
); // time since last Nav data, updated each GPS task interval
1431 // check for no data/gps timeout/cable disconnection etc
1432 if (cmp32(gpsData
.now
, gpsData
.lastNavMessage
) > GPS_TIMEOUT_MS
) {
1433 gpsSetState(GPS_STATE_LOST_COMMUNICATION
);
1437 #if defined(USE_VIRTUAL_GPS)
1444 switch (gpsData
.state
) {
1445 case GPS_STATE_UNKNOWN
:
1446 case GPS_STATE_INITIALIZED
:
1449 case GPS_STATE_DETECT_BAUD
:
1450 case GPS_STATE_CHANGE_BAUD
:
1451 case GPS_STATE_CONFIGURE
:
1452 gpsConfigureHardware();
1455 case GPS_STATE_LOST_COMMUNICATION
:
1457 // previously we would attempt a different baud rate here if gps auto-baud was enabled. that code has been removed.
1459 DISABLE_STATE(GPS_FIX
);
1460 gpsSetState(GPS_STATE_DETECT_BAUD
);
1463 case GPS_STATE_RECEIVING_DATA
:
1464 #ifdef USE_GPS_UBLOX
1465 if (gpsConfig()->provider
== GPS_UBLOX
|| gpsConfig()->provider
== GPS_NMEA
) { // TODO Send ublox message to nmea GPS?
1466 if (gpsConfig()->autoConfig
== GPS_AUTOCONFIG_ON
) {
1467 // when we are connected up, and get a 3D fix, enable the 'flight' fix model
1468 if (!gpsData
.ubloxUsingFlightModel
&& STATE(GPS_FIX
)) {
1469 gpsData
.ubloxUsingFlightModel
= true;
1470 ubloxSendNAV5Message(gpsConfig()->gps_ublox_flight_model
);
1475 DEBUG_SET(DEBUG_GPS_CONNECTION
, 2, gpsData
.now
- gpsData
.lastNavMessage
); // time since last Nav data, updated each GPS task interval
1476 // check for no data/gps timeout/cable disconnection etc
1477 if (cmp32(gpsData
.now
, gpsData
.lastNavMessage
) > GPS_TIMEOUT_MS
) {
1478 gpsSetState(GPS_STATE_LOST_COMMUNICATION
);
1483 DEBUG_SET(DEBUG_GPS_CONNECTION
, 4, (gpsData
.state
* 100 + gpsData
.state_position
));
1484 DEBUG_SET(DEBUG_GPS_CONNECTION
, 6, gpsData
.ackState
);
1486 if (sensors(SENSOR_GPS
)) {
1487 updateGpsIndicator(currentTimeUs
);
1490 static bool hasBeeped
= false;
1491 if (!ARMING_FLAG(ARMED
)) {
1492 if (!gpsConfig()->gps_set_home_point_once
) {
1493 // clear the home fix icon between arms if the user configuration is to reset home point between arms
1494 DISABLE_STATE(GPS_FIX_HOME
);
1496 // while disarmed, beep when requirements for a home fix are met
1497 // ?? should we also beep if home fix requirements first appear after arming?
1498 if (!hasBeeped
&& STATE(GPS_FIX
) && gpsSol
.numSat
>= gpsRescueConfig()->minSats
) {
1499 beeper(BEEPER_READY_BEEP
);
1504 DEBUG_SET(DEBUG_GPS_DOP
, 0, gpsSol
.numSat
);
1505 DEBUG_SET(DEBUG_GPS_DOP
, 1, gpsSol
.dop
.pdop
);
1506 DEBUG_SET(DEBUG_GPS_DOP
, 2, gpsSol
.dop
.hdop
);
1507 DEBUG_SET(DEBUG_GPS_DOP
, 3, gpsSol
.dop
.vdop
);
1509 executeTimeUs
= micros() - currentTimeUs
;
1510 if (executeTimeUs
> (gpsStateDurationFractionUs
[gpsCurrentState
] >> GPS_TASK_DECAY_SHIFT
)) {
1511 gpsStateDurationFractionUs
[gpsCurrentState
] += (2 << GPS_TASK_DECAY_SHIFT
);
1513 // Slowly decay the max time
1514 gpsStateDurationFractionUs
[gpsCurrentState
]--;
1516 schedulerSetNextStateTime(gpsStateDurationFractionUs
[gpsCurrentState
] >> GPS_TASK_DECAY_SHIFT
);
1518 DEBUG_SET(DEBUG_GPS_CONNECTION
, 5, executeTimeUs
);
1519 // keeping temporarily, to be used when debugging the scheduler stuff
1520 // DEBUG_SET(DEBUG_GPS_CONNECTION, 6, (gpsStateDurationFractionUs[gpsCurrentState] >> GPS_TASK_DECAY_SHIFT));
1523 static void gpsNewData(uint16_t c
)
1525 DEBUG_SET(DEBUG_GPS_CONNECTION
, 1, gpsSol
.navIntervalMs
);
1526 if (!gpsNewFrame(c
)) {
1527 // no new nav solution data
1530 if (gpsData
.state
== GPS_STATE_RECEIVING_DATA
) {
1531 DEBUG_SET(DEBUG_GPS_CONNECTION
, 3, gpsData
.now
- gpsData
.lastNavMessage
); // interval since last Nav data was received
1532 gpsData
.lastNavMessage
= gpsData
.now
;
1533 sensorsSet(SENSOR_GPS
);
1534 // use the baud rate debug once receiving data
1536 GPS_update
^= GPS_DIRECT_TICK
;
1540 #ifdef USE_GPS_UBLOX
1541 static ubloxVersion_e
ubloxParseVersion(const uint32_t version
) {
1542 for (size_t i
= 0; i
< ARRAYLEN(ubloxVersionMap
); ++i
) {
1543 if (version
== ubloxVersionMap
[i
].hw
) {
1544 return (ubloxVersion_e
) i
;
1548 return UBX_VERSION_UNDEF
;// (ubloxVersion_e) version;
1552 bool gpsNewFrame(uint8_t c
)
1554 switch (gpsConfig()->provider
) {
1555 case GPS_NMEA
: // NMEA
1557 return gpsNewFrameNMEA(c
);
1560 case GPS_UBLOX
: // UBX binary
1561 #ifdef USE_GPS_UBLOX
1562 return gpsNewFrameUBLOX(c
);
1571 // Check for healthy communications
1572 bool gpsIsHealthy(void)
1574 return (gpsData
.state
== GPS_STATE_RECEIVING_DATA
);
1577 /* This is a light implementation of a GPS frame decoding
1578 This should work with most of modern GPS devices configured to output 5 frames.
1579 It assumes there are some NMEA GGA frames to decode on the serial bus
1580 Now verifies checksum correctly before applying data
1582 Here we use only the following data :
1585 - GPS fix is/is not ok
1586 - GPS num sat (4 is enough to be +/- reliable)
1588 - GPS altitude (for OSD displaying)
1589 - GPS speed (for OSD displaying)
1598 // This code is used for parsing NMEA data
1600 /* Alex optimization
1601 The latitude or longitude is coded this way in NMEA frames
1602 dm.f coded as degrees + minutes + minute decimal
1604 - d can be 1 or more char long. generally: 2 char long for latitude, 3 char long for longitude
1605 - m is always 2 char long
1606 - f can be 1 or more char long
1607 This function converts this format in a unique unsigned long where 1 degree = 10 000 000
1609 EOS increased the precision here, even if we think that the gps is not precise enough, with 10e5 precision it has 76cm resolution
1610 with 10e7 it's around 1 cm now. Increasing it further is irrelevant, since even 1cm resolution is unrealistic, however increased
1611 resolution also increased precision of nav calculations
1612 static uint32_t GPS_coord_to_degrees(char *coordinateString)
1614 char *p = s, *d = s;
1615 uint8_t min, deg = 0;
1616 uint16_t frac = 0, mult = 10000;
1618 while (*p) { // parse the string until its end
1620 frac += (*p - '0') * mult; // calculate only fractional part on up to 5 digits (d != s condition is true when the . is located)
1624 d = p; // locate '.' char in the string
1630 deg *= 10; // convert degrees : all chars before minutes ; for the first iteration, deg = 0
1631 deg += *(s++) - '0';
1633 min = *(d - 1) - '0' + (*(d - 2) - '0') * 10; // convert minutes : 2 previous char before '.'
1634 return deg * 10000000UL + (min * 100000UL + frac) * 10UL / 6;
1640 static uint32_t grab_fields(char *src
, uint8_t mult
)
1641 { // convert string to uint32
1645 for (i
= 0; src
[i
] != 0; i
++) {
1646 if ((i
== 0) && (src
[0] == '-')) { // detect negative sign
1648 continue; // jump to next character if the first one was a negative sign
1650 if (src
[i
] == '.') {
1659 if (src
[i
] >= '0' && src
[i
] <= '9') {
1660 tmp
+= src
[i
] - '0';
1663 return 0; // out of bounds
1666 return isneg
? -tmp
: tmp
; // handle negative altitudes
1669 typedef struct gpsDataNmea_s
{
1678 uint16_t ground_course
;
1683 static void parseFieldNmea(gpsDataNmea_t
*data
, char *str
, uint8_t gpsFrame
, uint8_t idx
)
1685 static uint8_t svMessageNum
= 0;
1686 uint8_t svSatNum
= 0, svPacketIdx
= 0, svSatParam
= 0;
1690 case FRAME_GGA
: //************* GPGGA FRAME parsing
1693 data
->time
= ((uint8_t)(str
[5] - '0') * 10 + (uint8_t)(str
[7] - '0')) * 100;
1696 data
->latitude
= GPS_coord_to_degrees(str
);
1699 if (str
[0] == 'S') data
->latitude
*= -1;
1702 data
->longitude
= GPS_coord_to_degrees(str
);
1705 if (str
[0] == 'W') data
->longitude
*= -1;
1708 gpsSetFixState(str
[0] > '0');
1711 data
->numSat
= grab_fields(str
, 0);
1714 data
->altitudeCm
= grab_fields(str
, 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
1719 case FRAME_RMC
: //************* GPRMC FRAME parsing
1722 data
->time
= grab_fields(str
, 2); // UTC time hhmmss.ss
1725 data
->speed
= ((grab_fields(str
, 1) * 5144L) / 1000L); // speed in cm/s added by Mis
1728 data
->ground_course
= (grab_fields(str
, 1)); // ground course deg * 10
1731 data
->date
= grab_fields(str
, 0); // date dd/mm/yy
1739 // Total number of messages of this type in this cycle
1743 svMessageNum
= grab_fields(str
, 0);
1746 // Total number of SVs visible
1747 GPS_numCh
= MIN(grab_fields(str
, 0), GPS_SV_MAXSATS_LEGACY
);
1753 svPacketIdx
= (idx
- 4) / 4 + 1; // satellite number in packet, 1-4
1754 svSatNum
= svPacketIdx
+ (4 * (svMessageNum
- 1)); // global satellite number
1755 svSatParam
= idx
- 3 - (4 * (svPacketIdx
- 1)); // parameter number for satellite
1757 if (svSatNum
> GPS_SV_MAXSATS_LEGACY
)
1760 switch (svSatParam
) {
1763 GPS_svinfo
[svSatNum
- 1].chn
= svSatNum
;
1764 GPS_svinfo
[svSatNum
- 1].svid
= grab_fields(str
, 0);
1767 // Elevation, in degrees, 90 maximum
1770 // Azimuth, degrees from True North, 000 through 359
1773 // SNR, 00 through 99 dB (null when not tracking)
1774 GPS_svinfo
[svSatNum
- 1].cno
= grab_fields(str
, 0);
1775 GPS_svinfo
[svSatNum
- 1].quality
= 0; // only used by ublox
1779 #ifdef USE_DASHBOARD
1780 dashboardGpsNavSvInfoRcvCount
++;
1787 data
->pdop
= grab_fields(str
, 2); // pDOP * 100
1790 data
->hdop
= grab_fields(str
, 2); // hDOP * 100
1793 data
->vdop
= grab_fields(str
, 2); // vDOP * 100
1800 static bool writeGpsSolutionNmea(gpsSolutionData_t
*sol
, const gpsDataNmea_t
*data
, uint8_t gpsFrame
)
1802 int navDeltaTimeMs
= 100;
1803 const uint32_t msInTenSeconds
= 10000;
1807 #ifdef USE_DASHBOARD
1808 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_NMEA_GGA
;
1810 if (STATE(GPS_FIX
)) {
1811 sol
->llh
.lat
= data
->latitude
;
1812 sol
->llh
.lon
= data
->longitude
;
1813 sol
->numSat
= data
->numSat
;
1814 sol
->llh
.altCm
= data
->altitudeCm
;
1816 navDeltaTimeMs
= (msInTenSeconds
+ data
->time
- gpsData
.lastNavSolTs
) % msInTenSeconds
;
1817 gpsData
.lastNavSolTs
= data
->time
;
1818 sol
->navIntervalMs
= constrain(navDeltaTimeMs
, 50, 2500);
1819 // return only one true statement to trigger one "newGpsDataReady" flag per GPS loop
1823 #ifdef USE_DASHBOARD
1824 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_NMEA_GSA
;
1826 sol
->dop
.pdop
= data
->pdop
;
1827 sol
->dop
.hdop
= data
->hdop
;
1828 sol
->dop
.vdop
= data
->vdop
;
1832 #ifdef USE_DASHBOARD
1833 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_NMEA_RMC
;
1835 sol
->groundSpeed
= data
->speed
;
1836 sol
->groundCourse
= data
->ground_course
;
1838 // This check will miss 00:00:00.00, but we shouldn't care - next report will be valid
1839 if(!rtcHasTime() && data
->date
!= 0 && data
->time
!= 0) {
1840 dateTime_t temp_time
;
1841 temp_time
.year
= (data
->date
% 100) + 2000;
1842 temp_time
.month
= (data
->date
/ 100) % 100;
1843 temp_time
.day
= (data
->date
/ 10000) % 100;
1844 temp_time
.hours
= (data
->time
/ 1000000) % 100;
1845 temp_time
.minutes
= (data
->time
/ 10000) % 100;
1846 temp_time
.seconds
= (data
->time
/ 100) % 100;
1847 temp_time
.millis
= (data
->time
& 100) * 10;
1848 rtcSetDateTime(&temp_time
);
1858 static bool gpsNewFrameNMEA(char c
)
1860 static gpsDataNmea_t gps_msg
;
1861 static char string
[15];
1862 static uint8_t param
= 0, offset
= 0, parity
= 0;
1863 static uint8_t checksum_param
, gps_frame
= NO_FRAME
;
1864 bool receivedNavMessage
= false;
1877 if (param
== 0) { // frame identification (5 chars, e.g. "GPGGA", "GNGGA", "GLGGA", ...)
1878 gps_frame
= NO_FRAME
;
1879 if (strcmp(&string
[2], "GGA") == 0) {
1880 gps_frame
= FRAME_GGA
;
1881 } else if (strcmp(&string
[2], "RMC") == 0) {
1882 gps_frame
= FRAME_RMC
;
1883 } else if (strcmp(&string
[2], "GSV") == 0) {
1884 gps_frame
= FRAME_GSV
;
1885 } else if (strcmp(&string
[2], "GSA") == 0) {
1886 gps_frame
= FRAME_GSA
;
1890 // parse string and write data into gps_msg
1891 parseFieldNmea(&gps_msg
, string
, gps_frame
, param
);
1903 if (checksum_param
) { //parity checksum
1904 #ifdef USE_DASHBOARD
1907 uint8_t checksum
= 16 * ((string
[0] >= 'A') ? string
[0] - 'A' + 10 : string
[0] - '0') + ((string
[1] >= 'A') ? string
[1] - 'A' + 10 : string
[1] - '0');
1908 if (checksum
== parity
) {
1909 #ifdef USE_DASHBOARD
1910 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_IGNORED
;
1911 dashboardGpsPacketCount
++;
1913 receivedNavMessage
= writeGpsSolutionNmea(&gpsSol
, &gps_msg
, gps_frame
); // // write gps_msg into gpsSol
1915 #ifdef USE_DASHBOARD
1917 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_ERROR
;
1926 string
[offset
++] = c
;
1927 if (!checksum_param
)
1932 return receivedNavMessage
;
1934 #endif // USE_GPS_NMEA
1936 #ifdef USE_GPS_UBLOX
1938 typedef struct ubxNavPosllh_s
{
1939 uint32_t time
; // GPS msToW
1942 int32_t altitude_ellipsoid
;
1943 int32_t altitudeMslMm
;
1944 uint32_t horizontal_accuracy
;
1945 uint32_t vertical_accuracy
;
1948 typedef struct ubxNavStatus_s
{
1949 uint32_t time
; // GPS msToW
1952 uint8_t differential_status
;
1954 uint32_t time_to_first_fix
;
1955 uint32_t uptime
; // milliseconds
1958 typedef struct ubxNavDop_s
{
1959 uint32_t itow
; // GPS Millisecond Time of Week
1960 uint16_t gdop
; // Geometric DOP
1961 uint16_t pdop
; // Position DOP
1962 uint16_t tdop
; // Time DOP
1963 uint16_t vdop
; // Vertical DOP
1964 uint16_t hdop
; // Horizontal DOP
1965 uint16_t ndop
; // Northing DOP
1966 uint16_t edop
; // Easting DOP
1969 typedef struct ubxNavSol_s
{
1978 uint32_t position_accuracy_3d
;
1979 int32_t ecef_x_velocity
;
1980 int32_t ecef_y_velocity
;
1981 int32_t ecef_z_velocity
;
1982 uint32_t speed_accuracy
;
1983 uint16_t position_DOP
;
1989 typedef struct ubxNavPvt_s
{
2019 uint8_t reserved0
[5];
2025 typedef struct ubxNavVelned_s
{
2026 uint32_t time
; // GPS msToW
2033 uint32_t speed_accuracy
;
2034 uint32_t heading_accuracy
;
2037 typedef struct ubxNavSvinfoChannel_s
{
2038 uint8_t chn
; // Channel number, 255 for SVx not assigned to channel
2039 uint8_t svid
; // Satellite ID
2040 uint8_t flags
; // Bitmask
2041 uint8_t quality
; // Bitfield
2042 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
2043 uint8_t elev
; // Elevation in integer degrees
2044 int16_t azim
; // Azimuth in integer degrees
2045 int32_t prRes
; // Pseudo range residual in centimetres
2046 } ubxNavSvinfoChannel_t
;
2048 typedef struct ubxNavSatSv_s
{
2050 uint8_t svId
; // Satellite ID
2051 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
2052 int8_t elev
; // Elevation in integer degrees
2053 int16_t azim
; // Azimuth in integer degrees
2054 int16_t prRes
; // Pseudo range residual in decimetres
2055 uint32_t flags
; // Bitmask
2058 typedef struct ubxNavSvinfo_s
{
2059 uint32_t time
; // GPS Millisecond time of week
2060 uint8_t numCh
; // Number of channels
2061 uint8_t globalFlags
; // Bitmask, Chip hardware generation 0:Antaris, 1:u-blox 5, 2:u-blox 6
2062 uint16_t reserved2
; // Reserved
2063 ubxNavSvinfoChannel_t channel
[GPS_SV_MAXSATS_M8N
]; // 32 satellites * 12 bytes
2066 typedef struct ubxNavSat_s
{
2067 uint32_t time
; // GPS Millisecond time of week
2070 uint8_t reserved0
[2];
2071 ubxNavSatSv_t svs
[GPS_SV_MAXSATS_M8N
];
2074 typedef struct ubxAck_s
{
2075 uint8_t clsId
; // Class ID of the acknowledged message
2076 uint8_t msgId
; // Message ID of the acknowledged message
2081 FIX_DEAD_RECKONING
= 1,
2084 FIX_GPS_DEAD_RECKONING
= 4,
2089 NAV_STATUS_FIX_VALID
= 1,
2090 NAV_STATUS_TIME_WEEK_VALID
= 4,
2091 NAV_STATUS_TIME_SECOND_VALID
= 8
2092 } ubxNavStatusBits_e
;
2099 // Do we have a new valid fix?
2100 static bool ubxHaveNewValidFix
= false;
2102 // Do we have new position information?
2103 static bool ubxHaveNewPosition
= false;
2105 // Do we have new speed information?
2106 static bool ubxHaveNewSpeed
= false;
2108 // From the UBX protocol docs, the largest payload we receive is NAV-SAT, which
2109 // is calculated as 8 + 12*numCh. Max reported sats can be up to 56.
2110 // We're using the max for M8 (32) for our sizing, since Configurator only
2111 // supports a max of 32 sats and we want to limit the payload buffer space used.
2112 #define UBLOX_PAYLOAD_SIZE (8 + 12 * GPS_SV_MAXSATS_M8N)
2113 #define UBLOX_MAX_PAYLOAD_SANITY_SIZE 776 // Any returned payload length greater than a 64 sat NAV-SAT is considered unreasonable, and probably corrupted data.
2115 // Received message frame fields.
2116 // - Preamble sync character 1 & 2 are not saved, only detected for parsing.
2117 // - Message class & message ID indicate the type of message receieved.
2118 static uint8_t ubxRcvMsgClass
;
2119 static uint8_t ubxRcvMsgID
;
2120 // - Payload length assembled from the length LSB & MSB bytes.
2121 static uint16_t ubxRcvMsgPayloadLength
;
2122 // - Payload, each message type has its own payload field layout, represented by the elements of this union.
2123 // Note that the size of the buffer is determined by the longest possible payload, currently UBX-NAV-SAT.
2124 // See size define comments above. Warning, this is fragile! If another message type becomes the largest
2125 // payload instead of UBX-NAV-SAT, UBLOX_PAYLOAD_SIZE above needs to be adjusted!
2127 ubxNavPosllh_t ubxNavPosllh
;
2128 ubxNavStatus_t ubxNavStatus
;
2129 ubxNavDop_t ubxNavDop
;
2130 ubxNavSol_t ubxNavSol
;
2131 ubxNavVelned_t ubxNavVelned
;
2132 ubxNavPvt_t ubxNavPvt
;
2133 ubxNavSvinfo_t ubxNavSvinfo
;
2134 ubxNavSat_t ubxNavSat
;
2135 ubxCfgGnss_t ubxCfgGnss
;
2136 ubxMonVer_t ubxMonVer
;
2138 uint8_t rawBytes
[UBLOX_PAYLOAD_SIZE
]; // Used for adding raw bytes to the payload. WARNING: This byte array must be as large as the largest payload for any message type above!
2140 // - Checksum A & B. Uses the 8-bit Fletcher algorithm (TCP standard RFC 1145).
2141 static uint8_t ubxRcvMsgChecksumA
;
2142 static uint8_t ubxRcvMsgChecksumB
;
2144 // Message frame parsing state machine control.
2146 UBX_PARSE_PREAMBLE_SYNC_1
,
2147 UBX_PARSE_PREAMBLE_SYNC_2
,
2148 UBX_PARSE_MESSAGE_CLASS
,
2149 UBX_PARSE_MESSAGE_ID
,
2150 UBX_PARSE_PAYLOAD_LENGTH_LSB
,
2151 UBX_PARSE_PAYLOAD_LENGTH_MSB
,
2152 UBX_PARSE_PAYLOAD_CONTENT
,
2153 UBX_PARSE_CHECKSUM_A
,
2154 UBX_PARSE_CHECKSUM_B
2155 } ubxFrameParseState_e
;
2156 static ubxFrameParseState_e ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_1
;
2157 static uint16_t ubxFrameParsePayloadCounter
;
2159 // SCEDEBUG To help debug which message is slow to process
2160 // static uint8_t lastUbxRcvMsgClass;
2161 // static uint8_t lastUbxRcvMsgID;
2163 // Combines message class & ID for a single value to switch on.
2164 #define CLSMSG(cls, msg) (((cls) << 8) | (msg))
2166 static bool UBLOX_parse_gps(void)
2168 // lastUbxRcvMsgClass = ubxRcvMsgClass;
2169 // lastUbxRcvMsgID = ubxRcvMsgID;
2171 #ifdef USE_DASHBOARD
2172 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_IGNORED
;
2174 switch (CLSMSG(ubxRcvMsgClass
, ubxRcvMsgID
)) {
2176 case CLSMSG(CLASS_MON
, MSG_MON_VER
):
2177 #ifdef USE_DASHBOARD
2178 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_MONVER
;
2180 gpsData
.platformVersion
= ubloxParseVersion(strtoul(ubxRcvMsgPayload
.ubxMonVer
.hwVersion
, NULL
, 16));
2181 gpsData
.ubloxM7orAbove
= gpsData
.platformVersion
>= UBX_VERSION_M7
;
2182 gpsData
.ubloxM8orAbove
= gpsData
.platformVersion
>= UBX_VERSION_M8
;
2183 gpsData
.ubloxM9orAbove
= gpsData
.platformVersion
>= UBX_VERSION_M9
;
2185 case CLSMSG(CLASS_NAV
, MSG_NAV_POSLLH
):
2186 #ifdef USE_DASHBOARD
2187 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_POSLLH
;
2189 //i2c_dataset.time = _buffer.ubxNavPosllh.time;
2190 gpsSol
.llh
.lon
= ubxRcvMsgPayload
.ubxNavPosllh
.longitude
;
2191 gpsSol
.llh
.lat
= ubxRcvMsgPayload
.ubxNavPosllh
.latitude
;
2192 gpsSol
.llh
.altCm
= ubxRcvMsgPayload
.ubxNavPosllh
.altitudeMslMm
/ 10; //alt in cm
2193 gpsSol
.time
= ubxRcvMsgPayload
.ubxNavPosllh
.time
;
2194 calculateNavInterval();
2195 gpsSetFixState(ubxHaveNewValidFix
);
2196 ubxHaveNewPosition
= true;
2198 case CLSMSG(CLASS_NAV
, MSG_NAV_STATUS
):
2199 #ifdef USE_DASHBOARD
2200 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_STATUS
;
2202 ubxHaveNewValidFix
= (ubxRcvMsgPayload
.ubxNavStatus
.fix_status
& NAV_STATUS_FIX_VALID
) && (ubxRcvMsgPayload
.ubxNavStatus
.fix_type
== FIX_3D
);
2203 if (!ubxHaveNewValidFix
)
2204 DISABLE_STATE(GPS_FIX
);
2206 case CLSMSG(CLASS_NAV
, MSG_NAV_DOP
):
2207 #ifdef USE_DASHBOARD
2208 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_DOP
;
2210 gpsSol
.dop
.pdop
= ubxRcvMsgPayload
.ubxNavDop
.pdop
;
2211 gpsSol
.dop
.hdop
= ubxRcvMsgPayload
.ubxNavDop
.hdop
;
2212 gpsSol
.dop
.vdop
= ubxRcvMsgPayload
.ubxNavDop
.vdop
;
2214 case CLSMSG(CLASS_NAV
, MSG_NAV_SOL
):
2215 #ifdef USE_DASHBOARD
2216 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_SOL
;
2218 ubxHaveNewValidFix
= (ubxRcvMsgPayload
.ubxNavSol
.fix_status
& NAV_STATUS_FIX_VALID
) && (ubxRcvMsgPayload
.ubxNavSol
.fix_type
== FIX_3D
);
2219 if (!ubxHaveNewValidFix
)
2220 DISABLE_STATE(GPS_FIX
);
2221 gpsSol
.numSat
= ubxRcvMsgPayload
.ubxNavSol
.satellites
;
2223 //set clock, when gps time is available
2224 if (!rtcHasTime() && (ubxRcvMsgPayload
.ubxNavSol
.fix_status
& NAV_STATUS_TIME_SECOND_VALID
) && (ubxRcvMsgPayload
.ubxNavSol
.fix_status
& NAV_STATUS_TIME_WEEK_VALID
)) {
2225 //calculate rtctime: week number * ms in a week + ms of week + fractions of second + offset to UNIX reference year - 18 leap seconds
2226 rtcTime_t temp_time
= (((int64_t) ubxRcvMsgPayload
.ubxNavSol
.week
) * 7 * 24 * 60 * 60 * 1000) + ubxRcvMsgPayload
.ubxNavSol
.time
+ (ubxRcvMsgPayload
.ubxNavSol
.time_nsec
/ 1000000) + 315964800000LL - 18000;
2231 case CLSMSG(CLASS_NAV
, MSG_NAV_VELNED
):
2232 #ifdef USE_DASHBOARD
2233 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_VELNED
;
2235 gpsSol
.speed3d
= ubxRcvMsgPayload
.ubxNavVelned
.speed_3d
; // cm/s
2236 gpsSol
.groundSpeed
= ubxRcvMsgPayload
.ubxNavVelned
.speed_2d
; // cm/s
2237 gpsSol
.groundCourse
= (uint16_t) (ubxRcvMsgPayload
.ubxNavVelned
.heading_2d
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
2238 ubxHaveNewSpeed
= true;
2240 case CLSMSG(CLASS_NAV
, MSG_NAV_PVT
):
2241 #ifdef USE_DASHBOARD
2242 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_SOL
;
2244 ubxHaveNewValidFix
= (ubxRcvMsgPayload
.ubxNavPvt
.flags
& NAV_STATUS_FIX_VALID
) && (ubxRcvMsgPayload
.ubxNavPvt
.fixType
== FIX_3D
);
2245 gpsSol
.time
= ubxRcvMsgPayload
.ubxNavPvt
.time
;
2246 calculateNavInterval();
2247 gpsSol
.llh
.lon
= ubxRcvMsgPayload
.ubxNavPvt
.lon
;
2248 gpsSol
.llh
.lat
= ubxRcvMsgPayload
.ubxNavPvt
.lat
;
2249 gpsSol
.llh
.altCm
= ubxRcvMsgPayload
.ubxNavPvt
.hMSL
/ 10; //alt in cm
2250 gpsSetFixState(ubxHaveNewValidFix
);
2251 ubxHaveNewPosition
= true;
2252 gpsSol
.numSat
= ubxRcvMsgPayload
.ubxNavPvt
.numSV
;
2253 gpsSol
.acc
.hAcc
= ubxRcvMsgPayload
.ubxNavPvt
.hAcc
;
2254 gpsSol
.acc
.vAcc
= ubxRcvMsgPayload
.ubxNavPvt
.vAcc
;
2255 gpsSol
.acc
.sAcc
= ubxRcvMsgPayload
.ubxNavPvt
.sAcc
;
2256 gpsSol
.speed3d
= (uint16_t) sqrtf(powf(ubxRcvMsgPayload
.ubxNavPvt
.gSpeed
/ 10, 2.0f
) + powf(ubxRcvMsgPayload
.ubxNavPvt
.velD
/ 10, 2.0f
));
2257 gpsSol
.groundSpeed
= ubxRcvMsgPayload
.ubxNavPvt
.gSpeed
/ 10; // cm/s
2258 gpsSol
.groundCourse
= (uint16_t) (ubxRcvMsgPayload
.ubxNavPvt
.headMot
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
2259 gpsSol
.dop
.pdop
= ubxRcvMsgPayload
.ubxNavPvt
.pDOP
;
2260 ubxHaveNewSpeed
= true;
2262 //set clock, when gps time is available
2263 if (!rtcHasTime() && (ubxRcvMsgPayload
.ubxNavPvt
.valid
& NAV_VALID_DATE
) && (ubxRcvMsgPayload
.ubxNavPvt
.valid
& NAV_VALID_TIME
)) {
2265 dt
.year
= ubxRcvMsgPayload
.ubxNavPvt
.year
;
2266 dt
.month
= ubxRcvMsgPayload
.ubxNavPvt
.month
;
2267 dt
.day
= ubxRcvMsgPayload
.ubxNavPvt
.day
;
2268 dt
.hours
= ubxRcvMsgPayload
.ubxNavPvt
.hour
;
2269 dt
.minutes
= ubxRcvMsgPayload
.ubxNavPvt
.min
;
2270 dt
.seconds
= ubxRcvMsgPayload
.ubxNavPvt
.sec
;
2271 dt
.millis
= (ubxRcvMsgPayload
.ubxNavPvt
.nano
> 0) ? ubxRcvMsgPayload
.ubxNavPvt
.nano
/ 1000000 : 0; // up to 5ms of error
2272 rtcSetDateTime(&dt
);
2276 case CLSMSG(CLASS_NAV
, MSG_NAV_SVINFO
):
2277 #ifdef USE_DASHBOARD
2278 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_SVINFO
;
2280 GPS_numCh
= MIN(ubxRcvMsgPayload
.ubxNavSvinfo
.numCh
, GPS_SV_MAXSATS_LEGACY
);
2281 // If we're receiving UBX-NAV-SVINFO messages, we detected a module version M7 or older.
2282 // We can receive far more sats than we can handle for Configurator, which is the primary consumer for sat info.
2283 // We're using the max for legacy (16) for our sizing, this smaller sizing triggers Configurator to know it's
2284 // an M7 or earlier module and to use the older sat list format.
2285 // We simply ignore any sats above that max, the down side is we may not see sats used for the solution, but
2286 // the intent in Configurator is to see if sats are being acquired and their strength, so this is not an issue.
2287 for (unsigned i
= 0; i
< ARRAYLEN(GPS_svinfo
); i
++) {
2288 if (i
< GPS_numCh
) {
2289 GPS_svinfo
[i
].chn
= ubxRcvMsgPayload
.ubxNavSvinfo
.channel
[i
].chn
;
2290 GPS_svinfo
[i
].svid
= ubxRcvMsgPayload
.ubxNavSvinfo
.channel
[i
].svid
;
2291 GPS_svinfo
[i
].quality
= ubxRcvMsgPayload
.ubxNavSvinfo
.channel
[i
].quality
;
2292 GPS_svinfo
[i
].cno
= ubxRcvMsgPayload
.ubxNavSvinfo
.channel
[i
].cno
;
2294 GPS_svinfo
[i
] = (GPS_svinfo_t
){0};
2297 #ifdef USE_DASHBOARD
2298 dashboardGpsNavSvInfoRcvCount
++;
2301 case CLSMSG(CLASS_NAV
, MSG_NAV_SAT
):
2302 #ifdef USE_DASHBOARD
2303 *dashboardGpsPacketLogCurrentChar
= DASHBOARD_LOG_UBLOX_SVINFO
; // The display log only shows SVINFO for both SVINFO and SAT.
2305 GPS_numCh
= MIN(ubxRcvMsgPayload
.ubxNavSat
.numSvs
, GPS_SV_MAXSATS_M8N
);
2306 // If we're receiving UBX-NAV-SAT messages, we detected a module M8 or newer.
2307 // We can receive far more sats than we can handle for Configurator, which is the primary consumer for sat info.
2308 // We're using the max for M8 (32) for our sizing, since Configurator only supports a max of 32 sats and we
2309 // want to limit the payload buffer space used.
2310 // We simply ignore any sats above that max, the down side is we may not see sats used for the solution, but
2311 // the intent in Configurator is to see if sats are being acquired and their strength, so this is not an issue.
2312 for (unsigned i
= 0; i
< ARRAYLEN(GPS_svinfo
); i
++) {
2313 if (i
< GPS_numCh
) {
2314 GPS_svinfo
[i
].chn
= ubxRcvMsgPayload
.ubxNavSat
.svs
[i
].gnssId
;
2315 GPS_svinfo
[i
].svid
= ubxRcvMsgPayload
.ubxNavSat
.svs
[i
].svId
;
2316 GPS_svinfo
[i
].cno
= ubxRcvMsgPayload
.ubxNavSat
.svs
[i
].cno
;
2317 GPS_svinfo
[i
].quality
= ubxRcvMsgPayload
.ubxNavSat
.svs
[i
].flags
;
2319 GPS_svinfo
[i
] = (GPS_svinfo_t
){ .chn
= 255 };
2323 // Setting the number of channels higher than GPS_SV_MAXSATS_LEGACY is the only way to tell BF Configurator we're sending the
2324 // enhanced sat list info without changing the MSP protocol. Also, we're sending the complete list each time even if it's empty, so
2325 // BF Conf can erase old entries shown on screen when channels are removed from the list.
2326 // TODO: GPS_numCh = MAX(GPS_numCh, GPS_SV_MAXSATS_LEGACY + 1);
2327 GPS_numCh
= GPS_SV_MAXSATS_M8N
;
2328 #ifdef USE_DASHBOARD
2329 dashboardGpsNavSvInfoRcvCount
++;
2332 case CLSMSG(CLASS_CFG
, MSG_CFG_GNSS
):
2334 const uint16_t messageSize
= 4 + (ubxRcvMsgPayload
.ubxCfgGnss
.numConfigBlocks
* sizeof(ubxConfigblock_t
));
2335 ubxMessage_t tx_buffer
;
2337 // prevent buffer overflow on invalid numConfigBlocks
2338 const int size
= MIN(messageSize
, sizeof(tx_buffer
.payload
));
2339 memcpy(&tx_buffer
.payload
, &ubxRcvMsgPayload
, size
);
2341 for (int i
= 0; i
< ubxRcvMsgPayload
.ubxCfgGnss
.numConfigBlocks
; i
++) {
2342 if (ubxRcvMsgPayload
.ubxCfgGnss
.configblocks
[i
].gnssId
== UBLOX_GNSS_SBAS
) {
2343 if (gpsConfig()->sbasMode
== SBAS_NONE
) {
2344 tx_buffer
.payload
.cfg_gnss
.configblocks
[i
].flags
&= ~UBLOX_GNSS_ENABLE
; // Disable SBAS
2348 if (ubxRcvMsgPayload
.ubxCfgGnss
.configblocks
[i
].gnssId
== UBLOX_GNSS_GALILEO
) {
2349 if (gpsConfig()->gps_ublox_use_galileo
) {
2350 tx_buffer
.payload
.cfg_gnss
.configblocks
[i
].flags
|= UBLOX_GNSS_ENABLE
; // Enable Galileo
2352 tx_buffer
.payload
.cfg_gnss
.configblocks
[i
].flags
&= ~UBLOX_GNSS_ENABLE
; // Disable Galileo
2357 ubloxSendConfigMessage(&tx_buffer
, MSG_CFG_GNSS
, messageSize
, false);
2360 case CLSMSG(CLASS_ACK
, MSG_ACK_ACK
):
2361 if ((gpsData
.ackState
== UBLOX_ACK_WAITING
) && (ubxRcvMsgPayload
.ubxAck
.msgId
== gpsData
.ackWaitingMsgId
)) {
2362 gpsData
.ackState
= UBLOX_ACK_GOT_ACK
;
2365 case CLSMSG(CLASS_ACK
, MSG_ACK_NACK
):
2366 if ((gpsData
.ackState
== UBLOX_ACK_WAITING
) && (ubxRcvMsgPayload
.ubxAck
.msgId
== gpsData
.ackWaitingMsgId
)) {
2367 gpsData
.ackState
= UBLOX_ACK_GOT_NACK
;
2376 // we only return true when we get new position and speed data
2377 // this ensures we don't use stale data
2378 if (ubxHaveNewPosition
&& ubxHaveNewSpeed
) {
2379 ubxHaveNewSpeed
= ubxHaveNewPosition
= false;
2385 static bool gpsNewFrameUBLOX(uint8_t data
)
2387 bool newPositionDataReceived
= false;
2389 switch (ubxFrameParseState
) {
2390 case UBX_PARSE_PREAMBLE_SYNC_1
:
2391 if (PREAMBLE1
== data
) {
2392 // Might be a new UBX message, go on to look for next preamble byte.
2393 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_2
;
2396 // Not a new UBX message, stay in this state for the next incoming byte.
2398 case UBX_PARSE_PREAMBLE_SYNC_2
:
2399 if (PREAMBLE2
== data
) {
2400 // Matches the two-byte preamble, seems to be a legit message, go on to process the rest of the message.
2401 ubxFrameParseState
= UBX_PARSE_MESSAGE_CLASS
;
2404 // False start, if this byte is not a preamble 1, restart new message parsing.
2405 // If this byte is a preamble 1, we might have gotten two in a row, so stay here and look for preamble 2 again.
2406 if (PREAMBLE1
!= data
) {
2407 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_1
;
2410 case UBX_PARSE_MESSAGE_CLASS
:
2411 ubxRcvMsgChecksumB
= ubxRcvMsgChecksumA
= data
; // Reset & start the checksum A & B accumulators.
2412 ubxRcvMsgClass
= data
;
2413 ubxFrameParseState
= UBX_PARSE_MESSAGE_ID
;
2415 case UBX_PARSE_MESSAGE_ID
:
2416 ubxRcvMsgChecksumB
+= (ubxRcvMsgChecksumA
+= data
); // Accumulate both checksums.
2418 ubxFrameParseState
= UBX_PARSE_PAYLOAD_LENGTH_LSB
;
2420 case UBX_PARSE_PAYLOAD_LENGTH_LSB
:
2421 ubxRcvMsgChecksumB
+= (ubxRcvMsgChecksumA
+= data
);
2422 ubxRcvMsgPayloadLength
= data
; // Payload length LSB.
2423 ubxFrameParseState
= UBX_PARSE_PAYLOAD_LENGTH_MSB
;
2425 case UBX_PARSE_PAYLOAD_LENGTH_MSB
:
2426 ubxRcvMsgChecksumB
+= (ubxRcvMsgChecksumA
+= data
); // Accumulate both checksums.
2427 ubxRcvMsgPayloadLength
+= (uint16_t)(data
<< 8); //Payload length MSB.
2428 if (ubxRcvMsgPayloadLength
== 0) {
2429 // No payload for this message, skip to checksum checking.
2430 ubxFrameParseState
= UBX_PARSE_CHECKSUM_A
;
2433 if (ubxRcvMsgPayloadLength
> UBLOX_MAX_PAYLOAD_SANITY_SIZE
) {
2434 // Payload length is not reasonable, treat as a bad packet, restart new message parsing.
2435 // Note that we do not parse the rest of the message, better to leave it and look for a new message.
2436 #ifdef USE_DASHBOARD
2437 logErrorToPacketLog();
2439 if (PREAMBLE1
== data
) {
2440 // If this byte is a preamble 1 value, it might be a new message, so look for preamble 2 instead of starting over.
2441 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_2
;
2443 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_1
;
2447 // Payload length seems legit, go on to receive the payload content.
2448 ubxFrameParsePayloadCounter
= 0;
2449 ubxFrameParseState
= UBX_PARSE_PAYLOAD_CONTENT
;
2451 case UBX_PARSE_PAYLOAD_CONTENT
:
2452 ubxRcvMsgChecksumB
+= (ubxRcvMsgChecksumA
+= data
); // Accumulate both checksums.
2453 if (ubxFrameParsePayloadCounter
< UBLOX_PAYLOAD_SIZE
) {
2454 // Only add bytes to the buffer if we haven't reached the max supported payload size.
2455 // Note that we still read & checksum every byte so the checksum calculates correctly.
2456 ubxRcvMsgPayload
.rawBytes
[ubxFrameParsePayloadCounter
] = data
;
2458 if (++ubxFrameParsePayloadCounter
>= ubxRcvMsgPayloadLength
) {
2459 // All bytes for payload length processed.
2460 ubxFrameParseState
= UBX_PARSE_CHECKSUM_A
;
2463 // More payload content left, stay in this state.
2465 case UBX_PARSE_CHECKSUM_A
:
2466 if (ubxRcvMsgChecksumA
== data
) {
2467 // Checksum A matches, go on to checksum B.
2468 ubxFrameParseState
= UBX_PARSE_CHECKSUM_B
;
2471 // Bad checksum A, restart new message parsing.
2472 // Note that we do not parse checksum B, new message processing will handle skipping it if needed.
2473 #ifdef USE_DASHBOARD
2474 logErrorToPacketLog();
2476 if (PREAMBLE1
== data
) {
2477 // If this byte is a preamble 1 value, it might be a new message, so look for preamble 2 instead of starting over.
2478 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_2
;
2480 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_1
;
2483 case UBX_PARSE_CHECKSUM_B
:
2484 if (ubxRcvMsgChecksumB
== data
) {
2485 // Checksum B also matches, successfully received a new full packet!
2486 #ifdef USE_DASHBOARD
2487 dashboardGpsPacketCount
++; // Packet counter used by dashboard device.
2488 shiftPacketLog(); // Make space for message handling to add the message type char to the dashboard device packet log.
2490 // Handle the parsed message. Note this is a questionable inverted call dependency, but something for a later refactoring.
2491 newPositionDataReceived
= UBLOX_parse_gps(); // True only when we have new position data from the parsed message.
2492 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_1
; // Restart new message parsing.
2495 // Bad checksum B, restart new message parsing.
2496 #ifdef USE_DASHBOARD
2497 logErrorToPacketLog();
2499 if (PREAMBLE1
== data
) {
2500 // If this byte is a preamble 1 value, it might be a new message, so look for preamble 2 instead of starting over.
2501 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_2
;
2503 ubxFrameParseState
= UBX_PARSE_PREAMBLE_SYNC_1
;
2508 // Note this function returns if UBLOX_parse_gps() found new position data, NOT whether this function successfully parsed the frame or not.
2509 return newPositionDataReceived
;
2511 #endif // USE_GPS_UBLOX
2513 static void gpsHandlePassthrough(uint8_t data
)
2516 #ifdef USE_DASHBOARD
2517 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
2518 // Should be handled via a generic callback hook, so the GPS module doesn't have to be coupled to the dashboard module.
2519 dashboardUpdate(micros());
2524 // forward GPS data to specified port (used by CLI)
2525 // return false if forwarding failed
2526 // curently only way to stop forwarding is to reset the board
2527 bool gpsPassthrough(serialPort_t
*gpsPassthroughPort
)
2530 // GPS port is not open for some reason - no GPS, MSP GPS, ..
2533 waitForSerialPortToFinishTransmitting(gpsPort
);
2534 waitForSerialPortToFinishTransmitting(gpsPassthroughPort
);
2536 if (!(gpsPort
->mode
& MODE_TX
)) {
2537 // try to switch TX mode on
2538 serialSetMode(gpsPort
, gpsPort
->mode
| MODE_TX
);
2541 #ifdef USE_DASHBOARD
2542 if (featureIsEnabled(FEATURE_DASHBOARD
)) {
2543 // Should be handled via a generic callback hook, so the GPS module doesn't have to be coupled to the dashboard module.
2544 dashboardShowFixedPage(PAGE_GPS
);
2548 serialPassthrough(gpsPort
, gpsPassthroughPort
, &gpsHandlePassthrough
, NULL
);
2549 // allow exitting passthrough mode in future
2553 float GPS_cosLat
= 1.0f
; // this is used to offset the shrinking longitude as we go towards the poles
2554 // longitude difference * scale is approximate distance in degrees
2556 void GPS_calc_longitude_scaling(int32_t lat
)
2558 GPS_cosLat
= cos_approx(DEGREES_TO_RADIANS((float)lat
/ GPS_DEGREES_DIVIDER
));
2561 ////////////////////////////////////////////////////////////////////////////////////
2562 // Calculate the distance flown from gps position data
2564 static void GPS_calculateDistanceFlown(bool initialize
)
2566 static gpsLocation_t lastLLH
= {0};
2569 GPS_distanceFlownInCm
= 0;
2571 if (STATE(GPS_FIX_HOME
) && ARMING_FLAG(ARMED
)) {
2572 uint16_t speed
= gpsConfig()->gps_use_3d_speed
? gpsSol
.speed3d
: gpsSol
.groundSpeed
;
2573 // Only add up movement when speed is faster than minimum threshold
2574 if (speed
> GPS_DISTANCE_FLOWN_MIN_SPEED_THRESHOLD_CM_S
) {
2576 GPS_distance_cm_bearing(&gpsSol
.llh
, &lastLLH
, gpsConfig()->gps_use_3d_speed
, &dist
, NULL
);
2577 GPS_distanceFlownInCm
+= dist
;
2581 lastLLH
= gpsSol
.llh
;
2584 void GPS_reset_home_position(void)
2585 // runs, if GPS is defined, on arming via tryArm() in core.c, and on gyro cal via processRcStickPositions() in rc_controls.c
2587 if (!STATE(GPS_FIX_HOME
) || !gpsConfig()->gps_set_home_point_once
) {
2588 if (STATE(GPS_FIX
) && gpsSol
.numSat
>= gpsRescueConfig()->minSats
) {
2589 // those checks are always true for tryArm, but may not be true for gyro cal
2590 GPS_home_llh
= gpsSol
.llh
;
2591 GPS_calc_longitude_scaling(gpsSol
.llh
.lat
);
2592 ENABLE_STATE(GPS_FIX_HOME
);
2593 // no point beeping success here since:
2594 // when triggered by tryArm, the arming beep is modified to indicate the GPS home fix status on arming, and
2595 // when triggered by gyro cal, the gyro cal beep takes priority over the GPS beep, so we won't hear the GPS beep
2596 // PS: to test for gyro cal, check for !ARMED, since we cannot be here while disarmed other than via gyro cal
2600 #ifdef USE_GPS_UBLOX
2601 // disable Sat Info requests on arming
2602 if (gpsConfig()->provider
== GPS_UBLOX
) {
2603 setSatInfoMessageRate(0);
2606 GPS_calculateDistanceFlown(true); // Initialize
2609 ////////////////////////////////////////////////////////////////////////////////////
2610 // Get distance between two points in cm using spherical to Cartesian transform
2611 // One one latitude unit, or one longitude unit at the equator, equals 1.113195 cm.
2612 // Get bearing from pos1 to pos2, returns values with 0.01 degree precision
2613 void GPS_distance_cm_bearing(const gpsLocation_t
*from
, const gpsLocation_t
* to
, bool dist3d
, uint32_t *pDist
, int32_t *pBearing
)
2615 // TO DO : handle crossing the 180 degree meridian, as in `GPS_distance2d()`
2616 float dLat
= (to
->lat
- from
->lat
) * EARTH_ANGLE_TO_CM
;
2617 float dLon
= (to
->lon
- from
->lon
) * GPS_cosLat
* EARTH_ANGLE_TO_CM
; // convert to local angle
2618 float dAlt
= dist3d
? to
->altCm
- from
->altCm
: 0;
2621 *pDist
= sqrtf(sq(dLat
) + sq(dLon
) + sq(dAlt
));
2624 int32_t bearing
= 9000.0f
- RADIANS_TO_DEGREES(atan2_approx(dLat
, dLon
)) * 100.0f
; // Convert the output to 100xdeg / adjust to clockwise from North
2627 *pBearing
= bearing
;
2631 static void GPS_calculateDistanceAndDirectionToHome(void)
2633 if (STATE(GPS_FIX_HOME
)) {
2636 GPS_distance_cm_bearing(&gpsSol
.llh
, &GPS_home_llh
, false, &dist
, &dir
);
2637 GPS_distanceToHome
= dist
/ 100; // m
2638 GPS_distanceToHomeCm
= dist
; // cm
2639 GPS_directionToHome
= dir
/ 10; // degrees * 10 or decidegrees
2641 // If we don't have home set, do not display anything
2642 GPS_distanceToHome
= 0;
2643 GPS_distanceToHomeCm
= 0;
2644 GPS_directionToHome
= 0;
2648 // return distance vector in local, cartesian ENU coordinates
2649 // note that parameter order is from, to
2650 void GPS_distance2d(const gpsLocation_t
*from
, const gpsLocation_t
*to
, vector2_t
*distance
)
2652 int32_t deltaLon
= to
->lon
- from
->lon
;
2653 // In case we crossed the 180° meridian:
2654 const int32_t deg180
= 180 * GPS_DEGREES_DIVIDER
; // number of integer longitude steps in 180 degrees
2655 if (deltaLon
> deg180
) {
2656 deltaLon
-= deg180
; // 360 * GPS_DEGREES_DIVIDER overflows int32_t, so use 180 twice
2658 } else if (deltaLon
<= -deg180
) {
2662 distance
->x
= deltaLon
* GPS_cosLat
* EARTH_ANGLE_TO_CM
; // East-West distance, positive East
2663 distance
->y
= (float)(to
->lat
- from
->lat
) * EARTH_ANGLE_TO_CM
; // North-South distance, positive North
2666 void onGpsNewData(void)
2668 if (!STATE(GPS_FIX
)) {
2669 // if we don't have a 3D fix don't give data to GPS rescue
2673 currentGpsStamp
++; // new GPS data available
2675 gpsDataIntervalSeconds
= gpsSol
.navIntervalMs
* 0.001f
; // range for navIntervalMs is constrained to 50 - 2500
2676 gpsDataFrequencyHz
= 1.0f
/ gpsDataIntervalSeconds
;
2678 GPS_calculateDistanceAndDirectionToHome();
2679 if (ARMING_FLAG(ARMED
)) {
2680 GPS_calculateDistanceFlown(false);
2683 #ifdef USE_GPS_LAP_TIMER
2684 gpsLapTimerNewGpsData();
2685 #endif // USE_GPS_LAP_TIMER
2689 // check if new data has been received since last check
2690 // if client stamp is initialized to 0, gpsHasNewData will return false until first GPS position update
2691 // if client stamp is initialized to ~0, gpsHasNewData will return true on first call
2692 bool gpsHasNewData(uint16_t* stamp
) {
2693 if (*stamp
!= currentGpsStamp
) {
2694 *stamp
= currentGpsStamp
;
2701 void gpsSetFixState(bool state
)
2704 ENABLE_STATE(GPS_FIX
);
2705 ENABLE_STATE(GPS_FIX_EVER
);
2707 DISABLE_STATE(GPS_FIX
);
2711 float getGpsDataIntervalSeconds(void)
2713 return gpsDataIntervalSeconds
;
2716 float getGpsDataFrequencyHz(void)
2718 return gpsDataFrequencyHz
;
2721 baudRate_e
getGpsPortActualBaudRateIndex(void)
2723 return lookupBaudRateIndex(serialGetBaudRate(gpsPort
));