2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
26 #include "build/build_config.h"
28 #if defined(USE_GPS) && defined(USE_GPS_PROTO_UBLOX)
30 #include "build/debug.h"
33 #include "common/axis.h"
34 #include "common/typeconversion.h"
35 #include "common/gps_conversion.h"
36 #include "common/maths.h"
37 #include "common/utils.h"
39 #include "drivers/serial.h"
40 #include "drivers/time.h"
42 #include "fc/config.h"
43 #include "fc/runtime_config.h"
45 #include "io/serial.h"
47 #include "io/gps_private.h"
49 #include "scheduler/protothreads.h"
51 #define GPS_CFG_CMD_TIMEOUT_MS 200
52 #define GPS_VERSION_RETRY_TIMES 2
53 #define MAX_UBLOX_PAYLOAD_SIZE 256
54 #define UBLOX_BUFFER_SIZE MAX_UBLOX_PAYLOAD_SIZE
55 #define UBLOX_SBAS_MESSAGE_LENGTH 16
57 #define UBX_DYNMODEL_PEDESTRIAN 3
58 #define UBX_DYNMODEL_AIR_1G 6
59 #define UBX_DYNMODEL_AIR_4G 8
61 #define UBX_FIXMODE_2D_ONLY 1
62 #define UBX_FIXMODE_3D_ONLY 2
63 #define UBX_FIXMODE_AUTO 3
65 #define UBX_VALID_GPS_DATE(valid) (valid & 1 << 0)
66 #define UBX_VALID_GPS_TIME(valid) (valid & 1 << 1)
67 #define UBX_VALID_GPS_DATE_TIME(valid) (UBX_VALID_GPS_DATE(valid) && UBX_VALID_GPS_TIME(valid))
69 // SBAS_AUTO, SBAS_EGNOS, SBAS_WAAS, SBAS_MSAS, SBAS_GAGAN, SBAS_NONE
70 static const uint32_t ubloxScanMode1
[] = {
71 0x00000000, 0x00000851, 0x0004E004, 0x00020200, 0x00000180, 0x00000000,
74 static const char * baudInitDataNMEA
[GPS_BAUDRATE_COUNT
] = {
75 "$PUBX,41,1,0003,0001,115200,0*1E\r\n", // GPS_BAUDRATE_115200
76 "$PUBX,41,1,0003,0001,57600,0*2D\r\n", // GPS_BAUDRATE_57600
77 "$PUBX,41,1,0003,0001,38400,0*26\r\n", // GPS_BAUDRATE_38400
78 "$PUBX,41,1,0003,0001,19200,0*23\r\n", // GPS_BAUDRATE_19200
79 "$PUBX,41,1,0003,0001,9600,0*16\r\n" // GPS_BAUDRATE_9600
104 uint8_t bytes
[60]; // sizeof Galileo config
122 } __attribute__((packed
)) ubx_message
;
125 char swVersion
[30]; // Zero-terminated Software Version String
126 char hwVersion
[10]; // Zero-terminated Hardware Version String
130 uint32_t time
; // GPS msToW
133 int32_t altitude_ellipsoid
;
134 int32_t altitude_msl
;
135 uint32_t horizontal_accuracy
;
136 uint32_t vertical_accuracy
;
140 uint32_t time
; // GPS msToW
143 uint8_t differential_status
;
145 uint32_t time_to_first_fix
;
146 uint32_t uptime
; // milliseconds
158 uint32_t position_accuracy_3d
;
159 int32_t ecef_x_velocity
;
160 int32_t ecef_y_velocity
;
161 int32_t ecef_z_velocity
;
162 uint32_t speed_accuracy
;
163 uint16_t position_DOP
;
170 uint32_t time
; // GPS msToW
177 uint32_t speed_accuracy
;
178 uint32_t heading_accuracy
;
182 uint8_t chn
; // Channel number, 255 for SVx not assigned to channel
183 uint8_t svid
; // Satellite ID
184 uint8_t flags
; // Bitmask
185 uint8_t quality
; // Bitfield
186 uint8_t cno
; // Carrier to Noise Ratio (Signal Strength) // dbHz, 0-55.
187 uint8_t elev
; // Elevation in integer degrees
188 int16_t azim
; // Azimuth in integer degrees
189 int32_t prRes
; // Pseudo range residual in centimetres
190 } ubx_nav_svinfo_channel
;
193 uint32_t time
; // GPS Millisecond time of week
194 uint8_t numCh
; // Number of channels
195 uint8_t globalFlags
; // Bitmask, Chip hardware generation 0:Antaris, 1:u-blox 5, 2:u-blox 6
196 uint16_t reserved2
; // Reserved
197 ubx_nav_svinfo_channel channel
[16]; // 16 satellites * 12 byte
201 uint32_t time
; // GPS msToW
214 uint32_t time
; // GPS msToW
230 int32_t altitude_ellipsoid
;
231 int32_t altitude_msl
;
232 uint32_t horizontal_accuracy
;
233 uint32_t vertical_accuracy
;
239 uint32_t speed_accuracy
;
240 uint32_t heading_accuracy
;
241 uint16_t position_DOP
;
258 MSG_CLASS_UBX
= 0x01,
259 MSG_CLASS_NMEA
= 0xF0,
278 MSG_CFG_SET_RATE
= 0x01,
279 MSG_CFG_NAV_SETTINGS
= 0x24,
282 } ubx_protocol_bytes
;
286 FIX_DEAD_RECKONING
= 1,
289 FIX_GPS_DEAD_RECKONING
= 4,
294 NAV_STATUS_FIX_VALID
= 1
295 } ubx_nav_status_bits
;
303 // Packet checksum accumulators
304 static uint8_t _ck_a
;
305 static uint8_t _ck_b
;
307 // State machine state
308 static bool _skip_packet
;
309 static uint8_t _step
;
310 static uint8_t _msg_id
;
311 static uint16_t _payload_length
;
312 static uint16_t _payload_counter
;
314 static uint8_t next_fix_type
;
315 static uint8_t _class
;
316 static uint8_t _ack_state
;
317 static uint8_t _ack_waiting_msg
;
319 // do we have new position information?
320 static bool _new_position
;
322 // do we have new speed information?
323 static bool _new_speed
;
325 // Need this to determine if Galileo capable only
326 static bool capGalileo
;
328 // Example packet sizes from UBlox u-center from a Glonass capable GPS receiver.
329 //15:17:55 R -> UBX NAV-STATUS, Size 24, 'Navigation Status'
330 //15:17:55 R -> UBX NAV-POSLLH, Size 36, 'Geodetic Position'
331 //15:17:55 R -> UBX NAV-VELNED, Size 44, 'Velocity in WGS 84'
332 //15:17:55 R -> UBX NAV-CLOCK, Size 28, 'Clock Status'
333 //15:17:55 R -> UBX NAV-AOPSTATUS, Size 24, 'AOP Status'
334 //15:17:55 R -> UBX 03-09, Size 208, 'Unknown'
335 //15:17:55 R -> UBX 03-10, Size 336, 'Unknown'
336 //15:17:55 R -> UBX NAV-SOL, Size 60, 'Navigation Solution'
337 //15:17:55 R -> UBX NAV, Size 100, 'Navigation'
338 //15:17:55 R -> UBX NAV-SVINFO, Size 328, 'Satellite Status and Information'
349 ubx_nav_posllh posllh
;
350 ubx_nav_status status
;
351 ubx_nav_solution solution
;
352 ubx_nav_velned velned
;
354 ubx_nav_svinfo svinfo
;
356 ubx_nav_timeutc timeutc
;
358 uint8_t bytes
[UBLOX_BUFFER_SIZE
];
361 void _update_checksum(uint8_t *data
, uint8_t len
, uint8_t *ck_a
, uint8_t *ck_b
)
370 static uint8_t gpsMapFixType(bool fixValid
, uint8_t ubloxFixType
)
372 if (fixValid
&& ubloxFixType
== FIX_2D
)
374 if (fixValid
&& ubloxFixType
== FIX_3D
)
379 static void sendConfigMessageUBLOX(void)
381 uint8_t ck_a
=0, ck_b
=0;
382 send_buffer
.message
.header
.preamble1
=PREAMBLE1
;
383 send_buffer
.message
.header
.preamble2
=PREAMBLE2
;
384 _update_checksum(&send_buffer
.bytes
[2], send_buffer
.message
.header
.length
+4, &ck_a
, &ck_b
);
385 send_buffer
.bytes
[send_buffer
.message
.header
.length
+6] = ck_a
;
386 send_buffer
.bytes
[send_buffer
.message
.header
.length
+7] = ck_b
;
387 serialWriteBuf(gpsState
.gpsPort
, send_buffer
.bytes
, send_buffer
.message
.header
.length
+8);
389 // Save state for ACK waiting
390 _ack_waiting_msg
= send_buffer
.message
.header
.msg_id
;
391 _ack_state
= UBX_ACK_WAITING
;
394 static void pollVersion(void)
396 send_buffer
.message
.header
.msg_class
= CLASS_MON
;
397 send_buffer
.message
.header
.msg_id
= MSG_VER
;
398 send_buffer
.message
.header
.length
= 0;
399 sendConfigMessageUBLOX();
402 static const uint8_t default_payload
[] = {
403 0xFF, 0xFF, 0x03, 0x03, 0x00, // CFG-NAV5 - Set engine settings (original MWII code)
404 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00, 0x05, 0x00, 0xFA, 0x00, // Collected by resetting a GPS unit to defaults. Changing mode to Pedistrian and
405 0xFA, 0x00, 0x64, 0x00, 0x2C, 0x01, 0x00, 0x3C, 0x00, 0x00, 0x00, // capturing the data from the U-Center binary console.
406 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
409 // Note the organisation of the bytes reflects the structure of the payload
410 // 4 bytes then 8*number of elements (7)
411 static const uint8_t galileo_payload
[] = {
412 0x00, 0x00, 0x20, 0x07, // GNSS / min / max / enable
413 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS / 8 / 16 / Y
414 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS / 1 / 3 / Y
415 0x02, 0x04, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, // Galileo / 4 / 8 / Y
416 0x03, 0x08, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01, // BeiDou / 8 / 16 / N
417 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, // IMES / 0 / 8 / N
418 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01, // QZSS / 0 / 3 / N
419 0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01 // GLONASS / 8 / 14 / Y
422 static void configureGalileo(void)
424 send_buffer
.message
.header
.msg_class
= CLASS_CFG
;
425 send_buffer
.message
.header
.msg_id
= MSG_CFG_GNSS
;
426 send_buffer
.message
.header
.length
= sizeof(galileo_payload
);
427 memcpy(send_buffer
.message
.payload
.bytes
, galileo_payload
, sizeof(galileo_payload
));
428 sendConfigMessageUBLOX();
431 static void configureNAV5(uint8_t dynModel
, uint8_t fixMode
)
433 send_buffer
.message
.header
.msg_class
= CLASS_CFG
;
434 send_buffer
.message
.header
.msg_id
= MSG_CFG_NAV_SETTINGS
;
435 send_buffer
.message
.header
.length
= 0x24;
436 memcpy(send_buffer
.message
.payload
.bytes
, default_payload
, sizeof(default_payload
));
437 send_buffer
.message
.payload
.bytes
[2] = dynModel
;
438 send_buffer
.message
.payload
.bytes
[3] = fixMode
;
439 sendConfigMessageUBLOX();
442 static void configureMSG(uint8_t class, uint8_t id
, uint8_t rate
)
444 send_buffer
.message
.header
.msg_class
= CLASS_CFG
;
445 send_buffer
.message
.header
.msg_id
= MSG_CFG_SET_RATE
;
446 send_buffer
.message
.header
.length
= 3;
447 send_buffer
.message
.payload
.msg
.class = class;
448 send_buffer
.message
.payload
.msg
.id
= id
;
449 send_buffer
.message
.payload
.msg
.rate
= rate
;
450 sendConfigMessageUBLOX();
456 * timeRef 0 UTC, 1 GPS
458 static void configureRATE(uint16_t measRate
)
460 send_buffer
.message
.header
.msg_class
= CLASS_CFG
;
461 send_buffer
.message
.header
.msg_id
= MSG_CFG_RATE
;
462 send_buffer
.message
.header
.length
= 6;
463 send_buffer
.message
.payload
.rate
.meas
=measRate
;
464 send_buffer
.message
.payload
.rate
.nav
=1;
465 send_buffer
.message
.payload
.rate
.time
=1;
466 sendConfigMessageUBLOX();
471 static void configureSBAS(void)
473 send_buffer
.message
.header
.msg_class
= CLASS_CFG
;
474 send_buffer
.message
.header
.msg_id
= MSG_CFG_SBAS
;
475 send_buffer
.message
.header
.length
= 8;
476 send_buffer
.message
.payload
.sbas
.mode
=(gpsState
.gpsConfig
->sbasMode
== SBAS_NONE
?2:3);
477 send_buffer
.message
.payload
.sbas
.usage
=3;
478 send_buffer
.message
.payload
.sbas
.maxSBAS
=3;
479 send_buffer
.message
.payload
.sbas
.scanmode2
=0;
480 send_buffer
.message
.payload
.sbas
.scanmode1
=ubloxScanMode1
[gpsState
.gpsConfig
->sbasMode
];
481 sendConfigMessageUBLOX();
484 static bool gpsParceFrameUBLOX(void)
488 //i2c_dataset.time = _buffer.posllh.time;
489 gpsSol
.llh
.lon
= _buffer
.posllh
.longitude
;
490 gpsSol
.llh
.lat
= _buffer
.posllh
.latitude
;
491 gpsSol
.llh
.alt
= _buffer
.posllh
.altitude_msl
/ 10; //alt in cm
492 gpsSol
.eph
= gpsConstrainEPE(_buffer
.posllh
.horizontal_accuracy
/ 10);
493 gpsSol
.epv
= gpsConstrainEPE(_buffer
.posllh
.vertical_accuracy
/ 10);
494 gpsSol
.flags
.validEPE
= 1;
495 if (next_fix_type
!= GPS_NO_FIX
)
496 gpsSol
.fixType
= next_fix_type
;
497 _new_position
= true;
500 next_fix_type
= gpsMapFixType(_buffer
.status
.fix_status
& NAV_STATUS_FIX_VALID
, _buffer
.status
.fix_type
);
501 if (next_fix_type
== GPS_NO_FIX
)
502 gpsSol
.fixType
= GPS_NO_FIX
;
505 next_fix_type
= gpsMapFixType(_buffer
.solution
.fix_status
& NAV_STATUS_FIX_VALID
, _buffer
.solution
.fix_type
);
506 if (next_fix_type
== GPS_NO_FIX
)
507 gpsSol
.fixType
= GPS_NO_FIX
;
508 gpsSol
.numSat
= _buffer
.solution
.satellites
;
509 gpsSol
.hdop
= gpsConstrainHDOP(_buffer
.solution
.position_DOP
);
512 gpsSol
.groundSpeed
= _buffer
.velned
.speed_2d
; // cm/s
513 gpsSol
.groundCourse
= (uint16_t) (_buffer
.velned
.heading_2d
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
514 gpsSol
.velNED
[0] = _buffer
.velned
.ned_north
;
515 gpsSol
.velNED
[1] = _buffer
.velned
.ned_east
;
516 gpsSol
.velNED
[2] = _buffer
.velned
.ned_down
;
517 gpsSol
.flags
.validVelNE
= 1;
518 gpsSol
.flags
.validVelD
= 1;
522 if (UBX_VALID_GPS_DATE_TIME(_buffer
.timeutc
.valid
)) {
523 gpsSol
.time
.year
= _buffer
.timeutc
.year
;
524 gpsSol
.time
.month
= _buffer
.timeutc
.month
;
525 gpsSol
.time
.day
= _buffer
.timeutc
.day
;
526 gpsSol
.time
.hours
= _buffer
.timeutc
.hour
;
527 gpsSol
.time
.minutes
= _buffer
.timeutc
.min
;
528 gpsSol
.time
.seconds
= _buffer
.timeutc
.sec
;
529 gpsSol
.time
.millis
= _buffer
.timeutc
.nano
/ (1000*1000);
531 gpsSol
.flags
.validTime
= 1;
533 gpsSol
.flags
.validTime
= 0;
537 next_fix_type
= gpsMapFixType(_buffer
.pvt
.fix_status
& NAV_STATUS_FIX_VALID
, _buffer
.pvt
.fix_type
);
538 gpsSol
.fixType
= next_fix_type
;
539 gpsSol
.llh
.lon
= _buffer
.pvt
.longitude
;
540 gpsSol
.llh
.lat
= _buffer
.pvt
.latitude
;
541 gpsSol
.llh
.alt
= _buffer
.pvt
.altitude_msl
/ 10; //alt in cm
542 gpsSol
.velNED
[X
]=_buffer
.pvt
.ned_north
/ 10; // to cm/s
543 gpsSol
.velNED
[Y
]=_buffer
.pvt
.ned_east
/ 10; // to cm/s
544 gpsSol
.velNED
[Z
]=_buffer
.pvt
.ned_down
/ 10; // to cm/s
545 gpsSol
.groundSpeed
= _buffer
.pvt
.speed_2d
/ 10; // to cm/s
546 gpsSol
.groundCourse
= (uint16_t) (_buffer
.pvt
.heading_2d
/ 10000); // Heading 2D deg * 100000 rescaled to deg * 10
547 gpsSol
.numSat
= _buffer
.pvt
.satellites
;
548 gpsSol
.eph
= gpsConstrainEPE(_buffer
.pvt
.horizontal_accuracy
/ 10);
549 gpsSol
.epv
= gpsConstrainEPE(_buffer
.pvt
.vertical_accuracy
/ 10);
550 gpsSol
.hdop
= gpsConstrainHDOP(_buffer
.pvt
.position_DOP
);
551 gpsSol
.flags
.validVelNE
= 1;
552 gpsSol
.flags
.validVelD
= 1;
553 gpsSol
.flags
.validEPE
= 1;
555 if (UBX_VALID_GPS_DATE_TIME(_buffer
.pvt
.valid
)) {
556 gpsSol
.time
.year
= _buffer
.pvt
.year
;
557 gpsSol
.time
.month
= _buffer
.pvt
.month
;
558 gpsSol
.time
.day
= _buffer
.pvt
.day
;
559 gpsSol
.time
.hours
= _buffer
.pvt
.hour
;
560 gpsSol
.time
.minutes
= _buffer
.pvt
.min
;
561 gpsSol
.time
.seconds
= _buffer
.pvt
.sec
;
562 gpsSol
.time
.millis
= _buffer
.pvt
.nano
/ (1000*1000);
564 gpsSol
.flags
.validTime
= 1;
566 gpsSol
.flags
.validTime
= 0;
569 _new_position
= true;
573 if (_class
== CLASS_MON
) {
574 //uint32_t swver = _buffer.ver.swVersion;
575 // EXT CORE 3.01 (107900)
576 // 01234567890123456789012
577 gpsState
.hwVersion
= fastA2I(_buffer
.ver
.hwVersion
);
578 capGalileo
= ((gpsState
.hwVersion
>= 80000) && (_buffer
.ver
.swVersion
[9] > '2')); // M8N and SW major 3 or later
582 if ((_ack_state
== UBX_ACK_WAITING
) && (_buffer
.ack
.msg
== _ack_waiting_msg
)) {
583 _ack_state
= UBX_ACK_GOT_ACK
;
587 if ((_ack_state
== UBX_ACK_WAITING
) && (_buffer
.ack
.msg
== _ack_waiting_msg
)) {
588 _ack_state
= UBX_ACK_GOT_NAK
;
595 // we only return true when we get new position and speed data
596 // this ensures we don't use stale data
597 if (_new_position
&& _new_speed
) {
598 _new_speed
= _new_position
= false;
605 static bool gpsNewFrameUBLOX(uint8_t data
)
610 case 0: // Sync char 1 (0xB5)
611 if (PREAMBLE1
== data
) {
612 _skip_packet
= false;
616 case 1: // Sync char 2 (0x62)
617 if (PREAMBLE2
!= data
) {
626 _ck_b
= _ck_a
= data
; // reset the checksum accumulators
630 _ck_b
+= (_ck_a
+= data
); // checksum byte
633 case 4: // Payload length (part 1)
635 _ck_b
+= (_ck_a
+= data
); // checksum byte
636 _payload_length
= data
; // payload length low byte
638 case 5: // Payload length (part 2)
640 _ck_b
+= (_ck_a
+= data
); // checksum byte
641 _payload_length
|= (uint16_t)(data
<< 8);
642 if (_payload_length
> MAX_UBLOX_PAYLOAD_SIZE
) {
643 // we can't receive the whole packet, just log the error and start searching for the next packet.
648 // prepare to receive payload
649 _payload_counter
= 0;
650 if (_payload_length
== 0) {
655 _ck_b
+= (_ck_a
+= data
); // checksum byte
656 if (_payload_counter
< MAX_UBLOX_PAYLOAD_SIZE
) {
657 _buffer
.bytes
[_payload_counter
] = data
;
659 // NOTE: check counter BEFORE increasing so that a payload_size of 65535 is correctly handled. This can happen if garbage data is received.
660 if (_payload_counter
== _payload_length
- 1) {
668 _skip_packet
= true; // bad checksum
678 break; // bad checksum
681 gpsStats
.packetCount
++;
687 if (gpsParceFrameUBLOX()) {
695 STATIC_PROTOTHREAD(gpsConfigure
)
697 ptBegin(gpsConfigure
);
700 gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT
);
703 switch (gpsState
.gpsConfig
->dynModel
) {
704 case GPS_DYNMODEL_PEDESTRIAN
:
705 configureNAV5(UBX_DYNMODEL_PEDESTRIAN
, UBX_FIXMODE_AUTO
);
707 case GPS_DYNMODEL_AIR_1G
: // Default to this
709 configureNAV5(UBX_DYNMODEL_AIR_1G
, UBX_FIXMODE_AUTO
);
711 case GPS_DYNMODEL_AIR_4G
:
712 configureNAV5(UBX_DYNMODEL_AIR_4G
, UBX_FIXMODE_AUTO
);
715 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
717 // Disable NMEA messages
718 gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT
);
720 configureMSG(MSG_CLASS_NMEA
, MSG_NMEA_GGA
, 0);
721 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
723 configureMSG(MSG_CLASS_NMEA
, MSG_NMEA_GLL
, 0);
724 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
726 configureMSG(MSG_CLASS_NMEA
, MSG_NMEA_GSA
, 0);
727 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
729 configureMSG(MSG_CLASS_NMEA
, MSG_NMEA_GSV
, 0);
730 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
732 configureMSG(MSG_CLASS_NMEA
, MSG_NMEA_RMC
, 0);
733 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
735 configureMSG(MSG_CLASS_NMEA
, MSG_NMEA_VGS
, 0);
736 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
738 // Configure UBX binary messages
739 gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT
);
741 if ((gpsState
.gpsConfig
->provider
== GPS_UBLOX
) || (gpsState
.hwVersion
< 70000)) {
742 configureMSG(MSG_CLASS_UBX
, MSG_POSLLH
, 1);
743 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
745 configureMSG(MSG_CLASS_UBX
, MSG_STATUS
, 1);
746 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
748 configureMSG(MSG_CLASS_UBX
, MSG_SOL
, 1);
749 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
751 configureMSG(MSG_CLASS_UBX
, MSG_VELNED
, 1);
752 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
754 configureMSG(MSG_CLASS_UBX
, MSG_TIMEUTC
, 10);
755 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
757 // This may fail on old UBLOX units, advance forward on both ACK and NAK
758 configureMSG(MSG_CLASS_UBX
, MSG_PVT
, 0);
759 ptWait(_ack_state
== UBX_ACK_GOT_ACK
|| _ack_state
== UBX_ACK_GOT_NAK
);
762 configureMSG(MSG_CLASS_UBX
, MSG_POSLLH
, 0);
763 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
765 configureMSG(MSG_CLASS_UBX
, MSG_STATUS
, 0);
766 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
768 configureMSG(MSG_CLASS_UBX
, MSG_SOL
, 0);
769 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
771 configureMSG(MSG_CLASS_UBX
, MSG_VELNED
, 0);
772 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
774 configureMSG(MSG_CLASS_UBX
, MSG_TIMEUTC
, 0);
775 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
777 configureMSG(MSG_CLASS_UBX
, MSG_PVT
, 1);
778 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
781 configureMSG(MSG_CLASS_UBX
, MSG_SVINFO
, 0);
782 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
784 // Configure data rate
785 gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT
);
786 if ((gpsState
.gpsConfig
->provider
== GPS_UBLOX7PLUS
) && (gpsState
.hwVersion
>= 70000)) {
787 configureRATE(100); // 10Hz
790 configureRATE(200); // 5Hz
792 ptWait(_ack_state
== UBX_ACK_GOT_ACK
);
795 // If particular SBAS setting is not supported by the hardware we'll get a NAK,
796 // however GPS would be functional. We are waiting for any response - ACK/NACK
797 gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT
);
799 ptWaitTimeout((_ack_state
== UBX_ACK_GOT_ACK
|| _ack_state
== UBX_ACK_GOT_NAK
), GPS_CFG_CMD_TIMEOUT_MS
);
802 if (gpsState
.gpsConfig
->ubloxUseGalileo
&& capGalileo
) {
803 // If GALILEO is not supported by the hardware we'll get a NAK,
804 // however GPS would otherwise be perfectly initialized, so we are just waiting for any response
805 gpsSetProtocolTimeout(GPS_SHORT_TIMEOUT
);
807 ptWaitTimeout((_ack_state
== UBX_ACK_GOT_ACK
|| _ack_state
== UBX_ACK_GOT_NAK
), GPS_CFG_CMD_TIMEOUT_MS
);
813 static ptSemaphore_t semNewDataReady
;
815 STATIC_PROTOTHREAD(gpsProtocolReceiverThread
)
817 ptBegin(gpsProtocolReceiverThread
);
820 // Wait until there are bytes to consume
821 ptWait(serialRxBytesWaiting(gpsState
.gpsPort
));
823 // Consume bytes until buffer empty of until we have full message received
824 while (serialRxBytesWaiting(gpsState
.gpsPort
)) {
825 uint8_t newChar
= serialRead(gpsState
.gpsPort
);
826 if (gpsNewFrameUBLOX(newChar
)) {
827 ptSemaphoreSignal(semNewDataReady
);
836 STATIC_PROTOTHREAD(gpsProtocolStateThread
)
838 ptBegin(gpsProtocolStateThread
);
841 if (gpsState
.gpsConfig
->autoBaud
!= GPS_AUTOBAUD_OFF
) {
844 // 0. Wait for TX buffer to be empty
845 ptWait(isSerialTransmitBufferEmpty(gpsState
.gpsPort
));
847 // 1. Set serial port to baud rate specified by [autoBaudrateIndex]
848 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.autoBaudrateIndex
]]);
849 gpsState
.autoBaudrateIndex
= (gpsState
.autoBaudrateIndex
+ 1) % GPS_BAUDRATE_COUNT
;
851 // 2. Send an $UBX command to switch the baud rate specified by portConfig [baudrateIndex]
852 serialPrint(gpsState
.gpsPort
, baudInitDataNMEA
[gpsState
.baudrateIndex
]);
854 // 3. Wait for command to be received and processed by GPS
855 ptWait(isSerialTransmitBufferEmpty(gpsState
.gpsPort
));
857 // 4. Switch to [baudrateIndex]
858 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]]);
860 // 5. Attempt to configure the GPS
861 ptDelayMs(GPS_BAUD_CHANGE_DELAY
);
863 // 0. Wait for TX buffer to be empty
864 ptWait(isSerialTransmitBufferEmpty(gpsState
.gpsPort
));
866 // Try sending baud rate switch command at all common baud rates
867 gpsSetProtocolTimeout((GPS_BAUD_CHANGE_DELAY
+ 50) * (GPS_BAUDRATE_COUNT
));
868 for (gpsState
.autoBaudrateIndex
= 0; gpsState
.autoBaudrateIndex
< GPS_BAUDRATE_COUNT
; gpsState
.autoBaudrateIndex
++) {
869 // 2. Set serial port to baud rate and send an $UBX command to switch the baud rate specified by portConfig [baudrateIndex]
870 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.autoBaudrateIndex
]]);
871 serialPrint(gpsState
.gpsPort
, baudInitDataNMEA
[gpsState
.baudrateIndex
]);
873 // 3. Wait for serial port to finish transmitting
874 ptWait(isSerialTransmitBufferEmpty(gpsState
.gpsPort
));
876 // 4. Extra wait to make sure GPS processed the command
877 ptDelayMs(GPS_BAUD_CHANGE_DELAY
);
880 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]]);
884 // No auto baud - set port baud rate to [baudrateIndex]
885 // Wait for TX buffer to be empty
886 ptWait(isSerialTransmitBufferEmpty(gpsState
.gpsPort
));
888 // Set baud rate and reset GPS timeout
889 serialSetBaudRate(gpsState
.gpsPort
, baudRates
[gpsToSerialBaudRate
[gpsState
.baudrateIndex
]]);
892 // Configure GPS module if enabled
893 if (gpsState
.gpsConfig
->autoConfig
) {
894 // Reset protocol timeout
895 gpsSetProtocolTimeout(MAX(GPS_TIMEOUT
, ((GPS_VERSION_RETRY_TIMES
+ 3) * GPS_CFG_CMD_TIMEOUT_MS
)));
897 // Attempt to detect GPS hw version
898 gpsState
.hwVersion
= 0;
899 gpsState
.autoConfigStep
= 0;
903 gpsState
.autoConfigStep
++;
904 ptWaitTimeout((gpsState
.hwVersion
!= 0), GPS_CFG_CMD_TIMEOUT_MS
);
905 } while(gpsState
.autoConfigStep
< GPS_VERSION_RETRY_TIMES
&& gpsState
.hwVersion
== 0);
908 ptSpawn(gpsConfigure
);
911 // GPS setup done, reset timeout
912 gpsSetProtocolTimeout(GPS_TIMEOUT
);
914 // GPS is ready - execute the gpsProcessNewSolutionData() based on gpsProtocolReceiverThread semaphore
916 ptSemaphoreWait(semNewDataReady
);
917 gpsProcessNewSolutionData();
923 void gpsRestartUBLOX(void)
925 ptSemaphoreInit(semNewDataReady
);
926 ptRestart(ptGetHandle(gpsProtocolReceiverThread
));
927 ptRestart(ptGetHandle(gpsProtocolStateThread
));
930 void gpsHandleUBLOX(void)
932 // Run the protocol threads
933 gpsProtocolReceiverThread();
934 gpsProtocolStateThread();
936 // If thread stopped - signal communication loss and restart
937 if (ptIsStopped(ptGetHandle(gpsProtocolReceiverThread
)) || ptIsStopped(ptGetHandle(gpsProtocolStateThread
))) {
938 gpsSetState(GPS_LOST_COMMUNICATION
);